Files
cm-dashboard/dashboard/src/metrics/store.rs
Christoffer Martinsson 3d2b37b26c Remove hardcoded defaults and migrate dashboard config to NixOS
- Remove all unused configuration options from dashboard config module
- Eliminate hardcoded defaults - dashboard now requires config file like agent
- Keep only actually used config: zmq.subscriber_ports and hosts.predefined_hosts
- Remove unused get_host_metrics function from metric store
- Clean up missing module imports (hosts, utils)
- Make dashboard fail fast if no configuration provided
- Align dashboard config approach with agent configuration pattern
2025-10-21 21:54:23 +02:00

128 lines
4.1 KiB
Rust

use cm_dashboard_shared::Metric;
use std::collections::HashMap;
use std::time::{Duration, Instant};
use tracing::{debug, info, warn};
use super::MetricDataPoint;
/// Central metric storage for the dashboard
pub struct MetricStore {
/// Current metrics: hostname -> metric_name -> metric
current_metrics: HashMap<String, HashMap<String, Metric>>,
/// Historical metrics for trending
historical_metrics: HashMap<String, Vec<MetricDataPoint>>,
/// Last update timestamp per host
last_update: HashMap<String, Instant>,
/// Configuration
max_metrics_per_host: usize,
history_retention: Duration,
}
impl MetricStore {
pub fn new(max_metrics_per_host: usize, history_retention_hours: u64) -> Self {
Self {
current_metrics: HashMap::new(),
historical_metrics: HashMap::new(),
last_update: HashMap::new(),
max_metrics_per_host,
history_retention: Duration::from_secs(history_retention_hours * 3600),
}
}
/// Update metrics for a specific host
pub fn update_metrics(&mut self, hostname: &str, metrics: Vec<Metric>) {
let now = Instant::now();
debug!("Updating {} metrics for host {}", metrics.len(), hostname);
// Get or create host entry
let host_metrics = self
.current_metrics
.entry(hostname.to_string())
.or_insert_with(HashMap::new);
// Get or create historical entry
let host_history = self
.historical_metrics
.entry(hostname.to_string())
.or_insert_with(Vec::new);
// Update current metrics and add to history
for metric in metrics {
let metric_name = metric.name.clone();
// Store current metric
host_metrics.insert(metric_name.clone(), metric.clone());
// Add to history
host_history.push(MetricDataPoint { received_at: now });
}
// Update last update timestamp
self.last_update.insert(hostname.to_string(), now);
// Get metrics count before cleanup
let metrics_count = host_metrics.len();
// Cleanup old history and enforce limits
self.cleanup_host_data(hostname);
info!(
"Updated metrics for {}: {} current metrics",
hostname, metrics_count
);
}
/// Get current metric for a specific host
pub fn get_metric(&self, hostname: &str, metric_name: &str) -> Option<&Metric> {
self.current_metrics.get(hostname)?.get(metric_name)
}
/// Get all current metrics for a host as a vector
pub fn get_metrics_for_host(&self, hostname: &str) -> Vec<&Metric> {
if let Some(metrics_map) = self.current_metrics.get(hostname) {
metrics_map.values().collect()
} else {
Vec::new()
}
}
/// Get connected hosts (hosts with recent updates)
pub fn get_connected_hosts(&self, timeout: Duration) -> Vec<String> {
let now = Instant::now();
self.last_update
.iter()
.filter_map(|(hostname, &last_update)| {
if now.duration_since(last_update) <= timeout {
Some(hostname.clone())
} else {
None
}
})
.collect()
}
/// Cleanup old data and enforce limits
fn cleanup_host_data(&mut self, hostname: &str) {
let now = Instant::now();
// Cleanup historical data
if let Some(history) = self.historical_metrics.get_mut(hostname) {
// Remove old entries
history.retain(|dp| now.duration_since(dp.received_at) <= self.history_retention);
// Enforce size limit
if history.len() > self.max_metrics_per_host {
let excess = history.len() - self.max_metrics_per_host;
history.drain(0..excess);
warn!(
"Trimmed {} old metrics for host {} (size limit: {})",
excess, hostname, self.max_metrics_per_host
);
}
}
}
}