Major improvements: - Add CSV SEL event parser for Kaytus firmware format - Add PCIe device parser with link speed/width detection - Add GPU temperature and PCIe link monitoring - Add disk backplane parser for storage bay information - Fix memory module detection (only show installed DIMMs) Parser enhancements: - Parse RESTful PCIe Device info (max/current link width/speed) - Parse GPU sensor data (core and memory temperatures) - Parse diskbackplane info (slot count, installed drives) - Parse SEL events from CSV format (selelist.csv) - Fix memory Present status logic (check mem_mod_status) Web interface improvements: - Add PCIe link degradation highlighting (red when current < max) - Add storage table with Present status and location - Update memory specification to show only installed modules with frequency - Sort events from newest to oldest - Filter out N/A serial numbers from display Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
91 lines
2.6 KiB
Go
91 lines
2.6 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])
|
|
reading.RawValue = valueStr // Keep original string for reference
|
|
}
|
|
}
|
|
} 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"
|
|
}
|
|
}
|