--- # node_health — READ-ONLY hardware/health inspection of a node (bare metal or VM). # # Sibling of roles/cluster_verify: cluster_verify answers "is the CLUSTER healthy", # node_health answers "is the HARDWARE under it healthy". Same posture — it inspects and # reports, it never changes the node. Deliberately NOT part of node_baseline: baseline # installs and configures, health only looks. That separation is why this role must never # `apt install` anything (smartmontools is declared in node_baseline_packages; if it is # missing here the SMART checks SKIP with a reason, they do not self-heal). # # Serves R-ENV-01 (reproducible node provisioning — you cannot certify a node you have not # measured), R-ENV-03 (bare-metal parity) and R-OBS-04 (node-level observability). # --- Reporting --------------------------------------------------------------------- # Artifacts land under infra/ansible/_artifacts/node-health/, mirroring the repo's # conformance/_artifacts/ convention: generated output, regenerated on every run, listed # in .gitignore, never committed (it also carries drive serial numbers). node_health_artifact_dir: "{{ playbook_dir }}/../_artifacts/node-health" node_health_write_artifacts: true # Print the human summary to the play output as well as writing it to the artifact dir. node_health_show_summary: true # Report-only by default: a health check that aborts the play hides the other 20 findings. # Set true (e.g. in CI or a pre-build gate) to make any FAIL check end the play non-zero. node_health_fail_on_fail: false # Same, one step stricter: also fail on WARN. node_health_fail_on_warn: false # --- Root-only checks --------------------------------------------------------------- # Everything this role can read WITHOUT root, it reads without root. Only SMART needs # escalation (the ATA/NVMe passthrough ioctls are root-only). The role PROBES become # first and, if escalation is unavailable (e.g. prod's vaulted become password is not # present — infra/PROD-PROVISIONING.md §5), those checks report SKIPPED with the exact # reason. Never a hard failure, and never a fake PASS: a skip is honest. node_health_try_privileged: true # --nocheck=standby: never spin up a sleeping disk just to read it. Read-only hygiene. node_health_smartctl_args: "--json=c --all --nocheck=standby" # --- Thresholds --------------------------------------------------------------------- # All verdicts come from here, so a site can retune without touching task logic. node_health_thresholds: # Filesystems fs_used_pct_warn: 80 fs_used_pct_fail: 90 inode_used_pct_warn: 80 # Memory (percentage of MemTotal still available) mem_available_pct_warn: 15 mem_available_pct_fail: 5 # Temperatures (deg C, any sensor) temp_warn_c: 70 temp_fail_c: 85 # Pressure-stall: "some" avg60, percent of wall time stalled. Dev learned to care about # CPU PSI specifically (it shows saturation long before load average makes it obvious). psi_some_avg60_warn: 10.0 psi_some_avg60_fail: 40.0 # Network nic_min_speed_mbps: 1000 # A single upstream resolver is a known SPOF on this project — it broke containerd # image pulls and CoreDNS forwarding on dev (DECISIONS-LOG 2026-08-31, finding 5). min_nameservers: 2 # SMART (only evaluated when smartctl actually ran) smart_reallocated_warn: 1 smart_reallocated_fail: 50 smart_pending_warn: 1 smart_pending_fail: 10 smart_lifetime_remaining_warn_pct: 20 smart_lifetime_remaining_fail_pct: 10 smart_temp_warn_c: 55 smart_temp_fail_c: 70 # Power-on hours past which a consumer SSD is simply old. Informational — age alone is # not a failure, but it belongs in the operator's field of view when deciding whether a # drive may hold production state (PROD-PROVISIONING.md §2). smart_power_on_hours_notice: 40000 # ATA SMART attribute IDs surfaced verbatim in the report. Vendors disagree on names, so # the report prints whatever the drive calls them and the checks key off the IDs. node_health_smart_attribute_ids: [5, 9, 177, 179, 181, 182, 183, 184, 187, 188, 190, 194, 195, 196, 197, 198, 199, 202, 231, 232, 233, 241, 242, 246]