Files
logpile/internal/parser/vendors/inspur/component.go
Michael Chus 512957545a Add LOGPile BMC diagnostic log analyzer
Features:
- Modular parser architecture for vendor-specific formats
- Inspur/Kaytus parser supporting asset.json, devicefrusdr.log,
  component.log, idl.log, and syslog files
- PCI Vendor/Device ID lookup for hardware identification
- Web interface with tabs: Events, Sensors, Config, Serials, Firmware
- Server specification summary with component grouping
- Export to CSV, JSON, TXT formats
- BMC alarm parsing from IDL logs (memory errors, PSU events, etc.)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 04:11:23 +03:00

148 lines
4.0 KiB
Go

package inspur
import (
"encoding/json"
"fmt"
"regexp"
"strings"
"time"
"git.mchus.pro/mchus/logpile/internal/models"
)
// ParseComponentLog parses component.log file and extracts PSU and other info
func ParseComponentLog(content []byte, hw *models.HardwareConfig) {
if hw == nil {
return
}
text := string(content)
// Parse RESTful PSU info
parsePSUInfo(text, hw)
}
// ParseComponentLogEvents extracts events from component.log (memory errors, etc.)
func ParseComponentLogEvents(content []byte) []models.Event {
var events []models.Event
text := string(content)
// Parse RESTful Memory info for Warning/Error status
memEvents := parseMemoryEvents(text)
events = append(events, memEvents...)
return events
}
// PSUInfo represents the RESTful PSU info structure
type PSUInfo struct {
PowerSupplies []struct {
ID int `json:"id"`
Present int `json:"present"`
VendorID string `json:"vendor_id"`
Model string `json:"model"`
SerialNum string `json:"serial_num"`
FwVer string `json:"fw_ver"`
RatedPower int `json:"rated_power"`
Status string `json:"status"`
} `json:"power_supplies"`
}
func parsePSUInfo(text string, hw *models.HardwareConfig) {
// Find RESTful PSU info section
re := regexp.MustCompile(`RESTful PSU info:\s*(\{[\s\S]*?\})\s*(?:RESTful|BMC|$)`)
match := re.FindStringSubmatch(text)
if match == nil {
return
}
jsonStr := match[1]
// Clean up the JSON (it might have newlines)
jsonStr = strings.ReplaceAll(jsonStr, "\n", "")
var psuInfo PSUInfo
if err := json.Unmarshal([]byte(jsonStr), &psuInfo); err != nil {
return
}
// Clear existing PSU data and populate with RESTful data
hw.PowerSupply = nil
for _, psu := range psuInfo.PowerSupplies {
if psu.Present != 1 {
continue
}
hw.PowerSupply = append(hw.PowerSupply, models.PSU{
Slot: formatPSUSlot(psu.ID),
Model: psu.Model,
WattageW: psu.RatedPower,
SerialNumber: psu.SerialNum,
Status: psu.Status,
})
}
}
func formatPSUSlot(id int) string {
return fmt.Sprintf("PSU%d", id)
}
// MemoryInfo represents the RESTful Memory info structure
type MemoryInfo struct {
MemModules []struct {
MemModID int `json:"mem_mod_id"`
MemModSlot string `json:"mem_mod_slot"`
MemModSize int `json:"mem_mod_size"`
MemModVendor string `json:"mem_mod_vendor"`
MemModPartNum string `json:"mem_mod_part_num"`
MemModSerial string `json:"mem_mod_serial_num"`
Status string `json:"status"`
} `json:"mem_modules"`
}
func parseMemoryEvents(text string) []models.Event {
var events []models.Event
// Find RESTful Memory info section
re := regexp.MustCompile(`RESTful Memory info:\s*(\{[\s\S]*?\})\s*RESTful HDD`)
match := re.FindStringSubmatch(text)
if match == nil {
return events
}
jsonStr := match[1]
jsonStr = strings.ReplaceAll(jsonStr, "\n", "")
var memInfo MemoryInfo
if err := json.Unmarshal([]byte(jsonStr), &memInfo); err != nil {
return events
}
// Generate events for memory modules with Warning or Error status
for _, mem := range memInfo.MemModules {
if mem.Status == "Warning" || mem.Status == "Error" || mem.Status == "Critical" {
severity := models.SeverityWarning
if mem.Status == "Error" || mem.Status == "Critical" {
severity = models.SeverityCritical
}
description := fmt.Sprintf("Memory module %s: %s", mem.MemModSlot, mem.Status)
if mem.MemModSize == 0 {
description = fmt.Sprintf("Memory module %s not detected (capacity 0GB)", mem.MemModSlot)
}
events = append(events, models.Event{
ID: fmt.Sprintf("mem_%d", mem.MemModID),
Timestamp: time.Now(), // No timestamp in source, use current
Source: "Memory",
SensorType: "memory",
SensorName: mem.MemModSlot,
EventType: "Memory Status",
Severity: severity,
Description: description,
RawData: fmt.Sprintf("Slot: %s, Vendor: %s, P/N: %s, S/N: %s", mem.MemModSlot, mem.MemModVendor, mem.MemModPartNum, mem.MemModSerial),
})
}
}
return events
}