export: align reanimator and enrich redfish metrics
This commit is contained in:
@@ -30,25 +30,41 @@ Implementation files:
|
||||
- `internal/exporter/reanimator_models.go`
|
||||
- `internal/exporter/reanimator_converter.go`
|
||||
- `internal/server/handlers.go`
|
||||
- `bible-local/docs/hardware-ingest-contract.md`
|
||||
|
||||
Conversion rules:
|
||||
- canonical source is `hardware.devices`
|
||||
- canonical source is merged canonical inventory derived from `hardware.devices` plus legacy hardware slices
|
||||
- output must conform to the strict Reanimator ingest contract in `docs/hardware-ingest-contract.md`
|
||||
- timestamps are RFC3339
|
||||
- status is normalized to Reanimator-friendly values
|
||||
- missing PCIe serials may be generated from board serial + slot
|
||||
- missing CPU serials may be generated from board serial + socket
|
||||
- CPU `firmware` field means CPU microcode, not generic processor firmware inventory
|
||||
- `NULL`-style board manufacturer/product values are treated as absent
|
||||
- optional component telemetry/health fields are exported when LOGPile already has the data
|
||||
- partial `hardware.devices` must not suppress components still present only in legacy parser/collector fields
|
||||
- `present` is not serialized for exported components; presence is expressed by the existence of the component record itself
|
||||
|
||||
## Inclusion rules
|
||||
|
||||
Included:
|
||||
- empty memory slots (`present=false`) for topology visibility
|
||||
- PCIe-class devices even when serial must be synthesized
|
||||
- contract `v2.4` component telemetry and health fields when source data exists
|
||||
- hardware sensors grouped into `fans`, `power`, `temperatures`, `other`
|
||||
- Redfish linked metric docs that carry component telemetry: `ProcessorMetrics`, `MemoryMetrics`, `DriveMetrics`, `EnvironmentMetrics`, `Metrics`
|
||||
|
||||
Excluded:
|
||||
- memory with missing serial number
|
||||
- memory with `present=false` or `status=Empty`
|
||||
- CPUs with `present=false`
|
||||
- storage without `serial_number`
|
||||
- storage with `present=false`
|
||||
- power supplies without `serial_number`
|
||||
- power supplies with `present=false`
|
||||
- non-present network adapters
|
||||
- non-present PCIe / GPU devices
|
||||
- device-bound firmware duplicated at top-level firmware list
|
||||
- any field not present in the strict ingest contract
|
||||
|
||||
## Batch convert
|
||||
|
||||
@@ -61,3 +77,8 @@ Behavior:
|
||||
- each file is parsed independently
|
||||
- one bad file must not fail the whole batch if at least one conversion succeeds
|
||||
- result artifact is temporary and deleted after download
|
||||
|
||||
## CSV export
|
||||
|
||||
`GET /api/export/csv` uses the same merged canonical inventory as Reanimator export,
|
||||
with legacy network-card fallback kept only for records that still have no canonical device match.
|
||||
|
||||
@@ -299,4 +299,159 @@ not the firmware inventory ID. The patterns were dead code from the moment they
|
||||
correct prefix+digit checks (`"gpu" + digit`, `"nic" + digit`) and explicit string checks
|
||||
(`"nvmecontroller"`, `"power supply"`, `"software inventory"`).
|
||||
|
||||
## ADL-020 — Dell TSR device-bound firmware filtered via FQDD; InfiniBand routed to NetworkAdapters
|
||||
|
||||
**Date:** 2026-03-15
|
||||
**Context:** Dell TSR `sysinfo_DCIM_SoftwareIdentity.xml` lists firmware for every installed
|
||||
component. `parseSoftwareIdentityXML` dumped all of these into `hardware.firmware` without
|
||||
filtering, so device-bound entries such as `"Mellanox Network Adapter"` (FQDD `InfiniBand.Slot.1-1`)
|
||||
and `"PERC H755 Front"` (FQDD `RAID.SL.3-1`) appeared in the reanimator export alongside system
|
||||
firmware like BIOS and iDRAC. Confirmed on PowerEdge R6625 (8VS2LG4).
|
||||
|
||||
Additionally, `DCIM_InfiniBandView` was not handled in the parser switch, so Mellanox ConnectX-6
|
||||
appeared only as a PCIe device with `model: "16x or x16"` (from `DataBusWidth` fallback).
|
||||
`parseControllerView` called `addFirmware` with description `"storage controller"` instead of the
|
||||
FQDD, so the FQDD-based filter in the exporter could not remove it.
|
||||
|
||||
**Decision:**
|
||||
1. `isDeviceBoundFirmwareFQDD` extended with `"infiniband."` and `"fc."` prefixes; `"raid.backplane."`
|
||||
broadened to `"raid."` to cover `RAID.SL.*`, `RAID.Integrated.*`, etc.
|
||||
2. `DCIM_InfiniBandView` routed to `parseNICView` → device appears as `NetworkAdapter` with correct
|
||||
firmware, MAC address, and VendorID/DeviceID.
|
||||
3. `"InfiniBand."` added to `pcieFQDDNoisePrefix` to suppress the duplicate `DCIM_PCIDeviceView`
|
||||
entry (DataBusWidth-only, no useful data).
|
||||
4. `parseControllerView` now passes `fqdd` as the `addFirmware` description so the FQDD filter
|
||||
removes the entry in the exporter.
|
||||
5. `parsePCIeDeviceView` now prioritises `props["description"]` (chip model, e.g. `"MT28908 Family
|
||||
[ConnectX-6]"`) over `props["devicedescription"]` (location string) for `pcie.Description`.
|
||||
6. `convertPCIeDevices` model fallback order: `PartNumber → Description → DeviceClass`.
|
||||
|
||||
**Consequences:**
|
||||
- `hardware.firmware` contains only system-level entries; NIC/RAID/storage-controller firmware
|
||||
lives on the respective device record.
|
||||
- `TestParseDellInfiniBandView` and `TestIsDeviceBoundFirmwareFQDD` guard the regression.
|
||||
- Any future Dell TSR device class whose FQDD prefix is not yet in the prefix list may still leak;
|
||||
extend `isDeviceBoundFirmwareFQDD` and add a test case when encountered.
|
||||
|
||||
---
|
||||
|
||||
## ADL-021 — pci.ids enrichment: chip model and vendor resolved from PCI IDs when source data is generic or missing
|
||||
|
||||
**Date:** 2026-03-15
|
||||
**Context:**
|
||||
Dell TSR `DCIM_InfiniBandView.ProductName` reports a generic marketing name ("Mellanox Network
|
||||
Adapter") instead of the precise chip identifier ("MT28908 Family [ConnectX-6]"). The actual
|
||||
chip model is available in `pci.ids` by VendorID:DeviceID (15B3:101B). Vendor name may also be
|
||||
absent when no `VendorName` / `Manufacturer` property is present.
|
||||
|
||||
The general rule was established: *if model is not found in source data but PCI IDs are known,
|
||||
resolve model from `pci.ids`*. This rule applies broadly across all export paths.
|
||||
|
||||
**Decision (two-layer enrichment):**
|
||||
1. **Parser layer (Dell, `parseNICView`):** When `VendorID != 0 && DeviceID != 0`, prefer
|
||||
`pciids.DeviceName(vendorID, deviceID)` over the product name from logs. This makes the chip
|
||||
identifier the primary model for NIC/InfiniBand adapters (more specific than marketing name).
|
||||
Fill `Vendor` from `pciids.VendorName(vendorID)` when the vendor field is otherwise empty.
|
||||
Same fallback applied in `parsePCIeDeviceView` for empty `Description`.
|
||||
2. **Exporter layer (`convertPCIeFromDevices`):** General rule — when `d.Model == ""` after all
|
||||
legacy fallbacks and `VendorID != 0 && DeviceID != 0`, set `model = pciids.DeviceName(...)`.
|
||||
Also fill empty `manufacturer` from `pciids.VendorName(...)`. This covers all parsers/sources.
|
||||
|
||||
**Consequences:**
|
||||
- Mellanox InfiniBand slot now reports `model: "MT28908 Family [ConnectX-6]"` and
|
||||
`manufacturer: "Mellanox Technologies"` in the reanimator export.
|
||||
- For NICs where pci.ids has no entry, the original product name is kept (pci.ids returns "").
|
||||
- `TestParseDellInfiniBandView` asserts the model and vendor from pci.ids.
|
||||
|
||||
---
|
||||
|
||||
## ADL-022 — CPUAffinity parsed into NUMANode for PCIe, NIC, and controller devices
|
||||
|
||||
**Date:** 2026-03-15
|
||||
**Context:**
|
||||
Dell TSR DCIM view classes report `CPUAffinity` for NIC, InfiniBand, PCIe, and controller
|
||||
devices. Values are "1", "2" (NUMA node index), or "Not Applicable" (for devices that bridge
|
||||
both CPUs or have no CPU affinity). This data is needed for topology-aware diagnostics.
|
||||
|
||||
**Decision:**
|
||||
- Add `NUMANode int` (JSON: `"numa_node,omitempty"`) to `models.PCIeDevice`,
|
||||
`models.NetworkAdapter`, `models.HardwareDevice`, and `ReanimatorPCIe`.
|
||||
- Parse from `props["cpuaffinity"]` using `parseIntLoose`: numeric values ("1", "2") map
|
||||
directly; "Not Applicable" returns 0 (omitted via `omitempty`).
|
||||
- Thread through `buildDevicesFromLegacy` (PCIe and NIC sections) and `convertPCIeFromDevices`.
|
||||
- `parseControllerView` also parses CPUAffinity since RAID controllers have NUMA affinity.
|
||||
|
||||
**Consequences:**
|
||||
- `numa_node: 1` or `2` appears in reanimator export for devices with known affinity.
|
||||
- Value 0 / absent means "not reported" — covers both "Not Applicable" and sources that don't
|
||||
provide CPUAffinity at all.
|
||||
- `TestParseDellCPUAffinity` verifies numeric values parsed correctly and "Not Applicable"→0.
|
||||
|
||||
---
|
||||
|
||||
## ADL-023 — Reanimator export must match ingest contract exactly
|
||||
|
||||
**Date:** 2026-03-15
|
||||
**Context:**
|
||||
LOGPile's Reanimator export had drifted from the strict ingest contract. It emitted fields that
|
||||
Reanimator does not currently accept (`status_at_collection`, `numa_node`),
|
||||
while missing fields and sections now present in the contract (`hardware.sensors`,
|
||||
`pcie_devices[].mac_addresses`). Memory export rules also diverged from the ingest side: empty or
|
||||
serial-less DIMMs were still exported.
|
||||
|
||||
**Decision:**
|
||||
- Treat the Reanimator ingest contract as the authoritative schema for `GET /api/export/reanimator`.
|
||||
- Emit only fields present in the current upstream contract revision.
|
||||
- Add `hardware.sensors`, `pcie_devices[].mac_addresses`, `pcie_devices[].numa_node`, and
|
||||
upstream-approved component telemetry/health fields.
|
||||
- Leave out fields that are still not part of the upstream contract.
|
||||
- Map internal `source_type=archive` to external `source_type=logfile`.
|
||||
- Skip memory entries that are empty, not present, or missing serial numbers.
|
||||
- Generate CPU and PCIe serials only in the forms allowed by the contract.
|
||||
- Mirror the applied contract in `bible-local/docs/hardware-ingest-contract.md`.
|
||||
|
||||
**Consequences:**
|
||||
- Some previously exported diagnostic fields are intentionally dropped from the Reanimator payload
|
||||
until the upstream contract adds them.
|
||||
- Internal models may retain richer fields than the current export schema.
|
||||
- `hardware.devices` is canonical only after merge with legacy hardware slices; partial parser-owned
|
||||
canonical records must not hide CPUs, memory, storage, NICs, or PSUs still stored in legacy
|
||||
fields.
|
||||
- CSV and Reanimator exports must use the same merged canonical inventory to avoid divergent export
|
||||
contents across surfaces.
|
||||
- Future exporter changes must update both the code and the mirrored contract document together.
|
||||
|
||||
---
|
||||
|
||||
## ADL-024 — Component presence is implicit; Redfish linked metrics are part of replay correctness
|
||||
|
||||
**Date:** 2026-03-15
|
||||
**Context:**
|
||||
The upstream ingest contract allows `present`, but current export semantics do not need to send
|
||||
`present=true` for populated components. At the same time, several important Redfish component
|
||||
telemetry fields were only available through linked metric resources such as `ProcessorMetrics`,
|
||||
`MemoryMetrics`, and `DriveMetrics`. Without collecting and replaying these linked documents,
|
||||
live collection and raw snapshot replay still underreported component health fields.
|
||||
|
||||
**Decision:**
|
||||
- Do not serialize `present=true` in Reanimator export. Presence is represented by the presence of
|
||||
the component record itself.
|
||||
- Do not export component records marked `present=false`.
|
||||
- Interpret CPU `firmware` in Reanimator payload as CPU microcode.
|
||||
- Treat Redfish linked metric resources `ProcessorMetrics`, `MemoryMetrics`, `DriveMetrics`,
|
||||
`EnvironmentMetrics`, and generic `Metrics` as part of analyzer correctness when they are linked
|
||||
from component resources.
|
||||
- Replay logic must merge these linked metric resources back into CPU, memory, storage, PCIe, GPU,
|
||||
NIC, and PSU component `Details` the same way live collection expects them to be used.
|
||||
|
||||
**Consequences:**
|
||||
- Reanimator payloads are smaller and avoid redundant `present=true` noise while still excluding
|
||||
empty slots and absent components.
|
||||
- Any future exporter change that reintroduces serialized component presence needs an explicit
|
||||
contract review.
|
||||
- Raw Redfish snapshot completeness now includes linked per-component metric resources, not only
|
||||
top-level inventory members.
|
||||
- CPU microcode is no longer expected in top-level `hardware.firmware`; it belongs on the CPU
|
||||
component record.
|
||||
|
||||
<!-- Add new decisions below this line using the format above -->
|
||||
|
||||
@@ -21,6 +21,7 @@ Keep top-level docs minimal and put maintained architecture/API contracts here.
|
||||
| [05-collectors.md](05-collectors.md) | Live collection behavior |
|
||||
| [06-parsers.md](06-parsers.md) | Archive parser framework and vendor coverage |
|
||||
| [07-exporters.md](07-exporters.md) | Raw export, Reanimator export, batch convert |
|
||||
| [docs/hardware-ingest-contract.md](docs/hardware-ingest-contract.md) | Reanimator ingest schema mirrored locally |
|
||||
| [08-build-release.md](08-build-release.md) | Build and release workflow |
|
||||
| [09-testing.md](09-testing.md) | Test expectations and regression rules |
|
||||
| [10-decisions.md](10-decisions.md) | Architectural Decision Log |
|
||||
|
||||
330
bible-local/docs/hardware-ingest-contract.md
Normal file
330
bible-local/docs/hardware-ingest-contract.md
Normal file
@@ -0,0 +1,330 @@
|
||||
# Hardware Ingest Contract
|
||||
|
||||
Version: 2.4
|
||||
Updated: 2026-03-15
|
||||
Source: Reanimator Core `hardware-ingest-contract.md`
|
||||
|
||||
This file mirrors the external Reanimator hardware-ingest contract that LOGPile targets.
|
||||
The Reanimator endpoint uses strict JSON decoding. Any field not listed here must not be emitted.
|
||||
|
||||
## Endpoint
|
||||
|
||||
```http
|
||||
POST /ingest/hardware
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
The ingest request is asynchronous.
|
||||
Accepted response:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "accepted",
|
||||
"job_id": "job_01J..."
|
||||
}
|
||||
```
|
||||
|
||||
Final result is available from:
|
||||
|
||||
```http
|
||||
GET /ingest/hardware/jobs/{job_id}
|
||||
```
|
||||
|
||||
## Top-level payload
|
||||
|
||||
```json
|
||||
{
|
||||
"filename": "redfish://10.10.10.103",
|
||||
"source_type": "api",
|
||||
"protocol": "redfish",
|
||||
"target_host": "10.10.10.103",
|
||||
"collected_at": "2026-02-10T15:30:00Z",
|
||||
"hardware": {
|
||||
"board": {},
|
||||
"firmware": [],
|
||||
"cpus": [],
|
||||
"memory": [],
|
||||
"storage": [],
|
||||
"pcie_devices": [],
|
||||
"power_supplies": [],
|
||||
"sensors": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Top-level rules
|
||||
|
||||
- `collected_at` is required and must be RFC3339
|
||||
- `hardware.board.serial_number` is required
|
||||
- `source_type` allowed values: `api`, `logfile`, `manual`
|
||||
- `protocol` allowed values: `redfish`, `ipmi`, `snmp`, `ssh`
|
||||
- Unknown JSON keys are rejected by Reanimator
|
||||
|
||||
## Shared component status fields
|
||||
|
||||
Allowed on `cpus`, `memory`, `storage`, `pcie_devices`, `power_supplies`:
|
||||
|
||||
- `status`
|
||||
- `status_checked_at`
|
||||
- `status_changed_at`
|
||||
- `status_history`
|
||||
- `error_description`
|
||||
|
||||
`status_history[]` items:
|
||||
|
||||
- `status`
|
||||
- `changed_at`
|
||||
- `details`
|
||||
|
||||
Do not emit `status_at_collection`; it is not part of the current strict ingest schema.
|
||||
|
||||
## `hardware.board`
|
||||
|
||||
Allowed fields:
|
||||
|
||||
- `serial_number` required
|
||||
- `manufacturer`
|
||||
- `product_name`
|
||||
- `part_number`
|
||||
- `uuid`
|
||||
|
||||
String values equal to `"NULL"` should be omitted.
|
||||
|
||||
## `hardware.firmware`
|
||||
|
||||
Allowed fields:
|
||||
|
||||
- `device_name`
|
||||
- `version`
|
||||
|
||||
Only system-level firmware belongs here.
|
||||
Device-bound firmware must stay on the relevant device record and must not be duplicated at the top level.
|
||||
|
||||
## `hardware.cpus`
|
||||
|
||||
Allowed fields:
|
||||
|
||||
- `socket`
|
||||
- `model`
|
||||
- `manufacturer`
|
||||
- `cores`
|
||||
- `threads`
|
||||
- `frequency_mhz`
|
||||
- `max_frequency_mhz`
|
||||
- `temperature_c`
|
||||
- `power_w`
|
||||
- `throttled`
|
||||
- `correctable_error_count`
|
||||
- `uncorrectable_error_count`
|
||||
- `life_remaining_pct`
|
||||
- `life_used_pct`
|
||||
- `serial_number`
|
||||
- `firmware`
|
||||
- `present`
|
||||
- shared status fields
|
||||
|
||||
Exporter rule:
|
||||
- if CPU serial is missing, generate `{board_serial}-CPU-{socket}`
|
||||
|
||||
## `hardware.memory`
|
||||
|
||||
Allowed fields:
|
||||
|
||||
- `slot`
|
||||
- `location`
|
||||
- `present`
|
||||
- `serial_number`
|
||||
- `part_number`
|
||||
- `manufacturer`
|
||||
- `size_mb`
|
||||
- `type`
|
||||
- `max_speed_mhz`
|
||||
- `current_speed_mhz`
|
||||
- `temperature_c`
|
||||
- `correctable_ecc_error_count`
|
||||
- `uncorrectable_ecc_error_count`
|
||||
- `life_remaining_pct`
|
||||
- `life_used_pct`
|
||||
- `spare_blocks_remaining_pct`
|
||||
- `performance_degraded`
|
||||
- `data_loss_detected`
|
||||
- shared status fields
|
||||
|
||||
Exporter rules:
|
||||
- skip memory items with missing `serial_number`
|
||||
- skip memory items with `present=false`
|
||||
- skip memory items with `status=Empty`
|
||||
|
||||
## `hardware.storage`
|
||||
|
||||
Allowed fields:
|
||||
|
||||
- `slot`
|
||||
- `serial_number`
|
||||
- `model`
|
||||
- `manufacturer`
|
||||
- `type`
|
||||
- `interface`
|
||||
- `size_gb`
|
||||
- `temperature_c`
|
||||
- `power_on_hours`
|
||||
- `power_cycles`
|
||||
- `unsafe_shutdowns`
|
||||
- `media_errors`
|
||||
- `error_log_entries`
|
||||
- `written_bytes`
|
||||
- `read_bytes`
|
||||
- `life_used_pct`
|
||||
- `firmware`
|
||||
- `present`
|
||||
- `remaining_endurance_pct`
|
||||
- `life_remaining_pct`
|
||||
- `available_spare_pct`
|
||||
- `reallocated_sectors`
|
||||
- `current_pending_sectors`
|
||||
- `offline_uncorrectable`
|
||||
- shared status fields
|
||||
|
||||
Exporter rule:
|
||||
- skip storage items with missing `serial_number`
|
||||
|
||||
## `hardware.pcie_devices`
|
||||
|
||||
Allowed fields:
|
||||
|
||||
- `slot`
|
||||
- `vendor_id`
|
||||
- `device_id`
|
||||
- `numa_node`
|
||||
- `temperature_c`
|
||||
- `power_w`
|
||||
- `life_remaining_pct`
|
||||
- `life_used_pct`
|
||||
- `ecc_corrected_total`
|
||||
- `ecc_uncorrected_total`
|
||||
- `hw_slowdown`
|
||||
- `battery_charge_pct`
|
||||
- `battery_health_pct`
|
||||
- `battery_temperature_c`
|
||||
- `battery_voltage_v`
|
||||
- `battery_replace_required`
|
||||
- `sfp_temperature_c`
|
||||
- `sfp_tx_power_dbm`
|
||||
- `sfp_rx_power_dbm`
|
||||
- `sfp_voltage_v`
|
||||
- `sfp_bias_ma`
|
||||
- `bdf`
|
||||
- `device_class`
|
||||
- `manufacturer`
|
||||
- `model`
|
||||
- `serial_number`
|
||||
- `firmware`
|
||||
- `link_width`
|
||||
- `link_speed`
|
||||
- `max_link_width`
|
||||
- `max_link_speed`
|
||||
- `mac_addresses`
|
||||
- `present`
|
||||
- shared status fields
|
||||
|
||||
Known `device_class` values:
|
||||
|
||||
- `MassStorageController`
|
||||
- `StorageController`
|
||||
- `NetworkController`
|
||||
- `EthernetController`
|
||||
- `FibreChannelController`
|
||||
- `VideoController`
|
||||
- `ProcessingAccelerator`
|
||||
- `DisplayController`
|
||||
|
||||
Exporter rules:
|
||||
|
||||
- if PCIe serial is missing or placeholder-like, generate `{board_serial}-PCIE-{slot}`
|
||||
- `numa_node` is allowed again in the upstream contract
|
||||
- network adapters should emit `mac_addresses` when available
|
||||
- do not emit fields outside the upstream contract
|
||||
|
||||
## `hardware.power_supplies`
|
||||
|
||||
Allowed fields:
|
||||
|
||||
- `slot`
|
||||
- `present`
|
||||
- `serial_number`
|
||||
- `part_number`
|
||||
- `model`
|
||||
- `vendor`
|
||||
- `wattage_w`
|
||||
- `firmware`
|
||||
- `input_type`
|
||||
- `input_voltage`
|
||||
- `input_power_w`
|
||||
- `output_power_w`
|
||||
- `temperature_c`
|
||||
- `life_remaining_pct`
|
||||
- `life_used_pct`
|
||||
- shared status fields
|
||||
|
||||
Exporter rule:
|
||||
- skip PSUs with missing `serial_number`
|
||||
|
||||
## `hardware.sensors`
|
||||
|
||||
Shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"fans": [],
|
||||
"power": [],
|
||||
"temperatures": [],
|
||||
"other": []
|
||||
}
|
||||
```
|
||||
|
||||
### `sensors.fans`
|
||||
|
||||
Allowed fields:
|
||||
|
||||
- `name` required
|
||||
- `location`
|
||||
- `rpm`
|
||||
- `status`
|
||||
|
||||
### `sensors.power`
|
||||
|
||||
Allowed fields:
|
||||
|
||||
- `name` required
|
||||
- `location`
|
||||
- `voltage_v`
|
||||
- `current_a`
|
||||
- `power_w`
|
||||
- `status`
|
||||
|
||||
### `sensors.temperatures`
|
||||
|
||||
Allowed fields:
|
||||
|
||||
- `name` required
|
||||
- `location`
|
||||
- `celsius`
|
||||
- `threshold_warning_celsius`
|
||||
- `threshold_critical_celsius`
|
||||
- `status`
|
||||
|
||||
### `sensors.other`
|
||||
|
||||
Allowed fields:
|
||||
|
||||
- `name` required
|
||||
- `location`
|
||||
- `value`
|
||||
- `unit`
|
||||
- `status`
|
||||
|
||||
Sensor rules:
|
||||
|
||||
- dedupe within one payload by `(section, name)`, keeping the first item
|
||||
- skip sensors without `name`
|
||||
- the current LOGPile exporter maps generic `SensorReading` values into these four groups heuristically
|
||||
Reference in New Issue
Block a user