tests: Updated topotest and documentation

Added topotest and documentation for BGP wide GR configurations

Signed-off-by: Pooja Jagadeesh Doijode <pdoijode@nvidia.com>
This commit is contained in:
Pooja Jagadeesh Doijode 2024-06-26 17:34:44 -07:00
parent c30b683338
commit ecbca1ae1b
4 changed files with 414 additions and 24 deletions

View File

@ -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

View File

@ -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))

View File

@ -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:]

View File

@ -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][
if "bgp" not in input_dict[dut] and "graceful-restart" in input_dict[dut]:
if (
"graceful-restart" in input_dict[dut]["graceful-restart"]
and input_dict[dut]["graceful-restart"][
"graceful-restart"
]
):
lmode = "Restart*"
elif (
"graceful-restart-disable"
in input_dict[dut]["graceful-restart"]
and input_dict[dut]["graceful-restart"][
"graceful-restart-disable"
]
):
lmode = "Disable*"
else:
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
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"