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
This commit is contained in:
2025-10-16 23:55:05 +02:00
parent 7a664ef0fb
commit 8a36472a3d
81 changed files with 7702 additions and 9608 deletions

292
agent/src/config/mod.rs Normal file
View File

@@ -0,0 +1,292 @@
use anyhow::Result;
use cm_dashboard_shared::CacheConfig;
use serde::{Deserialize, Serialize};
use std::path::Path;
pub mod defaults;
pub mod loader;
pub mod validation;
use defaults::*;
/// Main agent configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentConfig {
pub zmq: ZmqConfig,
pub collectors: CollectorConfig,
pub cache: CacheConfig,
pub notifications: NotificationConfig,
pub collection_interval_seconds: u64,
}
/// ZMQ communication configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ZmqConfig {
pub publisher_port: u16,
pub command_port: u16,
pub bind_address: String,
pub timeout_ms: u64,
pub heartbeat_interval_ms: u64,
}
/// Collector configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CollectorConfig {
pub cpu: CpuConfig,
pub memory: MemoryConfig,
pub disk: DiskConfig,
pub processes: ProcessConfig,
pub systemd: SystemdConfig,
pub smart: SmartConfig,
pub backup: BackupConfig,
pub network: NetworkConfig,
}
/// CPU collector configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CpuConfig {
pub enabled: bool,
pub interval_seconds: u64,
pub load_warning_threshold: f32,
pub load_critical_threshold: f32,
pub temperature_warning_threshold: f32,
pub temperature_critical_threshold: f32,
}
/// Memory collector configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryConfig {
pub enabled: bool,
pub interval_seconds: u64,
pub usage_warning_percent: f32,
pub usage_critical_percent: f32,
}
/// Disk collector configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiskConfig {
pub enabled: bool,
pub interval_seconds: u64,
pub usage_warning_percent: f32,
pub usage_critical_percent: f32,
pub auto_discover: bool,
pub devices: Vec<String>,
}
/// Process collector configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProcessConfig {
pub enabled: bool,
pub interval_seconds: u64,
pub top_processes_count: usize,
}
/// Systemd services collector configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemdConfig {
pub enabled: bool,
pub interval_seconds: u64,
pub auto_discover: bool,
pub services: Vec<String>,
pub memory_warning_mb: f32,
pub memory_critical_mb: f32,
}
/// SMART collector configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SmartConfig {
pub enabled: bool,
pub interval_seconds: u64,
pub temperature_warning_celsius: f32,
pub temperature_critical_celsius: f32,
pub wear_warning_percent: f32,
pub wear_critical_percent: f32,
}
/// Backup collector configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BackupConfig {
pub enabled: bool,
pub interval_seconds: u64,
pub backup_paths: Vec<String>,
pub max_age_hours: u64,
}
/// Network collector configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkConfig {
pub enabled: bool,
pub interval_seconds: u64,
pub interfaces: Vec<String>,
pub auto_discover: bool,
}
/// Notification configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NotificationConfig {
pub enabled: bool,
pub smtp_host: String,
pub smtp_port: u16,
pub from_email: String,
pub to_email: String,
pub rate_limit_minutes: u64,
}
impl AgentConfig {
pub fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
loader::load_config(path)
}
pub fn validate(&self) -> Result<()> {
validation::validate_config(self)
}
}
impl Default for AgentConfig {
fn default() -> Self {
Self {
zmq: ZmqConfig::default(),
collectors: CollectorConfig::default(),
cache: CacheConfig::default(),
notifications: NotificationConfig::default(),
collection_interval_seconds: DEFAULT_COLLECTION_INTERVAL_SECONDS,
}
}
}
impl Default for ZmqConfig {
fn default() -> Self {
Self {
publisher_port: DEFAULT_ZMQ_PUBLISHER_PORT,
command_port: DEFAULT_ZMQ_COMMAND_PORT,
bind_address: DEFAULT_ZMQ_BIND_ADDRESS.to_string(),
timeout_ms: DEFAULT_ZMQ_TIMEOUT_MS,
heartbeat_interval_ms: DEFAULT_ZMQ_HEARTBEAT_INTERVAL_MS,
}
}
}
impl Default for CollectorConfig {
fn default() -> Self {
Self {
cpu: CpuConfig::default(),
memory: MemoryConfig::default(),
disk: DiskConfig::default(),
processes: ProcessConfig::default(),
systemd: SystemdConfig::default(),
smart: SmartConfig::default(),
backup: BackupConfig::default(),
network: NetworkConfig::default(),
}
}
}
impl Default for CpuConfig {
fn default() -> Self {
Self {
enabled: true,
interval_seconds: DEFAULT_CPU_INTERVAL_SECONDS,
load_warning_threshold: DEFAULT_CPU_LOAD_WARNING,
load_critical_threshold: DEFAULT_CPU_LOAD_CRITICAL,
temperature_warning_threshold: DEFAULT_CPU_TEMP_WARNING,
temperature_critical_threshold: DEFAULT_CPU_TEMP_CRITICAL,
}
}
}
impl Default for MemoryConfig {
fn default() -> Self {
Self {
enabled: true,
interval_seconds: DEFAULT_MEMORY_INTERVAL_SECONDS,
usage_warning_percent: DEFAULT_MEMORY_WARNING_PERCENT,
usage_critical_percent: DEFAULT_MEMORY_CRITICAL_PERCENT,
}
}
}
impl Default for DiskConfig {
fn default() -> Self {
Self {
enabled: true,
interval_seconds: DEFAULT_DISK_INTERVAL_SECONDS,
usage_warning_percent: DEFAULT_DISK_WARNING_PERCENT,
usage_critical_percent: DEFAULT_DISK_CRITICAL_PERCENT,
auto_discover: true,
devices: Vec::new(),
}
}
}
impl Default for ProcessConfig {
fn default() -> Self {
Self {
enabled: true,
interval_seconds: DEFAULT_PROCESS_INTERVAL_SECONDS,
top_processes_count: DEFAULT_TOP_PROCESSES_COUNT,
}
}
}
impl Default for SystemdConfig {
fn default() -> Self {
Self {
enabled: true,
interval_seconds: DEFAULT_SYSTEMD_INTERVAL_SECONDS,
auto_discover: true,
services: Vec::new(),
memory_warning_mb: DEFAULT_SERVICE_MEMORY_WARNING_MB,
memory_critical_mb: DEFAULT_SERVICE_MEMORY_CRITICAL_MB,
}
}
}
impl Default for SmartConfig {
fn default() -> Self {
Self {
enabled: true,
interval_seconds: DEFAULT_SMART_INTERVAL_SECONDS,
temperature_warning_celsius: DEFAULT_SMART_TEMP_WARNING,
temperature_critical_celsius: DEFAULT_SMART_TEMP_CRITICAL,
wear_warning_percent: DEFAULT_SMART_WEAR_WARNING,
wear_critical_percent: DEFAULT_SMART_WEAR_CRITICAL,
}
}
}
impl Default for BackupConfig {
fn default() -> Self {
Self {
enabled: true,
interval_seconds: DEFAULT_BACKUP_INTERVAL_SECONDS,
backup_paths: Vec::new(),
max_age_hours: DEFAULT_BACKUP_MAX_AGE_HOURS,
}
}
}
impl Default for NetworkConfig {
fn default() -> Self {
Self {
enabled: true,
interval_seconds: DEFAULT_NETWORK_INTERVAL_SECONDS,
interfaces: Vec::new(),
auto_discover: true,
}
}
}
impl Default for NotificationConfig {
fn default() -> Self {
Self {
enabled: true,
smtp_host: DEFAULT_SMTP_HOST.to_string(),
smtp_port: DEFAULT_SMTP_PORT,
from_email: DEFAULT_FROM_EMAIL.to_string(),
to_email: DEFAULT_TO_EMAIL.to_string(),
rate_limit_minutes: DEFAULT_NOTIFICATION_RATE_LIMIT_MINUTES,
}
}
}