Files
logpile/internal/parser/vendors/generic/parser.go
Michael Chus 21ea129933 misc: sds format support, convert limits, dell dedup, supermicro removal, bible updates
Parser / archive:
- Add .sds extension as tar-format alias (archive.go)
- Add tests for multipart upload size limits (multipart_limits_test.go)
- Remove supermicro crashdump parser (ADL-015)

Dell parser:
- Remove GPU duplicates from PCIeDevices (DCIM_VideoView vs DCIM_PCIDeviceView
  both list the same GPU; VideoView record is authoritative)

Server:
- Add LOGPILE_CONVERT_MAX_MB env var for independent convert batch size limit
- Improve "file too large" error message with current limit value

Web:
- Add CONVERT_MAX_FILES_PER_BATCH = 1000 cap
- Minor UI copy and CSS fixes

Bible:
- bible-local/06-parsers.md: add pci.ids enrichment rule (enrich model from
  pciids when name is empty but vendor_id+device_id are present)
- Sync bible submodule and local overview/architecture docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 22:23:44 +03:00

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.1"
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),
})
}
}
}