Files
logpile/internal/parser/vendors/inspur/sdr.go
Michael Chus 512957545a Add LOGPile BMC diagnostic log analyzer
Features:
- Modular parser architecture for vendor-specific formats
- Inspur/Kaytus parser supporting asset.json, devicefrusdr.log,
  component.log, idl.log, and syslog files
- PCI Vendor/Device ID lookup for hardware identification
- Web interface with tabs: Events, Sensors, Config, Serials, Firmware
- Server specification summary with component grouping
- Export to CSV, JSON, TXT formats
- BMC alarm parsing from IDL logs (memory errors, PSU events, etc.)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 04:11:23 +03:00

90 lines
2.5 KiB
Go

package inspur
import (
"bufio"
"regexp"
"strconv"
"strings"
"git.mchus.pro/mchus/logpile/internal/models"
)
// SDR sensor reading patterns
var (
sdrLineRegex = regexp.MustCompile(`^(\S+)\s+\|\s+(.+?)\s+\|\s+(\w+)$`)
valueRegex = regexp.MustCompile(`^([\d.]+)\s+(.+)$`)
)
// ParseSDR parses BMC SDR (Sensor Data Record) output
func ParseSDR(content []byte) []models.SensorReading {
var readings []models.SensorReading
scanner := bufio.NewScanner(strings.NewReader(string(content)))
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" || strings.HasPrefix(line, "BMC sdr Info:") {
continue
}
matches := sdrLineRegex.FindStringSubmatch(line)
if matches == nil {
continue
}
name := strings.TrimSpace(matches[1])
valueStr := strings.TrimSpace(matches[2])
status := strings.TrimSpace(matches[3])
reading := models.SensorReading{
Name: name,
Status: status,
}
// Parse value and unit
if valueStr != "disabled" && valueStr != "no reading" && !strings.HasPrefix(valueStr, "0x") {
if vm := valueRegex.FindStringSubmatch(valueStr); vm != nil {
if v, err := strconv.ParseFloat(vm[1], 64); err == nil {
reading.Value = v
reading.Unit = strings.TrimSpace(vm[2])
}
}
} else if strings.HasPrefix(valueStr, "0x") {
reading.RawValue = valueStr
}
// Determine sensor type
reading.Type = determineSensorType(name)
readings = append(readings, reading)
}
return readings
}
func determineSensorType(name string) string {
nameLower := strings.ToLower(name)
switch {
case strings.Contains(nameLower, "temp"):
return "temperature"
case strings.Contains(nameLower, "fan") && strings.Contains(nameLower, "speed"):
return "fan_speed"
case strings.Contains(nameLower, "fan") && strings.Contains(nameLower, "status"):
return "fan_status"
case strings.HasSuffix(nameLower, "_vin") || strings.HasSuffix(nameLower, "_vout") ||
strings.HasSuffix(nameLower, "_v") || strings.Contains(nameLower, "volt"):
return "voltage"
case strings.Contains(nameLower, "power") || strings.HasSuffix(nameLower, "_pin") ||
strings.HasSuffix(nameLower, "_pout") || strings.HasSuffix(nameLower, "_pwr"):
return "power"
case strings.Contains(nameLower, "psu") && strings.Contains(nameLower, "status"):
return "psu_status"
case strings.Contains(nameLower, "cpu") && strings.Contains(nameLower, "status"):
return "cpu_status"
case strings.Contains(nameLower, "hdd") || strings.Contains(nameLower, "nvme"):
return "storage_status"
default:
return "other"
}
}