]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/zebra_rib/test_zebra_rib.py
tests: zebra_rib remove a sleep
[mirror_frr.git] / tests / topotests / zebra_rib / test_zebra_rib.py
1 #!/usr/bin/env python
2 #
3 # test_zebra_rib.py
4 #
5 # Copyright (c) 2019 by
6 # Cumulus Networks, Inc
7 # Donald Sharp
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_zebra_rib.py: Test some basic zebra <-> kernel interactions
26 """
27
28 import os
29 import re
30 import sys
31 from functools import partial
32 import pytest
33 import json
34 import platform
35
36 # Save the Current Working Directory to find configuration files.
37 CWD = os.path.dirname(os.path.realpath(__file__))
38 sys.path.append(os.path.join(CWD, "../"))
39
40 # pylint: disable=C0413
41 # Import topogen and topotest helpers
42 from lib import topotest
43 from lib.topogen import Topogen, TopoRouter, get_topogen
44 from lib.topolog import logger
45 from time import sleep
46
47
48 pytestmark = [pytest.mark.sharpd]
49 krel = platform.release()
50
51
52 def config_macvlan(tgen, r_str, device, macvlan):
53 "Creates specified macvlan interace on physical device"
54
55 if topotest.version_cmp(krel, "5.1") < 0:
56 return
57
58 router = tgen.gears[r_str]
59 router.run(
60 "ip link add {} link {} type macvlan mode bridge".format(macvlan, device)
61 )
62 router.run("ip link set {} up".format(macvlan))
63
64
65 def setup_module(mod):
66 "Sets up the pytest environment"
67 topodef = {"s1": ("r1", "r1", "r1", "r1", "r1", "r1", "r1", "r1")}
68 tgen = Topogen(topodef, mod.__name__)
69 tgen.start_topology()
70
71 router_list = tgen.routers()
72 for rname, router in router_list.items():
73 router.load_config(
74 TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
75 )
76 router.load_config(
77 TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname))
78 )
79
80 # Macvlan interface for protodown func test */
81 config_macvlan(tgen, "r1", "r1-eth0", "r1-eth0-macvlan")
82 # Initialize all routers.
83 tgen.start_router()
84
85
86 def teardown_module(mod):
87 "Teardown the pytest environment"
88 tgen = get_topogen()
89 tgen.stop_topology()
90
91
92 def test_zebra_kernel_admin_distance():
93 "Test some basic kernel routes added that should be accepted"
94 logger.info("Test some basic kernel routes that should be accepted")
95 tgen = get_topogen()
96 if tgen.routers_have_failure():
97 pytest.skip("skipped because of router(s) failure")
98
99 r1 = tgen.gears["r1"]
100
101 # Route with 255/8192 metric
102
103 distance = 255
104 metric = 8192
105
106 def makekmetric(dist, metric):
107 return (dist << 24) + metric
108
109 r1.run(
110 "ip route add 4.5.1.0/24 via 192.168.210.2 dev r1-eth0 metric "
111 + str(makekmetric(255, 8192))
112 )
113 # Route with 1/1 metric
114 r1.run(
115 "ip route add 4.5.2.0/24 via 192.168.211.2 dev r1-eth1 metric "
116 + str(makekmetric(1, 1))
117 )
118 # Route with 10/1 metric
119 r1.run(
120 "ip route add 4.5.3.0/24 via 192.168.212.2 dev r1-eth2 metric "
121 + str(makekmetric(10, 1))
122 )
123 # Same route with a 160/1 metric
124 r1.run(
125 "ip route add 4.5.3.0/24 via 192.168.213.2 dev r1-eth3 metric "
126 + str(makekmetric(160, 1))
127 )
128
129 # Currently I believe we have a bug here with the same route and different
130 # metric. That needs to be properly resolved. Making a note for
131 # coming back around later and fixing this.
132 # tgen.mininet_cli()
133 for i in range(1, 2):
134 json_file = "{}/r1/v4_route_{}.json".format(CWD, i)
135 expected = json.loads(open(json_file).read())
136
137 test_func = partial(
138 topotest.router_json_cmp,
139 r1,
140 "show ip route 4.5.{}.0 json".format(i),
141 expected,
142 )
143 _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
144 assertmsg = '"r1" JSON output mismatches'
145 assert result is None, assertmsg
146 # tgen.mininet_cli()
147
148
149 def test_zebra_kernel_override():
150 "Test that a FRR route with a lower admin distance takes over"
151 logger.info("Test kernel override with a better admin distance")
152 tgen = get_topogen()
153 if tgen.routers_have_failure():
154 pytest.skip("skipped because of previous test failure")
155
156 r1 = tgen.gears["r1"]
157 r1.vtysh_cmd("conf\nip route 4.5.1.0/24 192.168.216.3")
158 json_file = "{}/r1/v4_route_1_static_override.json".format(CWD)
159 expected = json.loads(open(json_file).read())
160
161 test_func = partial(
162 topotest.router_json_cmp, r1, "show ip route 4.5.1.0 json", expected
163 )
164 _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
165 assert result is None, '"r1" JSON output mismatches'
166
167 logger.info(
168 "Test that the removal of the static route allows the kernel to take back over"
169 )
170 r1.vtysh_cmd("conf\nno ip route 4.5.1.0/24 192.168.216.3")
171 json_file = "{}/r1/v4_route_1.json".format(CWD)
172 expected = json.loads(open(json_file).read())
173
174 test_func = partial(
175 topotest.router_json_cmp, r1, "show ip route 4.5.1.0 json", expected
176 )
177 _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
178 assert result is None, '"r1" JSON output mismatches'
179
180
181 def test_route_map_usage():
182 "Test that FRR only reruns over routes associated with the routemap"
183 logger.info("Test that FRR runs on selected re's on route-map changes")
184 tgen = get_topogen()
185 if tgen.routers_have_failure():
186 pytest.skip("Skipped because of previous test failure")
187
188 thisDir = os.path.dirname(os.path.realpath(__file__))
189
190 r1 = tgen.gears["r1"]
191 # set the delay timer to 1 to improve test coverage (HA)
192 r1.vtysh_cmd("conf\nzebra route-map delay-timer 1")
193 r1.vtysh_cmd("conf\nroute-map static permit 10\nset src 192.168.215.1")
194 r1.vtysh_cmd("conf\naccess-list 5 seq 5 permit 10.0.0.44/32")
195 r1.vtysh_cmd("conf\naccess-list 10 seq 5 permit 10.0.1.0/24")
196 r1.vtysh_cmd(
197 "conf\nroute-map sharp permit 10\nmatch ip address 10\nset src 192.168.214.1"
198 )
199 r1.vtysh_cmd("conf\nroute-map sharp permit 20\nset src 192.168.213.1")
200 r1.vtysh_cmd("conf\nip protocol static route-map static")
201 r1.vtysh_cmd("conf\nip protocol sharp route-map sharp")
202 sleep(4)
203 r1.vtysh_cmd("conf\nip route 10.100.100.100/32 192.168.216.3")
204 r1.vtysh_cmd("conf\nip route 10.100.100.101/32 10.0.0.44")
205 r1.vtysh_cmd("sharp install route 10.0.0.0 nexthop 192.168.216.3 500")
206
207 def check_initial_routes_installed(router):
208 output = json.loads(router.vtysh_cmd("show ip route summ json"))
209 expected = {
210 "routes": [{"type": "static", "rib": 2}, {"type": "sharp", "rib": 500}]
211 }
212 return topotest.json_cmp(output, expected)
213
214 test_func = partial(check_initial_routes_installed, r1)
215 success, result = topotest.run_and_expect(test_func, None, count=40, wait=1)
216
217 static_rmapfile = "%s/r1/static_rmap.ref" % (thisDir)
218 expected = open(static_rmapfile).read().rstrip()
219 expected = ("\n".join(expected.splitlines()) + "\n").rstrip()
220 logger.info(
221 "Does the show route-map static command run the correct number of times"
222 )
223
224 def check_static_map_correct_runs():
225 actual = r1.vtysh_cmd("show route-map static")
226 actual = ("\n".join(actual.splitlines()) + "\n").rstrip()
227 return topotest.get_textdiff(
228 actual,
229 expected,
230 title1="Actual Route-map output",
231 title2="Expected Route-map output",
232 )
233
234 ok, result = topotest.run_and_expect(
235 check_static_map_correct_runs, "", count=5, wait=1
236 )
237 assert ok, result
238
239 sharp_rmapfile = "%s/r1/sharp_rmap.ref" % (thisDir)
240 expected = open(sharp_rmapfile).read().rstrip()
241 expected = ("\n".join(expected.splitlines()) + "\n").rstrip()
242 logger.info("Does the show route-map sharp command run the correct number of times")
243
244 def check_sharp_map_correct_runs():
245 actual = r1.vtysh_cmd("show route-map sharp")
246 actual = ("\n".join(actual.splitlines()) + "\n").rstrip()
247 return topotest.get_textdiff(
248 actual,
249 expected,
250 title1="Actual Route-map output",
251 title2="Expected Route-map output",
252 )
253
254 ok, result = topotest.run_and_expect(
255 check_sharp_map_correct_runs, "", count=5, wait=1
256 )
257 assert ok, result
258
259 logger.info(
260 "Add a extension to the static route-map to see the static route go away"
261 " and test that the routes installed are correct"
262 )
263
264 r1.vtysh_cmd("conf\nroute-map sharp deny 5\nmatch ip address 5")
265 # we are only checking the kernel here as that this will give us the implied
266 # testing of both the route-map and staticd withdrawing the route
267 # let's spot check that the routes were installed correctly
268 # in the kernel
269 sharp_ipfile = "%s/r1/iproute.ref" % (thisDir)
270 expected = open(sharp_ipfile).read().rstrip()
271 expected = ("\n".join(expected.splitlines()) + "\n").rstrip()
272
273 def check_routes_installed():
274 actual = r1.run("ip route show")
275 actual = ("\n".join(actual.splitlines()) + "\n").rstrip()
276 actual = re.sub(r" nhid [0-9][0-9]", "", actual)
277 actual = re.sub(r" proto sharp", " proto XXXX", actual)
278 actual = re.sub(r" proto static", " proto XXXX", actual)
279 actual = re.sub(r" proto 194", " proto XXXX", actual)
280 actual = re.sub(r" proto 196", " proto XXXX", actual)
281 actual = re.sub(r" proto kernel", " proto XXXX", actual)
282 actual = re.sub(r" proto 2", " proto XXXX", actual)
283 # Some platforms have double spaces? Why??????
284 actual = re.sub(r" proto XXXX ", " proto XXXX ", actual)
285 actual = re.sub(r" metric", " metric", actual)
286 actual = re.sub(r" link ", " link ", actual)
287 return topotest.get_textdiff(
288 actual,
289 expected,
290 title1="Actual ip route show",
291 title2="Expected ip route show",
292 )
293
294 ok, result = topotest.run_and_expect(check_routes_installed, "", count=5, wait=1)
295 assert ok, result
296
297
298 def test_protodown():
299 "Run protodown basic functionality test and report results."
300 pdown = False
301 count = 0
302 tgen = get_topogen()
303 if topotest.version_cmp(krel, "5.1") < 0:
304 tgen.errors = "kernel 5.1 needed for protodown tests"
305 pytest.skip(tgen.errors)
306
307 r1 = tgen.gears["r1"]
308
309 # Set interface protodown on
310 r1.vtysh_cmd("sharp interface r1-eth0-macvlan protodown")
311
312 # Timeout to wait for dplane to handle it
313 while count < 10:
314 count += 1
315 output = r1.vtysh_cmd("show interface r1-eth0-macvlan")
316 if re.search(r"protodown reasons:.*sharp", output):
317 pdown = True
318 break
319 sleep(1)
320
321 assert pdown is True, "Interface r1-eth0-macvlan not set protodown"
322
323 # Set interface protodown off
324 r1.vtysh_cmd("no sharp interface r1-eth0-macvlan protodown")
325
326 # Timeout to wait for dplane to handle it
327 while count < 10:
328 count += 1
329 output = r1.vtysh_cmd("show interface r1-eth0-macvlan")
330 if not re.search(r"protodown reasons:.*sharp", output):
331 pdown = False
332 break
333 sleep(1)
334
335 assert pdown is False, "Interface r1-eth0-macvlan not set protodown off"
336
337
338 def test_memory_leak():
339 "Run the memory leak test and report results."
340 tgen = get_topogen()
341 if not tgen.is_memleak_enabled():
342 pytest.skip("Memory leak test/report is disabled")
343
344 tgen.report_memory_leaks()
345
346
347 if __name__ == "__main__":
348 args = ["-s"] + sys.argv[1:]
349 sys.exit(pytest.main(args))