package inspurlegacy import ( "regexp" "strings" "time" "git.mchus.pro/mchus/logpile/internal/models" ) const megaraidTimeLayout = "1/2/2006 15:04:05" var megaraidDescWarning = []string{ "medium error", "unexpected sense", "error on pd", "patrol read", "puncturing", "rebuild", "copyback", "reassign", } var megaraidDescCritical = []string{ "failed", "offline", "predictive failure", "not responding", "removed", "degraded", "punctured", "bad block", } var megaraidSeqRe = regexp.MustCompile(`^(?:Event Sequence Number|r)\s*:\s*(\d+)`) // ParseMegaRAIDLog parses a MegaRAID controller event log (raid0.log). Records // are separated by blank lines; each carries "Timestamp : M/D/YYYY ; HH:MM:SS", // a Class, and a "Description of the event" line. func ParseMegaRAIDLog(content []byte, source string) []models.Event { var events []models.Event var cur map[string]string flush := func() { if cur == nil { return } if e, ok := megaraidEvent(cur, source); ok { events = append(events, e) } cur = nil } for _, line := range strings.Split(string(content), "\n") { line = strings.TrimRight(line, "\r") trimmed := strings.TrimSpace(line) if trimmed == "" { flush() continue } if megaraidSeqRe.MatchString(trimmed) { flush() cur = make(map[string]string) } if cur == nil { cur = make(map[string]string) } k, v, ok := strings.Cut(trimmed, ":") if !ok { continue } key := strings.TrimSpace(k) if _, exists := cur[key]; !exists { cur[key] = strings.TrimSpace(v) } } flush() return events } func megaraidEvent(m map[string]string, source string) (models.Event, bool) { desc := m["Description of the event"] if desc == "" { return models.Event{}, false } ts, ok := parseMegaRAIDTimestamp(m["Timestamp"]) if !ok || bogusYear(ts) { return models.Event{}, false } severity := severityFromWord(m["Class"]) low := strings.ToLower(desc) for _, p := range megaraidDescCritical { if strings.Contains(low, p) { severity = models.SeverityCritical } } if severity == models.SeverityInfo { for _, p := range megaraidDescWarning { if strings.Contains(low, p) { severity = models.SeverityWarning } } } seq := m["Event Sequence Number"] if seq == "" { seq = m["r"] } return models.Event{ ID: "megaraid_" + source + "_" + seq, Timestamp: ts, Source: "RAID/" + strings.TrimSuffix(source, ".log"), SensorType: "raid", SensorName: strings.TrimSpace(m["Locale"]), EventType: strings.TrimSpace(m["Event code"]), Severity: severity, Description: desc, }, true } // parseMegaRAIDTimestamp accepts "M/D/YYYY ; HH:MM:SS". Non-wall-clock forms // ("... Seconds", "Not Present") are rejected. func parseMegaRAIDTimestamp(s string) (time.Time, bool) { s = strings.TrimSpace(s) if !strings.Contains(s, ";") { return time.Time{}, false } parts := strings.SplitN(s, ";", 2) datePart := strings.TrimSpace(parts[0]) timePart := strings.TrimSpace(parts[1]) ts, err := time.Parse(megaraidTimeLayout, datePart+" "+timePart) if err != nil { return time.Time{}, false } return ts, true }