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