From 8edaa78ad7408e079eaecae59dea2933981b63bc Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Wed, 11 Nov 2026 01:11:00 -0500 Subject: [PATCH] Revert #31: open link-down alerts from a monitor glitch were revived and ticketed #31 treated any open interface_down alert as previously up. Production had six such alerts all created in the same second on 2026-03-02 (a monitor glitch, on ports that were never in use: large1 enp36s0 and a USB adapter, compute-storage-01 nic1-3, micro1 enp3s0). Reviving them opened per-host tickets and a P1 cluster-wide ticket. Back to the previous behaviour while a safer fix is worked out. Co-Authored-By: Claude Opus 5.5 --- monitor.py | 6 ------ tests/test_monitor.py | 19 ------------------- 2 files changed, 25 deletions(-) diff --git a/monitor.py b/monitor.py index 99258cf..6894ece 100644 --- a/monitor.py +++ b/monitor.py @@ -1195,10 +1195,6 @@ class NetworkMonitor: def _process_interfaces(self, states: Dict[str, Dict[str, bool]], suppressions: list) -> None: baseline = db.get_baseline() new_baseline = {k: dict(v) for k, v in baseline.items()} - # An interface with an open link-down alert was up before it went down, whatever the - # baseline says now: after a baseline reset it reads as "first seen down", which would - # leave the alert open forever without updating, ticketing or resolving it. - open_down = set(db.get_open_event_targets('interface_down')) # Only count hosts with genuine regressions (UP→DOWN) toward cluster threshold hosts_with_regression: List[str] = [] @@ -1211,8 +1207,6 @@ class NetworkMonitor: for iface, is_up in ifaces.items(): prev = baseline.get(host, {}).get(iface) # 'up', 'initial_down', or None - if (host, iface) in open_down: - prev = 'up' if is_up: new_baseline[host][iface] = 'up' diff --git a/tests/test_monitor.py b/tests/test_monitor.py index e68d1a8..ad418c4 100644 --- a/tests/test_monitor.py +++ b/tests/test_monitor.py @@ -31,7 +31,6 @@ def db(): mock_db.cutsheet_load.return_value = {'items': [], 'cables': [], 'port_labels': []} mock_db.get_pending_recovery_notes.return_value = [] mock_db.get_device_link.return_value = '' - mock_db.get_open_event_targets.return_value = [] yield mock_db @@ -118,24 +117,6 @@ class TestDeadHostDetection: mon._process_interfaces({HOST_A: {'eth0': True}}, []) assert ('cluster_network_issue', 'test-cluster', '') in upserted(db) - def test_open_alert_survives_a_baseline_reset(self, mon, db): - # The baseline lost eth1 (reset), but its link-down alert is still open: keep alerting - db.get_open_event_targets.return_value = [('alpha', 'eth1')] - db.upsert_event.return_value = (3, False, 2, None) - mon._process_interfaces({HOST_A: {'eth0': True, 'eth1': False}, HOST_B: {'eth0': True}}, []) - assert ('interface_down', 'alpha', 'eth1') in upserted(db) - assert 'Interface eth1 link-down' in mon.tickets.create.call_args.args[0] - - def test_open_alert_resolves_when_the_link_returns(self, mon, db): - db.get_open_event_targets.return_value = [('alpha', 'eth1')] - mon._process_interfaces({HOST_A: {'eth0': True, 'eth1': True}, HOST_B: {'eth0': True}}, []) - db.resolve_event.assert_any_call('interface_down', 'alpha', 'eth1') - - def test_first_seen_down_without_an_alert_still_stays_quiet(self, mon, db): - db.get_open_event_targets.return_value = [] - mon._process_interfaces({HOST_A: {'eth0': True, 'eth1': False}, HOST_B: {'eth0': True}}, []) - assert ('interface_down', 'alpha', 'eth1') not in upserted(db) - def test_dead_host_shows_as_down_on_dashboard(self, mon): snap = mon._collect_snapshot({HOST_A: {'eth0': True}}) assert snap['hosts']['alpha']['status'] == 'up' -- 2.47.3