]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/zebra_rib/test_zebra_rib.py
tests: add protodown topotest using sharpd
[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 sleep(4)
207
208 static_rmapfile = "%s/r1/static_rmap.ref" % (thisDir)
209 expected = open(static_rmapfile).read().rstrip()
210 expected = ("\n".join(expected.splitlines()) + "\n").rstrip()
211 logger.info(
212 "Does the show route-map static command run the correct number of times"
213 )
214
215 def check_static_map_correct_runs():
216 actual = r1.vtysh_cmd("show route-map static")
217 actual = ("\n".join(actual.splitlines()) + "\n").rstrip()
218 return topotest.get_textdiff(
219 actual,
220 expected,
221 title1="Actual Route-map output",
222 title2="Expected Route-map output",
223 )
224
225 ok, result = topotest.run_and_expect(
226 check_static_map_correct_runs, "", count=5, wait=1
227 )
228 assert ok, result
229
230 sharp_rmapfile = "%s/r1/sharp_rmap.ref" % (thisDir)
231 expected = open(sharp_rmapfile).read().rstrip()
232 expected = ("\n".join(expected.splitlines()) + "\n").rstrip()
233 logger.info("Does the show route-map sharp command run the correct number of times")
234
235 def check_sharp_map_correct_runs():
236 actual = r1.vtysh_cmd("show route-map sharp")
237 actual = ("\n".join(actual.splitlines()) + "\n").rstrip()
238 return topotest.get_textdiff(
239 actual,
240 expected,
241 title1="Actual Route-map output",
242 title2="Expected Route-map output",
243 )
244
245 ok, result = topotest.run_and_expect(
246 check_sharp_map_correct_runs, "", count=5, wait=1
247 )
248 assert ok, result
249
250 logger.info(
251 "Add a extension to the static route-map to see the static route go away"
252 " and test that the routes installed are correct"
253 )
254
255 r1.vtysh_cmd("conf\nroute-map sharp deny 5\nmatch ip address 5")
256 # we are only checking the kernel here as that this will give us the implied
257 # testing of both the route-map and staticd withdrawing the route
258 # let's spot check that the routes were installed correctly
259 # in the kernel
260 sharp_ipfile = "%s/r1/iproute.ref" % (thisDir)
261 expected = open(sharp_ipfile).read().rstrip()
262 expected = ("\n".join(expected.splitlines()) + "\n").rstrip()
263
264 def check_routes_installed():
265 actual = r1.run("ip route show")
266 actual = ("\n".join(actual.splitlines()) + "\n").rstrip()
267 actual = re.sub(r" nhid [0-9][0-9]", "", actual)
268 actual = re.sub(r" proto sharp", " proto XXXX", actual)
269 actual = re.sub(r" proto static", " proto XXXX", actual)
270 actual = re.sub(r" proto 194", " proto XXXX", actual)
271 actual = re.sub(r" proto 196", " proto XXXX", actual)
272 actual = re.sub(r" proto kernel", " proto XXXX", actual)
273 actual = re.sub(r" proto 2", " proto XXXX", actual)
274 # Some platforms have double spaces? Why??????
275 actual = re.sub(r" proto XXXX ", " proto XXXX ", actual)
276 actual = re.sub(r" metric", " metric", actual)
277 actual = re.sub(r" link ", " link ", actual)
278 return topotest.get_textdiff(
279 actual,
280 expected,
281 title1="Actual ip route show",
282 title2="Expected ip route show",
283 )
284
285 ok, result = topotest.run_and_expect(check_routes_installed, "", count=5, wait=1)
286 assert ok, result
287
288
289 def test_protodown():
290 "Run protodown basic functionality test and report results."
291 pdown = False
292 count = 0
293 tgen = get_topogen()
294 if topotest.version_cmp(krel, "5.1") < 0:
295 tgen.errors = "kernel 5.1 needed for protodown tests"
296 pytest.skip(tgen.errors)
297
298 r1 = tgen.gears["r1"]
299
300 # Set interface protodown on
301 r1.vtysh_cmd("sharp interface r1-eth0-macvlan protodown")
302
303 # Timeout to wait for dplane to handle it
304 while count < 10:
305 count += 1
306 output = r1.vtysh_cmd("show interface r1-eth0-macvlan")
307 if re.search(r"protodown reasons:.*sharp", output):
308 pdown = True
309 break
310 sleep(1)
311
312 assert pdown is True, "Interface r1-eth0-macvlan not set protodown"
313
314 # Set interface protodown off
315 r1.vtysh_cmd("no sharp interface r1-eth0-macvlan protodown")
316
317 # Timeout to wait for dplane to handle it
318 while count < 10:
319 count += 1
320 output = r1.vtysh_cmd("show interface r1-eth0-macvlan")
321 if not re.search(r"protodown reasons:.*sharp", output):
322 pdown = False
323 break
324 sleep(1)
325
326 assert pdown is False, "Interface r1-eth0-macvlan not set protodown off"
327
328
329 def test_memory_leak():
330 "Run the memory leak test and report results."
331 tgen = get_topogen()
332 if not tgen.is_memleak_enabled():
333 pytest.skip("Memory leak test/report is disabled")
334
335 tgen.report_memory_leaks()
336
337
338 if __name__ == "__main__":
339 args = ["-s"] + sys.argv[1:]
340 sys.exit(pytest.main(args))