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>
148 lines
4.0 KiB
Go
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
|
|
}
|