]>
Commit | Line | Data |
---|---|---|
687c62fc | 1 | #!/usr/bin/env python |
acddc0ed | 2 | # SPDX-License-Identifier: ISC |
687c62fc IS |
3 | |
4 | # | |
5 | # Copyright (c) 2021 by VMware, Inc. ("VMware") | |
6 | # Used Copyright (c) 2018 by Network Device Education Foundation, | |
7 | # Inc. ("NetDEF") in this file. | |
8 | # | |
687c62fc IS |
9 | |
10 | """ | |
11 | Following tests are covered to test BGP VRF Lite: | |
12 | 1. Verify that locally imported routes are selected as best path over eBGP imported routes | |
13 | peers. | |
14 | 2. Verify ECMP for imported routes from different VRFs. | |
15 | """ | |
16 | ||
17 | import os | |
18 | import sys | |
19 | import time | |
20 | import pytest | |
21 | import platform | |
22 | from time import sleep | |
23 | ||
24 | # Save the Current Working Directory to find configuration files. | |
25 | CWD = os.path.dirname(os.path.realpath(__file__)) | |
26 | sys.path.append(os.path.join(CWD, "../")) | |
27 | sys.path.append(os.path.join(CWD, "../lib/")) | |
28 | ||
29 | # Required to instantiate the topology builder class. | |
30 | ||
31 | # pylint: disable=C0413 | |
32 | # Import topogen and topotest helpers | |
33 | from lib.topogen import Topogen, get_topogen | |
34 | from lib.topotest import version_cmp | |
35 | ||
36 | from lib.common_config import ( | |
37 | start_topology, | |
38 | write_test_header, | |
39 | check_address_types, | |
40 | write_test_footer, | |
41 | reset_config_on_routers, | |
42 | verify_rib, | |
43 | step, | |
44 | create_static_routes, | |
45 | check_router_status, | |
46 | apply_raw_config | |
47 | ) | |
48 | ||
49 | from lib.topolog import logger | |
50 | from lib.bgp import ( | |
51 | verify_bgp_convergence, | |
52 | create_router_bgp, | |
53 | verify_bgp_rib, | |
54 | verify_bgp_bestpath | |
55 | ) | |
56 | from lib.topojson import build_config_from_json | |
57 | ||
58 | pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] | |
59 | ||
60 | # Global variables | |
61 | NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} | |
62 | NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"} | |
63 | NETWORK1_3 = {"ipv4": "10.10.10.1/32", "ipv6": "10:10::1/128"} | |
64 | NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"} | |
65 | NETWORK1_5 = {"ipv4": "110.110.110.1/32", "ipv6": "110:110::1/128"} | |
66 | NETWORK1_6 = {"ipv4": "110.110.110.100/32", "ipv6": "110:110::100/128"} | |
67 | ||
68 | NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"} | |
69 | NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"} | |
70 | NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"} | |
71 | NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"} | |
72 | NETWORK2_5 = {"ipv4": "220.220.220.20/32", "ipv6": "220:220::20/128"} | |
73 | NETWORK2_6 = {"ipv4": "220.220.220.200/32", "ipv6": "220:220::200/128"} | |
74 | ||
75 | NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"} | |
76 | NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"} | |
77 | ||
78 | PREFIX_LIST = { | |
79 | "ipv4": ["11.11.11.1", "22.22.22.2", "22.22.22.22"], | |
80 | "ipv6": ["11:11::1", "22:22::2", "22:22::22"], | |
81 | } | |
82 | PREFERRED_NEXT_HOP = "global" | |
83 | VRF_LIST = ["RED", "BLUE", "GREEN"] | |
84 | COMM_VAL_1 = "100:100" | |
85 | COMM_VAL_2 = "500:500" | |
86 | COMM_VAL_3 = "600:600" | |
87 | BESTPATH = { | |
88 | "ipv4": "0.0.0.0", | |
89 | "ipv6": "::" | |
90 | } | |
91 | ||
92 | def setup_module(mod): | |
93 | """ | |
94 | Sets up the pytest environment | |
95 | ||
96 | * `mod`: module name | |
97 | """ | |
98 | ||
99 | testsuite_run_time = time.asctime(time.localtime(time.time())) | |
100 | logger.info("Testsuite start time: {}".format(testsuite_run_time)) | |
101 | logger.info("=" * 40) | |
102 | ||
103 | logger.info("Running setup_module to create topology") | |
104 | ||
105 | # This function initiates the topology build with Topogen... | |
106 | json_file = "{}/bgp_vrf_lite_best_path_topo2.json".format(CWD) | |
107 | tgen = Topogen(json_file, mod.__name__) | |
108 | global topo | |
109 | topo = tgen.json_topo | |
110 | # ... and here it calls Mininet initialization functions. | |
111 | ||
112 | # Starting topology, create tmp files which are loaded to routers | |
d60a3f0e | 113 | # to start daemons and then start routers |
687c62fc IS |
114 | start_topology(tgen) |
115 | ||
116 | # Run these tests for kernel version 4.19 or above | |
117 | if version_cmp(platform.release(), "4.19") < 0: | |
118 | error_msg = ( | |
119 | "BGP vrf dynamic route leak tests will not run " | |
120 | '(have kernel "{}", but it requires >= 4.19)'.format(platform.release()) | |
121 | ) | |
122 | pytest.skip(error_msg) | |
123 | ||
124 | # Creating configuration from JSON | |
125 | build_config_from_json(tgen, topo) | |
126 | ||
127 | global BGP_CONVERGENCE | |
128 | global ADDR_TYPES | |
129 | ADDR_TYPES = check_address_types() | |
130 | ||
131 | BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) | |
132 | assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( | |
133 | BGP_CONVERGENCE | |
134 | ) | |
135 | ||
136 | logger.info("Running setup_module() done") | |
137 | ||
138 | ||
139 | def teardown_module(): | |
140 | """Teardown the pytest environment""" | |
141 | ||
142 | logger.info("Running teardown_module to delete topology") | |
143 | ||
144 | tgen = get_topogen() | |
145 | ||
146 | # Stop toplogy and Remove tmp files | |
147 | tgen.stop_topology() | |
148 | ||
149 | logger.info( | |
150 | "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) | |
151 | ) | |
152 | logger.info("=" * 40) | |
153 | ||
154 | ||
155 | ##################################################### | |
156 | # | |
157 | # Testcases | |
158 | # | |
159 | ##################################################### | |
160 | ||
161 | def test_dynamic_import_ecmp_imported_routed_diffrent_vrfs_p0(request): | |
162 | """ | |
163 | Verify ECMP for imported routes from different VRFs. | |
164 | """ | |
165 | ||
166 | tgen = get_topogen() | |
167 | tc_name = request.node.name | |
168 | write_test_header(tc_name) | |
169 | if tgen.routers_have_failure(): | |
170 | check_router_status(tgen) | |
171 | reset_config_on_routers(tgen) | |
172 | ||
173 | step("Configure same static routes in tenant vrfs RED and GREEN on router " | |
174 | "R3 and redistribute in respective BGP process") | |
175 | ||
176 | for vrf_name in ["RED", "GREEN"]: | |
177 | for addr_type in ADDR_TYPES: | |
178 | if vrf_name == "GREEN": | |
179 | next_hop_vrf = topo["routers"]["r1"]["links"][ | |
180 | "r3-link3"][addr_type].split("/")[0] | |
181 | else: | |
182 | next_hop_vrf = topo["routers"]["r2"]["links"][ | |
183 | "r3-link1"][addr_type].split("/")[0] | |
184 | static_routes = { | |
185 | "r3": { | |
186 | "static_routes": [ | |
187 | { | |
188 | "network": [NETWORK1_1[addr_type]], | |
189 | "next_hop": next_hop_vrf, | |
190 | "vrf": vrf_name | |
191 | } | |
192 | ] | |
193 | } | |
194 | } | |
195 | ||
196 | result = create_static_routes(tgen, static_routes) | |
197 | assert result is True, "Testcase {} :Failed \n Error: {}". \ | |
198 | format(tc_name, result) | |
199 | ||
200 | step("Redistribute static route on BGP VRF : {}".format(vrf_name)) | |
201 | temp = {} | |
202 | for addr_type in ADDR_TYPES: | |
203 | temp.update({ | |
204 | addr_type: { | |
205 | "unicast": { | |
206 | "redistribute": [{ | |
207 | "redist_type": "static" | |
208 | }] | |
209 | } | |
210 | } | |
211 | }) | |
212 | ||
213 | redist_dict = {"r3": {"bgp": [{ | |
214 | "vrf": vrf_name, "local_as": 3, "address_family": temp | |
215 | }]}} | |
216 | ||
217 | result = create_router_bgp(tgen, topo, redist_dict) | |
218 | assert result is True, "Testcase {} :Failed \n Error: {}". \ | |
219 | format(tc_name, result) | |
220 | ||
221 | step("Verify that configured static routes are installed in respective " | |
222 | "BGP table for vrf RED & GREEN") | |
223 | for vrf_name in ["RED", "GREEN"]: | |
224 | for addr_type in ADDR_TYPES: | |
225 | if vrf_name == "GREEN": | |
226 | next_hop_vrf = topo["routers"]["r1"]["links"][ | |
227 | "r3-link3"][addr_type].split("/")[0] | |
228 | else: | |
229 | next_hop_vrf = topo["routers"]["r2"]["links"][ | |
230 | "r3-link1"][addr_type].split("/")[0] | |
231 | static_routes = { | |
232 | "r3": { | |
233 | "static_routes": [ | |
234 | { | |
235 | "network": [NETWORK1_1[addr_type]], | |
236 | "vrf": vrf_name | |
237 | } | |
238 | ] | |
239 | } | |
240 | } | |
241 | ||
242 | result = verify_bgp_rib(tgen, addr_type, "r3", static_routes, | |
243 | next_hop=next_hop_vrf) | |
244 | assert result is True, "Testcase {} : Failed \n Error {}". \ | |
245 | format(tc_name, result) | |
246 | ||
247 | result = verify_rib(tgen, addr_type, "r3", static_routes, | |
248 | next_hop=next_hop_vrf) | |
249 | assert result is True, "Testcase {} : Failed \n Error {}". \ | |
250 | format(tc_name, result) | |
251 | ||
252 | step("Import vrf RED and GREEN into default vrf and Configure ECMP") | |
253 | bgp_val = [] | |
254 | for vrf_name in ["RED", "GREEN"]: | |
255 | temp = {} | |
256 | for addr_type in ADDR_TYPES: | |
257 | temp.update({ | |
258 | addr_type: { | |
259 | "unicast": { | |
260 | "import": { | |
261 | "vrf": vrf_name | |
262 | }, | |
263 | "maximum_paths": { | |
264 | "ebgp": 2 | |
265 | } | |
266 | } | |
267 | } | |
268 | }) | |
269 | ||
270 | bgp_val.append({ | |
271 | "local_as": 3, "address_family": temp | |
272 | }) | |
273 | ||
274 | import_dict = {"r3": {"bgp": bgp_val}} | |
275 | ||
276 | result = create_router_bgp(tgen, topo, import_dict) | |
277 | assert result is True, "Testcase {} :Failed \n Error: {}". \ | |
278 | format(tc_name, result) | |
279 | ||
280 | step("Configure bgp bestpath on router r3") | |
281 | r3_raw_config = { | |
282 | "r3": { | |
283 | "raw_config": [ | |
284 | "router bgp 3", | |
285 | "bgp bestpath as-path multipath-relax" | |
286 | ] | |
287 | } | |
288 | } | |
289 | result = apply_raw_config(tgen, r3_raw_config) | |
290 | assert result is True, "Testcase {} :Failed \n Error: {}". \ | |
291 | format(tc_name, result) | |
292 | ||
293 | step("Verify that routes are imported with two different next-hop vrfs " | |
294 | "and IPs. Additionally R3 must do ECMP for both the routes.") | |
295 | ||
296 | for addr_type in ADDR_TYPES: | |
297 | next_hop_vrf = [ | |
298 | topo["routers"]["r2"]["links"]["r3-link1"][addr_type]. \ | |
299 | split("/")[0], | |
300 | topo["routers"]["r1"]["links"]["r3-link3"][addr_type]. \ | |
301 | split("/")[0] | |
302 | ] | |
303 | static_routes = { | |
304 | "r3": { | |
305 | "static_routes": [ | |
306 | { | |
307 | "network": [NETWORK1_1[addr_type]], | |
308 | } | |
309 | ] | |
310 | } | |
311 | } | |
312 | ||
313 | result = verify_bgp_rib(tgen, addr_type, "r3", static_routes, | |
314 | next_hop=next_hop_vrf) | |
315 | assert result is True, "Testcase {} : Failed \n Error {}". \ | |
316 | format(tc_name, result) | |
317 | ||
318 | result = verify_rib(tgen, addr_type, "r3", static_routes, | |
319 | next_hop=next_hop_vrf) | |
320 | assert result is True, "Testcase {} : Failed \n Error {}". \ | |
321 | format(tc_name, result) | |
322 | ||
323 | step("Now change the next-hop of static routes in vrf RED and GREEN to " | |
324 | "same IP address") | |
325 | for addr_type in ADDR_TYPES: | |
326 | next_hop_vrf = topo["routers"]["r1"]["links"][ | |
327 | "r3-link3"][addr_type].split("/")[0] | |
328 | static_routes = { | |
329 | "r3": { | |
330 | "static_routes": [ | |
331 | { | |
332 | "network": [NETWORK1_1[addr_type]], | |
333 | "next_hop": next_hop_vrf, | |
334 | "vrf": "RED" | |
335 | }, | |
336 | { | |
337 | "network": [NETWORK1_1[addr_type]], | |
338 | "next_hop": topo["routers"]["r2"]["links"][ | |
339 | "r3-link1"][addr_type].split("/")[0], | |
340 | "vrf": "RED", | |
341 | "delete": True | |
342 | } | |
343 | ] | |
344 | } | |
345 | } | |
346 | ||
347 | result = create_static_routes(tgen, static_routes) | |
348 | assert result is True, "Testcase {} :Failed \n Error: {}". \ | |
349 | format(tc_name, result) | |
350 | ||
351 | step("Verify that now routes are imported with two different next-hop " | |
352 | "vrfs but same IPs. Additionally R3 must do ECMP for both the routes") | |
353 | ||
354 | for addr_type in ADDR_TYPES: | |
355 | next_hop_vrf = [ | |
356 | topo["routers"]["r1"]["links"]["r3-link3"][addr_type].\ | |
357 | split("/")[0], | |
358 | topo["routers"]["r1"]["links"]["r3-link3"][addr_type]. \ | |
359 | split("/")[0] | |
360 | ] | |
361 | static_routes = { | |
362 | "r3": { | |
363 | "static_routes": [ | |
364 | { | |
365 | "network": [NETWORK1_1[addr_type]], | |
366 | } | |
367 | ] | |
368 | } | |
369 | } | |
370 | ||
371 | result = verify_bgp_rib(tgen, addr_type, "r3", static_routes, | |
372 | next_hop=next_hop_vrf) | |
373 | assert result is True, "Testcase {} : Failed \n Error {}". \ | |
374 | format(tc_name, result) | |
375 | ||
376 | result = verify_rib(tgen, addr_type, "r3", static_routes, | |
377 | next_hop=next_hop_vrf) | |
378 | assert result is True, "Testcase {} : Failed \n Error {}". \ | |
379 | format(tc_name, result) | |
380 | ||
381 | write_test_footer(tc_name) | |
382 | ||
383 | ||
384 | def test_locally_imported_routes_selected_as_bestpath_over_ebgp_imported_routes_p0(request): | |
385 | """ | |
386 | Verify ECMP for imported routes from different VRFs. | |
387 | """ | |
388 | ||
389 | tgen = get_topogen() | |
390 | tc_name = request.node.name | |
391 | write_test_header(tc_name) | |
392 | if tgen.routers_have_failure(): | |
393 | check_router_status(tgen) | |
394 | reset_config_on_routers(tgen) | |
395 | ||
396 | step("Configure same static routes on R2 and R3 vrfs and redistribute in BGP " | |
397 | "for GREEN and RED vrf instances") | |
398 | for dut, network in zip(["r2", "r3"], [ | |
399 | [NETWORK1_1, NETWORK1_2], [NETWORK1_1, NETWORK1_2]]): | |
400 | for vrf_name, network_vrf in zip(["RED", "GREEN"], network): | |
401 | step("Configure static route for VRF : {} on {}".format(vrf_name, | |
402 | dut)) | |
403 | for addr_type in ADDR_TYPES: | |
404 | static_routes = { | |
405 | dut: { | |
406 | "static_routes": [ | |
407 | { | |
408 | "network": [network_vrf[addr_type]], | |
409 | "next_hop": "blackhole", | |
410 | "vrf": vrf_name | |
411 | } | |
412 | ] | |
413 | } | |
414 | } | |
415 | ||
416 | result = create_static_routes(tgen, static_routes) | |
417 | assert result is True, "Testcase {} :Failed \n Error: {}". \ | |
418 | format(tc_name, result) | |
419 | ||
420 | for dut, as_num in zip(["r2", "r3"], ["2", "3"]): | |
421 | for vrf_name in ["RED", "GREEN"]: | |
422 | step("Redistribute static route on BGP VRF : {}".format(vrf_name)) | |
423 | temp = {} | |
424 | for addr_type in ADDR_TYPES: | |
425 | temp.update({ | |
426 | addr_type: { | |
427 | "unicast": { | |
428 | "redistribute": [{ | |
429 | "redist_type": "static" | |
430 | }] | |
431 | } | |
432 | } | |
433 | }) | |
434 | ||
435 | redist_dict = {dut: {"bgp": [{ | |
436 | "vrf": vrf_name, "local_as": as_num, "address_family": temp | |
437 | }]}} | |
438 | ||
439 | result = create_router_bgp(tgen, topo, redist_dict) | |
440 | assert result is True, "Testcase {} :Failed \n Error: {}". \ | |
441 | format(tc_name, result) | |
442 | ||
443 | step("Verify that R2 and R3 has installed redistributed routes in default " | |
444 | "and RED vrfs and GREEN respectively:") | |
445 | for dut, network in zip(["r2", "r3"], | |
446 | [[NETWORK1_1, NETWORK1_2], | |
447 | [NETWORK1_1, NETWORK1_2]]): | |
448 | for vrf_name, network_vrf in zip(["RED", "GREEN"], network): | |
449 | for addr_type in ADDR_TYPES: | |
450 | static_routes = { | |
451 | dut: { | |
452 | "static_routes": [ | |
453 | { | |
454 | "network": [network_vrf[addr_type]], | |
455 | "next_hop": "blackhole", | |
456 | "vrf": vrf_name | |
457 | } | |
458 | ] | |
459 | } | |
460 | } | |
461 | result = verify_bgp_rib(tgen, addr_type, dut, static_routes) | |
462 | assert result is True, "Testcase {} : Failed \n Error {}". \ | |
463 | format(tc_name, result) | |
464 | ||
465 | step("Import vrf RED's route in vrf GREEN on R3") | |
466 | temp = {} | |
467 | for addr_type in ADDR_TYPES: | |
468 | temp.update({ | |
469 | addr_type: { | |
470 | "unicast": { | |
471 | "import": { | |
472 | "vrf": "RED" | |
473 | } | |
474 | } | |
475 | } | |
476 | }) | |
477 | ||
478 | import_dict = {"r3": {"bgp": [{ | |
479 | "vrf": "GREEN", "local_as": 3, "address_family": temp | |
480 | }]}} | |
481 | ||
482 | result = create_router_bgp(tgen, topo, import_dict) | |
483 | assert result is True, "Testcase {} :Failed \n Error: {}". \ | |
484 | format(tc_name, result) | |
485 | ||
486 | step("Verify that locally imported routes are installed over eBGP imported" | |
487 | " routes from VRF RED into VRF GREEN") | |
488 | for addr_type in ADDR_TYPES: | |
489 | static_routes = { | |
490 | "r3": { | |
491 | "static_routes": [ | |
492 | { | |
493 | "network": [NETWORK1_2[addr_type]], | |
494 | "next_hop": "blackhole", | |
495 | "vrf": "GREEN" | |
496 | } | |
497 | ] | |
498 | } | |
499 | } | |
500 | ||
501 | input_routes = { | |
502 | "r3": { | |
503 | addr_type: [ | |
504 | { | |
505 | "network": NETWORK1_2[addr_type], | |
506 | "bestpath": BESTPATH[addr_type], | |
507 | "vrf": "GREEN" | |
508 | } | |
509 | ] | |
510 | } | |
511 | } | |
512 | ||
513 | result = verify_bgp_bestpath(tgen, addr_type, input_routes) | |
514 | assert result is True, "Testcase {} : Failed \n Error {}". \ | |
515 | format(tc_name, result) | |
516 | ||
517 | result = verify_rib(tgen, addr_type, "r3", static_routes) | |
518 | assert result is True, "Testcase {} : Failed \n Error {}". \ | |
519 | format(tc_name, result) | |
520 | ||
521 | write_test_footer(tc_name) | |
522 | ||
523 | ||
524 | if __name__ == "__main__": | |
525 | args = ["-s"] + sys.argv[1:] | |
526 | sys.exit(pytest.main(args)) |