Replace df-based auto-discovery with UUID-based detection using NixOS hardware configuration data. Each host now has predefined filesystem configurations with predictable metric names. - Add FilesystemConfig struct with UUID, mount point, and filesystem type - Remove auto_discover and devices fields from DiskConfig - Add host-specific UUID defaults for cmbox, srv01, srv02, simonbox, steambox - Remove legacy get_mounted_disks() df-based detection method - Update DiskCollector to use UUID resolution via /dev/disk/by-uuid/ - Generate predictable metric names: disk_root_*, disk_boot_*, etc. - Maintain fallback for labbox/wslbox (no UUIDs configured yet) Provides consistent metric names across reboots and reliable detection aligned with NixOS deployments without dependency on mount order.
418 lines
13 KiB
Rust
418 lines
13 KiB
Rust
use anyhow::Result;
|
|
use cm_dashboard_shared::CacheConfig;
|
|
use gethostname::gethostname;
|
|
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 filesystems: Vec<FilesystemConfig>,
|
|
}
|
|
|
|
/// Filesystem configuration entry
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct FilesystemConfig {
|
|
pub name: String, // Human-readable name (e.g., "root", "boot", "home")
|
|
pub uuid: String, // UUID for /dev/disk/by-uuid/ resolution
|
|
pub mount_point: String, // Expected mount point (e.g., "/", "/boot")
|
|
pub fs_type: String, // Filesystem type (e.g., "ext4", "vfat")
|
|
pub monitor: bool, // Whether to monitor this filesystem
|
|
}
|
|
|
|
/// 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 {
|
|
let hostname = gethostname::gethostname().to_string_lossy().to_string();
|
|
let filesystems = get_default_filesystems_for_host(&hostname);
|
|
|
|
Self {
|
|
enabled: true,
|
|
interval_seconds: DEFAULT_DISK_INTERVAL_SECONDS,
|
|
usage_warning_percent: DEFAULT_DISK_WARNING_PERCENT,
|
|
usage_critical_percent: DEFAULT_DISK_CRITICAL_PERCENT,
|
|
filesystems,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Get default filesystem configurations for known CMTEC hosts
|
|
fn get_default_filesystems_for_host(hostname: &str) -> Vec<FilesystemConfig> {
|
|
match hostname {
|
|
"cmbox" => vec![
|
|
FilesystemConfig {
|
|
name: "root".to_string(),
|
|
uuid: "4cade5ce-85a5-4a03-83c8-dfd1d3888d79".to_string(),
|
|
mount_point: "/".to_string(),
|
|
fs_type: "ext4".to_string(),
|
|
monitor: true,
|
|
},
|
|
FilesystemConfig {
|
|
name: "boot".to_string(),
|
|
uuid: "AB4D-62EC".to_string(),
|
|
mount_point: "/boot".to_string(),
|
|
fs_type: "vfat".to_string(),
|
|
monitor: true,
|
|
},
|
|
],
|
|
"srv02" => vec![
|
|
FilesystemConfig {
|
|
name: "root".to_string(),
|
|
uuid: "5a880608-c79f-458f-a031-30206aa27ca7".to_string(),
|
|
mount_point: "/".to_string(),
|
|
fs_type: "ext4".to_string(),
|
|
monitor: true,
|
|
},
|
|
FilesystemConfig {
|
|
name: "boot".to_string(),
|
|
uuid: "6B2E-2AD9".to_string(),
|
|
mount_point: "/boot".to_string(),
|
|
fs_type: "vfat".to_string(),
|
|
monitor: true,
|
|
},
|
|
],
|
|
"simonbox" => vec![
|
|
FilesystemConfig {
|
|
name: "root".to_string(),
|
|
uuid: "b74284a9-2899-4f71-bdb0-fd07dc4baab3".to_string(),
|
|
mount_point: "/".to_string(),
|
|
fs_type: "ext4".to_string(),
|
|
monitor: true,
|
|
},
|
|
FilesystemConfig {
|
|
name: "boot".to_string(),
|
|
uuid: "F6A3-AD2B".to_string(),
|
|
mount_point: "/boot".to_string(),
|
|
fs_type: "vfat".to_string(),
|
|
monitor: true,
|
|
},
|
|
FilesystemConfig {
|
|
name: "steampool_1".to_string(),
|
|
uuid: "09300cb7-0938-4dba-8a42-7a7aaf60db51".to_string(),
|
|
mount_point: "/steampool_1".to_string(),
|
|
fs_type: "ext4".to_string(),
|
|
monitor: true,
|
|
},
|
|
FilesystemConfig {
|
|
name: "steampool_2".to_string(),
|
|
uuid: "a2d61a41-3f2a-4760-b62e-5eb8caf50d1a".to_string(),
|
|
mount_point: "/steampool_2".to_string(),
|
|
fs_type: "ext4".to_string(),
|
|
monitor: true,
|
|
},
|
|
],
|
|
"steambox" => vec![
|
|
FilesystemConfig {
|
|
name: "root".to_string(),
|
|
uuid: "4514ca9f-2d0a-40df-b14b-e342f39c3e6a".to_string(),
|
|
mount_point: "/".to_string(),
|
|
fs_type: "ext4".to_string(),
|
|
monitor: true,
|
|
},
|
|
FilesystemConfig {
|
|
name: "boot".to_string(),
|
|
uuid: "8FD2-1B13".to_string(),
|
|
mount_point: "/boot".to_string(),
|
|
fs_type: "vfat".to_string(),
|
|
monitor: true,
|
|
},
|
|
FilesystemConfig {
|
|
name: "steampool".to_string(),
|
|
uuid: "0ebe8abb-bbe7-4224-947b-86bf38981f60".to_string(),
|
|
mount_point: "/mnt/steampool".to_string(),
|
|
fs_type: "ext4".to_string(),
|
|
monitor: true,
|
|
},
|
|
],
|
|
"srv01" => vec![
|
|
FilesystemConfig {
|
|
name: "root".to_string(),
|
|
uuid: "cd98df34-03a3-4d68-8338-d90d2920f9f8".to_string(),
|
|
mount_point: "/".to_string(),
|
|
fs_type: "ext4".to_string(),
|
|
monitor: true,
|
|
},
|
|
FilesystemConfig {
|
|
name: "boot".to_string(),
|
|
uuid: "13E1-4DDE".to_string(),
|
|
mount_point: "/boot".to_string(),
|
|
fs_type: "vfat".to_string(),
|
|
monitor: true,
|
|
},
|
|
],
|
|
// labbox and wslbox have no UUIDs configured yet
|
|
"labbox" | "wslbox" => {
|
|
Vec::new()
|
|
},
|
|
_ => {
|
|
// Unknown hosts use auto-discovery
|
|
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,
|
|
}
|
|
}
|
|
}
|