]> git.proxmox.com Git - mirror_frr.git/blame - tests/topotests/config_timing/test_config_timing.py
zebra: Fix nexthop group memory leak
[mirror_frr.git] / tests / topotests / config_timing / test_config_timing.py
CommitLineData
bb2cfcd0
CH
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"""
25Test the timing of config operations.
26
27The initial add of 10k routes is used as a baseline for timing and all future
28operations are expected to complete in under 2 times that baseline. This is a
29lot of slop; however, the pre-batching code some of these operations (e.g.,
30adding the same set of 10k routes) would take 100 times longer, so the intention
31is to catch those types of regressions.
32"""
33
34import datetime
35import ipaddress
36import math
37import os
38import sys
39import pytest
40
41
42CWD = os.path.dirname(os.path.realpath(__file__))
43sys.path.append(os.path.join(CWD, "../"))
44
45# pylint: disable=C0413
46from lib.topogen import Topogen, TopoRouter, get_topogen
47from lib.topolog import logger
bb2cfcd0
CH
48
49pytestmark = [pytest.mark.staticd]
50
a53c08bc 51
e82b531d
CH
52def build_topo(tgen):
53 tgen.add_router("r1")
54 switch = tgen.add_switch("s1")
55 switch.add_link(tgen.gears["r1"])
bb2cfcd0
CH
56
57
58def setup_module(mod):
e82b531d 59 tgen = Topogen(build_topo, mod.__name__)
bb2cfcd0
CH
60 tgen.start_topology()
61
62 router_list = tgen.routers()
63 for rname, router in router_list.items():
64 router.load_config(
a53c08bc
CH
65 TopoRouter.RD_ZEBRA,
66 os.path.join(CWD, "{}/zebra.conf".format(rname)),
bb2cfcd0
CH
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
75def teardown_module(mod):
76 tgen = get_topogen()
77 tgen.stop_topology()
78
a53c08bc 79
925d7f92
IR
80def get_ip_networks(super_prefix, base_count, count):
81 count_log2 = math.log(base_count, 2)
bb2cfcd0
CH
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
a53c08bc 89
bb2cfcd0
CH
90def test_static_timing():
91 tgen = get_topogen()
92
93 if tgen.routers_have_failure():
94 pytest.skip(tgen.errors)
95
96 def do_config(
925d7f92 97 base_count,
a53c08bc
CH
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,
bb2cfcd0
CH
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():
a53c08bc 118 router.logger.info("{} {} static {} routes".format(optype, count, iptype))
bb2cfcd0
CH
119
120 # Generate config file.
121 config_file = os.path.join(
a53c08bc 122 router.logdir, rname, "{}-routes-{}.conf".format(iptype.lower(), optype)
bb2cfcd0
CH
123 )
124 with open(config_file, "w") as f:
925d7f92 125 for i, net in enumerate(get_ip_networks(super_prefix, base_count, count)):
bb2cfcd0
CH
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
a53c08bc
CH
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 ]
bb2cfcd0
CH
170
171 bad_indices = []
172 for ipv6 in [False, True]:
a53c08bc 173 base_delta = do_config(
925d7f92 174 prefix_count, prefix_count, bad_indices, 0, 0, True, ipv6, prefix_base[ipv6][0]
a53c08bc 175 )
bb2cfcd0
CH
176
177 # Another set of same number of prefixes
a53c08bc 178 do_config(
e57c66d5 179 prefix_count, prefix_count, bad_indices, base_delta, 3, True, ipv6, prefix_base[ipv6][1]
a53c08bc 180 )
bb2cfcd0
CH
181
182 # Duplicate config
a53c08bc 183 do_config(
e57c66d5 184 prefix_count, prefix_count, bad_indices, base_delta, 3, True, ipv6, prefix_base[ipv6][0]
a53c08bc 185 )
bb2cfcd0
CH
186
187 # Remove 1/2 of duplicate
a53c08bc 188 do_config(
925d7f92 189 prefix_count,
a53c08bc
CH
190 prefix_count // 2,
191 bad_indices,
192 base_delta,
e57c66d5 193 3,
a53c08bc
CH
194 False,
195 ipv6,
196 prefix_base[ipv6][0],
197 )
bb2cfcd0
CH
198
199 # Add all back in so 1/2 replicate 1/2 new
a53c08bc 200 do_config(
e57c66d5 201 prefix_count, prefix_count, bad_indices, base_delta, 3, True, ipv6, prefix_base[ipv6][0]
a53c08bc 202 )
bb2cfcd0
CH
203
204 # remove all
a53c08bc 205 delta = do_config(
e57c66d5 206 prefix_count, prefix_count, bad_indices, base_delta, 3, False, ipv6, prefix_base[ipv6][0]
a53c08bc
CH
207 )
208 delta += do_config(
e57c66d5 209 prefix_count, prefix_count, bad_indices, base_delta, 3, False, ipv6, prefix_base[ipv6][1]
a53c08bc
CH
210 )
211
bb2cfcd0
CH
212
213if __name__ == "__main__":
214 args = ["-s"] + sys.argv[1:]
215 sys.exit(pytest.main(args))