fix(smart v0.1.15): contrôleur NVMe + règle udev disk group

Cause racine : smartctl -a -j /dev/nvme0n1 (namespace) retourne exit 4
et omet smart_status car les commandes admin échouent via le namespace.
Solution : utiliser /dev/nvme0 (contrôleur) accessible grâce à la règle
udev SUBSYSTEM==nvme GROUP=disk.

- smart.rs : scan /sys/class/nvme/ pour les contrôleurs (nvme0, nvme1)
  au lieu de /sys/block/ pour les namespaces (nvme0n1)
- deploy/99-nanometrics-smart.rules : udev rule KERNEL==nvme* GROUP=disk
- deploy/install.sh : déploie la règle udev + udevadm trigger

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gilles Soulier
2026-05-23 13:39:13 +02:00
parent ee5e8710a3
commit a9506a5505
5 changed files with 39 additions and 20 deletions
+1 -1
View File
@@ -248,7 +248,7 @@ dependencies = [
[[package]]
name = "nanometrics-agent"
version = "0.1.14"
version = "0.1.15"
dependencies = [
"libc",
"rumqttc",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nanometrics-agent"
version = "0.1.14"
version = "0.1.15"
edition = "2021"
[lib]
+22 -18
View File
@@ -91,24 +91,28 @@ pub fn collect() -> Option<Vec<crate::payload::SmartMetrics>> {
eprintln!("[smart] smartctl introuvable dans PATH");
return None;
}
let mut devs: Vec<String> = std::fs::read_dir("/sys/block")
.into_iter()
.flatten()
.flatten()
.map(|e| e.file_name().into_string().unwrap_or_default())
.filter_map(|n| {
if n.starts_with("sd") {
Some(format!("/dev/{}", n))
} else if n.starts_with("nvme") && n[4..].contains('n') {
// nvme0n1, nvme1n1 — namespace block device ; "nvme0" (contrôleur) ne passerait pas
Some(format!("/dev/{}", n))
} else {
None
}
})
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect();
let mut set = std::collections::HashSet::new();
// SATA/SAS : /sys/block/sd* → /dev/sda, /dev/sdb…
for e in std::fs::read_dir("/sys/block").into_iter().flatten().flatten() {
let n = e.file_name().into_string().unwrap_or_default();
if n.starts_with("sd") {
set.insert(format!("/dev/{}", n));
}
}
// NVMe : /sys/class/nvme/nvme* → /dev/nvme0, /dev/nvme1…
// On utilise le contrôleur (char device), pas le namespace (block device),
// car smartctl ne peut exécuter les commandes admin SMART que via le contrôleur.
// La règle udev 99-nanometrics-smart.rules lui donne l'accès groupe disk.
for e in std::fs::read_dir("/sys/class/nvme").into_iter().flatten().flatten() {
let n = e.file_name().into_string().unwrap_or_default();
if n.starts_with("nvme") {
set.insert(format!("/dev/{}", n));
}
}
let mut devs: Vec<String> = set.into_iter().collect();
devs.sort();
eprintln!("[smart] disques détectés: {:?}", devs);