mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-11 14:00:29 +00:00
674ca18968
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
128 lines
3.9 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|
|
}
|