]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/route_scale/scale_test_common.py
*: auto-convert to SPDX License IDs
[mirror_frr.git] / tests / topotests / route_scale / scale_test_common.py
1 #!/usr/bin/env python
2 # SPDX-License-Identifier: ISC
3
4 #
5 # scale_test_common.py
6 #
7 # Copyright (c) 2020 by
8 # Cumulus Networks, Inc.
9 # Donald Sharp
10 #
11
12 """
13 scale_test_common.py: Common routines for testing route scale
14
15 """
16
17 import os
18 import re
19 import sys
20 import pytest
21 import json
22 from functools import partial
23
24 # Save the Current Working Directory to find configuration files.
25 CWD = os.path.dirname(os.path.realpath(__file__))
26 sys.path.append(os.path.join(CWD, "../"))
27
28 # pylint: disable=C0413
29 # Import topogen and topotest helpers
30 from lib import topotest
31 from lib.topogen import Topogen, TopoRouter, get_topogen
32 from lib.topolog import logger
33
34
35 #####################################################
36 ##
37 ## Network Topology Definition
38 ##
39 #####################################################
40
41
42 def scale_build_common(tgen):
43 "Build function"
44
45 # Populate routers
46 for routern in range(1, 2):
47 tgen.add_router("r{}".format(routern))
48
49 # Populate switches
50 for switchn in range(1, 33):
51 switch = tgen.add_switch("sw{}".format(switchn))
52 switch.add_link(tgen.gears["r1"])
53
54
55 def scale_setup_module(module):
56 "Setup topology"
57 tgen = Topogen(scale_build_common, module.__name__)
58 tgen.start_topology()
59
60 router_list = tgen.routers()
61 for rname, router in router_list.items():
62 router.load_config(
63 TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
64 )
65 router.load_config(
66 TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname))
67 )
68
69 tgen.start_router()
70 # tgen.mininet_cli()
71
72
73 def scale_teardown_module(_mod):
74 "Teardown the pytest environment"
75 tgen = get_topogen()
76
77 # This function tears down the whole topology.
78 tgen.stop_topology()
79
80
81 def scale_converge_protocols():
82 "Wait for protocol convergence"
83
84 tgen = get_topogen()
85 # Don't run this test if we have any failure.
86 if tgen.routers_have_failure():
87 pytest.skip(tgen.errors)
88
89
90 def run_one_setup(r1, s):
91 "Run one ecmp config"
92
93 # Extract params
94 expected_installed = s["expect_in"]
95 expected_removed = s["expect_rem"]
96
97 retries = s["retries"]
98 wait = s["wait"]
99
100 for d in expected_installed["routes"]:
101 if d["type"] == "sharp":
102 count = d["rib"]
103 break
104
105 logger.info("Testing {} routes X {} ecmp".format(count, s["ecmp"]))
106
107 r1.vtysh_cmd(
108 "sharp install route 1.0.0.0 \
109 nexthop-group {} {}".format(
110 s["nhg"], count
111 ),
112 isjson=False,
113 )
114
115 test_func = partial(
116 topotest.router_json_cmp, r1, "show ip route summary json", expected_installed
117 )
118 success, result = topotest.run_and_expect(test_func, None, retries, wait)
119 assert success, "Route scale test install failed:\n{}".format(result)
120
121 output = r1.vtysh_cmd("sharp data route", isjson=False)
122 logger.info("{} routes X {} ecmp installed".format(count, s["ecmp"]))
123 logger.info(output)
124 r1.vtysh_cmd("sharp remove route 1.0.0.0 {}".format(count), isjson=False)
125 test_func = partial(
126 topotest.router_json_cmp, r1, "show ip route summary json", expected_removed
127 )
128 success, result = topotest.run_and_expect(test_func, None, retries, wait)
129 assert success, "Route scale test remove failed:\n{}".format(result)
130
131 output = r1.vtysh_cmd("sharp data route", isjson=False)
132 logger.info("{} routes x {} ecmp removed".format(count, s["ecmp"]))
133 logger.info(output)
134
135
136 def route_install_helper(iter):
137 "Test route install for a variety of ecmp"
138
139 tgen = get_topogen()
140 # Don't run this test if we have any failure.
141 if tgen.routers_have_failure():
142 pytest.skip(tgen.errors)
143
144 r1 = tgen.gears["r1"]
145
146 # Avoid top ecmp case for runs with < 4G memory
147 output = tgen.net.cmd_raises("free")
148 m = re.search(r"Mem:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)", output)
149 total_mem = int(m.group(2))
150 if total_mem < 4000000 and iter == 5:
151 logger.info(
152 "Limited memory available: {}, skipping x32 testcase".format(total_mem)
153 )
154 return;
155
156 installed_file = "{}/r1/installed.routes.json".format(CWD)
157 expected_installed = json.loads(open(installed_file).read())
158
159 removed_file = "{}/r1/no.routes.json".format(CWD)
160 expected_removed = json.loads(open(removed_file).read())
161
162 # dict keys of params: ecmp number, corresponding nhg name, timeout,
163 # number of times to wait
164 scale_keys = ["ecmp", "nhg", "wait", "retries", "expect_in", "expect_rem"]
165
166 # Table of defaults, used for timeout values and 'expected' objects
167 scale_defaults = dict(
168 zip(scale_keys, [None, None, 10, 50, expected_installed, expected_removed])
169 )
170
171 # List of params for each step in the test; note extra time given
172 # for the highest ecmp steps. Executing 'show' at scale can be costly
173 # so we widen the interval there too.
174 scale_steps = [
175 [1, "one"],
176 [2, "two"],
177 [4, "four"],
178 [8, "eight"],
179 [16, "sixteen", 10, 40],
180 [32, "thirtytwo", 10, 40],
181 ]
182
183 # Build up a list of dicts with params for each step of the test;
184 # use defaults where the step doesn't supply a value
185 scale_setups = []
186 s = scale_steps[iter]
187
188 d = dict(zip(scale_keys, s))
189 for k in scale_keys:
190 if k not in d:
191 d[k] = scale_defaults[k]
192
193 run_one_setup(r1, d)
194
195
196 # Mem leak testcase
197 def scale_test_memory_leak():
198 "Run the memory leak test and report results."
199 tgen = get_topogen()
200 if not tgen.is_memleak_enabled():
201 pytest.skip("Memory leak test/report is disabled")
202 tgen.report_memory_leaks()