Unify Redfish analysis through raw replay and add storage volumes

This commit is contained in:
Mikhail Chusavitin
2026-02-24 18:34:13 +03:00
parent 7a1285db99
commit 66fb90233f
5 changed files with 294 additions and 109 deletions

View File

@@ -51,6 +51,7 @@ func ReplayRedfishFromRawPayloads(rawPayloads map[string]any, emit ProgressFn) (
processors, _ := r.getCollectionMembers(joinPath(primarySystem, "/Processors"))
memory, _ := r.getCollectionMembers(joinPath(primarySystem, "/Memory"))
storageDevices := r.collectStorage(primarySystem)
storageVolumes := r.collectStorageVolumes(primarySystem)
if emit != nil {
emit(Progress{Status: "running", Progress: 80, Message: "Redfish snapshot: replay network/BMC..."})
@@ -74,6 +75,7 @@ func ReplayRedfishFromRawPayloads(rawPayloads map[string]any, emit ProgressFn) (
CPUs: parseCPUs(processors),
Memory: parseMemory(memory),
Storage: storageDevices,
Volumes: storageVolumes,
PCIeDevices: pcieDevices,
GPUs: gpus,
PowerSupply: psus,
@@ -256,6 +258,15 @@ func (r redfishSnapshotReader) collectStorage(systemPath string) []models.Storag
}
}
for _, driveDoc := range r.collectKnownStorageMembers(systemPath, []string{
"/Storage/IntelVROC/Drives",
"/Storage/IntelVROC/Controllers/1/Drives",
}) {
if looksLikeDrive(driveDoc) {
out = append(out, parseDrive(driveDoc))
}
}
simpleStorageMembers, _ := r.getCollectionMembers(joinPath(systemPath, "/SimpleStorage"))
for _, member := range simpleStorageMembers {
devices, ok := member["Devices"].([]interface{})
@@ -298,6 +309,49 @@ func (r redfishSnapshotReader) collectStorage(systemPath string) []models.Storag
return dedupeStorage(out)
}
func (r redfishSnapshotReader) collectStorageVolumes(systemPath string) []models.StorageVolume {
var out []models.StorageVolume
storageMembers, _ := r.getCollectionMembers(joinPath(systemPath, "/Storage"))
for _, member := range storageMembers {
controller := firstNonEmpty(asString(member["Id"]), asString(member["Name"]))
volumeCollectionPath := redfishLinkedPath(member, "Volumes")
if volumeCollectionPath == "" {
continue
}
volumeDocs, err := r.getCollectionMembers(volumeCollectionPath)
if err != nil {
continue
}
for _, volDoc := range volumeDocs {
if looksLikeVolume(volDoc) {
out = append(out, parseStorageVolume(volDoc, controller))
}
}
}
for _, volDoc := range r.collectKnownStorageMembers(systemPath, []string{
"/Storage/IntelVROC/Volumes",
"/Storage/HA-RAID/Volumes",
"/Storage/MRVL.HA-RAID/Volumes",
}) {
if looksLikeVolume(volDoc) {
out = append(out, parseStorageVolume(volDoc, storageControllerFromPath(asString(volDoc["@odata.id"]))))
}
}
return dedupeStorageVolumes(out)
}
func (r redfishSnapshotReader) collectKnownStorageMembers(systemPath string, relativeCollections []string) []map[string]interface{} {
var out []map[string]interface{}
for _, rel := range relativeCollections {
docs, err := r.getCollectionMembers(joinPath(systemPath, rel))
if err != nil || len(docs) == 0 {
continue
}
out = append(out, docs...)
}
return out
}
func (r redfishSnapshotReader) probeSupermicroNVMeDiskBays(backplanePath string) []map[string]interface{} {
return r.probeDirectDiskBayChildren(joinPath(backplanePath, "/Drives"))
}
@@ -351,6 +405,12 @@ func (r redfishSnapshotReader) collectPSUs(chassisPaths []string) []models.PSU {
seen := make(map[string]struct{})
idx := 1
for _, chassisPath := range chassisPaths {
if memberDocs, err := r.getCollectionMembers(joinPath(chassisPath, "/PowerSubsystem/PowerSupplies")); err == nil && len(memberDocs) > 0 {
for _, doc := range memberDocs {
idx = appendPSU(&out, seen, parsePSU(doc, idx), idx)
}
continue
}
if powerDoc, err := r.getJSON(joinPath(chassisPath, "/Power")); err == nil {
if members, ok := powerDoc["PowerSupplies"].([]interface{}); ok && len(members) > 0 {
for _, item := range members {
@@ -358,37 +418,10 @@ func (r redfishSnapshotReader) collectPSUs(chassisPaths []string) []models.PSU {
if !ok {
continue
}
psu := parsePSU(doc, idx)
idx++
key := firstNonEmpty(psu.SerialNumber, psu.Slot+"|"+psu.Model)
if key == "" {
continue
}
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
out = append(out, psu)
idx = appendPSU(&out, seen, parsePSU(doc, idx), idx)
}
}
}
memberDocs, err := r.getCollectionMembers(joinPath(chassisPath, "/PowerSubsystem/PowerSupplies"))
if err != nil || len(memberDocs) == 0 {
continue
}
for _, doc := range memberDocs {
psu := parsePSU(doc, idx)
idx++
key := firstNonEmpty(psu.SerialNumber, psu.Slot+"|"+psu.Model)
if key == "" {
continue
}
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
out = append(out, psu)
}
}
return out
}