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:
94
internal/parser/vendors/inspur/audit.go
vendored
Normal file
94
internal/parser/vendors/inspur/audit.go
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
package inspur
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// auditSNChangedNVMeRegex matches:
|
||||
// "Front Back Plane N NVMe DiskM SN changed from X to Y"
|
||||
// Captures: disk_num, new_serial
|
||||
var auditSNChangedNVMeRegex = regexp.MustCompile(`NVMe Disk(\d+)\s+SN changed from \S+\s+to\s+(\S+)`)
|
||||
|
||||
// auditSNChangedRAIDRegex matches:
|
||||
// "Raid(Pcie Slot:N) HDD(enclosure id:E slot:S) SN changed from X to Y"
|
||||
// Captures: pcie_slot, enclosure_id, slot_num, new_serial
|
||||
var auditSNChangedRAIDRegex = regexp.MustCompile(`Raid\(Pcie Slot:(\d+)\) HDD\(enclosure id:(\d+) slot:(\d+)\)\s+SN changed from \S+\s+to\s+(\S+)`)
|
||||
|
||||
// ParseAuditLogNVMeSerials parses audit.log and returns the final (latest) serial number
|
||||
// per NVMe disk number. The disk number matches the numeric suffix in PCIe location
|
||||
// strings like "#NVME0", "#NVME2", etc. from devicefrusdr.log.
|
||||
// Entries where the serial changed to "NULL" are excluded.
|
||||
func ParseAuditLogNVMeSerials(content []byte) map[int]string {
|
||||
serials := make(map[int]string)
|
||||
|
||||
for _, line := range strings.Split(string(content), "\n") {
|
||||
m := auditSNChangedNVMeRegex.FindStringSubmatch(line)
|
||||
if m == nil {
|
||||
continue
|
||||
}
|
||||
diskNum, err := strconv.Atoi(m[1])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
serial := strings.TrimSpace(m[2])
|
||||
if strings.EqualFold(serial, "NULL") || serial == "" {
|
||||
delete(serials, diskNum)
|
||||
} else {
|
||||
serials[diskNum] = serial
|
||||
}
|
||||
}
|
||||
if len(serials) == 0 {
|
||||
return nil
|
||||
}
|
||||
return serials
|
||||
}
|
||||
|
||||
// ParseAuditLogRAIDSerials parses audit.log and returns the final (latest) serial number
|
||||
// per RAID backplane disk. Key format is "BP{enclosure_id-1}:{slot_num}" (e.g. "BP0:0").
|
||||
//
|
||||
// Each disk slot is claimed by a specific RAID controller (Pcie Slot:N). NULL events from
|
||||
// an old controller do not clear serials assigned by a newer controller, preventing stale
|
||||
// deletions when disks are migrated between RAID arrays.
|
||||
func ParseAuditLogRAIDSerials(content []byte) map[string]string {
|
||||
// owner tracks which PCIe RAID controller slot last assigned a serial to a disk key.
|
||||
serials := make(map[string]string)
|
||||
owner := make(map[string]int)
|
||||
|
||||
for _, line := range strings.Split(string(content), "\n") {
|
||||
m := auditSNChangedRAIDRegex.FindStringSubmatch(line)
|
||||
if m == nil {
|
||||
continue
|
||||
}
|
||||
pcieSlot, err := strconv.Atoi(m[1])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
enclosureID, err := strconv.Atoi(m[2])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
slotNum, err := strconv.Atoi(m[3])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
serial := strings.TrimSpace(m[4])
|
||||
key := fmt.Sprintf("BP%d:%d", enclosureID-1, slotNum)
|
||||
if strings.EqualFold(serial, "NULL") || serial == "" {
|
||||
// Only clear if this controller was the last to set the serial.
|
||||
if owner[key] == pcieSlot {
|
||||
delete(serials, key)
|
||||
delete(owner, key)
|
||||
}
|
||||
} else {
|
||||
serials[key] = serial
|
||||
owner[key] = pcieSlot
|
||||
}
|
||||
}
|
||||
if len(serials) == 0 {
|
||||
return nil
|
||||
}
|
||||
return serials
|
||||
}
|
||||
Reference in New Issue
Block a user