feat(v0.1.5): SMART multi-disques — collecte tous les disques détectés
Agent: - SmartMetrics + champ device (nom du disque ex: sda, nvme0) - smart: Option<Vec<SmartMetrics>> — tous les disques, pas seulement le 1er - collect() itère /sys/block, accumule les résultats de tous les disques valides Serveur: - SmartMetrics.Device + Smart []SmartMetrics dans AgentMetrics - InsertMetrics: stocke smart_json (JSON array) au lieu de colonnes plates - GetLastMetrics: désérialise smart_json - Migration: smart_json TEXT ajoutée Dashboard: - Tuile: une icône shield/triangle par disque avec tooltip incluant le nom - Popup détail: un bouton SMART par disque (couleur ok/err) - showSmart(agentId, diskIdx): affiche le disque sélectionné Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Generated
+1
-1
@@ -248,7 +248,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nanometrics-agent"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rumqttc",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nanometrics-agent"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use serde::Deserialize;
|
||||
use crate::payload::SmartMetrics;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SmartJson {
|
||||
@@ -42,7 +41,7 @@ pub fn is_available() -> bool {
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn parse_json(json: &str) -> Result<SmartMetrics, serde_json::Error> {
|
||||
pub fn parse_json(json: &str) -> Result<crate::payload::SmartMetrics, serde_json::Error> {
|
||||
let s: SmartJson = serde_json::from_str(json)?;
|
||||
|
||||
let temperature = s.temperature.as_ref().map(|t| t.current)
|
||||
@@ -71,7 +70,8 @@ pub fn parse_json(json: &str) -> Result<SmartMetrics, serde_json::Error> {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(SmartMetrics {
|
||||
Ok(crate::payload::SmartMetrics {
|
||||
device: String::new(),
|
||||
passed: s.smart_status.passed,
|
||||
temperature,
|
||||
reallocated_sectors: reallocated,
|
||||
@@ -80,11 +80,10 @@ pub fn parse_json(json: &str) -> Result<SmartMetrics, serde_json::Error> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn collect() -> Option<SmartMetrics> {
|
||||
pub fn collect() -> Option<Vec<crate::payload::SmartMetrics>> {
|
||||
if !is_available() {
|
||||
return None;
|
||||
}
|
||||
// Détecter les disques réels depuis /sys/block (sd*, nvme*)
|
||||
let mut devs: Vec<String> = std::fs::read_dir("/sys/block")
|
||||
.into_iter()
|
||||
.flatten()
|
||||
@@ -95,14 +94,18 @@ pub fn collect() -> Option<SmartMetrics> {
|
||||
.collect();
|
||||
devs.sort();
|
||||
|
||||
let mut results = Vec::new();
|
||||
for dev in &devs {
|
||||
let Ok(output) = std::process::Command::new("smartctl")
|
||||
.args(["-j", dev])
|
||||
.output() else { continue };
|
||||
let json = String::from_utf8_lossy(&output.stdout);
|
||||
if let Ok(metrics) = parse_json(&json) {
|
||||
return Some(metrics);
|
||||
results.push(crate::payload::SmartMetrics {
|
||||
device: dev.trim_start_matches("/dev/").to_string(),
|
||||
..metrics
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
if results.is_empty() { None } else { Some(results) }
|
||||
}
|
||||
|
||||
@@ -18,11 +18,13 @@ pub struct AgentMetrics {
|
||||
pub network_rx: Option<u64>,
|
||||
pub network_tx: Option<u64>,
|
||||
pub temperature: Option<f32>,
|
||||
pub smart: Option<SmartMetrics>,
|
||||
pub smart: Option<Vec<SmartMetrics>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct SmartMetrics {
|
||||
#[serde(default)]
|
||||
pub device: String,
|
||||
pub passed: bool,
|
||||
pub temperature: Option<i64>,
|
||||
pub reallocated_sectors: Option<i64>,
|
||||
|
||||
@@ -32,13 +32,14 @@ fn test_serialize_avec_smart() {
|
||||
let m = AgentMetrics {
|
||||
hostname: "srv-01".to_string(),
|
||||
ip: "10.0.0.11".to_string(),
|
||||
smart: Some(SmartMetrics {
|
||||
smart: Some(vec![SmartMetrics {
|
||||
device: "sda".to_string(),
|
||||
passed: true,
|
||||
temperature: Some(34),
|
||||
reallocated_sectors: Some(0),
|
||||
power_on_hours: Some(4213),
|
||||
wear_level: Some(98),
|
||||
}),
|
||||
}]),
|
||||
status: "online".to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user