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
This commit is contained in:
Christoffer Martinsson 2025-10-25 17:13:04 +02:00
parent 83cb43bcf1
commit 9160fac80b

View File

@ -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<Vec<String>, 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<Vec<Metric>, CollectorError> {
async fn collect_device_smart(&self, device: &str, status_tracker: &mut StatusTracker) -> Result<Vec<Metric>, 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<Vec<Metric>, CollectorError> {
fn parse_smart_output(&self, device: &str, output: &str, status_tracker: &mut StatusTracker) -> Result<Vec<Metric>, CollectorError> {
let mut metrics = Vec::new();
let device_name = device.trim_start_matches("/dev/");
let mut health_ok = true;
let mut temperature: Option<f64> = None;
let mut temperature: Option<f32> = 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<f64> {
fn extract_temperature(&self, line: &str) -> Option<f32> {
let parts: Vec<&str> = line.split_whitespace().collect();
for (i, part) in parts.iter().enumerate() {
if let Ok(temp) = part.parse::<f64>() {
if let Ok(temp) = part.parse::<f32>() {
// 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<Vec<Metric>, CollectorError> {
async fn collect(&self, status_tracker: &mut StatusTracker) -> Result<Vec<Metric>, 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);
}