Commit 4af07cc77d for frr
commit 4af07cc77d3c3f8fbf15616f9d8be073fe00ae16
Author: Andreas Karis <ak.karis@gmail.com>
Date: Thu Sep 17 00:48:54 2026 +0200
tests: add topo test for add/mod/rm SRv6 segments with frr-reload.py
Add a frr-reload topotest that exercises SRv6 segment-routing
configuration transitions via frr-reload.py. The test loads a
populated SRv6 configuration with a locator and prefix, then reloads
to various target states.
Signed-off-by: Andreas Karis <ak.karis@gmail.com>
diff --git a/tests/topotests/frr_reload_segment_routing/__init__.py b/tests/topotests/frr_reload_segment_routing/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/topotests/frr_reload_segment_routing/r1/frr.conf b/tests/topotests/frr_reload_segment_routing/r1/frr.conf
new file mode 100644
index 0000000000..cdf4cb4feb
--- /dev/null
+++ b/tests/topotests/frr_reload_segment_routing/r1/frr.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/frr_reload_segment_routing/test_frr_reload_segment_routing.py b/tests/topotests/frr_reload_segment_routing/test_frr_reload_segment_routing.py
new file mode 100644
index 0000000000..d424384111
--- /dev/null
+++ b/tests/topotests/frr_reload_segment_routing/test_frr_reload_segment_routing.py
@@ -0,0 +1,199 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2026 by
+# Andreas Karis <ak.karis@gmail.com>
+#
+
+"""
+test_frr_reload_segment_routing.py
+
+test frr-reload.py on 'segment-routing' section deletion.
+
+1. Save clean configuration to 'frr-clean.conf'
+2. For each test case:
+ a. Create and load first configuration, 'frr-from.conf'
+ b. Create and load second configuration, 'frr-to.conf'
+ c. Load clean configuration from 'frr-clean.conf'
+"""
+
+import os
+import sys
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, get_topogen
+
+
+def build_topo(tgen):
+ tgen.add_router("r1")
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ for router in tgen.routers().values():
+ router.load_frr_config()
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_frr_reload_segment_routing():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+
+ def sanitize_config(config: str):
+ sanitize_list = (
+ "!",
+ "frr version",
+ "frr defaults",
+ "hostname",
+ "domainname",
+ "log",
+ "service",
+ "end",
+ )
+
+ sanitized_config = []
+ for line in config.splitlines():
+ stripped = line.strip()
+ if len(stripped) == 0:
+ continue
+
+ drop_line = False
+ for sanitize_item in sanitize_list:
+ if stripped.startswith(sanitize_item):
+ drop_line = True
+ break
+ if drop_line:
+ continue
+ sanitized_config.append(stripped)
+
+ return sanitized_config
+
+ frrdir = tgen.config.get(tgen.CONFIG_SECTION, "frrdir")
+ frrreload = frrdir + "/frr-reload.py --reload"
+
+ r1.cmd_raises("vtysh -c 'write terminal no-header' > frr-clean.conf")
+
+ test_cases = [
+ {
+ "from": """
+segment-routing
+ srv6
+ locators
+ locator MAIN
+ prefix fd00:0:33::/48 block-len 32 node-len 16
+ exit
+ exit
+ exit
+exit
+ """,
+ "to": "",
+ "expected_to": "",
+ },
+ {
+ "from": """
+segment-routing
+ srv6
+ locators
+ locator MAIN
+ prefix fd00:0:33::/48 block-len 32 node-len 16
+ exit
+ exit
+ exit
+exit
+ """,
+ "to": """
+segment-routing
+ srv6
+ locators
+ locator MAIN
+ exit
+ exit
+ exit
+exit
+ """,
+ "expected_to": """
+segment-routing
+ srv6
+ locators
+ locator MAIN
+ exit
+ exit
+ exit
+exit
+ """,
+ },
+ {
+ "from": """
+segment-routing
+ srv6
+ locators
+ locator MAIN
+ prefix fd00:0:33::/48 block-len 32 node-len 16
+ exit
+ exit
+ exit
+exit
+ """,
+ "to": """
+segment-routing
+ srv6
+ locators
+ exit
+ exit
+ exit
+exit
+ """,
+ "expected_to": "",
+ },
+ ]
+
+ for tc in test_cases:
+ clean_config = r1.cmd_raises("cat frr-clean.conf | head -n-1")
+
+ r1.cmd_raises(f"echo '{clean_config}{tc['from']}' > frr-from.conf")
+ r1.cmd_raises(f"{frrreload} frr-from.conf")
+ running_config = r1.cmd_raises("vtysh -c 'write terminal no-header'")
+ assert sanitize_config(tc["from"]) == sanitize_config(running_config), (
+ "running configuration does not contain expected"
+ )
+
+ r1.cmd_raises(f"echo '{clean_config}{tc['to']}' > frr-to.conf")
+ r1.cmd_raises(f"{frrreload} frr-to.conf")
+ running_config = r1.cmd_raises("vtysh -c 'write terminal no-header'")
+ assert sanitize_config(tc["expected_to"]) == sanitize_config(running_config), (
+ "running configuration does not equal expected"
+ )
+
+ r1.cmd_raises(f"{frrreload} frr-clean.conf")
+
+
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))