]> git.proxmox.com Git - mirror_frr.git/blame - tests/topotests/config_timing/test_config_timing.py
*: auto-convert to SPDX License IDs
[mirror_frr.git] / tests / topotests / config_timing / test_config_timing.py
CommitLineData
bb2cfcd0 1#!/usr/bin/env python
acddc0ed 2# SPDX-License-Identifier: ISC
bb2cfcd0
CH
3#
4# June 2 2021, Christian Hopps <chopps@labn.net>
5#
6# Copyright (c) 2021, LabN Consulting, L.L.C.
7# Copyright (c) 2019-2020 by
8# Donatas Abraitis <donatas.abraitis@gmail.com>
9#
bb2cfcd0
CH
10
11"""
12Test the timing of config operations.
13
14The initial add of 10k routes is used as a baseline for timing and all future
15operations are expected to complete in under 2 times that baseline. This is a
16lot of slop; however, the pre-batching code some of these operations (e.g.,
17adding the same set of 10k routes) would take 100 times longer, so the intention
18is to catch those types of regressions.
19"""
20
21import datetime
22import ipaddress
23import math
24import os
25import sys
26import pytest
27
28
29CWD = os.path.dirname(os.path.realpath(__file__))
30sys.path.append(os.path.join(CWD, "../"))
31
32# pylint: disable=C0413
33from lib.topogen import Topogen, TopoRouter, get_topogen
34from lib.topolog import logger
bb2cfcd0
CH
35
36pytestmark = [pytest.mark.staticd]
37
a53c08bc 38
e82b531d
CH
39def build_topo(tgen):
40 tgen.add_router("r1")
41 switch = tgen.add_switch("s1")
42 switch.add_link(tgen.gears["r1"])
bb2cfcd0
CH
43
44
45def setup_module(mod):
e82b531d 46 tgen = Topogen(build_topo, mod.__name__)
bb2cfcd0
CH
47 tgen.start_topology()
48
49 router_list = tgen.routers()
50 for rname, router in router_list.items():
51 router.load_config(
a53c08bc
CH
52 TopoRouter.RD_ZEBRA,
53 os.path.join(CWD, "{}/zebra.conf".format(rname)),
bb2cfcd0
CH
54 )
55 router.load_config(
56 TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname))
57 )
58
59 tgen.start_router()
60
61
62def teardown_module(mod):
63 tgen = get_topogen()
64 tgen.stop_topology()
65
a53c08bc 66
925d7f92
IR
67def get_ip_networks(super_prefix, base_count, count):
68 count_log2 = math.log(base_count, 2)
bb2cfcd0
CH
69 if count_log2 != int(count_log2):
70 count_log2 = int(count_log2) + 1
71 else:
72 count_log2 = int(count_log2)
73 network = ipaddress.ip_network(super_prefix)
74 return tuple(network.subnets(count_log2))[0:count]
75
a53c08bc 76
bb2cfcd0
CH
77def test_static_timing():
78 tgen = get_topogen()
79
80 if tgen.routers_have_failure():
81 pytest.skip(tgen.errors)
82
83 def do_config(
925d7f92 84 base_count,
a53c08bc
CH
85 count,
86 bad_indices,
87 base_delta,
88 d_multiplier,
89 add=True,
90 do_ipv6=False,
91 super_prefix=None,
92 en_dbg=False,
bb2cfcd0
CH
93 ):
94 router_list = tgen.routers()
95 tot_delta = float(0)
96
97 optype = "adding" if add else "removing"
98 iptype = "IPv6" if do_ipv6 else "IPv4"
99 if super_prefix is None:
100 super_prefix = u"2001::/48" if do_ipv6 else u"10.0.0.0/8"
101 via = u"lo"
102 optyped = "added" if add else "removed"
103
104 for rname, router in router_list.items():
a53c08bc 105 router.logger.info("{} {} static {} routes".format(optype, count, iptype))
bb2cfcd0
CH
106
107 # Generate config file.
108 config_file = os.path.join(
a53c08bc 109 router.logdir, rname, "{}-routes-{}.conf".format(iptype.lower(), optype)
bb2cfcd0
CH
110 )
111 with open(config_file, "w") as f:
925d7f92 112 for i, net in enumerate(get_ip_networks(super_prefix, base_count, count)):
bb2cfcd0
CH
113 if i in bad_indices:
114 if add:
115 f.write("ip route {} {} bad_input\n".format(net, via))
116 else:
117 f.write("no ip route {} {} bad_input\n".format(net, via))
118 elif add:
119 f.write("ip route {} {}\n".format(net, via))
120 else:
121 f.write("no ip route {} {}\n".format(net, via))
122
123 # Enable debug
124 if en_dbg:
125 router.vtysh_cmd("debug northbound callbacks configuration")
126
127 # Load config file.
128 load_command = 'vtysh -f "{}"'.format(config_file)
129 tstamp = datetime.datetime.now()
130 output = router.run(load_command)
131 delta = (datetime.datetime.now() - tstamp).total_seconds()
132 tot_delta += delta
133
134 router.logger.info(
135 "\nvtysh command => {}\nvtysh output <= {}\nin {}s".format(
136 load_command, output, delta
137 )
138 )
139
140 limit_delta = base_delta * d_multiplier
141 logger.info(
142 "{} {} {} static routes under {} in {}s (limit: {}s)".format(
143 optyped, count, iptype.lower(), super_prefix, tot_delta, limit_delta
144 )
145 )
146 if limit_delta:
147 assert tot_delta <= limit_delta
148
149 return tot_delta
150
9a5602b8 151
bb2cfcd0 152 # Number of static routes
9a5602b8
DS
153 router = tgen.gears["r1"]
154 output = router.run("vtysh -h | grep address-sanitizer")
155 if output == "":
156 logger.info("No Address Sanitizer, generating 10000 routes")
157 prefix_count = 10000
158 else:
159 logger.info("Address Sanitizer build, only testing 50 routes")
160 prefix_count = 50
161
a53c08bc
CH
162 prefix_base = [
163 [u"10.0.0.0/8", u"11.0.0.0/8"],
164 [u"2100:1111:2220::/44", u"2100:3333:4440::/44"],
165 ]
bb2cfcd0
CH
166
167 bad_indices = []
168 for ipv6 in [False, True]:
a53c08bc 169 base_delta = do_config(
925d7f92 170 prefix_count, prefix_count, bad_indices, 0, 0, True, ipv6, prefix_base[ipv6][0]
a53c08bc 171 )
bb2cfcd0
CH
172
173 # Another set of same number of prefixes
a53c08bc 174 do_config(
e57c66d5 175 prefix_count, prefix_count, bad_indices, base_delta, 3, True, ipv6, prefix_base[ipv6][1]
a53c08bc 176 )
bb2cfcd0
CH
177
178 # Duplicate config
a53c08bc 179 do_config(
e57c66d5 180 prefix_count, prefix_count, bad_indices, base_delta, 3, True, ipv6, prefix_base[ipv6][0]
a53c08bc 181 )
bb2cfcd0
CH
182
183 # Remove 1/2 of duplicate
a53c08bc 184 do_config(
925d7f92 185 prefix_count,
a53c08bc
CH
186 prefix_count // 2,
187 bad_indices,
188 base_delta,
e57c66d5 189 3,
a53c08bc
CH
190 False,
191 ipv6,
192 prefix_base[ipv6][0],
193 )
bb2cfcd0
CH
194
195 # Add all back in so 1/2 replicate 1/2 new
a53c08bc 196 do_config(
e57c66d5 197 prefix_count, prefix_count, bad_indices, base_delta, 3, True, ipv6, prefix_base[ipv6][0]
a53c08bc 198 )
bb2cfcd0
CH
199
200 # remove all
a53c08bc 201 delta = do_config(
e57c66d5 202 prefix_count, prefix_count, bad_indices, base_delta, 3, False, ipv6, prefix_base[ipv6][0]
a53c08bc
CH
203 )
204 delta += do_config(
e57c66d5 205 prefix_count, prefix_count, bad_indices, base_delta, 3, False, ipv6, prefix_base[ipv6][1]
a53c08bc
CH
206 )
207
bb2cfcd0
CH
208
209if __name__ == "__main__":
210 args = ["-s"] + sys.argv[1:]
211 sys.exit(pytest.main(args))