From 67b686f8c73009ef9cde04f70b9181aa473e1d21 Mon Sep 17 00:00:00 2001 From: Christoffer Martinsson Date: Fri, 28 Nov 2025 14:25:12 +0100 Subject: [PATCH] Remove RAM and disk collection for services Complete removal of service resource metrics: Agent: - Remove memory_mb and disk_gb fields from ServiceData struct - Remove get_service_memory_usage() method - Remove get_service_disk_usage() method - Remove get_directory_size() method - Remove unused warn import Dashboard: - Remove memory_mb and disk_gb from ServiceInfo struct - Remove memory/disk display from format_parent_service_line - Remove memory/disk parsing in legacy metric path - Remove unused format_disk_size() function Service resource metrics were slow, unreliable, and never worked properly since structured data migration. Will be handled differently in the future. --- agent/src/collectors/systemd.rs | 108 +-------------------------- dashboard/src/ui/widgets/services.rs | 60 +-------------- shared/src/agent_data.rs | 2 - 3 files changed, 3 insertions(+), 167 deletions(-) diff --git a/agent/src/collectors/systemd.rs b/agent/src/collectors/systemd.rs index bcaa6be..170d8b0 100644 --- a/agent/src/collectors/systemd.rs +++ b/agent/src/collectors/systemd.rs @@ -4,7 +4,7 @@ use cm_dashboard_shared::{AgentData, ServiceData, SubServiceData, SubServiceMetr use std::process::Command; use std::sync::RwLock; use std::time::Instant; -use tracing::{debug, warn}; +use tracing::debug; use super::{Collector, CollectorError}; use crate::config::SystemdConfig; @@ -87,9 +87,6 @@ impl SystemdCollector { for service_name in &monitored_services { match self.get_service_status(service_name) { Ok((active_status, _detailed_info)) => { - let memory_mb = self.get_service_memory_usage(service_name).await.unwrap_or(0.0); - let disk_gb = self.get_service_disk_usage(service_name).await.unwrap_or(0.0); - let mut sub_services = Vec::new(); // Sub-service metrics for specific services (always include cached results) @@ -155,8 +152,6 @@ impl SystemdCollector { // Create complete service data let service_data = ServiceData { name: service_name.clone(), - memory_mb, - disk_gb, user_stopped: false, // TODO: Integrate with service tracker service_status: self.calculate_service_status(service_name, &active_status), sub_services, @@ -416,80 +411,6 @@ impl SystemdCollector { true } - /// Get disk usage for a specific service - async fn get_service_disk_usage(&self, service_name: &str) -> Result { - // Check if this service has configured directory paths - if let Some(dirs) = self.config.service_directories.get(service_name) { - // Service has configured paths - use the first accessible one - for dir in dirs { - if let Some(size) = self.get_directory_size(dir).await { - return Ok(size); - } - } - // If configured paths failed, return 0 - return Ok(0.0); - } - - // No configured path - try to get WorkingDirectory from systemctl (with 2 second timeout) - let output = Command::new("timeout") - .args(&["2", "systemctl", "show", &format!("{}.service", service_name), "--property=WorkingDirectory"]) - .output() - .map_err(|e| CollectorError::SystemRead { - path: format!("WorkingDirectory for {}", service_name), - error: e.to_string(), - })?; - - let output_str = String::from_utf8_lossy(&output.stdout); - for line in output_str.lines() { - if line.starts_with("WorkingDirectory=") && !line.contains("[not set]") { - let dir = line.strip_prefix("WorkingDirectory=").unwrap_or(""); - if !dir.is_empty() && dir != "/" { - return Ok(self.get_directory_size(dir).await.unwrap_or(0.0)); - } - } - } - - Ok(0.0) - } - - /// Get size of a directory in GB (with 2 second timeout) - async fn get_directory_size(&self, path: &str) -> Option { - use super::run_command_with_timeout; - - // Use -s (summary) and --apparent-size for speed, 2 second timeout - let mut cmd = Command::new("sudo"); - cmd.args(&["du", "-s", "--apparent-size", "--block-size=1", path]); - - let output = run_command_with_timeout(cmd, 2).await.ok()?; - - if !output.status.success() { - // Log permission errors for debugging but don't spam logs - let stderr = String::from_utf8_lossy(&output.stderr); - if stderr.contains("Permission denied") { - debug!("Permission denied accessing directory: {}", path); - } else if stderr.contains("timed out") { - warn!("Directory size check timed out for {}", path); - } else { - debug!("Failed to get size for directory {}: {}", path, stderr); - } - return None; - } - - let output_str = String::from_utf8(output.stdout).ok()?; - let size_str = output_str.split_whitespace().next()?; - if let Ok(size_bytes) = size_str.parse::() { - let size_gb = size_bytes as f32 / (1024.0 * 1024.0 * 1024.0); - // Return size even if very small (minimum 0.001 GB = 1MB for visibility) - if size_gb > 0.0 { - Some(size_gb.max(0.001)) - } else { - None - } - } else { - None - } - } - /// Calculate service status, taking user-stopped services into account fn calculate_service_status(&self, service_name: &str, active_status: &str) -> Status { match active_status.to_lowercase().as_str() { @@ -507,33 +428,6 @@ impl SystemdCollector { } } - /// Get memory usage for a specific service - async fn get_service_memory_usage(&self, service_name: &str) -> Result { - let output = Command::new("systemctl") - .args(&["show", &format!("{}.service", service_name), "--property=MemoryCurrent"]) - .output() - .map_err(|e| CollectorError::SystemRead { - path: format!("memory usage for {}", service_name), - error: e.to_string(), - })?; - - let output_str = String::from_utf8_lossy(&output.stdout); - - for line in output_str.lines() { - if line.starts_with("MemoryCurrent=") { - if let Some(mem_str) = line.strip_prefix("MemoryCurrent=") { - if mem_str != "[not set]" { - if let Ok(memory_bytes) = mem_str.parse::() { - return Ok(memory_bytes as f32 / (1024.0 * 1024.0)); // Convert to MB - } - } - } - } - } - - Ok(0.0) - } - /// Check if service collection cache should be updated fn should_update_cache(&self) -> bool { let state = self.state.read().unwrap(); diff --git a/dashboard/src/ui/widgets/services.rs b/dashboard/src/ui/widgets/services.rs index 5a9ee2a..dc8cae4 100644 --- a/dashboard/src/ui/widgets/services.rs +++ b/dashboard/src/ui/widgets/services.rs @@ -28,8 +28,6 @@ pub struct ServicesWidget { #[derive(Clone)] struct ServiceInfo { - memory_mb: Option, - disk_gb: Option, metrics: Vec<(String, f32, Option)>, // (label, value, unit) widget_status: Status, service_type: String, // "nginx_site", "container", "image", or empty for parent services @@ -52,8 +50,6 @@ impl ServicesWidget { if metric_name.starts_with("service_") { if let Some(end_pos) = metric_name .rfind("_status") - .or_else(|| metric_name.rfind("_memory_mb")) - .or_else(|| metric_name.rfind("_disk_gb")) .or_else(|| metric_name.rfind("_latency_ms")) { let service_part = &metric_name[8..end_pos]; // Remove "service_" prefix @@ -76,36 +72,8 @@ impl ServicesWidget { None } - /// Format disk size with appropriate units (kB/MB/GB) - fn format_disk_size(size_gb: f32) -> String { - let size_mb = size_gb * 1024.0; // Convert GB to MB - - if size_mb >= 1024.0 { - // Show as GB - format!("{:.1}GB", size_gb) - } else if size_mb >= 1.0 { - // Show as MB - format!("{:.0}MB", size_mb) - } else if size_mb >= 0.001 { - // Convert to kB - let size_kb = size_mb * 1024.0; - format!("{:.0}kB", size_kb) - } else { - // Show very small sizes as bytes - let size_bytes = size_mb * 1024.0 * 1024.0; - format!("{:.0}B", size_bytes) - } - } - /// Format parent service line - returns text without icon for span formatting fn format_parent_service_line(&self, name: &str, info: &ServiceInfo) -> String { - let memory_str = info - .memory_mb - .map_or("0M".to_string(), |m| format!("{:.0}M", m)); - let disk_str = info - .disk_gb - .map_or("0".to_string(), |d| Self::format_disk_size(d)); - // Truncate long service names to fit layout (account for icon space) let short_name = if name.len() > 22 { format!("{}...", &name[..19]) @@ -125,8 +93,8 @@ impl ServicesWidget { }; format!( - "{:<23} {:<10} {:<8} {:<8}", - short_name, status_str, memory_str, disk_str + "{:<23} {:<10}", + short_name, status_str ) } @@ -309,8 +277,6 @@ impl Widget for ServicesWidget { for service in &agent_data.services { // Store parent service let parent_info = ServiceInfo { - memory_mb: Some(service.memory_mb), - disk_gb: Some(service.disk_gb), metrics: Vec::new(), // Parent services don't have custom metrics widget_status: service.service_status, service_type: String::new(), // Parent services have no type @@ -327,8 +293,6 @@ impl Widget for ServicesWidget { .collect(); let sub_info = ServiceInfo { - memory_mb: None, // Not used for sub-services - disk_gb: None, // Not used for sub-services metrics, widget_status: sub_service.service_status, service_type: sub_service.service_type.clone(), @@ -371,8 +335,6 @@ impl ServicesWidget { self.parent_services .entry(parent_service) .or_insert(ServiceInfo { - memory_mb: None, - disk_gb: None, metrics: Vec::new(), widget_status: Status::Unknown, service_type: String::new(), @@ -380,14 +342,6 @@ impl ServicesWidget { if metric.name.ends_with("_status") { service_info.widget_status = metric.status; - } else if metric.name.ends_with("_memory_mb") { - if let Some(memory) = metric.value.as_f32() { - service_info.memory_mb = Some(memory); - } - } else if metric.name.ends_with("_disk_gb") { - if let Some(disk) = metric.value.as_f32() { - service_info.disk_gb = Some(disk); - } } } Some(sub_name) => { @@ -407,8 +361,6 @@ impl ServicesWidget { sub_service_list.push(( sub_name.clone(), ServiceInfo { - memory_mb: None, - disk_gb: None, metrics: Vec::new(), widget_status: Status::Unknown, service_type: String::new(), // Unknown type in legacy path @@ -419,14 +371,6 @@ impl ServicesWidget { if metric.name.ends_with("_status") { sub_service_info.widget_status = metric.status; - } else if metric.name.ends_with("_memory_mb") { - if let Some(memory) = metric.value.as_f32() { - sub_service_info.memory_mb = Some(memory); - } - } else if metric.name.ends_with("_disk_gb") { - if let Some(disk) = metric.value.as_f32() { - sub_service_info.disk_gb = Some(disk); - } } } } diff --git a/shared/src/agent_data.rs b/shared/src/agent_data.rs index 97e7e42..17e123b 100644 --- a/shared/src/agent_data.rs +++ b/shared/src/agent_data.rs @@ -136,8 +136,6 @@ pub struct PoolDriveData { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ServiceData { pub name: String, - pub memory_mb: f32, - pub disk_gb: f32, pub user_stopped: bool, pub service_status: Status, pub sub_services: Vec,