Batch convert processed files strictly sequentially. Parsing/converting
is CPU-bound and stateless per file, so run it across a GOMAXPROCS-sized
worker pool; zip writing and progress updates stay serialized in
original file order.
bmc_event_summary was a derived Assert/Deassert summary added in 4409594
alongside real fixes for GPU fault handling. It's not part of the Reanimator
hardware-ingest contract (event_logs is the only accepted log channel) and
was silently dropped on import — pure dead weight, removed.
Three related status bugs surfaced while auditing converted exports against
the contract, all specific to Inspur/onekeylog dumps:
- CPU status from RESTful CPU info was parsed but never assigned to
models.CPU, and was skipped entirely whenever asset.json already supplied
a CPU list (its own inventory has no status field) — CPUs always exported
as Unknown even when the source reported OK.
- PCIe device status (RESTful "status": 1) was parsed but never mapped onto
models.PCIeDevice, so RESTful-only devices always lost status.
- For GPUs specifically, asset.go emits two device records per physical GPU
(a generic pcie_devices entry enriched with real status, and a separate
gpus entry with a resolved model name but no status). dedupePCIe picks a
single winner by quality score, and a better model name outweighed having
a real status — the winner kept "Unknown" even when a losing duplicate had
the real value. dedupePCIe now backfills status onto the winner from a
losing duplicate when the winner's is Unknown.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Applies a single universal check (negotiated LinkWidth < MaxLinkWidth) in
convertPCIeFromDevices, the shared conversion path all vendor parsers feed
into, so a narrower-than-supported link (bad seat, bent connector, wrong
riser) surfaces as a Warning status regardless of which parser produced the
reading — instead of the previous OtrdDiagnoseComponent.json-only check that
only covered the newer HGX dump layout.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
MAC-format mismatches between collector sources (dash vs colon separators)
were preventing duplicate NIC/PCIe entries from merging in the canonical
device dedup pass. Add MAC address normalization and merge devices that
share a normalized MAC before the existing serial/BDF-based dedup runs.
Also fix a bug in the Inspur redis-dump serial fallback parser: when a
field's inline value was the placeholder "N/A", the code incorrectly fell
through to a window-scan fallback that could pick up an unrelated adjacent
Redis key name (e.g. "AssetInfoPCIEMMIOSpace") as a fake serial number.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add PCI Vendor ID / Device ID to hardware.storage[] per the updated Reanimator
ingest contract, mirroring the existing pcie_devices[] fields. Populated for
Redfish-collected NVMe drives (live + TSR replay, via linked PCIeFunctions)
and for Inspur (from asset.json's own PcieInfo[], joined by PcieSlot). Also
fixes canonicalDevicesForExport dropping the fields when converting Storage
into the canonical HardwareDevice list used by the actual export path.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Points internal/chart at reanimator/chart@ba751c8, which adds the
hardware.licenses[] section (preferred order/title/columns) to the
viewer, matching the reanimator export from the previous commit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Implements the hardware.licenses[] contract section (v2.12, refreshed from
reanimator/core's hardware-ingest-contract.md — was v2.11 locally).
- models.License / HardwareConfig.Licenses mirror the contract field set.
- collector.collectLicenses() reads the standard DMTF
/redfish/v1/LicenseService/Licenses collection during Redfish-walk replay;
it's a generic DMTF resource, not Dell-specific, so any future vendor's
Redfish walk gets license collection for free through
ReplayRedfishFromRawPayloads.
- vendors/dell merges replayed Licenses like every other category.
- exporter.convertLicenses/dedupeLicenses wire hw.Licenses into the
reanimator export directly (no canonical-devices merge — licenses have no
physical identity to merge on), setting Present on every record from the
start (per the ADL-049 round-trip lesson).
- chart viewer renders a licenses section in /chart/current.
Verified end-to-end on the PowerEdge R7715 (1TVFYL4) TSR: 3 system-level
licenses extracted and correctly exported/rendered.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ReanimatorMemory.Present and ReanimatorPSU.Present were already declared as
*bool fields (matching ReanimatorStorage.Present), but convertMemoryFromDevices
and convertPSUsFromDevices never set them, unlike convertStorageFromDevices.
Exported JSON therefore had "present" for storage but not for memory/PSU
items. Re-importing a previously exported reanimator.json via
parseUploadedSnapshot (a direct json.Unmarshal into models.AnalysisResult)
left Present=false on those two categories, and the existing
IsInstalledInventory()/present-required filters then dropped them on the next
/chart/current render — reproduced live: fresh TSR upload showed Memory and
Power Supplies correctly, re-uploading the exported reanimator.json for the
same result did not.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
iDRAC10-generation TSR bundles no longer ship sysinfo_dcim_view.xml /
sysinfo_dcim_softwareidentity.xml, so the dell parser produced events but
no hardware inventory for them. These bundles instead carry
redfishidracwalk.tar.gz, a captured dump of the iDRAC's own Redfish tree.
Add vendors/redfishtree, a shared helper that reconstructs a path->document
map from a tar.gz/zip-packaged Redfish walk (vendor-independent detection:
path hint + /redfish/v1 service-root/Systems/Chassis structural check) and
replays it through the existing collector.ReplayRedfishFromRawPayloads.
vendors/dell uses it to enrich DCIM-XML-derived data (append-only, existing
dedupe passes resolve overlaps). Also register vendors/redfishwalk, a
low-confidence fallback VendorParser using the same helpers, so any other
vendor that starts shipping this kind of raw Redfish walk is picked up
automatically without a dedicated parser.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Inspur/Kaytus onekeylog dumps only exposed the mechanical carrier FRU serial
(component/fru.txt Board Product) as board identity. That serial doesn't
change when the actual NVIDIA HGX baseboard (SXM+NVSwitch "delta board") is
swapped, causing false "board unchanged" conclusions. Parse the real HGX
tray/baseboard Model/PartNumber/SerialNumber triples from
HGX_HWInfo_FWVersion.log into a new HardwareConfig.HGX field, and normalize
Redfish's NA/N-A placeholders to empty across HGX identity parsing.
Closes#21
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fixes#20. This onekeylog variant has no devicefrusdr.log at all: FRU/sensors
come from raw ipmitool text output, PCIe/GPU presence has a dedicated
structural snapshot, SEL lives at a different path, and BMC component
failures are logged separately from SEL/IDL.
- Fall back to component/fru.txt (same FRU block format as devicefrusdr.log)
and component/sensor.txt / sdr.txt (ipmitool sensor list / sdr elist)
when devicefrusdr.log is absent.
- Parse log/bmc/diagnose/OtrdDiagnoseComponent.json's PCIe Device Info array
for GPU/PCIe presence and link state, independent of SEL/IDL alarm
history; flag devices running below their negotiated max link
speed/width as degraded with a Warning event.
- Fall back to log/sel.csv (same format as selelist.csv) when selelist.csv
is absent.
- Parse log/bmc/commer-comp/{commerslot,commerhmc,commerswvr,commerswcpld}
logs (including rotated *.tar.gz.N parts) into failure events, filtering
known-noisy lines.
- Collapse SEL events duplicated across sources by
(timestamp, event_type, description).
- Surface a CollectionError when FRU/sensors are still empty after all
fallbacks, instead of silently returning an empty inventory.
- Fix ParseFRU: a later placeholder "Product Serial : 0" / "Product Part
Number : NULL" line in the same FRU block (e.g. SCM_FRU) was overwriting
an already-parsed real Board Serial/Part Number.
Verified against dump_23DB01633_20260727-1359.tar.gz (HGX B200, KR9288-X3):
fru 0→21, sensors 0→303, 8 GPUs present at Gen5 x16 in slots 100-107.
Deferred (not covered by this change): BIOS-change-settings context and
BIOS POST codes from the same layout — see bible-local/10-decisions.md
ADL-048.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ParseIDLLog deduped by eventID|eventType|description, which is identical
across every occurrence of a recurring alarm (e.g. the flapping PCIe
presence check on Inspur HGX boards). That collapsed every later
occurrence into the first one, so applyGPUStatusFromEvents computed GPU
health from a stale, truncated event list and marked failed GPUs as OK.
Some onekeylog BMC firmware variants split the combined component.log
into per-file D-Bus GetAll transcripts under component/ (e.g.
PowerSupplyInfo.txt, FanInfo.txt), which the inspur parser did not
read, leaving PSU and fan data empty. Add a GETALL block parser and
wire it as a fallback for PSU and fan telemetry when component.log is
absent; document the layout and known gaps in bible-local.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AHS files can exceed 100 MB; the previous 10 MB universal cap silently
truncated them and caused incomplete event parsing. Per-extension limits
are now used: .ahs gets 1 GB, all other single-file types keep 10 MB.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the BMC HDD API returns an empty array (RAID controller attached via
PCIe, e.g. PM8204-2GB), disk serial numbers are now recovered from smartd
startup messages in SOLHostCapture.log.
Enrichment runs in three passes: model-match on existing slots, positional
fill of empty backplane placeholders, then new entries for any remainder.
Both log/ and runningdata/var/ copies are merged with serial deduplication.
Parser version bumped to 2.1.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- identifier-normalization: use strings.EqualFold in h3c/parser.go
- import-export: CSV now uses UTF-8 BOM and semicolon delimiter
- go-code-style: translate all Russian source strings to English (ADL-007)
- go-background-tasks: add Type, Message, Result fields to Job struct
- go-api: wrap list endpoints in {items, total_count, page, per_page, total_pages}
- module-structure: rename helpers.go → context_sleep.go
- build-version-display: htmlError renders version footer on error pages
- go-logging: migrate all log.Printf calls to log/slog with structured attrs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
NF-series storage servers (e.g. NF5280M6) have no GPU/outboard-PCIe
topology, so the previous score gate (topologyScore==0 || boardScore==0
→ return 0) always produced score=0 despite SystemManufacturer="Inspur"
being available. These servers fell into mode=fallback, activating the
AMI profile and probing /Oem/Ami paths that don't exist on the BMC.
Add manufacturer-based detection: SystemManufacturer or
ChassisManufacturer containing "inspur" contributes 60 points —
enough to enter matched mode on its own. GPU servers with full
topology+board signals still score higher as before.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a PDF button to the report header. Clicking it opens
/chart/current?print=true in a new tab, which auto-triggers
window.print() so the user can save to PDF via the browser dialog.
- chart submodule bumped: PrintMode support (no filter JS, auto-print,
@media print CSS)
- handlers.go: passes PrintMode=true when ?print=true query param is set
- index.html: PDF button alongside Raw Data / Reanimator
- app.js: printReport() helper; button shown/hidden with other exports
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
The PSU regex used "RESTful Network" as its end anchor, but in standard
Inspur component.log layout the PCIE Device section sits between PSU and
Network Adapter. The lazy [\s\S]*? captured across the PCIE error block,
producing invalid JSON and silently dropping all PSU data.
Changed anchor to RESTful (?:PCIE|Network) — matches whichever section
immediately follows PSU in a given archive.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When BMC firmware fails to read capacity for a present DIMM, size_mb stays
0. If another DIMM with the same part number in the same batch has a known
size, use it to fill the gap.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dedup by version caused CPU1 Microcode to be omitted when both CPUs run
the same version, leaving the firmware column blank for the second socket.
Each CPU gets its own firmware entry keyed by index.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs in onekeylog archives that lack asset.json:
- CPU count was always 0: ParseComponentLog never parsed the "RESTful CPU
info" section. Added parseCPUInfo as a fallback when hw.CPUs is empty
(asset.json remains the primary source when present). Also worked around
a Go JSON case-insensitive collision between "proc_id" (int) and
"PROC_ID" (string CPUID) by adding an explicit PROC_ID field with an
exact-case tag.
- Only 1 of 2 DIMMs shown: Present condition required mem_mod_size > 0,
but some BMC firmware reports size=0 for a physically installed module
while still providing serial and part number. Now treats a DIMM as
present when status=1 and any of size/serial/partnum is non-empty.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
IOMMUGroup was added to models.PCIeDevice but never wired into the
converter — missing from Details in buildDevicesFromLegacy, no field
in ReanimatorPCIe, and convertPCIeFromDevices never read it.
Add IOMMUGroup *int to ReanimatorPCIe, propagate through Details,
add intPtrFromDetailMap helper.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parse inventory_volume.log: Intel VROC (VMD) RAID volumes including
RAID level, capacity (GiB/TiB support added), status and member drives.
Add Drives []string to StorageVolume model.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lenovo ThinkSystem SR650 V3 (and similar XCC-based servers) caused
collection runs of 23+ minutes because the BMC exposes two large high-
error-rate subtrees in the snapshot BFS:
- Chassis/1/Sensors: 315 individual sensor members, 282/315 failing,
~3.7s per request → ~19 minutes wasted. These documents are never
read by any LOGPile parser (thermal/power data comes from aggregate
Chassis/*/Thermal and Chassis/*/Power endpoints).
- Chassis/1/Oem/Lenovo: 75 requests (LEDs×47, Slots×26, etc.),
68/75 failing → 8+ minutes wasted on non-inventory data.
Add a Lenovo profile (matched on SystemManufacturer/OEMNamespace "Lenovo")
that sets SnapshotExcludeContains to block individual sensor documents and
non-inventory Lenovo OEM subtrees from the snapshot BFS queue. Also sets
rate policy thresholds appropriate for XCC BMC latency (p95 often 3-5s).
Add SnapshotExcludeContains []string to AcquisitionTuning and check it
in the snapshot enqueue closure in redfish.go.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Supermicro HGX BMC reports all 8 B200 GPU PCIe devices with Name
"PCIe Device" — a generic label shared by every GPU, not a unique
hardware position. pcieDedupKey used slot as the primary key, so all
8 GPUs collapsed to one entry in the UI (the first, serial 1654925165720).
Add isGenericPCIeSlotName to detect non-positional slot labels and fall
through to serial/BDF for dedup instead, preserving each GPU separately.
Positional slots (#GPU0, SLOT-NIC1, etc.) continue to use slot-first dedup.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
parseGPUWithSupplementalDocs did not read PCIeInterface from the device
doc, only from function docs. xFusion GPU PCIeCard entries carry link
width/speed in PCIeInterface (LanesInUse/Maxlanes/PCIeType/MaxPCIeType)
so GPU link width was always empty for xFusion servers.
Also apply the xFusion OEM function-level fallback for GPU function docs,
consistent with the NIC and PCIeDevice paths.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
xFusion iBMC exposes PCIe link width in two non-standard ways:
- PCIeInterface uses "Maxlanes" (lowercase 'l') instead of "MaxLanes"
- PCIeFunction docs carry width/speed in Oem.xFusion.LinkWidth ("X8"),
Oem.xFusion.LinkWidthAbility, Oem.xFusion.LinkSpeed, and
Oem.xFusion.LinkSpeedAbility rather than the standard CurrentLinkWidth int
Add redfishEnrichFromOEMxFusionPCIeLink and parseXFusionLinkWidth helpers,
apply them as fallbacks in NIC and PCIeDevice enrichment paths.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove power-on and power-off functionality from the Redfish collector;
keep host power-state detection and show a warning in the UI when the
host is powered off before collection starts.
Add a "Пропустить зависшие" (skip hung) button that lets the user abort
stuck Redfish collection phases without losing already-collected data.
Introduces a two-level context model in Collect(): the outer job context
covers the full lifecycle including replay; an inner collectCtx covers
snapshot, prefetch, and plan-B phases only. Closing the skipCh cancels
collectCtx immediately — aborts all in-flight HTTP requests and exits
plan-B loops — then replay runs on whatever rawTree was collected.
Signal path: UI → POST /api/collect/{id}/skip → JobManager.SkipJob()
→ close(skipCh) → goroutine in Collect() → cancelCollect().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>