mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 03:10:46 +08:00
Add config-sync cycle retention alongside config history keep.
Mirror LLDP job retention so finished sync cycles are pruned by policy while keeping per-NE config version history separate. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
53ddb3b520
commit
a6c7267b47
10 changed files with 162 additions and 7 deletions
|
|
@ -3,13 +3,17 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
from datetime import datetime, timezone
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from unittest.mock import MagicMock, patch
|
||||
from uuid import uuid4
|
||||
|
||||
from netx_api.config_sync_codec import compress_text, decompress_text
|
||||
from netx_api.config_sync_commands import command_list, commands_for_vendor, normalize_vendor_key
|
||||
from netx_api.config_sync_recovery import recover_config_sync_on_startup
|
||||
from netx_api.config_sync_runner import _claim_task, _save_success_snapshot
|
||||
from netx_api.config_sync_service import prune_config_sync_cycles
|
||||
from netx_api.db import Base, SessionLocal, engine
|
||||
from netx_api.models import ConfigSyncCycle, ConfigSyncTask
|
||||
|
||||
|
||||
class ConfigSyncCommandsTests(unittest.TestCase):
|
||||
|
|
@ -208,10 +212,11 @@ class ConfigSyncClaimTests(unittest.TestCase):
|
|||
|
||||
|
||||
class ConfigSyncRecoveryTests(unittest.TestCase):
|
||||
@patch("netx_api.config_sync_recovery.prune_config_sync_cycles")
|
||||
@patch("netx_api.config_sync_recovery.dispatch_cycle", return_value=2)
|
||||
@patch("netx_api.config_sync_recovery.finalize_cycle")
|
||||
@patch("netx_api.config_sync_recovery.sync_cycle_progress")
|
||||
def test_requeues_orphans_and_resumes(self, _sync, _fin, dispatch):
|
||||
def test_requeues_orphans_and_resumes(self, _sync, _fin, dispatch, _prune):
|
||||
cycle = MagicMock()
|
||||
cycle.id = "c1"
|
||||
cycle.status = "running"
|
||||
|
|
@ -250,10 +255,11 @@ class ConfigSyncRecoveryTests(unittest.TestCase):
|
|||
self.assertEqual(resumed, 2)
|
||||
dispatch.assert_called_once_with("c1")
|
||||
|
||||
@patch("netx_api.config_sync_recovery.prune_config_sync_cycles")
|
||||
@patch("netx_api.config_sync_recovery.dispatch_cycle")
|
||||
@patch("netx_api.config_sync_recovery.finalize_cycle")
|
||||
@patch("netx_api.config_sync_recovery.sync_cycle_progress")
|
||||
def test_closes_older_active_keeps_newest(self, _sync, _fin, dispatch):
|
||||
def test_closes_older_active_keeps_newest(self, _sync, _fin, dispatch, _prune):
|
||||
old = MagicMock()
|
||||
old.id = "old"
|
||||
old.status = "running"
|
||||
|
|
@ -287,5 +293,66 @@ class ConfigSyncRecoveryTests(unittest.TestCase):
|
|||
dispatch.assert_not_called()
|
||||
|
||||
|
||||
class ConfigSyncCyclePruneTests(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
Base.metadata.create_all(bind=engine)
|
||||
with engine.begin() as conn:
|
||||
try:
|
||||
conn.exec_driver_sql(
|
||||
"ALTER TABLE config_sync_policy ADD COLUMN IF NOT EXISTS cycle_keep INTEGER DEFAULT 30"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.db = SessionLocal()
|
||||
self.db.query(ConfigSyncTask).delete()
|
||||
self.db.query(ConfigSyncCycle).delete()
|
||||
self.db.commit()
|
||||
|
||||
def tearDown(self) -> None:
|
||||
self.db.close()
|
||||
|
||||
def test_prune_cycles_keeps_newest_and_active(self) -> None:
|
||||
now = datetime.utcnow()
|
||||
for i in range(5):
|
||||
cid = uuid4().hex
|
||||
self.db.add(
|
||||
ConfigSyncCycle(
|
||||
id=cid,
|
||||
trigger_mode="manual",
|
||||
status="success",
|
||||
created_at=now - timedelta(minutes=5 - i),
|
||||
ended_at=now - timedelta(minutes=5 - i),
|
||||
)
|
||||
)
|
||||
self.db.add(
|
||||
ConfigSyncTask(
|
||||
id=uuid4().hex,
|
||||
cycle_id=cid,
|
||||
source="managed",
|
||||
target_id=f"ne-{i}",
|
||||
status="success",
|
||||
)
|
||||
)
|
||||
open_id = uuid4().hex
|
||||
self.db.add(
|
||||
ConfigSyncCycle(
|
||||
id=open_id,
|
||||
trigger_mode="manual",
|
||||
status="running",
|
||||
created_at=now,
|
||||
)
|
||||
)
|
||||
self.db.commit()
|
||||
dropped = prune_config_sync_cycles(self.db, keep=2)
|
||||
self.assertEqual(dropped, 3)
|
||||
left = {r.id for r in self.db.query(ConfigSyncCycle).all()}
|
||||
self.assertIn(open_id, left)
|
||||
self.assertEqual(len(left), 3)
|
||||
self.assertEqual(self.db.query(ConfigSyncTask).count(), 2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue