package collector import ( "context" "encoding/json" "fmt" "net/http" "net/http/httptest" "net/url" "strings" "testing" "time" "git.mchus.pro/mchus/logpile/internal/collector/redfishprofile" "git.mchus.pro/mchus/logpile/internal/models" ) func testAnalysisPlan(d redfishprofile.AnalysisDirectives) redfishprofile.ResolvedAnalysisPlan { return redfishprofile.ResolvedAnalysisPlan{Directives: d} } func TestRedfishConnectorCollect(t *testing.T) { mux := http.NewServeMux() register := func(path string, payload interface{}) { mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(payload) }) } register("/redfish/v1", map[string]interface{}{"Name": "ServiceRoot"}) register("/redfish/v1/Systems/1", map[string]interface{}{ "Manufacturer": "Supermicro", "Model": "SYS-TEST", "SerialNumber": "SYS123", "BiosVersion": "2.1a", }) register("/redfish/v1/Systems/1/Bios", map[string]interface{}{"Version": "2.1a"}) register("/redfish/v1/Systems/1/SecureBoot", map[string]interface{}{"SecureBootCurrentBoot": "Enabled"}) register("/redfish/v1/Systems/1/Processors", map[string]interface{}{ "Members": []map[string]string{ {"@odata.id": "/redfish/v1/Systems/1/Processors/CPU1"}, }, }) register("/redfish/v1/Systems/1/Processors/CPU1", map[string]interface{}{ "Name": "CPU1", "Model": "Xeon Gold", "TotalCores": 32, "TotalThreads": 64, "MaxSpeedMHz": 3600, }) register("/redfish/v1/Systems/1/Memory", map[string]interface{}{ "Members": []map[string]string{ {"@odata.id": "/redfish/v1/Systems/1/Memory/DIMM1"}, }, }) register("/redfish/v1/Systems/1/Memory/DIMM1", map[string]interface{}{ "Name": "DIMM A1", "CapacityMiB": 32768, "MemoryDeviceType": "DDR5", "OperatingSpeedMhz": 4800, "Status": map[string]interface{}{ "Health": "OK", }, }) register("/redfish/v1/Systems/1/Storage", map[string]interface{}{ "Members": []map[string]string{ {"@odata.id": "/redfish/v1/Systems/1/Storage/1"}, }, }) register("/redfish/v1/Systems/1/Storage/1", map[string]interface{}{ "Drives": []map[string]string{ {"@odata.id": "/redfish/v1/Systems/1/Storage/1/Drives/1"}, }, }) register("/redfish/v1/Systems/1/Storage/1/Drives/1", map[string]interface{}{ "Name": "Drive1", "Model": "NVMe Test", "MediaType": "SSD", "Protocol": "NVMe", "CapacityGB": 960, "SerialNumber": "SN123", }) register("/redfish/v1/Systems/1/PCIeDevices", map[string]interface{}{ "Members": []map[string]string{ {"@odata.id": "/redfish/v1/Systems/1/PCIeDevices/GPU1"}, }, }) register("/redfish/v1/Systems/1/PCIeDevices/GPU1", map[string]interface{}{ "Id": "GPU1", "Name": "NVIDIA H100", "Model": "NVIDIA H100 PCIe", "Manufacturer": "NVIDIA", "SerialNumber": "GPU-SN-001", "PCIeFunctions": map[string]interface{}{ "@odata.id": "/redfish/v1/Systems/1/PCIeDevices/GPU1/PCIeFunctions", }, }) register("/redfish/v1/Systems/1/PCIeDevices/GPU1/PCIeFunctions", map[string]interface{}{ "Members": []map[string]string{ {"@odata.id": "/redfish/v1/Systems/1/PCIeFunctions/GPU1F0"}, }, }) register("/redfish/v1/Systems/1/PCIeFunctions/GPU1F0", map[string]interface{}{ "FunctionId": "0000:65:00.0", "VendorId": "0x10DE", "DeviceId": "0x2331", "ClassCode": "0x030200", "CurrentLinkWidth": 16, "CurrentLinkSpeed": "16.0 GT/s", "MaxLinkWidth": 16, "MaxLinkSpeed": "16.0 GT/s", }) register("/redfish/v1/Chassis/1/NetworkAdapters", map[string]interface{}{ "Members": []map[string]string{ {"@odata.id": "/redfish/v1/Chassis/1/NetworkAdapters/1"}, }, }) register("/redfish/v1/Chassis/1/Power", map[string]interface{}{ "PowerSupplies": []map[string]interface{}{ { "MemberId": "PSU1", "Name": "PSU Slot 1", "Model": "PWS-2K01A-1R", "Manufacturer": "Delta", "PowerCapacityWatts": 2000, "PowerInputWatts": 1600, "LastPowerOutputWatts": 1200, "LineInputVoltage": 230, "Status": map[string]interface{}{ "Health": "OK", "State": "Enabled", }, }, }, }) register("/redfish/v1/Chassis/1/NetworkAdapters/1", map[string]interface{}{ "Name": "Mellanox", "Model": "ConnectX-6", "SerialNumber": "NIC123", }) register("/redfish/v1/Managers/1", map[string]interface{}{ "FirmwareVersion": "1.25", }) register("/redfish/v1/Managers/1/NetworkProtocol", map[string]interface{}{ "Id": "NetworkProtocol", }) ts := httptest.NewServer(mux) defer ts.Close() c := NewRedfishConnector() result, err := c.Collect(context.Background(), Request{ Host: ts.URL, Port: 443, Protocol: "redfish", Username: "admin", AuthType: "password", Password: "secret", TLSMode: "strict", }, nil) if err != nil { t.Fatalf("collect failed: %v", err) } if result.Hardware == nil { t.Fatalf("expected hardware config") } if result.Hardware.BoardInfo.ProductName != "SYS-TEST" { t.Fatalf("unexpected board model: %q", result.Hardware.BoardInfo.ProductName) } if len(result.Hardware.CPUs) != 1 { t.Fatalf("expected one CPU, got %d", len(result.Hardware.CPUs)) } if len(result.Hardware.Memory) != 1 { t.Fatalf("expected one DIMM, got %d", len(result.Hardware.Memory)) } if len(result.Hardware.Storage) != 1 { t.Fatalf("expected one drive, got %d", len(result.Hardware.Storage)) } if len(result.Hardware.NetworkAdapters) != 1 { t.Fatalf("expected one nic, got %d", len(result.Hardware.NetworkAdapters)) } if len(result.Hardware.GPUs) != 1 { t.Fatalf("expected one gpu, got %d", len(result.Hardware.GPUs)) } if result.Hardware.GPUs[0].BDF != "0000:65:00.0" { t.Fatalf("unexpected gpu BDF: %q", result.Hardware.GPUs[0].BDF) } if len(result.Hardware.PCIeDevices) != 1 { t.Fatalf("expected one pcie device, got %d", len(result.Hardware.PCIeDevices)) } if len(result.Hardware.PowerSupply) != 1 { t.Fatalf("expected one psu, got %d", len(result.Hardware.PowerSupply)) } if result.Hardware.PowerSupply[0].WattageW != 2000 { t.Fatalf("unexpected psu wattage: %d", result.Hardware.PowerSupply[0].WattageW) } if len(result.Hardware.Firmware) == 0 { t.Fatalf("expected firmware entries") } if result.RawPayloads == nil { t.Fatalf("expected raw payloads") } treeAny, ok := result.RawPayloads["redfish_tree"] if !ok { t.Fatalf("expected redfish_tree in raw payloads") } tree, ok := treeAny.(map[string]interface{}) if !ok || len(tree) == 0 { t.Fatalf("expected non-empty redfish_tree, got %#v", treeAny) } } func TestRedfishConnectorProbe(t *testing.T) { mux := http.NewServeMux() register := func(path string, payload interface{}) { mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(payload) }) } register("/redfish/v1", map[string]interface{}{"Name": "ServiceRoot"}) register("/redfish/v1/Systems/1", map[string]interface{}{ "@odata.id": "/redfish/v1/Systems/1", "PowerState": "Off", "Actions": map[string]interface{}{ "#ComputerSystem.Reset": map[string]interface{}{ "target": "/redfish/v1/Systems/1/Actions/ComputerSystem.Reset", "ResetType@Redfish.AllowableValues": []interface{}{"On", "ForceOff"}, }, }, }) ts := httptest.NewTLSServer(mux) defer ts.Close() connector := NewRedfishConnector() port := 443 host := "" if u, err := url.Parse(ts.URL); err == nil { host = u.Hostname() if p := u.Port(); p != "" { fmt.Sscanf(p, "%d", &port) } } got, err := connector.Probe(context.Background(), Request{ Host: host, Protocol: "redfish", Port: port, Username: "admin", AuthType: "password", Password: "secret", TLSMode: "insecure", }) if err != nil { t.Fatalf("probe failed: %v", err) } if got == nil || !got.Reachable { t.Fatalf("expected reachable probe result, got %+v", got) } if got.HostPoweredOn { t.Fatalf("expected powered off host") } if got.HostPowerState != "Off" { t.Fatalf("expected power state Off, got %q", got.HostPowerState) } if !got.PowerControlAvailable { t.Fatalf("expected power control available") } } func TestRedfishConnectorProbe_FallsBackToPowerSummary(t *testing.T) { mux := http.NewServeMux() register := func(path string, payload interface{}) { mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(payload) }) } register("/redfish/v1", map[string]interface{}{"Name": "ServiceRoot"}) register("/redfish/v1/Systems", map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"}, }, }) register("/redfish/v1/Systems/1", map[string]interface{}{ "@odata.id": "/redfish/v1/Systems/1", "PowerSummary": map[string]interface{}{ "PowerState": "On", }, "Actions": map[string]interface{}{ "#ComputerSystem.Reset": map[string]interface{}{ "target": "/redfish/v1/Systems/1/Actions/ComputerSystem.Reset", "ResetType@Redfish.AllowableValues": []interface{}{"On", "ForceOff"}, }, }, }) ts := httptest.NewTLSServer(mux) defer ts.Close() connector := NewRedfishConnector() port := 443 host := "" if u, err := url.Parse(ts.URL); err == nil { host = u.Hostname() if p := u.Port(); p != "" { fmt.Sscanf(p, "%d", &port) } } got, err := connector.Probe(context.Background(), Request{ Host: host, Protocol: "redfish", Port: port, Username: "admin", AuthType: "password", Password: "secret", TLSMode: "insecure", }) if err != nil { t.Fatalf("probe failed: %v", err) } if got == nil || !got.Reachable { t.Fatalf("expected reachable probe result, got %+v", got) } if !got.HostPoweredOn { t.Fatalf("expected powered on host from PowerSummary") } if got.HostPowerState != "On" { t.Fatalf("expected power state On, got %q", got.HostPowerState) } if !got.PowerControlAvailable { t.Fatalf("expected power control available") } } func TestEnsureHostPowerForCollection_WaitsForStablePowerOn(t *testing.T) { t.Setenv("LOGPILE_REDFISH_POWERON_STABILIZATION", "1ms") t.Setenv("LOGPILE_REDFISH_BMC_READY_WAITS", "1ms,1ms") powerState := "Off" resetCalls := 0 mux := http.NewServeMux() mux.HandleFunc("/redfish/v1/Systems/1", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(map[string]interface{}{ "@odata.id": "/redfish/v1/Systems/1", "PowerState": powerState, "MemorySummary": map[string]interface{}{ "TotalSystemMemoryGiB": 128, }, "Actions": map[string]interface{}{ "#ComputerSystem.Reset": map[string]interface{}{ "target": "/redfish/v1/Systems/1/Actions/ComputerSystem.Reset", "ResetType@Redfish.AllowableValues": []interface{}{"On"}, }, }, }) }) mux.HandleFunc("/redfish/v1/Systems/1/Actions/ComputerSystem.Reset", func(w http.ResponseWriter, r *http.Request) { resetCalls++ powerState = "On" w.WriteHeader(http.StatusOK) }) ts := httptest.NewTLSServer(mux) defer ts.Close() u, err := url.Parse(ts.URL) if err != nil { t.Fatalf("parse server url: %v", err) } port := 443 if u.Port() != "" { fmt.Sscanf(u.Port(), "%d", &port) } c := NewRedfishConnector() hostOn, changed := c.ensureHostPowerForCollection(context.Background(), c.httpClientWithTimeout(Request{TLSMode: "insecure"}, 5*time.Second), Request{ Host: u.Hostname(), Protocol: "redfish", Port: port, Username: "admin", AuthType: "password", Password: "secret", TLSMode: "insecure", PowerOnIfHostOff: true, }, ts.URL, "/redfish/v1/Systems/1", nil) if !hostOn || !changed { t.Fatalf("expected stable power-on result, got hostOn=%v changed=%v", hostOn, changed) } if resetCalls != 1 { t.Fatalf("expected one reset call, got %d", resetCalls) } } func TestEnsureHostPowerForCollection_FailsIfHostDoesNotStayOnAfterStabilization(t *testing.T) { t.Setenv("LOGPILE_REDFISH_POWERON_STABILIZATION", "1ms") t.Setenv("LOGPILE_REDFISH_BMC_READY_WAITS", "1ms,1ms") powerState := "Off" mux := http.NewServeMux() mux.HandleFunc("/redfish/v1/Systems/1", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") current := powerState if powerState == "On" { powerState = "Off" } _ = json.NewEncoder(w).Encode(map[string]interface{}{ "@odata.id": "/redfish/v1/Systems/1", "PowerState": current, "Actions": map[string]interface{}{ "#ComputerSystem.Reset": map[string]interface{}{ "target": "/redfish/v1/Systems/1/Actions/ComputerSystem.Reset", "ResetType@Redfish.AllowableValues": []interface{}{"On"}, }, }, }) }) mux.HandleFunc("/redfish/v1/Systems/1/Actions/ComputerSystem.Reset", func(w http.ResponseWriter, r *http.Request) { powerState = "On" w.WriteHeader(http.StatusOK) }) ts := httptest.NewTLSServer(mux) defer ts.Close() u, err := url.Parse(ts.URL) if err != nil { t.Fatalf("parse server url: %v", err) } port := 443 if u.Port() != "" { fmt.Sscanf(u.Port(), "%d", &port) } c := NewRedfishConnector() hostOn, changed := c.ensureHostPowerForCollection(context.Background(), c.httpClientWithTimeout(Request{TLSMode: "insecure"}, 5*time.Second), Request{ Host: u.Hostname(), Protocol: "redfish", Port: port, Username: "admin", AuthType: "password", Password: "secret", TLSMode: "insecure", PowerOnIfHostOff: true, }, ts.URL, "/redfish/v1/Systems/1", nil) if hostOn || changed { t.Fatalf("expected unstable power-on result to fail, got hostOn=%v changed=%v", hostOn, changed) } } func TestEnsureHostPowerForCollection_UsesPowerSummaryState(t *testing.T) { t.Setenv("LOGPILE_REDFISH_POWERON_STABILIZATION", "1ms") t.Setenv("LOGPILE_REDFISH_BMC_READY_WAITS", "1ms,1ms") powerState := "On" mux := http.NewServeMux() mux.HandleFunc("/redfish/v1/Systems/1", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(map[string]interface{}{ "@odata.id": "/redfish/v1/Systems/1", "PowerSummary": map[string]interface{}{ "PowerState": powerState, }, "MemorySummary": map[string]interface{}{ "TotalSystemMemoryGiB": 128, }, "Actions": map[string]interface{}{ "#ComputerSystem.Reset": map[string]interface{}{ "target": "/redfish/v1/Systems/1/Actions/ComputerSystem.Reset", "ResetType@Redfish.AllowableValues": []interface{}{"On"}, }, }, }) }) ts := httptest.NewTLSServer(mux) defer ts.Close() u, err := url.Parse(ts.URL) if err != nil { t.Fatalf("parse server url: %v", err) } port := 443 if u.Port() != "" { fmt.Sscanf(u.Port(), "%d", &port) } c := NewRedfishConnector() hostOn, changed := c.ensureHostPowerForCollection(context.Background(), c.httpClientWithTimeout(Request{TLSMode: "insecure"}, 5*time.Second), Request{ Host: u.Hostname(), Protocol: "redfish", Port: port, Username: "admin", AuthType: "password", Password: "secret", TLSMode: "insecure", PowerOnIfHostOff: true, }, ts.URL, "/redfish/v1/Systems/1", nil) if !hostOn || changed { t.Fatalf("expected already-on host from PowerSummary, got hostOn=%v changed=%v", hostOn, changed) } } func TestWaitForHostPowerState_UsesPowerSummaryState(t *testing.T) { powerState := "Off" mux := http.NewServeMux() mux.HandleFunc("/redfish/v1/Systems/1", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") current := powerState if powerState == "Off" { powerState = "On" } _ = json.NewEncoder(w).Encode(map[string]interface{}{ "@odata.id": "/redfish/v1/Systems/1", "PowerSummary": map[string]interface{}{ "PowerState": current, }, }) }) ts := httptest.NewTLSServer(mux) defer ts.Close() u, err := url.Parse(ts.URL) if err != nil { t.Fatalf("parse server url: %v", err) } port := 443 if u.Port() != "" { fmt.Sscanf(u.Port(), "%d", &port) } c := NewRedfishConnector() ok := c.waitForHostPowerState(context.Background(), c.httpClientWithTimeout(Request{TLSMode: "insecure"}, 5*time.Second), Request{ Host: u.Hostname(), Protocol: "redfish", Port: port, Username: "admin", AuthType: "password", Password: "secret", TLSMode: "insecure", }, ts.URL, "/redfish/v1/Systems/1", true, 3*time.Second) if !ok { t.Fatalf("expected waitForHostPowerState to use PowerSummary") } } func TestParsePCIeDeviceSlot_FromNestedRedfishSlotLocation(t *testing.T) { doc := map[string]interface{}{ "Id": "NIC1", "Slot": map[string]interface{}{ "Lanes": 16, "Location": map[string]interface{}{ "PartLocation": map[string]interface{}{ "LocationOrdinalValue": 1, "LocationType": "Slot", "ServiceLabel": "PCIe Slot 1 (1)", }, }, "PCIeType": "Gen5", "SlotType": "FullLength", }, } got := parsePCIeDevice(doc, nil) if got.Slot != "PCIe Slot 1 (1)" { t.Fatalf("unexpected slot: %q", got.Slot) } } func TestParsePCIeDeviceSlot_EmptyMapFallsBackToID(t *testing.T) { doc := map[string]interface{}{ "Id": "NIC42", "Slot": map[string]interface{}{}, } got := parsePCIeDevice(doc, nil) if got.Slot != "NIC42" { t.Fatalf("unexpected slot fallback: %q", got.Slot) } if got.Slot == "map[]" { t.Fatalf("slot should not stringify empty map") } } func TestReplayRedfishFromRawPayloads_FallbackCollectionMembersByPrefix(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": "Supermicro", "Model": "SYS-TEST", "SerialNumber": "SYS123", }, // Intentionally missing /redfish/v1/Systems/1/Processors collection. "/redfish/v1/Systems/1/Processors/CPU1": map[string]interface{}{ "Id": "CPU1", "Model": "Xeon Gold", "TotalCores": 32, "TotalThreads": 64, }, "/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", }, "/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/Processors", "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 len(got.Hardware.CPUs) != 1 { t.Fatalf("expected one CPU via prefix fallback, got %d", len(got.Hardware.CPUs)) } if _, ok := got.RawPayloads["redfish_fetch_errors"]; !ok { t.Fatalf("expected raw payloads to preserve redfish_fetch_errors") } } func TestReplayRedfishFromRawPayloads_FallbackCollectionMembersSkipsPlaceholderNumericDocs(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": "Multillect", "Model": "MLT-S06", "SerialNumber": "430044262001626", "Storage": map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Storage"}, }, "/redfish/v1/Systems/1/Storage": map[string]interface{}{ "@odata.id": "/redfish/v1/Systems/1/Storage", "Members": []interface{}{}, "Members@odata.count": 0, }, "/redfish/v1/Systems/1/Storage/1": map[string]interface{}{ "@odata.id": "/redfish/v1/Systems/1/Storage/1", "@odata.type": "#Storage.v1_7_1.Storage", "Drives": []interface{}{}, "Drives@odata.count": "0", "LogicalDisk": []interface{}{}, "PhysicalDisk": []interface{}{}, }, "/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", "NetworkAdapters": map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/NetworkAdapters"}, "PCIeDevices": map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices"}, }, "/redfish/v1/Chassis/1/NetworkAdapters": map[string]interface{}{ "@odata.id": "/redfish/v1/Chassis/1/NetworkAdapters", "Members": []interface{}{}, "Members@odata.count": 0, }, "/redfish/v1/Chassis/1/NetworkAdapters/1": map[string]interface{}{ "@odata.context": "/redfish/v1/$metadata#Chassis/Members/1/NetworkAdapters/Members/$entity", "@odata.id": "/redfish/v1/Chassis/1/NetworkAdapters/1", "@odata.type": "#NetworkAdapter.v1_0_0.Networkadapter", "Id": "1", "Name": "1", }, "/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{ "@odata.id": "/redfish/v1/Chassis/1/PCIeDevices", "Members": []interface{}{}, "Members@odata.count": 0, }, "/redfish/v1/Chassis/1/PCIeDevices/1": map[string]interface{}{ "@odata.context": "/redfish/v1/$metadata#PCIeDevice.PCIeDevice", "@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/1", "@odata.type": "#PCIeDevice.v1_4_0.PCIeDevice", "Id": "1", "Name": "PCIe Device", }, "/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", }, }, } got, err := ReplayRedfishFromRawPayloads(raw, nil) if err != nil { t.Fatalf("replay failed: %v", err) } if got.Hardware == nil { t.Fatalf("expected hardware") } if len(got.Hardware.NetworkAdapters) != 0 { t.Fatalf("expected placeholder network adapters to be skipped, got %d", len(got.Hardware.NetworkAdapters)) } if len(got.Hardware.PCIeDevices) != 0 { t.Fatalf("expected placeholder PCIe devices to be skipped, got %d", len(got.Hardware.PCIeDevices)) } if len(got.Hardware.Storage) != 0 { t.Fatalf("expected placeholder storage members to be skipped, got %d", len(got.Hardware.Storage)) } } func TestReplayRedfishFromRawPayloads_PrefersActiveBMCInterfaceForBoardMAC(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": "Multillect", "Model": "MLT-S06", "SerialNumber": "430044262001626", }, "/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"}, "/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/v1/Managers/1/EthernetInterfaces": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Managers/1/EthernetInterfaces/eth0"}, map[string]interface{}{"@odata.id": "/redfish/v1/Managers/1/EthernetInterfaces/eth1"}, }, "Oem": map[string]interface{}{ "Public": map[string]interface{}{ "NcsiEnabled": true, "LLDP": map[string]interface{}{ "LLDPMode": "Rx", "Members": []interface{}{ map[string]interface{}{ "EthIndex": "eth1", "ChassisName": "castor.netwell.local", "PortDesc": "ge-0/0/17", "PortId": "531", "VlanId": 20, }, }, }, }, }, }, "/redfish/v1/Managers/1/EthernetInterfaces/eth0": map[string]interface{}{ "Id": "eth0", "MACAddress": "00:25:6c:70:00:13", "LinkStatus": "NoLink", "SpeedMbps": 65535, }, "/redfish/v1/Managers/1/EthernetInterfaces/eth1": map[string]interface{}{ "Id": "eth1", "MACAddress": "00:25:6c:70:00:12", "LinkStatus": "LinkActive", "SpeedMbps": 1000, "IPv4Addresses": []interface{}{ map[string]interface{}{ "Address": "172.16.41.42", "Gateway": "172.16.41.1", "SubnetMask": "255.255.255.0", }, }, }, }, } 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.BMCMACAddress != "00:25:6C:70:00:12" { t.Fatalf("expected active BMC MAC from eth1, got %q", got.Hardware.BoardInfo.BMCMACAddress) } summary, ok := got.RawPayloads["redfish_bmc_network_summary"].(map[string]any) if !ok { t.Fatalf("expected redfish_bmc_network_summary") } if summary["interface_id"] != "eth1" { t.Fatalf("expected eth1 summary, got %#v", summary["interface_id"]) } } func TestReplayRedfishFromRawPayloads_AddsSensorsListHintSummary(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": "Multillect", "Model": "MLT-S06", "SerialNumber": "430044262001626", }, "/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"}, "/redfish/v1/Chassis/1/SensorsList": map[string]interface{}{ "SensorsList": []interface{}{ map[string]interface{}{"SensorName": "DIMM000_Status", "SensorType": "Memory", "Status": "OK"}, map[string]interface{}{"SensorName": "DIMM001_Status", "SensorType": "Memory", "Status": "nop"}, map[string]interface{}{"SensorName": "DIMM100_Status", "SensorType": "Memory", "Status": "OK"}, map[string]interface{}{"SensorName": "HDD0_F_Status", "SensorType": "Drive Slot", "Status": "nop"}, map[string]interface{}{"SensorName": "NVME0_F_Status", "SensorType": "Drive Slot", "Status": "nop"}, map[string]interface{}{"SensorName": "Logical_Drive", "SensorType": "Drive Slot", "Status": "OK"}, }, }, "/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"}, }, } got, err := ReplayRedfishFromRawPayloads(raw, nil) if err != nil { t.Fatalf("replay failed: %v", err) } hints, ok := got.RawPayloads["redfish_sensor_hints"].(map[string]any) if !ok { t.Fatalf("expected redfish_sensor_hints") } memHints, ok := hints["memory_slots"].(map[string]any) if !ok { t.Fatalf("expected memory_slots hint") } if asInt(memHints["present_count"]) != 2 { t.Fatalf("expected 2 present memory slot hints, got %#v", memHints["present_count"]) } driveHints, ok := hints["drive_slots"].(map[string]any) if !ok { t.Fatalf("expected drive_slots hint") } if asInt(driveHints["physical_total"]) != 2 { t.Fatalf("expected 2 physical drive slots, got %#v", driveHints["physical_total"]) } if driveHints["logical_drive_status"] != "OK" { t.Fatalf("expected logical drive status OK, got %#v", driveHints["logical_drive_status"]) } foundMemoryEvent := false foundDriveEvent := false for _, ev := range got.Events { if strings.Contains(ev.Description, "Memory slot sensors report 2 populated positions out of 3") { foundMemoryEvent = true } if strings.Contains(ev.Description, "Drive slot sensors report 0 active physical slots out of 2") { foundDriveEvent = true } } if !foundMemoryEvent { t.Fatalf("expected memory slot hint event") } if !foundDriveEvent { t.Fatalf("expected drive slot hint event") } } func TestReplayRedfishFromRawPayloads_PreservesSourceTimezoneAndUTCCollectedAt(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": "Inspur", "Model": "NF5688M7", "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"}, "/redfish/v1/Managers": map[string]interface{}{ "Members": []interface{}{map[string]interface{}{"@odata.id": "/redfish/v1/Managers/1"}}, }, "/redfish/v1/Managers/1": map[string]interface{}{ "DateTime": "2026-02-28T04:18:18+08:00", "DateTimeLocalOffset": "+08:00", }, }, } got, err := ReplayRedfishFromRawPayloads(raw, nil) if err != nil { t.Fatalf("replay failed: %v", err) } if got.SourceTimezone != "+08:00" { t.Fatalf("expected source_timezone +08:00, got %q", got.SourceTimezone) } wantCollectedAt := time.Date(2026, 2, 27, 20, 18, 18, 0, time.UTC) if !got.CollectedAt.Equal(wantCollectedAt) { t.Fatalf("expected collected_at %s, got %s", wantCollectedAt, got.CollectedAt) } if got.RawPayloads["source_timezone"] != "+08:00" { t.Fatalf("expected source_timezone in raw payloads, got %#v", got.RawPayloads["source_timezone"]) } } func TestReplayRedfishFromRawPayloads_ParsesInlineThresholdAndDiscreteSensors(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{}{ "Id": "1", "Manufacturer": "Inspur", "Model": "NF5688M7", "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", }, "/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/v1/Chassis/1/ThresholdSensors": map[string]interface{}{ "Sensors": []interface{}{ map[string]interface{}{ "Name": "Inlet_Temp", "Reading": 16, "ReadingUnits": "deg_c", "State": "Enabled", }, }, }, "/redfish/v1/Chassis/1/DiscreteSensors": map[string]interface{}{ "Sensors": []interface{}{ map[string]interface{}{ "Name": "PSU_Redundant", "State": "Disabled", }, }, }, }, } got, err := ReplayRedfishFromRawPayloads(raw, nil) if err != nil { t.Fatalf("replay failed: %v", err) } if len(got.Sensors) == 0 { t.Fatalf("expected sensors from inline ThresholdSensors") } foundSensor := false for _, s := range got.Sensors { if s.Name == "Inlet_Temp" { foundSensor = true break } } if !foundSensor { t.Fatalf("expected Inlet_Temp sensor in replay output") } foundEvent := false for _, ev := range got.Events { if ev.EventType == "Discrete Sensor Status" && ev.SensorName == "PSU_Redundant" { foundEvent = true break } } if !foundEvent { t.Fatalf("expected discrete sensor warning event from inline DiscreteSensors") } } func TestReplayRedfishFromRawPayloads_CollectsThermalAndPowerSensors(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{}{ "Id": "1", "Manufacturer": "Inspur", "Model": "NF5688M7", "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", }, "/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/v1/Chassis/1/Thermal": map[string]interface{}{ "Fans": []interface{}{ map[string]interface{}{ "Name": "FAN0_F_Speed", "Reading": 9279, "ReadingUnits": "RPM", "Status": map[string]interface{}{ "Health": "OK", "State": "Enabled", }, }, }, "Temperatures": []interface{}{ map[string]interface{}{ "Name": "CPU0_Temp", "ReadingCelsius": 44, "Status": map[string]interface{}{ "Health": "OK", "State": "Enabled", }, }, }, }, "/redfish/v1/Chassis/1/Power": map[string]interface{}{ "Oem": map[string]interface{}{ "Public": map[string]interface{}{ "TotalPower": 1836, "CurrentCPUPowerWatts": 304, "CurrentMemoryPowerWatts": 75, "CurrentFANPowerWatts": 180, }, }, "PowerControl": []interface{}{ map[string]interface{}{ "Name": "System Power Control 1", "PowerConsumedWatts": 1836, "Status": map[string]interface{}{ "Health": "OK", "State": "Enabled", }, }, }, "PowerSupplies": []interface{}{ map[string]interface{}{ "Name": "Power Supply 1", "PowerInputWatts": 180, "LastPowerOutputWatts": 155, "LineInputVoltage": 223.25, "Status": map[string]interface{}{ "Health": "OK", "State": "Enabled", }, }, }, }, }, } got, err := ReplayRedfishFromRawPayloads(raw, nil) if err != nil { t.Fatalf("replay failed: %v", err) } if len(got.Sensors) == 0 { t.Fatalf("expected non-empty sensors") } expected := map[string]bool{ "FAN0_F_Speed": false, "CPU0_Temp": false, "Total_Power": false, "System Power Control 1_Consumed": false, "Power Supply 1_InputPower": false, } for _, s := range got.Sensors { if _, ok := expected[s.Name]; ok { expected[s.Name] = true } } for name, found := range expected { if !found { t.Fatalf("expected sensor %q in replay output", name) } } } func TestEnrichNICFromPCIeFunctions(t *testing.T) { nic := parseNIC(map[string]interface{}{ "Id": "1", "Model": "MCX75310AAS-NEAT", "Manufacturer": "Supermicro", "SerialNumber": "NIC-SN-1", "Controllers": []interface{}{ map[string]interface{}{ "Links": map[string]interface{}{ "PCIeDevices": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/NIC1"}, }, }, "Location": map[string]interface{}{ "PartLocation": map[string]interface{}{"ServiceLabel": "PCIe Slot 1 (1)"}, }, }, }, }) pcieDoc := map[string]interface{}{ "Id": "NIC1", "PCIeFunctions": map[string]interface{}{ "@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/NIC1/PCIeFunctions", }, } functionDocs := []map[string]interface{}{ { "FunctionId": "0000:17:00.0", "VendorId": "0x15b3", "DeviceId": "0x1021", "CurrentLinkWidth": 16, "CurrentLinkSpeedGTs": "32 GT/s", "MaxLinkWidth": 16, "MaxLinkSpeedGTs": "32 GT/s", }, } enrichNICFromPCIe(&nic, pcieDoc, functionDocs, nil) if nic.VendorID != 0x15b3 || nic.DeviceID != 0x1021 { t.Fatalf("unexpected NIC IDs: vendor=%#x device=%#x", nic.VendorID, nic.DeviceID) } if nic.Location != "PCIe Slot 1 (1)" { t.Fatalf("unexpected NIC location: %q", nic.Location) } if nic.BDF != "0000:17:00.0" { t.Fatalf("unexpected NIC BDF: %q", nic.BDF) } if nic.LinkWidth != 16 || nic.MaxLinkWidth != 16 { t.Fatalf("unexpected NIC link width state: current=%d max=%d", nic.LinkWidth, nic.MaxLinkWidth) } if nic.LinkSpeed != "32 GT/s" || nic.MaxLinkSpeed != "32 GT/s" { t.Fatalf("unexpected NIC link speed state: current=%q max=%q", nic.LinkSpeed, nic.MaxLinkSpeed) } } func TestParseNIC_PortCountFromControllerCapabilities(t *testing.T) { nic := parseNIC(map[string]interface{}{ "Id": "1", "Controllers": []interface{}{ map[string]interface{}{ "ControllerCapabilities": map[string]interface{}{ "NetworkPortCount": 2, }, }, }, }) if nic.PortCount != 2 { t.Fatalf("expected port_count=2, got %d", nic.PortCount) } } func TestParseNIC_PrefersControllerSlotLabelAndPCIeInterface(t *testing.T) { nic := parseNIC(map[string]interface{}{ "Id": "1", "Model": "MCX75310AAS-NEAT", "Manufacturer": "Supermicro", "Controllers": []interface{}{ map[string]interface{}{ "Location": map[string]interface{}{ "PartLocation": map[string]interface{}{ "ServiceLabel": "PCIe Slot 1 (1)", }, }, "PCIeInterface": map[string]interface{}{ "LanesInUse": 16, "MaxLanes": 16, "PCIeType": "Gen5", "MaxPCIeType": "Gen5", }, }, }, }) if nic.Slot != "PCIe Slot 1 (1)" { t.Fatalf("expected slot from controller location, got %q", nic.Slot) } if nic.Location != "PCIe Slot 1 (1)" { t.Fatalf("expected location from controller location, got %q", nic.Location) } if nic.LinkWidth != 16 || nic.MaxLinkWidth != 16 { t.Fatalf("expected link widths from controller PCIeInterface, got current=%d max=%d", nic.LinkWidth, nic.MaxLinkWidth) } if nic.LinkSpeed != "Gen5" || nic.MaxLinkSpeed != "Gen5" { t.Fatalf("expected link speeds from controller PCIeInterface, got current=%q max=%q", nic.LinkSpeed, nic.MaxLinkSpeed) } } func TestParseNIC_DropsUnrealisticPortCount(t *testing.T) { nic := parseNIC(map[string]interface{}{ "Id": "1", "Controllers": []interface{}{ map[string]interface{}{ "ControllerCapabilities": map[string]interface{}{ "NetworkPortCount": 825307750, }, }, }, }) if nic.PortCount != 0 { t.Fatalf("expected unrealistic port count to be dropped, got %d", nic.PortCount) } } func TestParsePCIeDevice_PrefersFunctionClassOverDeviceType(t *testing.T) { doc := map[string]interface{}{ "Id": "NIC1", "DeviceType": "SingleFunction", "Model": "MCX75310AAS-NEAT", "PartNumber": "MCX75310AAS-NEAT", } functionDocs := []map[string]interface{}{ { "DeviceClass": "NetworkController", "VendorId": "0x15b3", "DeviceId": "0x1021", }, } got := parsePCIeDevice(doc, functionDocs) if got.DeviceClass == "SingleFunction" { t.Fatalf("device class should not keep generic redfish DeviceType") } if got.DeviceClass == "" { t.Fatalf("device class should be resolved") } } func TestParsePCIeComponents_DoNotTreatNumericFunctionIDAsBDF(t *testing.T) { pcieFn := parsePCIeFunction(map[string]interface{}{ "Id": "1", "FunctionId": "1", "DeviceClass": "NetworkController", "VendorId": "0x15b3", "DeviceId": "0x1021", }, 1) if pcieFn.BDF != "" { t.Fatalf("expected empty BDF for numeric FunctionId, got %q", pcieFn.BDF) } gpu := parseGPU(map[string]interface{}{ "Id": "GPU1", "Name": "GPU1", "PCIeFunctions": map[string]interface{}{ "@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions", }, }, []map[string]interface{}{ { "FunctionId": "1", "VendorId": "0x10de", "DeviceId": "0x2331", }, }, 1) if gpu.BDF != "" { t.Fatalf("expected GPU BDF to stay empty when only numeric FunctionId exists, got %q", gpu.BDF) } nic := parseNIC(map[string]interface{}{"Id": "1"}) enrichNICFromPCIe(&nic, map[string]interface{}{}, []map[string]interface{}{ { "FunctionId": "1", "VendorId": "0x15b3", "DeviceId": "0x1021", }, }, nil) if nic.BDF != "" { t.Fatalf("expected NIC BDF to stay empty when only numeric FunctionId exists, got %q", nic.BDF) } } func TestParseComponents_UseNestedSerialNumberFallback(t *testing.T) { doc := map[string]interface{}{ "Name": "dev0", "Id": "dev0", "Model": "model0", "Manufacturer": "vendor0", "SerialNumber": "N/A", "Oem": map[string]interface{}{ "VendorX": map[string]interface{}{ "SerialNumber": "SN-OK-001", }, }, } cpus := parseCPUs([]map[string]interface{}{doc}) if len(cpus) != 1 || cpus[0].SerialNumber != "SN-OK-001" { t.Fatalf("expected CPU serial fallback, got %+v", cpus) } dimms := parseMemory([]map[string]interface{}{doc}) if len(dimms) != 1 || dimms[0].SerialNumber != "SN-OK-001" { t.Fatalf("expected DIMM serial fallback, got %+v", dimms) } drive := parseDrive(doc) if drive.SerialNumber != "SN-OK-001" { t.Fatalf("expected drive serial fallback, got %q", drive.SerialNumber) } nic := parseNIC(doc) if nic.SerialNumber != "SN-OK-001" { t.Fatalf("expected NIC serial fallback, got %q", nic.SerialNumber) } psu := parsePSU(doc, 1) if psu.SerialNumber != "SN-OK-001" { t.Fatalf("expected PSU serial fallback, got %q", psu.SerialNumber) } pcie := parsePCIeDevice(doc, nil) if pcie.SerialNumber != "SN-OK-001" { t.Fatalf("expected PCIe device serial fallback, got %q", pcie.SerialNumber) } pcieFn := parsePCIeFunction(doc, 1) if pcieFn.SerialNumber != "SN-OK-001" { t.Fatalf("expected PCIe function serial fallback, got %q", pcieFn.SerialNumber) } } func TestParseCPU_UsesPublicSerialAsPPINAndCurrentSpeedMHz(t *testing.T) { cpus := parseCPUs([]map[string]interface{}{ { "Id": "CPU0", "Model": "Intel Xeon", "TotalCores": 48, "TotalThreads": 96, "MaxSpeedMHz": 4000, "OperatingSpeedMHz": 0, "Oem": map[string]interface{}{ "Public": map[string]interface{}{ "SerialNumber": "6FB5241E81CECDFD", "CurrentSpeedMHz": 2700, }, }, }, }) if len(cpus) != 1 { t.Fatalf("expected one CPU, got %d", len(cpus)) } if cpus[0].PPIN != "6FB5241E81CECDFD" { t.Fatalf("expected PPIN from Oem.Public.SerialNumber, got %+v", cpus[0]) } if cpus[0].SerialNumber != "" { t.Fatalf("expected empty CPU serial number when only Public serial exists, got %+v", cpus[0]) } if cpus[0].FrequencyMHz != 2700 { t.Fatalf("expected CPU frequency from Oem.Public.CurrentSpeedMHz, got %+v", cpus[0]) } } func TestParseCPUAndMemory_CollectOemDetails(t *testing.T) { cpus := parseCPUs([]map[string]interface{}{ { "Id": "CPU0", "Model": "Intel Xeon", "CorrectableErrors": 7, "TemperatureCelsius": 63, "Oem": map[string]interface{}{ "VendorX": map[string]interface{}{ "MicrocodeVersion": "0x2b000643", "UncorrectableErrors": 1, "ThermalThrottled": true, }, }, }, }) if len(cpus) != 1 || cpus[0].Details == nil { t.Fatalf("expected CPU details, got %+v", cpus) } if cpus[0].Details["microcode"] != "0x2b000643" { t.Fatalf("expected CPU microcode detail, got %#v", cpus[0].Details) } if cpus[0].Details["correctable_error_count"] != int64(7) || cpus[0].Details["uncorrectable_error_count"] != int64(1) { t.Fatalf("expected CPU error counters, got %#v", cpus[0].Details) } if cpus[0].Details["throttled"] != true || cpus[0].Details["temperature_c"] != 63.0 { t.Fatalf("expected CPU thermal details, got %#v", cpus[0].Details) } dimms := parseMemory([]map[string]interface{}{ { "Id": "DIMM0", "DeviceLocator": "CPU0_C0D0", "CapacityMiB": 32768, "SerialNumber": "DIMM-001", "Oem": map[string]interface{}{ "VendorX": map[string]interface{}{ "CorrectableECCErrorCount": 12, "UncorrectableECCErrorCount": 2, "TemperatureC": 41.5, "SpareBlocksRemainingPercent": 88, "PerformanceDegraded": true, "DataLossDetected": false, }, }, }, }) if len(dimms) != 1 || dimms[0].Details == nil { t.Fatalf("expected DIMM details, got %+v", dimms) } if dimms[0].Details["correctable_ecc_error_count"] != int64(12) || dimms[0].Details["uncorrectable_ecc_error_count"] != int64(2) { t.Fatalf("expected DIMM ECC counters, got %#v", dimms[0].Details) } if dimms[0].Details["temperature_c"] != 41.5 || dimms[0].Details["spare_blocks_remaining_pct"] != 88.0 { t.Fatalf("expected DIMM telemetry details, got %#v", dimms[0].Details) } if dimms[0].Details["performance_degraded"] != true || dimms[0].Details["data_loss_detected"] != false { t.Fatalf("expected DIMM boolean health details, got %#v", dimms[0].Details) } } func TestReplayRedfishFromRawPayloads_UsesProcessorAndMemoryMetrics(t *testing.T) { rawPayloads := map[string]any{ "redfish_tree": map[string]interface{}{ "/redfish/v1": map[string]interface{}{}, "/redfish/v1/Systems": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"}, }, }, "/redfish/v1/Systems/1": map[string]interface{}{ "Id": "1", "Processors": map[string]interface{}{ "@odata.id": "/redfish/v1/Systems/1/Processors", }, "Memory": map[string]interface{}{ "@odata.id": "/redfish/v1/Systems/1/Memory", }, }, "/redfish/v1/Systems/1/Processors": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Processors/CPU0"}, }, }, "/redfish/v1/Systems/1/Processors/CPU0": map[string]interface{}{ "Id": "CPU0", "ProcessorType": "CPU", "Model": "Intel Xeon", "ProcessorMetrics": map[string]interface{}{ "@odata.id": "/redfish/v1/Systems/1/Processors/CPU0/ProcessorMetrics", }, }, "/redfish/v1/Systems/1/Processors/CPU0/ProcessorMetrics": map[string]interface{}{ "CorrectableErrors": 10, "ThermalThrottled": true, "MicrocodeVersion": "0x2b000643", "TemperatureCelsius": 66, }, "/redfish/v1/Systems/1/Memory": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Memory/DIMM0"}, }, }, "/redfish/v1/Systems/1/Memory/DIMM0": map[string]interface{}{ "Id": "DIMM0", "DeviceLocator": "CPU0_C0D0", "CapacityMiB": 32768, "SerialNumber": "DIMM-001", "MemoryMetrics": map[string]interface{}{ "@odata.id": "/redfish/v1/Systems/1/Memory/DIMM0/MemoryMetrics", }, }, "/redfish/v1/Systems/1/Memory/DIMM0/MemoryMetrics": map[string]interface{}{ "CorrectableECCErrorCount": 14, "TemperatureCelsius": 42, "PerformanceDegraded": true, "SpareBlocksRemainingPercent": 91, }, }, } result, err := ReplayRedfishFromRawPayloads(rawPayloads, nil) if err != nil { t.Fatalf("ReplayRedfishFromRawPayloads() failed: %v", err) } if len(result.Hardware.CPUs) != 1 || result.Hardware.CPUs[0].Details == nil { t.Fatalf("expected CPU details from replay metrics, got %+v", result.Hardware.CPUs) } if result.Hardware.CPUs[0].Details["correctable_error_count"] != int64(10) || result.Hardware.CPUs[0].Details["microcode"] != "0x2b000643" { t.Fatalf("expected CPU replay metrics details, got %#v", result.Hardware.CPUs[0].Details) } if len(result.Hardware.Memory) != 1 || result.Hardware.Memory[0].Details == nil { t.Fatalf("expected memory details from replay metrics, got %+v", result.Hardware.Memory) } if result.Hardware.Memory[0].Details["correctable_ecc_error_count"] != int64(14) || result.Hardware.Memory[0].Details["performance_degraded"] != true { t.Fatalf("expected DIMM replay metrics details, got %#v", result.Hardware.Memory[0].Details) } } func TestReplayRedfishFromRawPayloads_UsesDriveMetrics(t *testing.T) { rawPayloads := map[string]any{ "redfish_tree": map[string]interface{}{ "/redfish/v1": map[string]interface{}{}, "/redfish/v1/Systems": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"}, }, }, "/redfish/v1/Systems/1": map[string]interface{}{ "Id": "1", "Storage": map[string]interface{}{ "@odata.id": "/redfish/v1/Systems/1/Storage", }, }, "/redfish/v1/Systems/1/Storage": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Storage/RAID1"}, }, }, "/redfish/v1/Systems/1/Storage/RAID1": map[string]interface{}{ "Id": "RAID1", "Drives": map[string]interface{}{ "@odata.id": "/redfish/v1/Systems/1/Storage/RAID1/Drives", }, }, "/redfish/v1/Systems/1/Storage/RAID1/Drives": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Storage/RAID1/Drives/Drive0"}, }, }, "/redfish/v1/Systems/1/Storage/RAID1/Drives/Drive0": map[string]interface{}{ "Id": "Drive0", "Model": "NVMe SSD", "SerialNumber": "SSD-001", "DriveMetrics": map[string]interface{}{ "@odata.id": "/redfish/v1/Systems/1/Storage/RAID1/Drives/Drive0/DriveMetrics", }, }, "/redfish/v1/Systems/1/Storage/RAID1/Drives/Drive0/DriveMetrics": map[string]interface{}{ "PowerOnHours": 1001, "MediaErrors": 3, "AvailableSparePercent": 92, "TemperatureCelsius": 37, }, }, } result, err := ReplayRedfishFromRawPayloads(rawPayloads, nil) if err != nil { t.Fatalf("ReplayRedfishFromRawPayloads() failed: %v", err) } if len(result.Hardware.Storage) != 1 || result.Hardware.Storage[0].Details == nil { t.Fatalf("expected storage details from replay drive metrics, got %+v", result.Hardware.Storage) } if result.Hardware.Storage[0].Details["power_on_hours"] != int64(1001) || result.Hardware.Storage[0].Details["media_errors"] != int64(3) { t.Fatalf("expected drive metrics counters, got %#v", result.Hardware.Storage[0].Details) } if result.Hardware.Storage[0].Details["available_spare_pct"] != 92.0 || result.Hardware.Storage[0].Details["temperature_c"] != 37.0 { t.Fatalf("expected drive metrics telemetry, got %#v", result.Hardware.Storage[0].Details) } } func TestRedfishCollectionMemberRefs_IncludesOemPublicMembers(t *testing.T) { collection := map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/Drives/OB01"}, }, "Oem": map[string]interface{}{ "Public": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/Drives/FP00HDD00"}, map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/Drives/FP00HDD02"}, }, }, }, } got := redfishCollectionMemberRefs(collection) if len(got) != 3 { t.Fatalf("expected 3 member refs, got %d: %v", len(got), got) } } func TestParseDriveAndPSU_CollectComponentMetricsIntoDetails(t *testing.T) { drive := parseDrive(map[string]interface{}{ "Id": "Drive0", "Model": "NVMe SSD", "SerialNumber": "SSD-001", "TemperatureCelsius": 38.5, "PowerOnHours": 12450, "UnsafeShutdowns": 3, "PredictedMediaLifeLeftPercent": 91, "Oem": map[string]interface{}{ "Public": map[string]interface{}{ "AvailableSparePercent": 87, }, }, }) if drive.Details == nil { t.Fatalf("expected drive details to be populated") } if got := drive.Details["temperature_c"]; got != 38.5 { t.Fatalf("expected drive temperature detail 38.5, got %#v", got) } if got := drive.Details["power_on_hours"]; got != int64(12450) { t.Fatalf("expected drive power_on_hours detail, got %#v", got) } if got := drive.Details["life_remaining_pct"]; got != 91.0 { t.Fatalf("expected drive life_remaining_pct detail, got %#v", got) } if got := drive.Details["available_spare_pct"]; got != 87.0 { t.Fatalf("expected drive available_spare_pct detail from Oem/Public, got %#v", got) } driveOEM := parseDrive(map[string]interface{}{ "Id": "Drive1", "Model": "NVMe SSD 2", "SerialNumber": "SSD-002", "Oem": map[string]interface{}{ "Public": map[string]interface{}{ "temperature": 19, "PercentAvailableSpare": 93, "PercentageUsed": 7, }, }, }) if driveOEM.Details == nil { t.Fatalf("expected oem drive details to be populated") } if got := driveOEM.Details["temperature_c"]; got != 19.0 { t.Fatalf("expected lowercase OEM drive temperature 19, got %#v", got) } if got := driveOEM.Details["available_spare_pct"]; got != 93.0 { t.Fatalf("expected OEM available_spare_pct 93, got %#v", got) } if got := driveOEM.Details["life_used_pct"]; got != 7.0 { t.Fatalf("expected OEM life_used_pct 7, got %#v", got) } psu := parsePSU(map[string]interface{}{ "MemberId": "PSU0", "SerialNumber": "PSU-001", "TemperatureCelsius": 41, "Oem": map[string]interface{}{ "Public": map[string]interface{}{ "LifeRemainingPercent": 96, }, }, }, 1) if psu.Details == nil { t.Fatalf("expected psu details to be populated") } if got := psu.Details["temperature_c"]; got != 41.0 { t.Fatalf("expected psu temperature detail 41, got %#v", got) } if got := psu.Details["life_remaining_pct"]; got != 96.0 { t.Fatalf("expected psu life_remaining_pct detail, got %#v", got) } } func TestParseGPUPCIeAndNIC_CollectComponentMetricsIntoDetails(t *testing.T) { functionDocs := []map[string]interface{}{ { "FunctionId": "0000:17:00.0", "VendorId": "0x10de", "DeviceId": "0x2331", "TemperatureCelsius": 48.5, "PowerConsumedWatts": 315.0, "ECCCorrectedTotal": 12, "BatteryHealthPercent": 87, "SFPTemperatureCelsius": 36.2, }, } gpu := parseGPU(map[string]interface{}{ "Id": "GPU0", "Model": "NVIDIA H100", "Manufacturer": "NVIDIA", }, functionDocs, 1) if gpu.Details == nil || gpu.Details["temperature_c"] != 48.5 || gpu.Details["power_w"] != 315.0 { t.Fatalf("expected gpu details from function docs, got %#v", gpu.Details) } pcie := parsePCIeDevice(map[string]interface{}{ "Id": "NIC1", }, []map[string]interface{}{ { "FunctionId": "0000:18:00.0", "VendorId": "0x15b3", "DeviceId": "0x1021", "SFPTXPowerDBm": -1.8, "SFPRXPowerDBm": -2.1, "SFPBiasMA": 5.5, "BatteryReplaceRequired": true, }, }) if pcie.Details == nil || pcie.Details["sfp_tx_power_dbm"] != -1.8 || pcie.Details["battery_replace_required"] != true { t.Fatalf("expected pcie details from function docs, got %#v", pcie.Details) } nic := parseNIC(map[string]interface{}{"Id": "1"}) enrichNICFromPCIe(&nic, map[string]interface{}{}, []map[string]interface{}{ { "FunctionId": "0000:19:00.0", "SFPTemperatureCelsius": 34.0, }, }, nil) if nic.Details == nil || nic.Details["sfp_temperature_c"] != 34.0 { t.Fatalf("expected nic details from linked pcie function, got %#v", nic.Details) } } func TestParseComponentDetails_UseLinkedSupplementalMetrics(t *testing.T) { drive := parseDriveWithSupplementalDocs( map[string]interface{}{ "Id": "Drive0", "SerialNumber": "SSD-001", }, map[string]interface{}{ "PowerOnHours": 5001, "MediaErrors": 2, "TemperatureC": 39.5, "LifeUsedPercent": 9, }, ) if drive.Details == nil || drive.Details["power_on_hours"] != int64(5001) || drive.Details["temperature_c"] != 39.5 { t.Fatalf("expected drive details from supplemental metrics, got %#v", drive.Details) } psu := parsePSUWithSupplementalDocs( map[string]interface{}{ "MemberId": "PSU0", "SerialNumber": "PSU-001", }, 1, map[string]interface{}{ "Temperature": 44, "LifeRemainingPercent": 97, }, ) if psu.Details == nil || psu.Details["temperature_c"] != 44.0 || psu.Details["life_remaining_pct"] != 97.0 { t.Fatalf("expected psu details from supplemental metrics, got %#v", psu.Details) } gpu := parseGPUWithSupplementalDocs( map[string]interface{}{ "Id": "GPU0", "Model": "NVIDIA H100", "Manufacturer": "NVIDIA", }, nil, []map[string]interface{}{ { "PowerConsumptionWatts": 305.0, "HWSlowdown": true, }, }, 1, ) if gpu.Details == nil || gpu.Details["power_w"] != 305.0 || gpu.Details["hw_slowdown"] != true { t.Fatalf("expected gpu details from supplemental metrics, got %#v", gpu.Details) } } func TestRecoverCriticalRedfishDocsPlanB_RetriesMembersFromExistingCollection(t *testing.T) { t.Setenv("LOGPILE_REDFISH_CRITICAL_COOLDOWN", "0s") t.Setenv("LOGPILE_REDFISH_CRITICAL_SLOW_GAP", "0s") t.Setenv("LOGPILE_REDFISH_CRITICAL_PLANB_RETRIES", "1") t.Setenv("LOGPILE_REDFISH_CRITICAL_RETRIES", "1") t.Setenv("LOGPILE_REDFISH_CRITICAL_BACKOFF", "0s") const memberPath = "/redfish/v1/Chassis/1/Drives/FP00HDD00" mux := http.NewServeMux() mux.HandleFunc(memberPath, func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(map[string]interface{}{ "Id": "FP00HDD00", "Name": "FP00HDD00", "Model": "HDD-TEST", "MediaType": "HDD", "Protocol": "SAS", "CapacityBytes": int64(2000398934016), "SerialNumber": "HDD-SN-001", }) }) ts := httptest.NewServer(mux) defer ts.Close() rawTree := map[string]interface{}{ "/redfish/v1/Chassis/1/Drives": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/Drives/OB01"}, }, "Oem": map[string]interface{}{ "Public": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": memberPath}, }, }, }, }, } fetchErrs := map[string]string{ memberPath: "Get \"https://example/redfish/v1/Chassis/1/Drives/FP00HDD00\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", } c := NewRedfishConnector() recovered := c.recoverCriticalRedfishDocsPlanB( context.Background(), ts.Client(), Request{}, ts.URL, []string{"/redfish/v1/Chassis/1/Drives"}, rawTree, fetchErrs, redfishprofile.AcquisitionTuning{ RecoveryPolicy: redfishprofile.AcquisitionRecoveryPolicy{ EnableCriticalCollectionMemberRetry: true, EnableCriticalSlowProbe: true, }, }, nil, ) if recovered == 0 { t.Fatalf("expected plan-B to recover at least one document") } if _, ok := rawTree[memberPath]; !ok { t.Fatalf("expected recovered member doc for %s", memberPath) } if _, ok := fetchErrs[memberPath]; ok { t.Fatalf("expected fetch error for %s to be cleared after recovery", memberPath) } } func TestRecoverCriticalRedfishDocsPlanB_RetriesMembersFromSystemMemoryCollection(t *testing.T) { t.Setenv("LOGPILE_REDFISH_CRITICAL_COOLDOWN", "0s") t.Setenv("LOGPILE_REDFISH_CRITICAL_SLOW_GAP", "0s") t.Setenv("LOGPILE_REDFISH_CRITICAL_PLANB_RETRIES", "1") t.Setenv("LOGPILE_REDFISH_CRITICAL_RETRIES", "1") t.Setenv("LOGPILE_REDFISH_CRITICAL_BACKOFF", "0s") const systemPath = "/redfish/v1/Systems/1" const memoryPath = "/redfish/v1/Systems/1/Memory" const dimmPath = "/redfish/v1/Systems/1/Memory/CPU1_C1D1" mux := http.NewServeMux() mux.HandleFunc(dimmPath, func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(map[string]interface{}{ "Id": "CPU1_C1D1", "Name": "CPU1_C1D1", "DeviceLocator": "CPU1_C1D1", "CapacityMiB": 65536, "MemoryDeviceType": "DDR5", "Status": map[string]interface{}{"State": "Enabled", "Health": "OK"}, "SerialNumber": "DIMM-SN-001", "PartNumber": "DIMM-PN-001", }) }) ts := httptest.NewServer(mux) defer ts.Close() rawTree := map[string]interface{}{ memoryPath: map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": dimmPath}, }, }, } fetchErrs := map[string]string{ dimmPath: `Get "https://example/redfish/v1/Systems/1/Memory/CPU1_C1D1": context deadline exceeded (Client.Timeout exceeded while awaiting headers)`, } plan := redfishprofile.BuildAcquisitionPlan(redfishprofile.MatchSignals{}) match := redfishprofile.MatchProfiles(redfishprofile.MatchSignals{}) resolved := redfishprofile.ResolveAcquisitionPlan(match, plan, redfishprofile.DiscoveredResources{ SystemPaths: []string{systemPath}, }, redfishprofile.MatchSignals{}) criticalPaths := resolved.CriticalPaths hasMemoryPath := false for _, p := range criticalPaths { if p == memoryPath { hasMemoryPath = true break } } if !hasMemoryPath { t.Fatalf("expected critical endpoints to include %s", memoryPath) } c := NewRedfishConnector() recovered := c.recoverCriticalRedfishDocsPlanB( context.Background(), ts.Client(), Request{}, ts.URL, criticalPaths, rawTree, fetchErrs, redfishprofile.AcquisitionTuning{ RecoveryPolicy: redfishprofile.AcquisitionRecoveryPolicy{ EnableCriticalCollectionMemberRetry: true, EnableCriticalSlowProbe: true, }, }, nil, ) if recovered == 0 { t.Fatalf("expected plan-B to recover at least one DIMM document") } if _, ok := rawTree[dimmPath]; !ok { t.Fatalf("expected recovered DIMM doc for %s", dimmPath) } if _, ok := fetchErrs[dimmPath]; ok { t.Fatalf("expected DIMM fetch error for %s to be cleared", dimmPath) } } func TestRecoverCriticalRedfishDocsPlanB_SkipsMemberRetryWithoutRecoveryPolicy(t *testing.T) { t.Setenv("LOGPILE_REDFISH_CRITICAL_COOLDOWN", "0s") t.Setenv("LOGPILE_REDFISH_CRITICAL_SLOW_GAP", "0s") t.Setenv("LOGPILE_REDFISH_CRITICAL_PLANB_RETRIES", "1") t.Setenv("LOGPILE_REDFISH_CRITICAL_RETRIES", "1") t.Setenv("LOGPILE_REDFISH_CRITICAL_BACKOFF", "0s") const memoryPath = "/redfish/v1/Systems/1/Memory" const dimmPath = "/redfish/v1/Systems/1/Memory/CPU1_C1D1" rawTree := map[string]interface{}{ memoryPath: map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": dimmPath}, }, }, } fetchErrs := map[string]string{ dimmPath: `Get "https://example/redfish/v1/Systems/1/Memory/CPU1_C1D1": context deadline exceeded (Client.Timeout exceeded while awaiting headers)`, } c := NewRedfishConnector() recovered := c.recoverCriticalRedfishDocsPlanB( context.Background(), http.DefaultClient, Request{}, "https://example", []string{memoryPath}, rawTree, fetchErrs, redfishprofile.AcquisitionTuning{}, nil, ) if recovered != 0 { t.Fatalf("expected no recovery without recovery policy, got %d", recovered) } if _, ok := rawTree[dimmPath]; ok { t.Fatalf("did not expect recovered DIMM doc for %s", dimmPath) } if _, ok := fetchErrs[dimmPath]; !ok { t.Fatalf("expected DIMM fetch error for %s to remain", dimmPath) } } func TestReplayCollectStorage_ProbesSupermicroNVMeDiskBayWhenCollectionEmpty(t *testing.T) { r := redfishSnapshotReader{tree: map[string]interface{}{ "/redfish/v1/Systems": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"}, }, }, "/redfish/v1/Systems/1/Storage": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Storage/NVMeSSD"}, }, }, "/redfish/v1/Systems/1/Storage/NVMeSSD": map[string]interface{}{ "Drives": map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Storage/NVMeSSD/Drives"}, }, "/redfish/v1/Systems/1/Storage/NVMeSSD/Drives": map[string]interface{}{ "Members": []interface{}{}, }, "/redfish/v1/Chassis": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane"}, }, }, "/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane": map[string]interface{}{ "Drives": map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane/Drives"}, }, "/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane/Drives": map[string]interface{}{ "Members@odata.count": 0, "Members": []interface{}{}, }, "/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane/Drives/Disk.Bay.0": map[string]interface{}{ "Id": "Disk.Bay.0", "Name": "Disk.Bay.0", "Manufacturer": "INTEL", "SerialNumber": "BTLJ035203XT1P0FGN", "Model": "INTEL SSDPE2KX010T8", "CapacityBytes": int64(1000204886016), "Protocol": "NVMe", "MediaType": "SSD", "Status": map[string]interface{}{"State": "Enabled", "Health": "OK"}, }, }} got := r.collectStorage("/redfish/v1/Systems/1", testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableSupermicroNVMeBackplane: true})) if len(got) != 1 { t.Fatalf("expected one drive from direct Disk.Bay probe, got %d", len(got)) } if got[0].SerialNumber != "BTLJ035203XT1P0FGN" { t.Fatalf("unexpected serial: %q", got[0].SerialNumber) } if got[0].SizeGB == 0 { t.Fatalf("expected size to be parsed from CapacityBytes") } } func TestReplayCollectStorage_SkipsEnclosureRecoveryWhenDirectiveDisabled(t *testing.T) { r := redfishSnapshotReader{tree: map[string]interface{}{ "/redfish/v1/Systems/1/Storage": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Storage/1"}, }, }, "/redfish/v1/Systems/1/Storage/1": map[string]interface{}{ "Links": map[string]interface{}{ "Enclosures": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Enclosures/1"}, }, }, }, "/redfish/v1/Enclosures/1/Drives": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Enclosures/1/Drives/Drive1"}, }, }, "/redfish/v1/Enclosures/1/Drives/Drive1": map[string]interface{}{ "Id": "Drive1", "Name": "Drive1", "Model": "INTEL SSD", "SerialNumber": "ENCLOSURE-DRIVE-001", "Protocol": "SATA", "MediaType": "SSD", }, }} got := r.collectStorage("/redfish/v1/Systems/1", testAnalysisPlan(redfishprofile.AnalysisDirectives{})) if len(got) != 0 { t.Fatalf("expected no enclosure recovery when directive is off, got %d", len(got)) } } func TestReplayCollectStorage_UsesKnownControllerRecoveryWhenEnabled(t *testing.T) { r := redfishSnapshotReader{tree: map[string]interface{}{ "/redfish/v1/Systems/1/Storage/IntelVROC/Drives": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Storage/IntelVROC/Drives/1"}, }, }, "/redfish/v1/Systems/1/Storage/IntelVROC/Drives/1": map[string]interface{}{ "Id": "1", "Name": "Drive1", "Model": "VROC SSD", "SerialNumber": "VROC-001", "Protocol": "NVMe", "MediaType": "SSD", }, }} got := r.collectStorage("/redfish/v1/Systems/1", redfishprofile.ResolvedAnalysisPlan{ Directives: redfishprofile.AnalysisDirectives{EnableKnownStorageControllerRecovery: true}, KnownStorageDriveCollections: []string{"/Storage/IntelVROC/Drives"}, }) if len(got) != 1 { t.Fatalf("expected one drive from known controller recovery, got %d", len(got)) } if got[0].SerialNumber != "VROC-001" { t.Fatalf("unexpected serial %q", got[0].SerialNumber) } } 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"}, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true})) 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, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true})) 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 TestReplayCollectGPUs_DedupUsesRedfishPathBeforeHeuristics(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": "H100-PCIE-80G", "Model": "H100-PCIE-80G", "Manufacturer": "NVIDIA", "SerialNumber": "N/A", }, "/redfish/v1/Systems/1/GraphicsControllers/GPU1": map[string]interface{}{ "Id": "GPU1", "Name": "H100-PCIE-80G", "Model": "H100-PCIE-80G", "Manufacturer": "NVIDIA", "SerialNumber": "N/A", }, }} got := r.collectGPUs([]string{"/redfish/v1/Systems/1"}, nil, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true})) if len(got) != 2 { t.Fatalf("expected both GPUs to be kept by unique redfish path, got %d", len(got)) } } func TestParseGPU_UsesNestedOemSerialNumber(t *testing.T) { doc := map[string]interface{}{ "Id": "GPU4", "Name": "H100-PCIE-80G", "Model": "H100-PCIE-80G", "Manufacturer": "NVIDIA", "SerialNumber": "N/A", "Oem": map[string]interface{}{ "SerialNumber": "1794024010533", }, } got := parseGPU(doc, nil, 1) if got.SerialNumber != "1794024010533" { t.Fatalf("expected nested OEM serial number, got %q", got.SerialNumber) } } 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") } } func TestReplayRedfishFromRawPayloads_StoresAnalysisProfilesMetadata(t *testing.T) { raw := map[string]any{ "redfish_tree": map[string]interface{}{ "/redfish/v1": map[string]interface{}{ "Vendor": "AMI", "Product": "AMI Redfish", "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": "Micro-Star International Co., Ltd.", "Model": "CG290", }, "/redfish/v1/Chassis": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1"}, }, }, "/redfish/v1/Chassis/1": map[string]interface{}{ "Manufacturer": "Micro-Star International Co., Ltd.", "Model": "CG290", }, "/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", }, }, } got, err := ReplayRedfishFromRawPayloads(raw, nil) if err != nil { t.Fatalf("replay failed: %v", err) } meta, ok := got.RawPayloads["redfish_analysis_profiles"].(map[string]any) if !ok { t.Fatalf("expected redfish_analysis_profiles metadata") } if meta["mode"] != redfishprofile.ModeMatched { t.Fatalf("expected matched mode, got %#v", meta["mode"]) } profiles, ok := meta["profiles"].([]string) if !ok { t.Fatalf("expected []string profiles, got %T", meta["profiles"]) } foundMSI := false for _, profile := range profiles { if profile == "msi" { foundMSI = true break } } if !foundMSI { t.Fatalf("expected msi in applied profiles, got %v", profiles) } planMeta, ok := got.RawPayloads["redfish_analysis_plan"].(map[string]any) if !ok { t.Fatalf("expected redfish_analysis_plan metadata") } directives, ok := planMeta["directives"].(map[string]any) if !ok { t.Fatalf("expected directives map in redfish_analysis_plan") } if directives["generic_graphics_controller_dedup"] != true { t.Fatalf("expected generic_graphics_controller_dedup directive, got %#v", directives["generic_graphics_controller_dedup"]) } } func TestReplayRedfishFromRawPayloads_AddsDriveFetchWarning(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": "Inspur", "Model": "NF5688M7", "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": "Inspur", "Model": "NF5688M7", }, "/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/Chassis/1/Drives/FP00HDD00", "error": `Get "...": context deadline exceeded (Client.Timeout exceeded while awaiting headers)`, }, }, } got, err := ReplayRedfishFromRawPayloads(raw, nil) if err != nil { t.Fatalf("replay failed: %v", err) } found := false for _, ev := range got.Events { if ev.Source == "Redfish" && ev.EventType == "Collection Warning" && strings.Contains(strings.ToLower(ev.Description), "drive documents were unavailable") { found = true break } } if !found { t.Fatalf("expected collection warning event for drive fetch errors") } } func TestReplayCollectGPUs_SkipsModelOnlyDuplicateFromGraphicsControllers(t *testing.T) { r := redfishSnapshotReader{tree: map[string]interface{}{ "/redfish/v1/Systems/1/PCIeDevices": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/PCIeDevices/3"}, map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/PCIeDevices/9"}, }, }, "/redfish/v1/Systems/1/PCIeDevices/3": map[string]interface{}{ "Id": "3", "Name": "PCIeCard3", "Model": "H200-SXM5-141G", "Manufacturer": "NVIDIA", "SerialNumber": "1654225094493", }, "/redfish/v1/Systems/1/PCIeDevices/9": map[string]interface{}{ "Id": "9", "Name": "PCIeCard9", "Model": "H200-SXM5-141G", "Manufacturer": "NVIDIA", "SerialNumber": "1654425002635", }, "/redfish/v1/Systems/1/GraphicsControllers": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/GraphicsControllers/GPU0"}, }, }, "/redfish/v1/Systems/1/GraphicsControllers/GPU0": map[string]interface{}{ "Id": "GPU0", "Name": "H200-SXM5-141G", "Model": "H200-SXM5-141G", "Manufacturer": "NVIDIA", "SerialNumber": "N/A", }, }} got := r.collectGPUs([]string{"/redfish/v1/Systems/1"}, nil, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true})) if len(got) != 2 { t.Fatalf("expected 2 GPUs without generic duplicate, got %d", len(got)) } for _, gpu := range got { if gpu.Slot == "H200-SXM5-141G" { t.Fatalf("unexpected model-only duplicate GPU row") } } } func TestReplayCollectGPUs_KeepsModelOnlyGraphicsDuplicateWhenDirectiveDisabled(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/4"}, map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/9"}, }, }, "/redfish/v1/Chassis/1/PCIeDevices/4": map[string]interface{}{ "Id": "4", "Name": "PCIeCard4", "Model": "H200-SXM5-141G", "Manufacturer": "NVIDIA", "SerialNumber": "1654225094493", }, "/redfish/v1/Chassis/1/PCIeDevices/9": map[string]interface{}{ "Id": "9", "Name": "PCIeCard9", "Model": "H200-SXM5-141G", "Manufacturer": "NVIDIA", "SerialNumber": "1654425002635", }, "/redfish/v1/Systems/1/GraphicsControllers": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/GraphicsControllers/GPU0"}, }, }, "/redfish/v1/Systems/1/GraphicsControllers/GPU0": map[string]interface{}{ "Id": "GPU0", "Name": "H200-SXM5-141G", "Model": "H200-SXM5-141G", "Manufacturer": "NVIDIA", "SerialNumber": "N/A", }, }} got := r.collectGPUs([]string{"/redfish/v1/Systems/1"}, []string{"/redfish/v1/Chassis/1"}, testAnalysisPlan(redfishprofile.AnalysisDirectives{})) if len(got) != 3 { t.Fatalf("expected model-only graphics duplicate to remain when directive is off, got %d", len(got)) } } func TestApplyBoardInfoFallbackFromDocs_SkipsComponentProductNames(t *testing.T) { board := models.BoardInfo{ SerialNumber: "23E100051", } docs := []map[string]interface{}{ { "Model": "DDR5 DIMM", "Manufacturer": "DELTA", "SerialNumber": "802C1A2507D284B001", }, { "PlatformId": "NF5688M7", "Manufacturer": "Inspur", "PartNumber": "YZMB-00001", }, } applyBoardInfoFallbackFromDocs(&board, docs) if board.ProductName != "NF5688M7" { t.Fatalf("expected server model from fallback docs, got %q", board.ProductName) } if board.Manufacturer != "Inspur" { t.Fatalf("expected manufacturer from server fallback doc, got %q", board.Manufacturer) } } func TestDedupeStorage_IgnoresPlaceholderSerial(t *testing.T) { in := []models.Storage{ {Slot: "OB01", Model: "N/A", SerialNumber: "N/A"}, {Slot: "OB02", Model: "N/A", SerialNumber: "N/A"}, {Slot: "OB03", Model: "N/A", SerialNumber: "N/A"}, {Slot: "OB04", Model: "N/A", SerialNumber: "N/A"}, } out := dedupeStorage(in) if len(out) != 4 { t.Fatalf("expected all placeholder-serial NVMe drives to be kept by slot key, got %d", len(out)) } } func TestDedupeStorage_MergesPlaceholderSlotsWithRichDrivesByOrder(t *testing.T) { in := []models.Storage{ {Slot: "PCIe8_RAID_Disk_1:0", Type: "SSD", Model: "SOLIDIGM SSDSC2K", SizeGB: 1787, SerialNumber: "S1", Present: true}, {Slot: "PCIe8_RAID_Disk_1:1", Type: "SSD", Model: "SOLIDIGM SSDSC2K", SizeGB: 1787, SerialNumber: "S2", Present: true}, {Slot: "PCIe8_RAID_Disk_1:2", Type: "SSD", Model: "SOLIDIGM SSDSC2K", SizeGB: 1787, SerialNumber: "S3", Present: true}, {Slot: "OB01", Type: "NVMe", Model: "N/A", SerialNumber: "N/A", Present: true}, {Slot: "OB02", Type: "NVMe", Model: "N/A", SerialNumber: "N/A", Present: true}, {Slot: "OB03", Type: "NVMe", Model: "N/A", SerialNumber: "N/A", Present: true}, {Slot: "OB04", Type: "NVMe", Model: "N/A", SerialNumber: "N/A", Present: true}, {Slot: "FP00HDD00", Type: "NVMe", Model: "INTEL SSDPE2KE032T8", SizeGB: 2980, SerialNumber: "N1", Present: true}, {Slot: "FP00HDD02", Type: "NVMe", Model: "INTEL SSDPE2KE032T8", SizeGB: 2980, SerialNumber: "N2", Present: true}, {Slot: "FP00HDD04", Type: "NVMe", Model: "INTEL SSDPE2KE032T8", SizeGB: 2980, SerialNumber: "N3", Present: true}, {Slot: "FP00HDD06", Type: "NVMe", Model: "INTEL SSDPE2KE032T8", SizeGB: 2980, SerialNumber: "N4", Present: true}, } out := dedupeStorage(in) if len(out) != 7 { t.Fatalf("expected 7 rows after placeholder merge, got %d", len(out)) } bySlot := make(map[string]models.Storage, len(out)) for _, d := range out { bySlot[d.Slot] = d if strings.HasPrefix(d.Slot, "FP00HDD") { t.Fatalf("expected FP donor slot %q to be absorbed by placeholder slot", d.Slot) } } if bySlot["OB01"].SerialNumber != "N1" || bySlot["OB02"].SerialNumber != "N2" || bySlot["OB03"].SerialNumber != "N3" || bySlot["OB04"].SerialNumber != "N4" { t.Fatalf("expected OB slots to be enriched in order, got OB01=%q OB02=%q OB03=%q OB04=%q", bySlot["OB01"].SerialNumber, bySlot["OB02"].SerialNumber, bySlot["OB03"].SerialNumber, bySlot["OB04"].SerialNumber) } if bySlot["OB01"].Model != "INTEL SSDPE2KE032T8" || bySlot["OB01"].SizeGB != 2980 { t.Fatalf("expected OB01 to inherit rich model/size, got model=%q size=%d", bySlot["OB01"].Model, bySlot["OB01"].SizeGB) } } func TestDedupeNetworkAdapters_MergesBySlotAndKeepsRicherData(t *testing.T) { in := []models.NetworkAdapter{ { Slot: "NIC-A", Model: "N/A", Vendor: "", Present: true, }, { Slot: "NIC-A", Model: "ConnectX-7", Vendor: "NVIDIA", SerialNumber: "NICSN001", Firmware: "28.41.2020", PortCount: 2, MACAddresses: []string{"00:11:22:33:44:55"}, Present: true, }, } out := dedupeNetworkAdapters(in) if len(out) != 1 { t.Fatalf("expected merged single NIC row, got %d", len(out)) } if out[0].SerialNumber != "NICSN001" || out[0].Model != "ConnectX-7" || out[0].Vendor != "NVIDIA" { t.Fatalf("expected richer NIC fields preserved, got %+v", out[0]) } } func TestDedupePCIeDevices_MergesByLooseKeyAndKeepsBDF(t *testing.T) { in := []models.PCIeDevice{ { Slot: "PCIe Slot 3", DeviceClass: "Network Controller", PartNumber: "MCX75310AAS-NEAT", }, { Slot: "PCIe Slot 3", DeviceClass: "Network Controller", PartNumber: "MCX75310AAS-NEAT", BDF: "0000:af:00.0", VendorID: 0x15b3, DeviceID: 0x1021, SerialNumber: "MT000123", }, } out := dedupePCIeDevices(in) if len(out) != 1 { t.Fatalf("expected merged single PCIe row, got %d", len(out)) } if out[0].BDF != "0000:af:00.0" || out[0].SerialNumber != "MT000123" || out[0].VendorID == 0 || out[0].DeviceID == 0 { t.Fatalf("expected richer PCIe fields preserved, got %+v", out[0]) } } func TestAppendPSU_MergesRicherDuplicate(t *testing.T) { var out []models.PSU seen := make(map[string]int) idx := 1 idx = appendPSU(&out, seen, models.PSU{ Slot: "PSU1", Model: "N/A", Present: true, }, idx) _ = appendPSU(&out, seen, models.PSU{ Slot: "PSU1", Model: "DLG2700BW54C31", SerialNumber: "DGPLV2515025L", WattageW: 2700, Firmware: "00.01.04", Present: true, }, idx) if len(out) != 1 { t.Fatalf("expected PSU duplicate merge, got %d rows", len(out)) } if out[0].SerialNumber != "DGPLV2515025L" || out[0].WattageW != 2700 || out[0].Model != "DLG2700BW54C31" { t.Fatalf("expected richer PSU fields preserved, got %+v", out[0]) } } func TestRedfishPSUNominalWattage_PrefersInputRangeOutputWattage(t *testing.T) { doc := map[string]interface{}{ "PowerCapacityWatts": 22600, "InputRanges": []interface{}{ map[string]interface{}{"OutputWattage": 2700}, map[string]interface{}{"OutputWattage": 3200}, }, } if got := redfishPSUNominalWattage(doc); got != 3200 { t.Fatalf("redfishPSUNominalWattage() = %d, want 3200", got) } } func TestReplayCollectGPUs_DropsModelOnlyPlaceholderWhenConcreteDiscoveredLater(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"}, }, }, "/redfish/v1/Systems/1/GraphicsControllers/GPU0": map[string]interface{}{ "Id": "GPU0", "Name": "H200-SXM5-141G", "Model": "H200-SXM5-141G", }, "/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/4"}, }, }, "/redfish/v1/Chassis/1/PCIeDevices/4": map[string]interface{}{ "Id": "4", "Name": "PCIeCard4", "Model": "H200-SXM5-141G", "Manufacturer": "NVIDIA", "BDF": "0000:0f:00.0", }, }} got := r.collectGPUs([]string{"/redfish/v1/Systems/1"}, []string{"/redfish/v1/Chassis/1"}, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true})) if len(got) != 1 { t.Fatalf("expected generic graphics placeholder to be dropped, got %d GPUs", len(got)) } if got[0].Slot != "PCIeCard4" { t.Fatalf("expected concrete PCIe GPU to remain, got slot=%q", got[0].Slot) } } func TestReplayCollectGPUs_MergesGraphicsSerialIntoConcretePCIeGPU(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/GPU4"}, }, }, "/redfish/v1/Systems/1/GraphicsControllers/GPU4": map[string]interface{}{ "Id": "4", "Name": "H100-PCIE-80G", "Model": "H100-PCIE-80G", "Manufacturer": "NVIDIA", "Oem": map[string]interface{}{ "SerialNumber": "1794024010533", }, }, "/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/8"}, }, }, "/redfish/v1/Chassis/1/PCIeDevices/8": map[string]interface{}{ "Id": "8", "Name": "PCIeCard8", "Model": "H100-PCIE-80G", "Manufacturer": "NVIDIA", "SerialNumber": "N/A", "BDF": "0000:b1:00.0", }, }} got := r.collectGPUs([]string{"/redfish/v1/Systems/1"}, []string{"/redfish/v1/Chassis/1"}, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true})) if len(got) != 1 { t.Fatalf("expected merged single GPU row, got %d", len(got)) } if got[0].Slot != "PCIeCard8" { t.Fatalf("expected concrete PCIe slot, got %q", got[0].Slot) } if got[0].SerialNumber != "1794024010533" { t.Fatalf("expected merged serial from graphics controller, got %q", got[0].SerialNumber) } } func TestReplayCollectGPUs_MergesAmbiguousSameModelByOrder(t *testing.T) { tree := map[string]interface{}{ "/redfish/v1/Systems/1/GraphicsControllers": map[string]interface{}{ "Members": []interface{}{}, }, "/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{ "Members": []interface{}{}, }, } pcieIDs := []int{4, 8, 12, 14, 20, 23, 26, 30} serials := []string{ "1654425002361", "1654425004310", "1654425004204", "1654225097289", "1654225095717", "1654425002114", "1654425002714", "1654425002991", } for i := 0; i < len(pcieIDs); i++ { gpuPath := fmt.Sprintf("/redfish/v1/Systems/1/GraphicsControllers/GPU%d", i+1) pciePath := fmt.Sprintf("/redfish/v1/Chassis/1/PCIeDevices/%d", pcieIDs[i]) tree["/redfish/v1/Systems/1/GraphicsControllers"].(map[string]interface{})["Members"] = append(tree["/redfish/v1/Systems/1/GraphicsControllers"].(map[string]interface{})["Members"].([]interface{}), map[string]interface{}{"@odata.id": gpuPath}) tree["/redfish/v1/Chassis/1/PCIeDevices"].(map[string]interface{})["Members"] = append(tree["/redfish/v1/Chassis/1/PCIeDevices"].(map[string]interface{})["Members"].([]interface{}), map[string]interface{}{"@odata.id": pciePath}) tree[gpuPath] = map[string]interface{}{ "Id": fmt.Sprintf("GPU%d", i+1), "Name": "H200-SXM5-141G", "Model": "H200-SXM5-141G", "Manufacturer": "NVIDIA", "SerialNumber": serials[i], } tree[pciePath] = map[string]interface{}{ "Id": fmt.Sprintf("%d", pcieIDs[i]), "Name": fmt.Sprintf("PCIeCard%d", pcieIDs[i]), "Model": "H200-SXM5-141G", "Manufacturer": "NVIDIA", "BDF": fmt.Sprintf("0000:%02x:00.0", i+1), } } r := redfishSnapshotReader{tree: tree} got := r.collectGPUs([]string{"/redfish/v1/Systems/1"}, []string{"/redfish/v1/Chassis/1"}, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true})) if len(got) != len(pcieIDs) { t.Fatalf("expected %d merged GPUs, got %d", len(pcieIDs), len(got)) } bySlot := make(map[string]models.GPU, len(got)) for _, gpu := range got { bySlot[gpu.Slot] = gpu if strings.EqualFold(strings.TrimSpace(gpu.Slot), strings.TrimSpace(gpu.Model)) { t.Fatalf("expected model-only placeholder to be dropped, got slot=%q", gpu.Slot) } } for i, id := range pcieIDs { slot := fmt.Sprintf("PCIeCard%d", id) gpu, ok := bySlot[slot] if !ok { t.Fatalf("expected concrete slot %q in output", slot) } if gpu.SerialNumber != serials[i] { t.Fatalf("expected slot %s serial %s, got %s", slot, serials[i], gpu.SerialNumber) } } } // TestCollectGPUsFromProcessors_SupermicroHGX verifies that GPU-type Processor // entries (Supermicro HGX: HGX_Baseboard_0/Processors/GPU_SXM_N) are not added // as duplicates when the same GPU is already present via Chassis PCIeDevices. // The processor doc carries SerialNumber directly; the chassis ID ("HGX_GPU_SXM_1") // does NOT match the processor Id ("GPU_SXM_1"), so chassis-based serial lookup // fails and the dedup must fall back to the processor doc's own SerialNumber. func TestCollectGPUsFromProcessors_SupermicroHGX(t *testing.T) { tree := map[string]interface{}{ // Main chassis PCIeDevices — GPU1 and GPU2 with serials. "/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1"}, map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU2"}, }, }, "/redfish/v1/Chassis/1/PCIeDevices/GPU1": map[string]interface{}{ "Id": "GPU1", "Name": "GPU1", "Model": "NVIDIA H200", "Manufacturer": "NVIDIA", "SerialNumber": "SN001", "FirmwareVersion": "96.00.D9.00.02", "PCIeFunctions": map[string]interface{}{ "@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions", }, }, "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions/1"}, }, }, "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions/1": map[string]interface{}{ "FunctionId": "1", "ClassCode": "0x030200", }, "/redfish/v1/Chassis/1/PCIeDevices/GPU2": map[string]interface{}{ "Id": "GPU2", "Name": "GPU2", "Model": "NVIDIA H200", "Manufacturer": "NVIDIA", "SerialNumber": "SN002", "PCIeFunctions": map[string]interface{}{ "@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU2/PCIeFunctions", }, }, "/redfish/v1/Chassis/1/PCIeDevices/GPU2/PCIeFunctions": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU2/PCIeFunctions/1"}, }, }, "/redfish/v1/Chassis/1/PCIeDevices/GPU2/PCIeFunctions/1": map[string]interface{}{ "FunctionId": "2", "ClassCode": "0x030200", }, // HGX GPU chassis — named HGX_GPU_SXM_N (NOT GPU_SXM_N), so chassis-ID lookup // by processor Id "GPU_SXM_1" will NOT find them. "/redfish/v1/Chassis/HGX_GPU_SXM_1": map[string]interface{}{ "Id": "HGX_GPU_SXM_1", }, "/redfish/v1/Chassis/HGX_GPU_SXM_2": map[string]interface{}{ "Id": "HGX_GPU_SXM_2", }, // HGX Baseboard system with GPU-type Processors carrying the same serials. "/redfish/v1/Systems/HGX_Baseboard_0/Processors": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/HGX_Baseboard_0/Processors/GPU_SXM_1"}, map[string]interface{}{"@odata.id": "/redfish/v1/Systems/HGX_Baseboard_0/Processors/GPU_SXM_2"}, }, }, "/redfish/v1/Systems/HGX_Baseboard_0/Processors/GPU_SXM_1": map[string]interface{}{ "Id": "GPU_SXM_1", "Name": "Processor", "ProcessorType": "GPU", "Model": "NVIDIA H200", "Manufacturer": "NVIDIA", "SerialNumber": "SN001", "UUID": "aaaaaaaa-0000-0000-0000-000000000001", "Location": map[string]interface{}{ "PartLocation": map[string]interface{}{ "ServiceLabel": "SXM1", }, }, }, "/redfish/v1/Systems/HGX_Baseboard_0/Processors/GPU_SXM_2": map[string]interface{}{ "Id": "GPU_SXM_2", "Name": "Processor", "ProcessorType": "GPU", "Model": "NVIDIA H200", "Manufacturer": "NVIDIA", "SerialNumber": "SN002", "UUID": "aaaaaaaa-0000-0000-0000-000000000002", "Location": map[string]interface{}{ "PartLocation": map[string]interface{}{ "ServiceLabel": "SXM2", }, }, }, } r := redfishSnapshotReader{tree: tree} chassisPaths := []string{ "/redfish/v1/Chassis/1", "/redfish/v1/Chassis/HGX_GPU_SXM_1", "/redfish/v1/Chassis/HGX_GPU_SXM_2", } systemPaths := []string{"/redfish/v1/Systems/HGX_Baseboard_0"} gpus := r.collectGPUs(systemPaths, chassisPaths, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true})) gpus = r.collectGPUsFromProcessors(systemPaths, chassisPaths, gpus, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableProcessorGPUFallback: true})) if len(gpus) != 2 { var slots []string for _, g := range gpus { slots = append(slots, fmt.Sprintf("%s(sn=%s)", g.Slot, g.SerialNumber)) } t.Fatalf("expected 2 GPUs (no duplicates), got %d: %v", len(gpus), slots) } } func TestCollectGPUsFromProcessors_SupermicroHGXUsesChassisAliasSerial(t *testing.T) { tree := map[string]interface{}{ "/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1"}, }, }, "/redfish/v1/Chassis/1/PCIeDevices/GPU1": map[string]interface{}{ "Id": "GPU1", "Name": "GPU1", "Model": "NVIDIA H200", "Manufacturer": "NVIDIA", "SerialNumber": "SN-ALIAS-001", "PCIeFunctions": map[string]interface{}{ "@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions", }, }, "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions/1"}, }, }, "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions/1": map[string]interface{}{ "FunctionId": "1", "ClassCode": "0x030200", }, "/redfish/v1/Chassis/HGX_GPU_SXM_1": map[string]interface{}{ "Id": "HGX_GPU_SXM_1", "SerialNumber": "SN-ALIAS-001", }, "/redfish/v1/Systems/HGX_Baseboard_0/Processors": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/HGX_Baseboard_0/Processors/GPU_SXM_1"}, }, }, "/redfish/v1/Systems/HGX_Baseboard_0/Processors/GPU_SXM_1": map[string]interface{}{ "Id": "GPU_SXM_1", "Name": "Processor", "ProcessorType": "GPU", "Model": "NVIDIA H200", "Manufacturer": "NVIDIA", }, } r := redfishSnapshotReader{tree: tree} chassisPaths := []string{ "/redfish/v1/Chassis/1", "/redfish/v1/Chassis/HGX_GPU_SXM_1", } systemPaths := []string{"/redfish/v1/Systems/HGX_Baseboard_0"} gpus := r.collectGPUs(systemPaths, chassisPaths, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true})) gpus = r.collectGPUsFromProcessors(systemPaths, chassisPaths, gpus, redfishprofile.ResolvedAnalysisPlan{ Directives: redfishprofile.AnalysisDirectives{EnableProcessorGPUFallback: true, EnableProcessorGPUChassisAlias: true}, ProcessorGPUChassisLookupModes: []string{"hgx-alias"}, }) if len(gpus) != 1 { t.Fatalf("expected alias serial dedupe to keep 1 gpu, got %d", len(gpus)) } if gpus[0].SerialNumber != "SN-ALIAS-001" { t.Fatalf("expected serial from aliased chassis, got %q", gpus[0].SerialNumber) } } func TestCollectGPUsFromProcessors_MSIUsesIndexedChassisLookup(t *testing.T) { tree := map[string]interface{}{ "/redfish/v1/Chassis/GPU1": map[string]interface{}{ "Id": "GPU1", "SerialNumber": "MSI-SN-001", }, "/redfish/v1/Systems/1/Processors": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Processors/GPU_SXM_1"}, }, }, "/redfish/v1/Systems/1/Processors/GPU_SXM_1": map[string]interface{}{ "Id": "GPU_SXM_1", "Name": "Processor", "ProcessorType": "GPU", "Model": "NVIDIA RTX PRO 6000 Blackwell", "Manufacturer": "NVIDIA", }, } r := redfishSnapshotReader{tree: tree} gpus := r.collectGPUsFromProcessors( []string{"/redfish/v1/Systems/1"}, []string{"/redfish/v1/Chassis/GPU1"}, nil, redfishprofile.ResolvedAnalysisPlan{ Directives: redfishprofile.AnalysisDirectives{EnableProcessorGPUFallback: true, EnableMSIProcessorGPUChassisLookup: true}, ProcessorGPUChassisLookupModes: []string{"msi-index"}, }, ) if len(gpus) != 1 { t.Fatalf("expected one gpu, got %d", len(gpus)) } if gpus[0].SerialNumber != "MSI-SN-001" { t.Fatalf("expected serial from MSI indexed chassis lookup, got %q", gpus[0].SerialNumber) } } // TestReplayCollectGPUs_DedupCrossChassisSerial verifies that the same GPU // appearing under two Chassis PCIeDevice trees (e.g. Chassis/1/PCIeDevices/GPU1 // and Chassis/HGX_GPU_SXM_1/PCIeDevices/GPU_SXM_1) is deduplicated to one entry // when both expose the same serial number. func TestReplayCollectGPUs_DedupCrossChassisSerial(t *testing.T) { tree := map[string]interface{}{ "/redfish/v1/Chassis/1/PCIeDevices": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1"}, }, }, "/redfish/v1/Chassis/1/PCIeDevices/GPU1": map[string]interface{}{ "@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1", "Id": "GPU1", "Name": "GPU1", "Model": "NVIDIA H200", "Manufacturer": "NVIDIA", "SerialNumber": "SN-CROSSTEST-001", "PCIeFunctions": map[string]interface{}{ "@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions", }, }, "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions/1"}, }, }, "/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions/1": map[string]interface{}{ "FunctionId": "1", "ClassCode": "0x030200", }, // Same GPU exposed via dedicated HGX chassis — same serial, different path. "/redfish/v1/Chassis/HGX_GPU_SXM_1/PCIeDevices": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/HGX_GPU_SXM_1/PCIeDevices/GPU_SXM_1"}, }, }, "/redfish/v1/Chassis/HGX_GPU_SXM_1/PCIeDevices/GPU_SXM_1": map[string]interface{}{ "@odata.id": "/redfish/v1/Chassis/HGX_GPU_SXM_1/PCIeDevices/GPU_SXM_1", "Id": "GPU_SXM_1", "Name": "PCIe Device", "Model": "NVIDIA H200", "Manufacturer": "NVIDIA", "SerialNumber": "SN-CROSSTEST-001", "UUID": "deadbeef-0000-0000-0000-000000000001", "PCIeFunctions": map[string]interface{}{ "@odata.id": "/redfish/v1/Chassis/HGX_GPU_SXM_1/PCIeDevices/GPU_SXM_1/PCIeFunctions", }, }, "/redfish/v1/Chassis/HGX_GPU_SXM_1/PCIeDevices/GPU_SXM_1/PCIeFunctions": map[string]interface{}{ "Members": []interface{}{}, }, } r := redfishSnapshotReader{tree: tree} got := r.collectGPUs(nil, []string{ "/redfish/v1/Chassis/1", "/redfish/v1/Chassis/HGX_GPU_SXM_1", }, testAnalysisPlan(redfishprofile.AnalysisDirectives{EnableGenericGraphicsControllerDedup: true})) if len(got) != 1 { var slots []string for _, g := range got { slots = append(slots, fmt.Sprintf("%s(sn=%s)", g.Slot, g.SerialNumber)) } t.Fatalf("expected 1 GPU (cross-chassis serial dedup), got %d: %v", len(got), slots) } if got[0].SerialNumber != "SN-CROSSTEST-001" { t.Fatalf("unexpected serial %q", got[0].SerialNumber) } } // TestLooksLikeGPU_NVSwitchExcluded verifies that NVSwitch PCIe devices // are not classified as GPUs even though their manufacturer is NVIDIA. func TestLooksLikeGPU_NVSwitchExcluded(t *testing.T) { doc := map[string]interface{}{ "Id": "NVSwitch_0", "Name": "PCIe Device", "Model": "NVSwitch", "Manufacturer": "NVIDIA", "DeviceType": "SingleFunction", } if looksLikeGPU(doc, nil) { t.Fatal("NVSwitch should not be classified as a GPU") } } func TestFirmwareInventoryDeviceName_PrefersIDForGenericSoftwareInventory(t *testing.T) { doc := map[string]interface{}{ "Id": "HGX_FW_NVSwitch_0", "Name": "Software Inventory", "Version": "96.10.73.00.01", } got := firmwareInventoryDeviceName(doc) if got != "HGX_FW_NVSwitch_0" { t.Fatalf("expected firmware inventory id to be used, got %q", got) } } func TestParsePCIeDeviceWithSupplementalDocs_NVSwitchThermalMetrics(t *testing.T) { doc := map[string]interface{}{ "@odata.id": "/redfish/v1/Chassis/HGX_NVSwitch_0/PCIeDevices/NVSwitch_0", "Id": "NVSwitch_0", "Model": "NVSwitch", "Manufacturer": "NVIDIA", "DeviceType": "SingleFunction", } supplementalDocs := []map[string]interface{}{ { "TemperatureReadingsCelsius": []interface{}{ map[string]interface{}{ "DeviceName": "NVSwitch_0", "Reading": "31.593750", }, }, }, } got := parsePCIeDeviceWithSupplementalDocs(doc, nil, supplementalDocs) if got.Details == nil { t.Fatalf("expected NVSwitch details to be populated") } if temp := got.Details["temperature_c"]; temp != 31.59375 { t.Fatalf("expected NVSwitch thermal metric, got %#v", got.Details) } } func TestShouldCrawlPath_MemoryAndProcessorMetricsAreAllowed(t *testing.T) { if !shouldCrawlPath("/redfish/v1/Systems/1/Memory/CPU0_C0D0") { t.Fatalf("expected direct DIMM resource to be crawlable") } if shouldCrawlPath("/redfish/v1/Systems/1/Memory/CPU0_C0D0/Assembly") { t.Fatalf("expected DIMM assembly subresource to be skipped") } if !shouldCrawlPath("/redfish/v1/Systems/1/Memory/CPU0_C0D0/MemoryMetrics") { t.Fatalf("expected DIMM metrics subresource to be crawlable") } if !shouldCrawlPath("/redfish/v1/Systems/1/Processors/CPU0/ProcessorMetrics") { t.Fatalf("expected CPU metrics subresource to be crawlable") } if shouldCrawlPath("/redfish/v1/Chassis/1/PCIeDevices/0/PCIeFunctions/1") { t.Fatalf("expected noisy chassis pciefunctions branch to be skipped") } if !shouldCrawlPath("/redfish/v1/Fabrics/HGX_NVLinkFabric_0/Switches/NVSwitch_0") { t.Fatalf("expected NVSwitch fabric resource to be crawlable") } if !shouldCrawlPath("/redfish/v1/Fabrics/HGX_NVLinkFabric_0/Switches/NVSwitch_0/Ports/NVLink_0/Metrics") { t.Fatalf("expected NVLink port metrics to be crawlable") } } func TestIsRedfishMemoryMemberPath(t *testing.T) { cases := []struct { path string want bool }{ {path: "/redfish/v1/Systems/1/Memory", want: false}, {path: "/redfish/v1/Systems/1/Memory/CPU0_C0D0", want: true}, {path: "/redfish/v1/Systems/1/Memory/CPU0_C0D0/Assembly", want: false}, {path: "/redfish/v1/Systems/1/Memory/CPU0_C0D0/MemoryMetrics", want: false}, {path: "/redfish/v1/Chassis/1/Memory/CPU0_C0D0", want: false}, } for _, tc := range cases { got := isRedfishMemoryMemberPath(tc.path) if got != tc.want { t.Fatalf("isRedfishMemoryMemberPath(%q) = %v, want %v", tc.path, got, tc.want) } } } func TestRedfishSnapshotBranchKey(t *testing.T) { cases := []struct { path string want string }{ {path: "", want: ""}, {path: "/redfish/v1", want: ""}, {path: "/redfish/v1/Systems", want: "/redfish/v1/Systems"}, {path: "/redfish/v1/Systems/1", want: "/redfish/v1/Systems/1"}, {path: "/redfish/v1/Systems/1/Memory", want: "/redfish/v1/Systems/1/Memory"}, {path: "/redfish/v1/Systems/1/Memory/CPU0_C0D0", want: "/redfish/v1/Systems/1/Memory"}, {path: "/redfish/v1/Systems/1/PCIeDevices/GPU1", want: "/redfish/v1/Systems/1/PCIeDevices"}, {path: "/redfish/v1/Chassis/1/Sensors/1", want: "/redfish/v1/Chassis/1/Sensors"}, {path: "/redfish/v1/UpdateService/FirmwareInventory/BIOS", want: "/redfish/v1/UpdateService/FirmwareInventory"}, } for _, tc := range cases { got := redfishSnapshotBranchKey(tc.path) if got != tc.want { t.Fatalf("redfishSnapshotBranchKey(%q) = %q, want %q", tc.path, got, tc.want) } } } func TestShouldPostProbeCollectionPath(t *testing.T) { var tuning redfishprofile.AcquisitionTuning if shouldPostProbeCollectionPath("/redfish/v1/Chassis/1/Sensors", tuning) { t.Fatalf("expected sensors collection to be skipped by default") } if shouldPostProbeCollectionPath("/redfish/v1/Systems/1/Storage/RAID/Drives", tuning) { t.Fatalf("expected drives collection to be skipped without profile policy") } tuning.PostProbePolicy.EnableNumericCollectionProbe = true t.Setenv("LOGPILE_REDFISH_SENSOR_POSTPROBE", "1") if !shouldPostProbeCollectionPath("/redfish/v1/Chassis/1/Sensors", tuning) { t.Fatalf("expected sensors collection to be post-probed when enabled") } if !shouldPostProbeCollectionPath("/redfish/v1/Systems/1/Storage/RAID/Drives", tuning) { t.Fatalf("expected drives collection to be post-probed") } if shouldPostProbeCollectionPath("/redfish/v1/Chassis/1/Boards/BOARD1", tuning) { t.Fatalf("expected board member resource to be skipped from post-probe") } if shouldPostProbeCollectionPath("/redfish/v1/Chassis/1/Assembly/Oem/COMMONb/COMMONbAssembly/1", tuning) { t.Fatalf("expected assembly member resource to be skipped from post-probe") } } func TestShouldAdaptivePostProbeCollectionPath(t *testing.T) { tuning := redfishprofile.AcquisitionTuning{ PostProbePolicy: redfishprofile.AcquisitionPostProbePolicy{ EnableNumericCollectionProbe: true, }, } withExplicitNamedMembers := map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/EthernetInterfaces/NIC-0-0"}, map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/EthernetInterfaces/NIC-0-1"}, }, } if shouldAdaptivePostProbeCollectionPath("/redfish/v1/Systems/1/EthernetInterfaces", withExplicitNamedMembers, tuning) { t.Fatalf("expected explicit non-numeric members to skip adaptive post-probe") } withNumericMembers := map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/1"}, map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/2"}, }, } if !shouldAdaptivePostProbeCollectionPath("/redfish/v1/Chassis/1/PCIeDevices", withNumericMembers, tuning) { t.Fatalf("expected numeric members to allow adaptive post-probe") } withoutMembers := map[string]interface{}{"Name": "Drives"} if !shouldAdaptivePostProbeCollectionPath("/redfish/v1/Chassis/1/Drives", withoutMembers, tuning) { t.Fatalf("expected missing members to allow adaptive post-probe") } if shouldAdaptivePostProbeCollectionPath("/redfish/v1/Chassis/1/Drives", withoutMembers, redfishprofile.AcquisitionTuning{}) { t.Fatalf("expected post-probe to stay disabled without profile policy") } } func TestShouldAdaptiveNVMeProbe(t *testing.T) { withMembers := map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1/Drives/OB01"}, }, } if shouldAdaptiveNVMeProbe(withMembers) { t.Fatalf("expected drives collection with explicit members to skip NVMe probe") } withoutMembers := map[string]interface{}{"Name": "Drives"} if !shouldAdaptiveNVMeProbe(withoutMembers) { t.Fatalf("expected drives collection without members to allow NVMe probe") } } func TestRedfishAdaptivePrefetchTargets(t *testing.T) { tuning := redfishprofile.AcquisitionTuning{ PrefetchPolicy: redfishprofile.AcquisitionPrefetchPolicy{ IncludeSuffixes: []string{ "/Memory", "/Processors", "/Storage", }, }, } candidates := []string{ "/redfish/v1/Systems/1/Memory", "/redfish/v1/Systems/1/Processors", "/redfish/v1/Systems/1/Storage", } rawTree := map[string]interface{}{ "/redfish/v1/Systems/1/Memory": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Memory/DIMM1"}, }, }, "/redfish/v1/Systems/1/Storage": map[string]interface{}{ "Members": []interface{}{ map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1/Storage/1"}, }, }, } fetchErrs := map[string]string{ "/redfish/v1/Systems/1/Memory/DIMM1": "Get \"https://bmc/redfish/v1/Systems/1/Memory/DIMM1\": context deadline exceeded", "/redfish/v1/Systems/1/Storage/1": "status 404 from /redfish/v1/Systems/1/Storage/1: not found", "/redfish/v1/Systems/1/Processors": "Get \"https://bmc/redfish/v1/Systems/1/Processors\": context deadline exceeded", "/redfish/v1/Systems/1/Storage/Volumes": "status 404 from /redfish/v1/Systems/1/Storage/Volumes: not found", } got := redfishAdaptivePrefetchTargets(redfishPrefetchTargets(candidates, tuning), rawTree, fetchErrs) joined := strings.Join(got, "\n") for _, wanted := range []string{ "/redfish/v1/Systems/1/Memory", "/redfish/v1/Systems/1/Processors", } { if !strings.Contains(joined, wanted) { t.Fatalf("expected adaptive prefetch target %q", wanted) } } if strings.Contains(joined, "/redfish/v1/Systems/1/Storage") { t.Fatalf("expected storage with only non-retryable missing members to be skipped") } } func TestResolveAcquisitionPlan_DefaultSkipsNoisyBranches(t *testing.T) { signals := redfishprofile.MatchSignals{} match := redfishprofile.MatchProfiles(signals) plan := redfishprofile.BuildAcquisitionPlan(signals) resolved := redfishprofile.ResolveAcquisitionPlan(match, plan, redfishprofile.DiscoveredResources{ SystemPaths: []string{"/redfish/v1/Systems/1"}, ChassisPaths: []string{"/redfish/v1/Chassis/1"}, ManagerPaths: []string{"/redfish/v1/Managers/1"}, }, signals) seeds := resolved.SeedPaths joined := strings.Join(seeds, "\n") for _, noisy := range []string{ "/redfish/v1/Fabrics", "/redfish/v1/Chassis/1/Backplanes", "/redfish/v1/Chassis/1/Boards", "/redfish/v1/Chassis/1/Sensors", "/redfish/v1/Managers/1/LogServices", } { if strings.Contains(joined, noisy) { t.Fatalf("unexpected noisy seed %q", noisy) } } for _, wanted := range []string{ "/redfish/v1/Systems/1/Memory", "/redfish/v1/Systems/1/PCIeDevices", "/redfish/v1/Chassis/1/Drives", "/redfish/v1/Chassis/1/NetworkAdapters", "/redfish/v1/Managers/1/NetworkProtocol", } { if !strings.Contains(joined, wanted) { t.Fatalf("expected seed %q", wanted) } } } func TestShouldPrefetchCriticalPath_UsesPrefetchPolicy(t *testing.T) { tuning := redfishprofile.AcquisitionTuning{ PrefetchPolicy: redfishprofile.AcquisitionPrefetchPolicy{ IncludeSuffixes: []string{"/Storage", "/Oem/Public"}, ExcludeContains: []string{"/Assembly"}, }, } if !shouldPrefetchCriticalPath("/redfish/v1/Systems/1/Storage", tuning) { t.Fatal("expected storage path to be prefetched when included by policy") } if !shouldPrefetchCriticalPath("/redfish/v1/Systems/1/Oem/Public", tuning) { t.Fatal("expected OEM public path to be prefetched when included by policy") } if shouldPrefetchCriticalPath("/redfish/v1/Chassis/1/Assembly", tuning) { t.Fatal("expected excluded path to skip prefetch") } if shouldPrefetchCriticalPath("/redfish/v1/Chassis/1/Power", redfishprofile.AcquisitionTuning{}) { t.Fatal("expected empty prefetch policy to disable suffix-based prefetch") } } func TestRedfishPrefetchTargets_FilterNoisyBranches(t *testing.T) { tuning := redfishprofile.AcquisitionTuning{ PrefetchPolicy: redfishprofile.AcquisitionPrefetchPolicy{ IncludeSuffixes: []string{ "/Memory", "/Oem/Public/FRU", "/Drives", "/NetworkProtocol", }, ExcludeContains: []string{ "/Backplanes", "/Sensors", "/LogServices", }, }, } critical := []string{ "/redfish/v1/Systems/1", "/redfish/v1/Systems/1/Memory", "/redfish/v1/Systems/1/Oem/Public/FRU", "/redfish/v1/Chassis/1/Drives", "/redfish/v1/Chassis/1/Backplanes", "/redfish/v1/Chassis/1/Sensors", "/redfish/v1/Managers/1/LogServices", "/redfish/v1/Managers/1/NetworkProtocol", } got := redfishPrefetchTargets(critical, tuning) joined := strings.Join(got, "\n") for _, wanted := range []string{ "/redfish/v1/Systems/1", "/redfish/v1/Systems/1/Memory", "/redfish/v1/Systems/1/Oem/Public/FRU", "/redfish/v1/Chassis/1/Drives", "/redfish/v1/Managers/1/NetworkProtocol", } { if !strings.Contains(joined, wanted) { t.Fatalf("expected prefetch target %q", wanted) } } for _, noisy := range []string{ "/redfish/v1/Chassis/1/Backplanes", "/redfish/v1/Chassis/1/Sensors", "/redfish/v1/Managers/1/LogServices", } { if strings.Contains(joined, noisy) { t.Fatalf("unexpected noisy prefetch target %q", noisy) } } } // TestChassisTypeCanHaveNVMe verifies that non-storage chassis types (GPU modules, // RoT components, fabric zones) are excluded from NVMe bay probing, while storage // and unclassified chassis types are kept. // // Regression guard: on Supermicro HGX (SYS-A21GE-NBRT) all 35 sub-chassis (GPUs, // NVSwitches, PCIeRetimers, ERoT/IRoT, BMC, FPGA) have ChassisType=Module/Component/Zone // and expose empty /Drives collections. Without this filter each chassis triggered // 384 HTTP requests → ~22 minutes wasted per collection. (2026-03-12) func TestChassisTypeCanHaveNVMe(t *testing.T) { cases := []struct { chassisType string want bool }{ // Non-storage sub-module types — must return false {"Module", false}, // GPU SXM, PCIeRetimer, NVLinkManagementNIC {"module", false}, // case-insensitive {"Component", false}, // ERoT, IRoT, BMC, FPGA sub-chassis {"component", false}, {"Zone", false}, // HGX_Chassis_0 fabric zone {"zone", false}, // Storage-capable and generic types — must return true {"Enclosure", true}, // NVMe StorageBackplane {"RackMount", true}, // main server chassis {"Blade", true}, // blade server chassis {"StandAlone", true}, // standalone server {"", true}, // unknown type — probe to be safe } for _, tc := range cases { got := chassisTypeCanHaveNVMe(tc.chassisType) if got != tc.want { t.Errorf("chassisTypeCanHaveNVMe(%q) = %v, want %v", tc.chassisType, got, tc.want) } } } // TestNVMePostProbeSkipsNonStorageChassis verifies that the NVMe bay probe candidate // selection skips chassis whose ChassisType indicates they cannot hold NVMe drives. // // Simulates an HGX topology: one GPU chassis (Module) and one NVMe backplane // (Enclosure), both with empty /Drives collections. Only the backplane must be // selected as a probe candidate. func TestNVMePostProbeSkipsNonStorageChassis(t *testing.T) { // Build the out map as collectRawRedfishTree would produce it out := map[string]interface{}{ // GPU chassis — Module type, empty Drives: should be skipped "/redfish/v1/Chassis/HGX_GPU_SXM_1": map[string]interface{}{ "@odata.id": "/redfish/v1/Chassis/HGX_GPU_SXM_1", "ChassisType": "Module", "Name": "HGX_GPU_SXM_1", }, "/redfish/v1/Chassis/HGX_GPU_SXM_1/Drives": map[string]interface{}{ "@odata.id": "/redfish/v1/Chassis/HGX_GPU_SXM_1/Drives", "Members": []interface{}{}, "Members@odata.count": 0, }, // NVMe backplane — Enclosure type, empty Drives: must be selected "/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane": map[string]interface{}{ "@odata.id": "/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane", "ChassisType": "Enclosure", "Name": "Backplane", }, "/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane/Drives": map[string]interface{}{ "@odata.id": "/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane/Drives", "Members": []interface{}{}, "Members@odata.count": 0, }, } // Replicate the candidate selection logic from collectRawRedfishTree var selected []string for path, docAny := range out { normalized := normalizeRedfishPath(path) if !strings.HasSuffix(normalized, "/Drives") { continue } doc, _ := docAny.(map[string]interface{}) if !shouldAdaptiveNVMeProbe(doc) { continue } chassisPath := strings.TrimSuffix(normalized, "/Drives") if chassisDocAny, ok := out[chassisPath]; ok { if chassisDoc, ok := chassisDocAny.(map[string]interface{}); ok { if !chassisTypeCanHaveNVMe(asString(chassisDoc["ChassisType"])) { continue } } } selected = append(selected, normalized) } if len(selected) != 1 { t.Fatalf("expected 1 NVMe probe candidate (backplane), got %d: %v", len(selected), selected) } if !strings.Contains(selected[0], "StorageBackplane") { t.Fatalf("expected StorageBackplane to be selected, got %q", selected[0]) } } func TestIsVirtualStorageDrive_AMIVirtualMedia(t *testing.T) { doc := map[string]interface{}{ "Id": "USB_Device1_Port4", "Name": "Virtual Cdrom Device", "Model": "Virtual Cdrom Device", "Manufacturer": "American Megatrends Inc.", "Protocol": "USB", "CapacityBytes": 0, "SerialNumber": "AAAABBBBCCCC1", } if !isVirtualStorageDrive(doc) { t.Fatalf("expected AMI virtual media to be filtered") } }