// Package inspur provides parser for Inspur/Kaytus BMC diagnostic archives // Tested with: Kaytus KR4268X2 (onekeylog format) package inspur import ( "strings" "git.mchus.pro/mchus/logpile/internal/models" "git.mchus.pro/mchus/logpile/internal/parser" ) 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" } // 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 } } // Parse component.log for additional data (PSU, etc.) if f := parser.FindFileByName(files, "component.log"); f != nil { if result.Hardware == nil { result.Hardware = &models.HardwareConfig{} } 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)) } }