Files
logpile/internal/parser/vendors/inspur/parser.go
Michael Chus c7422e95aa v1.1.0: Parser versioning, server info, auto-browser, section overviews
- Add parser versioning with Version() method and version display on main screen
- Add server model and serial number to Configuration tab and TXT export
- Add auto-browser opening on startup with --no-browser flag
- Add Restart and Exit buttons with graceful shutdown
- Add section overview stats (CPU, Power, Storage, GPU, Network)
- Change PCIe Link display to "x16 PCIe Gen4" format
- Add Location column to Serials section
- Extract BoardInfo from FRU and PlatformId from ThermalConfig

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 13:49:43 +03:00

165 lines
4.4 KiB
Go

// Package inspur provides parser for Inspur/Kaytus BMC diagnostic archives
// Tested with: Inspur NF5468M7 / Kaytus KR4268X2 (onekeylog format)
//
// IMPORTANT: Increment parserVersion when modifying parser logic!
// This helps track which version was used to parse specific logs.
package inspur
import (
"strings"
"git.mchus.pro/mchus/logpile/internal/models"
"git.mchus.pro/mchus/logpile/internal/parser"
)
// parserVersion - version of this parser module
// IMPORTANT: Increment this version when making changes to parser logic!
const parserVersion = "1.0.0"
func init() {
parser.Register(&Parser{})
}
// Parser implements VendorParser for Inspur/Kaytus servers
type Parser struct{}
// Name returns human-readable parser name
func (p *Parser) Name() string {
return "Inspur/Kaytus BMC Parser"
}
// Vendor returns vendor identifier
func (p *Parser) Vendor() string {
return "inspur"
}
// Version returns parser version
// IMPORTANT: Update parserVersion constant when modifying parser logic!
func (p *Parser) Version() string {
return parserVersion
}
// Detect checks if archive matches Inspur/Kaytus format
// Returns confidence 0-100
func (p *Parser) Detect(files []parser.ExtractedFile) int {
confidence := 0
for _, f := range files {
path := strings.ToLower(f.Path)
// Strong indicators for Inspur/Kaytus onekeylog format
if strings.Contains(path, "onekeylog/") {
confidence += 30
}
if strings.Contains(path, "devicefrusdr.log") {
confidence += 25
}
if strings.Contains(path, "component/component.log") {
confidence += 15
}
// Check for asset.json with Inspur-specific structure
if strings.HasSuffix(path, "asset.json") {
if containsInspurMarkers(f.Content) {
confidence += 20
}
}
// Cap at 100
if confidence >= 100 {
return 100
}
}
return confidence
}
// containsInspurMarkers checks if content has Inspur-specific markers
func containsInspurMarkers(content []byte) bool {
s := string(content)
// Check for typical Inspur asset.json structure
return strings.Contains(s, "VersionInfo") &&
strings.Contains(s, "CpuInfo") &&
strings.Contains(s, "MemInfo")
}
// Parse parses Inspur/Kaytus archive
func (p *Parser) Parse(files []parser.ExtractedFile) (*models.AnalysisResult, error) {
result := &models.AnalysisResult{
Events: make([]models.Event, 0),
FRU: make([]models.FRUInfo, 0),
Sensors: make([]models.SensorReading, 0),
}
// Parse devicefrusdr.log (contains SDR and FRU data)
if f := parser.FindFileByName(files, "devicefrusdr.log"); f != nil {
p.parseDeviceFruSDR(f.Content, result)
}
// Parse asset.json
if f := parser.FindFileByName(files, "asset.json"); f != nil {
if hw, err := ParseAssetJSON(f.Content); err == nil {
result.Hardware = hw
}
}
// Extract BoardInfo from FRU data
if result.Hardware == nil {
result.Hardware = &models.HardwareConfig{}
}
extractBoardInfo(result.FRU, result.Hardware)
// Extract PlatformId (server model) from ThermalConfig
if f := parser.FindFileByName(files, "ThermalConfig_Cur.conf"); f != nil {
extractPlatformId(f.Content, result.Hardware)
}
// Parse component.log for additional data (PSU, etc.)
if f := parser.FindFileByName(files, "component.log"); f != nil {
ParseComponentLog(f.Content, result.Hardware)
// Extract events from component.log (memory errors, etc.)
componentEvents := ParseComponentLogEvents(f.Content)
result.Events = append(result.Events, componentEvents...)
}
// Parse IDL log (BMC alarms/diagnose events)
if f := parser.FindFileByName(files, "idl.log"); f != nil {
idlEvents := ParseIDLLog(f.Content)
result.Events = append(result.Events, idlEvents...)
}
// Parse syslog files
syslogFiles := parser.FindFileByPattern(files, "syslog/alert", "syslog/warning", "syslog/notice", "syslog/info")
for _, f := range syslogFiles {
events := ParseSyslog(f.Content, f.Path)
result.Events = append(result.Events, events...)
}
return result, nil
}
func (p *Parser) parseDeviceFruSDR(content []byte, result *models.AnalysisResult) {
lines := string(content)
// Find SDR section
sdrStart := strings.Index(lines, "BMC sdr Info:")
fruStart := strings.Index(lines, "BMC fru Info:")
if sdrStart != -1 {
var sdrContent string
if fruStart != -1 && fruStart > sdrStart {
sdrContent = lines[sdrStart:fruStart]
} else {
sdrContent = lines[sdrStart:]
}
result.Sensors = ParseSDR([]byte(sdrContent))
}
// Find FRU section
if fruStart != -1 {
fruContent := lines[fruStart:]
result.FRU = ParseFRU([]byte(fruContent))
}
}