]>
Commit | Line | Data |
---|---|---|
abd2a1ff DS |
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 | ||
35 | # Save the Current Working Directory to find configuration files. | |
36 | CWD = os.path.dirname(os.path.realpath(__file__)) | |
787e7624 | 37 | sys.path.append(os.path.join(CWD, "../")) |
abd2a1ff DS |
38 | |
39 | # pylint: disable=C0413 | |
40 | # Import topogen and topotest helpers | |
41 | from lib import topotest | |
42 | from lib.topogen import Topogen, TopoRouter, get_topogen | |
43 | from lib.topolog import logger | |
fed16811 | 44 | from time import sleep |
abd2a1ff DS |
45 | |
46 | # Required to instantiate the topology builder class. | |
47 | from mininet.topo import Topo | |
48 | ||
787e7624 | 49 | |
abd2a1ff DS |
50 | class ZebraTopo(Topo): |
51 | "Test topology builder" | |
787e7624 | 52 | |
abd2a1ff DS |
53 | def build(self, *_args, **_opts): |
54 | "Build function" | |
55 | tgen = get_topogen(self) | |
56 | ||
787e7624 | 57 | tgen.add_router("r1") |
abd2a1ff DS |
58 | |
59 | # Create a empty network for router 1 | |
787e7624 | 60 | switch = tgen.add_switch("s1") |
61 | switch.add_link(tgen.gears["r1"]) | |
62 | switch.add_link(tgen.gears["r1"]) | |
63 | switch.add_link(tgen.gears["r1"]) | |
64 | switch.add_link(tgen.gears["r1"]) | |
65 | switch.add_link(tgen.gears["r1"]) | |
66 | switch.add_link(tgen.gears["r1"]) | |
67 | switch.add_link(tgen.gears["r1"]) | |
68 | switch.add_link(tgen.gears["r1"]) | |
69 | ||
abd2a1ff DS |
70 | |
71 | def setup_module(mod): | |
72 | "Sets up the pytest environment" | |
73 | tgen = Topogen(ZebraTopo, mod.__name__) | |
74 | tgen.start_topology() | |
75 | ||
76 | router_list = tgen.routers() | |
e5f0ed14 | 77 | for rname, router in router_list.items(): |
abd2a1ff | 78 | router.load_config( |
5980ad0a DS |
79 | TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) |
80 | ) | |
fed16811 | 81 | router.load_config( |
5980ad0a DS |
82 | TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname)) |
83 | ) | |
abd2a1ff DS |
84 | |
85 | # Initialize all routers. | |
86 | tgen.start_router() | |
87 | ||
787e7624 | 88 | |
abd2a1ff DS |
89 | def teardown_module(mod): |
90 | "Teardown the pytest environment" | |
91 | tgen = get_topogen() | |
92 | tgen.stop_topology() | |
93 | ||
787e7624 | 94 | |
abd2a1ff DS |
95 | def test_zebra_kernel_admin_distance(): |
96 | "Test some basic kernel routes added that should be accepted" | |
97 | logger.info("Test some basic kernel routes that should be accepted") | |
98 | tgen = get_topogen() | |
99 | if tgen.routers_have_failure(): | |
787e7624 | 100 | pytest.skip("skipped because of router(s) failure") |
abd2a1ff | 101 | |
787e7624 | 102 | r1 = tgen.gears["r1"] |
abd2a1ff DS |
103 | |
104 | # Route with 255/8192 metric | |
787e7624 | 105 | r1.run("ip route add 4.5.1.0/24 via 192.168.210.2 dev r1-eth0 metric 4278198272") |
abd2a1ff | 106 | # Route with 1/1 metric |
787e7624 | 107 | r1.run("ip route add 4.5.2.0/24 via 192.168.211.2 dev r1-eth1 metric 16777217") |
abd2a1ff | 108 | # Route with 10/1 metric |
787e7624 | 109 | r1.run("ip route add 4.5.3.0/24 via 192.168.212.2 dev r1-eth2 metric 167772161") |
abd2a1ff | 110 | # Same route with a 160/1 metric |
787e7624 | 111 | r1.run("ip route add 4.5.3.0/24 via 192.168.213.2 dev r1-eth3 metric 2684354561") |
abd2a1ff | 112 | |
787e7624 | 113 | # Currently I believe we have a bug here with the same route and different |
114 | # metric. That needs to be properly resolved. Making a note for | |
115 | # coming back around later and fixing this. | |
116 | # tgen.mininet_cli() | |
abd2a1ff | 117 | for i in range(1, 2): |
787e7624 | 118 | json_file = "{}/r1/v4_route_{}.json".format(CWD, i) |
abd2a1ff DS |
119 | expected = json.loads(open(json_file).read()) |
120 | ||
787e7624 | 121 | test_func = partial( |
122 | topotest.router_json_cmp, | |
123 | r1, | |
124 | "show ip route 4.5.{}.0 json".format(i), | |
125 | expected, | |
126 | ) | |
127 | _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5) | |
abd2a1ff DS |
128 | assertmsg = '"r1" JSON output mismatches' |
129 | assert result is None, assertmsg | |
787e7624 | 130 | # tgen.mininet_cli() |
131 | ||
abd2a1ff DS |
132 | |
133 | def test_zebra_kernel_override(): | |
134 | "Test that a FRR route with a lower admin distance takes over" | |
135 | logger.info("Test kernel override with a better admin distance") | |
136 | tgen = get_topogen() | |
787e7624 | 137 | if tgen.routers_have_failure(): |
5094b56d | 138 | pytest.skip("skipped because of previous test failure") |
abd2a1ff | 139 | |
787e7624 | 140 | r1 = tgen.gears["r1"] |
abd2a1ff | 141 | r1.vtysh_cmd("conf\nip route 4.5.1.0/24 192.168.216.3") |
787e7624 | 142 | json_file = "{}/r1/v4_route_1_static_override.json".format(CWD) |
abd2a1ff DS |
143 | expected = json.loads(open(json_file).read()) |
144 | ||
787e7624 | 145 | test_func = partial( |
146 | topotest.router_json_cmp, r1, "show ip route 4.5.1.0 json", expected | |
147 | ) | |
148 | _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5) | |
abd2a1ff DS |
149 | assert result is None, '"r1" JSON output mismatches' |
150 | ||
787e7624 | 151 | logger.info( |
152 | "Test that the removal of the static route allows the kernel to take back over" | |
153 | ) | |
abd2a1ff | 154 | r1.vtysh_cmd("conf\nno ip route 4.5.1.0/24 192.168.216.3") |
787e7624 | 155 | json_file = "{}/r1/v4_route_1.json".format(CWD) |
abd2a1ff DS |
156 | expected = json.loads(open(json_file).read()) |
157 | ||
787e7624 | 158 | test_func = partial( |
159 | topotest.router_json_cmp, r1, "show ip route 4.5.1.0 json", expected | |
160 | ) | |
161 | _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5) | |
abd2a1ff DS |
162 | assert result is None, '"r1" JSON output mismatches' |
163 | ||
5980ad0a | 164 | |
fed16811 DS |
165 | def test_route_map_usage(): |
166 | "Test that FRR only reruns over routes associated with the routemap" | |
167 | logger.info("Test that FRR runs on selected re's on route-map changes") | |
168 | tgen = get_topogen() | |
169 | if tgen.routers_have_failure(): | |
170 | pytest.skip("Skipped because of previous test failure") | |
171 | ||
172 | thisDir = os.path.dirname(os.path.realpath(__file__)) | |
173 | ||
174 | r1 = tgen.gears["r1"] | |
175 | # set the delay timer to 1 to improve test coverage (HA) | |
176 | r1.vtysh_cmd("conf\nzebra route-map delay-timer 1") | |
177 | r1.vtysh_cmd("conf\nroute-map static permit 10\nset src 192.168.215.1") | |
178 | r1.vtysh_cmd("conf\naccess-list 5 seq 5 permit 10.0.0.44/32") | |
179 | r1.vtysh_cmd("conf\naccess-list 10 seq 5 permit 10.0.1.0/24") | |
5980ad0a DS |
180 | r1.vtysh_cmd( |
181 | "conf\nroute-map sharp permit 10\nmatch ip address 10\nset src 192.168.214.1" | |
182 | ) | |
fed16811 DS |
183 | r1.vtysh_cmd("conf\nroute-map sharp permit 20\nset src 192.168.213.1") |
184 | r1.vtysh_cmd("conf\nip protocol static route-map static") | |
185 | r1.vtysh_cmd("conf\nip protocol sharp route-map sharp") | |
186 | sleep(4) | |
187 | r1.vtysh_cmd("conf\nip route 10.100.100.100/32 192.168.216.3") | |
188 | r1.vtysh_cmd("conf\nip route 10.100.100.101/32 10.0.0.44") | |
189 | r1.vtysh_cmd("sharp install route 10.0.0.0 nexthop 192.168.216.3 500") | |
190 | sleep(4) | |
191 | ||
192 | static_rmapfile = "%s/r1/static_rmap.ref" % (thisDir) | |
193 | expected = open(static_rmapfile).read().rstrip() | |
5980ad0a | 194 | expected = ("\n".join(expected.splitlines()) + "\n").rstrip() |
fed16811 | 195 | actual = r1.vtysh_cmd("show route-map static") |
5980ad0a DS |
196 | actual = ("\n".join(actual.splitlines()) + "\n").rstrip() |
197 | logger.info( | |
198 | "Does the show route-map static command run the correct number of times" | |
199 | ) | |
fed16811 | 200 | |
5980ad0a DS |
201 | diff = topotest.get_textdiff( |
202 | actual, | |
203 | expected, | |
204 | title1="Actual Route-map output", | |
205 | title2="Expected Route-map output", | |
206 | ) | |
fed16811 DS |
207 | if diff: |
208 | logger.info("Actual:") | |
209 | logger.info(actual) | |
210 | logger.info("Expected:") | |
211 | logger.info(expected) | |
212 | srun = r1.vtysh_cmd("show run") | |
5980ad0a | 213 | srun = ("\n".join(srun.splitlines()) + "\n").rstrip() |
fed16811 DS |
214 | logger.info("Show run") |
215 | logger.info(srun) | |
216 | assert 0, "r1 static route processing:\n" | |
217 | ||
218 | sharp_rmapfile = "%s/r1/sharp_rmap.ref" % (thisDir) | |
219 | expected = open(sharp_rmapfile).read().rstrip() | |
5980ad0a | 220 | expected = ("\n".join(expected.splitlines()) + "\n").rstrip() |
fed16811 | 221 | actual = r1.vtysh_cmd("show route-map sharp") |
5980ad0a | 222 | actual = ("\n".join(actual.splitlines()) + "\n").rstrip() |
fed16811 DS |
223 | logger.info("Does the show route-map sharp command run the correct number of times") |
224 | ||
5980ad0a DS |
225 | diff = topotest.get_textdiff( |
226 | actual, | |
227 | expected, | |
228 | title1="Actual Route-map output", | |
229 | title2="Expected Route-map output", | |
230 | ) | |
fed16811 DS |
231 | if diff: |
232 | logger.info("Actual:") | |
233 | logger.info(actual) | |
234 | logger.info("Expected:") | |
235 | logger.info(expected) | |
236 | srun = r1.vtysh_cmd("show run") | |
5980ad0a | 237 | srun = ("\n".join(srun.splitlines()) + "\n").rstrip() |
fed16811 DS |
238 | logger.info("Show run:") |
239 | logger.info(srun) | |
240 | assert 0, "r1 sharp route-map processing:\n" | |
241 | ||
5980ad0a DS |
242 | logger.info( |
243 | "Add a extension to the static route-map to see the static route go away" | |
244 | ) | |
fed16811 DS |
245 | r1.vtysh_cmd("conf\nroute-map sharp deny 5\nmatch ip address 5") |
246 | sleep(2) | |
247 | # we are only checking the kernel here as that this will give us the implied | |
248 | # testing of both the route-map and staticd withdrawing the route | |
249 | # let's spot check that the routes were installed correctly | |
250 | # in the kernel | |
251 | logger.info("Test that the routes installed are correct") | |
252 | sharp_ipfile = "%s/r1/iproute.ref" % (thisDir) | |
253 | expected = open(sharp_ipfile).read().rstrip() | |
5980ad0a | 254 | expected = ("\n".join(expected.splitlines()) + "\n").rstrip() |
fed16811 | 255 | actual = r1.run("ip route show") |
5980ad0a | 256 | actual = ("\n".join(actual.splitlines()) + "\n").rstrip() |
fed16811 DS |
257 | actual = re.sub(r" nhid [0-9][0-9]", "", actual) |
258 | actual = re.sub(r" proto sharp", " proto XXXX", actual) | |
259 | actual = re.sub(r" proto static", " proto XXXX", actual) | |
260 | actual = re.sub(r" proto 194", " proto XXXX", actual) | |
261 | actual = re.sub(r" proto 196", " proto XXXX", actual) | |
262 | actual = re.sub(r" proto kernel", " proto XXXX", actual) | |
263 | actual = re.sub(r" proto 2", " proto XXXX", actual) | |
264 | # Some platforms have double spaces? Why?????? | |
265 | actual = re.sub(r" proto XXXX ", " proto XXXX ", actual) | |
266 | actual = re.sub(r" metric", " metric", actual) | |
267 | actual = re.sub(r" link ", " link ", actual) | |
5980ad0a DS |
268 | diff = topotest.get_textdiff( |
269 | actual, expected, title1="Actual ip route show", title2="Expected ip route show" | |
270 | ) | |
fed16811 DS |
271 | |
272 | if diff: | |
273 | logger.info("Actual:") | |
274 | logger.info(actual) | |
275 | logger.info("Expected:") | |
276 | logger.info(expected) | |
277 | srun = r1.vtysh_cmd("show run") | |
5980ad0a | 278 | srun = ("\n".join(srun.splitlines()) + "\n").rstrip() |
fed16811 DS |
279 | logger.info("Show run:") |
280 | logger.info(srun) | |
281 | assert 0, "r1 ip route show is not correct:" | |
abd2a1ff | 282 | |
5980ad0a | 283 | |
abd2a1ff DS |
284 | def test_memory_leak(): |
285 | "Run the memory leak test and report results." | |
286 | tgen = get_topogen() | |
287 | if not tgen.is_memleak_enabled(): | |
787e7624 | 288 | pytest.skip("Memory leak test/report is disabled") |
abd2a1ff DS |
289 | |
290 | tgen.report_memory_leaks() | |
291 | ||
787e7624 | 292 | |
293 | if __name__ == "__main__": | |
abd2a1ff DS |
294 | args = ["-s"] + sys.argv[1:] |
295 | sys.exit(pytest.main(args)) |