mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-05-29 19:21:08 -04:00
Enhance carbon-copy behavior to prevent overwriting devPresentLastScan in sync operations #1651
This commit is contained in:
@@ -326,12 +326,22 @@ def main():
|
||||
placeholders = ', '.join('?' for _ in insert_cols)
|
||||
|
||||
if sync_behavior == 'carbon-copy':
|
||||
# UPSERT: on MAC conflict update all columns except devMac.
|
||||
# UPSERT: on MAC conflict update all columns except devMac and
|
||||
# devPresentLastScan.
|
||||
# devMac is the PRIMARY KEY so it is excluded from the SET clause.
|
||||
# NOTE: this raw SQL bypasses can_overwrite_field() — ALL fields
|
||||
# devPresentLastScan is excluded to prevent a node's offline report
|
||||
# from clobbering the hub's own scan result: if a device is online
|
||||
# on the hub network but offline on a node, the raw UPSERT would
|
||||
# flip devPresentLastScan = 0 every sync cycle, triggering
|
||||
# Connected/Disconnected events on each scan and causing the device
|
||||
# to be flagged as Flapping. Presence is owned by
|
||||
# update_presence_from_CurrentScan(); the carbon-copy path respects
|
||||
# that contract by leaving devPresentLastScan to the normal pipeline.
|
||||
# NOTE: this raw SQL bypasses can_overwrite_field() — ALL other fields
|
||||
# including USER/LOCKED-sourced ones are overwritten. Node is fully
|
||||
# authoritative in this mode.
|
||||
update_cols = [col for col in insert_cols if col != 'devMac']
|
||||
_CARBON_COPY_SKIP = {'devMac', 'devPresentLastScan'}
|
||||
update_cols = [col for col in insert_cols if col not in _CARBON_COPY_SKIP]
|
||||
update_clause = ', '.join(f'{col}=excluded.{col}' for col in update_cols)
|
||||
sql = (
|
||||
f'INSERT INTO Devices ({columns}) VALUES ({placeholders}) '
|
||||
|
||||
@@ -351,7 +351,8 @@ def sync_insert_devices(
|
||||
placeholders = ", ".join("?" for _ in insert_cols)
|
||||
|
||||
if behavior == "carbon-copy":
|
||||
update_cols = [col for col in insert_cols if col != "devMac"]
|
||||
_CARBON_COPY_SKIP = {"devMac", "devPresentLastScan"}
|
||||
update_cols = [col for col in insert_cols if col not in _CARBON_COPY_SKIP]
|
||||
update_clause = ", ".join(f"{col}=excluded.{col}" for col in update_cols)
|
||||
sql = (
|
||||
f"INSERT INTO Devices ({columns}) VALUES ({placeholders}) "
|
||||
|
||||
@@ -668,6 +668,36 @@ class TestSyncBehavior:
|
||||
cur.execute("SELECT COUNT(*) AS cnt FROM Devices WHERE devMac = ?", ("aa:bb:cc:dd:ee:01",))
|
||||
assert cur.fetchone()["cnt"] == 1
|
||||
|
||||
def test_carbon_copy_does_not_overwrite_devPresentLastScan(self, conn):
|
||||
"""Regression: carbon-copy must NOT clobber devPresentLastScan.
|
||||
|
||||
Scenario: device is online on the hub (devPresentLastScan=1) but the
|
||||
node reports it as offline (devPresentLastScan=0). Without the fix the
|
||||
UPSERT would flip presence to 0, triggering a Device Down event on the
|
||||
next scan cycle and a Connected event on the scan after that, causing
|
||||
the device to accumulate enough churn events to be flagged as Flapping.
|
||||
"""
|
||||
cur = conn.cursor()
|
||||
# Hub already knows this device and currently sees it as online.
|
||||
cur.execute(
|
||||
"INSERT INTO Devices (devMac, devName, devPresentLastScan) VALUES (?, ?, ?)",
|
||||
("aa:bb:cc:dd:ee:01", "HubDevice", 1),
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
# Node reports same MAC as offline.
|
||||
device = make_device_dict(mac="aa:bb:cc:dd:ee:01", devPresentLastScan=0)
|
||||
sync_insert_devices(conn, [device], behavior="carbon-copy")
|
||||
|
||||
cur.execute(
|
||||
"SELECT devPresentLastScan FROM Devices WHERE devMac = ?",
|
||||
("aa:bb:cc:dd:ee:01",),
|
||||
)
|
||||
row = cur.fetchone()
|
||||
assert row["devPresentLastScan"] == 1, (
|
||||
"carbon-copy must not overwrite devPresentLastScan with a node's offline value"
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# hub-defaults — no direct write, hub pipeline handles it
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user