fix: dedup GPUs across multiple chassis PCIeDevice trees in Redfish collector
Supermicro HGX exposes each GPU under both Chassis/1/PCIeDevices and a dedicated Chassis/HGX_GPU_SXM_N/PCIeDevices. gpuDocDedupKey was keying by @odata.id path, so identical GPUs with the same serial were not deduplicated across sources. Now stable identifiers (serial → BDF → slot+model) take priority over path. Also includes Inspur parser improvements: NVMe model/serial enrichment from devicefrusdr.log and audit.log, RAID drive slot normalization to BP notation, PSU slot normalization, BMC/CPLD/VR firmware from RESTful version info section, and parser version bump to 1.8. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
79
internal/parser/vendors/inspur/pcie.go
vendored
79
internal/parser/vendors/inspur/pcie.go
vendored
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.mchus.pro/mchus/logpile/internal/models"
|
||||
@@ -37,6 +38,84 @@ type PCIeRESTInfo []struct {
|
||||
FwVer string `json:"fw_ver"`
|
||||
}
|
||||
|
||||
// ParsePCIeSlotDeviceNames parses devicefrusdr.log and returns a map from integer PCIe slot ID
|
||||
// to device name string. Used to enrich HddInfo entries in asset.json that lack model names.
|
||||
func ParsePCIeSlotDeviceNames(content []byte) map[int]string {
|
||||
info, ok := parsePCIeRESTJSON(content)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
result := make(map[int]string, len(info))
|
||||
for _, entry := range info {
|
||||
if entry.Slot <= 0 {
|
||||
continue
|
||||
}
|
||||
name := sanitizePCIeDeviceName(entry.DeviceName)
|
||||
if name != "" {
|
||||
result[entry.Slot] = name
|
||||
}
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// parsePCIeRESTJSON parses the RESTful PCIE Device info JSON from devicefrusdr.log content.
|
||||
func parsePCIeRESTJSON(content []byte) (PCIeRESTInfo, bool) {
|
||||
text := string(content)
|
||||
startMarker := "RESTful PCIE Device info:"
|
||||
endMarker := "BMC sdr Info:"
|
||||
|
||||
startIdx := strings.Index(text, startMarker)
|
||||
if startIdx == -1 {
|
||||
return nil, false
|
||||
}
|
||||
endIdx := strings.Index(text[startIdx:], endMarker)
|
||||
if endIdx == -1 {
|
||||
endIdx = len(text) - startIdx
|
||||
}
|
||||
jsonText := strings.TrimSpace(text[startIdx+len(startMarker) : startIdx+endIdx])
|
||||
|
||||
var info PCIeRESTInfo
|
||||
if err := json.Unmarshal([]byte(jsonText), &info); err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return info, true
|
||||
}
|
||||
|
||||
// ParsePCIeNVMeLocToSlot parses devicefrusdr.log and returns a map from NVMe location number
|
||||
// (the numeric suffix in "#NVME0", "#NVME2", etc.) to the integer PCIe slot ID.
|
||||
// This is used to correlate audit.log NVMe disk numbers with HddInfo PcieSlot values.
|
||||
func ParsePCIeNVMeLocToSlot(content []byte) map[int]int {
|
||||
info, ok := parsePCIeRESTJSON(content)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
nvmeLocRegex := regexp.MustCompile(`(?i)^#NVME(\d+)$`)
|
||||
result := make(map[int]int)
|
||||
for _, entry := range info {
|
||||
if entry.Slot <= 0 {
|
||||
continue
|
||||
}
|
||||
loc := strings.TrimSpace(entry.Location)
|
||||
m := nvmeLocRegex.FindStringSubmatch(loc)
|
||||
if m == nil {
|
||||
continue
|
||||
}
|
||||
locNum, err := strconv.Atoi(m[1])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
result[locNum] = entry.Slot
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ParsePCIeDevices parses RESTful PCIE Device info from devicefrusdr.log
|
||||
func ParsePCIeDevices(content []byte) []models.PCIeDevice {
|
||||
text := string(content)
|
||||
|
||||
Reference in New Issue
Block a user