Files
logpile/internal/parser/vendors/inspur_legacy/event_logs_test.go
T
Mikhail ChusavitinandClaude Sonnet 5 3311bafd8e 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
2026-09-02 12:51:53 +03:00

116 lines
4.1 KiB
Go

package inspurlegacy
import (
"testing"
"git.mchus.pro/mchus/logpile/internal/models"
)
func TestParseIDLEvents(t *testing.T) {
in := `1970-01-01T08:00:17|BIOS BOOT|Info|Asssert|1FFF06F0|OS Event System boot completed - boot device not specified
2026-05-23T08:16:41|PSU|Critical|Asssert|080053F2|PSU0 unit input off for insufficient input voltage..
2026-08-31T17:14:42|PCIE|Warning|Asssert|17FF01F1|Disk error Predictive Failure asserted
`
events := ParseIDLEvents([]byte(in))
if len(events) != 2 {
t.Fatalf("events = %d (1970 line must be dropped)", len(events))
}
if events[0].Severity != models.SeverityCritical || events[0].SensorName != "PSU" {
t.Errorf("event0 = %+v", events[0])
}
if events[0].Description != "PSU0 unit input off for insufficient input voltage" {
t.Errorf("trailing dots not trimmed: %q", events[0].Description)
}
if events[1].Severity != models.SeverityWarning {
t.Errorf("event1 severity = %s", events[1].Severity)
}
}
func TestParseSELLog(t *testing.T) {
in := ` f | Pre-Init Time-stamp | Button Power_Button | Power Button pressed | Asserted
e34 | 05/23/2026 | 08:16:41 | Power Supply PSU0_Status | Power Supply AC lost | Asserted
e36 | 05/23/2026 | 16:28:35 | Power Supply PSU0_Status | Power Supply AC lost | Deasserted
e45 | 08/31/2026 | 17:14:42 | Add-in Card RAID0_Status | Predictive Failure Asserted
`
events := ParseSELLog([]byte(in))
if len(events) != 3 {
t.Fatalf("events = %d (pre-init line must be skipped)", len(events))
}
if events[0].Severity != models.SeverityCritical {
t.Errorf("AC lost assert should be critical: %+v", events[0])
}
if events[1].Severity != models.SeverityInfo {
t.Errorf("deassert should be info: %+v", events[1])
}
if events[2].Severity != models.SeverityWarning {
t.Errorf("predictive failure should be warning: %+v", events[2])
}
}
func TestParseBlackbox(t *testing.T) {
in := `[1970-01-01 08:00:16] : [*]Power restore after AC power on.
[2026-05-23 08:16:41] : PSU-0 Fault: Input Under Voltage Protection, StatusWord=0x2848
`
events := ParseBlackbox([]byte(in))
if len(events) != 1 {
t.Fatalf("events = %d", len(events))
}
if events[0].Severity != models.SeverityCritical || events[0].SensorName != "PSU-0" {
t.Errorf("event = %+v", events[0])
}
}
func TestParseMegaRAIDLog(t *testing.T) {
in := `Event Sequence Number : 47100
Timestamp : 8/31/2026 ; 11:30:49
Event code : 93
Locale : PD event
Class : Information
Description of the event : Patrol Read corrected medium error on PD 16(e0x2e/s37) at 3a0e3b4a0
Event Sequence Number : 47200
Timestamp : 466920 Seconds
Class : Information
Description of the event : Controller cache discarded
`
events := ParseMegaRAIDLog([]byte(in), "raid0.log")
if len(events) != 1 {
t.Fatalf("events = %d (non-wall-clock timestamp must be dropped)", len(events))
}
if events[0].Severity != models.SeverityWarning {
t.Errorf("medium error should escalate to warning: %+v", events[0])
}
if events[0].Source != "RAID/raid0" {
t.Errorf("source = %q", events[0].Source)
}
}
func TestParseAMISyslog(t *testing.T) {
in := `<1> 1970-01-01T08:01:08.130000+08:00 localhost kernel: [ 6.350000] Helper Module Driver Version 1.2
<9> 2026-03-03T09:10:00.000000+03:00 localhost IPMIMain: CPU0 changed from NULL to Xeon
<27> 2026-07-09T02:29:25.700000+03:00 localhost dhcp6c[957]: client6_send: transmit failed: Network is unreachable
<3> 2026-07-09T03:02:02.330000+03:00 localhost kernel: i2c i2c-4: send_bytes: Timed out sending data
`
events := ParseAMISyslog([]byte(in), "err.log")
if len(events) != 3 {
t.Fatalf("events = %d (1970 line dropped)", len(events))
}
if events[0].Severity != models.SeverityInfo {
t.Errorf("provisioning notice should be downgraded to info: %s", events[0].Severity)
}
if events[1].SensorName != "dhcp6c[957]" {
t.Errorf("proc = %q", events[1].SensorName)
}
if events[2].Severity != models.SeverityWarning {
t.Errorf("pri 3 (err) -> %s, want warning", events[2].Severity)
}
}
func TestDedupeEvents(t *testing.T) {
e := models.Event{Source: "BMC/SEL", Description: "x"}
out := dedupeEvents([]models.Event{e, e, {Source: "BMC/IDL", Description: "x"}})
if len(out) != 2 {
t.Fatalf("dedupe kept %d", len(out))
}
}