158 lines
5.1 KiB
Go
158 lines
5.1 KiB
Go
// 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.1"
|
|
|
|
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 += 50
|
|
case strings.Contains(path, "rtosdump/versioninfo/app_revision.txt"):
|
|
confidence += 30
|
|
case strings.Contains(path, "appdump/sensor_alarm/sensor_info.txt"):
|
|
confidence += 10
|
|
case strings.Contains(path, "appdump/card_manage/card_info"):
|
|
confidence += 20
|
|
case strings.Contains(path, "logdump/netcard/netcard_info.txt"):
|
|
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{
|
|
Firmware: make([]models.FirmwareInfo, 0),
|
|
Devices: make([]models.HardwareDevice, 0),
|
|
CPUs: make([]models.CPU, 0),
|
|
Memory: make([]models.MemoryDIMM, 0),
|
|
Storage: make([]models.Storage, 0),
|
|
Volumes: make([]models.StorageVolume, 0),
|
|
PCIeDevices: make([]models.PCIeDevice, 0),
|
|
GPUs: make([]models.GPU, 0),
|
|
NetworkCards: make([]models.NIC, 0),
|
|
NetworkAdapters: make([]models.NetworkAdapter, 0),
|
|
PowerSupply: make([]models.PSU, 0),
|
|
},
|
|
}
|
|
|
|
if f := findByAnyPath(files, "appdump/frudata/fruinfo.txt", "rtosdump/versioninfo/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)
|
|
}
|
|
var nicCards []xfusionNICCard
|
|
if f := findByPath(files, "appdump/card_manage/card_info"); f != nil {
|
|
gpus, cards := parseCardInfo(f.Content)
|
|
result.Hardware.GPUs = gpus
|
|
nicCards = cards
|
|
}
|
|
if f := findByPath(files, "logdump/netcard/netcard_info.txt"); f != nil || len(nicCards) > 0 {
|
|
var content []byte
|
|
if f != nil {
|
|
content = f.Content
|
|
}
|
|
adapters, legacyNICs := mergeNetworkAdapters(nicCards, parseNetcardInfo(content))
|
|
result.Hardware.NetworkAdapters = adapters
|
|
result.Hardware.NetworkCards = legacyNICs
|
|
}
|
|
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)
|
|
}
|
|
if f := findByPath(files, "rtosdump/versioninfo/app_revision.txt"); f != nil {
|
|
parseAppRevision(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
|
|
parser.ApplyManufacturedYearWeekFromFRU(result.FRU, result.Hardware)
|
|
|
|
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
|
|
}
|
|
|
|
func findByAnyPath(files []parser.ExtractedFile, substrings ...string) *parser.ExtractedFile {
|
|
for _, substring := range substrings {
|
|
if f := findByPath(files, substring); f != nil {
|
|
return f
|
|
}
|
|
}
|
|
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
|
|
}
|