From 967e05ed74d771a6e21a54170de06fbb3b6dd581 Mon Sep 17 00:00:00 2001 From: Andreas Wrede Date: Fri, 8 May 2026 16:39:16 -0400 Subject: [PATCH] threshold: synthesize health_ok server-side for older ZFS clients Older hbd clients send zfs_monitor data with a `health` string but no `health_ok` numeric field (added in a recent plugin update). Without health_ok in the data, the wildcard threshold check found nothing and no CRITICAL alert was raised for DEGRADED/SUSPENDED pools. Synthesize health_ok from the health string in the server's nested- metric loop so alerts fire regardless of client version. Co-Authored-By: Claude Sonnet 4.6 --- hbd/server/threshold.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hbd/server/threshold.py b/hbd/server/threshold.py index 1bcbb8a..5a4e21f 100644 --- a/hbd/server/threshold.py +++ b/hbd/server/threshold.py @@ -1026,7 +1026,12 @@ class ThresholdChecker: for pool_name, pool_metrics in pools.items(): if not isinstance(pool_metrics, dict): continue - for metric_name, value in pool_metrics.items(): + # Synthesize health_ok from health string for older clients + # that predate the health_ok field. + pool_metrics_effective = dict(pool_metrics) + if "health" in pool_metrics and "health_ok" not in pool_metrics: + pool_metrics_effective["health_ok"] = 1 if pool_metrics["health"] == "ONLINE" else 0 + for metric_name, value in pool_metrics_effective.items(): # Try specific pool name first, then wildcard '*' metric_path = f"{plugin_name}.{pool_name}.{metric_name}" wildcard_path = f"{plugin_name}.*.{metric_name}" @@ -1043,7 +1048,7 @@ class ThresholdChecker: elif new_level == AlertLevel.WARNING and threshold.warning is not None: threshold_value = threshold.warning alert_state.hysteresis = threshold.hysteresis if new_level != AlertLevel.OK else None - pool_context = dict(pool_metrics) + pool_context = dict(pool_metrics_effective) pool_context["pool_name"] = pool_name old_level = alert_state.level if alert_state.update(new_level, value, threshold_value, threshold.operator.value):