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:
123
internal/parser/vendors/inspur/idl.go
vendored
Normal file
123
internal/parser/vendors/inspur/idl.go
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
package inspur
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.mchus.pro/mchus/logpile/internal/models"
|
||||
)
|
||||
|
||||
// ParseIDLLog parses the IDL (Inspur Diagnostic Log) file for BMC alarms
|
||||
// Format: |timestamp|component|type|severity|eventID|description|
|
||||
func ParseIDLLog(content []byte) []models.Event {
|
||||
var events []models.Event
|
||||
|
||||
// Pattern to match CommerDiagnose log entries
|
||||
// Example: |2025-12-02T17:54:27+08:00|MEMORY|Assert|Warning|0C180401|CPU1_C4D0 Memory Device Disabled...|
|
||||
re := regexp.MustCompile(`\|(\d{4}-\d{2}-\d{2}T[\d:]+[+-]\d{2}:\d{2})\|([^|]+)\|([^|]+)\|([^|]+)\|([^|]+)\|([^|]+)\|`)
|
||||
|
||||
lines := strings.Split(string(content), "\n")
|
||||
seenEvents := make(map[string]bool) // Deduplicate events
|
||||
|
||||
for _, line := range lines {
|
||||
if !strings.Contains(line, "CommerDiagnose") {
|
||||
continue
|
||||
}
|
||||
|
||||
matches := re.FindStringSubmatch(line)
|
||||
if matches == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
timestamp := matches[1]
|
||||
component := matches[2]
|
||||
eventType := matches[3]
|
||||
severityStr := matches[4]
|
||||
eventID := matches[5]
|
||||
description := matches[6]
|
||||
|
||||
// Parse timestamp
|
||||
ts, err := time.Parse("2006-01-02T15:04:05-07:00", timestamp)
|
||||
if err != nil {
|
||||
ts = time.Now()
|
||||
}
|
||||
|
||||
// Map severity
|
||||
severity := mapIDLSeverity(severityStr)
|
||||
|
||||
// Clean up description
|
||||
description = cleanDescription(description)
|
||||
|
||||
// Create unique key for deduplication
|
||||
eventKey := eventID + "|" + description
|
||||
if seenEvents[eventKey] {
|
||||
continue
|
||||
}
|
||||
seenEvents[eventKey] = true
|
||||
|
||||
// Extract sensor name from description if available
|
||||
sensorName := extractSensorName(description, component)
|
||||
|
||||
events = append(events, models.Event{
|
||||
ID: eventID,
|
||||
Timestamp: ts,
|
||||
Source: component,
|
||||
SensorType: strings.ToLower(component),
|
||||
SensorName: sensorName,
|
||||
EventType: eventType,
|
||||
Severity: severity,
|
||||
Description: description,
|
||||
})
|
||||
}
|
||||
|
||||
return events
|
||||
}
|
||||
|
||||
func mapIDLSeverity(s string) models.Severity {
|
||||
switch strings.ToLower(s) {
|
||||
case "critical", "error":
|
||||
return models.SeverityCritical
|
||||
case "warning":
|
||||
return models.SeverityWarning
|
||||
default:
|
||||
return models.SeverityInfo
|
||||
}
|
||||
}
|
||||
|
||||
func cleanDescription(desc string) string {
|
||||
// Remove trailing " - Assert" or similar
|
||||
desc = strings.TrimSuffix(desc, " - Assert")
|
||||
desc = strings.TrimSuffix(desc, " - Deassert")
|
||||
desc = strings.TrimSpace(desc)
|
||||
return desc
|
||||
}
|
||||
|
||||
func extractSensorName(desc, component string) string {
|
||||
// Try to extract sensor/device name from description
|
||||
// For memory: CPU1_C4D0, CPU1_C4D1, etc.
|
||||
if component == "MEMORY" {
|
||||
re := regexp.MustCompile(`(CPU\d+_C\d+D\d+)`)
|
||||
if matches := re.FindStringSubmatch(desc); matches != nil {
|
||||
return matches[1]
|
||||
}
|
||||
}
|
||||
|
||||
// For PSU: PSU0, PSU1, etc.
|
||||
if component == "PSU" || component == "POWER" {
|
||||
re := regexp.MustCompile(`(PSU\d+)`)
|
||||
if matches := re.FindStringSubmatch(desc); matches != nil {
|
||||
return matches[1]
|
||||
}
|
||||
}
|
||||
|
||||
// For temperature sensors
|
||||
if component == "TEMPERATURE" || component == "THERMAL" {
|
||||
re := regexp.MustCompile(`(\w+_Temp|\w+_DTS)`)
|
||||
if matches := re.FindStringSubmatch(desc); matches != nil {
|
||||
return matches[1]
|
||||
}
|
||||
}
|
||||
|
||||
return component
|
||||
}
|
||||
Reference in New Issue
Block a user