From 9160fac80bae2009ae3b13d69408d96470e58dcf Mon Sep 17 00:00:00 2001 From: Christoffer Martinsson Date: Sat, 25 Oct 2025 17:13:04 +0200 Subject: [PATCH] Fix smart collector compilation errors - Update to match current Metric structure - Use correct Status enum and collector interface - Fix MetricValue types and constructor usage - Builds successfully with warnings only --- agent/src/collectors/smart.rs | 79 +++++++++++++++-------------------- 1 file changed, 33 insertions(+), 46 deletions(-) diff --git a/agent/src/collectors/smart.rs b/agent/src/collectors/smart.rs index 6e0de94..491fa34 100644 --- a/agent/src/collectors/smart.rs +++ b/agent/src/collectors/smart.rs @@ -1,20 +1,14 @@ use async_trait::async_trait; -use cm_dashboard_shared::{Metric, MetricStatus, MetricValue}; +use cm_dashboard_shared::{Metric, MetricValue, Status, StatusTracker}; use std::process::Stdio; use tokio::process::Command; use tracing::{debug, warn}; use super::{Collector, CollectorError}; -pub struct SmartCollector { - hostname: String, -} +pub struct SmartCollector; impl SmartCollector { - pub fn new(hostname: String) -> Self { - Self { hostname } - } - /// Get list of storage devices to monitor async fn get_devices(&self) -> Result, CollectorError> { let output = Command::new("lsblk") @@ -23,7 +17,10 @@ impl SmartCollector { .stderr(Stdio::null()) .output() .await - .map_err(|e| CollectorError::Collection(e.to_string()))?; + .map_err(|e| CollectorError::SystemRead { + path: "lsblk".to_string(), + error: e.to_string() + })?; if !output.status.success() { return Ok(Vec::new()); // Return empty if lsblk fails @@ -46,7 +43,7 @@ impl SmartCollector { } /// Collect SMART data for a single device - async fn collect_device_smart(&self, device: &str) -> Result, CollectorError> { + async fn collect_device_smart(&self, device: &str, status_tracker: &mut StatusTracker) -> Result, CollectorError> { debug!("Collecting SMART data for device: {}", device); let output = Command::new("sudo") @@ -55,7 +52,10 @@ impl SmartCollector { .stderr(Stdio::null()) .output() .await - .map_err(|e| CollectorError::Collection(e.to_string()))?; + .map_err(|e| CollectorError::SystemRead { + path: "lsblk".to_string(), + error: e.to_string() + })?; if !output.status.success() { warn!("smartctl failed for device: {}", device); @@ -63,16 +63,16 @@ impl SmartCollector { } let stdout = String::from_utf8_lossy(&output.stdout); - self.parse_smart_output(device, &stdout) + self.parse_smart_output(device, &stdout, status_tracker) } /// Parse smartctl output and create metrics - fn parse_smart_output(&self, device: &str, output: &str) -> Result, CollectorError> { + fn parse_smart_output(&self, device: &str, output: &str, status_tracker: &mut StatusTracker) -> Result, CollectorError> { let mut metrics = Vec::new(); let device_name = device.trim_start_matches("/dev/"); let mut health_ok = true; - let mut temperature: Option = None; + let mut temperature: Option = None; for line in output.lines() { let line = line.trim(); @@ -94,45 +94,32 @@ impl SmartCollector { // Create health metric let health_status = if health_ok { - MetricStatus::Ok + Status::Ok } else { - MetricStatus::Critical + Status::Critical }; - metrics.push(Metric { - hostname: self.hostname.clone(), - metric_name: format!("smart_health_{}", device_name), - metric_value: MetricValue::String(if health_ok { "PASSED".to_string() } else { "FAILED".to_string() }), - status: health_status, - timestamp: chrono::Utc::now(), - tags: vec![ - ("device".to_string(), device_name.to_string()), - ("type".to_string(), "health".to_string()), - ], - }); + metrics.push(Metric::new( + format!("smart_health_{}", device_name), + MetricValue::String(if health_ok { "PASSED".to_string() } else { "FAILED".to_string() }), + health_status, + )); // Create temperature metric if available if let Some(temp) = temperature { let temp_status = if temp >= 70.0 { - MetricStatus::Critical + Status::Critical } else if temp >= 60.0 { - MetricStatus::Warning + Status::Warning } else { - MetricStatus::Ok + Status::Ok }; - metrics.push(Metric { - hostname: self.hostname.clone(), - metric_name: format!("smart_temperature_{}", device_name), - metric_value: MetricValue::Float(temp), - status: temp_status, - timestamp: chrono::Utc::now(), - tags: vec![ - ("device".to_string(), device_name.to_string()), - ("type".to_string(), "temperature".to_string()), - ("unit".to_string(), "celsius".to_string()), - ], - }); + metrics.push(Metric::new( + format!("smart_temperature_{}", device_name), + MetricValue::Float(temp), + temp_status, + ).with_unit("celsius".to_string())); } debug!("Collected {} SMART metrics for {}", metrics.len(), device); @@ -140,11 +127,11 @@ impl SmartCollector { } /// Extract temperature value from smartctl output line - fn extract_temperature(&self, line: &str) -> Option { + fn extract_temperature(&self, line: &str) -> Option { let parts: Vec<&str> = line.split_whitespace().collect(); for (i, part) in parts.iter().enumerate() { - if let Ok(temp) = part.parse::() { + if let Ok(temp) = part.parse::() { // Check if this looks like a temperature value (reasonable range) if temp > 0.0 && temp < 150.0 { // Check context around the number @@ -167,14 +154,14 @@ impl SmartCollector { #[async_trait] impl Collector for SmartCollector { - async fn collect(&mut self) -> Result, CollectorError> { + async fn collect(&self, status_tracker: &mut StatusTracker) -> Result, CollectorError> { debug!("Starting SMART data collection"); let devices = self.get_devices().await?; let mut all_metrics = Vec::new(); for device in devices { - match self.collect_device_smart(&device).await { + match self.collect_device_smart(&device, status_tracker).await { Ok(mut metrics) => { all_metrics.append(&mut metrics); }