Skip to main content

trueno_gpu/monitor/stress_test/
config.rs

1//! Stress test configuration types (TRUENO-SPEC-025 Section 7.3)
2//!
3//! Contains [`StressTestConfig`], [`StressTarget`], and [`ChaosPreset`].
4
5use std::time::Duration;
6
7use super::super::device::DeviceId;
8
9// ============================================================================
10// Stress Test Configuration (TRUENO-SPEC-025 Section 7.3)
11// ============================================================================
12
13/// Stress test configuration
14#[derive(Debug, Clone)]
15pub struct StressTestConfig {
16    /// Target resource(s) to stress
17    pub target: StressTarget,
18    /// Test duration
19    pub duration: Duration,
20    /// Intensity (0.0-1.0, where 1.0 = maximum load)
21    pub intensity: f64,
22    /// Ramp-up duration (gradual increase)
23    pub ramp_up: Duration,
24    /// Chaos engineering preset (optional)
25    pub chaos_preset: Option<ChaosPreset>,
26    /// Whether to collect detailed metrics
27    pub collect_metrics: bool,
28    /// Whether to export report on completion
29    pub export_report: bool,
30}
31
32impl Default for StressTestConfig {
33    fn default() -> Self {
34        Self {
35            target: StressTarget::All,
36            duration: Duration::from_secs(60),
37            intensity: 1.0,
38            ramp_up: Duration::from_secs(5),
39            chaos_preset: None,
40            collect_metrics: true,
41            export_report: true,
42        }
43    }
44}
45
46impl StressTestConfig {
47    /// Create a new stress test configuration
48    #[must_use]
49    pub fn new() -> Self {
50        Self::default()
51    }
52
53    /// Set target
54    #[must_use]
55    pub fn with_target(mut self, target: StressTarget) -> Self {
56        self.target = target;
57        self
58    }
59
60    /// Set duration
61    #[must_use]
62    pub fn with_duration(mut self, duration: Duration) -> Self {
63        self.duration = duration;
64        self
65    }
66
67    /// Set intensity
68    #[must_use]
69    pub fn with_intensity(mut self, intensity: f64) -> Self {
70        self.intensity = intensity.clamp(0.0, 1.0);
71        self
72    }
73
74    /// Set ramp-up duration
75    #[must_use]
76    pub fn with_ramp_up(mut self, ramp_up: Duration) -> Self {
77        self.ramp_up = ramp_up;
78        self
79    }
80
81    /// Set chaos preset
82    #[must_use]
83    pub fn with_chaos(mut self, preset: ChaosPreset) -> Self {
84        self.chaos_preset = Some(preset);
85        self
86    }
87
88    /// Parse duration from string (e.g., "60s", "5m", "1h")
89    #[must_use]
90    pub fn parse_duration(s: &str) -> Option<Duration> {
91        let s = s.trim();
92        if s.is_empty() {
93            return None;
94        }
95
96        let (num, unit) = s.split_at(s.len() - 1);
97        let value: u64 = num.parse().ok()?;
98
99        match unit {
100            "s" => Some(Duration::from_secs(value)),
101            "m" => Some(Duration::from_secs(value * 60)),
102            "h" => Some(Duration::from_secs(value * 3600)),
103            _ => None,
104        }
105    }
106}
107
108/// Stress target
109#[derive(Debug, Clone, PartialEq)]
110pub enum StressTarget {
111    /// Stress all resources
112    All,
113    /// Stress CPU only
114    Cpu,
115    /// Stress GPU (optionally specific device)
116    Gpu(Option<DeviceId>),
117    /// Stress memory (RAM + VRAM)
118    Memory,
119    /// Stress PCIe bandwidth
120    Pcie,
121    /// Custom combination
122    Custom(Vec<StressTarget>),
123}
124
125impl StressTarget {
126    /// Parse from string (e.g., "cpu", "gpu", "gpu:0", "memory", "pcie")
127    #[must_use]
128    pub fn parse(s: &str) -> Option<Self> {
129        let s = s.trim().to_lowercase();
130
131        if s == "all" {
132            return Some(Self::All);
133        }
134        if s == "cpu" {
135            return Some(Self::Cpu);
136        }
137        if s == "memory" {
138            return Some(Self::Memory);
139        }
140        if s == "pcie" {
141            return Some(Self::Pcie);
142        }
143        if s == "gpu" {
144            return Some(Self::Gpu(None));
145        }
146        if let Some(idx_str) = s.strip_prefix("gpu:") {
147            let idx: u32 = idx_str.parse().ok()?;
148            return Some(Self::Gpu(Some(DeviceId::nvidia(idx))));
149        }
150
151        None
152    }
153
154    /// Check if target includes CPU
155    #[must_use]
156    pub fn includes_cpu(&self) -> bool {
157        match self {
158            Self::All | Self::Cpu => true,
159            Self::Custom(targets) => targets.iter().any(|t| t.includes_cpu()),
160            _ => false,
161        }
162    }
163
164    /// Check if target includes GPU
165    #[must_use]
166    pub fn includes_gpu(&self) -> bool {
167        match self {
168            Self::All | Self::Gpu(_) => true,
169            Self::Custom(targets) => targets.iter().any(|t| t.includes_gpu()),
170            _ => false,
171        }
172    }
173
174    /// Check if target includes memory
175    #[must_use]
176    pub fn includes_memory(&self) -> bool {
177        match self {
178            Self::All | Self::Memory => true,
179            Self::Custom(targets) => targets.iter().any(|t| t.includes_memory()),
180            _ => false,
181        }
182    }
183
184    /// Check if target includes PCIe
185    #[must_use]
186    pub fn includes_pcie(&self) -> bool {
187        match self {
188            Self::All | Self::Pcie => true,
189            Self::Custom(targets) => targets.iter().any(|t| t.includes_pcie()),
190            _ => false,
191        }
192    }
193}
194
195// ============================================================================
196// Chaos Engineering Presets (from renacer)
197// ============================================================================
198
199/// Chaos engineering preset
200#[derive(Debug, Clone, Copy, PartialEq, Eq)]
201pub enum ChaosPreset {
202    /// Gentle chaos (minimal impact)
203    Gentle,
204    /// Moderate chaos
205    Moderate,
206    /// Aggressive chaos
207    Aggressive,
208    /// Extreme chaos (maximum stress)
209    Extreme,
210}
211
212impl ChaosPreset {
213    /// Parse from string
214    #[must_use]
215    pub fn parse(s: &str) -> Option<Self> {
216        match s.trim().to_lowercase().as_str() {
217            "gentle" => Some(Self::Gentle),
218            "moderate" => Some(Self::Moderate),
219            "aggressive" => Some(Self::Aggressive),
220            "extreme" => Some(Self::Extreme),
221            _ => None,
222        }
223    }
224
225    /// Get memory limit factor (0.0-1.0)
226    #[must_use]
227    pub fn memory_limit_factor(&self) -> f64 {
228        match self {
229            Self::Gentle => 0.9,     // 90% of available
230            Self::Moderate => 0.75,  // 75%
231            Self::Aggressive => 0.5, // 50%
232            Self::Extreme => 0.25,   // 25%
233        }
234    }
235
236    /// Get CPU throttle factor (0.0-1.0)
237    #[must_use]
238    pub fn cpu_throttle_factor(&self) -> f64 {
239        match self {
240            Self::Gentle => 1.0,     // No throttle
241            Self::Moderate => 0.9,   // 90%
242            Self::Aggressive => 0.7, // 70%
243            Self::Extreme => 0.5,    // 50%
244        }
245    }
246
247    /// Get network latency injection in milliseconds
248    #[must_use]
249    pub fn network_latency_ms(&self) -> u32 {
250        match self {
251            Self::Gentle => 0,
252            Self::Moderate => 10,
253            Self::Aggressive => 50,
254            Self::Extreme => 200,
255        }
256    }
257
258    /// Get failure injection rate (0.0-1.0)
259    #[must_use]
260    pub fn failure_rate(&self) -> f64 {
261        match self {
262            Self::Gentle => 0.0,
263            Self::Moderate => 0.01,
264            Self::Aggressive => 0.05,
265            Self::Extreme => 0.10,
266        }
267    }
268}