package dell import ( "strings" "git.mchus.pro/mchus/logpile/internal/collector" "git.mchus.pro/mchus/logpile/internal/models" "git.mchus.pro/mchus/logpile/internal/parser" "git.mchus.pro/mchus/logpile/internal/parser/vendors/redfishtree" ) // redfishidracwalk.tar.gz is the inventory source on newer iDRAC10-generation // TSR bundles, which no longer ship the sysinfo_dcim_view.xml / softwareidentity // CIM-XML files. It is a captured directory dump of the iDRAC's own Redfish // tree (see vendors/redfishtree for the detection/parsing rule), stored under // tsr/hardware/sysinfo/inventory/. func findRedfishWalkArchive(expanded []parser.ExtractedFile) *parser.ExtractedFile { for i := range expanded { path := strings.ToLower(strings.TrimSpace(expanded[i].Path)) if strings.HasSuffix(path, "redfishidracwalk.tar.gz") { return &expanded[i] } } // Fall back to the vendor-independent structural detection, in case a // future iDRAC generation renames the export. for _, f := range redfishtree.FindCandidateArchives(expanded) { if redfishtree.Build(f.Content) != nil { f := f return &f } } return nil } // parseRedfishWalk enriches result with hardware inventory replayed from a // captured Redfish tree dump. It only adds data; existing dedupe passes in // Parse() resolve overlaps with the DCIM-XML-derived source in favor of // whichever entry was appended first (DCIM-derived data, when present). func parseRedfishWalk(f *parser.ExtractedFile, result *models.AnalysisResult) { if f == nil { return } tree := redfishtree.Build(f.Content) if tree == nil { return } replayed, err := collector.ReplayRedfishFromRawPayloads(map[string]any{"redfish_tree": tree}, nil) if err != nil || replayed == nil { return } mergeRedfishReplay(result, replayed) } func mergeRedfishReplay(result *models.AnalysisResult, replayed *models.AnalysisResult) { if result.Hardware == nil { result.Hardware = &models.HardwareConfig{} } hw := result.Hardware if rhw := replayed.Hardware; rhw != nil { setIfEmpty(&hw.BoardInfo.Manufacturer, rhw.BoardInfo.Manufacturer) setIfEmpty(&hw.BoardInfo.ProductName, rhw.BoardInfo.ProductName) setIfEmpty(&hw.BoardInfo.Description, rhw.BoardInfo.Description) setIfEmpty(&hw.BoardInfo.SerialNumber, rhw.BoardInfo.SerialNumber) setIfEmpty(&hw.BoardInfo.PartNumber, rhw.BoardInfo.PartNumber) setIfEmpty(&hw.BoardInfo.Version, rhw.BoardInfo.Version) setIfEmpty(&hw.BoardInfo.UUID, rhw.BoardInfo.UUID) setIfEmpty(&hw.BoardInfo.BMCMACAddress, rhw.BoardInfo.BMCMACAddress) hw.Firmware = append(hw.Firmware, rhw.Firmware...) hw.CPUs = append(hw.CPUs, rhw.CPUs...) hw.Memory = append(hw.Memory, rhw.Memory...) hw.Storage = append(hw.Storage, rhw.Storage...) hw.Volumes = append(hw.Volumes, rhw.Volumes...) hw.PCIeDevices = append(hw.PCIeDevices, rhw.PCIeDevices...) hw.GPUs = append(hw.GPUs, rhw.GPUs...) hw.NetworkAdapters = append(hw.NetworkAdapters, rhw.NetworkAdapters...) hw.PowerSupply = append(hw.PowerSupply, rhw.PowerSupply...) hw.Licenses = append(hw.Licenses, rhw.Licenses...) } result.Sensors = append(result.Sensors, replayed.Sensors...) result.FRU = append(result.FRU, replayed.FRU...) result.Events = append(result.Events, replayed.Events...) if result.InventoryLastModifiedAt.IsZero() { result.InventoryLastModifiedAt = replayed.InventoryLastModifiedAt } }