feat(parser): lenovo xcc warnings and redfish logs - v1.1

This commit is contained in:
2026-04-13 20:34:04 +03:00
parent 89b6701f43
commit c9969fc3da
8 changed files with 283 additions and 26 deletions
+31 -10
View File
@@ -17,7 +17,7 @@ import (
"git.mchus.pro/mchus/logpile/internal/parser"
)
const parserVersion = "1.0"
const parserVersion = "1.1"
func init() {
parser.Register(&Parser{})
@@ -81,7 +81,9 @@ func (p *Parser) Parse(files []parser.ExtractedFile) (*models.AnalysisResult, er
result.Hardware.CPUs = parseCPUs(f.Content)
}
if f := findByPath(files, "tmp/inventory_dimm.log"); f != nil {
result.Hardware.Memory = parseDIMMs(f.Content)
memory, events := parseDIMMs(f.Content)
result.Hardware.Memory = memory
result.Events = append(result.Events, events...)
}
if f := findByPath(files, "tmp/inventory_disk.log"); f != nil {
result.Hardware.Storage = parseDisks(f.Content)
@@ -383,16 +385,18 @@ func parseCPUs(content []byte) []models.CPU {
return out
}
func parseDIMMs(content []byte) []models.MemoryDIMM {
func parseDIMMs(content []byte) ([]models.MemoryDIMM, []models.Event) {
var doc xccDIMMDoc
if err := json.Unmarshal(content, &doc); err != nil || len(doc.Items) == 0 {
return nil
return nil, nil
}
var out []models.MemoryDIMM
var events []models.Event
for _, item := range doc.Items {
for _, m := range item.Memory {
present := !strings.EqualFold(strings.TrimSpace(m.Status), "not present") &&
!strings.EqualFold(strings.TrimSpace(m.Status), "absent")
status := strings.TrimSpace(m.Status)
present := !strings.EqualFold(status, "not present") &&
!strings.EqualFold(status, "absent")
// memory_capacity is in GB (int); convert to MB
capacityGB := rawJSONToInt(m.Capacity)
dimm := models.MemoryDIMM{
@@ -406,12 +410,22 @@ func parseDIMMs(content []byte) []models.MemoryDIMM {
Manufacturer: strings.TrimSpace(m.Manufacturer),
SerialNumber: strings.TrimSpace(m.SerialNumber),
PartNumber: strings.TrimSpace(strings.TrimRight(m.PartNumber, " ")),
Status: strings.TrimSpace(m.Status),
Status: status,
}
out = append(out, dimm)
if isUnqualifiedDIMM(status) {
events = append(events, models.Event{
Source: "Memory",
SensorType: "Memory",
SensorName: dimm.Slot,
EventType: "DIMM Qualification",
Severity: models.SeverityWarning,
Description: status,
})
}
}
}
return out
return out, events
}
func parseDisks(content []byte) []models.Storage {
@@ -567,7 +581,7 @@ func parseEvents(content []byte) []models.Event {
ID: e.EventID,
Source: strings.TrimSpace(e.Source),
Description: strings.TrimSpace(e.Message),
Severity: xccSeverity(e.Severity),
Severity: xccSeverity(e.Severity, e.Message),
}
if t, err := parseXCCTime(e.Date); err == nil {
ev.Timestamp = t.UTC()
@@ -579,7 +593,10 @@ func parseEvents(content []byte) []models.Event {
// --- Helpers ---
func xccSeverity(s string) models.Severity {
func xccSeverity(s, message string) models.Severity {
if isUnqualifiedDIMM(message) {
return models.SeverityWarning
}
switch strings.ToUpper(strings.TrimSpace(s)) {
case "C":
return models.SeverityCritical
@@ -592,6 +609,10 @@ func xccSeverity(s string) models.Severity {
}
}
func isUnqualifiedDIMM(value string) bool {
return strings.Contains(strings.ToLower(strings.TrimSpace(value)), "unqualified dimm")
}
func parseXCCTime(s string) (time.Time, error) {
s = strings.TrimSpace(s)
formats := []string{