Files
pulse/pkg/diskinventory/identity_test.go
Pulse Test 674ca18968 fix(unifiedresources): merge PVE and agent rows for the same RAID array volume
The PVE disks/list poll and the agent SMART report both observe a RAID
controller's exported volume, but PVE surfaces its NAA identifier as a
bare-hex serial while smartctl reports the same value as a naa.-prefixed
WWN with no serial. The linked-disk join compared serial to serial and
WWN to WWN verbatim, so the volume rendered twice (/dev/sda and sda)
with independent metric histories.

Hardware identity comparison now normalizes reporter framing (naa., eui.,
wwn-, 0x prefixes, case) and folds serial and WWN together before
comparing. Values are never truncated: sibling volumes on one controller
share their leading WWN bytes, and a truncated udev ID_WWN must stay
unequal to a full identifier. Placeholder serials no longer count as an
identity match.

Refs #1720
2026-08-28 06:20:10 +01:00

128 lines
3.9 KiB
Go

package diskinventory
import "testing"
func TestPreferredIDPreservesDirectDeviceFallbacks(t *testing.T) {
for _, test := range []struct {
name string
device string
controller string
target string
want string
}{
{name: "sata", device: "/dev/sda", want: "host:sda"},
{name: "nvme", device: "nvme0n1", want: "host:nvme0n1"},
{name: "direct sas hctl", device: "sdb", controller: "0000:03:00.0", target: "6:0:0:0", want: "host:sdb"},
{name: "controller member", device: "sdc [megaraid,7]", controller: "sdc", target: "megaraid,7", want: "host:sdc@sdc/megaraid,7"},
{name: "areca member", device: "sdc [areca,1/1]", controller: "arcmsr0", target: "areca,1/1", want: "host:sdc@arcmsr0/areca,1/1"},
{name: "sssraid member", device: "sg2 [sssraid,0,1]", controller: "sssraid0", target: "sssraid,0,1", want: "host:sg2@sssraid0/sssraid,0,1"},
} {
t.Run(test.name, func(t *testing.T) {
got := PreferredID("", "", "host", test.device, test.controller, test.target)
if got != test.want {
t.Fatalf("PreferredID() = %q, want %q", got, test.want)
}
})
}
}
func TestPreferredIDKeepsExistingHardwareIdentityPriority(t *testing.T) {
if got := PreferredID(" SERIAL ", "WWN", "host", "sda", "controller", "megaraid,7"); got != "SERIAL" {
t.Fatalf("serial identity = %q, want SERIAL", got)
}
if got := PreferredID("", " WWN ", "host", "sda", "controller", "megaraid,7"); got != "WWN" {
t.Fatalf("WWN identity = %q, want WWN", got)
}
}
func TestPreferredIDRejectsPlaceholderHardwareIdentity(t *testing.T) {
for _, placeholder := range []string{
"UNKNOWN",
"N/A",
"DEFAULT-SERIAL",
"0000-0000-0000",
"FFFF:FFFF",
} {
if IsUsableHardwareID(placeholder) {
t.Fatalf("placeholder %q was treated as usable hardware identity", placeholder)
}
if got := PreferredID(placeholder, "", "host-a", "/dev/sda", "", ""); got != "host-a:sda" {
t.Fatalf("placeholder %q produced ID %q, want scoped fallback", placeholder, got)
}
}
if !IsUsableHardwareID("ZR5DLAYJ") || !IsUsableHardwareID("naa.5000c500abcdef01") {
t.Fatal("real disk serial/WWN was rejected")
}
}
func TestHardwareIdentityMatch(t *testing.T) {
cases := []struct {
name string
aSerial, aWWN string
bSerial, bWWN string
want bool
}{
{
name: "pve bare-hex serial matches smartctl naa wwn",
aSerial: "61866da053481f002f58a43b22f964a7",
aWWN: "0x61866da053481f00",
bWWN: "naa.61866da053481f002f58a43b22f964a7",
want: true,
},
{
name: "same serial different case",
aSerial: "zr5dlayj",
bSerial: "ZR5DLAYJ",
want: true,
},
{
name: "udev wwn-0x token matches naa wwn",
aWWN: "wwn-0x5000c500abcdef01",
bWWN: "naa.5000c500abcdef01",
want: true,
},
{
name: "eui prefix matches bare nvme id",
aWWN: "eui.0025385b91501234",
bWWN: "0025385b91501234",
want: true,
},
{
name: "truncated udev wwn never matches full sibling identifier",
aWWN: "0x61866da053481f00",
bSerial: "61866da053481f0030543ecb1d3b4cca",
bWWN: "naa.61866da053481f0030543ecb1d3b4cca",
want: false,
},
{
name: "placeholder serials do not match each other",
aSerial: "UNKNOWN",
bSerial: "UNKNOWN",
want: false,
},
{
name: "distinct disks stay distinct",
aSerial: "9410A0FWFVL9",
bSerial: "35C0A39YFVL9",
want: false,
},
{
name: "empty observations never match",
want: false,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := HardwareIdentityMatch(tc.aSerial, tc.aWWN, tc.bSerial, tc.bWWN)
if got != tc.want {
t.Fatalf("HardwareIdentityMatch(%q,%q,%q,%q) = %v, want %v",
tc.aSerial, tc.aWWN, tc.bSerial, tc.bWWN, got, tc.want)
}
mirrored := HardwareIdentityMatch(tc.bSerial, tc.bWWN, tc.aSerial, tc.aWWN)
if mirrored != tc.want {
t.Fatalf("match is not symmetric: mirrored = %v, want %v", mirrored, tc.want)
}
})
}
}