feat(host_agent): each drive's health, read with smartctl, alertable (#5789)
CI / lint (push) Successful in 3s
CI / integration (push) Successful in 1m24s
CI / unit (push) Successful in 1m32s
CI / publish (push) Successful in 1m10s

The root helper gains one read-only action, `drives`: it finds the drives
itself (smartctl --scan-open), reads each as JSON, and writes a summary the
agent carries on every sample (agent 1.20.0). Steward judges the figures in
drives.py (failing / worn / watch / ok / no SMART / unreadable), records them
per drive and as host roll-ups for covering rules, keeps the newest report on
the registration and lists it on the host page.

install-deps.sh installs smartmontools with apt, dnf, yum, zypper, pacman or
apk, run by install.sh and every agent playbook; one it can't install is
reported, never fatal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-15 09:17:50 -04:00
co-authored by Claude Opus 5.5
parent fdc5c07183
commit 6c4ef368f9
20 changed files with 848 additions and 11 deletions
@@ -0,0 +1,24 @@
# plugins/host_agent/migrations/versions/host_agent_009_drive_health.py
"""host_agent: each host's newest drive-health report (#5789)
Revision ID: host_agent_009_drive_health
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
revision: str = "host_agent_009_drive_health"
down_revision: Union[str, None] = "host_agent_008_maint_plans"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.add_column("host_agent_registrations", sa.Column("drive_health", sa.JSON(), nullable=True))
op.add_column("host_agent_registrations",
sa.Column("drive_health_at", sa.DateTime(timezone=True), nullable=True))
def downgrade() -> None:
op.drop_column("host_agent_registrations", "drive_health_at")
op.drop_column("host_agent_registrations", "drive_health")