diff --git a/agent/src/collectors/service.rs b/agent/src/collectors/service.rs index 268a4e3..048b4fb 100644 --- a/agent/src/collectors/service.rs +++ b/agent/src/collectors/service.rs @@ -389,6 +389,38 @@ impl ServiceCollector { Ok((parse(parts[0])?, parse(parts[1])?, parse(parts[2])?)) } + fn determine_cpu_status(&self, cpu_load_5: f32) -> String { + if cpu_load_5 >= 8.0 { + "critical".to_string() + } else if cpu_load_5 >= 5.0 { + "warning".to_string() + } else { + "ok".to_string() + } + } + + fn determine_memory_status(&self, usage_percent: f32) -> String { + if usage_percent >= 95.0 { + "critical".to_string() + } else if usage_percent >= 80.0 { + "warning".to_string() + } else { + "ok".to_string() + } + } + + fn determine_services_status(&self, healthy: usize, degraded: usize, failed: usize) -> String { + if failed > 0 { + "critical".to_string() + } else if degraded > 0 { + "warning".to_string() + } else if healthy > 0 { + "ok".to_string() + } else { + "unknown".to_string() + } + } + async fn get_cpu_cstate_info(&self) -> Option> { // Read C-state information to show all sleep state distributions let mut cstate_times: Vec<(String, u64)> = Vec::new(); @@ -966,6 +998,19 @@ impl Collector for ServiceCollector { let (cpu_load_1, cpu_load_5, cpu_load_15) = self.get_cpu_load().await.unwrap_or((0.0, 0.0, 0.0)); + let cpu_status = self.determine_cpu_status(cpu_load_5); + + // Calculate memory usage percentage and status + let memory_usage_percent = if system_memory.total_mb > 0.0 { + (system_memory.used_mb / system_memory.total_mb) * 100.0 + } else { + 0.0 + }; + let memory_status = self.determine_memory_status(memory_usage_percent); + + // Calculate overall services status + let services_status = self.determine_services_status(healthy, degraded, failed); + let cpu_cstate_info = self.get_cpu_cstate_info().await; let cpu_temp_c = self.get_cpu_temperature_c().await; let (gpu_load_percent, gpu_temp_c) = self.get_gpu_metrics().await; @@ -980,15 +1025,18 @@ impl Collector for ServiceCollector { "healthy": healthy, "degraded": degraded, "failed": failed, + "services_status": services_status, "memory_used_mb": total_memory_used, "memory_quota_mb": total_memory_quota, "system_memory_used_mb": system_memory.used_mb, "system_memory_total_mb": system_memory.total_mb, + "memory_status": memory_status, "disk_used_gb": total_disk_used, "disk_total_gb": total_disk_used, // For services, total = used (no quota concept) "cpu_load_1": cpu_load_1, "cpu_load_5": cpu_load_5, "cpu_load_15": cpu_load_15, + "cpu_status": cpu_status, "cpu_cstate": cpu_cstate_info, "cpu_temp_c": cpu_temp_c, "gpu_load_percent": gpu_load_percent, diff --git a/dashboard/src/data/metrics.rs b/dashboard/src/data/metrics.rs index 256b1af..3ca3eb3 100644 --- a/dashboard/src/data/metrics.rs +++ b/dashboard/src/data/metrics.rs @@ -44,6 +44,8 @@ pub struct ServiceSummary { pub healthy: usize, pub degraded: usize, pub failed: usize, + #[serde(default)] + pub services_status: Option, pub memory_used_mb: f32, pub memory_quota_mb: f32, #[serde(default)] @@ -51,6 +53,8 @@ pub struct ServiceSummary { #[serde(default)] pub system_memory_total_mb: f32, #[serde(default)] + pub memory_status: Option, + #[serde(default)] pub disk_used_gb: f32, #[serde(default)] pub disk_total_gb: f32, @@ -61,6 +65,8 @@ pub struct ServiceSummary { #[serde(default)] pub cpu_load_15: f32, #[serde(default)] + pub cpu_status: Option, + #[serde(default)] pub cpu_cstate: Option>, #[serde(default)] pub cpu_temp_c: Option, diff --git a/dashboard/src/ui/backup.rs b/dashboard/src/ui/backup.rs index 05d3e3a..e317034 100644 --- a/dashboard/src/ui/backup.rs +++ b/dashboard/src/ui/backup.rs @@ -53,7 +53,7 @@ fn render_metrics(frame: &mut Frame, _host: &HostDisplayData, metrics: &BackupMe data.add_row( Some(WidgetStatus::new(latest_status)), - vec![], + vec![format!("{} archives, {:.1} GiB total", metrics.backup.snapshot_count, metrics.backup.size_gb)], vec![ "Latest".to_string(), latest_time, @@ -61,17 +61,6 @@ fn render_metrics(frame: &mut Frame, _host: &HostDisplayData, metrics: &BackupMe ], ); - // Repository total - data.add_row( - Some(WidgetStatus::new(StatusLevel::Ok)), - vec![], - vec![ - "Repo".to_string(), - format!("{} archives", metrics.backup.snapshot_count), - format!("{:.1} GiB total", metrics.backup.size_gb), - ], - ); - // Disk usage if let Some(disk) = &metrics.disk { let disk_status = match disk.health.as_str() { @@ -84,9 +73,9 @@ fn render_metrics(frame: &mut Frame, _host: &HostDisplayData, metrics: &BackupMe Some(WidgetStatus::new(disk_status)), vec![], vec![ - "Disk usage".to_string(), + "Disk".to_string(), disk.health.clone(), - format!("{:.0} GB, {:.0}% used", disk.total_gb, disk.usage_percent), + format!("{:.1}/{:.0} GB ({:.0}%)", disk.used_gb, disk.total_gb, disk.usage_percent), ], ); } else { @@ -94,7 +83,7 @@ fn render_metrics(frame: &mut Frame, _host: &HostDisplayData, metrics: &BackupMe Some(WidgetStatus::new(StatusLevel::Unknown)), vec![], vec![ - "Disk usage".to_string(), + "Disk".to_string(), "Unknown".to_string(), "—".to_string(), ], diff --git a/dashboard/src/ui/services.rs b/dashboard/src/ui/services.rs index 9e68572..5424205 100644 --- a/dashboard/src/ui/services.rs +++ b/dashboard/src/ui/services.rs @@ -4,7 +4,7 @@ use ratatui::Frame; use crate::app::HostDisplayData; use crate::data::metrics::{ServiceStatus, ServiceSummary}; -use crate::ui::widget::{render_placeholder, render_widget_data, WidgetData, WidgetStatus, StatusLevel}; +use crate::ui::widget::{render_placeholder, render_widget_data, status_level_from_agent_status, WidgetData, WidgetStatus, StatusLevel}; pub fn render(frame: &mut Frame, host: Option<&HostDisplayData>, area: Rect) { match host { @@ -34,13 +34,8 @@ fn render_metrics( let color = summary_color(summary); let title = "Services".to_string(); - let widget_status = if summary.failed > 0 { - StatusLevel::Error - } else if summary.degraded > 0 { - StatusLevel::Warning - } else { - StatusLevel::Ok - }; + // Use agent-calculated services status + let widget_status = status_level_from_agent_status(summary.services_status.as_ref()); let mut data = WidgetData::new( title, diff --git a/dashboard/src/ui/system.rs b/dashboard/src/ui/system.rs index 1af22e7..b403a11 100644 --- a/dashboard/src/ui/system.rs +++ b/dashboard/src/ui/system.rs @@ -6,7 +6,7 @@ use crate::app::HostDisplayData; use crate::data::metrics::{ServiceMetrics, ServiceSummary}; use crate::ui::widget::{ combined_color, render_placeholder, render_combined_widget_data, status_color_for_cpu_load, status_color_from_metric, - status_color_from_percentage, WidgetDataSet, WidgetStatus, StatusLevel, + status_color_from_percentage, status_level_from_agent_status, WidgetDataSet, WidgetStatus, StatusLevel, }; pub fn render(frame: &mut Frame, host: Option<&HostDisplayData>, area: Rect) { @@ -57,8 +57,9 @@ fn render_metrics( PerfSeverity::Ok => Color::Green, }; - let memory_color = status_color_from_percentage(usage_ratio, 80.0, 95.0); - let cpu_load_color = status_color_for_cpu_load(summary.cpu_load_5); + // Use agent-calculated statuses instead of dashboard calculations + let memory_status = status_level_from_agent_status(summary.memory_status.as_ref()); + let cpu_status = status_level_from_agent_status(summary.cpu_status.as_ref()); let cpu_temp_color = status_color_from_metric(summary.cpu_temp_c, 80.0, 90.0); let gpu_load_color = summary .gpu_load_percent @@ -69,11 +70,9 @@ fn render_metrics( .map(|value| status_color_from_metric(Some(value), 75.0, 85.0)) .unwrap_or(Color::Green); - let cpu_icon_color = combined_color(&[cpu_load_color, cpu_temp_color]); let gpu_icon_color = combined_color(&[gpu_load_color, gpu_temp_color]); - // Memory dataset - let memory_status = status_level_from_color(memory_color); + // Memory dataset - use agent-calculated status let mut memory_dataset = WidgetDataSet::new(vec!["Memory usage".to_string()], Some(WidgetStatus::new(memory_status))); memory_dataset.add_row( Some(WidgetStatus::new(memory_status)), @@ -81,8 +80,7 @@ fn render_metrics( vec![format!("{:.1} / {:.1} GB", system_used / 1000.0, system_total / 1000.0)], ); - // CPU dataset - let cpu_status = status_level_from_color(cpu_icon_color); + // CPU dataset - use agent-calculated status let mut cpu_dataset = WidgetDataSet::new(vec!["CPU load".to_string(), "CPU temp".to_string()], Some(WidgetStatus::new(cpu_status))); cpu_dataset.add_row( Some(WidgetStatus::new(cpu_status)), diff --git a/dashboard/src/ui/widget.rs b/dashboard/src/ui/widget.rs index 95ec4c4..61b65cf 100644 --- a/dashboard/src/ui/widget.rs +++ b/dashboard/src/ui/widget.rs @@ -43,15 +43,25 @@ pub fn status_color_from_metric(value: Option, warn: f32, crit: f32) -> Col } pub fn status_color_for_cpu_load(load: f32) -> Color { - if load >= 4.0 { + if load >= 8.0 { Color::Red - } else if load >= 2.0 { + } else if load >= 5.0 { Color::Yellow } else { Color::Green } } +pub fn status_level_from_agent_status(agent_status: Option<&String>) -> StatusLevel { + match agent_status.map(|s| s.as_str()) { + Some("critical") => StatusLevel::Error, + Some("warning") => StatusLevel::Warning, + Some("ok") => StatusLevel::Ok, + Some("unknown") => StatusLevel::Unknown, + _ => StatusLevel::Unknown, + } +} + pub fn combined_color(colors: &[Color]) -> Color { if colors.iter().any(|&c| c == Color::Red) { Color::Red