diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
commit | e2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch) | |
tree | f0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /tests/topotests/config_timing | |
parent | Initial commit. (diff) | |
download | frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip |
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/topotests/config_timing')
-rw-r--r-- | tests/topotests/config_timing/r1/staticd.conf | 1 | ||||
-rw-r--r-- | tests/topotests/config_timing/r1/zebra.conf | 16 | ||||
-rw-r--r-- | tests/topotests/config_timing/test_config_timing.py | 262 |
3 files changed, 279 insertions, 0 deletions
diff --git a/tests/topotests/config_timing/r1/staticd.conf b/tests/topotests/config_timing/r1/staticd.conf new file mode 100644 index 0000000..0f9f97c --- /dev/null +++ b/tests/topotests/config_timing/r1/staticd.conf @@ -0,0 +1 @@ +log timestamp precision 3 diff --git a/tests/topotests/config_timing/r1/zebra.conf b/tests/topotests/config_timing/r1/zebra.conf new file mode 100644 index 0000000..b4dc338 --- /dev/null +++ b/tests/topotests/config_timing/r1/zebra.conf @@ -0,0 +1,16 @@ +log timestamp precision 3 + +ip prefix-list ANY permit 0.0.0.0/0 le 32 +ipv6 prefix-list ANY seq 10 permit any + +route-map RM-NONE4 deny 10 + +route-map RM-NONE6 deny 10 + +interface r1-eth0 + ip address 100.0.0.1/24 + ipv6 address 2102::1/64 +exit + +ip protocol static route-map RM-NONE4 +ipv6 protocol static route-map RM-NONE6 diff --git a/tests/topotests/config_timing/test_config_timing.py b/tests/topotests/config_timing/test_config_timing.py new file mode 100644 index 0000000..5c1b972 --- /dev/null +++ b/tests/topotests/config_timing/test_config_timing.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# June 2 2021, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2019-2020 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# + +""" +Test the timing of config operations. + +The initial add of 10k routes is used as a baseline for timing and all future +operations are expected to complete in under 2 times that baseline. This is a +lot of slop; however, the pre-batching code some of these operations (e.g., +adding the same set of 10k routes) would take 100 times longer, so the intention +is to catch those types of regressions. +""" + +import datetime +import ipaddress +import math +import os +import sys +import pytest +from lib import topotest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.staticd] + + +def build_topo(tgen): + tgen.add_router("r1") + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + 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_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def get_ip_networks(super_prefix, base_count, count): + count_log2 = math.log(base_count, 2) + if count_log2 != int(count_log2): + count_log2 = int(count_log2) + 1 + else: + count_log2 = int(count_log2) + network = ipaddress.ip_network(super_prefix) + return tuple(network.subnets(count_log2))[0:count] + + +def test_static_timing(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def do_config( + base_count, + count, + bad_indices, + base_delta, + d_multiplier, + add=True, + do_ipv6=False, + super_prefix=None, + en_dbg=False, + ): + router_list = tgen.routers() + tot_delta = float(0) + + optype = "adding" if add else "removing" + iptype = "IPv6" if do_ipv6 else "IPv4" + if super_prefix is None: + super_prefix = u"2001::/48" if do_ipv6 else u"10.0.0.0/8" + via = u"lo" + optyped = "added" if add else "removed" + + for rname, router in router_list.items(): + router.logger.info("{} {} static {} routes".format(optype, count, iptype)) + + # Generate config file. + config_file = os.path.join( + router.logdir, rname, "{}-routes-{}.conf".format(iptype.lower(), optype) + ) + with open(config_file, "w") as f: + for i, net in enumerate( + get_ip_networks(super_prefix, base_count, count) + ): + if i in bad_indices: + if add: + f.write("ip route {} {} bad_input\n".format(net, via)) + else: + f.write("no ip route {} {} bad_input\n".format(net, via)) + elif add: + f.write("ip route {} {}\n".format(net, via)) + else: + f.write("no ip route {} {}\n".format(net, via)) + + # Enable debug + if en_dbg: + router.vtysh_cmd("debug northbound callbacks configuration") + + # Load config file. + load_command = 'vtysh -f "{}"'.format(config_file) + tstamp = datetime.datetime.now() + output = router.run(load_command) + delta = (datetime.datetime.now() - tstamp).total_seconds() + tot_delta += delta + + router.logger.debug( + "\nvtysh command => {}\nvtysh output <= {}\nin {}s".format( + load_command, output, delta + ) + ) + + limit_delta = base_delta * d_multiplier + logger.info( + "{} {} {} static routes under {} in {}s (limit: {}s)".format( + optyped, count, iptype.lower(), super_prefix, tot_delta, limit_delta + ) + ) + if limit_delta: + assert tot_delta <= limit_delta + + return tot_delta + + # Number of static routes + router = tgen.gears["r1"] + output = router.net.cmd_legacy("vtysh -h | grep address-sanitizer", warn=False) + if output == "": + logger.info("No Address Sanitizer, generating 10000 routes") + prefix_count = 10000 + else: + logger.info("Address Sanitizer build, only testing 50 routes") + prefix_count = 50 + + prefix_base = [ + [u"10.0.0.0/8", u"11.0.0.0/8"], + [u"2100:1111:2220::/44", u"2100:3333:4440::/44"], + ] + + # This apparently needed to allow for various mgmtd/staticd/zebra connections to form + # which then SLOWS execution down. If we don't include this value then the + # initial, baseline establishing, time is 2 time faster (e.g., 5s instead of 10s), + # but all later runs are slower and fail. + # + # This should be done differently based on actual facts. + topotest.sleep(5) + + bad_indices = [] + for ipv6 in [False, True]: + base_delta = do_config( + prefix_count, + prefix_count, + bad_indices, + 0, + 0, + True, + ipv6, + prefix_base[ipv6][0], + ) + + # Another set of same number of prefixes + do_config( + prefix_count, + prefix_count, + bad_indices, + base_delta, + 3, + True, + ipv6, + prefix_base[ipv6][1], + ) + + # Duplicate config + do_config( + prefix_count, + prefix_count, + bad_indices, + base_delta, + 3, + True, + ipv6, + prefix_base[ipv6][0], + ) + + # Remove 1/2 of duplicate + do_config( + prefix_count, + prefix_count // 2, + bad_indices, + base_delta, + 3, + False, + ipv6, + prefix_base[ipv6][0], + ) + + # Add all back in so 1/2 replicate 1/2 new + do_config( + prefix_count, + prefix_count, + bad_indices, + base_delta, + 3, + True, + ipv6, + prefix_base[ipv6][0], + ) + + # remove all + delta = do_config( + prefix_count, + prefix_count, + bad_indices, + base_delta, + 3, + False, + ipv6, + prefix_base[ipv6][0], + ) + delta += do_config( + prefix_count, + prefix_count, + bad_indices, + base_delta, + 3, + False, + ipv6, + prefix_base[ipv6][1], + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) |