fix(exporter): disambiguate PSU slot collisions to avoid Reanimator "other"
Some collectors (BEE-SP observed so far) report every PSU at slot "0". The export itself already keeps every PSU as a separate record (dedupePSUs keys on serial first), but Reanimator relies on slot to track installed position, so PSUs sharing a slot value land in "other components" instead of being tracked as PSU installations. Renumber colliding slots to stable 0-based positions by encounter order. This doesn't invent a serial or other identity — the PSUs are already distinguished by serial — it only assigns the position field Reanimator needs when the source failed to. Files where every PSU already has a unique slot are unaffected. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
48baae41f4
commit
a590242382
@@ -49,7 +49,7 @@ func ConvertToReanimator(result *models.AnalysisResult) (*ReanimatorExport, erro
|
|||||||
Memory: dedupeMemory(convertMemoryFromDevices(devices, collectedAt)),
|
Memory: dedupeMemory(convertMemoryFromDevices(devices, collectedAt)),
|
||||||
Storage: dedupeStorage(convertStorageFromDevices(devices, collectedAt)),
|
Storage: dedupeStorage(convertStorageFromDevices(devices, collectedAt)),
|
||||||
PCIeDevices: dedupePCIe(convertPCIeFromDevices(devices, collectedAt)),
|
PCIeDevices: dedupePCIe(convertPCIeFromDevices(devices, collectedAt)),
|
||||||
PowerSupplies: dedupePSUs(convertPSUsFromDevices(devices, collectedAt)),
|
PowerSupplies: disambiguatePSUSlots(dedupePSUs(convertPSUsFromDevices(devices, collectedAt))),
|
||||||
Sensors: convertSensors(result.Sensors),
|
Sensors: convertSensors(result.Sensors),
|
||||||
EventLogs: convertEventLogs(result.Events, collectedAt),
|
EventLogs: convertEventLogs(result.Events, collectedAt),
|
||||||
Licenses: dedupeLicenses(convertLicenses(result.Hardware.Licenses, collectedAt)),
|
Licenses: dedupeLicenses(convertLicenses(result.Hardware.Licenses, collectedAt)),
|
||||||
@@ -2183,6 +2183,35 @@ func dedupePSUs(items []ReanimatorPSU) []ReanimatorPSU {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// disambiguatePSUSlots renumbers PSU slots when two or more distinct PSUs
|
||||||
|
// (different serials, already survived dedupePSUs) report the same slot
|
||||||
|
// string from the source. Some collectors (e.g. BEE-SP) report every PSU
|
||||||
|
// at slot "0". A shared slot value prevents Reanimator from telling the
|
||||||
|
// PSUs apart by installed position, so they land in "other components"
|
||||||
|
// instead of being tracked as PSU installations. Reassigning stable 0-based
|
||||||
|
// positions by encounter order is the best available disambiguation without
|
||||||
|
// inventing a serial or other identity data we don't have.
|
||||||
|
func disambiguatePSUSlots(items []ReanimatorPSU) []ReanimatorPSU {
|
||||||
|
if len(items) < 2 {
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
counts := make(map[string]int, len(items))
|
||||||
|
for _, it := range items {
|
||||||
|
counts[strings.ToLower(strings.TrimSpace(it.Slot))]++
|
||||||
|
}
|
||||||
|
next := make(map[string]int, len(items))
|
||||||
|
for i := range items {
|
||||||
|
key := strings.ToLower(strings.TrimSpace(items[i].Slot))
|
||||||
|
if counts[key] < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
idx := next[key]
|
||||||
|
next[key]++
|
||||||
|
items[i].Slot = strconv.Itoa(idx)
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
func dedupePCIe(items []ReanimatorPCIe) []ReanimatorPCIe {
|
func dedupePCIe(items []ReanimatorPCIe) []ReanimatorPCIe {
|
||||||
if len(items) < 2 {
|
if len(items) < 2 {
|
||||||
return items
|
return items
|
||||||
|
|||||||
Reference in New Issue
Block a user