]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/config_timing/test_config_timing.py
Merge pull request #12959 from leonshaw/fix/zif-link-nsid
[mirror_frr.git] / tests / topotests / config_timing / test_config_timing.py
1 #!/usr/bin/env python
2 # SPDX-License-Identifier: ISC
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 #
10
11 """
12 Test the timing of config operations.
13
14 The initial add of 10k routes is used as a baseline for timing and all future
15 operations are expected to complete in under 2 times that baseline. This is a
16 lot of slop; however, the pre-batching code some of these operations (e.g.,
17 adding the same set of 10k routes) would take 100 times longer, so the intention
18 is to catch those types of regressions.
19 """
20
21 import datetime
22 import ipaddress
23 import math
24 import os
25 import sys
26 import pytest
27 from lib import topotest
28
29 CWD = os.path.dirname(os.path.realpath(__file__))
30 sys.path.append(os.path.join(CWD, "../"))
31
32 # pylint: disable=C0413
33 from lib.topogen import Topogen, TopoRouter, get_topogen
34 from lib.topolog import logger
35
36 pytestmark = [pytest.mark.staticd]
37
38
39 def build_topo(tgen):
40 tgen.add_router("r1")
41 switch = tgen.add_switch("s1")
42 switch.add_link(tgen.gears["r1"])
43
44
45 def setup_module(mod):
46 tgen = Topogen(build_topo, mod.__name__)
47 tgen.start_topology()
48
49 router_list = tgen.routers()
50 for rname, router in router_list.items():
51 router.load_config(
52 TopoRouter.RD_ZEBRA,
53 os.path.join(CWD, "{}/zebra.conf".format(rname)),
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
62 def teardown_module(mod):
63 tgen = get_topogen()
64 tgen.stop_topology()
65
66
67 def get_ip_networks(super_prefix, base_count, count):
68 count_log2 = math.log(base_count, 2)
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
76
77 def test_static_timing():
78 tgen = get_topogen()
79
80 if tgen.routers_have_failure():
81 pytest.skip(tgen.errors)
82
83 def do_config(
84 base_count,
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,
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():
105 router.logger.info("{} {} static {} routes".format(optype, count, iptype))
106
107 # Generate config file.
108 config_file = os.path.join(
109 router.logdir, rname, "{}-routes-{}.conf".format(iptype.lower(), optype)
110 )
111 with open(config_file, "w") as f:
112 for i, net in enumerate(
113 get_ip_networks(super_prefix, base_count, count)
114 ):
115 if i in bad_indices:
116 if add:
117 f.write("ip route {} {} bad_input\n".format(net, via))
118 else:
119 f.write("no ip route {} {} bad_input\n".format(net, via))
120 elif add:
121 f.write("ip route {} {}\n".format(net, via))
122 else:
123 f.write("no ip route {} {}\n".format(net, via))
124
125 # Enable debug
126 if en_dbg:
127 router.vtysh_cmd("debug northbound callbacks configuration")
128
129 # Load config file.
130 load_command = 'vtysh -f "{}"'.format(config_file)
131 tstamp = datetime.datetime.now()
132 output = router.run(load_command)
133 delta = (datetime.datetime.now() - tstamp).total_seconds()
134 tot_delta += delta
135
136 router.logger.debug(
137 "\nvtysh command => {}\nvtysh output <= {}\nin {}s".format(
138 load_command, output, delta
139 )
140 )
141
142 limit_delta = base_delta * d_multiplier
143 logger.info(
144 "{} {} {} static routes under {} in {}s (limit: {}s)".format(
145 optyped, count, iptype.lower(), super_prefix, tot_delta, limit_delta
146 )
147 )
148 if limit_delta:
149 assert tot_delta <= limit_delta
150
151 return tot_delta
152
153 # Number of static routes
154 router = tgen.gears["r1"]
155 output = router.net.cmd_legacy("vtysh -h | grep address-sanitizer", warn=False)
156 if output == "":
157 logger.info("No Address Sanitizer, generating 10000 routes")
158 prefix_count = 10000
159 else:
160 logger.info("Address Sanitizer build, only testing 50 routes")
161 prefix_count = 50
162
163 prefix_base = [
164 [u"10.0.0.0/8", u"11.0.0.0/8"],
165 [u"2100:1111:2220::/44", u"2100:3333:4440::/44"],
166 ]
167
168 # This apparently needed to allow for various mgmtd/staticd/zebra connections to form
169 # which then SLOWS execution down. If we don't include this value then the
170 # initial, baseline establishing, time is 2 time faster (e.g., 5s instead of 10s),
171 # but all later runs are slower and fail.
172 #
173 # This should be done differently based on actual facts.
174 topotest.sleep(5)
175
176 bad_indices = []
177 for ipv6 in [False, True]:
178 base_delta = do_config(
179 prefix_count,
180 prefix_count,
181 bad_indices,
182 0,
183 0,
184 True,
185 ipv6,
186 prefix_base[ipv6][0],
187 )
188
189 # Another set of same number of prefixes
190 do_config(
191 prefix_count,
192 prefix_count,
193 bad_indices,
194 base_delta,
195 3,
196 True,
197 ipv6,
198 prefix_base[ipv6][1],
199 )
200
201 # Duplicate config
202 do_config(
203 prefix_count,
204 prefix_count,
205 bad_indices,
206 base_delta,
207 3,
208 True,
209 ipv6,
210 prefix_base[ipv6][0],
211 )
212
213 # Remove 1/2 of duplicate
214 do_config(
215 prefix_count,
216 prefix_count // 2,
217 bad_indices,
218 base_delta,
219 3,
220 False,
221 ipv6,
222 prefix_base[ipv6][0],
223 )
224
225 # Add all back in so 1/2 replicate 1/2 new
226 do_config(
227 prefix_count,
228 prefix_count,
229 bad_indices,
230 base_delta,
231 3,
232 True,
233 ipv6,
234 prefix_base[ipv6][0],
235 )
236
237 # remove all
238 delta = do_config(
239 prefix_count,
240 prefix_count,
241 bad_indices,
242 base_delta,
243 3,
244 False,
245 ipv6,
246 prefix_base[ipv6][0],
247 )
248 delta += do_config(
249 prefix_count,
250 prefix_count,
251 bad_indices,
252 base_delta,
253 3,
254 False,
255 ipv6,
256 prefix_base[ipv6][1],
257 )
258
259
260 if __name__ == "__main__":
261 args = ["-s"] + sys.argv[1:]
262 sys.exit(pytest.main(args))