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>
148 lines
3.7 KiB
Go
148 lines
3.7 KiB
Go
// Package generic provides a fallback parser for unrecognized text files
|
|
package generic
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"git.mchus.pro/mchus/logpile/internal/models"
|
|
"git.mchus.pro/mchus/logpile/internal/parser"
|
|
)
|
|
|
|
// parserVersion - version of this parser module
|
|
const parserVersion = "1.0.0"
|
|
|
|
func init() {
|
|
parser.Register(&Parser{})
|
|
}
|
|
|
|
// Parser implements VendorParser for generic text files
|
|
type Parser struct{}
|
|
|
|
// Name returns human-readable parser name
|
|
func (p *Parser) Name() string {
|
|
return "Generic Text File Parser"
|
|
}
|
|
|
|
// Vendor returns vendor identifier
|
|
func (p *Parser) Vendor() string {
|
|
return "generic"
|
|
}
|
|
|
|
// Version returns parser version
|
|
func (p *Parser) Version() string {
|
|
return parserVersion
|
|
}
|
|
|
|
// Detect checks if this is a text file (fallback with low confidence)
|
|
// Returns confidence 0-100
|
|
func (p *Parser) Detect(files []parser.ExtractedFile) int {
|
|
// Only detect if there's exactly one file (plain .gz or single file)
|
|
if len(files) != 1 {
|
|
return 0
|
|
}
|
|
|
|
file := files[0]
|
|
|
|
// Check if content looks like text (not binary)
|
|
if !isLikelyText(file.Content) {
|
|
return 0
|
|
}
|
|
|
|
// Return low confidence so other parsers have priority
|
|
return 15
|
|
}
|
|
|
|
// isLikelyText checks if content is likely text (not binary)
|
|
func isLikelyText(content []byte) bool {
|
|
// Check first 512 bytes for binary data
|
|
sample := content
|
|
if len(content) > 512 {
|
|
sample = content[:512]
|
|
}
|
|
|
|
binaryCount := 0
|
|
for _, b := range sample {
|
|
// Count non-printable characters (excluding common whitespace)
|
|
if b < 32 && b != '\n' && b != '\r' && b != '\t' {
|
|
binaryCount++
|
|
}
|
|
if b == 0 { // NULL byte is a strong indicator of binary
|
|
binaryCount += 10
|
|
}
|
|
}
|
|
|
|
// If less than 5% binary, consider it text
|
|
return binaryCount < len(sample)/20
|
|
}
|
|
|
|
// Parse parses generic text file
|
|
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),
|
|
}
|
|
|
|
// Initialize hardware config
|
|
result.Hardware = &models.HardwareConfig{}
|
|
|
|
if len(files) == 0 {
|
|
return result, nil
|
|
}
|
|
|
|
file := files[0]
|
|
content := string(file.Content)
|
|
|
|
// Create a single event with file info
|
|
result.Events = append(result.Events, models.Event{
|
|
Timestamp: time.Now(),
|
|
Source: "File",
|
|
EventType: "Text File",
|
|
Description: "Generic text file loaded",
|
|
Severity: models.SeverityInfo,
|
|
RawData: "Filename: " + file.Path,
|
|
})
|
|
|
|
// Try to extract some basic info from common file types
|
|
if strings.Contains(strings.ToLower(file.Path), "nvidia-bug-report") {
|
|
parseNvidiaBugReport(content, result)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// parseNvidiaBugReport extracts info from nvidia-bug-report files
|
|
func parseNvidiaBugReport(content string, result *models.AnalysisResult) {
|
|
lines := strings.Split(content, "\n")
|
|
|
|
// Look for GPU information
|
|
for i, line := range lines {
|
|
// Find NVIDIA driver version
|
|
if strings.Contains(line, "NVRM version:") || strings.Contains(line, "nvidia-smi") {
|
|
if i+5 < len(lines) {
|
|
result.Events = append(result.Events, models.Event{
|
|
Timestamp: time.Now(),
|
|
Source: "NVIDIA Driver",
|
|
EventType: "Driver Info",
|
|
Description: "NVIDIA driver information found",
|
|
Severity: models.SeverityInfo,
|
|
RawData: strings.TrimSpace(line),
|
|
})
|
|
}
|
|
}
|
|
|
|
// Find GPU devices
|
|
if strings.Contains(line, "/proc/driver/nvidia/gpus/") && strings.Contains(line, "***") {
|
|
result.Events = append(result.Events, models.Event{
|
|
Timestamp: time.Now(),
|
|
Source: "GPU",
|
|
EventType: "GPU Device",
|
|
Description: "GPU device detected",
|
|
Severity: models.SeverityInfo,
|
|
RawData: strings.TrimSpace(line),
|
|
})
|
|
}
|
|
}
|
|
}
|