From ecbca1ae1be5e4e3f68bc712696f28d01909803c Mon Sep 17 00:00:00 2001 From: Pooja Jagadeesh Doijode Date: Wed, 26 Jun 2024 17:34:44 -0700 Subject: [PATCH] tests: Updated topotest and documentation Added topotest and documentation for BGP wide GR configurations Signed-off-by: Pooja Jagadeesh Doijode --- doc/user/bgp.rst | 46 +++ .../test_bgp_gr_functionality_topo1-4.py | 301 ++++++++++++++++++ .../test_bgp_gr_notification.py | 10 +- tests/topotests/lib/bgp.py | 77 ++++- 4 files changed, 412 insertions(+), 22 deletions(-) diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 150a915e3a..034d579568 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -1080,6 +1080,52 @@ Default global mode is helper and default peer per mode is inherit from global. If per peer mode is configured, the GR mode of this particular peer will override the global mode. +.. _bgp-GR-config-mode-cmd: + +BGP GR Config Mode Commands +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. clicmd:: bgp graceful-restart + + This command will enable BGP graceful restart functionality for all BGP instances. + +.. clicmd:: bgp graceful-restart-disable + + This command will disable both the functionality graceful restart and helper + mode for all BGP instances + +.. clicmd:: bgp graceful-restart select-defer-time (0-3600) + + This is command, will set deferral time to value specified. + +.. clicmd:: bgp graceful-restart rib-stale-time (1-3600) + + This is command, will set the time for which stale routes are kept in RIB. + +.. clicmd:: bgp graceful-restart restart-time (0-4095) + + Set the time to wait to delete stale routes before a BGP open message + is received. + + Using with Long-lived Graceful Restart capability, this is recommended + setting this timer to 0 and control stale routes with + ``bgp long-lived-graceful-restart stale-time``. + + Default value is 120. + +.. clicmd:: bgp graceful-restart stalepath-time (1-4095) + + This is command, will set the max time (in seconds) to hold onto + restarting peer's stale paths. + + It also controls Enhanced Route-Refresh timer. + + If this command is configured and the router does not receive a Route-Refresh EoRR + message, the router removes the stale routes from the BGP table after the timer + expires. The stale path timer is started when the router receives a Route-Refresh + BoRR message + + .. _bgp-GR-global-mode-cmd: BGP GR Global Mode Commands diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py index 31aaa0b8a6..de4b94032c 100644 --- a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py @@ -81,6 +81,8 @@ import os import sys import time import pytest +import functools +import json # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) @@ -91,6 +93,7 @@ sys.path.append(os.path.join("../lib/")) # Import topogen and topotest helpers from lib.topogen import Topogen, get_topogen from lib.topolog import logger +from lib import topotest # Required to instantiate the topology builder class. @@ -1680,6 +1683,304 @@ def BGP_GR_TC_52_p1(request): write_test_footer(tc_name) +def test_BGP_GR_TC_53_p1(request): + """ + Test Objective : Peer-level inherit from BGP wide Restarting + Global Mode : GR Restart + PerPeer Mode : None + GR Mode effective : GR Restart + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Configure R1 as GR restarting node in global level") + + input_dict = { + "r1": {"graceful-restart": {"graceful-restart": True}}, + "r2": {"graceful-restart": {"graceful-restart-helper": True}}, + } + + output = tgen.gears["r1"].vtysh_cmd( + """ + configure terminal + bgp graceful-restart + ! + """ + ) + + step("Verify that R2 receives GR restarting capabilities" " from R1") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Kill BGPd on router R1") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + step( + "Verify that R1 keeps the stale entries in FIB and R2 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Configure graceful-restart-disable at config global level verify that the functionality works + step("Bring up BGP on R1 and configure graceful-restart-disable") + + start_router_daemons(tgen, "r1", ["bgpd"]) + + input_dict = { + "r1": {"graceful-restart": {"graceful-restart-disable": True}}, + "r2": {"graceful-restart": {"graceful-restart-helper": True}}, + } + + output = tgen.gears["r1"].vtysh_cmd( + """ + configure terminal + bgp graceful-restart-disable + ! + """ + ) + + step("Verify on R2 that R1 does't advertise any GR capabilities") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Kill BGP on R1") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + step("Verify on R2 and R1 that none of the routers keep stale entries") + + for addr_type in ADDR_TYPES: + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + dut = "r2" + peer = "r1" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + + step( + "Bring up BGP on R1, enable GR and configure bgp graceful-restart restart-time at global level" + ) + + start_router_daemons(tgen, "r1", ["bgpd"]) + + output = tgen.gears["r1"].vtysh_cmd( + """ + configure terminal + no bgp graceful-restart-disable + bgp graceful-restart + bgp graceful-restart stalepath-time 420 + bgp graceful-restart restart-time 240 + bgp graceful-restart select-defer-time 420 + ! + """ + ) + + step("Verify on R2 that R1 sent the updated GR restart-time") + + def _bgp_check_if_gr_restart_time_was_updated(): + output = json.loads(tgen.gears["r2"].vtysh_cmd("show bgp neighbor json")) + + expected = { + "192.168.0.1": { + "gracefulRestartInfo": { + "localGrMode": "Helper*", + "remoteGrMode": "Restart", + "timers": { + "configuredRestartTimer": 120, + "receivedRestartTimer": 240, + }, + }, + }, + "fd00::1": { + "gracefulRestartInfo": { + "localGrMode": "Helper*", + "remoteGrMode": "Restart", + "timers": { + "configuredRestartTimer": 120, + "receivedRestartTimer": 240, + }, + }, + }, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_gr_restart_time_was_updated, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "R2 did not receive the updated GR restart-time of 240s" + + def _bgp_check_if_gr_timer_on_restarting_node_was_updated(): + output = json.loads(tgen.gears["r1"].vtysh_cmd("show bgp neighbor json")) + + expected = { + "192.168.0.2": { + "gracefulRestartInfo": { + "localGrMode": "Restart*", + "remoteGrMode": "Helper", + "timers": { + "configuredRestartTimer": 240, + "receivedRestartTimer": 120, + }, + "ipv4Unicast": { + "timers": {"stalePathTimer": 420, "selectionDeferralTimer": 420} + }, + }, + }, + "fd00::2": { + "gracefulRestartInfo": { + "localGrMode": "Restart*", + "remoteGrMode": "Helper", + "timers": { + "configuredRestartTimer": 240, + "receivedRestartTimer": 120, + }, + "ipv6Unicast": { + "timers": {"stalePathTimer": 420, "selectionDeferralTimer": 420} + }, + }, + }, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_if_gr_timer_on_restarting_node_was_updated, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert ( + result is None + ), "R1 did not update the GR select-deferral and stale-path timer to 420s" + + write_test_footer(tc_name) + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py b/tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py index 16459a25a3..5d8338d6eb 100644 --- a/tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py +++ b/tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py @@ -195,16 +195,16 @@ def test_bgp_administrative_reset_gr(): step("Reset and shutdown R1") _bgp_clear_r1_and_shutdown() - step("Check if Hard Reset notification wasn't sent from R2") - test_func = functools.partial(_bgp_check_hard_reset) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) - assert result is None, "Failed to send Administrative Reset notification from R2" - step("Check if stale routes are retained on R1") test_func = functools.partial(_bgp_check_gr_notification_stale) _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) assert result is None, "Failed to see retained stale routes on R1" + step("Check if Hard Reset notification wasn't sent from R2") + test_func = functools.partial(_bgp_check_hard_reset) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to send Administrative Reset notification from R2" + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py index 4250c405f3..3f4ed6e0b8 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -3266,27 +3266,48 @@ def verify_graceful_restart( lmode = None rmode = None - # Local GR mode - if "address_family" in input_dict[dut]["bgp"]: - bgp_neighbors = input_dict[dut]["bgp"]["address_family"][addr_type][ - "unicast" - ]["neighbor"][peer]["dest_link"] - for dest_link, data in bgp_neighbors.items(): + # Local GR mode + if "bgp" not in input_dict[dut] and "graceful-restart" in input_dict[dut]: if ( - "graceful-restart-helper" in data - and data["graceful-restart-helper"] + "graceful-restart" in input_dict[dut]["graceful-restart"] + and input_dict[dut]["graceful-restart"][ + "graceful-restart" + ] ): - lmode = "Helper" - elif "graceful-restart" in data and data["graceful-restart"]: - lmode = "Restart" + lmode = "Restart*" elif ( - "graceful-restart-disable" in data - and data["graceful-restart-disable"] + "graceful-restart-disable" + in input_dict[dut]["graceful-restart"] + and input_dict[dut]["graceful-restart"][ + "graceful-restart-disable" + ] ): - lmode = "Disable" + lmode = "Disable*" else: - lmode = None + lmode = "Helper*" + + if lmode is None: + if "address_family" in input_dict[dut]["bgp"]: + bgp_neighbors = input_dict[dut]["bgp"]["address_family"][addr_type][ + "unicast" + ]["neighbor"][peer]["dest_link"] + + for dest_link, data in bgp_neighbors.items(): + if ( + "graceful-restart-helper" in data + and data["graceful-restart-helper"] + ): + lmode = "Helper" + elif "graceful-restart" in data and data["graceful-restart"]: + lmode = "Restart" + elif ( + "graceful-restart-disable" in data + and data["graceful-restart-disable"] + ): + lmode = "Disable" + else: + lmode = None if lmode is None: if "graceful-restart" in input_dict[dut]["bgp"]: @@ -3314,7 +3335,8 @@ def verify_graceful_restart( return True # Remote GR mode - if "address_family" in input_dict[peer]["bgp"]: + + if "bgp" in input_dict[peer] and "address_family" in input_dict[peer]["bgp"]: bgp_neighbors = input_dict[peer]["bgp"]["address_family"][addr_type][ "unicast" ]["neighbor"][dut]["dest_link"] @@ -3336,7 +3358,7 @@ def verify_graceful_restart( rmode = None if rmode is None: - if "graceful-restart" in input_dict[peer]["bgp"]: + if "bgp" in input_dict[peer] and "graceful-restart" in input_dict[peer]["bgp"]: if ( "graceful-restart" in input_dict[peer]["bgp"]["graceful-restart"] @@ -3355,6 +3377,27 @@ def verify_graceful_restart( rmode = "Disable" else: rmode = "Helper" + + if rmode is None: + if "bgp" not in input_dict[peer] and "graceful-restart" in input_dict[peer]: + if ( + "graceful-restart" + in input_dict[peer]["graceful-restart"] + and input_dict[peer]["graceful-restart"][ + "graceful-restart" + ] + ): + rmode = "Restart" + elif ( + "graceful-restart-disable" + in input_dict[peer]["graceful-restart"] + and input_dict[peer]["graceful-restart"][ + "graceful-restart-disable" + ] + ): + rmode = "Disable" + else: + rmode = "Helper" else: rmode = "Helper" -- 2.39.5