package inspurlegacy import ( "strings" "time" "git.mchus.pro/mchus/logpile/internal/models" ) const selTimeLayout = "01/02/2006 15:04:05" // criticalSELPhrases escalate an otherwise-Info SEL record to critical. var criticalSELPhrases = []string{ "ac lost", "power supply failure", "redundancy lost", "non-recoverable", "uncorrectable", "input under voltage error", "processor error", "thermal trip", "asserted - critical", } // warningSELPhrases escalate an otherwise-Info SEL record to warning. var warningSELPhrases = []string{ "correctable", "warning", "degraded", "under voltage warning", "redundancy degraded", "predictive failure", } // ParseSELLog parses the pipe-delimited sel.log. Real records are // "id | MM/DD/YYYY | HH:MM:SS | sensor | event | Asserted|Deasserted". // "Pre-Init Time-stamp" records have no usable timestamp and are skipped. func ParseSELLog(content []byte) []models.Event { var events []models.Event for _, line := range strings.Split(string(content), "\n") { line = strings.TrimSpace(line) if line == "" { continue } fields := splitTrim(line, "|") // Real records have 5 or 6 columns: id | date | time | sensor | event // [ | Asserted/Deasserted ]. The direction is folded into the event text // on some records (e.g. "Predictive Failure Asserted"). if len(fields) < 5 { continue } date, tm := fields[1], fields[2] if !strings.Contains(date, "/") { continue // Pre-Init Time-stamp and similar } ts, err := time.Parse(selTimeLayout, date+" "+tm) if err != nil || bogusYear(ts) { continue } sensor := fields[3] event := fields[4] direction := "" if len(fields) >= 6 { direction = fields[5] } severity := selSeverity(event, direction) events = append(events, models.Event{ ID: "sel_" + strings.TrimSpace(fields[0]), Timestamp: ts, Source: "BMC/SEL", SensorType: "sel", SensorName: sensor, EventType: direction, Severity: severity, Description: strings.TrimSpace(sensor + ": " + event), RawData: line, }) } return events } func selSeverity(event, direction string) models.Severity { if strings.EqualFold(strings.TrimSpace(direction), "Deasserted") { return models.SeverityInfo } low := strings.ToLower(event) for _, p := range criticalSELPhrases { if strings.Contains(low, p) { return models.SeverityCritical } } for _, p := range warningSELPhrases { if strings.Contains(low, p) { return models.SeverityWarning } } return models.SeverityInfo } func splitTrim(s, sep string) []string { parts := strings.Split(s, sep) for i := range parts { parts[i] = strings.TrimSpace(parts[i]) } return parts }