Files
logpile/internal/parser/vendors/inspur/idl.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

124 lines
3.0 KiB
Go

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
}