collector/redfish: improve GPU SN/model fallback and warnings

This commit is contained in:
2026-02-28 12:52:22 +03:00
parent ddab93a5ee
commit 9aadf2f1e9
3 changed files with 436 additions and 11 deletions

View File

@@ -421,3 +421,226 @@ func TestReplayCollectStorage_ProbesSupermicroNVMeDiskBayWhenCollectionEmpty(t *
t.Fatalf("expected size to be parsed from CapacityBytes")
}
}
func TestReplayCollectGPUs_DoesNotCollapseOnPlaceholderSerialAndSkipsNIC(t *testing.T) {
r := redfishSnapshotReader{tree: map[string]interface{}{
"/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{
"Members": []interface{}{
map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/3"},
map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/9"},
map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/7"},
},
},
"/redfish/v1/Chassis/1/PCIeDevices/3": map[string]interface{}{
"Id": "3",
"Name": "PCIeCard3",
"Model": "H200-SXM5-141G",
"Manufacturer": "NVIDIA",
"SerialNumber": "N/A",
"Oem": map[string]interface{}{
"Public": map[string]interface{}{
"DeviceClass": "DisplayController",
},
},
},
"/redfish/v1/Chassis/1/PCIeDevices/9": map[string]interface{}{
"Id": "9",
"Name": "PCIeCard9",
"Model": "H200-SXM5-141G",
"Manufacturer": "NVIDIA",
"SerialNumber": "N/A",
"Oem": map[string]interface{}{
"Public": map[string]interface{}{
"DeviceClass": "DisplayController",
},
},
},
"/redfish/v1/Chassis/1/PCIeDevices/7": map[string]interface{}{
"Id": "7",
"Name": "PCIeCard7",
"Model": "MCX631102AN-ADAT",
"Manufacturer": "NVIDIA",
"SerialNumber": "MT2538J00CZE",
"Oem": map[string]interface{}{
"Public": map[string]interface{}{
"DeviceClass": "NetworkController",
},
},
},
}}
got := r.collectGPUs(nil, []string{"/redfish/v1/Chassis/1"})
if len(got) != 2 {
t.Fatalf("expected 2 GPUs (two H200 cards), got %d", len(got))
}
for _, gpu := range got {
if gpu.Model == "MCX631102AN-ADAT" {
t.Fatalf("network adapter should not be classified as GPU")
}
}
}
func TestParseBoardInfo_NormalizesNullPlaceholders(t *testing.T) {
got := parseBoardInfo(map[string]interface{}{
"Manufacturer": "NULL",
"Model": "NULL",
"SerialNumber": "23E100051",
"PartNumber": "0 ",
"UUID": "fa403f6f-2ee9-11f0-bab9-346f1104085a",
})
if got.Manufacturer != "" {
t.Fatalf("expected empty manufacturer, got %q", got.Manufacturer)
}
if got.ProductName != "" {
t.Fatalf("expected empty product name, got %q", got.ProductName)
}
if got.PartNumber != "" {
t.Fatalf("expected empty part number, got %q", got.PartNumber)
}
if got.SerialNumber != "23E100051" {
t.Fatalf("unexpected serial number: %q", got.SerialNumber)
}
}
func TestShouldCrawlPath_SkipsJsonSchemas(t *testing.T) {
if shouldCrawlPath("/redfish/v1/JsonSchemas") {
t.Fatalf("expected /JsonSchemas to be skipped")
}
if shouldCrawlPath("/redfish/v1/JsonSchemas/ComputerSystem.v1_8_0") {
t.Fatalf("expected JsonSchemas members to be skipped")
}
if !shouldCrawlPath("/redfish/v1/Systems/1") {
t.Fatalf("expected normal hardware path to be crawled")
}
}
func TestReplayCollectGPUs_FromGraphicsControllers(t *testing.T) {
r := redfishSnapshotReader{tree: map[string]interface{}{
"/redfish/v1/Systems/1/GraphicsControllers": map[string]interface{}{
"Members": []interface{}{
map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/GraphicsControllers/GPU0"},
map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/GraphicsControllers/GPU1"},
},
},
"/redfish/v1/Systems/1/GraphicsControllers/GPU0": map[string]interface{}{
"Id": "GPU0",
"Name": "GPU0",
"Model": "H200-SXM5-141G",
"Manufacturer": "NVIDIA",
"SerialNumber": "1654225094493",
"Status": map[string]interface{}{"State": "Enabled", "Health": "OK"},
},
"/redfish/v1/Systems/1/GraphicsControllers/GPU1": map[string]interface{}{
"Id": "GPU1",
"Name": "GPU1",
"Model": "H200-SXM5-141G",
"Manufacturer": "NVIDIA",
"SerialNumber": "1654425002635",
"Status": map[string]interface{}{"State": "Enabled", "Health": "OK"},
},
}}
got := r.collectGPUs([]string{"/redfish/v1/Systems/1"}, nil)
if len(got) != 2 {
t.Fatalf("expected 2 GPUs from GraphicsControllers, got %d", len(got))
}
if got[0].SerialNumber == "" || got[1].SerialNumber == "" {
t.Fatalf("expected GPU serial numbers from GraphicsControllers")
}
}
func TestParseBoardInfoWithFallback_UsesFRU(t *testing.T) {
system := map[string]interface{}{
"Manufacturer": "NULL",
"Model": "NULL",
"SerialNumber": "23E100051",
"PartNumber": "0",
}
chassis := map[string]interface{}{
"Manufacturer": "NULL",
"Model": "NULL",
}
fru := map[string]interface{}{
"FRUInfo": map[string]interface{}{
"Board": map[string]interface{}{
"Manufacturer": "Kaytus",
"ProductName": "KR4268X2",
},
},
}
got := parseBoardInfoWithFallback(system, chassis, fru)
if got.ProductName != "KR4268X2" {
t.Fatalf("expected product from FRU, got %q", got.ProductName)
}
if got.Manufacturer != "Kaytus" {
t.Fatalf("expected manufacturer from FRU, got %q", got.Manufacturer)
}
if got.SerialNumber != "23E100051" {
t.Fatalf("expected serial from system, got %q", got.SerialNumber)
}
}
func TestReplayRedfishFromRawPayloads_AddsMissingServerModelWarning(t *testing.T) {
raw := map[string]any{
"redfish_tree": map[string]interface{}{
"/redfish/v1": map[string]interface{}{
"Systems": map[string]interface{}{"@odata.id": "/redfish/v1/Systems"},
"Chassis": map[string]interface{}{"@odata.id": "/redfish/v1/Chassis"},
"Managers": map[string]interface{}{"@odata.id": "/redfish/v1/Managers"},
},
"/redfish/v1/Systems": map[string]interface{}{
"Members": []interface{}{
map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"},
},
},
"/redfish/v1/Systems/1": map[string]interface{}{
"Manufacturer": "NULL",
"Model": "NULL",
"SerialNumber": "23E100051",
},
"/redfish/v1/Chassis": map[string]interface{}{
"Members": []interface{}{
map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1"},
},
},
"/redfish/v1/Chassis/1": map[string]interface{}{
"Id": "1",
"Manufacturer": "NULL",
"Model": "NULL",
},
"/redfish/v1/Managers": map[string]interface{}{
"Members": []interface{}{
map[string]interface{}{"@odata.id": "/redfish/v1/Managers/1"},
},
},
"/redfish/v1/Managers/1": map[string]interface{}{
"Id": "1",
},
},
"redfish_fetch_errors": []map[string]interface{}{
{"path": "/redfish/v1/Systems/1/Oem/Public/FRU", "error": "status 500"},
},
}
got, err := ReplayRedfishFromRawPayloads(raw, nil)
if err != nil {
t.Fatalf("replay failed: %v", err)
}
if got.Hardware == nil {
t.Fatalf("expected hardware")
}
if got.Hardware.BoardInfo.ProductName != "" {
t.Fatalf("expected empty model for warning test, got %q", got.Hardware.BoardInfo.ProductName)
}
found := false
for _, ev := range got.Events {
if ev.Source == "Redfish" && ev.EventType == "Collection Warning" {
found = true
break
}
}
if !found {
t.Fatalf("expected collection warning event about missing server model")
}
}