feat(inspur_legacy): new parser for pre-Kaytus Inspur onekeylog
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
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
be37f1852b
commit
3311bafd8e
+123
@@ -0,0 +1,123 @@
|
||||
package inspurlegacy
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.mchus.pro/mchus/logpile/internal/models"
|
||||
)
|
||||
|
||||
const megaraidTimeLayout = "1/2/2006 15:04:05"
|
||||
|
||||
var megaraidDescWarning = []string{
|
||||
"medium error", "unexpected sense", "error on pd", "patrol read", "puncturing",
|
||||
"rebuild", "copyback", "reassign",
|
||||
}
|
||||
var megaraidDescCritical = []string{
|
||||
"failed", "offline", "predictive failure", "not responding", "removed",
|
||||
"degraded", "punctured", "bad block",
|
||||
}
|
||||
|
||||
var megaraidSeqRe = regexp.MustCompile(`^(?:Event Sequence Number|r)\s*:\s*(\d+)`)
|
||||
|
||||
// ParseMegaRAIDLog parses a MegaRAID controller event log (raid0.log). Records
|
||||
// are separated by blank lines; each carries "Timestamp : M/D/YYYY ; HH:MM:SS",
|
||||
// a Class, and a "Description of the event" line.
|
||||
func ParseMegaRAIDLog(content []byte, source string) []models.Event {
|
||||
var events []models.Event
|
||||
var cur map[string]string
|
||||
flush := func() {
|
||||
if cur == nil {
|
||||
return
|
||||
}
|
||||
if e, ok := megaraidEvent(cur, source); ok {
|
||||
events = append(events, e)
|
||||
}
|
||||
cur = nil
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(string(content), "\n") {
|
||||
line = strings.TrimRight(line, "\r")
|
||||
trimmed := strings.TrimSpace(line)
|
||||
if trimmed == "" {
|
||||
flush()
|
||||
continue
|
||||
}
|
||||
if megaraidSeqRe.MatchString(trimmed) {
|
||||
flush()
|
||||
cur = make(map[string]string)
|
||||
}
|
||||
if cur == nil {
|
||||
cur = make(map[string]string)
|
||||
}
|
||||
k, v, ok := strings.Cut(trimmed, ":")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(k)
|
||||
if _, exists := cur[key]; !exists {
|
||||
cur[key] = strings.TrimSpace(v)
|
||||
}
|
||||
}
|
||||
flush()
|
||||
return events
|
||||
}
|
||||
|
||||
func megaraidEvent(m map[string]string, source string) (models.Event, bool) {
|
||||
desc := m["Description of the event"]
|
||||
if desc == "" {
|
||||
return models.Event{}, false
|
||||
}
|
||||
ts, ok := parseMegaRAIDTimestamp(m["Timestamp"])
|
||||
if !ok || bogusYear(ts) {
|
||||
return models.Event{}, false
|
||||
}
|
||||
|
||||
severity := severityFromWord(m["Class"])
|
||||
low := strings.ToLower(desc)
|
||||
for _, p := range megaraidDescCritical {
|
||||
if strings.Contains(low, p) {
|
||||
severity = models.SeverityCritical
|
||||
}
|
||||
}
|
||||
if severity == models.SeverityInfo {
|
||||
for _, p := range megaraidDescWarning {
|
||||
if strings.Contains(low, p) {
|
||||
severity = models.SeverityWarning
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
seq := m["Event Sequence Number"]
|
||||
if seq == "" {
|
||||
seq = m["r"]
|
||||
}
|
||||
return models.Event{
|
||||
ID: "megaraid_" + source + "_" + seq,
|
||||
Timestamp: ts,
|
||||
Source: "RAID/" + strings.TrimSuffix(source, ".log"),
|
||||
SensorType: "raid",
|
||||
SensorName: strings.TrimSpace(m["Locale"]),
|
||||
EventType: strings.TrimSpace(m["Event code"]),
|
||||
Severity: severity,
|
||||
Description: desc,
|
||||
}, true
|
||||
}
|
||||
|
||||
// parseMegaRAIDTimestamp accepts "M/D/YYYY ; HH:MM:SS". Non-wall-clock forms
|
||||
// ("... Seconds", "Not Present") are rejected.
|
||||
func parseMegaRAIDTimestamp(s string) (time.Time, bool) {
|
||||
s = strings.TrimSpace(s)
|
||||
if !strings.Contains(s, ";") {
|
||||
return time.Time{}, false
|
||||
}
|
||||
parts := strings.SplitN(s, ";", 2)
|
||||
datePart := strings.TrimSpace(parts[0])
|
||||
timePart := strings.TrimSpace(parts[1])
|
||||
ts, err := time.Parse(megaraidTimeLayout, datePart+" "+timePart)
|
||||
if err != nil {
|
||||
return time.Time{}, false
|
||||
}
|
||||
return ts, true
|
||||
}
|
||||
Reference in New Issue
Block a user