mirror of
https://github.com/hansjone/netx.git
synced 2026-10-17 20:33:19 +08:00
Optimize cutover compare/monitor per the 2026-10-12 plan.
Correctness (A): normalize port-map names with the template rules (snapshot v2, v1 replays unchanged); protect mappings/templates that are referenced; stop dropping edited mapping text; accept pulled-but-not-shutdown ports as migrated; validate baseline ownership on project create; one canonical key form; fix built-in template tolerances and upgrade old packaged values in place. Cross-vendor identity (B): single identity pipeline for every path, per-side and per-vendor interface rules, value maps, filters after normalization, mapping pre-flight for compare and cutover projects, SQL eligibility fixes. Data quality (V): no_data / suspect_parse / not_collected sheet states, command slice coverage, directional compare modes, value-sorted duplicate pairing, before-side raw names in results and CSV. Robustness (H): bounded auto-run history, background evaluate/finish with polling, manual route tables out of the acceptance gate, baseline row cache, scoped duplicate-key failures, merged HF tasks with a per-NE session cap, same-device cutover, explicit scope_by_port_map, read-only GETs, resolvable red tickets. Frontend: pre-flight report, mapping table view, template editor support for the new rules, data-state banners. Docs: canonical field contract. Not included: Huawei status parsers (no real samples), SQL-path route compare. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
fff7d63b9b
commit
315539e0fd
59 changed files with 6483 additions and 487 deletions
|
|
@ -90,6 +90,33 @@ class CompareSnapshotTests(unittest.TestCase):
|
|||
self.assertEqual(detail["sheets"][0]["compare_fields"], ["state"])
|
||||
self.assertEqual(self.run.summary_json["config_snapshot"], original)
|
||||
|
||||
def _use_raw_port_map(self, version: int):
|
||||
# Map written exactly as the device prints it (not the normalized form).
|
||||
self.pair.before_if, self.pair.after_if = "GE-1", "GE-2"
|
||||
self.db.commit()
|
||||
self.run = _create_running_run(
|
||||
self.db, job=self.job, tpl=self.tpl, before_batch_id="before",
|
||||
after_batch_id="after", sheets_cfg=template_metrics(self.tpl),
|
||||
)
|
||||
snap = deepcopy(self.run.summary_json)
|
||||
snap["config_snapshot"]["version"] = version
|
||||
self.run.summary_json = snap
|
||||
self.db.commit()
|
||||
|
||||
def test_v2_run_normalizes_raw_port_map_names(self):
|
||||
self._use_raw_port_map(2)
|
||||
detail = self.execute()
|
||||
self.assertEqual(detail["summary"]["unchanged"], 1)
|
||||
self.assertEqual(detail["summary"]["added"], 0)
|
||||
self.assertEqual(detail["summary"]["removed"], 0)
|
||||
|
||||
def test_v1_run_keeps_legacy_port_map_matching(self):
|
||||
self._use_raw_port_map(1)
|
||||
detail = self.execute()
|
||||
self.assertEqual(detail["summary"]["unchanged"], 0)
|
||||
self.assertEqual(detail["summary"]["added"], 1)
|
||||
self.assertEqual(detail["summary"]["removed"], 1)
|
||||
|
||||
def test_compact_hydration_restores_original_normalized_values(self):
|
||||
self.execute()
|
||||
self.edit_current_settings()
|
||||
|
|
@ -98,6 +125,15 @@ class CompareSnapshotTests(unittest.TestCase):
|
|||
self.assertEqual(diff["mapped_before"]["interface"], "gei-2.100")
|
||||
self.assertEqual(diff["after"]["interface"], "gei-2.100")
|
||||
|
||||
def test_diff_rows_expose_the_before_side_name_as_printed(self):
|
||||
self.execute()
|
||||
diff = list_run_diffs(self.db, self.run.id, metric_id="sheet", kind="unchanged")["items"][0]
|
||||
self.assertEqual(diff["key"]["interface"], "gei-2.100")
|
||||
self.assertEqual(diff["raw_key_before"], {"id": "1", "interface": "GE-1.100"})
|
||||
with zipfile.ZipFile(io.BytesIO(export_run_zip(self.db, self.run.id))) as archive:
|
||||
rows = list(csv.DictReader(io.StringIO(archive.read("tables/sheet.csv").decode("utf-8-sig"))))
|
||||
self.assertEqual(rows[0]["interface__raw_before"], "GE-1.100")
|
||||
|
||||
def test_live_search_does_not_inherit_new_filters_or_transforms(self):
|
||||
with patch("netx_api.biz_state.compare_service._UNCHANGED_SAMPLE_MAX", 0):
|
||||
self.execute()
|
||||
|
|
@ -132,7 +168,7 @@ class CompareSnapshotTests(unittest.TestCase):
|
|||
self.edit_current_settings()
|
||||
detail = get_run(self.db, self.run.id)
|
||||
self.assertEqual(detail["job_name"], "Original job")
|
||||
self.assertEqual(detail["config_snapshot_version"], 1)
|
||||
self.assertEqual(detail["config_snapshot_version"], 2)
|
||||
self.assertEqual(detail["template"]["iface_normalize_rules"], [{"from": "GE", "to": "gei"}])
|
||||
self.assertNotIn("config_snapshot", detail["summary"])
|
||||
detail["template"]["iface_normalize_rules"].clear()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue