From 22f8dd3bbbc0cdfc0ef0bbe7055020a8bd643b9e Mon Sep 17 00:00:00 2001 From: rcourtman Date: Sun, 5 Apr 2026 21:13:34 +0100 Subject: [PATCH] Normalize typed SMART device labels for temp merge --- internal/monitoring/merge_temps_test.go | 51 +++++++++++++++++++++++++ internal/monitoring/monitor.go | 26 ++++++++++--- 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/internal/monitoring/merge_temps_test.go b/internal/monitoring/merge_temps_test.go index 2b5d9a689..c6e55c69f 100644 --- a/internal/monitoring/merge_temps_test.go +++ b/internal/monitoring/merge_temps_test.go @@ -104,6 +104,26 @@ func TestMergeNVMeTempsIntoDisks(t *testing.T) { {Node: "node1", DevPath: "/dev/sdc", Temperature: 35}, }, }, + { + name: "SMART temperature matched by typed device label", + disks: []models.PhysicalDisk{ + {Node: "node1", DevPath: "/dev/sdd", Temperature: 0}, + }, + nodes: []models.Node{ + { + Name: "node1", + Temperature: &models.Temperature{ + Available: true, + SMART: []models.DiskTemp{ + {Device: "sdd [scsi]", Temperature: 39}, + }, + }, + }, + }, + expected: []models.PhysicalDisk{ + {Node: "node1", DevPath: "/dev/sdd", Temperature: 39}, + }, + }, { name: "NVMe legacy fallback when no SMART match", disks: []models.PhysicalDisk{ @@ -331,3 +351,34 @@ func TestMergeNVMeTempsIntoDisks_OriginalSliceUnchanged(t *testing.T) { t.Errorf("original disk temperature was modified: got %d, want 0", original[0].Temperature) } } + +func TestMergeHostAgentSMARTIntoDisks_MatchesTypedDeviceLabel(t *testing.T) { + disks := []models.PhysicalDisk{{ + Node: "node1", + Instance: "inst", + DevPath: "/dev/sda", + Temperature: 0, + }} + nodes := []models.Node{{ + Name: "node1", + Instance: "inst", + LinkedHostAgentID: "host-1", + }} + hosts := []models.Host{{ + ID: "host-1", + Sensors: models.HostSensorSummary{ + SMART: []models.HostDiskSMART{{ + Device: "/dev/sda [scsi]", + Temperature: 41, + }}, + }, + }} + + merged := mergeHostAgentSMARTIntoDisks(disks, nodes, hosts) + if len(merged) != 1 { + t.Fatalf("expected 1 merged disk, got %#v", merged) + } + if merged[0].Temperature != 41 { + t.Fatalf("expected SMART temperature 41, got %#v", merged[0]) + } +} diff --git a/internal/monitoring/monitor.go b/internal/monitoring/monitor.go index 0be293fe4..aa64ddca7 100644 --- a/internal/monitoring/monitor.go +++ b/internal/monitoring/monitor.go @@ -162,6 +162,20 @@ func (m *Monitor) getInstanceConfig(instanceName string) *config.PVEInstance { return nil } +func normalizeSMARTDeviceIdentifier(device string) string { + normalized := strings.TrimSpace(device) + if normalized == "" { + return "" + } + + if base, suffix, found := strings.Cut(normalized, " ["); found && strings.HasSuffix(suffix, "]") { + normalized = base + } + + normalized = strings.TrimSpace(normalized) + return strings.TrimPrefix(normalized, "/dev/") +} + func mergeNVMeTempsIntoDisks(disks []models.PhysicalDisk, nodes []models.Node) []models.PhysicalDisk { if len(disks) == 0 || len(nodes) == 0 { return disks @@ -275,10 +289,10 @@ func mergeNVMeTempsIntoDisks(disks []models.PhysicalDisk, nodes []models.Node) [ // Last resort: match by device path (normalized) if updated[i].Temperature == 0 { - normalizedDevPath := strings.TrimPrefix(updated[i].DevPath, "/dev/") + normalizedDevPath := normalizeSMARTDeviceIdentifier(updated[i].DevPath) for _, temp := range smartTemps { - normalizedTempDev := strings.TrimPrefix(temp.Device, "/dev/") - if normalizedTempDev == normalizedDevPath { + normalizedTempDev := normalizeSMARTDeviceIdentifier(temp.Device) + if normalizedTempDev != "" && normalizedTempDev == normalizedDevPath { if temp.Temperature > 0 && !temp.StandbySkipped { updated[i].Temperature = temp.Temperature log.Debug(). @@ -401,10 +415,10 @@ func mergeHostAgentSMARTIntoDisks(disks []models.PhysicalDisk, nodes []models.No // Last resort: match by device path if matched == nil { - normalizedDevPath := strings.TrimPrefix(updated[i].DevPath, "/dev/") + normalizedDevPath := normalizeSMARTDeviceIdentifier(updated[i].DevPath) for j := range smartData { - normalizedDiskDev := strings.TrimPrefix(smartData[j].Device, "/dev/") - if normalizedDiskDev == normalizedDevPath { + normalizedDiskDev := normalizeSMARTDeviceIdentifier(smartData[j].Device) + if normalizedDiskDev != "" && normalizedDiskDev == normalizedDevPath { matched = &smartData[j] break }