feat(collector): capture additional smartctl diagnostics

Record host read and write commands, controller busy time, error log
entries, warning and critical temperature time, and thermal management
transitions from the existing smartctl -a -j acquisition. Schema 7 adds
nullable columns to samples so legacy rows read as unknown, and the
Diagnostics panel shows each counter with its change.
This commit is contained in:
xavierk
2026-10-05 21:12:25 +05:30
parent 948e097952
commit ab1d894adc
7 changed files with 332 additions and 10 deletions
+45 -2
View File
@@ -11,7 +11,23 @@ from typing import Optional
# Schema version - increment on each migration
SCHEMA_VERSION = 6
SCHEMA_VERSION = 7
# Diagnostic counters added in schema 7 (issue #111), named after the
# NVMe health-log keys they come from. All nullable: legacy rows and drives that
# do not report a counter stay unknown rather than zero.
DIAGNOSTIC_SAMPLE_COLUMNS = (
"host_reads",
"host_writes",
"controller_busy_time",
"num_err_log_entries",
"warning_temp_time",
"critical_comp_time",
"thm_temp1_trans_count",
"thm_temp1_total_time",
"thm_temp2_trans_count",
"thm_temp2_total_time",
)
# Packaged default placement (spec §8.3). The config may override it, but a
@@ -109,7 +125,17 @@ def _create_schema(conn: sqlite3.Connection):
bytes_read INTEGER,
critical_warning INTEGER,
segment_id INTEGER,
local_tz TEXT
local_tz TEXT,
host_reads INTEGER,
host_writes INTEGER,
controller_busy_time INTEGER,
num_err_log_entries INTEGER,
warning_temp_time INTEGER,
critical_comp_time INTEGER,
thm_temp1_trans_count INTEGER,
thm_temp1_total_time INTEGER,
thm_temp2_trans_count INTEGER,
thm_temp2_total_time INTEGER
)
""")
@@ -303,6 +329,7 @@ def _apply_migrations(conn: sqlite3.Connection, current_version: int):
4: _migrate_3_to_4,
5: _migrate_4_to_5,
6: _migrate_5_to_6,
7: _migrate_6_to_7,
}
while current_version < SCHEMA_VERSION:
target_version = current_version + 1
@@ -408,6 +435,22 @@ def _migrate_5_to_6(conn: sqlite3.Connection) -> None:
repair_legacy_local_day_evidence(conn)
def _migrate_6_to_7(conn: sqlite3.Connection) -> None:
"""Add nullable diagnostic counters to samples (issue #111).
Additive only: existing rows are untouched and read NULL (unknown).
Rollback (SQLite 3.35+, with the collector stopped): for each column in
DIAGNOSTIC_SAMPLE_COLUMNS run ``ALTER TABLE samples DROP COLUMN <name>``,
then ``PRAGMA user_version=6``. Dropping loses only the new counters.
"""
cols = {row[1] for row in conn.execute("PRAGMA table_info(samples)").fetchall()}
if not cols:
return
for column in DIAGNOSTIC_SAMPLE_COLUMNS:
if column not in cols:
conn.execute(f"ALTER TABLE samples ADD COLUMN {column} INTEGER")
def migrate_to_latest(store_path: Path) -> int:
"""Apply forward-only migrations to bring the store to SCHEMA_VERSION.