fix(parser,exporter): reconcile BMC-dump and live-CD exports of the same server
The audit tool that ingests Reanimator exports treats any per-component field
change between imports as a component replacement. Importing an xFusion BMC dump
and an easy_bee BEE-SP bundle for one server produced large spurious diffs.
xfusion:
- parseMemInfo tolerates a stray 0x0A inside the binary SPD "bom number" column
(it was splitting a DIMM record in two and emitting a phantom "slot s" module)
- DIMM slot from the "dimm name" column ("DIMM071"), mainboard "location" dropped
- GPU slot = BDF (Reanimator contract)
- NIC emitted per PCI function from netcard_info.txt (BDF + per-port MAC, shared
card serial) instead of one card-level adapter with the wrong BDF, which had
been colliding with a GPU and vanishing in dedup
- NIC manufacturer left blank when it is the system OEM so the exporter resolves
the silicon vendor from pci.ids
- "(U6216)" chip designator stripped from firmware versions
easy_bee:
- PSU bay numbers rebased 0-indexed -> 1-indexed; bare single-letter PSU
"firmware" (a leaked FRU version) cleared
- board.part_number taken from the bundle's ipmitool-fru.txt chassis
"Product Part Number" to match the BMC value
exporter (cross-vendor):
- canonicalMemorySlot: drop dmidecode "(J)" channel tag, Memory111 -> DIMM111
- canonicalGPUModel: NVIDIA DC GPUs reduce to the bare chip token
- canonicalStorageMediaAndInterface: NVMe is a bus not a medium
- manufacturerFromStorageModel: fill blank drive vendor from the model prefix
- isRemovableUSBStorageDevice: drop live-CD boot sticks
- isOnboardControllerPCIeDevice: drop SATA/NVMe/MegaRAID/PCIe-switch controller
functions that only an lspci scan reports (keep add-in cards with an identity)
For the reference server every physical component now appears in both exports
keyed identically; residual diffs are one-sided enrichment only.
Refs ADL-061, ADL-062.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VFy4m7cVv4cqp25jJh2gSB
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
5b65c99b0e
commit
9b2c654182
+80
-1
@@ -3,6 +3,7 @@ package easy_bee
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -122,10 +123,14 @@ func (p *Parser) Parse(files []parser.ExtractedFile) (*models.AnalysisResult, er
|
||||
GPUs: append([]models.GPU(nil), snapshot.Hardware.GPUs...),
|
||||
NetworkCards: append([]models.NIC(nil), snapshot.Hardware.NetworkCards...),
|
||||
NetworkAdapters: normalizeNetworkAdapters(snapshot.Hardware.NetworkAdapters),
|
||||
PowerSupply: append([]models.PSU(nil), snapshot.Hardware.PowerSupply...),
|
||||
PowerSupply: normalizePSUSlots(snapshot.Hardware.PowerSupply),
|
||||
},
|
||||
}
|
||||
|
||||
if pn := chassisProductPartNumber(files, result.Hardware.BoardInfo.ProductName); pn != "" {
|
||||
result.Hardware.BoardInfo.PartNumber = pn
|
||||
}
|
||||
|
||||
result.Events = append(result.Events, snapshot.Events...)
|
||||
result.Events = append(result.Events, convertRuntimeToEvents(snapshot.Runtime, result.CollectedAt)...)
|
||||
result.Events = append(result.Events, convertEventLogs(snapshot.Hardware.EventLogs)...)
|
||||
@@ -524,6 +529,80 @@ func normalizePCIeDevices(items []models.PCIeDevice) []models.PCIeDevice {
|
||||
return out
|
||||
}
|
||||
|
||||
// normalizePSUSlots rebases 0-indexed PSU bay numbers to 1-indexed, matching the
|
||||
// BMC/vendor convention (bays are labelled PSU1..PSUn on the chassis). The
|
||||
// easy-bee snapshot numbers them from 0; without this, the same server imported
|
||||
// from a BMC dump vs. a live-CD run would show every PSU as moved to a new bay.
|
||||
// chassisProductPartNumber pulls the chassis-level "Product Part Number" out of
|
||||
// the bundle's ipmitool-fru.txt for the FRU whose "Product Name" matches the
|
||||
// board product name. The easy-bee snapshot records the baseboard's own product
|
||||
// name ("BC15MBSF") as board.part_number, while a BMC dump reports the chassis
|
||||
// product part number ("0619KUGG") — this aligns easy-bee to the BMC value.
|
||||
func chassisProductPartNumber(files []parser.ExtractedFile, boardProductName string) string {
|
||||
boardProductName = strings.TrimSpace(boardProductName)
|
||||
if boardProductName == "" {
|
||||
return ""
|
||||
}
|
||||
var fru *parser.ExtractedFile
|
||||
for i := range files {
|
||||
if strings.HasSuffix(strings.ToLower(files[i].Path), "ipmitool-fru.txt") {
|
||||
fru = &files[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if fru == nil {
|
||||
return ""
|
||||
}
|
||||
var lastProductName string
|
||||
for _, line := range strings.Split(string(fru.Content), "\n") {
|
||||
key, value, ok := strings.Cut(line, ":")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
key = strings.TrimSpace(key)
|
||||
value = strings.TrimSpace(value)
|
||||
switch key {
|
||||
case "Product Name":
|
||||
lastProductName = value
|
||||
case "Product Part Number":
|
||||
if strings.EqualFold(lastProductName, boardProductName) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func normalizePSUSlots(items []models.PSU) []models.PSU {
|
||||
out := append([]models.PSU(nil), items...)
|
||||
for i := range out {
|
||||
// The easy-bee snapshot leaks the FRU "Product Version" (a bare letter like
|
||||
// "B") into the firmware field; that is not a firmware revision.
|
||||
if fw := strings.TrimSpace(out[i].Firmware); len(fw) == 1 {
|
||||
out[i].Firmware = ""
|
||||
}
|
||||
}
|
||||
minSlot, allNumeric := -1, len(out) > 0
|
||||
for _, p := range out {
|
||||
n, err := strconv.Atoi(strings.TrimSpace(p.Slot))
|
||||
if err != nil {
|
||||
allNumeric = false
|
||||
break
|
||||
}
|
||||
if minSlot == -1 || n < minSlot {
|
||||
minSlot = n
|
||||
}
|
||||
}
|
||||
if !allNumeric || minSlot != 0 {
|
||||
return out
|
||||
}
|
||||
for i := range out {
|
||||
n, _ := strconv.Atoi(strings.TrimSpace(out[i].Slot))
|
||||
out[i].Slot = strconv.Itoa(n + 1)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func normalizeNetworkAdapters(items []models.NetworkAdapter) []models.NetworkAdapter {
|
||||
out := append([]models.NetworkAdapter(nil), items...)
|
||||
for i := range out {
|
||||
|
||||
Reference in New Issue
Block a user