Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
becdca1d7e | ||
|
|
e10440ae32 |
@@ -3592,7 +3592,7 @@ func parseNIC(doc map[string]interface{}) models.NetworkAdapter {
|
|||||||
}
|
}
|
||||||
if pcieIf, ok := ctrl["PCIeInterface"].(map[string]interface{}); ok && linkWidth == 0 && maxLinkWidth == 0 && linkSpeed == "" && maxLinkSpeed == "" {
|
if pcieIf, ok := ctrl["PCIeInterface"].(map[string]interface{}); ok && linkWidth == 0 && maxLinkWidth == 0 && linkSpeed == "" && maxLinkSpeed == "" {
|
||||||
linkWidth = asInt(pcieIf["LanesInUse"])
|
linkWidth = asInt(pcieIf["LanesInUse"])
|
||||||
maxLinkWidth = asInt(pcieIf["MaxLanes"])
|
maxLinkWidth = firstNonZeroInt(asInt(pcieIf["MaxLanes"]), asInt(pcieIf["Maxlanes"]))
|
||||||
linkSpeed = firstNonEmpty(asString(pcieIf["PCIeType"]), asString(pcieIf["CurrentLinkSpeedGTs"]), asString(pcieIf["CurrentLinkSpeed"]))
|
linkSpeed = firstNonEmpty(asString(pcieIf["PCIeType"]), asString(pcieIf["CurrentLinkSpeedGTs"]), asString(pcieIf["CurrentLinkSpeed"]))
|
||||||
maxLinkSpeed = firstNonEmpty(asString(pcieIf["MaxPCIeType"]), asString(pcieIf["MaxLinkSpeedGTs"]), asString(pcieIf["MaxLinkSpeed"]))
|
maxLinkSpeed = firstNonEmpty(asString(pcieIf["MaxPCIeType"]), asString(pcieIf["MaxLinkSpeedGTs"]), asString(pcieIf["MaxLinkSpeed"]))
|
||||||
}
|
}
|
||||||
@@ -3705,6 +3705,9 @@ func enrichNICFromPCIe(nic *models.NetworkAdapter, pcieDoc map[string]interface{
|
|||||||
if strings.TrimSpace(nic.MaxLinkSpeed) == "" {
|
if strings.TrimSpace(nic.MaxLinkSpeed) == "" {
|
||||||
nic.MaxLinkSpeed = firstNonEmpty(asString(pcieDoc["MaxLinkSpeedGTs"]), asString(pcieDoc["MaxLinkSpeed"]))
|
nic.MaxLinkSpeed = firstNonEmpty(asString(pcieDoc["MaxLinkSpeedGTs"]), asString(pcieDoc["MaxLinkSpeed"]))
|
||||||
}
|
}
|
||||||
|
if nic.LinkWidth == 0 || nic.MaxLinkWidth == 0 || nic.LinkSpeed == "" || nic.MaxLinkSpeed == "" {
|
||||||
|
redfishEnrichFromOEMxFusionPCIeLink(pcieDoc, &nic.LinkWidth, &nic.MaxLinkWidth, &nic.LinkSpeed, &nic.MaxLinkSpeed)
|
||||||
|
}
|
||||||
if normalizeRedfishIdentityField(nic.SerialNumber) == "" {
|
if normalizeRedfishIdentityField(nic.SerialNumber) == "" {
|
||||||
nic.SerialNumber = findFirstNormalizedStringByKeys(pcieDoc, "SerialNumber")
|
nic.SerialNumber = findFirstNormalizedStringByKeys(pcieDoc, "SerialNumber")
|
||||||
}
|
}
|
||||||
@@ -3736,6 +3739,9 @@ func enrichNICFromPCIe(nic *models.NetworkAdapter, pcieDoc map[string]interface{
|
|||||||
if strings.TrimSpace(nic.MaxLinkSpeed) == "" {
|
if strings.TrimSpace(nic.MaxLinkSpeed) == "" {
|
||||||
nic.MaxLinkSpeed = firstNonEmpty(asString(fn["MaxLinkSpeedGTs"]), asString(fn["MaxLinkSpeed"]))
|
nic.MaxLinkSpeed = firstNonEmpty(asString(fn["MaxLinkSpeedGTs"]), asString(fn["MaxLinkSpeed"]))
|
||||||
}
|
}
|
||||||
|
if nic.LinkWidth == 0 || nic.MaxLinkWidth == 0 || nic.LinkSpeed == "" || nic.MaxLinkSpeed == "" {
|
||||||
|
redfishEnrichFromOEMxFusionPCIeLink(fn, &nic.LinkWidth, &nic.MaxLinkWidth, &nic.LinkSpeed, &nic.MaxLinkSpeed)
|
||||||
|
}
|
||||||
if normalizeRedfishIdentityField(nic.SerialNumber) == "" {
|
if normalizeRedfishIdentityField(nic.SerialNumber) == "" {
|
||||||
nic.SerialNumber = findFirstNormalizedStringByKeys(fn, "SerialNumber")
|
nic.SerialNumber = findFirstNormalizedStringByKeys(fn, "SerialNumber")
|
||||||
}
|
}
|
||||||
@@ -4302,6 +4308,21 @@ func parseGPUWithSupplementalDocs(doc map[string]interface{}, functionDocs []map
|
|||||||
gpu.DeviceID = asHexOrInt(doc["DeviceId"])
|
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 {
|
for _, fn := range functionDocs {
|
||||||
if gpu.BDF == "" {
|
if gpu.BDF == "" {
|
||||||
gpu.BDF = sanitizeRedfishBDF(asString(fn["FunctionId"]))
|
gpu.BDF = sanitizeRedfishBDF(asString(fn["FunctionId"]))
|
||||||
@@ -4324,6 +4345,9 @@ func parseGPUWithSupplementalDocs(doc map[string]interface{}, functionDocs []map
|
|||||||
if gpu.CurrentLinkSpeed == "" {
|
if gpu.CurrentLinkSpeed == "" {
|
||||||
gpu.CurrentLinkSpeed = firstNonEmpty(asString(fn["CurrentLinkSpeedGTs"]), asString(fn["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) {
|
if isMissingOrRawPCIModel(gpu.Model) {
|
||||||
@@ -4384,6 +4408,9 @@ func parsePCIeDeviceWithSupplementalDocs(doc map[string]interface{}, functionDoc
|
|||||||
if dev.MaxLinkSpeed == "" {
|
if dev.MaxLinkSpeed == "" {
|
||||||
dev.MaxLinkSpeed = firstNonEmpty(asString(fn["MaxLinkSpeedGTs"]), asString(fn["MaxLinkSpeed"]))
|
dev.MaxLinkSpeed = firstNonEmpty(asString(fn["MaxLinkSpeedGTs"]), asString(fn["MaxLinkSpeed"]))
|
||||||
}
|
}
|
||||||
|
if dev.LinkWidth == 0 || dev.MaxLinkWidth == 0 || dev.LinkSpeed == "" || dev.MaxLinkSpeed == "" {
|
||||||
|
redfishEnrichFromOEMxFusionPCIeLink(fn, &dev.LinkWidth, &dev.MaxLinkWidth, &dev.LinkSpeed, &dev.MaxLinkSpeed)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if dev.DeviceClass == "" || isGenericPCIeClassLabel(dev.DeviceClass) {
|
if dev.DeviceClass == "" || isGenericPCIeClassLabel(dev.DeviceClass) {
|
||||||
dev.DeviceClass = firstNonEmpty(redfishFirstStringAcrossDocs(supplementalDocs, "DeviceType"), dev.DeviceClass)
|
dev.DeviceClass = firstNonEmpty(redfishFirstStringAcrossDocs(supplementalDocs, "DeviceType"), dev.DeviceClass)
|
||||||
@@ -4633,6 +4660,59 @@ func buildBDFfromOemPublic(doc map[string]interface{}) string {
|
|||||||
return fmt.Sprintf("%04x:%02x:%02x.%x", segment, bus, dev, fn)
|
return fmt.Sprintf("%04x:%02x:%02x.%x", segment, bus, dev, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// redfishEnrichFromOEMxFusionPCIeLink fills in missing PCIe link width/speed
|
||||||
|
// from the xFusion OEM namespace. xFusion reports link width as a string like
|
||||||
|
// "X8" in Oem.xFusion.LinkWidth / Oem.xFusion.LinkWidthAbility, and link speed
|
||||||
|
// as a string like "Gen4 (16.0GT/s)" in Oem.xFusion.LinkSpeed /
|
||||||
|
// Oem.xFusion.LinkSpeedAbility. These fields appear on PCIeFunction docs.
|
||||||
|
func redfishEnrichFromOEMxFusionPCIeLink(doc map[string]interface{}, linkWidth, maxLinkWidth *int, linkSpeed, maxLinkSpeed *string) {
|
||||||
|
oem, _ := doc["Oem"].(map[string]interface{})
|
||||||
|
if oem == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xf, _ := oem["xFusion"].(map[string]interface{})
|
||||||
|
if xf == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if *linkWidth == 0 {
|
||||||
|
*linkWidth = parseXFusionLinkWidth(asString(xf["LinkWidth"]))
|
||||||
|
}
|
||||||
|
if *maxLinkWidth == 0 {
|
||||||
|
*maxLinkWidth = parseXFusionLinkWidth(asString(xf["LinkWidthAbility"]))
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(*linkSpeed) == "" {
|
||||||
|
*linkSpeed = strings.TrimSpace(asString(xf["LinkSpeed"]))
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(*maxLinkSpeed) == "" {
|
||||||
|
*maxLinkSpeed = strings.TrimSpace(asString(xf["LinkSpeedAbility"]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseXFusionLinkWidth converts an xFusion link-width string like "X8" or
|
||||||
|
// "x16" to the integer lane count. Returns 0 for unrecognised values.
|
||||||
|
func parseXFusionLinkWidth(s string) int {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
if s == "" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
s = strings.TrimPrefix(strings.ToUpper(s), "X")
|
||||||
|
v := asInt(s)
|
||||||
|
if v <= 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// firstNonZeroInt returns the first argument that is non-zero.
|
||||||
|
func firstNonZeroInt(vals ...int) int {
|
||||||
|
for _, v := range vals {
|
||||||
|
if v != 0 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
func normalizeRedfishIdentityField(v string) string {
|
func normalizeRedfishIdentityField(v string) string {
|
||||||
v = strings.TrimSpace(v)
|
v = strings.TrimSpace(v)
|
||||||
if v == "" {
|
if v == "" {
|
||||||
|
|||||||
@@ -1341,6 +1341,48 @@ func TestParseNIC_PrefersControllerSlotLabelAndPCIeInterface(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseNIC_xFusionMaxlanesAndOEMLinkWidth(t *testing.T) {
|
||||||
|
// xFusion uses "Maxlanes" (lowercase 'l') in PCIeInterface, not "MaxLanes".
|
||||||
|
// xFusion also stores per-function link width as Oem.xFusion.LinkWidth = "X8".
|
||||||
|
nic := parseNIC(map[string]interface{}{
|
||||||
|
"Id": "OCPCard1",
|
||||||
|
"Model": "ConnectX-6 Lx",
|
||||||
|
"Controllers": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"PCIeInterface": map[string]interface{}{
|
||||||
|
"LanesInUse": 8,
|
||||||
|
"Maxlanes": 8, // xFusion uses lowercase 'l'
|
||||||
|
"PCIeType": "Gen4",
|
||||||
|
"MaxPCIeType": "Gen4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if nic.LinkWidth != 8 || nic.MaxLinkWidth != 8 {
|
||||||
|
t.Fatalf("expected link widths 8/8 from xFusion Maxlanes, got current=%d max=%d", nic.LinkWidth, nic.MaxLinkWidth)
|
||||||
|
}
|
||||||
|
|
||||||
|
// enrichNICFromPCIe: OEM xFusion LinkWidth on a PCIeFunction doc.
|
||||||
|
nic2 := models.NetworkAdapter{}
|
||||||
|
fnDoc := map[string]interface{}{
|
||||||
|
"Oem": map[string]interface{}{
|
||||||
|
"xFusion": map[string]interface{}{
|
||||||
|
"LinkWidth": "X8",
|
||||||
|
"LinkWidthAbility": "X8",
|
||||||
|
"LinkSpeed": "Gen4 (16.0GT/s)",
|
||||||
|
"LinkSpeedAbility": "Gen4 (16.0GT/s)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
enrichNICFromPCIe(&nic2, map[string]interface{}{}, []map[string]interface{}{fnDoc}, nil)
|
||||||
|
if nic2.LinkWidth != 8 || nic2.MaxLinkWidth != 8 {
|
||||||
|
t.Fatalf("expected link width 8 from xFusion OEM LinkWidth, got current=%d max=%d", nic2.LinkWidth, nic2.MaxLinkWidth)
|
||||||
|
}
|
||||||
|
if nic2.LinkSpeed != "Gen4 (16.0GT/s)" || nic2.MaxLinkSpeed != "Gen4 (16.0GT/s)" {
|
||||||
|
t.Fatalf("expected link speed from xFusion OEM LinkSpeed, got current=%q max=%q", nic2.LinkSpeed, nic2.MaxLinkSpeed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestParseNIC_DropsUnrealisticPortCount(t *testing.T) {
|
func TestParseNIC_DropsUnrealisticPortCount(t *testing.T) {
|
||||||
nic := parseNIC(map[string]interface{}{
|
nic := parseNIC(map[string]interface{}{
|
||||||
"Id": "1",
|
"Id": "1",
|
||||||
@@ -2773,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) {
|
func TestParseGPU_UsesNestedOemSerialNumber(t *testing.T) {
|
||||||
doc := map[string]interface{}{
|
doc := map[string]interface{}{
|
||||||
"Id": "GPU4",
|
"Id": "GPU4",
|
||||||
|
|||||||
Reference in New Issue
Block a user