diff --git a/internal/storagehealth/topology.go b/internal/storagehealth/topology.go index c8c6b3dde..25b61af35 100644 --- a/internal/storagehealth/topology.go +++ b/internal/storagehealth/topology.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/rcourtman/pulse-go-rewrite/internal/models" + unraidstatus "github.com/rcourtman/pulse-go-rewrite/internal/unraid" ) func AssessHostRAIDArray(array models.HostRAIDArray) Assessment { @@ -346,13 +347,16 @@ func unraidDiskStateCounts(storage models.HostUnraidStorage) (disabled, invalid, func isUnraidEmptySlot(disk models.HostUnraidDisk) bool { rawStatus := strings.ToUpper(strings.TrimSpace(disk.RawStatus)) status := strings.ToLower(strings.TrimSpace(disk.Status)) + if unraidstatus.IsExplicitMissingMember(rawStatus) { + return false + } if !strings.Contains(rawStatus, "DISK_NP") && status != "missing" { return false } return strings.TrimSpace(disk.Device) == "" && strings.TrimSpace(disk.Model) == "" && strings.TrimSpace(disk.Serial) == "" && - strings.TrimSpace(disk.Filesystem) == "" && + !unraidstatus.HasMeaningfulFilesystem(disk.Filesystem) && disk.SizeBytes == 0 } diff --git a/internal/storagehealth/topology_test.go b/internal/storagehealth/topology_test.go index 9352106c1..e61c095a1 100644 --- a/internal/storagehealth/topology_test.go +++ b/internal/storagehealth/topology_test.go @@ -237,7 +237,8 @@ func TestAssessUnraidStorageTreatsEmptyNoPresentSlotsAsUnprotected(t *testing.T) {Name: "parity", Role: "parity", Status: "missing", RawStatus: "DISK_NP_DSBL"}, {Name: "md1p1", Device: "/dev/sde", Status: "online", RawStatus: "DISK_OK", SizeBytes: 5860522532}, {Name: "disk5", Role: "data", Status: "missing", RawStatus: "DISK_NP", Slot: 5}, - {Name: "parity2", Role: "parity", Status: "missing", RawStatus: "DISK_NP_DSBL", Slot: 29}, + {Name: "disk6", Role: "data", Status: "missing", RawStatus: "DISK_NP", Filesystem: "auto", Slot: 6}, + {Name: "parity2", Role: "parity", Status: "missing", RawStatus: "DISK_NP_DSBL", Filesystem: "auto", Slot: 29}, }, }) @@ -281,6 +282,27 @@ func TestAssessUnraidStorageUsesDiskStatusesOverAggregateCounters(t *testing.T) } } +func TestAssessUnraidStoragePreservesExplicitMissingMemberWithoutIdentity(t *testing.T) { + assessment := AssessUnraidStorage(models.HostUnraidStorage{ + ArrayStarted: true, + Disks: []models.HostUnraidDisk{ + {Name: "parity", Role: "parity", Status: "online"}, + {Name: "disk1", Role: "data", Status: "online"}, + {Name: "disk2", Role: "data", Status: "missing", RawStatus: "DISK_NP_MISSING", Filesystem: "auto"}, + }, + }) + + if assessment.Level != RiskCritical { + t.Fatalf("Level = %q, want %q", assessment.Level, RiskCritical) + } + for _, reason := range assessment.Reasons { + if reason.Code == "unraid_missing_disks" { + return + } + } + t.Fatalf("explicit missing member without identity was not preserved: %+v", assessment.Reasons) +} + func TestAssessUnraidStoragePreservesGenuineStructuredMissingDisk(t *testing.T) { assessment := AssessUnraidStorage(models.HostUnraidStorage{ ArrayStarted: true,