feat: surface BMC collection errors in parse-errors panel and event log

When Inspur component.log sections return {"error":"...","code":N} instead
of hardware data, the parser now:
- stores them in AnalysisResult.CollectionErrors (new model field)
- mirrors each one into result.Events with Source="BMC/<section>"
  so the chart viewer event table shows the specific BMC module
- feeds them into /api/parse-errors as bmc_collection_error entries

UI adds a collapsible "Collection diagnostics" panel below the chart
iframe (outside /chart) that appears when /api/parse-errors returns
any items; resets on data clear.

Affected sections in this dump: HDD (1458), PCIe Devices (1458),
Network Adapters (1458), Disk Backplane.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Mikhail Chusavitin
2026-05-21 14:30:01 +03:00
parent 4f7b5b826a
commit 27373aa104
7 changed files with 258 additions and 6 deletions
+21 -1
View File
@@ -16,7 +16,7 @@ import (
// parserVersion - version of this parser module
// IMPORTANT: Increment this version when making changes to parser logic!
const parserVersion = "1.9"
const parserVersion = "2.0"
func init() {
parser.Register(&Parser{})
@@ -163,6 +163,26 @@ func (p *Parser) Parse(files []parser.ExtractedFile) (*models.AnalysisResult, er
// (fan RPM, backplane temperature, PSU summary power, etc.).
componentSensors := ParseComponentLogSensors(f.Content)
result.Sensors = mergeSensorReadings(result.Sensors, componentSensors)
// Record sections where BMC itself returned an error instead of data,
// and mirror each one into the Events stream so they appear in the log viewer.
// Source is set to "BMC/<section>" so the viewer can show the specific module.
for _, ce := range ParseComponentLogCollectionErrors(f.Content) {
result.CollectionErrors = append(result.CollectionErrors, ce)
desc := ce.Message
if ce.Code != 0 {
desc = fmt.Sprintf("%s (code %d)", ce.Message, ce.Code)
}
result.Events = append(result.Events, models.Event{
ID: fmt.Sprintf("bmc_collection_error_%s", strings.ToLower(strings.ReplaceAll(ce.Section, " ", "_"))),
Timestamp: time.Time{}, // no timestamp available
Source: fmt.Sprintf("BMC/%s", ce.Section),
SensorType: "bmc_collection_error",
EventType: "Collection Error",
Severity: models.SeverityWarning,
Description: desc,
})
}
}
// Enrich runtime component data from Redis snapshot (serials, FW, telemetry),