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