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
+116
@@ -0,0 +1,116 @@
|
||||
package inspurlegacy
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.mchus.pro/mchus/logpile/internal/models"
|
||||
)
|
||||
|
||||
// amiSyslogRe matches "<pri> <ISO8601> host proc[pid]: message".
|
||||
var amiSyslogRe = regexp.MustCompile(`^<(\d+)>\s*(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\S*)\s+(\S+)\s+(\S+?):\s*(.*)$`)
|
||||
|
||||
// ParseAMISyslog parses a flat AMI BMC severity log (err.log, warning.log, ...).
|
||||
func ParseAMISyslog(content []byte, source string) []models.Event {
|
||||
var events []models.Event
|
||||
lineNo := 0
|
||||
for _, line := range strings.Split(string(content), "\n") {
|
||||
lineNo++
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
m := amiSyslogRe.FindStringSubmatch(line)
|
||||
if m == nil {
|
||||
continue
|
||||
}
|
||||
pri, err := strconv.Atoi(m[1])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ts, err := parseSyslogTime(m[2])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
proc := m[4]
|
||||
msg := strings.TrimSpace(m[5])
|
||||
// AMI BMC pre-NTP lines carry a fabricated 1970 wall clock; without a
|
||||
// trustworthy boot epoch they cannot become dated events.
|
||||
if bogusYear(ts) {
|
||||
continue
|
||||
}
|
||||
|
||||
severity := syslogSeverity(pri, source)
|
||||
if isBenignSyslogMessage(msg) {
|
||||
severity = models.SeverityInfo
|
||||
}
|
||||
|
||||
events = append(events, models.Event{
|
||||
ID: strings.TrimSuffix(source, ".log") + "_" + itoa(lineNo),
|
||||
Timestamp: ts,
|
||||
Source: "syslog/" + strings.TrimSuffix(source, ".log"),
|
||||
SensorType: "syslog",
|
||||
SensorName: proc,
|
||||
Severity: severity,
|
||||
Description: msg,
|
||||
RawData: line,
|
||||
})
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
func parseSyslogTime(s string) (time.Time, error) {
|
||||
if t, err := time.Parse(time.RFC3339, s); err == nil {
|
||||
return t, nil
|
||||
}
|
||||
return time.Parse("2006-01-02T15:04:05.999999-07:00", s)
|
||||
}
|
||||
|
||||
// benignSyslogSubstrings are driver banners and inventory-provisioning notices
|
||||
// that AMI routes to alert.log/warning.log despite describing no fault. The PRI
|
||||
// value is not trustworthy for this observed set.
|
||||
var benignSyslogSubstrings = []string{
|
||||
"helper module driver version",
|
||||
"copyright (c)",
|
||||
"changed from null to",
|
||||
"sn changed from",
|
||||
"color depth is",
|
||||
}
|
||||
|
||||
func isBenignSyslogMessage(msg string) bool {
|
||||
low := strings.ToLower(msg)
|
||||
for _, s := range benignSyslogSubstrings {
|
||||
if strings.Contains(low, s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// syslogSeverity derives severity from the RFC 5424 PRI low three bits, falling
|
||||
// back to the source filename.
|
||||
func syslogSeverity(pri int, source string) models.Severity {
|
||||
switch pri & 7 {
|
||||
case 0, 1, 2:
|
||||
return models.SeverityCritical
|
||||
case 3, 4:
|
||||
return models.SeverityWarning
|
||||
case 5, 6, 7:
|
||||
return models.SeverityInfo
|
||||
}
|
||||
return severityFromSource(source)
|
||||
}
|
||||
|
||||
func severityFromSource(source string) models.Severity {
|
||||
low := strings.ToLower(source)
|
||||
switch {
|
||||
case strings.Contains(low, "emerg"), strings.Contains(low, "alert"), strings.Contains(low, "crit"):
|
||||
return models.SeverityCritical
|
||||
case strings.Contains(low, "err"), strings.Contains(low, "warn"):
|
||||
return models.SeverityWarning
|
||||
default:
|
||||
return models.SeverityInfo
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user