2 # SPDX-License-Identifier: ISC
5 # Copyright (c) 2019 by VMware, Inc. ("VMware")
6 # Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
7 # ("NetDEF") in this file.
12 Following tests are covered to test ecmp functionality on EBGP.
13 1. Verify routes installed as per maximum-paths configuration (8/16/32)
14 2. Disable/Shut selected paths nexthops and verify other next are installed in
15 the RIB of DUT. Enable interfaces and verify RIB count.
16 3. Verify BGP table and RIB in DUT after clear BGP routes and neighbors.
17 4. Verify routes are cleared from BGP and RIB table of DUT when
18 redistribute static configuration is removed.
19 5. Shut BGP neighbors one by one and verify BGP and routing table updated
21 6. Delete static routes and verify routers are cleared from BGP table and RIB
23 7. Verify routes are cleared from BGP and RIB table of DUT when advertise
24 network configuration is removed.
31 # Save the Current Working Directory to find configuration files.
32 CWD
= os
.path
.dirname(os
.path
.realpath(__file__
))
33 sys
.path
.append(os
.path
.join(CWD
, "../"))
34 sys
.path
.append(os
.path
.join(CWD
, "../../"))
36 # pylint: disable=C0413
37 # Import topogen and topotest helpers
38 from lib
.topogen
import Topogen
, get_topogen
40 from lib
.common_config
import (
48 reset_config_on_routers
,
49 required_linux_kernel_version
,
51 from lib
.topolog
import logger
52 from lib
.bgp
import verify_bgp_convergence
, create_router_bgp
, clear_bgp
53 from lib
.topojson
import build_config_from_json
56 pytestmark
= [pytest
.mark
.bgpd
, pytest
.mark
.staticd
]
60 NEXT_HOPS
= {"ipv4": [], "ipv6": []}
63 NETWORK
= {"ipv4": "11.0.20.1/32", "ipv6": "1::/64"}
64 NEXT_HOP_IP
= {"ipv4": "10.0.0.1", "ipv6": "fd00::1"}
65 BGP_CONVERGENCE
= False
68 def setup_module(mod
):
70 Sets up the pytest environment.
74 global NEXT_HOPS
, INTF_LIST_R3
, INTF_LIST_R2
, TEST_STATIC
77 # Required linux kernel version for this suite to run.
78 result
= required_linux_kernel_version("4.15")
79 if result
is not True:
80 pytest
.skip("Kernel requirements are not met, kernel version should be >=4.15")
82 testsuite_run_time
= time
.asctime(time
.localtime(time
.time()))
83 logger
.info("Testsuite start time: {}".format(testsuite_run_time
))
86 logger
.info("Running setup_module to create topology")
88 # This function initiates the topology build with Topogen...
89 json_file
= "{}/ebgp_ecmp_topo2.json".format(CWD
)
90 tgen
= Topogen(json_file
, mod
.__name
__)
94 # Starting topology, create tmp files which are loaded to routers
95 # to start daemons and then start routers
98 # Creating configuration from JSON
99 build_config_from_json(tgen
, topo
)
101 # Don't run this test if we have any failure.
102 if tgen
.routers_have_failure():
103 pytest
.skip(tgen
.errors
)
106 # Api call verify whether BGP is converged
107 ADDR_TYPES
= check_address_types()
109 BGP_CONVERGENCE
= verify_bgp_convergence(tgen
, topo
)
110 assert BGP_CONVERGENCE
is True, "setup_module :Failed \n Error:" " {}".format(
115 val
for links
, val
in topo
["routers"]["r2"]["links"].items() if "r3" in links
117 for adt
in ADDR_TYPES
:
118 NEXT_HOPS
[adt
] = [val
[adt
].split("/")[0] for val
in link_data
]
120 NEXT_HOPS
[adt
] = sorted(NEXT_HOPS
[adt
], key
=lambda x
: int(x
.split(".")[2]))
122 NEXT_HOPS
[adt
] = sorted(
123 NEXT_HOPS
[adt
], key
=lambda x
: int(x
.split(":")[-3], 16)
126 INTF_LIST_R2
= [val
["interface"].split("/")[0] for val
in link_data
]
127 INTF_LIST_R2
= sorted(INTF_LIST_R2
, key
=lambda x
: int(x
.split("eth")[1]))
130 val
for links
, val
in topo
["routers"]["r3"]["links"].items() if "r2" in links
132 INTF_LIST_R3
= [val
["interface"].split("/")[0] for val
in link_data
]
133 INTF_LIST_R3
= sorted(INTF_LIST_R3
, key
=lambda x
: int(x
.split("eth")[1]))
135 # STATIC_ROUTE = True
136 logger
.info("Running setup_module() done")
139 def teardown_module():
141 Teardown the pytest environment.
146 logger
.info("Running teardown_module to delete topology")
150 # Stop toplogy and Remove tmp files
154 def static_or_nw(tgen
, topo
, tc_name
, test_type
, dut
):
156 if test_type
== "redist_static":
157 input_dict_static
= {
160 {"network": NETWORK
["ipv4"], "next_hop": NEXT_HOP_IP
["ipv4"]},
161 {"network": NETWORK
["ipv6"], "next_hop": NEXT_HOP_IP
["ipv6"]},
165 logger
.info("Configuring static route on router %s", dut
)
166 result
= create_static_routes(tgen
, input_dict_static
)
167 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
176 "unicast": {"redistribute": [{"redist_type": "static"}]}
179 "unicast": {"redistribute": [{"redist_type": "static"}]}
186 logger
.info("Configuring redistribute static route on router %s", dut
)
187 result
= create_router_bgp(tgen
, topo
, input_dict_2
)
188 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
192 elif test_type
== "advertise_nw":
199 "advertise_networks": [{"network": NETWORK
["ipv4"]}]
204 "advertise_networks": [{"network": NETWORK
["ipv6"]}]
213 "Advertising networks %s %s from router %s",
218 result
= create_router_bgp(tgen
, topo
, input_dict_nw
)
219 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
224 @pytest.mark
.parametrize("ecmp_num", ["8", "16", "32"])
225 @pytest.mark
.parametrize("test_type", ["redist_static", "advertise_nw"])
226 def test_modify_ecmp_max_paths(request
, ecmp_num
, test_type
):
228 Verify routes installed as per maximum-paths
229 configuration (8/16/32).
232 tc_name
= request
.node
.name
233 write_test_header(tc_name
)
236 reset_config_on_routers(tgen
)
238 static_or_nw(tgen
, topo
, tc_name
, test_type
, "r2")
263 logger
.info("Configuring bgp maximum-paths %s on router r3", ecmp_num
)
264 result
= create_router_bgp(tgen
, topo
, input_dict
)
265 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
267 # Verifying RIB routes
271 for addr_type
in ADDR_TYPES
:
272 input_dict_1
= {"r3": {"static_routes": [{"network": NETWORK
[addr_type
]}]}}
274 logger
.info("Verifying %s routes on r3", addr_type
)
276 # Only test the count of nexthops; the actual nexthop addresses
277 # can vary and are not deterministic.
284 next_hop
=NEXT_HOPS
[addr_type
][: int(ecmp_num
)],
289 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
293 write_test_footer(tc_name
)
296 @pytest.mark
.parametrize("ecmp_num", ["8", "16", "32"])
297 @pytest.mark
.parametrize("test_type", ["redist_static", "advertise_nw"])
298 def test_ecmp_after_clear_bgp(request
, ecmp_num
, test_type
):
299 """Verify BGP table and RIB in DUT after clear BGP routes and neighbors"""
301 tc_name
= request
.node
.name
302 write_test_header(tc_name
)
305 reset_config_on_routers(tgen
)
307 # Verifying RIB routes
311 static_or_nw(tgen
, topo
, tc_name
, test_type
, "r2")
312 for addr_type
in ADDR_TYPES
:
313 input_dict_1
= {"r3": {"static_routes": [{"network": NETWORK
[addr_type
]}]}}
315 logger
.info("Verifying %s routes on r3", addr_type
)
321 next_hop
=NEXT_HOPS
[addr_type
][: int(ecmp_num
)],
324 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
329 for addr_type
in ADDR_TYPES
:
330 clear_bgp(tgen
, addr_type
, dut
)
332 # Verify BGP convergence
333 result
= verify_bgp_convergence(tgen
, topo
)
334 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
336 for addr_type
in ADDR_TYPES
:
337 input_dict_1
= {"r3": {"static_routes": [{"network": NETWORK
[addr_type
]}]}}
338 logger
.info("Verifying %s routes on r3", addr_type
)
344 next_hop
=NEXT_HOPS
[addr_type
][: int(ecmp_num
)],
347 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
351 write_test_footer(tc_name
)
354 def test_ecmp_remove_redistribute_static(request
):
355 """Verify routes are cleared from BGP and RIB table of DUT when
356 redistribute static configuration is removed."""
358 tc_name
= request
.node
.name
359 write_test_header(tc_name
)
362 reset_config_on_routers(tgen
)
363 static_or_nw(tgen
, topo
, tc_name
, "redist_static", "r2")
364 for addr_type
in ADDR_TYPES
:
366 # Verifying RIB routes
369 input_dict_1
= {"r3": {"static_routes": [{"network": NETWORK
[addr_type
]}]}}
371 logger
.info("Verifying %s routes on r3", addr_type
)
377 next_hop
=NEXT_HOPS
[addr_type
],
380 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
390 "redistribute": [{"redist_type": "static", "delete": True}]
395 "redistribute": [{"redist_type": "static", "delete": True}]
403 logger
.info("Remove redistribute static")
404 result
= create_router_bgp(tgen
, topo
, input_dict_2
)
405 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
407 for addr_type
in ADDR_TYPES
:
409 # Verifying RIB routes
412 input_dict_1
= {"r3": {"static_routes": [{"network": NETWORK
[addr_type
]}]}}
414 logger
.info("Verifying %s routes on r3 are deleted", addr_type
)
426 ), "Testcase {} : Failed \n Expected: Routes still present in {} RIB. Found: {}".format(
430 logger
.info("Enable redistribute static")
435 "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
436 "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
441 result
= create_router_bgp(tgen
, topo
, input_dict_2
)
442 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
444 for addr_type
in ADDR_TYPES
:
445 # Verifying RIB routes
448 input_dict_1
= {"r3": {"static_routes": [{"network": NETWORK
[addr_type
]}]}}
449 logger
.info("Verifying %s routes on r3", addr_type
)
455 next_hop
=NEXT_HOPS
[addr_type
],
458 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
462 write_test_footer(tc_name
)
465 @pytest.mark
.parametrize("test_type", ["redist_static", "advertise_nw"])
466 def test_ecmp_shut_bgp_neighbor(request
, test_type
):
467 """Shut BGP neighbors one by one and verify BGP and routing table updated
468 accordingly in DUT"""
470 tc_name
= request
.node
.name
471 write_test_header(tc_name
)
474 logger
.info(INTF_LIST_R2
)
475 # Verifying RIB routes
479 reset_config_on_routers(tgen
)
480 static_or_nw(tgen
, topo
, tc_name
, test_type
, "r2")
482 for addr_type
in ADDR_TYPES
:
483 input_dict
= {"r3": {"static_routes": [{"network": NETWORK
[addr_type
]}]}}
485 logger
.info("Verifying %s routes on r3", addr_type
)
491 next_hop
=NEXT_HOPS
[addr_type
],
494 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
498 for intf_num
in range(len(INTF_LIST_R2
) + 1, 16):
499 intf_val
= INTF_LIST_R2
[intf_num
: intf_num
+ 16]
501 input_dict_1
= {"r2": {"interface_list": [intf_val
], "status": "down"}}
502 logger
.info("Shutting down neighbor interface {} on r2".format(intf_val
))
503 result
= interface_status(tgen
, topo
, input_dict_1
)
504 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
508 for addr_type
in ADDR_TYPES
:
509 if intf_num
+ 16 < 32:
510 check_hops
= NEXT_HOPS
[addr_type
]
514 input_dict
= {"r3": {"static_routes": [{"network": NETWORK
[addr_type
]}]}}
515 logger
.info("Verifying %s routes on r3", addr_type
)
517 tgen
, addr_type
, dut
, input_dict
, next_hop
=check_hops
, protocol
=protocol
519 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
523 input_dict_1
= {"r2": {"interface_list": INTF_LIST_R2
, "status": "up"}}
525 logger
.info("Enabling all neighbor interface {} on r2")
526 result
= interface_status(tgen
, topo
, input_dict_1
)
527 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
529 static_or_nw(tgen
, topo
, tc_name
, test_type
, "r2")
530 for addr_type
in ADDR_TYPES
:
531 input_dict
= {"r3": {"static_routes": [{"network": NETWORK
[addr_type
]}]}}
533 logger
.info("Verifying %s routes on r3", addr_type
)
539 next_hop
=NEXT_HOPS
[addr_type
],
542 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
546 write_test_footer(tc_name
)
549 def test_ecmp_remove_static_route(request
):
551 Delete static routes and verify routers are cleared from BGP table,
555 tc_name
= request
.node
.name
556 write_test_header(tc_name
)
559 # Verifying RIB routes
563 reset_config_on_routers(tgen
)
565 static_or_nw(tgen
, topo
, tc_name
, "redist_static", "r2")
566 for addr_type
in ADDR_TYPES
:
567 input_dict_1
= {"r3": {"static_routes": [{"network": NETWORK
[addr_type
]}]}}
569 logger
.info("Verifying %s routes on r3", addr_type
)
575 next_hop
=NEXT_HOPS
[addr_type
],
578 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
582 for addr_type
in ADDR_TYPES
:
587 "network": NETWORK
[addr_type
],
588 "next_hop": NEXT_HOP_IP
[addr_type
],
595 logger
.info("Remove static routes")
596 result
= create_static_routes(tgen
, input_dict_2
)
597 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
601 logger
.info("Verifying %s routes on r3 are removed", addr_type
)
613 ), "Testcase {} : Failed \n Expected: Routes still present in {} RIB. Found: {}".format(
617 for addr_type
in ADDR_TYPES
:
618 # Enable static routes
622 {"network": NETWORK
[addr_type
], "next_hop": NEXT_HOP_IP
[addr_type
]}
627 logger
.info("Enable static route")
628 result
= create_static_routes(tgen
, input_dict_4
)
629 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
633 logger
.info("Verifying %s routes on r3", addr_type
)
639 next_hop
=NEXT_HOPS
[addr_type
],
642 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
647 def test_ecmp_remove_nw_advertise(request
):
649 Verify routes are cleared from BGP and RIB table of DUT,
650 when advertise network configuration is removed
653 tc_name
= request
.node
.name
654 write_test_header(tc_name
)
657 # Verifying RIB routes
661 reset_config_on_routers(tgen
)
662 static_or_nw(tgen
, topo
, tc_name
, "advertise_nw", "r2")
663 for addr_type
in ADDR_TYPES
:
664 input_dict
= {"r3": {"static_routes": [{"network": NETWORK
[addr_type
]}]}}
666 logger
.info("Verifying %s routes on r3", addr_type
)
672 next_hop
=NEXT_HOPS
[addr_type
],
675 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
685 "advertise_networks": [
686 {"network": NETWORK
["ipv4"], "delete": True}
692 "advertise_networks": [
693 {"network": NETWORK
["ipv6"], "delete": True}
702 logger
.info("Withdraw advertised networks")
703 result
= create_router_bgp(tgen
, topo
, input_dict_3
)
704 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
706 for addr_type
in ADDR_TYPES
:
707 input_dict
= {"r3": {"static_routes": [{"network": NETWORK
[addr_type
]}]}}
709 logger
.info("Verifying %s routes on r3", addr_type
)
721 ), "Testcase {} : Failed \n Expected: Routes still present in {} RIB. Found: {}".format(
725 static_or_nw(tgen
, topo
, tc_name
, "advertise_nw", "r2")
726 for addr_type
in ADDR_TYPES
:
727 input_dict
= {"r3": {"static_routes": [{"network": NETWORK
[addr_type
]}]}}
728 logger
.info("Verifying %s routes on r3", addr_type
)
734 next_hop
=NEXT_HOPS
[addr_type
],
737 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
741 write_test_footer(tc_name
)
744 if __name__
== "__main__":
745 args
= ["-s"] + sys
.argv
[1:]
746 sys
.exit(pytest
.main(args
))