Christoffer Martinsson 8a36472a3d Implement real-time process monitoring and fix UI hardcoded data
This commit addresses several key issues identified during development:

Major Changes:
- Replace hardcoded top CPU/RAM process display with real system data
- Add intelligent process monitoring to CpuCollector using ps command
- Fix disk metrics permission issues in systemd collector
- Optimize service collection to focus on status, memory, and disk only
- Update dashboard widgets to display live process information

Process Monitoring Implementation:
- Added collect_top_cpu_process() and collect_top_ram_process() methods
- Implemented ps-based monitoring with accurate CPU percentages
- Added filtering to prevent self-monitoring artifacts (ps commands)
- Enhanced error handling and validation for process data
- Dashboard now shows realistic values like "claude (PID 2974) 11.0%"

Service Collection Optimization:
- Removed CPU monitoring from systemd collector for efficiency
- Enhanced service directory permission error logging
- Simplified services widget to show essential metrics only
- Fixed service-to-directory mapping accuracy

UI and Dashboard Improvements:
- Reorganized dashboard layout with btop-inspired multi-panel design
- Updated system panel to include real top CPU/RAM process display
- Enhanced widget formatting and data presentation
- Removed placeholder/hardcoded data throughout the interface

Technical Details:
- Updated agent/src/collectors/cpu.rs with process monitoring
- Modified dashboard/src/ui/mod.rs for real-time process display
- Enhanced systemd collector error handling and disk metrics
- Updated CLAUDE.md documentation with implementation details
2025-10-16 23:55:05 +02:00

173 lines
4.5 KiB
Rust

use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::path::Path;
/// Main dashboard configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DashboardConfig {
pub zmq: ZmqConfig,
pub ui: UiConfig,
pub hosts: HostsConfig,
pub metrics: MetricsConfig,
pub widgets: WidgetsConfig,
}
/// ZMQ consumer configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ZmqConfig {
pub subscriber_ports: Vec<u16>,
pub connection_timeout_ms: u64,
pub reconnect_interval_ms: u64,
}
/// UI configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiConfig {
pub refresh_rate_ms: u64,
pub theme: String,
pub preserve_layout: bool,
}
/// Hosts configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HostsConfig {
pub auto_discovery: bool,
pub predefined_hosts: Vec<String>,
pub default_host: Option<String>,
}
/// Metrics configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetricsConfig {
pub history_retention_hours: u64,
pub max_metrics_per_host: usize,
}
/// Widget configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WidgetsConfig {
pub cpu: WidgetConfig,
pub memory: WidgetConfig,
pub storage: WidgetConfig,
pub services: WidgetConfig,
pub backup: WidgetConfig,
}
/// Individual widget configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WidgetConfig {
pub enabled: bool,
pub metrics: Vec<String>,
}
impl DashboardConfig {
pub fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
let path = path.as_ref();
let content = std::fs::read_to_string(path)?;
let config: DashboardConfig = toml::from_str(&content)?;
Ok(config)
}
}
impl Default for DashboardConfig {
fn default() -> Self {
Self {
zmq: ZmqConfig::default(),
ui: UiConfig::default(),
hosts: HostsConfig::default(),
metrics: MetricsConfig::default(),
widgets: WidgetsConfig::default(),
}
}
}
impl Default for ZmqConfig {
fn default() -> Self {
Self {
subscriber_ports: vec![6130],
connection_timeout_ms: 15000,
reconnect_interval_ms: 5000,
}
}
}
impl Default for UiConfig {
fn default() -> Self {
Self {
refresh_rate_ms: 100,
theme: "default".to_string(),
preserve_layout: true,
}
}
}
impl Default for HostsConfig {
fn default() -> Self {
Self {
auto_discovery: true,
predefined_hosts: vec![
"cmbox".to_string(),
"labbox".to_string(),
"simonbox".to_string(),
"steambox".to_string(),
"srv01".to_string(),
],
default_host: Some("cmbox".to_string()),
}
}
}
impl Default for MetricsConfig {
fn default() -> Self {
Self {
history_retention_hours: 24,
max_metrics_per_host: 10000,
}
}
}
impl Default for WidgetsConfig {
fn default() -> Self {
Self {
cpu: WidgetConfig {
enabled: true,
metrics: vec![
"cpu_load_1min".to_string(),
"cpu_load_5min".to_string(),
"cpu_load_15min".to_string(),
"cpu_temperature_celsius".to_string(),
],
},
memory: WidgetConfig {
enabled: true,
metrics: vec![
"memory_usage_percent".to_string(),
"memory_total_gb".to_string(),
"memory_available_gb".to_string(),
],
},
storage: WidgetConfig {
enabled: true,
metrics: vec![
"disk_nvme0_temperature_celsius".to_string(),
"disk_nvme0_wear_percent".to_string(),
"disk_nvme0_usage_percent".to_string(),
],
},
services: WidgetConfig {
enabled: true,
metrics: vec![
"service_ssh_status".to_string(),
"service_ssh_memory_mb".to_string(),
],
},
backup: WidgetConfig {
enabled: true,
metrics: vec![
"backup_status".to_string(),
"backup_last_run_timestamp".to_string(),
],
},
}
}
}