|
| 1 | +# SPDX-License-Identifier: ISC |
| 2 | +# -*- coding: utf-8 eval: (blacken-mode 1) -*- |
| 3 | +# |
| 4 | +# December 31 2025, Christian Hopps <chopps@labn.net> |
| 5 | +# |
| 6 | +# Copyright (c) 2025, LabN Consulting, L.L.C. |
| 7 | +# |
| 8 | +""" |
| 9 | +Test static route functionality |
| 10 | +""" |
| 11 | +import datetime |
| 12 | +import logging |
| 13 | +import os |
| 14 | +import re |
| 15 | +import time |
| 16 | + |
| 17 | +import pytest |
| 18 | +from lib.common_config import step |
| 19 | +from lib.topogen import Topogen |
| 20 | +from munet.base import Timeout |
| 21 | +from munet.watchlog import WatchLog |
| 22 | + |
| 23 | +pytestmark = [pytest.mark.staticd] |
| 24 | + |
| 25 | + |
| 26 | +@pytest.fixture(scope="function") |
| 27 | +def tgen(request): |
| 28 | + "Setup/Teardown the environment and provide tgen argument to tests" |
| 29 | + |
| 30 | + topodef = { |
| 31 | + "s1": ("r1",), |
| 32 | + } |
| 33 | + |
| 34 | + tgen = Topogen(topodef, request.module.__name__) |
| 35 | + tgen.start_topology() |
| 36 | + |
| 37 | + for router in tgen.routers().values(): |
| 38 | + router.load_frr_config("frr.conf") |
| 39 | + |
| 40 | + tgen.start_router() |
| 41 | + yield tgen |
| 42 | + tgen.stop_topology() |
| 43 | + |
| 44 | + |
| 45 | +def scan_for_match(wl, regex, timeout=30): |
| 46 | + regex = re.compile(regex) |
| 47 | + to = Timeout(timeout) |
| 48 | + logging.debug("scanning %s for %s", wl.path, regex) |
| 49 | + while to: |
| 50 | + content = wl.snapshot_refresh() |
| 51 | + if m := regex.search(content): |
| 52 | + logging.debug("found '%s' in %s", m.group(0), wl.path) |
| 53 | + return m |
| 54 | + time.sleep(0.5) |
| 55 | + raise TimeoutError(f"timeout waiting for {regex} in {wl.path}") |
| 56 | + |
| 57 | +def test_quit_during_config(tgen): |
| 58 | + if tgen.routers_have_failure(): |
| 59 | + pytest.skip(tgen.errors) |
| 60 | + |
| 61 | + r1g = tgen.gears["r1"] |
| 62 | + r1 = r1g.net |
| 63 | + wl = WatchLog(r1.rundir / "mgmtd.log") |
| 64 | + |
| 65 | + # Get a config file with `count` static IPv4 routes |
| 66 | + count = 10 * 1024 |
| 67 | + config_file = os.path.join(r1.logdir, "bigconfig.conf") |
| 68 | + with open(config_file, "w") as cfile: |
| 69 | + for i in range((1 << 24), (1 << 24) + count * 4, 4): |
| 70 | + dq0 = (i >> 24) & 0xff |
| 71 | + dq1 = (i >> 16) & 0xff |
| 72 | + dq2 = (i >> 8) & 0xff |
| 73 | + dq3 = i & 0xff |
| 74 | + cfile.write(f"ip route {dq0}.{dq1}.{dq2}.{dq3}/30 101.0.0.2\n") |
| 75 | + |
| 76 | + step(f"add {count} static routes", reset=True) |
| 77 | + load_command = 'vtysh -f "{}"'.format(config_file) |
| 78 | + |
| 79 | + wl.snapshot() |
| 80 | + config_proc = r1.popen(load_command) |
| 81 | + try: |
| 82 | + |
| 83 | + # Wait for part of the configuration to start being applied |
| 84 | + scan_for_match(wl, re.escape(r"ip route 1.0.1.0/30 101.0.0.2")) |
| 85 | + logging.info("partial config applied, waiting for completion") |
| 86 | + |
| 87 | + # Now stop the router to see if we get any core files |
| 88 | + r1.stopRouter(False) |
| 89 | + finally: |
| 90 | + if config_proc: |
| 91 | + config_proc.kill() |
0 commit comments