Files
logpile/internal/parser/vendors/nvidia_bug_report/psu.go
Mikhail Chusavitin 70cd541d9e v1.3.0: Add multiple vendor parsers and enhanced hardware detection
New parsers:
- NVIDIA Field Diagnostics parser with dmidecode output support
- NVIDIA Bug Report parser with comprehensive hardware extraction
- Supermicro crashdump (CDump.txt) parser
- Generic fallback parser for unrecognized text files

Enhanced GPU parsing (nvidia-bug-report):
- Model and manufacturer detection (NVIDIA H100 80GB HBM3)
- UUID, Video BIOS version, IRQ information
- Bus location (BDF), DMA size/mask, device minor
- PCIe bus type details

New hardware detection (nvidia-bug-report):
- System Information: server S/N, UUID, manufacturer, product name
- CPU: model, S/N, cores, threads, frequencies from dmidecode
- Memory: P/N, S/N, manufacturer, speed for all DIMMs
- Power Supplies: manufacturer, model, S/N, wattage, status
- Network Adapters: Ethernet/InfiniBand controllers with VPD data
  - Model, P/N, S/N from lspci Vital Product Data
  - Port count/type detection (QSFP56, OSFP, etc.)
  - Support for ConnectX-6/7 adapters

Archive handling improvements:
- Plain .gz file support (not just tar.gz)
- Increased size limit for plain gzip files (50MB)
- Better error handling for mixed archive formats

Web interface enhancements:
- Display parser name and filename badges
- Improved file info section with visual indicators

Co-Authored-By: Claude (qwen3-coder:480b) <noreply@anthropic.com>
2026-01-30 17:19:47 +03:00

117 lines
3.1 KiB
Go

package nvidia_bug_report
import (
"bufio"
"strconv"
"strings"
"git.mchus.pro/mchus/logpile/internal/models"
)
// parsePSUInfo extracts Power Supply information from dmidecode output
func parsePSUInfo(content string, result *models.AnalysisResult) {
scanner := bufio.NewScanner(strings.NewReader(content))
var currentPSU *models.PSU
inPowerSupply := false
for scanner.Scan() {
line := scanner.Text()
trimmed := strings.TrimSpace(line)
// Start of System Power Supply section
if strings.Contains(trimmed, "System Power Supply") {
inPowerSupply = true
currentPSU = &models.PSU{}
continue
}
// End of current section (empty line or new section with Handle)
if inPowerSupply && (trimmed == "" || strings.HasPrefix(trimmed, "Handle ")) {
// Save PSU if it has valid data
if currentPSU != nil && currentPSU.Slot != "" {
// Only add if PSU is present
if strings.Contains(strings.ToLower(currentPSU.Status), "present") {
result.Hardware.PowerSupply = append(result.Hardware.PowerSupply, *currentPSU)
}
}
inPowerSupply = false
currentPSU = nil
continue
}
// Parse fields within System Power Supply section
if inPowerSupply && currentPSU != nil && strings.Contains(line, ":") {
parts := strings.SplitN(trimmed, ":", 2)
if len(parts) != 2 {
continue
}
field := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
if value == "" || value == "Not Specified" || value == "Unknown" || value == "UNKNOWN" {
continue
}
switch field {
case "Location":
currentPSU.Slot = value
case "Name":
// Use Name as Model if Model is not set later
if currentPSU.Model == "" {
currentPSU.Model = value
}
case "Manufacturer":
currentPSU.Vendor = value
case "Serial Number":
currentPSU.SerialNumber = value
case "Model Part Number":
// Use Model Part Number as the primary model identifier
currentPSU.Model = value
case "Revision":
currentPSU.Firmware = value
case "Max Power Capacity":
// Parse wattage like "2700 W"
if wattage := parsePowerWattage(value); wattage > 0 {
currentPSU.WattageW = wattage
}
case "Status":
currentPSU.Status = value
case "Type":
// Could store PSU type if needed (e.g., "Switching")
case "Plugged":
// Could track if PSU is plugged
case "Hot Replaceable":
// Could track if hot-swappable
}
}
}
// Save last PSU if exists
if currentPSU != nil && currentPSU.Slot != "" {
if strings.Contains(strings.ToLower(currentPSU.Status), "present") {
result.Hardware.PowerSupply = append(result.Hardware.PowerSupply, *currentPSU)
}
}
}
// parsePowerWattage parses power capacity strings like "2700 W" or "1200 Watts"
func parsePowerWattage(powerStr string) int {
parts := strings.Fields(powerStr)
if len(parts) < 1 {
return 0
}
// Try to parse the number
wattageStr := parts[0]
wattage, err := strconv.Atoi(wattageStr)
if err != nil {
return 0
}
// Check if unit is specified (W, Watts, etc.) and convert if needed
// For now, assume it's always in Watts
return wattage
}