From 025477457866cc6faa4dec86652b8621fc912eb4 Mon Sep 17 00:00:00 2001 From: Christian Hopps Date: Sun, 5 Sep 2021 18:59:26 -0400 Subject: [PATCH] tests: refactor parallel reset/load config for non-json Refactor the bgp_auth test to create common_config code to allow non-json based tests to reset routers and load configs in parallel. Signed-off-by: Christian Hopps --- tests/topotests/bgp_auth/test_bgp_auth.py | 264 +++++----------------- tests/topotests/conftest.py | 7 + tests/topotests/lib/common_config.py | 107 ++++++++- tests/topotests/lib/topogen.py | 7 +- tests/topotests/lib/topojson.py | 16 +- tests/topotests/lib/topotest.py | 19 ++ 6 files changed, 187 insertions(+), 233 deletions(-) diff --git a/tests/topotests/bgp_auth/test_bgp_auth.py b/tests/topotests/bgp_auth/test_bgp_auth.py index f32bc49f58..f01c7f206a 100644 --- a/tests/topotests/bgp_auth/test_bgp_auth.py +++ b/tests/topotests/bgp_auth/test_bgp_auth.py @@ -40,85 +40,43 @@ test_bgp_auth.py: Test BGP Md5 Authentication setup is 3 routers with 3 links between each each link in a different vrf Default, blue and red respectively Tests check various fiddling with passwords and checking that the peer -establishment is as expected and passwords are not leaked across sockets +establishment is as expected and passwords are not leaked across sockets for bgp instances """ +# pylint: disable=C0413 -import os -import sys import json +import os import platform -import pytest +import sys from time import sleep -# Save the Current Working Directory to find configuration files. -CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, "../")) - -# pylint: disable=C0413 -# Import topogen and topotest helpers -from lib import common_config -from lib import topotest -from lib.topogen import Topogen, TopoRouter, get_topogen +import pytest +from lib import common_config, topotest from lib.common_config import ( - FRRCFG_FILE, - FRRCFG_BKUP_FILE, - load_config_to_routers, - reset_config_on_routers, + save_initial_config_on_routers, + reset_with_new_configs, ) - -ERROR_LIST = ["Malformed", "Failure", "Unknown", "Incomplete"] +from lib.topogen import Topogen, TopoRouter, get_topogen pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd] - -def reload_new_configs(tgen, *cflist): - reset_config_on_routers(tgen) - - routers = tgen.routers() - for rname, router in routers.items(): - destname = "{}/{}/{}".format(tgen.logdir, rname, FRRCFG_FILE) - wmode="w" - for cfbase in cflist: - confname = os.path.join(CWD, "{}/{}".format(rname, cfbase)) - with open(confname, "r") as cf: - with open(destname, wmode) as df: - df.write(cf.read()) - wmode="a" - - # import pdb - # pdb.set_trace() - common_config.ROUTER_LIST = routers - load_config_to_routers(tgen, routers, save_bkup=False) - - -class InvalidCLIError(Exception): - """Raise when the CLI command is wrong""" +CWD = os.path.dirname(os.path.realpath(__file__)) def build_topo(tgen): - # Create routers tgen.add_router("R1") tgen.add_router("R2") tgen.add_router("R3") - # R1-R2 1 tgen.add_link(tgen.gears["R1"], tgen.gears["R2"]) - # R1-R3 1 tgen.add_link(tgen.gears["R1"], tgen.gears["R3"]) - # R2-R3 1 tgen.add_link(tgen.gears["R2"], tgen.gears["R3"]) - # R1-R2 2 tgen.add_link(tgen.gears["R1"], tgen.gears["R2"]) - # R1-R3 2 tgen.add_link(tgen.gears["R1"], tgen.gears["R3"]) - # R2-R3 2 tgen.add_link(tgen.gears["R2"], tgen.gears["R3"]) - # R1-R2 3 tgen.add_link(tgen.gears["R1"], tgen.gears["R2"]) - # R1-R3 2 tgen.add_link(tgen.gears["R1"], tgen.gears["R3"]) - # R2-R3 2 tgen.add_link(tgen.gears["R2"], tgen.gears["R3"]) @@ -202,26 +160,15 @@ def setup_module(mod): # For all registred routers, load the zebra configuration file for rname, router in router_list.items(): - router.load_config( - TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) - ) - router.load_config( - TopoRouter.RD_OSPF, os.path.join(CWD, "{}/empty.conf".format(rname)) - ) - router.load_config( - TopoRouter.RD_BGP, os.path.join(CWD, "{}/empty.conf".format(rname)) - ) + router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + router.load_config(TopoRouter.RD_OSPF) + router.load_config(TopoRouter.RD_BGP) - # After loading the configurations, this function loads configured daemons. + # After copying the configurations, this function loads configured daemons. tgen.start_router() - # Set the initial (saved) config to the zebra config. reset_config_on_routers - # will use this config to reset to. - routers = tgen.routers() - for rname, router in routers.items(): - confname = os.path.join(CWD, "{}/zebra.conf".format(rname)) - destname = "{}/{}/{}".format(tgen.logdir, rname, FRRCFG_BKUP_FILE) - router.cmd_raises("cp {} {}".format(confname, destname)) + # Save the initial router config. reset_config_on_routers will return to this config. + save_initial_config_on_routers(tgen) def teardown_module(mod): @@ -272,94 +219,30 @@ def print_diag(vrf): print(router.vtysh_cmd("show bgp {} neighbor".format(vrf_str(vrf)))) -def configure(conf_file): - "configure from a file" - - tgen = get_topogen() - router_list = tgen.routers() - for rname, router in router_list.items(): - with open( - os.path.join(CWD, "{}/{}").format(router.name, conf_file), "r+" - ) as cfg: - new_config = cfg.read() - - output = router.vtysh_multicmd(new_config, pretty_output=False) - for out_err in ERROR_LIST: - if out_err.lower() in output.lower(): - raise InvalidCLIError("%s" % output) - - -def clear_bgp(): - "clear bgp configuration for a vrf" - - tgen = get_topogen() - r1 = tgen.gears["R1"] - r2 = tgen.gears["R2"] - r3 = tgen.gears["R3"] - - r1.vtysh_cmd("conf t\nno router bgp 65001") - r2.vtysh_cmd("conf t\nno router bgp 65002") - r3.vtysh_cmd("conf t\nno router bgp 65003") - r1.vtysh_cmd("conf t\nno router bgp 65001 vrf blue") - r2.vtysh_cmd("conf t\nno router bgp 65002 vrf blue") - r3.vtysh_cmd("conf t\nno router bgp 65003 vrf blue") - r1.vtysh_cmd("conf t\nno router bgp 65001 vrf red") - r2.vtysh_cmd("conf t\nno router bgp 65002 vrf red") - r3.vtysh_cmd("conf t\nno router bgp 65003 vrf red") - - -def configure_bgp(conf_file): - "configure bgp from file" - - clear_bgp() - configure(conf_file) - - -def clear_ospf(): - "clear ospf configuration for a vrf" - - tgen = get_topogen() - router_list = tgen.routers() - for rname, router in router_list.items(): - router.vtysh_cmd("conf t\nno router ospf") - router.vtysh_cmd("conf t\nno router ospf vrf blue") - router.vtysh_cmd("conf t\nno router ospf vrf red") - +@common_config.retry(retry_timeout=190) +def _check_neigh_state(router, peer, state, vrf=""): + "check BGP neighbor state on a router" -def configure_ospf(conf_file): - "configure bgp from file" + neigh_output = router.vtysh_cmd( + "show bgp {} neighbors {} json".format(vrf_str(vrf), peer) + ) - clear_ospf() - configure(conf_file) + peer_state = "Unknown" + neigh_output_json = json.loads(neigh_output) + if peer in neigh_output_json: + peer_state = neigh_output_json[peer]["bgpState"] + if peer_state == state: + return True + return "{} peer with {} expected state {} got {} ".format( + router.name, peer, state, peer_state + ) def check_neigh_state(router, peer, state, vrf=""): "check BGP neighbor state on a router" - count = 0 - matched = False - neigh_output = "" - while count < 125: - if vrf == "": - neigh_output = router.vtysh_cmd("show bgp neighbors {} json".format(peer)) - else: - neigh_output = router.vtysh_cmd( - "show bgp vrf {} neighbors {} json".format(vrf, peer) - ) - neigh_output_json = json.loads(neigh_output) - if peer in neigh_output_json.keys(): - if neigh_output_json[peer]["bgpState"] == state: - matched = True - break - count += 1 - sleep(1) - - assertmsg = "{} could not peer {} state expected {} got {} ".format( - router.name, peer, state, neigh_output_json[peer]["bgpState"] - ) - if matched != True: - print_diag(vrf) - assert matched == True, assertmsg + assertmsg = _check_neigh_state(router, peer, state, vrf) + assert assertmsg is True, assertmsg def check_all_peers_established(vrf=""): @@ -517,28 +400,21 @@ def check_vrf_peer_change_passwords(vrf="", prefix="no"): def test_default_peer_established(tgen): "default vrf 3 peers same password" - # configure_bgp("bgpd.conf") - # configure_ospf("ospfd.conf") - reload_new_configs(tgen, "bgpd.conf", "ospfd.conf") + reset_with_new_configs(tgen, "bgpd.conf", "ospfd.conf") check_all_peers_established() - # tgen.mininet_cli() def test_default_peer_remove_passwords(tgen): "selectively remove passwords checking state" - # configure_bgp("bgpd.conf") - # configure_ospf("ospfd.conf") - reload_new_configs(tgen, "bgpd.conf", "ospfd.conf") + reset_with_new_configs(tgen, "bgpd.conf", "ospfd.conf") check_vrf_peer_remove_passwords() def test_default_peer_change_passwords(tgen): "selectively change passwords checking state" - # configure_bgp("bgpd.conf") - # configure_ospf("ospfd.conf") - reload_new_configs(tgen, "bgpd.conf", "ospfd.conf") + reset_with_new_configs(tgen, "bgpd.conf", "ospfd.conf") check_vrf_peer_change_passwords() @@ -549,11 +425,8 @@ def test_default_prefix_peer_established(tgen): if topotest.version_cmp(platform.release(), "5.3") < 0: return - # configure_bgp("bgpd_prefix.conf") - # configure_ospf("ospfd.conf") - reload_new_configs(tgen, "bgpd_prefix.conf", "ospfd.conf") + reset_with_new_configs(tgen, "bgpd_prefix.conf", "ospfd.conf") check_all_peers_established() - # tgen.mininet_cli() def test_prefix_peer_remove_passwords(tgen): @@ -563,9 +436,7 @@ def test_prefix_peer_remove_passwords(tgen): if topotest.version_cmp(platform.release(), "5.3") < 0: return - # configure_bgp("bgpd_prefix.conf") - # configure_ospf("ospfd.conf") - reload_new_configs(tgen, "bgpd_prefix.conf", "ospfd.conf") + reset_with_new_configs(tgen, "bgpd_prefix.conf", "ospfd.conf") check_vrf_peer_remove_passwords(prefix="yes") @@ -576,9 +447,7 @@ def test_prefix_peer_change_passwords(tgen): if topotest.version_cmp(platform.release(), "5.3") < 0: return - # configure_bgp("bgpd_prefix.conf") - # configure_ospf("ospfd.conf") - reload_new_configs(tgen, "bgpd_prefix.conf", "ospfd.conf") + reset_with_new_configs(tgen, "bgpd_prefix.conf", "ospfd.conf") check_vrf_peer_change_passwords(prefix="yes") @@ -586,28 +455,21 @@ def test_vrf_peer_established(tgen): "default vrf 3 peers same password with VRF config" # clean routers and load vrf config - # configure_bgp("bgpd_vrf.conf") - # configure_ospf("ospfd_vrf.conf") - reload_new_configs(tgen, "bgpd_vrf.conf", "ospfd_vrf.conf") + reset_with_new_configs(tgen, "bgpd_vrf.conf", "ospfd_vrf.conf") check_all_peers_established("blue") - # tgen.mininet_cli() def test_vrf_peer_remove_passwords(tgen): "selectively remove passwords checking state with VRF config" - # configure_bgp("bgpd_vrf.conf") - # configure_ospf("ospfd_vrf.conf") - reload_new_configs(tgen, "bgpd_vrf.conf", "ospfd_vrf.conf") + reset_with_new_configs(tgen, "bgpd_vrf.conf", "ospfd_vrf.conf") check_vrf_peer_remove_passwords(vrf="blue") def test_vrf_peer_change_passwords(tgen): "selectively change passwords checking state with VRF config" - # configure_bgp("bgpd_vrf.conf") - # configure_ospf("ospfd_vrf.conf") - reload_new_configs(tgen, "bgpd_vrf.conf", "ospfd_vrf.conf") + reset_with_new_configs(tgen, "bgpd_vrf.conf", "ospfd_vrf.conf") check_vrf_peer_change_passwords(vrf="blue") @@ -618,9 +480,7 @@ def test_vrf_prefix_peer_established(tgen): if topotest.version_cmp(platform.release(), "5.3") < 0: return - # configure_bgp("bgpd_vrf_prefix.conf") - # configure_ospf("ospfd_vrf.conf") - reload_new_configs(tgen, "bgpd_vrf_prefix.conf", "ospfd_vrf.conf") + reset_with_new_configs(tgen, "bgpd_vrf_prefix.conf", "ospfd_vrf.conf") check_all_peers_established("blue") @@ -631,9 +491,7 @@ def test_vrf_prefix_peer_remove_passwords(tgen): if topotest.version_cmp(platform.release(), "5.3") < 0: return - # configure_bgp("bgpd_vrf_prefix.conf") - # configure_ospf("ospfd_vrf.conf") - reload_new_configs(tgen, "bgpd_vrf_prefix.conf", "ospfd_vrf.conf") + reset_with_new_configs(tgen, "bgpd_vrf_prefix.conf", "ospfd_vrf.conf") check_vrf_peer_remove_passwords(vrf="blue", prefix="yes") @@ -644,47 +502,36 @@ def test_vrf_prefix_peer_change_passwords(tgen): if topotest.version_cmp(platform.release(), "5.3") < 0: return - # configure_bgp("bgpd_vrf_prefix.conf") - # configure_ospf("ospfd_vrf.conf") - reload_new_configs(tgen, "bgpd_vrf_prefix.conf", "ospfd_vrf.conf") + reset_with_new_configs(tgen, "bgpd_vrf_prefix.conf", "ospfd_vrf.conf") check_vrf_peer_change_passwords(vrf="blue", prefix="yes") def test_multiple_vrf_peer_established(tgen): "default vrf 3 peers same password with multiple VRFs" - # configure_bgp("bgpd_multi_vrf.conf") - # configure_ospf("ospfd_multi_vrf.conf") - reload_new_configs(tgen, "bgpd_multi_vrf.conf", "ospfd_multi_vrf.conf") + reset_with_new_configs(tgen, "bgpd_multi_vrf.conf", "ospfd_multi_vrf.conf") check_all_peers_established("blue") check_all_peers_established("red") - # tgen.mininet_cli() def test_multiple_vrf_peer_remove_passwords(tgen): "selectively remove passwords checking state with multiple VRFs" - # configure_bgp("bgpd_multi_vrf.conf") - # configure_ospf("ospfd_multi_vrf.conf") - reload_new_configs(tgen, "bgpd_multi_vrf.conf", "ospfd_multi_vrf.conf") + reset_with_new_configs(tgen, "bgpd_multi_vrf.conf", "ospfd_multi_vrf.conf") check_vrf_peer_remove_passwords("blue") check_all_peers_established("red") check_vrf_peer_remove_passwords("red") check_all_peers_established("blue") - # tgen.mininet_cli() def test_multiple_vrf_peer_change_passwords(tgen): "selectively change passwords checking state with multiple VRFs" - # configure_bgp("bgpd_multi_vrf.conf") - # configure_ospf("ospfd_multi_vrf.conf") - reload_new_configs(tgen, "bgpd_multi_vrf.conf", "ospfd_multi_vrf.conf") + reset_with_new_configs(tgen, "bgpd_multi_vrf.conf", "ospfd_multi_vrf.conf") check_vrf_peer_change_passwords("blue") check_all_peers_established("red") check_vrf_peer_change_passwords("red") check_all_peers_established("blue") - # tgen.mininet_cli() def test_multiple_vrf_prefix_peer_established(tgen): @@ -694,12 +541,9 @@ def test_multiple_vrf_prefix_peer_established(tgen): if topotest.version_cmp(platform.release(), "5.3") < 0: return - # configure_bgp("bgpd_multi_vrf_prefix.conf") - # configure_ospf("ospfd_multi_vrf.conf") - reload_new_configs(tgen, "bgpd_multi_vrf_prefix.conf", "ospfd_multi_vrf.conf") + reset_with_new_configs(tgen, "bgpd_multi_vrf_prefix.conf", "ospfd_multi_vrf.conf") check_all_peers_established("blue") check_all_peers_established("red") - # tgen.mininet_cli() def test_multiple_vrf_prefix_peer_remove_passwords(tgen): @@ -709,14 +553,11 @@ def test_multiple_vrf_prefix_peer_remove_passwords(tgen): if topotest.version_cmp(platform.release(), "5.3") < 0: return - # configure_bgp("bgpd_multi_vrf_prefix.conf") - # configure_ospf("ospfd_multi_vrf.conf") - reload_new_configs(tgen, "bgpd_multi_vrf_prefix.conf", "ospfd_multi_vrf.conf") + reset_with_new_configs(tgen, "bgpd_multi_vrf_prefix.conf", "ospfd_multi_vrf.conf") check_vrf_peer_remove_passwords(vrf="blue", prefix="yes") check_all_peers_established("red") check_vrf_peer_remove_passwords(vrf="red", prefix="yes") check_all_peers_established("blue") - # tgen.mininet_cli() def test_multiple_vrf_prefix_peer_change_passwords(tgen): @@ -726,14 +567,11 @@ def test_multiple_vrf_prefix_peer_change_passwords(tgen): if topotest.version_cmp(platform.release(), "5.3") < 0: return - # configure_bgp("bgpd_multi_vrf_prefix.conf") - # configure_ospf("ospfd_multi_vrf.conf") - reload_new_configs(tgen, "bgpd_multi_vrf_prefix.conf", "ospfd_multi_vrf.conf") + reset_with_new_configs(tgen, "bgpd_multi_vrf_prefix.conf", "ospfd_multi_vrf.conf") check_vrf_peer_change_passwords(vrf="blue", prefix="yes") check_all_peers_established("red") check_vrf_peer_change_passwords(vrf="red", prefix="yes") check_all_peers_established("blue") - # tgen.mininet_cli() def test_memory_leak(tgen): diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py index 2dfeff70f5..ed55490c09 100755 --- a/tests/topotests/conftest.py +++ b/tests/topotests/conftest.py @@ -255,6 +255,7 @@ def pytest_configure(config): cli_level = config.getini("log_cli_level") if cli_level is not None: config.option.log_cli_level = cli_level + # --------------------------------------- # Record our options in global dictionary # --------------------------------------- @@ -329,6 +330,12 @@ def setup_session_auto(): logger.debug("After the run (is_worker: %s)", is_worker) +def pytest_runtest_setup(item): + module = item.parent.module + script_dir = os.path.abspath(os.path.dirname(module.__file__)) + os.environ["PYTEST_TOPOTEST_SCRIPTDIR"] = script_dir + + def pytest_runtest_makereport(item, call): "Log all assert messages to default logger with error level" diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index cbc73915a7..bfb6cd1ef2 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -49,7 +49,6 @@ FRRCFG_FILE = "frr_json.conf" FRRCFG_BKUP_FILE = "frr_json_initial.conf" ERROR_LIST = ["Malformed", "Failure", "Unknown", "Incomplete"] -ROUTER_LIST = [] #### CD = os.path.dirname(os.path.realpath(__file__)) @@ -471,6 +470,40 @@ def check_router_status(tgen): return True +def save_initial_config_on_routers(tgen): + """Save current configuration on routers to FRRCFG_BKUP_FILE. + + FRRCFG_BKUP_FILE is the file that will be restored when `reset_config_on_routers()` + is called. + + Parameters + ---------- + * `tgen` : Topogen object + """ + router_list = tgen.routers() + target_cfg_fmt = tgen.logdir + "/{}/frr_json_initial.conf" + + # Get all running configs in parallel + procs = {} + for rname in router_list: + logger.info("Fetching running config for router %s", rname) + procs[rname] = router_list[rname].popen( + ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"], + stdin=None, + stdout=open(target_cfg_fmt.format(rname), "w"), + stderr=subprocess.PIPE, + ) + for rname, p in procs.items(): + _, error = p.communicate() + if p.returncode: + logger.error( + "Get running config for %s failed %d: %s", rname, p.returncode, error + ) + raise InvalidCLIError( + "vtysh show running error on {}: {}".format(rname, error) + ) + + def reset_config_on_routers(tgen, routerName=None): """ Resets configuration on routers to the snapshot created using input JSON @@ -490,8 +523,12 @@ def reset_config_on_routers(tgen, routerName=None): # Trim the router list if needed router_list = tgen.routers() if routerName: - if (routerName not in ROUTER_LIST) or (routerName not in router_list): - logger.debug("Exiting API: reset_config_on_routers: no routers") + if routerName not in router_list: + logger.warning( + "Exiting API: reset_config_on_routers: no router %s", + routerName, + exc_info=True, + ) return True router_list = {routerName: router_list[routerName]} @@ -622,6 +659,44 @@ def reset_config_on_routers(tgen, routerName=None): return True +def prep_load_config_to_routers(tgen, *config_name_list): + """Create common config for `load_config_to_routers`. + + The common config file is constructed from the list of sub-config files passed as + position arguments to this function. Each entry in `config_name_list` is looked for + under the router sub-directory in the test directory and those files are + concatenated together to create the common config. e.g., + + # Routers are "r1" and "r2", test file is `example/test_example_foo.py` + prepare_load_config_to_routers(tgen, "bgpd.conf", "ospfd.conf") + + When the above call is made the files in + + example/r1/bgpd.conf + example/r1/ospfd.conf + + Are concat'd together into a single config file that will be loaded on r1, and + + example/r2/bgpd.conf + example/r2/ospfd.conf + + Are concat'd together into a single config file that will be loaded on r2 when + the call to `load_config_to_routers` is made. + """ + + routers = tgen.routers() + for rname, router in routers.items(): + destname = "{}/{}/{}".format(tgen.logdir, rname, FRRCFG_FILE) + wmode = "w" + for cfbase in config_name_list: + script_dir = os.environ["PYTEST_TOPOTEST_SCRIPTDIR"] + confname = os.path.join(script_dir, "{}/{}".format(rname, cfbase)) + with open(confname, "r") as cf: + with open(destname, wmode) as df: + df.write(cf.read()) + wmode = "a" + + def load_config_to_routers(tgen, routers, save_bkup=False): """ Loads configuration on routers from the file FRRCFG_FILE. @@ -644,7 +719,7 @@ def load_config_to_routers(tgen, routers, save_bkup=False): base_router_list = tgen.routers() router_list = {} for router in routers: - if (router not in ROUTER_LIST) or (router not in base_router_list): + if router not in base_router_list: continue router_list[router] = base_router_list[router] @@ -759,6 +834,21 @@ def load_config_to_router(tgen, routerName, save_bkup=False): return load_config_to_routers(tgen, [routerName], save_bkup) +def reset_with_new_configs(tgen, *cflist): + """Reset the router to initial config, then load new configs. + + Resets routers to the initial config state (see `save_initial_config_on_routers() + and `reset_config_on_routers()` `), then concat list of router sub-configs together + and load onto the routers (see `prep_load_config_to_routers()` and + `load_config_to_routers()`) + """ + routers = tgen.routers() + + reset_config_on_routers(tgen) + prep_load_config_to_routers(tgen, *cflist) + load_config_to_routers(tgen, tgen.routers(), save_bkup=False) + + def get_frr_ipv6_linklocal(tgen, router, intf=None, vrf=None): """ API to get the link local ipv6 address of a particular interface using @@ -882,20 +972,19 @@ def start_topology(tgen, daemon=None): * `tgen` : topogen object """ - global ROUTER_LIST # Starting topology tgen.start_topology() # Starting daemons router_list = tgen.routers() - ROUTER_LIST = sorted( + routers_sorted = sorted( router_list.keys(), key=lambda x: int(re_search("[0-9]+", x).group(0)) ) linux_ver = "" router_list = tgen.routers() - for rname in ROUTER_LIST: + for rname in routers_sorted: router = router_list[rname] # It will help in debugging the failures, will give more details on which @@ -1034,11 +1123,11 @@ def topo_daemons(tgen, topo=None): topo = tgen.json_topo router_list = tgen.routers() - ROUTER_LIST = sorted( + routers_sorted = sorted( router_list.keys(), key=lambda x: int(re_search("[0-9]+", x).group(0)) ) - for rtr in ROUTER_LIST: + for rtr in routers_sorted: if "ospf" in topo["routers"][rtr] and "ospfd" not in daemon_list: daemon_list.append("ospfd") diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index fa943b299b..325f65dd6f 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -786,13 +786,16 @@ class TopoRouter(TopoGear): return self.net.checkCapability(daemonstr, param) def load_config(self, daemon, source=None, param=None): - """ - Loads daemon configuration from the specified source + """Loads daemon configuration from the specified source Possible daemon values are: TopoRouter.RD_ZEBRA, TopoRouter.RD_RIP, TopoRouter.RD_RIPNG, TopoRouter.RD_OSPF, TopoRouter.RD_OSPF6, TopoRouter.RD_ISIS, TopoRouter.RD_BGP, TopoRouter.RD_LDP, TopoRouter.RD_PIM, TopoRouter.RD_PBR, TopoRouter.RD_SNMP. + Possible `source` values are `None` for an empty config file, a path name which is + used directly, or a file name with no path components which is first looked for + directly and then looked for under a sub-directory named after router. + This API unfortunately allows for source to not exist for any and all routers. """ diff --git a/tests/topotests/lib/topojson.py b/tests/topotests/lib/topojson.py index beb298eb3a..4f23e1ace0 100644 --- a/tests/topotests/lib/topojson.py +++ b/tests/topotests/lib/topojson.py @@ -44,8 +44,6 @@ from lib.ospf import create_router_ospf, create_router_ospf6 from lib.pim import create_igmp_config, create_pim_config from lib.topolog import logger -ROUTER_LIST = [] - def build_topo_from_json(tgen, topo=None): """ @@ -58,26 +56,26 @@ def build_topo_from_json(tgen, topo=None): if topo is None: topo = tgen.json_topo - ROUTER_LIST = sorted( + router_list = sorted( topo["routers"].keys(), key=lambda x: int(re_search(r"\d+", x).group(0)) ) - SWITCH_LIST = [] + switch_list = [] if "switches" in topo: - SWITCH_LIST = sorted( + switch_list = sorted( topo["switches"].keys(), key=lambda x: int(re_search(r"\d+", x).group(0)) ) - listRouters = sorted(ROUTER_LIST[:]) - listSwitches = sorted(SWITCH_LIST[:]) + listRouters = sorted(router_list[:]) + listSwitches = sorted(switch_list[:]) listAllRouters = deepcopy(listRouters) dictSwitches = {} - for routerN in ROUTER_LIST: + for routerN in router_list: logger.info("Topo: Add router {}".format(routerN)) tgen.add_router(routerN) - for switchN in SWITCH_LIST: + for switchN in switch_list: logger.info("Topo: Add switch {}".format(switchN)) dictSwitches[switchN] = tgen.add_switch(switchN) diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index 39b01203e9..b6f55664a6 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -1488,7 +1488,25 @@ class Router(Node): return True def loadConf(self, daemon, source=None, param=None): + """Enabled and set config for a daemon. + + Arranges for loading of daemon configuration from the specified source. Possible + `source` values are `None` for an empty config file, a path name which is used + directly, or a file name with no path components which is first looked for + directly and then looked for under a sub-directory named after router. + """ + # Unfortunately this API allowsfor source to not exist for any and all routers. + if source: + head, tail = os.path.split(source) + if not head and not self.path_exists(tail): + script_dir = os.environ["PYTEST_TOPOTEST_SCRIPTDIR"] + router_relative = os.path.join(script_dir, self.name, tail) + if self.path_exists(router_relative): + source = router_relative + self.logger.info( + "using router relative configuration: {}".format(source) + ) # print "Daemons before:", self.daemons if daemon in self.daemons.keys(): @@ -1497,6 +1515,7 @@ class Router(Node): self.daemons_options[daemon] = param conf_file = "/etc/{}/{}.conf".format(self.routertype, daemon) if source is None or not os.path.exists(source): + self.cmd_raises("rm -f " + conf_file) self.cmd_raises("touch " + conf_file) else: self.cmd_raises("cp {} {}".format(source, conf_file)) -- 2.39.5