Files
Fenris/docs/adr/0006-collector-acquisition-path.md
T

29 lines
4.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 6. Collector acquisition path: smartctl counters, sysfs identity
## Status
Accepted — resolves [Choose the collector's NVMe acquisition path](https://git.bongbetic.com/xavierk/Fenris/issues/16) on the [Wayfinder map](https://git.bongbetic.com/xavierk/Fenris/issues/1).
## Context
The collector ([ADR 0003](0003-service-lifecycle-and-sanctioned-toggle.md)) must acquire SMART/Health counters, thermal evidence, and controller identity each run. The [controller-identity research](https://git.bongbetic.com/xavierk/Fenris/src/branch/research/controller-identity/docs/research/controller-identity.md) fixed the identity key to the normalized, kernel-exposed subsystem NQN and warned that normalization must be specified once and applied at write time — or a collector implementation change can split a drive's own history. [ADR 0004](0004-install-upgrade-removal-lifecycle.md) pins exact Python dependencies in a dedicated venv, and the [segment-metadata decision](https://git.bongbetic.com/xavierk/Fenris/issues/14) froze nullable `vid`/`ssvid`/`transport` alongside the identity fields. Three first-party paths were candidates: the official libnvme Python bindings (SWIG; sysfs-backed attribute getters delivering normalized values), `nvme` CLI JSON output, and the incumbent `smartctl -j` plus sysfs reads.
## Decision
1. **Pin.** Every collection run acquires counters and thermal evidence solely from `smartctl -a -j <device>` and controller identity (`subnqn`, `sn`, `mn`, `fr`, `transport`) solely from sysfs (`/sys/class/nvme/<ctrl>/`). No other acquisition path exists anywhere in the codebase.
2. **Hard pin, no fallback.** Any acquisition failure — missing binary, nonzero exit, malformed JSON, unreadable sysfs attribute — fails the whole collection run; [ADR 0005](0005-failure-detection-and-recovery.md)'s flat retry and freshness grading absorb the miss. A partial sample (identity without counters, or counters without identity) is never written: a transient read failure must not push a healthy drive down the degraded-identity path.
3. **Normalization once, at write time.** One collector-side function normalizes every identity field: trailing spaces and newlines stripped, no case folding, empty-after-strip stored blank. `smartctl` counter and thermal fields are consumed as-is (smartmontools already trims the strings it copies). Padded and unpadded renderings of the same field therefore yield byte-identical stored values.
4. **Segment metadata sourcing.** `transport` comes from the NVMe class sysfs directory; `vid`/`ssvid` from the PCI node (`/sys/class/nvme/<ctrl>/device/{vendor,subsystem_vendor}`) when present, null otherwise — metadata only, never key components.
5. **Prerequisites.** `make install` verifies `smartctl` is present and fails cleanly otherwise. The acquisition path adds no Python dependency and no OS package beyond smartmontools; the [ADR 0004](0004-install-upgrade-removal-lifecycle.md) lockfile is untouched.
## Considered options
- **libnvme Python bindings** — the purest API and natively-normalized getters, but the SWIG module is not on PyPI: entering the venv requires the distro's `python3-libnvme` through `--system-site-packages` or a from-source build, coupling the exact-lockfile venv to the system Python and the distro's shipping choices. Rejected on dependency weight for one privileged five-minute oneshot.
- **`nvme` CLI JSON** — one binary covers counters and identity, but it adds an OS package for what smartmontools already provides, emits untrimmed strings, and reports `subnqn` from Identify data rather than the kernel: when a controller reports an empty NQN the kernel synthesizes one for sysfs while `id-ctrl` JSON omits the field, so the identity ladder would drop a rung depending on the drive. Rejected on packaging and identity-key consistency.
## Consequences
- The venv stays pure-Python; the two acquisition channels per run (subprocess JSON plus sysfs reads) hide behind one acquisition function, gated by acceptance criteria AC-1–AC-5.
- Identity is read from exactly the source the identity key names; libnvme's getters wrap the same sysfs attributes, so the values agree byte-for-byte where both exist.
- Switching acquisition path later is history-sensitive: a future path must deliver byte-identical normalized identity values, or the change itself forces a controller-segment boundary.