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>
This commit is contained in:
147
internal/parser/vendors/inspur/component.go
vendored
Normal file
147
internal/parser/vendors/inspur/component.go
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user