Files
felhom-agent/internal/storage/observe_smart_test.go
T
admin ed97232598 v0.95.0: SMART coverage — union-path drives + LVM/dm root + device model
Implements SPIKE-smart-coverage-2026-07-25 fixes B+A (additive; MinAgent unchanged).
Fix B: storage.SmartReader.SMARTForBacking wired into the /disks union path (localapi
Smart seam) so registry/USB drives get a real SMART read (watchdog Known stays
enrich-free). Fix A: smartDeviceFor resolves dm/LVM to the whole disk via
/sys/block/<dm>/slaves (recursive; skips >1-disk); the builtin local dir on the LVM
root gets a SMART-only device from its containing filesystem (never touches
backing/durable_id). SmartSummary.ModelName captured from smartctl. Fix C (-d sat)
stays rejected. Tests + red-proofs (dm multi-disk skip, enrich smartHint, union
routing); Known-path-never-SMARTs asserted.
2026-07-25 08:21:45 +02:00

72 lines
3.2 KiB
Go

package storage
import (
"context"
"testing"
"gitea.dooplex.hu/admin/felhom-agent/internal/hub"
"gitea.dooplex.hu/admin/felhom-agent/internal/proxmox"
)
// Fix A (A2): the builtin `local` dir lives on the LVM root, so its backing is empty by design — but
// enrich now resolves the containing filesystem (/ → /dev/mapper/pve-root) and, via the dm sysfs
// slaves, the physical disk (/dev/sda). The system disk stops reading "Nincs adat".
// Red-proof: drop the `smartDev = ob.smartHint` fallback in enrich → local stays UNKNOWN and SMART
// is never called on /dev/sda.
func TestObserve_SystemDirSMARTViaContainingFS(t *testing.T) {
fixtureSysfs(t, map[string][]string{"dm-1": {"sda3"}}, map[string]string{"dm-1": "pve-root"})
api := &fakeStorageAPI{
node: "demo-felhom",
cluster: []proxmox.Storage{{Storage: "local", Type: "dir", Path: "/var/lib/vz"}},
nodeSt: []proxmox.Storage{{Storage: "local", Type: "dir", Path: "/var/lib/vz", Active: 1}},
}
host := &fakeHostReader{
mounts: []Mount{{Device: "/dev/mapper/pve-root", MountPoint: "/", FSType: "ext4"}},
}
ops := &fakeHostOps{smartByDevice: map[string]hub.SmartSummary{"/dev/sda": {Health: hub.SmartPassed, ModelName: strptr("AirDisk 512GB SSD")}}}
got, err := NewObserver(api, host, ops, quietLogger()).Observe(context.Background())
if err != nil {
t.Fatal(err)
}
local := byName(got)["local"]
if local.Smart.Health != hub.SmartPassed {
t.Errorf("system disk SMART not enriched via the containing fs: health=%q", local.Smart.Health)
}
if local.Smart.ModelName == nil || *local.Smart.ModelName != "AirDisk 512GB SSD" {
t.Errorf("model not carried: %v", local.Smart.ModelName)
}
// SMART must have run on the resolved PHYSICAL disk, never the dm/mapper node.
if len(ops.smartDevices) != 1 || ops.smartDevices[0] != "/dev/sda" {
t.Errorf("SMART should target /dev/sda, got %v", ops.smartDevices)
}
// The backing device / durable_id must stay untouched by the SMART-only resolution.
if local.BackingDevice != "" {
t.Errorf("system-dir SMART resolution leaked into BackingDevice: %q", local.BackingDevice)
}
}
// The watchdog Known() path MUST remain enrich-free (its slow root-shelling reads are the reason it
// exists as a separate fast path). Known must never invoke SMART.
// Red-proof: route Known through enrich → smartDevices is non-empty and this fails.
func TestKnown_NeverInvokesSMART(t *testing.T) {
fixtureSysfs(t, map[string][]string{"dm-1": {"sda3"}}, map[string]string{"dm-1": "pve-root"})
api := &fakeStorageAPI{
node: "demo-felhom",
cluster: []proxmox.Storage{{Storage: "local", Type: "dir", Path: "/var/lib/vz"}},
nodeSt: []proxmox.Storage{{Storage: "local", Type: "dir", Path: "/var/lib/vz", Active: 1}},
}
host := &fakeHostReader{mounts: []Mount{{Device: "/dev/mapper/pve-root", MountPoint: "/", FSType: "ext4"}}}
ops := &fakeHostOps{smartByDevice: map[string]hub.SmartSummary{"/dev/sda": {Health: hub.SmartPassed}}}
if _, err := NewObserver(api, host, ops, quietLogger()).Known(context.Background()); err != nil {
t.Fatal(err)
}
if len(ops.smartDevices) != 0 {
t.Errorf("Known() invoked SMART %d time(s) — it must stay enrich-free: %v", len(ops.smartDevices), ops.smartDevices)
}
}
func strptr(s string) *string { return &s }