diff --git a/agent/src/cache/manager.rs b/agent/src/cache/manager.rs index dc3c957..31f3198 100644 --- a/agent/src/cache/manager.rs +++ b/agent/src/cache/manager.rs @@ -6,7 +6,6 @@ use tracing::info; /// Manages metric caching with background tasks pub struct MetricCacheManager { cache: Arc, - config: CacheConfig, } impl MetricCacheManager { @@ -15,7 +14,6 @@ impl MetricCacheManager { Self { cache, - config, } } @@ -25,69 +23,14 @@ impl MetricCacheManager { info!("Cache manager background tasks disabled for debugging"); } - /// Check if metric should be collected - pub async fn should_collect_metric(&self, metric_name: &str) -> bool { - self.cache.should_collect(metric_name).await - } - /// Store metric in cache pub async fn cache_metric(&self, metric: Metric) { self.cache.store_metric(metric).await; } - /// Get cached metric if valid - pub async fn get_cached_metric(&self, metric_name: &str) -> Option { - self.cache.get_cached_metric(metric_name).await - } - - /// Get all valid cached metrics - pub async fn get_all_valid_metrics(&self) -> Vec { - self.cache.get_all_valid_metrics().await - } - /// Get all cached metrics (including expired ones) for broadcasting pub async fn get_all_cached_metrics(&self) -> Vec { self.cache.get_all_cached_metrics().await } - /// Cache warm-up: collect and cache high-priority metrics - pub async fn warm_cache(&self, collector_fn: F) - where - F: Fn(&str) -> Option, - { - if !self.config.enabled { - return; - } - - let high_priority_patterns = ["cpu_load_*", "memory_usage_*"]; - let mut warmed_count = 0; - - for pattern in &high_priority_patterns { - // This is a simplified warm-up - in practice, you'd iterate through - // known metric names or use a registry - if pattern.starts_with("cpu_load_") { - for suffix in &["1min", "5min", "15min"] { - let metric_name = format!("cpu_load_{}", suffix); - if let Some(metric) = collector_fn(&metric_name) { - self.cache_metric(metric).await; - warmed_count += 1; - } - } - } - } - - if warmed_count > 0 { - info!("Cache warmed with {} metrics", warmed_count); - } - } - - /// Get cache configuration - pub fn get_config(&self) -> &CacheConfig { - &self.config - } - - /// Get cache tier interval for a metric - pub fn get_cache_interval(&self, metric_name: &str) -> u64 { - self.config.get_cache_interval(metric_name) - } } \ No newline at end of file diff --git a/agent/src/cache/mod.rs b/agent/src/cache/mod.rs index 160e74b..e5ca512 100644 --- a/agent/src/cache/mod.rs +++ b/agent/src/cache/mod.rs @@ -24,26 +24,6 @@ impl ConfigurableCache { } } - /// Check if metric should be collected based on cache tier - pub async fn should_collect(&self, metric_name: &str) -> bool { - if !self.config.enabled { - return true; - } - - let cache = self.cache.read().await; - - if let Some(cached_metric) = cache.get(metric_name) { - let cache_interval = self.config.get_cache_interval(metric_name); - let elapsed = cached_metric.collected_at.elapsed().as_secs(); - - // Should collect if cache interval has passed - elapsed >= cache_interval - } else { - // Not cached yet, should collect - true - } - } - /// Store metric in cache pub async fn store_metric(&self, metric: Metric) { if !self.config.enabled { @@ -69,50 +49,6 @@ impl ConfigurableCache { // Cached metric (debug logging disabled for performance) } - /// Get cached metric if valid - pub async fn get_cached_metric(&self, metric_name: &str) -> Option { - if !self.config.enabled { - return None; - } - - let mut cache = self.cache.write().await; - - if let Some(cached_metric) = cache.get_mut(metric_name) { - let cache_interval = self.config.get_cache_interval(metric_name); - let elapsed = cached_metric.collected_at.elapsed().as_secs(); - - if elapsed < cache_interval { - cached_metric.access_count += 1; - // Cache hit (debug logging disabled for performance) - return Some(cached_metric.metric.clone()); - } else { - // Cache expired (debug logging disabled for performance) - } - } - - None - } - - /// Get all cached metrics that are still valid - pub async fn get_all_valid_metrics(&self) -> Vec { - if !self.config.enabled { - return vec![]; - } - - let cache = self.cache.read().await; - let mut valid_metrics = Vec::new(); - - for (metric_name, cached_metric) in cache.iter() { - let cache_interval = self.config.get_cache_interval(metric_name); - let elapsed = cached_metric.collected_at.elapsed().as_secs(); - - if elapsed < cache_interval { - valid_metrics.push(cached_metric.metric.clone()); - } - } - - valid_metrics - } /// Get all cached metrics (including expired ones) for broadcasting pub async fn get_all_cached_metrics(&self) -> Vec { @@ -162,43 +98,4 @@ impl ConfigurableCache { } } - /// Get cache statistics - pub async fn get_stats(&self) -> CacheStats { - let cache = self.cache.read().await; - - let mut stats_by_tier = HashMap::new(); - for (_metric_name, cached_metric) in cache.iter() { - let tier_name = cached_metric.tier - .as_ref() - .map(|t| t.description.clone()) - .unwrap_or_else(|| "default".to_string()); - - let tier_stats = stats_by_tier.entry(tier_name).or_insert(TierStats { - count: 0, - total_access_count: 0, - }); - - tier_stats.count += 1; - tier_stats.total_access_count += cached_metric.access_count; - } - - CacheStats { - total_entries: cache.len(), - stats_by_tier, - enabled: self.config.enabled, - } - } -} - -#[derive(Debug)] -pub struct CacheStats { - pub total_entries: usize, - pub stats_by_tier: HashMap, - pub enabled: bool, -} - -#[derive(Debug)] -pub struct TierStats { - pub count: usize, - pub total_access_count: u64, } \ No newline at end of file diff --git a/agent/src/collectors/cached_collector.rs b/agent/src/collectors/cached_collector.rs deleted file mode 100644 index 01c6fed..0000000 --- a/agent/src/collectors/cached_collector.rs +++ /dev/null @@ -1,74 +0,0 @@ -use super::{Collector, CollectorError}; -use crate::cache::MetricCacheManager; -use cm_dashboard_shared::Metric; -use async_trait::async_trait; -use std::sync::Arc; -use tracing::{debug, instrument}; - -/// Wrapper that adds caching to any collector -pub struct CachedCollector { - name: String, - inner: Box, - cache_manager: Arc, -} - -impl CachedCollector { - pub fn new( - name: String, - inner: Box, - cache_manager: Arc - ) -> Self { - Self { - name, - inner, - cache_manager, - } - } -} - -#[async_trait] -impl Collector for CachedCollector { - fn name(&self) -> &str { - &self.name - } - - #[instrument(skip(self), fields(collector = %self.name))] - async fn collect(&self) -> Result, CollectorError> { - // First, get all metrics this collector would normally produce - let all_metrics = self.inner.collect().await?; - - let mut result_metrics = Vec::new(); - let mut metrics_to_collect = Vec::new(); - - // Check cache for each metric - for metric in all_metrics { - if let Some(cached_metric) = self.cache_manager.get_cached_metric(&metric.name).await { - // Use cached version - result_metrics.push(cached_metric); - debug!("Using cached metric: {}", metric.name); - } else { - // Need to collect this metric - metrics_to_collect.push(metric.name.clone()); - result_metrics.push(metric); - } - } - - // Cache the newly collected metrics - for metric in &result_metrics { - if metrics_to_collect.contains(&metric.name) { - self.cache_manager.cache_metric(metric.clone()).await; - debug!("Cached new metric: {} (tier: {}s)", - metric.name, - self.cache_manager.get_cache_interval(&metric.name)); - } - } - - if !metrics_to_collect.is_empty() { - debug!("Collected {} new metrics, used {} cached metrics", - metrics_to_collect.len(), - result_metrics.len() - metrics_to_collect.len()); - } - - Ok(result_metrics) - } -} \ No newline at end of file diff --git a/agent/src/collectors/error.rs b/agent/src/collectors/error.rs index ade101a..bd160a7 100644 --- a/agent/src/collectors/error.rs +++ b/agent/src/collectors/error.rs @@ -7,16 +7,4 @@ pub enum CollectorError { #[error("Failed to parse value '{value}': {error}")] Parse { value: String, error: String }, - - #[error("System command failed: {command}: {error}")] - CommandFailed { command: String, error: String }, - - #[error("Configuration error: {message}")] - Configuration { message: String }, - - #[error("Metric calculation error: {message}")] - Calculation { message: String }, - - #[error("Timeout error: operation took longer than {timeout_ms}ms")] - Timeout { timeout_ms: u64 }, } \ No newline at end of file diff --git a/agent/src/collectors/mod.rs b/agent/src/collectors/mod.rs index 0ba52c9..47f575d 100644 --- a/agent/src/collectors/mod.rs +++ b/agent/src/collectors/mod.rs @@ -2,7 +2,6 @@ use async_trait::async_trait; use cm_dashboard_shared::Metric; use std::time::Duration; -pub mod cached_collector; pub mod cpu; pub mod memory; pub mod disk; diff --git a/agent/src/config/defaults.rs b/agent/src/config/defaults.rs index c417307..77c42ef 100644 --- a/agent/src/config/defaults.rs +++ b/agent/src/config/defaults.rs @@ -46,9 +46,6 @@ pub const DEFAULT_SMART_WEAR_CRITICAL: f32 = 90.0; // Backup configuration pub const DEFAULT_BACKUP_MAX_AGE_HOURS: u64 = 48; -// Cache configuration -pub const DEFAULT_CACHE_TTL_SECONDS: u64 = 30; -pub const DEFAULT_CACHE_MAX_ENTRIES: usize = 10000; // Notification configuration (from legacy) pub const DEFAULT_SMTP_HOST: &str = "localhost"; diff --git a/dashboard/src/app.rs b/dashboard/src/app.rs index 0b2f3d2..85ede64 100644 --- a/dashboard/src/app.rs +++ b/dashboard/src/app.rs @@ -18,7 +18,6 @@ use crate::metrics::MetricStore; use crate::ui::TuiApp; pub struct Dashboard { - config: DashboardConfig, zmq_consumer: ZmqConsumer, zmq_command_sender: ZmqCommandSender, metric_store: MetricStore, @@ -111,7 +110,6 @@ impl Dashboard { info!("Dashboard initialization complete"); Ok(Self { - config, zmq_consumer, zmq_command_sender, metric_store, diff --git a/dashboard/src/metrics/mod.rs b/dashboard/src/metrics/mod.rs index 3deb9cd..8749c83 100644 --- a/dashboard/src/metrics/mod.rs +++ b/dashboard/src/metrics/mod.rs @@ -1,136 +1,14 @@ -use cm_dashboard_shared::{Metric, Status}; use std::time::Instant; pub mod store; -pub mod subscription; pub use store::MetricStore; -/// Widget types that can subscribe to metrics -#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] -pub enum WidgetType { - Cpu, - Memory, - Storage, - Services, - Backup, - Hosts, - Alerts, -} /// Historical metric data point #[derive(Debug, Clone)] pub struct MetricDataPoint { - pub metric: Metric, pub received_at: Instant, } -/// Metric filtering and selection utilities -pub mod filter { - use super::*; - - /// Filter metrics by widget type subscription - pub fn filter_metrics_for_widget<'a>( - metrics: &'a [Metric], - subscriptions: &[String], - ) -> Vec<&'a Metric> { - metrics - .iter() - .filter(|metric| subscriptions.contains(&metric.name)) - .collect() - } - - /// Get metrics by pattern matching - pub fn filter_metrics_by_pattern<'a>( - metrics: &'a [Metric], - pattern: &str, - ) -> Vec<&'a Metric> { - if pattern.is_empty() { - return metrics.iter().collect(); - } - - metrics - .iter() - .filter(|metric| metric.name.contains(pattern)) - .collect() - } - - /// Aggregate status from multiple metrics - pub fn aggregate_widget_status(metrics: &[&Metric]) -> Status { - if metrics.is_empty() { - return Status::Unknown; - } - - let statuses: Vec = metrics.iter().map(|m| m.status).collect(); - Status::aggregate(&statuses) - } -} -/// Widget metric subscription definitions -pub mod subscriptions { - /// CPU widget metric subscriptions - pub const CPU_WIDGET_METRICS: &[&str] = &[ - "cpu_load_1min", - "cpu_load_5min", - "cpu_load_15min", - "cpu_temperature_celsius", - "cpu_frequency_mhz", - ]; - - /// Memory widget metric subscriptions - pub const MEMORY_WIDGET_METRICS: &[&str] = &[ - "memory_usage_percent", - "memory_total_gb", - "memory_used_gb", - "memory_available_gb", - "memory_swap_total_gb", - "memory_swap_used_gb", - "disk_tmp_size_mb", - "disk_tmp_total_mb", - "disk_tmp_usage_percent", - ]; - - /// Storage widget metric subscriptions - pub const STORAGE_WIDGET_METRICS: &[&str] = &[ - "disk_nvme0_temperature_celsius", - "disk_nvme0_wear_percent", - "disk_nvme0_spare_percent", - "disk_nvme0_hours", - "disk_nvme0_capacity_gb", - "disk_nvme0_usage_gb", - "disk_nvme0_usage_percent", - ]; - - /// Services widget metric subscriptions - /// Note: Individual service metrics are dynamically discovered - /// Pattern: "service_{name}_status" and "service_{name}_memory_mb" - pub const SERVICES_WIDGET_METRICS: &[&str] = &[ - // Individual service metrics will be matched by pattern in the widget - // e.g., "service_sshd_status", "service_nginx_status", etc. - ]; - - /// Backup widget metric subscriptions - pub const BACKUP_WIDGET_METRICS: &[&str] = &[ - "backup_overall_status", - "backup_duration_seconds", - "backup_last_run_timestamp", - "backup_total_services", - "backup_total_repo_size_gb", - "backup_services_completed_count", - "backup_services_failed_count", - "backup_services_disabled_count", - ]; - - /// Get all metric subscriptions for a widget type - pub fn get_widget_subscriptions(widget_type: super::WidgetType) -> &'static [&'static str] { - match widget_type { - super::WidgetType::Cpu => CPU_WIDGET_METRICS, - super::WidgetType::Memory => MEMORY_WIDGET_METRICS, - super::WidgetType::Storage => STORAGE_WIDGET_METRICS, - super::WidgetType::Services => SERVICES_WIDGET_METRICS, - super::WidgetType::Backup => BACKUP_WIDGET_METRICS, - super::WidgetType::Hosts => &[], // Hosts widget doesn't subscribe to specific metrics - super::WidgetType::Alerts => &[], // Alerts widget aggregates from all metrics - } - } -} \ No newline at end of file diff --git a/dashboard/src/metrics/store.rs b/dashboard/src/metrics/store.rs index a248403..4769bcc 100644 --- a/dashboard/src/metrics/store.rs +++ b/dashboard/src/metrics/store.rs @@ -1,9 +1,9 @@ -use cm_dashboard_shared::{Metric, Status}; +use cm_dashboard_shared::Metric; use std::collections::HashMap; use std::time::{Duration, Instant}; use tracing::{debug, info, warn}; -use super::{MetricDataPoint, WidgetType, subscriptions}; +use super::MetricDataPoint; /// Central metric storage for the dashboard pub struct MetricStore { @@ -54,7 +54,6 @@ impl MetricStore { // Add to history host_history.push(MetricDataPoint { - metric, received_at: now, }); } @@ -80,6 +79,7 @@ impl MetricStore { } /// Get all current metrics for a host + #[allow(dead_code)] pub fn get_host_metrics(&self, hostname: &str) -> Option<&HashMap> { self.current_metrics.get(hostname) } @@ -93,36 +93,7 @@ impl MetricStore { } } - /// Get metrics for a specific widget type - pub fn get_metrics_for_widget(&self, hostname: &str, widget_type: WidgetType) -> Vec<&Metric> { - let subscriptions = subscriptions::get_widget_subscriptions(widget_type); - - if let Some(host_metrics) = self.get_host_metrics(hostname) { - subscriptions - .iter() - .filter_map(|&metric_name| host_metrics.get(metric_name)) - .collect() - } else { - Vec::new() - } - } - /// Get aggregated status for a widget - pub fn get_widget_status(&self, hostname: &str, widget_type: WidgetType) -> Status { - let metrics = self.get_metrics_for_widget(hostname, widget_type); - - if metrics.is_empty() { - Status::Unknown - } else { - let statuses: Vec = metrics.iter().map(|m| m.status).collect(); - Status::aggregate(&statuses) - } - } - - /// Get list of all hosts with metrics - pub fn get_hosts(&self) -> Vec { - self.current_metrics.keys().cloned().collect() - } /// Get connected hosts (hosts with recent updates) pub fn get_connected_hosts(&self, timeout: Duration) -> Vec { @@ -140,45 +111,7 @@ impl MetricStore { .collect() } - /// Get last update timestamp for a host - pub fn get_last_update(&self, hostname: &str) -> Option { - self.last_update.get(hostname).copied() - } - /// Check if host is considered connected - pub fn is_host_connected(&self, hostname: &str, timeout: Duration) -> bool { - if let Some(&last_update) = self.last_update.get(hostname) { - Instant::now().duration_since(last_update) <= timeout - } else { - false - } - } - - /// Get metric value as specific type (helper function) - pub fn get_metric_value_f32(&self, hostname: &str, metric_name: &str) -> Option { - self.get_metric(hostname, metric_name)? - .value - .as_f32() - } - - /// Get metric value as string (helper function) - pub fn get_metric_value_string(&self, hostname: &str, metric_name: &str) -> Option { - Some(self.get_metric(hostname, metric_name)? - .value - .as_string()) - } - - /// Get historical data for a metric - pub fn get_metric_history(&self, hostname: &str, metric_name: &str) -> Vec<&MetricDataPoint> { - if let Some(history) = self.historical_metrics.get(hostname) { - history - .iter() - .filter(|dp| dp.metric.name == metric_name) - .collect() - } else { - Vec::new() - } - } /// Cleanup old data and enforce limits fn cleanup_host_data(&mut self, hostname: &str) { @@ -199,32 +132,4 @@ impl MetricStore { } } - /// Get storage statistics - pub fn get_stats(&self) -> MetricStoreStats { - let total_current_metrics: usize = self.current_metrics - .values() - .map(|host_metrics| host_metrics.len()) - .sum(); - - let total_historical_metrics: usize = self.historical_metrics - .values() - .map(|history| history.len()) - .sum(); - - MetricStoreStats { - total_hosts: self.current_metrics.len(), - total_current_metrics, - total_historical_metrics, - connected_hosts: self.get_connected_hosts(Duration::from_secs(30)).len(), - } - } -} - -/// Metric store statistics -#[derive(Debug, Clone)] -pub struct MetricStoreStats { - pub total_hosts: usize, - pub total_current_metrics: usize, - pub total_historical_metrics: usize, - pub connected_hosts: usize, } \ No newline at end of file diff --git a/dashboard/src/metrics/subscription.rs b/dashboard/src/metrics/subscription.rs deleted file mode 100644 index 25fee08..0000000 --- a/dashboard/src/metrics/subscription.rs +++ /dev/null @@ -1,160 +0,0 @@ -use std::collections::{HashMap, HashSet}; -use tracing::{debug, info}; - -use super::{WidgetType, subscriptions}; - -/// Manages metric subscriptions for widgets -pub struct SubscriptionManager { - /// Widget subscriptions: widget_type -> metric_names - widget_subscriptions: HashMap>, - /// All subscribed metric names (for efficient filtering) - all_subscribed_metrics: HashSet, - /// Active hosts - active_hosts: HashSet, -} - -impl SubscriptionManager { - pub fn new() -> Self { - let mut manager = Self { - widget_subscriptions: HashMap::new(), - all_subscribed_metrics: HashSet::new(), - active_hosts: HashSet::new(), - }; - - // Initialize default subscriptions - manager.initialize_default_subscriptions(); - - manager - } - - /// Initialize default widget subscriptions - fn initialize_default_subscriptions(&mut self) { - // Subscribe CPU widget to CPU metrics - self.subscribe_widget( - WidgetType::Cpu, - subscriptions::CPU_WIDGET_METRICS.iter().map(|&s| s.to_string()).collect() - ); - - // Subscribe Memory widget to memory metrics - self.subscribe_widget( - WidgetType::Memory, - subscriptions::MEMORY_WIDGET_METRICS.iter().map(|&s| s.to_string()).collect() - ); - - - info!("Initialized default widget subscriptions for {} widgets", - self.widget_subscriptions.len()); - } - - /// Subscribe a widget to specific metrics - pub fn subscribe_widget(&mut self, widget_type: WidgetType, metric_names: Vec) { - debug!("Subscribing {:?} widget to {} metrics", widget_type, metric_names.len()); - - // Update widget subscriptions - self.widget_subscriptions.insert(widget_type, metric_names.clone()); - - // Update global subscription set - for metric_name in metric_names { - self.all_subscribed_metrics.insert(metric_name); - } - - debug!("Total subscribed metrics: {}", self.all_subscribed_metrics.len()); - } - - /// Get metrics subscribed by a specific widget - pub fn get_widget_subscriptions(&self, widget_type: WidgetType) -> Vec { - self.widget_subscriptions - .get(&widget_type) - .cloned() - .unwrap_or_default() - } - - /// Get all subscribed metric names - pub fn get_all_subscribed_metrics(&self) -> Vec { - self.all_subscribed_metrics.iter().cloned().collect() - } - - /// Check if a metric is subscribed by any widget - pub fn is_metric_subscribed(&self, metric_name: &str) -> bool { - self.all_subscribed_metrics.contains(metric_name) - } - - /// Add a host to active hosts list - pub fn add_host(&mut self, hostname: String) { - if self.active_hosts.insert(hostname.clone()) { - info!("Added host to subscription manager: {}", hostname); - } - } - - /// Remove a host from active hosts list - pub fn remove_host(&mut self, hostname: &str) { - if self.active_hosts.remove(hostname) { - info!("Removed host from subscription manager: {}", hostname); - } - } - - /// Get list of active hosts - pub fn get_active_hosts(&self) -> Vec { - self.active_hosts.iter().cloned().collect() - } - - /// Get subscription statistics - pub fn get_stats(&self) -> SubscriptionStats { - SubscriptionStats { - total_widgets_subscribed: self.widget_subscriptions.len(), - total_metric_subscriptions: self.all_subscribed_metrics.len(), - active_hosts: self.active_hosts.len(), - } - } - - /// Update widget subscription dynamically - pub fn update_widget_subscription(&mut self, widget_type: WidgetType, metric_names: Vec) { - // Remove old subscriptions from global set - if let Some(old_subscriptions) = self.widget_subscriptions.get(&widget_type) { - for old_metric in old_subscriptions { - // Only remove if no other widget subscribes to it - let still_subscribed = self.widget_subscriptions - .iter() - .filter(|(&wt, _)| wt != widget_type) - .any(|(_, metrics)| metrics.contains(old_metric)); - - if !still_subscribed { - self.all_subscribed_metrics.remove(old_metric); - } - } - } - - // Add new subscriptions - self.subscribe_widget(widget_type, metric_names); - - debug!("Updated subscription for {:?} widget", widget_type); - } - - /// Get widgets that subscribe to a specific metric - pub fn get_widgets_for_metric(&self, metric_name: &str) -> Vec { - self.widget_subscriptions - .iter() - .filter_map(|(&widget_type, metrics)| { - if metrics.contains(&metric_name.to_string()) { - Some(widget_type) - } else { - None - } - }) - .collect() - } -} - -impl Default for SubscriptionManager { - fn default() -> Self { - Self::new() - } -} - -/// Subscription manager statistics -#[derive(Debug, Clone)] -pub struct SubscriptionStats { - pub total_widgets_subscribed: usize, - pub total_metric_subscriptions: usize, - pub active_hosts: usize, -} \ No newline at end of file diff --git a/dashboard/src/ui/mod.rs b/dashboard/src/ui/mod.rs index 6ae9273..c802368 100644 --- a/dashboard/src/ui/mod.rs +++ b/dashboard/src/ui/mod.rs @@ -14,7 +14,7 @@ pub mod widgets; pub mod theme; use widgets::{CpuWidget, MemoryWidget, ServicesWidget, BackupWidget, Widget}; -use crate::metrics::{MetricStore, WidgetType}; +use crate::metrics::MetricStore; use cm_dashboard_shared::{Metric, Status}; use theme::{Theme, Layout as ThemeLayout, Typography, Components, StatusIcons}; @@ -82,8 +82,14 @@ impl TuiApp { let all_metrics = metric_store.get_metrics_for_host(&hostname); if !all_metrics.is_empty() { // Get metrics first while hostname is borrowed - let cpu_metrics = metric_store.get_metrics_for_widget(&hostname, WidgetType::Cpu); - let memory_metrics = metric_store.get_metrics_for_widget(&hostname, WidgetType::Memory); + let cpu_metrics: Vec<&Metric> = all_metrics.iter() + .filter(|m| m.name.starts_with("cpu_") || m.name.contains("c_state_") || m.name.starts_with("process_top_")) + .copied() + .collect(); + let memory_metrics: Vec<&Metric> = all_metrics.iter() + .filter(|m| m.name.starts_with("memory_") || m.name.starts_with("disk_tmp_")) + .copied() + .collect(); let service_metrics: Vec<&Metric> = all_metrics.iter() .filter(|m| m.name.starts_with("service_")) .copied() diff --git a/dashboard/src/ui/theme.rs b/dashboard/src/ui/theme.rs index efffb12..edf0f49 100644 --- a/dashboard/src/ui/theme.rs +++ b/dashboard/src/ui/theme.rs @@ -3,6 +3,7 @@ use ratatui::widgets::{Block, Borders}; use cm_dashboard_shared::Status; /// Complete terminal color palette matching your configuration +#[allow(dead_code)] pub struct TerminalColors { // Primary colors pub foreground: Color, @@ -86,6 +87,7 @@ impl Default for TerminalColors { /// Comprehensive theming engine for dashboard consistency pub struct Theme; +#[allow(dead_code)] impl Theme { fn colors() -> &'static TerminalColors { static COLORS: std::sync::OnceLock = std::sync::OnceLock::new(); @@ -243,22 +245,6 @@ impl StatusIcons { } } - /// Get style for status-colored text (icon + text in status color) - pub fn get_status_style(status: Status) -> Style { - let color = match status { - Status::Ok => Theme::success(), // Green - Status::Warning => Theme::warning(), // Yellow - Status::Critical => Theme::error(), // Red - Status::Unknown => Theme::muted_text(), // Gray - }; - Style::default().fg(color).bg(Theme::background()) - } - - /// Format text with status icon (entire line uses status color) - pub fn format_with_status(status: Status, text: &str) -> String { - let icon = Self::get_icon(status); - format!("{} {}", icon, text) - } /// Create spans with status icon colored and text in foreground color pub fn create_status_spans(status: Status, text: &str) -> Vec> { @@ -282,17 +268,11 @@ impl StatusIcons { ] } - /// Get style for secondary text with status icon (icon in status color, text in secondary) - pub fn get_secondary_with_status_style(_status: Status) -> Style { - // For now, use secondary color but we could enhance this later - // The icon color will come from the status color in the formatted text - Style::default().fg(Theme::secondary_text()).bg(Theme::background()) - } } impl Components { /// Standard widget block with title using bright foreground for title - pub fn widget_block(title: &str) -> Block { + pub fn widget_block(title: &str) -> Block<'_> { Block::default() .title(title) .borders(Borders::ALL) @@ -300,40 +280,6 @@ impl Components { .title_style(Style::default().fg(Theme::border_title()).bg(Theme::background())) } - /// Status bar style - pub fn status_bar() -> Style { - Style::default() - .fg(Theme::muted_text()) - .bg(Theme::background()) - } - - /// CPU usage styling based on percentage - pub fn cpu_usage_style(percentage: u16) -> Style { - Style::default() - .fg(Theme::cpu_color(percentage)) - .bg(Theme::background()) - } - - /// Memory usage styling based on percentage - pub fn memory_usage_style(percentage: u16) -> Style { - Style::default() - .fg(Theme::memory_color(percentage)) - .bg(Theme::background()) - } - - /// Service status styling - pub fn service_status_style(status: Status) -> Style { - Style::default() - .fg(Theme::status_color(status)) - .bg(Theme::background()) - } - - /// Backup item styling - pub fn backup_item_style(status: Status) -> Style { - Style::default() - .fg(Theme::status_color(status)) - .bg(Theme::background()) - } } impl Typography { @@ -367,10 +313,4 @@ impl Typography { .add_modifier(Modifier::BOLD) } - /// Status text with dynamic colors - pub fn status(status: Status) -> Style { - Style::default() - .fg(Theme::status_color(status)) - .bg(Theme::background()) - } } diff --git a/dashboard/src/ui/widgets/backup.rs b/dashboard/src/ui/widgets/backup.rs index 91a8149..c566ec9 100644 --- a/dashboard/src/ui/widgets/backup.rs +++ b/dashboard/src/ui/widgets/backup.rs @@ -107,19 +107,6 @@ impl BackupWidget { } } - /// Format service status counts - fn format_service_counts(&self) -> String { - let completed = self.services_completed_count.unwrap_or(0); - let failed = self.services_failed_count.unwrap_or(0); - let disabled = self.services_disabled_count.unwrap_or(0); - let total = self.total_services.unwrap_or(0); - - if failed > 0 { - format!("{}✓ {}✗ {}◯ ({})", completed, failed, disabled, total) - } else { - format!("{}✓ {}◯ ({})", completed, disabled, total) - } - } /// Format disk usage in format "usedGB/totalGB" fn format_repo_size(&self) -> String { @@ -160,30 +147,7 @@ impl BackupWidget { } } - /// Get status indicator symbol - fn get_status_symbol(&self) -> &str { - match self.overall_status { - Status::Ok => "●", - Status::Warning => "◐", - Status::Critical => "◯", - Status::Unknown => "?", - } - } - /// Format size in GB to appropriate unit (kB/MB/GB) - fn format_size_gb(size_gb: f32) -> String { - if size_gb >= 1.0 { - format!("{:.1}GB", size_gb) - } else if size_gb >= 0.001 { - let size_mb = size_gb * 1024.0; - format!("{:.1}MB", size_mb) - } else if size_gb >= 0.000001 { - let size_kb = size_gb * 1024.0 * 1024.0; - format!("{:.0}kB", size_kb) - } else { - format!("{:.0}B", size_gb * 1024.0 * 1024.0 * 1024.0) - } - } /// Format product name display fn format_product_name(&self) -> String { diff --git a/dashboard/src/ui/widgets/services.rs b/dashboard/src/ui/widgets/services.rs index 636680e..56d00e6 100644 --- a/dashboard/src/ui/widgets/services.rs +++ b/dashboard/src/ui/widgets/services.rs @@ -118,36 +118,6 @@ impl ServicesWidget { disk_str) } - /// Format sub-service line (indented, no memory/disk columns) - returns text without icon for span formatting - fn format_sub_service_line(&self, name: &str, info: &ServiceInfo) -> String { - // Truncate long sub-service names to fit layout (accounting for indentation) - let short_name = if name.len() > 18 { - format!("{}...", &name[..15]) - } else { - name.to_string() - }; - - // Sub-services show latency if available, otherwise status - let status_str = if let Some(latency) = info.latency_ms { - if latency < 0.0 { - "timeout".to_string() - } else { - format!("{:.0}ms", latency) - } - } else { - match info.widget_status { - Status::Ok => "active".to_string(), - Status::Warning => "inactive".to_string(), - Status::Critical => "failed".to_string(), - Status::Unknown => "unknown".to_string(), - } - }; - - // Indent sub-services with " ├─ " prefix (no memory/disk columns) - format!(" ├─ {:<20} {:<10}", - short_name, - status_str) - } /// Create spans for sub-service with icon next to name fn create_sub_service_spans(&self, name: &str, info: &ServiceInfo) -> Vec> {