feat(inspur): source-PRI timezone timeline, PPIN serial, NVMe fault storage status

Event ingestion now uses the source syslog PRI and an explicit-offset
timezone timeline instead of assuming host-local time. CPU PPIN is
exported as the source-backed CPU serial, and an active NVMe fault SEL
event promotes the matching drive's storage status.

See ADL-056, ADL-057.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Mikhail Chusavitin
2026-08-27 17:05:45 +03:00
co-authored by Claude Sonnet 5
parent 07c270cda2
commit e7b8a8badc
13 changed files with 671 additions and 48 deletions
+72 -1
View File
@@ -1,6 +1,12 @@
package inspur
import "testing"
import (
"testing"
"time"
"git.mchus.pro/mchus/logpile/internal/models"
baseparser "git.mchus.pro/mchus/logpile/internal/parser"
)
func TestParseIDLLog_UsesBMCSourceForEventLogs(t *testing.T) {
content := []byte(`|2025-12-02T17:54:27+08:00|MEMORY|Assert|Warning|0C180401|CPU1_C4D0 Memory Device Disabled - Assert|`)
@@ -17,6 +23,14 @@ func TestParseIDLLog_UsesBMCSourceForEventLogs(t *testing.T) {
}
}
func TestParseIDLLog_DeassertedFaultIsInfo(t *testing.T) {
content := []byte(`|2026-05-07T23:57:08+08:00|BMC|Deassert|Critical|1800B002|Sys_Health Transition to Critical from less severe - Deassert|`)
events := ParseIDLLog(content)
if len(events) != 1 || events[0].Severity != models.SeverityInfo {
t.Fatalf("expected deasserted IDL fault to be informational, got %+v", events)
}
}
func TestParseSyslog_UsesHostSourceAndProcessAsSensorName(t *testing.T) {
content := []byte(`<13>2026-03-15T14:03:11+00:00 host123 systemd[1]: Started Example Service`)
@@ -31,3 +45,60 @@ func TestParseSyslog_UsesHostSourceAndProcessAsSensorName(t *testing.T) {
t.Fatalf("expected process name in sensor/component slot, got %#v", events[0])
}
}
func TestParseSyslog_UsesPriorityAndDowngradesKnownBenignMessages(t *testing.T) {
content := []byte("<1> 1970-01-01T08:01:38.795020+08:00 host kernel: [ 17.763621] Helper Module Driver Version 1.2\n" +
"<1> 2026-01-29T16:24:12.888636+08:00 host kernel: [ 16.191801] Helper Module Driver Version 1.2\n" +
"<132> 2026-07-25T08:42:47.001654+03:00 host kernel: [100009.801150] Color Depth is 15 bpp or higher\n" +
"<130> 2026-07-25T08:43:48.001964+03:00 host MCTPMain: MCTP_ERROR: No Response for Application. Timing-out...TimeoutMsec:4000\n")
events := ParseSyslog(content, "onekeylog/log/syslog/alert.log")
if len(events) != 3 {
t.Fatalf("expected 3 events, got %d", len(events))
}
if events[0].Severity != models.SeverityInfo {
t.Fatalf("expected benign driver alert to be info, got %#v", events[0])
}
if events[1].Severity != models.SeverityInfo {
t.Fatalf("expected KVM status line to be info, got %#v", events[1])
}
if events[2].Severity != models.SeverityCritical {
t.Fatalf("expected PRI=2 MCTP fault to be critical, got %#v", events[2])
}
}
func TestParserIncludesCriticalSyslogMCTPAndMESelfTest(t *testing.T) {
files := []baseparser.ExtractedFile{{
Path: "onekeylog/log/syslog/crit.log",
Content: []byte("<130> 2026-07-25T08:42:47.001654+03:00 host MCTPMain: MCTP_ERROR: No Response for Application. Timing-out...TimeoutMsec:4000\n" +
"<130> 2026-07-25T08:43:28.072198+03:00 host IPMIMain: Me self-tests is not normal, return code is 195!\n"),
}}
result, err := (&Parser{}).Parse(files)
if err != nil {
t.Fatalf("parse: %v", err)
}
if len(result.Events) != 2 {
t.Fatalf("expected both critical syslog events, got %d: %+v", len(result.Events), result.Events)
}
for _, event := range result.Events {
if event.Severity != models.SeverityCritical {
t.Fatalf("expected critical event, got %#v", event)
}
}
if result.Events[0].Timestamp.After(result.Events[1].Timestamp) {
t.Fatalf("events are not sorted: %+v", result.Events)
}
}
func TestSortInspurEventsPlacesUnknownTimestampsLast(t *testing.T) {
events := []models.Event{
{ID: "late", Timestamp: time.Date(2026, 7, 25, 8, 0, 0, 0, time.UTC)},
{ID: "unknown"},
{ID: "early", Timestamp: time.Date(2026, 7, 24, 8, 0, 0, 0, time.UTC)},
}
sortInspurEvents(events)
if events[0].ID != "early" || events[1].ID != "late" || events[2].ID != "unknown" {
t.Fatalf("unexpected event order: %+v", events)
}
}