mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-06-05 17:17:36 +00:00
Merge pull request #8782 from LabNConsulting/chopps/timing-test
tests: timing large config operations
This commit is contained in:
commit
cf86d36786
1
tests/topotests/config_timing/r1/staticd.conf
Normal file
1
tests/topotests/config_timing/r1/staticd.conf
Normal file
@ -0,0 +1 @@
|
|||||||
|
log timestamp precision 3
|
18
tests/topotests/config_timing/r1/zebra.conf
Normal file
18
tests/topotests/config_timing/r1/zebra.conf
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
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
|
||||||
|
exit-route-map
|
||||||
|
|
||||||
|
route-map RM-NONE6 deny 10
|
||||||
|
exit-route-map
|
||||||
|
|
||||||
|
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
|
186
tests/topotests/config_timing/test_config_timing.py
Normal file
186
tests/topotests/config_timing/test_config_timing.py
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# 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>
|
||||||
|
#
|
||||||
|
# Permission to use, copy, modify, and/or distribute this software
|
||||||
|
# for any purpose with or without fee is hereby granted, provided
|
||||||
|
# that the above copyright notice and this permission notice appear
|
||||||
|
# in all copies.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
|
||||||
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
|
||||||
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
|
||||||
|
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||||
|
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||||
|
# OF THIS SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
from mininet.topo import Topo
|
||||||
|
|
||||||
|
pytestmark = [pytest.mark.staticd]
|
||||||
|
|
||||||
|
class TimingTopo(Topo):
|
||||||
|
def build(self, *_args, **_opts):
|
||||||
|
tgen = get_topogen(self)
|
||||||
|
tgen.add_router("r1")
|
||||||
|
switch = tgen.add_switch("s1")
|
||||||
|
switch.add_link(tgen.gears["r1"])
|
||||||
|
|
||||||
|
|
||||||
|
def setup_module(mod):
|
||||||
|
tgen = Topogen(TimingTopo, 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, count):
|
||||||
|
count_log2 = math.log(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(
|
||||||
|
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, 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.info(
|
||||||
|
"\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
|
||||||
|
prefix_count = 10000
|
||||||
|
prefix_base = [[u"10.0.0.0/8", u"11.0.0.0/8"],
|
||||||
|
[u"2100:1111:2220::/44", u"2100:3333:4440::/44"]]
|
||||||
|
|
||||||
|
bad_indices = []
|
||||||
|
for ipv6 in [False, True]:
|
||||||
|
base_delta = do_config(prefix_count, bad_indices, 0, 0, True, ipv6, prefix_base[ipv6][0])
|
||||||
|
|
||||||
|
# Another set of same number of prefixes
|
||||||
|
do_config(prefix_count, bad_indices, base_delta, 2, True, ipv6, prefix_base[ipv6][1])
|
||||||
|
|
||||||
|
# Duplicate config
|
||||||
|
do_config(prefix_count, bad_indices, base_delta, 2, True, ipv6, prefix_base[ipv6][0])
|
||||||
|
|
||||||
|
# Remove 1/2 of duplicate
|
||||||
|
do_config(prefix_count / 2, bad_indices, base_delta, 2, False, ipv6, prefix_base[ipv6][0])
|
||||||
|
|
||||||
|
# Add all back in so 1/2 replicate 1/2 new
|
||||||
|
do_config(prefix_count, bad_indices, base_delta, 2, True, ipv6, prefix_base[ipv6][0])
|
||||||
|
|
||||||
|
# remove all
|
||||||
|
delta = do_config(prefix_count, bad_indices, base_delta, 2, False, ipv6, prefix_base[ipv6][0])
|
||||||
|
delta += do_config(prefix_count, bad_indices, base_delta, 2, False, ipv6, prefix_base[ipv6][1])
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
args = ["-s"] + sys.argv[1:]
|
||||||
|
sys.exit(pytest.main(args))
|
Loading…
Reference in New Issue
Block a user