// Package xfusion provides parser for xFusion iBMC diagnostic dump archives. // Tested with: xFusion G5500 V7 iBMC dump (tar.gz format, exported via iBMC UI) // // Archive structure: dump_info/AppDump/... and dump_info/LogDump/... // // IMPORTANT: Increment parserVersion when modifying parser logic! package xfusion import ( "strings" "git.mchus.pro/mchus/logpile/internal/models" "git.mchus.pro/mchus/logpile/internal/parser" ) const parserVersion = "1.0" func init() { parser.Register(&Parser{}) } // Parser implements VendorParser for xFusion iBMC dump archives. type Parser struct{} func (p *Parser) Name() string { return "xFusion iBMC Dump Parser" } func (p *Parser) Vendor() string { return "xfusion" } func (p *Parser) Version() string { return parserVersion } // Detect checks if files match the xFusion iBMC dump format. // Returns confidence score 0-100. func (p *Parser) Detect(files []parser.ExtractedFile) int { confidence := 0 for _, f := range files { path := strings.ToLower(f.Path) switch { case strings.Contains(path, "appdump/frudata/fruinfo.txt"): confidence += 60 case strings.Contains(path, "appdump/sensor_alarm/sensor_info.txt"): confidence += 20 case strings.Contains(path, "appdump/card_manage/card_info"): confidence += 20 } if confidence >= 100 { return 100 } } return confidence } // Parse parses xFusion iBMC dump and returns an analysis result. 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), Hardware: &models.HardwareConfig{ CPUs: make([]models.CPU, 0), Memory: make([]models.MemoryDIMM, 0), Storage: make([]models.Storage, 0), GPUs: make([]models.GPU, 0), NetworkCards: make([]models.NIC, 0), PowerSupply: make([]models.PSU, 0), Firmware: make([]models.FirmwareInfo, 0), }, } if f := findByPath(files, "appdump/frudata/fruinfo.txt"); f != nil { parseFRUInfo(f.Content, result) } if f := findByPath(files, "appdump/sensor_alarm/sensor_info.txt"); f != nil { result.Sensors = parseSensorInfo(f.Content) } if f := findByPath(files, "appdump/cpumem/cpu_info"); f != nil { result.Hardware.CPUs = parseCPUInfo(f.Content) } if f := findByPath(files, "appdump/cpumem/mem_info"); f != nil { result.Hardware.Memory = parseMemInfo(f.Content) } if f := findByPath(files, "appdump/card_manage/card_info"); f != nil { gpus, nics := parseCardInfo(f.Content) result.Hardware.GPUs = gpus result.Hardware.NetworkCards = nics } if f := findByPath(files, "appdump/bmc/psu_info.txt"); f != nil { result.Hardware.PowerSupply = parsePSUInfo(f.Content) } if f := findByPath(files, "appdump/storagemgnt/raid_controller_info.txt"); f != nil { parseStorageControllerInfo(f.Content, result) } for _, f := range findDiskInfoFiles(files) { disk := parseDiskInfo(f.Content) if disk != nil { result.Hardware.Storage = append(result.Hardware.Storage, *disk) } } if f := findByPath(files, "logdump/maintenance_log"); f != nil { result.Events = parseMaintenanceLog(f.Content) } result.Protocol = "ipmi" result.SourceType = models.SourceTypeArchive return result, nil } // findByPath returns the first file whose lowercased path contains the given substring. func findByPath(files []parser.ExtractedFile, substring string) *parser.ExtractedFile { for i := range files { if strings.Contains(strings.ToLower(files[i].Path), substring) { return &files[i] } } return nil } // findDiskInfoFiles returns all PhysicalDrivesInfo disk_info files. func findDiskInfoFiles(files []parser.ExtractedFile) []parser.ExtractedFile { var out []parser.ExtractedFile for _, f := range files { path := strings.ToLower(f.Path) if strings.Contains(path, "physicaldrivesinfo/") && strings.HasSuffix(path, "/disk_info") { out = append(out, f) } } return out }