From 73aa89f8f4cd30d85ff081ae5250305a6747f81f Mon Sep 17 00:00:00 2001 From: Andreas Wrede Date: Sat, 4 Apr 2026 12:43:30 -0400 Subject: [PATCH] fix web page issues --- hbd/server/hbdclass.py | 46 ++++++++++++++++++-------- hbd/server/templates/live.html | 60 +++++++++++++++++++++++++++------- hbd/server/threshold.py | 12 +++++-- 3 files changed, 91 insertions(+), 27 deletions(-) diff --git a/hbd/server/hbdclass.py b/hbd/server/hbdclass.py index da23962..31da6ac 100644 --- a/hbd/server/hbdclass.py +++ b/hbd/server/hbdclass.py @@ -308,19 +308,29 @@ class Host: d["dyn"] = str(self.dyn) d["num"] = self.num - # Add alert counts - warning_count = 0 - critical_count = 0 + # Add alert counts (split by acknowledged status) + warning_unacked = 0 + warning_acked = 0 + critical_unacked = 0 + critical_acked = 0 for metric_path, alert_state in self.alert_states.items(): # Import AlertLevel here to avoid circular imports from .threshold import AlertLevel if alert_state.level == AlertLevel.WARNING: - warning_count += 1 + if alert_state.acknowledged: + warning_acked += 1 + else: + warning_unacked += 1 elif alert_state.level == AlertLevel.CRITICAL: - critical_count += 1 + if alert_state.acknowledged: + critical_acked += 1 + else: + critical_unacked += 1 - d["alert_warning_count"] = warning_count - d["alert_critical_count"] = critical_count + d["alert_warning_unacked"] = warning_unacked + d["alert_warning_acked"] = warning_acked + d["alert_critical_unacked"] = critical_unacked + d["alert_critical_acked"] = critical_acked for c in ["IPv4", "IPv6"]: if c in self.connections: @@ -380,18 +390,28 @@ class Host: ddict[d] = self.__dict__[d] # Add alert counts (computed from alert_states) - warning_count = 0 - critical_count = 0 + warning_unacked = 0 + warning_acked = 0 + critical_unacked = 0 + critical_acked = 0 if hasattr(self, 'alert_states'): from .threshold import AlertLevel for metric_path, alert_state in self.alert_states.items(): if alert_state.level == AlertLevel.WARNING: - warning_count += 1 + if alert_state.acknowledged: + warning_acked += 1 + else: + warning_unacked += 1 elif alert_state.level == AlertLevel.CRITICAL: - critical_count += 1 + if alert_state.acknowledged: + critical_acked += 1 + else: + critical_unacked += 1 - ddict["alert_warning_count"] = warning_count - ddict["alert_critical_count"] = critical_count + ddict["alert_warning_unacked"] = warning_unacked + ddict["alert_warning_acked"] = warning_acked + ddict["alert_critical_unacked"] = critical_unacked + ddict["alert_critical_acked"] = critical_acked return ddict diff --git a/hbd/server/templates/live.html b/hbd/server/templates/live.html index f0e442c..295f429 100644 --- a/hbd/server/templates/live.html +++ b/hbd/server/templates/live.html @@ -276,11 +276,23 @@ c_name.innerHTML = data.name; } - // Set alert counts - var warningCount = data.alert_warning_count || 0; - var criticalCount = data.alert_critical_count || 0; - c_warning.innerHTML = warningCount > 0 ? warningCount : ""; - c_critical.innerHTML = criticalCount > 0 ? criticalCount : ""; + // Set alert counts in "x/y" format (unacked/acked) + var warningUnacked = data.alert_warning_unacked || 0; + var warningAcked = data.alert_warning_acked || 0; + var criticalUnacked = data.alert_critical_unacked || 0; + var criticalAcked = data.alert_critical_acked || 0; + + if (warningUnacked > 0 || warningAcked > 0) { + c_warning.innerHTML = warningAcked > 0 ? warningUnacked + "/" + warningAcked : warningUnacked; + } else { + c_warning.innerHTML = ""; + } + + if (criticalUnacked > 0 || criticalAcked > 0) { + c_critical.innerHTML = criticalAcked > 0 ? criticalUnacked + "/" + criticalAcked : criticalUnacked; + } else { + c_critical.innerHTML = ""; + } c_ipv4addr.innerHTML = data.connections[0].addr; c_ipv4state.innerHTML = data.connections[0].state; @@ -305,11 +317,23 @@ setup(); } - // Update warning and critical counts - var warningCount = data.alert_warning_count || 0; - var criticalCount = data.alert_critical_count || 0; - name_idx[data.name].cells[1].innerHTML = warningCount > 0 ? warningCount : ""; - name_idx[data.name].cells[2].innerHTML = criticalCount > 0 ? criticalCount : ""; + // Update warning and critical counts in "x/y" format (unacked/acked) + var warningUnacked = data.alert_warning_unacked || 0; + var warningAcked = data.alert_warning_acked || 0; + var criticalUnacked = data.alert_critical_unacked || 0; + var criticalAcked = data.alert_critical_acked || 0; + + if (warningUnacked > 0 || warningAcked > 0) { + name_idx[data.name].cells[1].innerHTML = warningAcked > 0 ? warningUnacked + "/" + warningAcked : warningUnacked; + } else { + name_idx[data.name].cells[1].innerHTML = ""; + } + + if (criticalUnacked > 0 || criticalAcked > 0) { + name_idx[data.name].cells[2].innerHTML = criticalAcked > 0 ? criticalUnacked + "/" + criticalAcked : criticalUnacked; + } else { + name_idx[data.name].cells[2].innerHTML = ""; + } for (var i = 0; i < data.connections.length; i++) { // Offset by 2 for the warning/critical count columns @@ -428,8 +452,20 @@ {% for host in hosts %} {{ host.name }} - {{ host.alert_warning_count if host.alert_warning_count > 0 else '' }} - {{ host.alert_critical_count if host.alert_critical_count > 0 else '' }} + + {%- set warning_unacked = host.alert_warning_unacked -%} + {%- set warning_acked = host.alert_warning_acked -%} + {%- if warning_unacked > 0 or warning_acked > 0 -%} + {{ warning_unacked }}{% if warning_acked > 0 %}/{{ warning_acked }}{% endif %} + {%- endif -%} + + + {%- set critical_unacked = host.alert_critical_unacked -%} + {%- set critical_acked = host.alert_critical_acked -%} + {%- if critical_unacked > 0 or critical_acked > 0 -%} + {{ critical_unacked }}{% if critical_acked > 0 %}/{{ critical_acked }}{% endif %} + {%- endif -%} + {% for conn in host.connections %} {{ conn.addr if conn.addr else '' }} {{ conn.state if conn.state else '' }} diff --git a/hbd/server/threshold.py b/hbd/server/threshold.py index 7efbec3..c2d982e 100644 --- a/hbd/server/threshold.py +++ b/hbd/server/threshold.py @@ -114,11 +114,19 @@ class AlertState: def to_dict(self) -> dict: """Convert alert state to dictionary for serialization.""" + import math + + # Helper to sanitize numeric values for JSON (handle inf/nan) + def sanitize_value(val): + if isinstance(val, float) and (math.isinf(val) or math.isnan(val)): + return None + return val + result = { "metric_path": self.metric_path, "level": self.level.name, "since": self.since, - "last_value": self.last_value, + "last_value": sanitize_value(self.last_value), "last_check": self.last_check, "notification_count": self.notification_count, "acknowledged": self.acknowledged, @@ -130,7 +138,7 @@ class AlertState: # Include threshold info if available if self.threshold_value is not None: - result["threshold_value"] = self.threshold_value + result["threshold_value"] = sanitize_value(self.threshold_value) if self.operator is not None: result["operator"] = self.operator if self.formatted_message is not None: