]>
Commit | Line | Data |
---|---|---|
1b66072c KK |
1 | #!/usr/bin/env python |
2 | ||
3 | # | |
4 | # Copyright (c) 2020 by VMware, Inc. ("VMware") | |
5 | # Used Copyright (c) 2018 by Network Device Education Foundation, | |
6 | # Inc. ("NetDEF") in this file. | |
7 | # | |
8 | # Permission to use, copy, modify, and/or distribute this software | |
9 | # for any purpose with or without fee is hereby granted, provided | |
10 | # that the above copyright notice and this permission notice appear | |
11 | # in all copies. | |
12 | # | |
13 | # THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES | |
14 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
15 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR | |
16 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY | |
17 | # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
18 | # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS | |
19 | # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | |
20 | # OF THIS SOFTWARE. | |
21 | # | |
22 | ||
23 | """ | |
24 | Following tests are covered to test EVPN-Type5 functionality: | |
1b66072c KK |
25 | 1. In absence of an overlay index all IP-Prefixes(RT-5) |
26 | are advertised with default values for below parameters: | |
27 | --> Ethernet Tag ID = GW IP address = ESI=0 | |
28 | 2. EVPN CLI output and JSON format validation. | |
29 | 3. RT verification(auto) | |
30 | """ | |
31 | ||
32 | import os | |
33 | import re | |
34 | import sys | |
35 | import json | |
36 | import time | |
37 | import pytest | |
38 | import platform | |
39 | from copy import deepcopy | |
40 | from time import sleep | |
41 | ||
42 | ||
43 | # Save the Current Working Directory to find configuration files. | |
44 | CWD = os.path.dirname(os.path.realpath(__file__)) | |
45 | sys.path.append(os.path.join(CWD, "../")) | |
46 | sys.path.append(os.path.join(CWD, "../lib/")) | |
47 | ||
48 | # Required to instantiate the topology builder class. | |
49 | ||
50 | # pylint: disable=C0413 | |
51 | # Import topogen and topotest helpers | |
52 | from lib.topotest import version_cmp | |
53 | from lib.topogen import Topogen, get_topogen | |
8db751b8 | 54 | from lib.micronet_compat import Topo |
1b66072c KK |
55 | |
56 | from lib.common_config import ( | |
57 | start_topology, | |
58 | write_test_header, | |
59 | check_address_types, | |
60 | write_test_footer, | |
61 | reset_config_on_routers, | |
62 | verify_rib, | |
63 | step, | |
64 | start_router_daemons, | |
1b66072c KK |
65 | create_static_routes, |
66 | create_vrf_cfg, | |
67 | create_route_maps, | |
68 | create_interface_in_kernel, | |
69 | check_router_status, | |
70 | configure_vxlan, | |
71 | configure_brctl, | |
72 | apply_raw_config, | |
73 | verify_vrf_vni, | |
701a0192 | 74 | verify_cli_json, |
1b66072c KK |
75 | ) |
76 | ||
77 | from lib.topolog import logger | |
78 | from lib.bgp import ( | |
79 | verify_bgp_convergence, | |
80 | create_router_bgp, | |
81 | clear_bgp, | |
82 | verify_best_path_as_per_bgp_attribute, | |
83 | verify_attributes_for_evpn_routes, | |
701a0192 | 84 | verify_evpn_routes, |
1b66072c KK |
85 | ) |
86 | from lib.topojson import build_topo_from_json, build_config_from_json | |
87 | ||
6a1e8951 DS |
88 | pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] |
89 | ||
90 | ||
1b66072c KK |
91 | # Reading the data from JSON File for topology creation |
92 | jsonFile = "{}/evpn_type5_chaos_topo1.json".format(CWD) | |
93 | try: | |
94 | with open(jsonFile, "r") as topoJson: | |
95 | topo = json.load(topoJson) | |
96 | except IOError: | |
97 | assert False, "Could not read file {}".format(jsonFile) | |
98 | ||
99 | # Reading the data from JSON File for topology creation | |
100 | # Global variables | |
101 | TCPDUMP_FILE = "evpn_log.txt" | |
1b66072c KK |
102 | NETWORK1_1 = {"ipv4": "10.1.1.1/32", "ipv6": "10::1/128"} |
103 | NETWORK1_2 = {"ipv4": "40.1.1.1/32", "ipv6": "40::1/128"} | |
104 | NETWORK1_3 = {"ipv4": "40.1.1.2/32", "ipv6": "40::2/128"} | |
105 | NETWORK1_4 = {"ipv4": "40.1.1.3/32", "ipv6": "40::3/128"} | |
106 | NETWORK2_1 = {"ipv4": "20.1.1.1/32", "ipv6": "20::1/128"} | |
107 | NETWORK3_1 = {"ipv4": "30.1.1.1/32", "ipv6": "30::1/128"} | |
108 | NETWORK4_1 = {"ipv4": "100.1.1.1/32 ", "ipv6": "100::100/128"} | |
109 | NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} | |
110 | VNI_1 = 75100 | |
111 | VNI_2 = 75200 | |
112 | VNI_3 = 75300 | |
113 | MAC_1 = "00:80:48:ba:d1:00" | |
114 | MAC_2 = "00:80:48:ba:d1:01" | |
115 | MAC_3 = "00:80:48:ba:d1:02" | |
116 | BRCTL_1 = "br100" | |
117 | BRCTL_2 = "br200" | |
118 | BRCTL_3 = "br300" | |
119 | VXLAN_1 = "vxlan75100" | |
120 | VXLAN_2 = "vxlan75200" | |
121 | VXLAN_3 = "vxlan75300" | |
122 | BRIDGE_INTF1 = "120.0.0.1" | |
123 | BRIDGE_INTF2 = "120.0.0.2" | |
124 | BRIDGE_INTF3 = "120.0.0.3" | |
125 | MULTICAST_MAC1 = "01:00:5e:00:52:02" | |
126 | ||
127 | VXLAN = { | |
128 | "vxlan_name": [VXLAN_1, VXLAN_2, VXLAN_3], | |
129 | "vxlan_id": [75100, 75200, 75300], | |
130 | "dstport": 4789, | |
131 | "local_addr": {"e1": BRIDGE_INTF1, "d1": BRIDGE_INTF2, "d2": BRIDGE_INTF3}, | |
132 | "learning": "no", | |
133 | } | |
134 | BRCTL = { | |
135 | "brctl_name": [BRCTL_1, BRCTL_2, BRCTL_3], | |
136 | "addvxlan": [VXLAN_1, VXLAN_2, VXLAN_3], | |
137 | "vrf": ["RED", "BLUE", "GREEN"], | |
138 | "stp": [0, 0, 0], | |
139 | } | |
140 | ||
141 | ||
142 | class CreateTopo(Topo): | |
143 | """ | |
144 | Test BasicTopo - topology 1 | |
145 | ||
146 | * `Topo`: Topology object | |
147 | """ | |
148 | ||
149 | def build(self, *_args, **_opts): | |
150 | """Build function""" | |
151 | tgen = get_topogen(self) | |
152 | ||
153 | # Building topology from json file | |
154 | build_topo_from_json(tgen, topo) | |
155 | ||
156 | ||
157 | def setup_module(mod): | |
158 | """ | |
159 | Sets up the pytest environment | |
160 | ||
161 | * `mod`: module name | |
162 | """ | |
163 | ||
164 | global topo | |
165 | testsuite_run_time = time.asctime(time.localtime(time.time())) | |
166 | logger.info("Testsuite start time: {}".format(testsuite_run_time)) | |
167 | logger.info("=" * 40) | |
168 | ||
169 | logger.info("Running setup_module to create topology") | |
170 | ||
171 | # This function initiates the topology build with Topogen... | |
172 | tgen = Topogen(CreateTopo, mod.__name__) | |
173 | # ... and here it calls Mininet initialization functions. | |
174 | ||
175 | # Starting topology, create tmp files which are loaded to routers | |
176 | # to start deamons and then start routers | |
177 | start_topology(tgen) | |
178 | ||
179 | # Creating configuration from JSON | |
180 | build_config_from_json(tgen, topo) | |
181 | ||
701a0192 | 182 | if version_cmp(platform.release(), "4.19") < 0: |
183 | error_msg = ( | |
184 | 'EVPN tests will not run (have kernel "{}", ' | |
185 | "but it requires >= 4.19)".format(platform.release()) | |
186 | ) | |
1b66072c KK |
187 | pytest.skip(error_msg) |
188 | ||
189 | global BGP_CONVERGENCE | |
190 | global ADDR_TYPES | |
191 | ADDR_TYPES = check_address_types() | |
192 | ||
193 | BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) | |
194 | assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( | |
195 | BGP_CONVERGENCE | |
196 | ) | |
197 | ||
198 | logger.info("Pre-requisite config for testsuite") | |
199 | prerequisite_config_for_test_suite(tgen) | |
200 | ||
201 | logger.info("Running setup_module() done") | |
202 | ||
203 | ||
204 | def teardown_module(): | |
205 | """Teardown the pytest environment""" | |
206 | ||
207 | logger.info("Running teardown_module to delete topology") | |
208 | ||
209 | tgen = get_topogen() | |
210 | ||
211 | # Stop toplogy and Remove tmp files | |
212 | tgen.stop_topology() | |
213 | ||
214 | logger.info( | |
215 | "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) | |
216 | ) | |
217 | logger.info("=" * 40) | |
218 | ||
219 | ||
220 | ##################################################### | |
221 | # | |
222 | # Testcases | |
223 | # | |
224 | ##################################################### | |
225 | ||
226 | ||
227 | def prerequisite_config_for_test_suite(tgen): | |
228 | """ | |
229 | API to do prerequisite config for testsuite | |
230 | ||
231 | parameters: | |
232 | ----------- | |
233 | * `tgen`: topogen object | |
234 | """ | |
235 | ||
236 | step("Configure vxlan, bridge interface") | |
237 | for dut in ["e1", "d1", "d2"]: | |
238 | step("[DUT: ]Configure vxlan") | |
239 | vxlan_input = { | |
240 | dut: { | |
241 | "vxlan": [ | |
242 | { | |
243 | "vxlan_name": VXLAN["vxlan_name"], | |
244 | "vxlan_id": VXLAN["vxlan_id"], | |
245 | "dstport": VXLAN["dstport"], | |
246 | "local_addr": VXLAN["local_addr"][dut], | |
247 | "learning": VXLAN["learning"], | |
248 | } | |
249 | ] | |
250 | } | |
251 | } | |
252 | ||
253 | result = configure_vxlan(tgen, vxlan_input) | |
254 | assert result is True, "Testcase {} :Failed \n Error: {}".format( | |
255 | tc_name, result | |
256 | ) | |
257 | ||
258 | step("Configure bridge interface") | |
259 | brctl_input = { | |
260 | dut: { | |
261 | "brctl": [ | |
262 | { | |
263 | "brctl_name": BRCTL["brctl_name"], | |
264 | "addvxlan": BRCTL["addvxlan"], | |
265 | "vrf": BRCTL["vrf"], | |
266 | "stp": BRCTL["stp"], | |
267 | } | |
268 | ] | |
269 | } | |
270 | } | |
271 | result = configure_brctl(tgen, topo, brctl_input) | |
272 | assert result is True, "Testcase {} :Failed \n Error: {}".format( | |
273 | tc_name, result | |
274 | ) | |
275 | ||
276 | step("Configure default routes") | |
277 | add_default_routes(tgen) | |
278 | ||
279 | ||
280 | def add_default_routes(tgen): | |
281 | """ | |
282 | API to do prerequisite config for testsuite | |
283 | ||
284 | parameters: | |
285 | ----------- | |
286 | * `tgen`: topogen object | |
287 | """ | |
288 | ||
289 | step("Add default routes..") | |
290 | ||
291 | default_routes = { | |
292 | "e1": { | |
293 | "static_routes": [ | |
294 | { | |
295 | "network": "{}/32".format(VXLAN["local_addr"]["d1"]), | |
296 | "next_hop": topo["routers"]["d1"]["links"]["e1-link1"][ | |
297 | "ipv4" | |
298 | ].split("/")[0], | |
299 | }, | |
300 | { | |
301 | "network": "{}/32".format(VXLAN["local_addr"]["d2"]), | |
302 | "next_hop": topo["routers"]["d2"]["links"]["e1-link1"][ | |
303 | "ipv4" | |
304 | ].split("/")[0], | |
305 | }, | |
306 | ] | |
307 | }, | |
308 | "d1": { | |
309 | "static_routes": [ | |
310 | { | |
311 | "network": "{}/32".format(VXLAN["local_addr"]["e1"]), | |
312 | "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][ | |
313 | "ipv4" | |
314 | ].split("/")[0], | |
315 | }, | |
316 | { | |
317 | "network": "{}/32".format(VXLAN["local_addr"]["d2"]), | |
318 | "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][ | |
319 | "ipv4" | |
320 | ].split("/")[0], | |
321 | }, | |
322 | ] | |
323 | }, | |
324 | "d2": { | |
325 | "static_routes": [ | |
326 | { | |
327 | "network": "{}/32".format(VXLAN["local_addr"]["d1"]), | |
328 | "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][ | |
329 | "ipv4" | |
330 | ].split("/")[0], | |
331 | }, | |
332 | { | |
333 | "network": "{}/32".format(VXLAN["local_addr"]["e1"]), | |
334 | "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][ | |
335 | "ipv4" | |
336 | ].split("/")[0], | |
337 | }, | |
338 | ] | |
339 | }, | |
340 | } | |
341 | ||
342 | result = create_static_routes(tgen, default_routes) | |
343 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
344 | ||
345 | ||
346 | def test_verify_overlay_index_p1(request): | |
347 | """ | |
348 | In absence of an overlay index all IP-Prefixes(RT-5) | |
349 | are advertised with default values for below parameters: | |
350 | --> Ethernet Tag ID = GW IP address = ESI=0 | |
351 | """ | |
352 | ||
353 | tgen = get_topogen() | |
354 | tc_name = request.node.name | |
355 | write_test_header(tc_name) | |
356 | check_router_status(tgen) | |
357 | reset_config_on_routers(tgen) | |
358 | add_default_routes(tgen) | |
359 | ||
360 | if tgen.routers_have_failure(): | |
361 | pytest.skip(tgen.errors) | |
362 | ||
363 | step("Following steps are taken care in base config:") | |
364 | step( | |
365 | "Configure BGP neighborship for both address families" | |
366 | "(IPv4 & IPv6) between Edge-1 and VFN routers(R1 and R2)" | |
367 | ) | |
368 | step( | |
369 | "Advertise prefixes from VNF routers R1 and R2 in associated " | |
370 | "VRFs for both address-family." | |
371 | ) | |
372 | step("Advertise VRF routes as in EVPN address family from Edge-1 " "router.") | |
373 | ||
374 | for addr_type in ADDR_TYPES: | |
375 | input_dict_1 = { | |
376 | "r1": { | |
377 | "static_routes": [ | |
378 | { | |
379 | "network": NETWORK1_1[addr_type], | |
380 | "next_hop": NEXT_HOP_IP[addr_type], | |
381 | "vrf": "RED", | |
382 | } | |
383 | ] | |
384 | }, | |
385 | "r2": { | |
386 | "static_routes": [ | |
387 | { | |
388 | "network": NETWORK2_1[addr_type], | |
389 | "next_hop": NEXT_HOP_IP[addr_type], | |
390 | "vrf": "BLUE", | |
391 | }, | |
392 | { | |
393 | "network": NETWORK3_1[addr_type], | |
394 | "next_hop": NEXT_HOP_IP[addr_type], | |
395 | "vrf": "GREEN", | |
701a0192 | 396 | }, |
1b66072c | 397 | ] |
701a0192 | 398 | }, |
1b66072c KK |
399 | } |
400 | ||
401 | result = create_static_routes(tgen, input_dict_1) | |
402 | assert result is True, "Testcase {} : Failed \n Error: {}".format( | |
403 | tc_name, result | |
404 | ) | |
405 | ||
406 | step("Verify: Prefixes are received in all VRFs on Edge-1 router.") | |
407 | ||
408 | for addr_type in ADDR_TYPES: | |
409 | input_routes = {key: topo["routers"][key] for key in ["r1"]} | |
410 | result = verify_rib(tgen, addr_type, "e1", input_routes) | |
411 | assert result is True, "Testcase {} :Failed \n Error: {}".format( | |
412 | tc_name, result | |
413 | ) | |
414 | ||
415 | for addr_type in ADDR_TYPES: | |
416 | input_routes = {key: topo["routers"][key] for key in ["r2"]} | |
417 | result = verify_rib(tgen, addr_type, "e1", input_routes) | |
418 | assert result is True, "Testcase {} :Failed \n Error: {}".format( | |
419 | tc_name, result | |
420 | ) | |
421 | ||
422 | step( | |
423 | "Verify that EVPN routes, received on DCG-1 and DCG-2 do not " | |
424 | "carry any overlay index and these indexes are set to default " | |
425 | "value=0. " | |
426 | ) | |
427 | ||
428 | for addr_type in ADDR_TYPES: | |
429 | input_routes = {key: topo["routers"][key] for key in ["r1"]} | |
430 | ||
431 | result = verify_attributes_for_evpn_routes( | |
432 | tgen, topo, "d1", input_routes, ethTag=0 | |
433 | ) | |
434 | assert result is True, "Testcase {} :Failed \n Error: {}".format( | |
435 | tc_name, result | |
436 | ) | |
437 | ||
438 | result = verify_attributes_for_evpn_routes( | |
439 | tgen, topo, "d2", input_routes, ethTag=0 | |
440 | ) | |
441 | assert result is True, "Testcase {} :Failed \n Error: {}".format( | |
442 | tc_name, result | |
443 | ) | |
444 | ||
445 | write_test_footer(tc_name) | |
446 | ||
447 | ||
448 | def test_evpn_cli_json_available_p1(request): | |
449 | """ | |
450 | EVPN CLI output and JSON format validation. | |
451 | """ | |
452 | ||
453 | tgen = get_topogen() | |
454 | tc_name = request.node.name | |
455 | write_test_header(tc_name) | |
456 | check_router_status(tgen) | |
457 | reset_config_on_routers(tgen) | |
458 | add_default_routes(tgen) | |
459 | ||
460 | if tgen.routers_have_failure(): | |
461 | pytest.skip(tgen.errors) | |
462 | ||
463 | step("Need to verify below CLIs and associated JSON format " "outputs:") | |
464 | ||
465 | input_dict = { | |
466 | "e1": { | |
467 | "cli": [ | |
468 | "show evpn vni detail", | |
469 | "show bgp l2vpn evpn all overlay", | |
701a0192 | 470 | "show bgp l2vpn evpn vni", |
1b66072c KK |
471 | ] |
472 | } | |
473 | } | |
474 | ||
475 | result = verify_cli_json(tgen, input_dict) | |
476 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
477 | ||
478 | write_test_footer(tc_name) | |
479 | ||
480 | ||
481 | def test_RT_verification_auto_p0(request): | |
482 | """ | |
483 | RT verification(auto) | |
484 | """ | |
485 | ||
486 | tgen = get_topogen() | |
487 | tc_name = request.node.name | |
488 | write_test_header(tc_name) | |
489 | check_router_status(tgen) | |
490 | reset_config_on_routers(tgen) | |
491 | add_default_routes(tgen) | |
492 | ||
493 | if tgen.routers_have_failure(): | |
494 | pytest.skip(tgen.errors) | |
495 | ||
496 | step( | |
497 | "Advertise overlapping prefixes from VNFs R1 and R2 in all VRFs " | |
498 | "RED, GREEN and BLUE 100.1.1.1/32 and 100::100/128" | |
499 | ) | |
500 | ||
501 | for addr_type in ADDR_TYPES: | |
502 | input_dict_1 = { | |
503 | "r1": { | |
504 | "static_routes": [ | |
505 | { | |
506 | "network": NETWORK4_1[addr_type], | |
507 | "next_hop": NEXT_HOP_IP[addr_type], | |
508 | "vrf": "RED", | |
509 | } | |
510 | ] | |
511 | }, | |
512 | "r2": { | |
513 | "static_routes": [ | |
514 | { | |
515 | "network": NETWORK4_1[addr_type], | |
516 | "next_hop": NEXT_HOP_IP[addr_type], | |
517 | "vrf": "BLUE", | |
518 | }, | |
519 | { | |
520 | "network": NETWORK4_1[addr_type], | |
521 | "next_hop": NEXT_HOP_IP[addr_type], | |
522 | "vrf": "GREEN", | |
701a0192 | 523 | }, |
1b66072c | 524 | ] |
701a0192 | 525 | }, |
1b66072c KK |
526 | } |
527 | ||
528 | result = create_static_routes(tgen, input_dict_1) | |
529 | assert result is True, "Testcase {} : Failed \n Error: {}".format( | |
530 | tc_name, result | |
531 | ) | |
532 | ||
533 | step( | |
534 | "Verify that Edge-1 receives same prefixes in all 3 VRFs via " | |
535 | "corresponding next-hop in associated VRF sh bgp vrf all" | |
536 | ) | |
537 | ||
538 | for addr_type in ADDR_TYPES: | |
539 | input_routes = { | |
540 | "r1": { | |
541 | "static_routes": [ | |
542 | { | |
543 | "network": NETWORK4_1[addr_type], | |
544 | "next_hop": NEXT_HOP_IP[addr_type], | |
545 | "vrf": "RED", | |
546 | } | |
547 | ] | |
548 | }, | |
549 | "r2": { | |
550 | "static_routes": [ | |
551 | { | |
552 | "network": NETWORK4_1[addr_type], | |
553 | "next_hop": NEXT_HOP_IP[addr_type], | |
554 | "vrf": "BLUE", | |
555 | }, | |
556 | { | |
557 | "network": NETWORK4_1[addr_type], | |
558 | "next_hop": NEXT_HOP_IP[addr_type], | |
559 | "vrf": "GREEN", | |
560 | }, | |
561 | ] | |
562 | }, | |
563 | } | |
564 | ||
565 | result = verify_rib(tgen, addr_type, "e1", input_routes) | |
566 | assert result is True, "Testcase {} : Failed \n Error: {}".format( | |
567 | tc_name, result | |
568 | ) | |
569 | ||
570 | step( | |
571 | "Configure 4-byte local AS number on Edge-1 and establish EVPN " | |
572 | "neighborship with DCG-1 & DCG-2." | |
573 | ) | |
574 | ||
575 | topo_local = deepcopy(topo) | |
576 | ||
577 | step("Delete BGP config for vrf RED.") | |
578 | ||
579 | input_dict_vni = { | |
580 | "e1": { | |
581 | "vrfs": [ | |
582 | {"name": "RED", "no_vni": VNI_1}, | |
583 | {"name": "BLUE", "no_vni": VNI_2}, | |
584 | {"name": "GREEN", "no_vni": VNI_3}, | |
585 | ] | |
586 | } | |
587 | } | |
588 | result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) | |
589 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
590 | ||
591 | input_dict_2 = {} | |
592 | for dut in ["e1"]: | |
593 | temp = {dut: {"bgp": []}} | |
594 | input_dict_2.update(temp) | |
595 | ||
596 | INDEX = [0, 1, 2, 3] | |
597 | VRFS = ["RED", "BLUE", "GREEN", None] | |
598 | AS_NUM = [100, 100, 100, 100] | |
599 | ||
600 | for index, vrf, as_num in zip(INDEX, VRFS, AS_NUM): | |
601 | topo_local["routers"][dut]["bgp"][index]["local_as"] = 4294967293 | |
602 | if vrf: | |
603 | temp[dut]["bgp"].append( | |
604 | {"local_as": as_num, "vrf": vrf, "delete": True} | |
605 | ) | |
606 | else: | |
607 | temp[dut]["bgp"].append({"local_as": as_num, "delete": True}) | |
608 | ||
609 | result = create_router_bgp(tgen, topo, input_dict_2) | |
610 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
611 | ||
612 | result = create_router_bgp(tgen, topo_local["routers"]) | |
613 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
614 | ||
615 | input_dict_vni = { | |
616 | "e1": { | |
617 | "vrfs": [ | |
618 | {"name": "RED", "vni": VNI_1}, | |
619 | {"name": "BLUE", "vni": VNI_2}, | |
620 | {"name": "GREEN", "vni": VNI_3}, | |
621 | ] | |
622 | } | |
623 | } | |
624 | result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) | |
625 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
626 | ||
627 | step( | |
628 | "Verify that all overlapping prefixes across different VRFs are " | |
629 | "advertised in EVPN with unique RD value(auto derived)." | |
630 | ) | |
631 | step( | |
632 | "Verify that FRR uses only the lower 2 bytes of ASN+VNI for auto " | |
633 | "derived RT value." | |
634 | ) | |
635 | ||
636 | for addr_type in ADDR_TYPES: | |
637 | input_routes_1 = { | |
638 | "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} | |
639 | } | |
640 | input_routes_2 = { | |
641 | "r2": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "BLUE"}]} | |
642 | } | |
643 | input_routes_3 = { | |
644 | "r2": { | |
645 | "static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "GREEN"}] | |
646 | } | |
647 | } | |
648 | ||
649 | result = verify_attributes_for_evpn_routes( | |
650 | tgen, topo, "e1", input_routes_1, rd="auto", rd_peer="e1" | |
651 | ) | |
652 | assert result is True, "Testcase {} :Failed \n Error: {}".format( | |
653 | tc_name, result | |
654 | ) | |
655 | ||
656 | result = verify_attributes_for_evpn_routes( | |
657 | tgen, topo, "e1", input_routes_1, rt="auto", rt_peer="e1" | |
658 | ) | |
659 | assert result is True, "Testcase {} :Failed \n Error: {}".format( | |
660 | tc_name, result | |
661 | ) | |
662 | ||
663 | result = verify_attributes_for_evpn_routes( | |
664 | tgen, topo, "e1", input_routes_2, rd="auto", rd_peer="e1" | |
665 | ) | |
666 | assert result is True, "Testcase {} :Failed \n Error: {}".format( | |
667 | tc_name, result | |
668 | ) | |
669 | ||
670 | result = verify_attributes_for_evpn_routes( | |
671 | tgen, topo, "e1", input_routes_2, rt="auto", rt_peer="e1" | |
672 | ) | |
673 | assert result is True, "Testcase {} :Failed \n Error: {}".format( | |
674 | tc_name, result | |
675 | ) | |
676 | ||
677 | result = verify_attributes_for_evpn_routes( | |
678 | tgen, topo, "e1", input_routes_3, rd="auto", rd_peer="e1" | |
679 | ) | |
680 | assert result is True, "Testcase {} :Failed \n Error: {}".format( | |
681 | tc_name, result | |
682 | ) | |
683 | ||
684 | result = verify_attributes_for_evpn_routes( | |
685 | tgen, topo, "e1", input_routes_3, rt="auto", rt_peer="e1" | |
686 | ) | |
687 | assert result is True, "Testcase {} :Failed \n Error: {}".format( | |
688 | tc_name, result | |
689 | ) | |
690 | ||
691 | step( | |
692 | "Verify that DCG-1(iBGP peer) automatically imports the prefixes" | |
693 | " from EVPN address-family to respective VRFs." | |
694 | ) | |
695 | step( | |
696 | "Verify if DCG-2(eBGP peer) automatically imports the prefixes " | |
697 | "from EVPN address-family to respective VRFs or not." | |
698 | ) | |
699 | ||
700 | for addr_type in ADDR_TYPES: | |
701 | input_routes = { | |
702 | "r1": { | |
703 | "static_routes": [ | |
704 | { | |
705 | "network": NETWORK4_1[addr_type], | |
706 | "next_hop": NEXT_HOP_IP[addr_type], | |
707 | "vrf": "RED", | |
708 | } | |
709 | ] | |
710 | }, | |
711 | "r2": { | |
712 | "static_routes": [ | |
713 | { | |
714 | "network": NETWORK4_1[addr_type], | |
715 | "next_hop": NEXT_HOP_IP[addr_type], | |
716 | "vrf": "BLUE", | |
717 | }, | |
718 | { | |
719 | "network": NETWORK4_1[addr_type], | |
720 | "next_hop": NEXT_HOP_IP[addr_type], | |
721 | "vrf": "GREEN", | |
722 | }, | |
723 | ] | |
724 | }, | |
725 | } | |
726 | ||
727 | result = verify_rib(tgen, addr_type, "d1", input_routes) | |
728 | assert result is True, "Testcase {} : Failed \n Error: {}".format( | |
729 | tc_name, result | |
730 | ) | |
731 | ||
732 | result = verify_rib(tgen, addr_type, "d2", input_routes) | |
733 | assert result is True, "Testcase {} : Failed \n Error: {}".format( | |
734 | tc_name, result | |
735 | ) | |
736 | ||
737 | step( | |
738 | "Change the VNI number for all 3 VRFs on Edge-1 as:" | |
739 | "RED : 75400, GREEN: 75500, BLUE: 75600" | |
740 | ) | |
741 | ||
742 | input_dict_vni = { | |
743 | "e1": { | |
744 | "vrfs": [ | |
745 | {"name": "RED", "no_vni": VNI_1}, | |
746 | {"name": "BLUE", "no_vni": VNI_2}, | |
747 | {"name": "GREEN", "no_vni": VNI_3}, | |
748 | ] | |
749 | } | |
750 | } | |
751 | result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) | |
752 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
753 | ||
754 | input_dict_vni = { | |
755 | "e1": { | |
756 | "vrfs": [ | |
757 | {"name": "RED", "vni": 75400}, | |
758 | {"name": "BLUE", "vni": 75500}, | |
759 | {"name": "GREEN", "vni": 75600}, | |
760 | ] | |
761 | } | |
762 | } | |
763 | result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) | |
764 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
765 | ||
766 | step("Delete configured vxlan") | |
767 | dut = "e1" | |
768 | vxlan_input = { | |
769 | dut: { | |
770 | "vxlan": [ | |
771 | { | |
772 | "vxlan_name": VXLAN["vxlan_name"], | |
773 | "vxlan_id": VXLAN["vxlan_id"], | |
774 | "dstport": VXLAN["dstport"], | |
775 | "local_addr": VXLAN["local_addr"][dut], | |
776 | "learning": VXLAN["learning"], | |
777 | "delete": True, | |
778 | } | |
779 | ] | |
780 | } | |
781 | } | |
782 | ||
783 | result = configure_vxlan(tgen, vxlan_input) | |
784 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
785 | ||
786 | step("Configured vxlan") | |
787 | VXLAN["vxlan_id"] = [75400, 75500, 75600] | |
788 | vxlan_input = { | |
789 | dut: { | |
790 | "vxlan": [ | |
791 | { | |
792 | "vxlan_name": VXLAN["vxlan_name"], | |
793 | "vxlan_id": VXLAN["vxlan_id"], | |
794 | "dstport": VXLAN["dstport"], | |
795 | "local_addr": VXLAN["local_addr"][dut], | |
796 | "learning": VXLAN["learning"], | |
797 | } | |
798 | ] | |
799 | } | |
800 | } | |
801 | ||
802 | result = configure_vxlan(tgen, vxlan_input) | |
803 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
804 | ||
805 | step("Configure bridge interface") | |
806 | brctl_input = { | |
807 | dut: { | |
808 | "brctl": [ | |
809 | { | |
810 | "brctl_name": BRCTL["brctl_name"], | |
811 | "addvxlan": BRCTL["addvxlan"], | |
812 | "vrf": BRCTL["vrf"], | |
813 | "stp": BRCTL["stp"], | |
814 | } | |
815 | ] | |
816 | } | |
817 | } | |
818 | result = configure_brctl(tgen, topo, brctl_input) | |
819 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
820 | ||
821 | step( | |
822 | "Verify on Edge-1 that auto derived RT value has changed for " | |
823 | "each VRF based on VNI number.." | |
824 | ) | |
825 | ||
826 | input_dict = { | |
827 | "e1": { | |
828 | "vrfs": [ | |
829 | {"RED": {"vni": 75400}}, | |
830 | {"BLUE": {"vni": 75500}}, | |
831 | {"GREEN": {"vni": 75600}}, | |
832 | ] | |
833 | } | |
834 | } | |
835 | ||
836 | result = verify_vrf_vni(tgen, input_dict) | |
837 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
838 | ||
839 | step( | |
840 | "Verify on Edge-1 that auto derived RT value has changed for " | |
841 | "each VRF based on VNI number." | |
842 | ) | |
843 | ||
844 | for addr_type in ADDR_TYPES: | |
845 | input_routes = { | |
846 | "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} | |
847 | } | |
848 | ||
849 | result = verify_attributes_for_evpn_routes( | |
850 | tgen, topo, "e1", input_routes, rt="auto", rt_peer="e1" | |
851 | ) | |
852 | assert result is True, "Testcase {} :Failed \n Error: {}".format( | |
853 | tc_name, result | |
854 | ) | |
855 | ||
856 | step( | |
857 | "Verify on DCG-2 that prefixes are not imported from EVPN " | |
858 | "address-family to VRFs as RT values are different on sending(" | |
859 | "edge-1) and receiving(DCG-2) end." | |
860 | ) | |
861 | ||
862 | for addr_type in ADDR_TYPES: | |
863 | input_routes = { | |
864 | "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} | |
865 | } | |
866 | ||
867 | result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False) | |
868 | assert result is not True, "Testcase {} :Failed \n " | |
869 | "Routes are still present: {}".format(tc_name, result) | |
870 | logger.info("Expected Behavior: {}".format(result)) | |
871 | ||
872 | step( | |
873 | "Revert back to original VNI number for all 3 VRFs on Edge-1 " | |
874 | "as: RED : 75100, GREEN: 75200, BLUE: 75300" | |
875 | ) | |
876 | ||
877 | input_dict_vni = { | |
878 | "e1": { | |
879 | "vrfs": [ | |
880 | {"name": "RED", "no_vni": 75400}, | |
881 | {"name": "BLUE", "no_vni": 75500}, | |
882 | {"name": "GREEN", "no_vni": 75600}, | |
883 | ] | |
884 | } | |
885 | } | |
886 | result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) | |
887 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
888 | ||
889 | input_dict_vni = { | |
890 | "e1": { | |
891 | "vrfs": [ | |
892 | {"name": "RED", "vni": VNI_1}, | |
893 | {"name": "BLUE", "vni": VNI_2}, | |
894 | {"name": "GREEN", "vni": VNI_3}, | |
895 | ] | |
896 | } | |
897 | } | |
898 | result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) | |
899 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
900 | ||
901 | step("Delete configured vxlan") | |
902 | dut = "e1" | |
903 | vxlan_input = { | |
904 | dut: { | |
905 | "vxlan": [ | |
906 | { | |
907 | "vxlan_name": VXLAN["vxlan_name"], | |
908 | "vxlan_id": VXLAN["vxlan_id"], | |
909 | "dstport": VXLAN["dstport"], | |
910 | "local_addr": VXLAN["local_addr"][dut], | |
911 | "learning": VXLAN["learning"], | |
912 | "delete": True, | |
913 | } | |
914 | ] | |
915 | } | |
916 | } | |
917 | result = configure_vxlan(tgen, vxlan_input) | |
918 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
919 | ||
920 | step("Configured vxlan") | |
921 | VXLAN["vxlan_id"] = [75100, 75200, 75300] | |
922 | vxlan_input = { | |
923 | dut: { | |
924 | "vxlan": [ | |
925 | { | |
926 | "vxlan_name": VXLAN["vxlan_name"], | |
927 | "vxlan_id": VXLAN["vxlan_id"], | |
928 | "dstport": VXLAN["dstport"], | |
929 | "local_addr": VXLAN["local_addr"][dut], | |
930 | "learning": VXLAN["learning"], | |
931 | } | |
932 | ] | |
933 | } | |
934 | } | |
935 | result = configure_vxlan(tgen, vxlan_input) | |
936 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
937 | ||
938 | step("Configure bridge interface") | |
939 | brctl_input = { | |
940 | dut: { | |
941 | "brctl": [ | |
942 | { | |
943 | "brctl_name": BRCTL["brctl_name"], | |
944 | "addvxlan": BRCTL["addvxlan"], | |
945 | "vrf": BRCTL["vrf"], | |
946 | "stp": BRCTL["stp"], | |
947 | } | |
948 | ] | |
949 | } | |
950 | } | |
951 | result = configure_brctl(tgen, topo, brctl_input) | |
952 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
953 | ||
954 | step( | |
955 | "Verify on Edge-1 that auto derived RT value has changed for " | |
956 | "each VRF based on VNI number." | |
957 | ) | |
958 | step( | |
959 | "Verify that DCG-1(iBGP peer) automatically imports the prefixes" | |
960 | " from EVPN address-family to respective VRFs." | |
961 | ) | |
962 | ||
963 | for addr_type in ADDR_TYPES: | |
964 | input_routes = { | |
965 | "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} | |
966 | } | |
967 | ||
968 | result = verify_attributes_for_evpn_routes( | |
969 | tgen, topo, "e1", input_routes, rt="auto", rt_peer="e1" | |
970 | ) | |
971 | assert result is True, "Testcase {} :Failed \n Error: {}".format( | |
972 | tc_name, result | |
973 | ) | |
974 | ||
975 | result = verify_rib(tgen, addr_type, "d1", input_routes) | |
976 | assert result is True, "Testcase {} :Failed \n Error: {}".format( | |
977 | tc_name, result | |
978 | ) | |
979 | ||
980 | step("Test with smaller VNI numbers (1-75000)") | |
981 | ||
982 | input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": VNI_1}]}} | |
983 | result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) | |
984 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
985 | ||
986 | input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "vni": 111}]}} | |
987 | result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) | |
988 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
989 | ||
990 | step( | |
991 | "Verify that DCG-2 receives EVPN prefixes along with auto " | |
992 | "derived RT values(based on smaller VNI numbers)" | |
993 | ) | |
994 | ||
995 | for addr_type in ADDR_TYPES: | |
996 | input_routes_1 = { | |
997 | "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} | |
998 | } | |
999 | ||
1000 | result = verify_attributes_for_evpn_routes( | |
1001 | tgen, topo, "d2", input_routes_1, rt="auto", rt_peer="e1", expected=False | |
1002 | ) | |
1003 | assert result is not True, "Testcase {} :Failed \n " | |
1004 | "Malfaromed Auto-RT value accepted: {}".format(tc_name, result) | |
1005 | logger.info("Expected Behavior: {}".format(result)) | |
1006 | ||
1007 | step("Configure VNI number more than boundary limit (16777215)") | |
1008 | ||
1009 | input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": 111}]}} | |
1010 | result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) | |
1011 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
1012 | ||
1013 | input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "vni": 16777215}]}} | |
1014 | result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) | |
1015 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
1016 | ||
1017 | step("CLI error for malformed VNI.") | |
1018 | input_dict = { | |
1019 | "e1": { | |
1020 | "vrfs": [{"RED": {"vni": 16777215, "routerMac": "None", "state": "Down"}}] | |
1021 | } | |
1022 | } | |
1023 | ||
1024 | result = verify_vrf_vni(tgen, input_dict) | |
1025 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
1026 | ||
1027 | for addr_type in ADDR_TYPES: | |
1028 | input_routes_1 = { | |
1029 | "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} | |
1030 | } | |
1031 | ||
1032 | result = verify_attributes_for_evpn_routes( | |
1033 | tgen, topo, "d2", input_routes_1, rt="auto", rt_peer="e1", expected=False | |
1034 | ) | |
1035 | assert result is not True, "Testcase {} :Failed \n " | |
1036 | "Malfaromed Auto-RT value accepted: {}".format(tc_name, result) | |
1037 | logger.info("Expected Behavior: {}".format(result)) | |
1038 | ||
1039 | step("Un-configure VNI number more than boundary limit (16777215)") | |
1040 | ||
1041 | input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": 16777215}]}} | |
1042 | result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) | |
1043 | assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) | |
1044 | ||
1045 | write_test_footer(tc_name) | |
1046 | ||
1047 | ||
1048 | if __name__ == "__main__": | |
1049 | args = ["-s"] + sys.argv[1:] | |
1050 | sys.exit(pytest.main(args)) |