export: align reanimator and enrich redfish metrics
This commit is contained in:
290
internal/parser/vendors/inspur/component.go
vendored
290
internal/parser/vendors/inspur/component.go
vendored
@@ -100,10 +100,18 @@ func parseMemoryInfo(text string, hw *models.HardwareConfig) {
|
||||
return
|
||||
}
|
||||
|
||||
// Replace memory data with detailed info from component.log
|
||||
hw.Memory = nil
|
||||
var merged []models.MemoryDIMM
|
||||
seen := make(map[string]int)
|
||||
for _, existing := range hw.Memory {
|
||||
key := inspurMemoryKey(existing)
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
seen[key] = len(merged)
|
||||
merged = append(merged, existing)
|
||||
}
|
||||
for _, mem := range memInfo.MemModules {
|
||||
hw.Memory = append(hw.Memory, models.MemoryDIMM{
|
||||
item := models.MemoryDIMM{
|
||||
Slot: mem.MemModSlot,
|
||||
Location: mem.MemModSlot,
|
||||
Present: mem.MemModStatus == 1 && mem.MemModSize > 0,
|
||||
@@ -117,8 +125,18 @@ func parseMemoryInfo(text string, hw *models.HardwareConfig) {
|
||||
PartNumber: strings.TrimSpace(mem.MemModPartNum),
|
||||
Status: mem.Status,
|
||||
Ranks: mem.MemModRanks,
|
||||
})
|
||||
}
|
||||
key := inspurMemoryKey(item)
|
||||
if idx, ok := seen[key]; ok {
|
||||
mergeInspurMemoryDIMM(&merged[idx], item)
|
||||
continue
|
||||
}
|
||||
if key != "" {
|
||||
seen[key] = len(merged)
|
||||
}
|
||||
merged = append(merged, item)
|
||||
}
|
||||
hw.Memory = merged
|
||||
}
|
||||
|
||||
// PSURESTInfo represents the RESTful PSU info structure
|
||||
@@ -159,10 +177,18 @@ func parsePSUInfo(text string, hw *models.HardwareConfig) {
|
||||
return
|
||||
}
|
||||
|
||||
// Clear existing PSU data and populate with RESTful data
|
||||
hw.PowerSupply = nil
|
||||
var merged []models.PSU
|
||||
seen := make(map[string]int)
|
||||
for _, existing := range hw.PowerSupply {
|
||||
key := inspurPSUKey(existing)
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
seen[key] = len(merged)
|
||||
merged = append(merged, existing)
|
||||
}
|
||||
for _, psu := range psuInfo.PowerSupplies {
|
||||
hw.PowerSupply = append(hw.PowerSupply, models.PSU{
|
||||
item := models.PSU{
|
||||
Slot: fmt.Sprintf("PSU%d", psu.ID),
|
||||
Present: psu.Present == 1,
|
||||
Model: strings.TrimSpace(psu.Model),
|
||||
@@ -178,8 +204,18 @@ func parsePSUInfo(text string, hw *models.HardwareConfig) {
|
||||
InputVoltage: psu.PSInVolt,
|
||||
OutputVoltage: psu.PSOutVolt,
|
||||
TemperatureC: psu.PSUMaxTemp,
|
||||
})
|
||||
}
|
||||
key := inspurPSUKey(item)
|
||||
if idx, ok := seen[key]; ok {
|
||||
mergeInspurPSU(&merged[idx], item)
|
||||
continue
|
||||
}
|
||||
if key != "" {
|
||||
seen[key] = len(merged)
|
||||
}
|
||||
merged = append(merged, item)
|
||||
}
|
||||
hw.PowerSupply = merged
|
||||
}
|
||||
|
||||
// HDDRESTInfo represents the RESTful HDD info structure
|
||||
@@ -357,7 +393,16 @@ func parseNetworkAdapterInfo(text string, hw *models.HardwareConfig) {
|
||||
return
|
||||
}
|
||||
|
||||
hw.NetworkAdapters = nil
|
||||
var merged []models.NetworkAdapter
|
||||
seen := make(map[string]int)
|
||||
for _, existing := range hw.NetworkAdapters {
|
||||
key := inspurNICKey(existing)
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
seen[key] = len(merged)
|
||||
merged = append(merged, existing)
|
||||
}
|
||||
for _, adapter := range netInfo.SysAdapters {
|
||||
var macs []string
|
||||
for _, port := range adapter.Ports {
|
||||
@@ -377,7 +422,7 @@ func parseNetworkAdapterInfo(text string, hw *models.HardwareConfig) {
|
||||
vendor = normalizeModelLabel(pciids.VendorName(adapter.VendorID))
|
||||
}
|
||||
|
||||
hw.NetworkAdapters = append(hw.NetworkAdapters, models.NetworkAdapter{
|
||||
item := models.NetworkAdapter{
|
||||
Slot: fmt.Sprintf("Slot %d", adapter.Slot),
|
||||
Location: adapter.Location,
|
||||
Present: adapter.Present == 1,
|
||||
@@ -392,8 +437,231 @@ func parseNetworkAdapterInfo(text string, hw *models.HardwareConfig) {
|
||||
PortType: adapter.PortType,
|
||||
MACAddresses: macs,
|
||||
Status: adapter.Status,
|
||||
})
|
||||
}
|
||||
key := inspurNICKey(item)
|
||||
if idx, ok := seen[key]; ok {
|
||||
mergeInspurNIC(&merged[idx], item)
|
||||
continue
|
||||
}
|
||||
if slotIdx := inspurFindNICBySlot(merged, item.Slot); slotIdx >= 0 {
|
||||
mergeInspurNIC(&merged[slotIdx], item)
|
||||
if key != "" {
|
||||
seen[key] = slotIdx
|
||||
}
|
||||
continue
|
||||
}
|
||||
if key != "" {
|
||||
seen[key] = len(merged)
|
||||
}
|
||||
merged = append(merged, item)
|
||||
}
|
||||
hw.NetworkAdapters = merged
|
||||
}
|
||||
|
||||
func inspurMemoryKey(item models.MemoryDIMM) string {
|
||||
return strings.ToLower(strings.TrimSpace(inspurFirstNonEmpty(item.SerialNumber, item.Slot, item.Location)))
|
||||
}
|
||||
|
||||
func mergeInspurMemoryDIMM(dst *models.MemoryDIMM, src models.MemoryDIMM) {
|
||||
if dst == nil {
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(dst.Slot) == "" {
|
||||
dst.Slot = src.Slot
|
||||
}
|
||||
if strings.TrimSpace(dst.Location) == "" {
|
||||
dst.Location = src.Location
|
||||
}
|
||||
dst.Present = dst.Present || src.Present
|
||||
if dst.SizeMB == 0 {
|
||||
dst.SizeMB = src.SizeMB
|
||||
}
|
||||
if strings.TrimSpace(dst.Type) == "" {
|
||||
dst.Type = src.Type
|
||||
}
|
||||
if strings.TrimSpace(dst.Technology) == "" {
|
||||
dst.Technology = src.Technology
|
||||
}
|
||||
if dst.MaxSpeedMHz == 0 {
|
||||
dst.MaxSpeedMHz = src.MaxSpeedMHz
|
||||
}
|
||||
if dst.CurrentSpeedMHz == 0 {
|
||||
dst.CurrentSpeedMHz = src.CurrentSpeedMHz
|
||||
}
|
||||
if strings.TrimSpace(dst.Manufacturer) == "" {
|
||||
dst.Manufacturer = src.Manufacturer
|
||||
}
|
||||
if strings.TrimSpace(dst.SerialNumber) == "" {
|
||||
dst.SerialNumber = src.SerialNumber
|
||||
}
|
||||
if strings.TrimSpace(dst.PartNumber) == "" {
|
||||
dst.PartNumber = src.PartNumber
|
||||
}
|
||||
if strings.TrimSpace(dst.Status) == "" {
|
||||
dst.Status = src.Status
|
||||
}
|
||||
if dst.Ranks == 0 {
|
||||
dst.Ranks = src.Ranks
|
||||
}
|
||||
}
|
||||
|
||||
func inspurPSUKey(item models.PSU) string {
|
||||
return strings.ToLower(strings.TrimSpace(inspurFirstNonEmpty(item.SerialNumber, item.Slot, item.Model)))
|
||||
}
|
||||
|
||||
func mergeInspurPSU(dst *models.PSU, src models.PSU) {
|
||||
if dst == nil {
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(dst.Slot) == "" {
|
||||
dst.Slot = src.Slot
|
||||
}
|
||||
dst.Present = dst.Present || src.Present
|
||||
if strings.TrimSpace(dst.Model) == "" {
|
||||
dst.Model = src.Model
|
||||
}
|
||||
if strings.TrimSpace(dst.Vendor) == "" {
|
||||
dst.Vendor = src.Vendor
|
||||
}
|
||||
if dst.WattageW == 0 {
|
||||
dst.WattageW = src.WattageW
|
||||
}
|
||||
if strings.TrimSpace(dst.SerialNumber) == "" {
|
||||
dst.SerialNumber = src.SerialNumber
|
||||
}
|
||||
if strings.TrimSpace(dst.PartNumber) == "" {
|
||||
dst.PartNumber = src.PartNumber
|
||||
}
|
||||
if strings.TrimSpace(dst.Firmware) == "" {
|
||||
dst.Firmware = src.Firmware
|
||||
}
|
||||
if strings.TrimSpace(dst.Status) == "" {
|
||||
dst.Status = src.Status
|
||||
}
|
||||
if strings.TrimSpace(dst.InputType) == "" {
|
||||
dst.InputType = src.InputType
|
||||
}
|
||||
if dst.InputPowerW == 0 {
|
||||
dst.InputPowerW = src.InputPowerW
|
||||
}
|
||||
if dst.OutputPowerW == 0 {
|
||||
dst.OutputPowerW = src.OutputPowerW
|
||||
}
|
||||
if dst.InputVoltage == 0 {
|
||||
dst.InputVoltage = src.InputVoltage
|
||||
}
|
||||
if dst.OutputVoltage == 0 {
|
||||
dst.OutputVoltage = src.OutputVoltage
|
||||
}
|
||||
if dst.TemperatureC == 0 {
|
||||
dst.TemperatureC = src.TemperatureC
|
||||
}
|
||||
}
|
||||
|
||||
func inspurNICKey(item models.NetworkAdapter) string {
|
||||
return strings.ToLower(strings.TrimSpace(inspurFirstNonEmpty(item.SerialNumber, strings.Join(item.MACAddresses, ","), item.Slot, item.Location)))
|
||||
}
|
||||
|
||||
func mergeInspurNIC(dst *models.NetworkAdapter, src models.NetworkAdapter) {
|
||||
if dst == nil {
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(dst.Slot) == "" {
|
||||
dst.Slot = src.Slot
|
||||
}
|
||||
if strings.TrimSpace(dst.Location) == "" {
|
||||
dst.Location = src.Location
|
||||
}
|
||||
dst.Present = dst.Present || src.Present
|
||||
if strings.TrimSpace(dst.BDF) == "" {
|
||||
dst.BDF = src.BDF
|
||||
}
|
||||
if strings.TrimSpace(dst.Model) == "" {
|
||||
dst.Model = src.Model
|
||||
}
|
||||
if strings.TrimSpace(dst.Description) == "" {
|
||||
dst.Description = src.Description
|
||||
}
|
||||
if strings.TrimSpace(dst.Vendor) == "" {
|
||||
dst.Vendor = src.Vendor
|
||||
}
|
||||
if dst.VendorID == 0 {
|
||||
dst.VendorID = src.VendorID
|
||||
}
|
||||
if dst.DeviceID == 0 {
|
||||
dst.DeviceID = src.DeviceID
|
||||
}
|
||||
if strings.TrimSpace(dst.SerialNumber) == "" {
|
||||
dst.SerialNumber = src.SerialNumber
|
||||
}
|
||||
if strings.TrimSpace(dst.PartNumber) == "" {
|
||||
dst.PartNumber = src.PartNumber
|
||||
}
|
||||
if strings.TrimSpace(dst.Firmware) == "" {
|
||||
dst.Firmware = src.Firmware
|
||||
}
|
||||
if dst.PortCount == 0 {
|
||||
dst.PortCount = src.PortCount
|
||||
}
|
||||
if strings.TrimSpace(dst.PortType) == "" {
|
||||
dst.PortType = src.PortType
|
||||
}
|
||||
if dst.LinkWidth == 0 {
|
||||
dst.LinkWidth = src.LinkWidth
|
||||
}
|
||||
if strings.TrimSpace(dst.LinkSpeed) == "" {
|
||||
dst.LinkSpeed = src.LinkSpeed
|
||||
}
|
||||
if dst.MaxLinkWidth == 0 {
|
||||
dst.MaxLinkWidth = src.MaxLinkWidth
|
||||
}
|
||||
if strings.TrimSpace(dst.MaxLinkSpeed) == "" {
|
||||
dst.MaxLinkSpeed = src.MaxLinkSpeed
|
||||
}
|
||||
if dst.NUMANode == 0 {
|
||||
dst.NUMANode = src.NUMANode
|
||||
}
|
||||
if strings.TrimSpace(dst.Status) == "" {
|
||||
dst.Status = src.Status
|
||||
}
|
||||
for _, mac := range src.MACAddresses {
|
||||
mac = strings.TrimSpace(mac)
|
||||
if mac == "" {
|
||||
continue
|
||||
}
|
||||
found := false
|
||||
for _, existing := range dst.MACAddresses {
|
||||
if strings.EqualFold(strings.TrimSpace(existing), mac) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
dst.MACAddresses = append(dst.MACAddresses, mac)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func inspurFindNICBySlot(items []models.NetworkAdapter, slot string) int {
|
||||
slot = strings.ToLower(strings.TrimSpace(slot))
|
||||
if slot == "" {
|
||||
return -1
|
||||
}
|
||||
for i := range items {
|
||||
if strings.ToLower(strings.TrimSpace(items[i].Slot)) == slot {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func inspurFirstNonEmpty(values ...string) string {
|
||||
for _, value := range values {
|
||||
if strings.TrimSpace(value) != "" {
|
||||
return strings.TrimSpace(value)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func parseFanSensors(text string) []models.SensorReading {
|
||||
|
||||
58
internal/parser/vendors/inspur/component_test.go
vendored
58
internal/parser/vendors/inspur/component_test.go
vendored
@@ -51,6 +51,64 @@ RESTful fan`
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNetworkAdapterInfo_MergesIntoExistingInventory(t *testing.T) {
|
||||
text := `RESTful Network Adapter info:
|
||||
{
|
||||
"sys_adapters": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "NIC1",
|
||||
"Location": "#CPU0_PCIE4",
|
||||
"present": 1,
|
||||
"slot": 4,
|
||||
"vendor_id": 32902,
|
||||
"device_id": 5409,
|
||||
"vendor": "Mellanox",
|
||||
"model": "ConnectX-6",
|
||||
"fw_ver": "22.1.0",
|
||||
"status": "OK",
|
||||
"sn": "",
|
||||
"pn": "",
|
||||
"port_num": 2,
|
||||
"port_type": "QSFP",
|
||||
"ports": [
|
||||
{ "id": 1, "mac_addr": "00:11:22:33:44:55" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RESTful fan`
|
||||
|
||||
hw := &models.HardwareConfig{
|
||||
NetworkAdapters: []models.NetworkAdapter{
|
||||
{
|
||||
Slot: "Slot 4",
|
||||
BDF: "0000:17:00.0",
|
||||
SerialNumber: "NIC-SN-1",
|
||||
Present: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
parseNetworkAdapterInfo(text, hw)
|
||||
|
||||
if len(hw.NetworkAdapters) != 1 {
|
||||
t.Fatalf("expected merged single adapter, got %d", len(hw.NetworkAdapters))
|
||||
}
|
||||
got := hw.NetworkAdapters[0]
|
||||
if got.BDF != "0000:17:00.0" {
|
||||
t.Fatalf("expected existing BDF to survive merge, got %q", got.BDF)
|
||||
}
|
||||
if got.Model != "ConnectX-6" {
|
||||
t.Fatalf("expected model from component log, got %q", got.Model)
|
||||
}
|
||||
if got.SerialNumber != "NIC-SN-1" {
|
||||
t.Fatalf("expected serial from existing inventory to survive merge, got %q", got.SerialNumber)
|
||||
}
|
||||
if len(got.MACAddresses) != 1 || got.MACAddresses[0] != "00:11:22:33:44:55" {
|
||||
t.Fatalf("expected MAC addresses from component log, got %#v", got.MACAddresses)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseComponentLogSensors_ExtractsFanBackplaneAndPSUSummary(t *testing.T) {
|
||||
text := `RESTful PSU info:
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user