fix(inspur): correctly handle PCIe Assert/Deassert GPU fault events
Three related fixes for IDL event processing: 1. idl.go: include EventType in dedup key so Deassert events are no longer silently dropped as duplicates of their Assert counterparts. 2. gpu_status.go: treat Deassert events as clearing all GPU faults — previously the code re-applied the same faulty GPU set from the description, leaving GPUs stuck in Critical even after alarm cleared. 3. reanimator_models/converter: add bmc_event_summary section to the Reanimator export — a deduplicated Critical/Warning event table with Active/Resolved status derived from Assert/Deassert pairs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -49,9 +49,10 @@ func ConvertToReanimator(result *models.AnalysisResult) (*ReanimatorExport, erro
|
||||
Memory: dedupeMemory(convertMemoryFromDevices(devices, collectedAt)),
|
||||
Storage: dedupeStorage(convertStorageFromDevices(devices, collectedAt)),
|
||||
PCIeDevices: dedupePCIe(convertPCIeFromDevices(devices, collectedAt)),
|
||||
PowerSupplies: dedupePSUs(convertPSUsFromDevices(devices, collectedAt)),
|
||||
Sensors: convertSensors(result.Sensors),
|
||||
EventLogs: convertEventLogs(result.Events, collectedAt),
|
||||
PowerSupplies: dedupePSUs(convertPSUsFromDevices(devices, collectedAt)),
|
||||
Sensors: convertSensors(result.Sensors),
|
||||
BMCEventSummary: buildBMCEventSummary(result.Events, collectedAt),
|
||||
EventLogs: convertEventLogs(result.Events, collectedAt),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2457,3 +2458,76 @@ func inferTargetHost(targetHost, filename string) string {
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// buildBMCEventSummary produces a summary table of Critical/Warning BMC events
|
||||
// with their resolution status derived from Assert/Deassert pairs.
|
||||
func buildBMCEventSummary(events []models.Event, collectedAt string) []ReanimatorBMCEventRow {
|
||||
type assertKey struct {
|
||||
id string
|
||||
desc string
|
||||
}
|
||||
type eventPair struct {
|
||||
assertEvent *models.Event
|
||||
deassertEvent *models.Event
|
||||
}
|
||||
|
||||
pairs := make(map[assertKey]*eventPair)
|
||||
order := make([]assertKey, 0)
|
||||
|
||||
for i := range events {
|
||||
e := &events[i]
|
||||
if e.Severity != models.SeverityCritical && e.Severity != models.SeverityWarning {
|
||||
continue
|
||||
}
|
||||
key := assertKey{id: e.ID, desc: e.Description}
|
||||
p, exists := pairs[key]
|
||||
if !exists {
|
||||
p = &eventPair{}
|
||||
pairs[key] = p
|
||||
order = append(order, key)
|
||||
}
|
||||
switch strings.ToLower(e.EventType) {
|
||||
case "deassert":
|
||||
if p.deassertEvent == nil || e.Timestamp.After(p.deassertEvent.Timestamp) {
|
||||
p.deassertEvent = e
|
||||
}
|
||||
default:
|
||||
if p.assertEvent == nil || e.Timestamp.Before(p.assertEvent.Timestamp) {
|
||||
p.assertEvent = e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rows := make([]ReanimatorBMCEventRow, 0, len(order))
|
||||
for _, key := range order {
|
||||
p := pairs[key]
|
||||
ref := p.assertEvent
|
||||
if ref == nil {
|
||||
ref = p.deassertEvent
|
||||
}
|
||||
if ref == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
status := "Active"
|
||||
resolvedAt := ""
|
||||
if p.deassertEvent != nil {
|
||||
status = "Resolved"
|
||||
resolvedAt = formatEventLogTime(p.deassertEvent.Timestamp, collectedAt)
|
||||
}
|
||||
|
||||
rows = append(rows, ReanimatorBMCEventRow{
|
||||
Severity: normalizeEventLogSeverity(ref.Severity),
|
||||
Component: strings.ToUpper(strings.TrimSpace(ref.SensorType)),
|
||||
MessageID: strings.TrimSpace(ref.ID),
|
||||
Timestamp: formatEventLogTime(ref.Timestamp, collectedAt),
|
||||
Description: strings.TrimSpace(ref.Description),
|
||||
Status: status,
|
||||
ResolvedAt: resolvedAt,
|
||||
})
|
||||
}
|
||||
if len(rows) == 0 {
|
||||
return nil
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user