]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/config_timing/test_config_timing.py
Merge pull request #12798 from donaldsharp/rib_match_multicast
[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
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(get_ip_networks(super_prefix, base_count, count)):
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
151
152 # Number of static routes
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
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 ]
166
167 bad_indices = []
168 for ipv6 in [False, True]:
169 base_delta = do_config(
170 prefix_count, prefix_count, bad_indices, 0, 0, True, ipv6, prefix_base[ipv6][0]
171 )
172
173 # Another set of same number of prefixes
174 do_config(
175 prefix_count, prefix_count, bad_indices, base_delta, 3, True, ipv6, prefix_base[ipv6][1]
176 )
177
178 # Duplicate config
179 do_config(
180 prefix_count, prefix_count, bad_indices, base_delta, 3, True, ipv6, prefix_base[ipv6][0]
181 )
182
183 # Remove 1/2 of duplicate
184 do_config(
185 prefix_count,
186 prefix_count // 2,
187 bad_indices,
188 base_delta,
189 3,
190 False,
191 ipv6,
192 prefix_base[ipv6][0],
193 )
194
195 # Add all back in so 1/2 replicate 1/2 new
196 do_config(
197 prefix_count, prefix_count, bad_indices, base_delta, 3, True, ipv6, prefix_base[ipv6][0]
198 )
199
200 # remove all
201 delta = do_config(
202 prefix_count, prefix_count, bad_indices, base_delta, 3, False, ipv6, prefix_base[ipv6][0]
203 )
204 delta += do_config(
205 prefix_count, prefix_count, bad_indices, base_delta, 3, False, ipv6, prefix_base[ipv6][1]
206 )
207
208
209 if __name__ == "__main__":
210 args = ["-s"] + sys.argv[1:]
211 sys.exit(pytest.main(args))