diff --git a/internal/collector/redfish.go b/internal/collector/redfish.go index 54d823b..5017842 100644 --- a/internal/collector/redfish.go +++ b/internal/collector/redfish.go @@ -4308,6 +4308,21 @@ func parseGPUWithSupplementalDocs(doc map[string]interface{}, functionDocs []map gpu.DeviceID = asHexOrInt(doc["DeviceId"]) } + if pcieIf, ok := doc["PCIeInterface"].(map[string]interface{}); ok { + if gpu.CurrentLinkWidth == 0 { + gpu.CurrentLinkWidth = asInt(pcieIf["LanesInUse"]) + } + if gpu.MaxLinkWidth == 0 { + gpu.MaxLinkWidth = firstNonZeroInt(asInt(pcieIf["MaxLanes"]), asInt(pcieIf["Maxlanes"])) + } + if gpu.CurrentLinkSpeed == "" { + gpu.CurrentLinkSpeed = firstNonEmpty(asString(pcieIf["PCIeType"]), asString(pcieIf["CurrentLinkSpeedGTs"]), asString(pcieIf["CurrentLinkSpeed"])) + } + if gpu.MaxLinkSpeed == "" { + gpu.MaxLinkSpeed = firstNonEmpty(asString(pcieIf["MaxPCIeType"]), asString(pcieIf["MaxLinkSpeedGTs"]), asString(pcieIf["MaxLinkSpeed"])) + } + } + for _, fn := range functionDocs { if gpu.BDF == "" { gpu.BDF = sanitizeRedfishBDF(asString(fn["FunctionId"])) @@ -4330,6 +4345,9 @@ func parseGPUWithSupplementalDocs(doc map[string]interface{}, functionDocs []map if gpu.CurrentLinkSpeed == "" { gpu.CurrentLinkSpeed = firstNonEmpty(asString(fn["CurrentLinkSpeedGTs"]), asString(fn["CurrentLinkSpeed"])) } + if gpu.CurrentLinkWidth == 0 || gpu.MaxLinkWidth == 0 || gpu.CurrentLinkSpeed == "" || gpu.MaxLinkSpeed == "" { + redfishEnrichFromOEMxFusionPCIeLink(fn, &gpu.CurrentLinkWidth, &gpu.MaxLinkWidth, &gpu.CurrentLinkSpeed, &gpu.MaxLinkSpeed) + } } if isMissingOrRawPCIModel(gpu.Model) { diff --git a/internal/collector/redfish_test.go b/internal/collector/redfish_test.go index bae1ef8..0f0e6c1 100644 --- a/internal/collector/redfish_test.go +++ b/internal/collector/redfish_test.go @@ -2815,6 +2815,28 @@ func TestReplayCollectGPUs_DedupUsesRedfishPathBeforeHeuristics(t *testing.T) { } } +func TestParseGPU_xFusionPCIeInterfaceMaxlanes(t *testing.T) { + // xFusion GPU PCIeDevices (PCIeCard1..N) carry link width in PCIeInterface + // with "Maxlanes" (lowercase 'l') rather than "MaxLanes". + doc := map[string]interface{}{ + "Id": "PCIeCard1", + "Model": "RTX PRO 6000", + "PCIeInterface": map[string]interface{}{ + "LanesInUse": 16, + "Maxlanes": 16, + "PCIeType": "Gen5", + "MaxPCIeType": "Gen5", + }, + } + gpu := parseGPU(doc, nil, 1) + if gpu.CurrentLinkWidth != 16 || gpu.MaxLinkWidth != 16 { + t.Fatalf("expected link widths 16/16 from PCIeInterface, got current=%d max=%d", gpu.CurrentLinkWidth, gpu.MaxLinkWidth) + } + if gpu.CurrentLinkSpeed != "Gen5" || gpu.MaxLinkSpeed != "Gen5" { + t.Fatalf("expected link speeds Gen5/Gen5 from PCIeInterface, got current=%q max=%q", gpu.CurrentLinkSpeed, gpu.MaxLinkSpeed) + } +} + func TestParseGPU_UsesNestedOemSerialNumber(t *testing.T) { doc := map[string]interface{}{ "Id": "GPU4",