]> git.proxmox.com Git - mirror_frr.git/blame - tests/topotests/config_timing/test_config_timing.py
Merge pull request #13206 from opensourcerouting/fix/docker_build_alpine_protobuf
[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
f637ac01 27from lib import topotest
bb2cfcd0
CH
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:
f637ac01 112 for i, net in enumerate(
113 get_ip_networks(super_prefix, base_count, count)
114 ):
bb2cfcd0
CH
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
e8f7a22f 136 router.logger.debug(
bb2cfcd0
CH
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
9a5602b8 154 router = tgen.gears["r1"]
e8f7a22f 155 output = router.net.cmd_legacy("vtysh -h | grep address-sanitizer", warn=False)
9a5602b8
DS
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
a53c08bc
CH
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 ]
bb2cfcd0 167
f637ac01 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
bb2cfcd0
CH
176 bad_indices = []
177 for ipv6 in [False, True]:
a53c08bc 178 base_delta = do_config(
f637ac01 179 prefix_count,
180 prefix_count,
181 bad_indices,
182 0,
183 0,
184 True,
185 ipv6,
186 prefix_base[ipv6][0],
a53c08bc 187 )
bb2cfcd0
CH
188
189 # Another set of same number of prefixes
a53c08bc 190 do_config(
f637ac01 191 prefix_count,
192 prefix_count,
193 bad_indices,
194 base_delta,
195 3,
196 True,
197 ipv6,
198 prefix_base[ipv6][1],
a53c08bc 199 )
bb2cfcd0
CH
200
201 # Duplicate config
a53c08bc 202 do_config(
f637ac01 203 prefix_count,
204 prefix_count,
205 bad_indices,
206 base_delta,
207 3,
208 True,
209 ipv6,
210 prefix_base[ipv6][0],
a53c08bc 211 )
bb2cfcd0
CH
212
213 # Remove 1/2 of duplicate
a53c08bc 214 do_config(
925d7f92 215 prefix_count,
a53c08bc
CH
216 prefix_count // 2,
217 bad_indices,
218 base_delta,
e57c66d5 219 3,
a53c08bc
CH
220 False,
221 ipv6,
222 prefix_base[ipv6][0],
223 )
bb2cfcd0
CH
224
225 # Add all back in so 1/2 replicate 1/2 new
a53c08bc 226 do_config(
f637ac01 227 prefix_count,
228 prefix_count,
229 bad_indices,
230 base_delta,
231 3,
232 True,
233 ipv6,
234 prefix_base[ipv6][0],
a53c08bc 235 )
bb2cfcd0
CH
236
237 # remove all
a53c08bc 238 delta = do_config(
f637ac01 239 prefix_count,
240 prefix_count,
241 bad_indices,
242 base_delta,
243 3,
244 False,
245 ipv6,
246 prefix_base[ipv6][0],
a53c08bc
CH
247 )
248 delta += do_config(
f637ac01 249 prefix_count,
250 prefix_count,
251 bad_indices,
252 base_delta,
253 3,
254 False,
255 ipv6,
256 prefix_base[ipv6][1],
a53c08bc
CH
257 )
258
bb2cfcd0
CH
259
260if __name__ == "__main__":
261 args = ["-s"] + sys.argv[1:]
262 sys.exit(pytest.main(args))