Older AMI-BMC Inspur onekeylog archives (NF5466M5 / NF5280M5 generation)
opened to an empty result: they carry none of the files the inspur parser
keys on (no asset.json, devicefrusdr.log, selelist.csv or component.log).
New package internal/parser/vendors/inspur_legacy (vendor id inspur_legacy),
separate from inspur:
- binary IPMI FRU decode (FRU.bin) -> board identity
- Inspur_AssetInfoInventory.log -> CPU / memory / PCIe / PSU inventory
- events from Inspur_<model>_<serial>_IDL, sel.log, blackbox.log,
MegaRAID raid0.log, and the flat AMI <severity>.log files
- no live sensors in this archive class -> recorded as a collection error
- BMC clock timestamps before 2010 dropped as un-set (1970 / ~2005 RTC)
Registry: add optional PrioritizedParser { DetectPriority() int } so a
confidence tie is broken by specificity. inspur_legacy returns 10 and also
declines (Detect 0) when modern Kaytus markers are present, so the two
Inspur parsers never fight over a newer dump.
Docs: ADL-065, 06-parsers.md, releases/v1.32.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017VL8wLGD6Lnp6hZCpqT6cZ
99 lines
2.3 KiB
Go
99 lines
2.3 KiB
Go
package inspurlegacy
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.mchus.pro/mchus/logpile/internal/models"
|
|
)
|
|
|
|
var blackboxLineRe = regexp.MustCompile(`^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\]\s*:\s*(.*)$`)
|
|
|
|
const blackboxTimeLayout = "2006-01-02 15:04:05"
|
|
|
|
// blackboxCritical / blackboxWarning classify the free-text message.
|
|
var (
|
|
blackboxCritical = []string{"fault", "error", "under voltage protection", "power good detect pin changed from 1 to 0", "critical"}
|
|
blackboxWarning = []string{"warning", "predfail", "changed from 0 to 1"}
|
|
)
|
|
|
|
// ParseBlackbox parses blackbox.log ("[YYYY-MM-DD HH:MM:SS] : message"), the
|
|
// BMC's persistent power/thermal/RAID fault ring buffer.
|
|
func ParseBlackbox(content []byte) []models.Event {
|
|
var events []models.Event
|
|
seq := 0
|
|
for _, line := range strings.Split(string(content), "\n") {
|
|
line = strings.TrimSpace(line)
|
|
m := blackboxLineRe.FindStringSubmatch(line)
|
|
if m == nil {
|
|
continue
|
|
}
|
|
ts, err := time.Parse(blackboxTimeLayout, m[1])
|
|
if err != nil || bogusYear(ts) {
|
|
continue
|
|
}
|
|
msg := strings.TrimSpace(m[2])
|
|
msg = strings.TrimPrefix(msg, "[*]")
|
|
seq++
|
|
|
|
events = append(events, models.Event{
|
|
ID: "blackbox_" + ts.Format("20060102T150405") + "_" + itoa(seq),
|
|
Timestamp: ts,
|
|
Source: "BMC/blackbox",
|
|
SensorType: "blackbox",
|
|
SensorName: blackboxSubject(msg),
|
|
Severity: classify(msg, blackboxCritical, blackboxWarning),
|
|
Description: msg,
|
|
RawData: line,
|
|
})
|
|
}
|
|
return events
|
|
}
|
|
|
|
var blackboxSubjectRe = regexp.MustCompile(`^(PSU-?\d+|psu \d+|RAID|raid\s*\d+|CPU-?\d+|FAN-?\d+)`)
|
|
|
|
func blackboxSubject(msg string) string {
|
|
if m := blackboxSubjectRe.FindString(msg); m != "" {
|
|
return strings.TrimSpace(m)
|
|
}
|
|
return "blackbox"
|
|
}
|
|
|
|
func classify(msg string, critical, warning []string) models.Severity {
|
|
low := strings.ToLower(msg)
|
|
for _, p := range critical {
|
|
if strings.Contains(low, p) {
|
|
return models.SeverityCritical
|
|
}
|
|
}
|
|
for _, p := range warning {
|
|
if strings.Contains(low, p) {
|
|
return models.SeverityWarning
|
|
}
|
|
}
|
|
return models.SeverityInfo
|
|
}
|
|
|
|
func itoa(i int) string {
|
|
if i == 0 {
|
|
return "0"
|
|
}
|
|
neg := i < 0
|
|
if neg {
|
|
i = -i
|
|
}
|
|
var b [20]byte
|
|
pos := len(b)
|
|
for i > 0 {
|
|
pos--
|
|
b[pos] = byte('0' + i%10)
|
|
i /= 10
|
|
}
|
|
if neg {
|
|
pos--
|
|
b[pos] = '-'
|
|
}
|
|
return string(b[pos:])
|
|
}
|