2 # SPDX-License-Identifier: ISC
6 # Copyright (c) 2019 by
7 # Cumulus Networks, Inc
12 test_zebra_rib.py: Test some basic zebra <-> kernel interactions
18 from functools
import partial
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
, "../"))
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
35 pytestmark
= [pytest
.mark
.sharpd
]
36 krel
= platform
.release()
39 def config_macvlan(tgen
, r_str
, device
, macvlan
):
40 "Creates specified macvlan interace on physical device"
42 if topotest
.version_cmp(krel
, "5.1") < 0:
45 router
= tgen
.gears
[r_str
]
47 "ip link add {} link {} type macvlan mode bridge".format(macvlan
, device
)
49 router
.run("ip link set {} up".format(macvlan
))
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
__)
59 router_list
= tgen
.routers()
60 for rname
, router
in router_list
.items():
62 TopoRouter
.RD_ZEBRA
, os
.path
.join(CWD
, "{}/zebra.conf".format(rname
))
65 TopoRouter
.RD_SHARP
, os
.path
.join(CWD
, "{}/sharpd.conf".format(rname
))
68 # Macvlan interface for protodown func test */
69 config_macvlan(tgen
, "r1", "r1-eth0", "r1-eth0-macvlan")
70 # Initialize all routers.
74 def teardown_module(mod
):
75 "Teardown the pytest environment"
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")
84 if tgen
.routers_have_failure():
85 pytest
.skip("skipped because of router(s) failure")
89 # Route with 255/8192 metric
94 def makekmetric(dist
, metric
):
95 return (dist
<< 24) + metric
98 "ip route add 4.5.1.0/24 via 192.168.210.2 dev r1-eth0 metric "
99 + str(makekmetric(255, 8192))
101 # Route with 1/1 metric
103 "ip route add 4.5.2.0/24 via 192.168.211.2 dev r1-eth1 metric "
104 + str(makekmetric(1, 1))
106 # Route with 10/1 metric
108 "ip route add 4.5.3.0/24 via 192.168.212.2 dev r1-eth2 metric "
109 + str(makekmetric(10, 1))
111 # Same route with a 160/1 metric
113 "ip route add 4.5.3.0/24 via 192.168.213.2 dev r1-eth3 metric "
114 + str(makekmetric(160, 1))
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.
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())
126 topotest
.router_json_cmp
,
128 "show ip route 4.5.{}.0 json".format(i
),
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
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")
141 if tgen
.routers_have_failure():
142 pytest
.skip("skipped because of previous test failure")
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())
150 topotest
.router_json_cmp
, r1
, "show ip route 4.5.1.0 json", expected
152 _
, result
= topotest
.run_and_expect(test_func
, None, count
=20, wait
=0.5)
153 assert result
is None, '"r1" JSON output mismatches'
156 "Test that the removal of the static route allows the kernel to take back over"
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())
163 topotest
.router_json_cmp
, r1
, "show ip route 4.5.1.0 json", expected
165 _
, result
= topotest
.run_and_expect(test_func
, None, count
=20, wait
=0.5)
166 assert result
is None, '"r1" JSON output mismatches'
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")
173 if tgen
.routers_have_failure():
174 pytest
.skip("Skipped because of previous test failure")
176 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
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")
185 "conf\nroute-map sharp permit 10\nmatch ip address 10\nset src 192.168.214.1"
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")
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")
195 def check_initial_routes_installed(router
):
196 output
= json
.loads(router
.vtysh_cmd("show ip route summ json"))
198 "routes": [{"type": "static", "rib": 2}, {"type": "sharp", "rib": 500}]
200 return topotest
.json_cmp(output
, expected
)
202 test_func
= partial(check_initial_routes_installed
, r1
)
203 success
, result
= topotest
.run_and_expect(test_func
, None, count
=40, wait
=1)
205 static_rmapfile
= "%s/r1/static_rmap.ref" % (thisDir
)
206 expected
= open(static_rmapfile
).read().rstrip()
207 expected
= ("\n".join(expected
.splitlines()) + "\n").rstrip()
209 "Does the show route-map static command run the correct number of times"
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(
218 title1
="Actual Route-map output",
219 title2
="Expected Route-map output",
222 ok
, result
= topotest
.run_and_expect(
223 check_static_map_correct_runs
, "", count
=5, wait
=1
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")
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(
238 title1
="Actual Route-map output",
239 title2
="Expected Route-map output",
242 ok
, result
= topotest
.run_and_expect(
243 check_sharp_map_correct_runs
, "", count
=5, wait
=1
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"
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
257 sharp_ipfile
= "%s/r1/iproute.ref" % (thisDir
)
258 expected
= open(sharp_ipfile
).read().rstrip()
259 expected
= ("\n".join(expected
.splitlines()) + "\n").rstrip()
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(
278 title1
="Actual ip route show",
279 title2
="Expected ip route show",
282 ok
, result
= topotest
.run_and_expect(check_routes_installed
, "", count
=5, wait
=1)
286 def test_protodown():
287 "Run protodown basic functionality test and report results."
291 if topotest
.version_cmp(krel
, "5.1") < 0:
292 tgen
.errors
= "kernel 5.1 needed for protodown tests"
293 pytest
.skip(tgen
.errors
)
295 r1
= tgen
.gears
["r1"]
297 # Set interface protodown on
298 r1
.vtysh_cmd("sharp interface r1-eth0-macvlan protodown")
300 # Timeout to wait for dplane to handle it
303 output
= r1
.vtysh_cmd("show interface r1-eth0-macvlan")
304 if re
.search(r
"protodown reasons:.*sharp", output
):
309 assert pdown
is True, "Interface r1-eth0-macvlan not set protodown"
311 # Set interface protodown off
312 r1
.vtysh_cmd("no sharp interface r1-eth0-macvlan protodown")
314 # Timeout to wait for dplane to handle it
317 output
= r1
.vtysh_cmd("show interface r1-eth0-macvlan")
318 if not re
.search(r
"protodown reasons:.*sharp", output
):
323 assert pdown
is False, "Interface r1-eth0-macvlan not set protodown off"
326 def test_memory_leak():
327 "Run the memory leak test and report results."
329 if not tgen
.is_memleak_enabled():
330 pytest
.skip("Memory leak test/report is disabled")
332 tgen
.report_memory_leaks()
335 if __name__
== "__main__":
336 args
= ["-s"] + sys
.argv
[1:]
337 sys
.exit(pytest
.main(args
))