v1.3.0: Add multiple vendor parsers and enhanced hardware detection

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>
This commit is contained in:
Mikhail Chusavitin
2026-01-30 17:19:47 +03:00
parent 21f4e5a67e
commit 70cd541d9e
24 changed files with 2930 additions and 12 deletions

View File

@@ -3,6 +3,7 @@ package parser
import (
"archive/tar"
"archive/zip"
"bytes"
"compress/gzip"
"fmt"
"io"
@@ -24,6 +25,8 @@ func ExtractArchive(archivePath string) ([]ExtractedFile, error) {
switch ext {
case ".gz", ".tgz":
return extractTarGz(archivePath)
case ".tar":
return extractTar(archivePath)
case ".zip":
return extractZip(archivePath)
default:
@@ -37,7 +40,9 @@ func ExtractArchiveFromReader(r io.Reader, filename string) ([]ExtractedFile, er
switch ext {
case ".gz", ".tgz":
return extractTarGzFromReader(r)
return extractTarGzFromReader(r, filename)
case ".tar":
return extractTarFromReader(r)
default:
return nil, fmt.Errorf("unsupported archive format: %s", ext)
}
@@ -50,17 +55,21 @@ func extractTarGz(archivePath string) ([]ExtractedFile, error) {
}
defer f.Close()
return extractTarGzFromReader(f)
return extractTarGzFromReader(f, filepath.Base(archivePath))
}
func extractTarGzFromReader(r io.Reader) ([]ExtractedFile, error) {
gzr, err := gzip.NewReader(r)
func extractTar(archivePath string) ([]ExtractedFile, error) {
f, err := os.Open(archivePath)
if err != nil {
return nil, fmt.Errorf("gzip reader: %w", err)
return nil, fmt.Errorf("open archive: %w", err)
}
defer gzr.Close()
defer f.Close()
tr := tar.NewReader(gzr)
return extractTarFromReader(f)
}
func extractTarFromReader(r io.Reader) ([]ExtractedFile, error) {
tr := tar.NewReader(r)
var files []ExtractedFile
for {
@@ -96,6 +105,75 @@ func extractTarGzFromReader(r io.Reader) ([]ExtractedFile, error) {
return files, nil
}
func extractTarGzFromReader(r io.Reader, filename string) ([]ExtractedFile, error) {
gzr, err := gzip.NewReader(r)
if err != nil {
return nil, fmt.Errorf("gzip reader: %w", err)
}
defer gzr.Close()
// Read all decompressed content into buffer
// Limit to 50MB for plain gzip files, 10MB per file for tar.gz
decompressed, err := io.ReadAll(io.LimitReader(gzr, 50*1024*1024))
if err != nil {
return nil, fmt.Errorf("read gzip content: %w", err)
}
// Try to read as tar archive
tr := tar.NewReader(bytes.NewReader(decompressed))
var files []ExtractedFile
header, err := tr.Next()
if err != nil {
// Not a tar archive - treat as a single gzipped file
if strings.Contains(err.Error(), "invalid tar header") || err == io.EOF {
// Get base filename without .gz extension
baseName := strings.TrimSuffix(filename, ".gz")
if gzr.Name != "" {
baseName = gzr.Name
}
return []ExtractedFile{
{
Path: baseName,
Content: decompressed,
},
}, nil
}
return nil, fmt.Errorf("tar read: %w", err)
}
// It's a valid tar archive, process it
for {
// Skip directories
if header.Typeflag != tar.TypeDir {
// Skip large files (>10MB)
if header.Size <= 10*1024*1024 {
content, err := io.ReadAll(tr)
if err != nil {
return nil, fmt.Errorf("read file %s: %w", header.Name, err)
}
files = append(files, ExtractedFile{
Path: header.Name,
Content: content,
})
}
}
// Read next header
header, err = tr.Next()
if err == io.EOF {
break
}
if err != nil {
return nil, fmt.Errorf("tar read: %w", err)
}
}
return files, nil
}
func extractZip(archivePath string) ([]ExtractedFile, error) {
r, err := zip.OpenReader(archivePath)
if err != nil {