Older AMI-BMC Inspur onekeylog archives (NF5466M5 / NF5280M5 generation)
opened to an empty result: they carry none of the files the inspur parser
keys on (no asset.json, devicefrusdr.log, selelist.csv or component.log).
New package internal/parser/vendors/inspur_legacy (vendor id inspur_legacy),
separate from inspur:
- binary IPMI FRU decode (FRU.bin) -> board identity
- Inspur_AssetInfoInventory.log -> CPU / memory / PCIe / PSU inventory
- events from Inspur_<model>_<serial>_IDL, sel.log, blackbox.log,
MegaRAID raid0.log, and the flat AMI <severity>.log files
- no live sensors in this archive class -> recorded as a collection error
- BMC clock timestamps before 2010 dropped as un-set (1970 / ~2005 RTC)
Registry: add optional PrioritizedParser { DetectPriority() int } so a
confidence tie is broken by specificity. inspur_legacy returns 10 and also
declines (Detect 0) when modern Kaytus markers are present, so the two
Inspur parsers never fight over a newer dump.
Docs: ADL-065, 06-parsers.md, releases/v1.32.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017VL8wLGD6Lnp6hZCpqT6cZ
159 lines
3.7 KiB
Go
159 lines
3.7 KiB
Go
package parser
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"sync"
|
|
)
|
|
|
|
var (
|
|
registry = make(map[string]VendorParser)
|
|
registryLock sync.RWMutex
|
|
)
|
|
|
|
// Register adds a vendor parser to the registry
|
|
// Called from vendor module init() functions
|
|
func Register(p VendorParser) {
|
|
registryLock.Lock()
|
|
defer registryLock.Unlock()
|
|
|
|
vendor := p.Vendor()
|
|
if _, exists := registry[vendor]; exists {
|
|
panic(fmt.Sprintf("parser already registered for vendor: %s", vendor))
|
|
}
|
|
registry[vendor] = p
|
|
}
|
|
|
|
// GetParser returns parser for specific vendor
|
|
func GetParser(vendor string) (VendorParser, bool) {
|
|
registryLock.RLock()
|
|
defer registryLock.RUnlock()
|
|
|
|
p, ok := registry[vendor]
|
|
return p, ok
|
|
}
|
|
|
|
// ListParsers returns list of all registered vendor names
|
|
func ListParsers() []string {
|
|
registryLock.RLock()
|
|
defer registryLock.RUnlock()
|
|
|
|
vendors := make([]string, 0, len(registry))
|
|
for v := range registry {
|
|
vendors = append(vendors, v)
|
|
}
|
|
sort.Strings(vendors)
|
|
return vendors
|
|
}
|
|
|
|
// ParserInfo contains information about a registered parser
|
|
type ParserInfo struct {
|
|
Vendor string `json:"vendor"`
|
|
Name string `json:"name"`
|
|
Version string `json:"version"`
|
|
}
|
|
|
|
// ListParsersInfo returns detailed info about all registered parsers
|
|
func ListParsersInfo() []ParserInfo {
|
|
registryLock.RLock()
|
|
defer registryLock.RUnlock()
|
|
|
|
parsers := make([]ParserInfo, 0, len(registry))
|
|
for _, p := range registry {
|
|
parsers = append(parsers, ParserInfo{
|
|
Vendor: p.Vendor(),
|
|
Name: p.Name(),
|
|
Version: p.Version(),
|
|
})
|
|
}
|
|
|
|
// Sort by vendor name
|
|
sort.Slice(parsers, func(i, j int) bool {
|
|
return parsers[i].Vendor < parsers[j].Vendor
|
|
})
|
|
|
|
return parsers
|
|
}
|
|
|
|
// DetectResult holds detection result for a parser
|
|
type DetectResult struct {
|
|
Parser VendorParser
|
|
Confidence int
|
|
}
|
|
|
|
// PrioritizedParser is an optional interface a parser may implement to break
|
|
// confidence ties in detection. A more specific parser that overlaps with a
|
|
// broader one on the same archive class returns a higher priority so it is
|
|
// chosen when both report equal confidence. Parsers that do not implement it
|
|
// are treated as priority 0.
|
|
type PrioritizedParser interface {
|
|
DetectPriority() int
|
|
}
|
|
|
|
func detectPriority(p VendorParser) int {
|
|
if pp, ok := p.(PrioritizedParser); ok {
|
|
return pp.DetectPriority()
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func lessByConfidenceThenPriority(a, b DetectResult) bool {
|
|
if a.Confidence != b.Confidence {
|
|
return a.Confidence > b.Confidence
|
|
}
|
|
return detectPriority(a.Parser) > detectPriority(b.Parser)
|
|
}
|
|
|
|
// DetectFormat tries to detect archive format and returns best matching parser
|
|
func DetectFormat(files []ExtractedFile) (VendorParser, error) {
|
|
registryLock.RLock()
|
|
defer registryLock.RUnlock()
|
|
|
|
var results []DetectResult
|
|
|
|
for _, p := range registry {
|
|
confidence := p.Detect(files)
|
|
if confidence > 0 {
|
|
results = append(results, DetectResult{
|
|
Parser: p,
|
|
Confidence: confidence,
|
|
})
|
|
}
|
|
}
|
|
|
|
if len(results) == 0 {
|
|
return nil, fmt.Errorf("no parser found for this archive format")
|
|
}
|
|
|
|
// Sort by confidence descending, breaking ties by detect priority.
|
|
sort.Slice(results, func(i, j int) bool {
|
|
return lessByConfidenceThenPriority(results[i], results[j])
|
|
})
|
|
|
|
return results[0].Parser, nil
|
|
}
|
|
|
|
// DetectAllFormats returns all parsers that can handle the files with their confidence
|
|
func DetectAllFormats(files []ExtractedFile) []DetectResult {
|
|
registryLock.RLock()
|
|
defer registryLock.RUnlock()
|
|
|
|
var results []DetectResult
|
|
|
|
for _, p := range registry {
|
|
confidence := p.Detect(files)
|
|
if confidence > 0 {
|
|
results = append(results, DetectResult{
|
|
Parser: p,
|
|
Confidence: confidence,
|
|
})
|
|
}
|
|
}
|
|
|
|
sort.Slice(results, func(i, j int) bool {
|
|
return lessByConfidenceThenPriority(results[i], results[j])
|
|
})
|
|
|
|
return results
|
|
}
|