fix(collector): surface RAID/HBA controller cards from Storage.StorageControllers[]

The RAID controller (XFusion XC170-M-8i / Broadcom SAS3808) never appeared
in inventory at all: it isn't listed in any Chassis/Systems PCIeDevices
collection on this BMC, and its dedicated Board resource link 404s (id
contains parentheses, same class of bug as the OCP NIC fixed earlier). Its
full identity -- model, firmware, BDF, vendor/device IDs -- was sitting
unread in the Storage resource's embedded StorageControllers[] array the
whole time.

Added parseStorageControllerPCIeDevice + collectStorageControllers to read
that array and surface the controller as a PCIeDevice entry, merged into
the existing pcie_devices list.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Mikhail Chusavitin
2026-08-18 14:30:56 +03:00
co-authored by Claude Sonnet 5
parent 1e4ec513e1
commit 6599ab49c2
4 changed files with 176 additions and 0 deletions
+46
View File
@@ -4552,6 +4552,52 @@ func parsePCIeFunctionWithSupplementalDocs(doc map[string]interface{}, supplemen
return dev
}
// parseStorageControllerPCIeDevice builds a PCIeDevice entry from a
// Storage.StorageControllers[] array member (Redfish's dedicated RAID/HBA
// controller resource). Some BMCs (e.g. xFusion) never expose this
// controller as a standalone PCIeDevice/Board resource — its Board link can
// even 404 (id containing parentheses) — so without reading this embedded
// array the RAID controller card never appears in inventory at all, even
// though its identity (model, firmware, BDF, vendor/device IDs) is fully
// present right here.
func parseStorageControllerPCIeDevice(ctrl map[string]interface{}) models.PCIeDevice {
oem := redfishOEMxFusionSection(ctrl)
dev := models.PCIeDevice{
Slot: firstNonEmpty(
asString(ctrl["CardModel"]),
asString(ctrl["Name"]),
asString(ctrl["MemberId"]),
),
BDF: sanitizeRedfishBDF(asString(oem["BDF"])),
DeviceClass: "RAIDController",
Manufacturer: firstNonEmpty(asString(ctrl["Manufacturer"]), asString(ctrl["CardManufacturer"])),
Model: firstNonEmpty(asString(ctrl["CardModel"]), asString(oem["Type"])),
Firmware: asString(ctrl["FirmwareVersion"]),
PartNumber: normalizeRedfishIdentityField(asString(ctrl["PartNumber"])),
SerialNumber: findFirstNormalizedStringByKeys(ctrl, "SerialNumber"),
VendorID: asHexOrInt(oem["VenderID"]),
DeviceID: asHexOrInt(oem["DeviceID"]),
Status: mapStatus(ctrl["Status"]),
}
if dev.Slot == "" {
dev.Slot = "RAIDController"
}
return dev
}
func redfishOEMxFusionSection(doc map[string]interface{}) map[string]interface{} {
oem, ok := doc["Oem"].(map[string]interface{})
if !ok {
return nil
}
xfusion, ok := oem["xFusion"].(map[string]interface{})
if !ok {
return nil
}
return xfusion
}
func isMissingOrRawPCIModel(model string) bool {
model = strings.TrimSpace(model)
if model == "" {