All checks were successful
Build and Release / build-and-release (push) Successful in 1m7s
Implements clean structured data collection eliminating all string metric parsing bugs. Collectors now populate AgentData directly with type-safe field access. Key improvements: - Mount points preserved correctly (/ and /boot instead of root/boot) - Tmpfs discovery added to memory collector - Temperature data flows as typed f32 fields - Zero string parsing overhead - Complete removal of MetricCollectionManager bridge - Direct ZMQ transmission of structured JSON All functionality maintained: service tracking, notifications, status evaluation, and multi-host monitoring.
88 lines
3.0 KiB
Rust
88 lines
3.0 KiB
Rust
use async_trait::async_trait;
|
|
use cm_dashboard_shared::{AgentData, BackupData};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::fs;
|
|
use std::path::Path;
|
|
use tracing::debug;
|
|
|
|
use super::{Collector, CollectorError};
|
|
|
|
/// Backup collector that reads backup status from JSON files with structured data output
|
|
pub struct BackupCollector {
|
|
/// Path to backup status file
|
|
status_file_path: String,
|
|
}
|
|
|
|
impl BackupCollector {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
status_file_path: "/var/lib/backup/status.json".to_string(),
|
|
}
|
|
}
|
|
|
|
/// Read backup status from JSON file
|
|
async fn read_backup_status(&self) -> Result<Option<BackupStatus>, CollectorError> {
|
|
if !Path::new(&self.status_file_path).exists() {
|
|
debug!("Backup status file not found: {}", self.status_file_path);
|
|
return Ok(None);
|
|
}
|
|
|
|
let content = fs::read_to_string(&self.status_file_path)
|
|
.map_err(|e| CollectorError::SystemRead {
|
|
path: self.status_file_path.clone(),
|
|
error: e.to_string(),
|
|
})?;
|
|
|
|
let status: BackupStatus = serde_json::from_str(&content)
|
|
.map_err(|e| CollectorError::Parse {
|
|
value: content.clone(),
|
|
error: format!("Failed to parse backup status JSON: {}", e),
|
|
})?;
|
|
|
|
Ok(Some(status))
|
|
}
|
|
|
|
/// Convert BackupStatus to BackupData and populate AgentData
|
|
async fn populate_backup_data(&self, agent_data: &mut AgentData) -> Result<(), CollectorError> {
|
|
if let Some(backup_status) = self.read_backup_status().await? {
|
|
let backup_data = BackupData {
|
|
status: backup_status.status,
|
|
last_run: Some(backup_status.last_run),
|
|
next_scheduled: Some(backup_status.next_scheduled),
|
|
total_size_gb: Some(backup_status.total_size_gb),
|
|
repository_health: Some(backup_status.repository_health),
|
|
};
|
|
|
|
agent_data.backup = backup_data;
|
|
} else {
|
|
// No backup status available - set default values
|
|
agent_data.backup = BackupData {
|
|
status: "unavailable".to_string(),
|
|
last_run: None,
|
|
next_scheduled: None,
|
|
total_size_gb: None,
|
|
repository_health: None,
|
|
};
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl Collector for BackupCollector {
|
|
async fn collect_structured(&self, agent_data: &mut AgentData) -> Result<(), CollectorError> {
|
|
debug!("Collecting backup status");
|
|
self.populate_backup_data(agent_data).await
|
|
}
|
|
}
|
|
|
|
/// Backup status structure from JSON file
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
struct BackupStatus {
|
|
pub status: String, // "completed", "running", "failed", etc.
|
|
pub last_run: u64, // Unix timestamp
|
|
pub next_scheduled: u64, // Unix timestamp
|
|
pub total_size_gb: f32, // Total backup size in GB
|
|
pub repository_health: String, // "ok", "warning", "error"
|
|
} |