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