feat(hardware): collect and export licenses via Dell iDRAC10 Redfish walk

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>
This commit is contained in:
Mikhail Chusavitin
2026-08-11 11:48:21 +03:00
co-authored by Claude Sonnet 5
parent eb6cc207ce
commit eaaf5c09d3
13 changed files with 582 additions and 10 deletions
+22
View File
@@ -106,6 +106,7 @@ type HardwareConfig struct {
NetworkCards []NIC `json:"network_cards,omitempty"`
NetworkAdapters []NetworkAdapter `json:"network_adapters,omitempty"`
PowerSupply []PSU `json:"power_supplies,omitempty"`
Licenses []License `json:"licenses,omitempty"`
}
const (
@@ -357,6 +358,27 @@ type PSU struct {
ErrorDescription string `json:"error_description,omitempty"`
}
// License represents a software/firmware license or feature-on-demand
// activation (BMC advanced licenses, vGPU, RAID feature unlocks, CPU
// feature-on-demand, etc). ComponentRef is empty for system-level licenses.
type License struct {
Name string `json:"name"`
LicenseKey string `json:"license_key,omitempty"`
Vendor string `json:"vendor,omitempty"`
Type string `json:"type,omitempty"`
Feature string `json:"feature,omitempty"`
ComponentRef string `json:"component_ref,omitempty"`
ActivatedAt time.Time `json:"activated_at,omitempty"`
ExpiresAt time.Time `json:"expires_at,omitempty"`
Present bool `json:"present"`
Status string `json:"status,omitempty"`
StatusCheckedAt *time.Time `json:"status_checked_at,omitempty"`
StatusChangedAt *time.Time `json:"status_changed_at,omitempty"`
StatusHistory []StatusHistoryEntry `json:"status_history,omitempty"`
ErrorDescription string `json:"error_description,omitempty"`
}
// GPU represents a graphics processing unit
type GPU struct {
Slot string `json:"slot"`