diff --git a/agent/src/collectors/disk.rs b/agent/src/collectors/disk.rs index e434ef4..546feb9 100644 --- a/agent/src/collectors/disk.rs +++ b/agent/src/collectors/disk.rs @@ -527,63 +527,6 @@ impl Collector for DiskCollector { timestamp: chrono::Utc::now().timestamp() as u64, }); - // Monitor /tmp directory size (keep existing functionality) - match self.get_directory_size("/tmp") { - Ok(tmp_size_bytes) => { - let tmp_size_mb = tmp_size_bytes as f64 / (1024.0 * 1024.0); - - // Get /tmp filesystem info (usually tmpfs with 2GB limit) - let (total_bytes, _) = match self.get_filesystem_info("/tmp") { - Ok((total, used)) => (total, used), - Err(_) => { - // Fallback: assume 2GB limit for tmpfs - (2 * 1024 * 1024 * 1024, tmp_size_bytes) - } - }; - - let total_mb = total_bytes as f64 / (1024.0 * 1024.0); - let usage_percent = (tmp_size_bytes as f64 / total_bytes as f64) * 100.0; - let status = self.calculate_usage_status(tmp_size_bytes, total_bytes); - - metrics.push(Metric { - name: "disk_tmp_size_mb".to_string(), - value: MetricValue::Float(tmp_size_mb as f32), - unit: Some("MB".to_string()), - description: Some(format!("Used: {:.1} MB", tmp_size_mb)), - status, - timestamp: chrono::Utc::now().timestamp() as u64, - }); - - metrics.push(Metric { - name: "disk_tmp_total_mb".to_string(), - value: MetricValue::Float(total_mb as f32), - unit: Some("MB".to_string()), - description: Some(format!("Total: {:.1} MB", total_mb)), - status: Status::Ok, - timestamp: chrono::Utc::now().timestamp() as u64, - }); - - metrics.push(Metric { - name: "disk_tmp_usage_percent".to_string(), - value: MetricValue::Float(usage_percent as f32), - unit: Some("%".to_string()), - description: Some(format!("Usage: {:.1}%", usage_percent)), - status, - timestamp: chrono::Utc::now().timestamp() as u64, - }); - } - Err(e) => { - debug!("Failed to get /tmp size: {}", e); - metrics.push(Metric { - name: "disk_tmp_size_mb".to_string(), - value: MetricValue::String("error".to_string()), - unit: Some("MB".to_string()), - description: Some(format!("Error: {}", e)), - status: Status::Unknown, - timestamp: chrono::Utc::now().timestamp() as u64, - }); - } - } let collection_time = start_time.elapsed(); debug!( diff --git a/agent/src/collectors/memory.rs b/agent/src/collectors/memory.rs index b486801..b5dd1b6 100644 --- a/agent/src/collectors/memory.rs +++ b/agent/src/collectors/memory.rs @@ -188,8 +188,98 @@ impl MemoryCollector { ); } + // Monitor tmpfs (/tmp) usage + if let Ok(tmpfs_metrics) = self.get_tmpfs_metrics() { + metrics.extend(tmpfs_metrics); + } + metrics } + + /// Get tmpfs (/tmp) usage metrics + fn get_tmpfs_metrics(&self) -> Result, CollectorError> { + use std::process::Command; + + let output = Command::new("df") + .arg("--block-size=1") + .arg("/tmp") + .output() + .map_err(|e| CollectorError::SystemRead { + path: "/tmp".to_string(), + error: e.to_string(), + })?; + + if !output.status.success() { + return Ok(Vec::new()); // Return empty if /tmp not available + } + + let output_str = String::from_utf8(output.stdout) + .map_err(|e| CollectorError::Parse { + value: "df output".to_string(), + error: e.to_string(), + })?; + + let lines: Vec<&str> = output_str.lines().collect(); + if lines.len() < 2 { + return Ok(Vec::new()); + } + + let fields: Vec<&str> = lines[1].split_whitespace().collect(); + if fields.len() < 4 { + return Ok(Vec::new()); + } + + let total_bytes: u64 = fields[1].parse() + .map_err(|e: std::num::ParseIntError| CollectorError::Parse { + value: fields[1].to_string(), + error: e.to_string(), + })?; + let used_bytes: u64 = fields[2].parse() + .map_err(|e: std::num::ParseIntError| CollectorError::Parse { + value: fields[2].to_string(), + error: e.to_string(), + })?; + + let total_gb = total_bytes as f32 / (1024.0 * 1024.0 * 1024.0); + let used_gb = used_bytes as f32 / (1024.0 * 1024.0 * 1024.0); + let usage_percent = if total_bytes > 0 { + (used_bytes as f32 / total_bytes as f32) * 100.0 + } else { + 0.0 + }; + + let mut metrics = Vec::new(); + let timestamp = chrono::Utc::now().timestamp() as u64; + + metrics.push(Metric { + name: "memory_tmp_usage_percent".to_string(), + value: MetricValue::Float(usage_percent), + unit: Some("%".to_string()), + description: Some("tmpfs /tmp usage percentage".to_string()), + status: Status::Ok, + timestamp, + }); + + metrics.push(Metric { + name: "memory_tmp_used_gb".to_string(), + value: MetricValue::Float(used_gb), + unit: Some("GB".to_string()), + description: Some("tmpfs /tmp used space".to_string()), + status: Status::Ok, + timestamp, + }); + + metrics.push(Metric { + name: "memory_tmp_total_gb".to_string(), + value: MetricValue::Float(total_gb), + unit: Some("GB".to_string()), + description: Some("tmpfs /tmp total space".to_string()), + status: Status::Ok, + timestamp, + }); + + Ok(metrics) + } } #[async_trait] diff --git a/dashboard/src/ui/widgets/system.rs b/dashboard/src/ui/widgets/system.rs index c178bec..0599e88 100644 --- a/dashboard/src/ui/widgets/system.rs +++ b/dashboard/src/ui/widgets/system.rs @@ -202,7 +202,10 @@ impl SystemWidget { } } - self.storage_pools = pools.into_values().collect(); + // Convert to sorted vec for consistent ordering + let mut pool_list: Vec = pools.into_values().collect(); + pool_list.sort_by(|a, b| a.name.cmp(&b.name)); // Sort alphabetically by name + self.storage_pools = pool_list; } /// Extract pool name from disk metric name @@ -354,21 +357,6 @@ impl Widget for SystemWidget { self.memory_total_gb = Some(total); } } - "disk_tmp_usage_percent" => { - if let MetricValue::Float(usage) = metric.value { - self.tmp_usage_percent = Some(usage); - } - } - "disk_tmp_used_gb" => { - if let MetricValue::Float(used) = metric.value { - self.tmp_used_gb = Some(used); - } - } - "disk_tmp_total_gb" => { - if let MetricValue::Float(total) = metric.value { - self.tmp_total_gb = Some(total); - } - } _ => {} } } @@ -430,8 +418,7 @@ impl Widget for SystemWidget { // Storage section with tree structure lines.extend(self.render_storage()); - let paragraph = Paragraph::new(Text::from(lines)) - .block(Block::default().borders(Borders::ALL).title("System")); + let paragraph = Paragraph::new(Text::from(lines)); frame.render_widget(paragraph, area); }