From f69c7d245ceb2682d3cf7da1ac6eebbab5e7ac2a Mon Sep 17 00:00:00 2001 From: Yaqiang Zhu Date: Fri, 28 Feb 2025 12:23:32 +0800 Subject: [PATCH] [bgp] Add bgp router id test (#17211) How did you do it? Test in default config, bgp router id should be aligned with Loopback IPv4 address Test when bgp_router_id and Loopback IPv4 address both exist in running config, bgp router id should be aligned with bgp_router_id in running config. And Loopback IPv4 address should be advertised to neighbor Test when bgp_router_id configured but Loopback IPv4 address missing, bgp could work well. How did you verify/test it? Run test in m0/t0/t1/dualtor/dualtor-aa testbed, all passed --- tests/bgp/test_bgp_fact.py | 6 +- tests/bgp/test_bgp_router_id.py | 139 ++++++++++++++++++ .../tests_mark_conditions.yaml | 6 + tests/ip/test_mgmt_ipv6_only.py | 2 +- 4 files changed, 148 insertions(+), 5 deletions(-) create mode 100644 tests/bgp/test_bgp_router_id.py diff --git a/tests/bgp/test_bgp_fact.py b/tests/bgp/test_bgp_fact.py index f2678ce4efa..ff4e493d43a 100644 --- a/tests/bgp/test_bgp_fact.py +++ b/tests/bgp/test_bgp_fact.py @@ -6,11 +6,9 @@ ] -def run_bgp_facts(duthosts, enum_frontend_dut_hostname, enum_asic_index): +def run_bgp_facts(duthost, enum_asic_index): """compare the bgp facts between observed states and target state""" - duthost = duthosts[enum_frontend_dut_hostname] - bgp_facts = duthost.bgp_facts(instance_id=enum_asic_index)['ansible_facts'] namespace = duthost.get_namespace_from_asic_id(enum_asic_index) config_facts = duthost.config_facts(host=duthost.hostname, source="running", namespace=namespace)['ansible_facts'] @@ -43,4 +41,4 @@ def run_bgp_facts(duthosts, enum_frontend_dut_hostname, enum_asic_index): def test_bgp_facts(duthosts, enum_frontend_dut_hostname, enum_asic_index): - run_bgp_facts(duthosts, enum_frontend_dut_hostname, enum_asic_index) + run_bgp_facts(duthosts[enum_frontend_dut_hostname], enum_asic_index) diff --git a/tests/bgp/test_bgp_router_id.py b/tests/bgp/test_bgp_router_id.py new file mode 100644 index 00000000000..2d60f231015 --- /dev/null +++ b/tests/bgp/test_bgp_router_id.py @@ -0,0 +1,139 @@ +import pytest +import logging +import re + +from tests.common.helpers.assertions import pytest_require, pytest_assert +from tests.bgp.test_bgp_fact import run_bgp_facts +from tests.common.utilities import wait_until + + +pytestmark = [ + pytest.mark.topology('any') +] + +logger = logging.getLogger(__name__) + +CUSTOMIZED_BGP_ROUTER_ID = "8.8.8.8" + + +def verify_bgp(enum_asic_index, duthost, expected_bgp_router_id, neighbor_type, nbrhosts): + output = duthost.shell("show ip bgp summary", module_ignore_errors=True)["stdout"] + + # Verify router id from DUT itself + pattern = r"BGP router identifier (\d+\.\d+\.\d+\.\d+)" + match = re.search(pattern, output) + pytest_assert(match, "Cannot get actual BGP router id from [{}]".format(output)) + pytest_assert(match.group(1) == expected_bgp_router_id, + "BGP router id unexpected, expected: {}, actual: {}".format(expected_bgp_router_id, match.group(1))) + + # Verify BGP sessions are established + run_bgp_facts(duthost, enum_asic_index) + + # Verify from peer device side to check + if neighbor_type not in ["sonic", "eos"]: + logger.warning("Unsupport neighbor type for neighbor bgp check: {}".format(neighbor_type)) + local_ip_map = {} + cfg_facts = duthost.config_facts(host=duthost.hostname, source="running")['ansible_facts'] + for _, item in cfg_facts.get("BGP_NEIGHBOR", {}).items(): + if "." in item["local_addr"]: + local_ip_map[item["name"]] = item["local_addr"] + + for neighbor_name, nbrhost in nbrhosts.items(): + pytest_assert(neighbor_name in local_ip_map, "Cannot find local ip for {}".format(neighbor_name)) + if neighbor_type == "sonic": + cmd = "show ip neighbors {}".format(local_ip_map[neighbor_name]) + elif neighbor_type == "eos": + cmd = "/usr/bin/Cli -c \"show ip bgp neighbors {}\"".format(local_ip_map[neighbor_name]) + output = nbrhost["host"].shell(cmd, module_ignore_errors=True)['stdout'] + pattern = r"BGP version 4, remote router ID (\d+\.\d+\.\d+\.\d+)" + match = re.search(pattern, output) + pytest_assert(match, "Cannot get remote BGP router id from [{}]".format(output)) + pytest_assert(match.group(1) == expected_bgp_router_id, + "BGP router id is unexpected, local: {}, fetch from remote: {}" + .format(expected_bgp_router_id, match.group(1))) + + +@pytest.fixture() +def loopback_ip(duthosts, enum_frontend_dut_hostname): + duthost = duthosts[enum_frontend_dut_hostname] + cfg_facts = duthost.config_facts(host=duthost.hostname, source="running")['ansible_facts'] + loopback_ip = None + loopback_table = cfg_facts.get("LOOPBACK_INTERFACE", {}) + for key in loopback_table.get("Loopback0", {}).keys(): + if "." in key: + loopback_ip = key.split("/")[0] + pytest_require(loopback_ip is not None, "Cannot get IPv4 address of Loopback0") + yield loopback_ip + + +def restart_bgp(duthost): + duthost.reset_service("bgp") + duthost.restart_service("bgp") + pytest_assert(wait_until(100, 10, 10, duthost.is_service_fully_started_per_asic_or_host, "bgp"), "BGP not started.") + pytest_assert(wait_until(100, 10, 10, duthost.check_default_route, "bgp"), "Default route not ready") + + +@pytest.fixture() +def router_id_setup_and_teardown(duthosts, enum_frontend_dut_hostname): + duthost = duthosts[enum_frontend_dut_hostname] + duthost.shell("sonic-db-cli CONFIG_DB hset \"DEVICE_METADATA|localhost\" \"bgp_router_id\" \"{}\"" + .format(CUSTOMIZED_BGP_ROUTER_ID), module_ignore_errors=True) + restart_bgp(duthost) + + yield + + duthost.shell("sonic-db-cli CONFIG_DB hdel \"DEVICE_METADATA|localhost\" \"bgp_router_id\"", + module_ignore_errors=True) + restart_bgp(duthost) + + +@pytest.fixture(scope="function") +def router_id_loopback_setup_and_teardown(duthosts, enum_frontend_dut_hostname, loopback_ip): + duthost = duthosts[enum_frontend_dut_hostname] + duthost.shell("sonic-db-cli CONFIG_DB hset \"DEVICE_METADATA|localhost\" \"bgp_router_id\" \"{}\"" + .format(CUSTOMIZED_BGP_ROUTER_ID), module_ignore_errors=True) + duthost.shell("sonic-db-cli CONFIG_DB del \"LOOPBACK_INTERFACE|Loopback0|{}/32\"".format(loopback_ip)) + restart_bgp(duthost) + + yield + + duthost.shell("sonic-db-cli CONFIG_DB hdel \"DEVICE_METADATA|localhost\" \"bgp_router_id\"", + module_ignore_errors=True) + duthost.shell("sonic-db-cli CONFIG_DB hset \"LOOPBACK_INTERFACE|Loopback0|{}/32\" \"NULL\" \"NULL\"" + .format(loopback_ip), module_ignore_errors=True) + restart_bgp(duthost) + + +def test_bgp_router_id_default(duthosts, enum_frontend_dut_hostname, enum_asic_index, nbrhosts, request, loopback_ip): + # Test in default config, the BGP router id should be aligned with Loopback IPv4 address + duthost = duthosts[enum_frontend_dut_hostname] + neighbor_type = request.config.getoption("neighbor_type") + verify_bgp(enum_asic_index, duthost, loopback_ip, neighbor_type, nbrhosts) + + +def test_bgp_router_id_set(duthosts, enum_frontend_dut_hostname, enum_asic_index, nbrhosts, request, loopback_ip, + router_id_setup_and_teardown): + # Test in the scenario that bgp_router_id and Loopback IPv4 address both exist in CONFIG_DB, the actual BGP router + # ID should be aligned with bgp_router_id in CONFIG_DB. And the Loopback IPv4 address should be advertised to BGP + # neighbor + duthost = duthosts[enum_frontend_dut_hostname] + neighbor_type = request.config.getoption("neighbor_type") + verify_bgp(enum_asic_index, duthost, CUSTOMIZED_BGP_ROUTER_ID, neighbor_type, nbrhosts) + # Verify Loopback ip has been advertised to neighbor + cfg_facts = duthost.config_facts(host=duthost.hostname, source="running")['ansible_facts'] + for remote_ip in cfg_facts.get("BGP_NEIGHBOR", {}).keys(): + if "." not in remote_ip: + continue + output = duthost.shell("show ip bgp neighbor {} advertised-routes| grep {}".format(remote_ip, loopback_ip), + module_ignore_errors=True) + pytest_assert(output["rc"] == 0, "Failed to check whether Loopback ipv4 address has been advertised") + pytest_assert(loopback_ip in output["stdout"], "Router advertised unexpected: {}".format(output["stdout"])) + + +def test_bgp_router_id_set_without_loopback(duthosts, enum_frontend_dut_hostname, enum_asic_index, nbrhosts, request, + router_id_loopback_setup_and_teardown): + # Test in the scenario that bgp_router_id specified but Loopback IPv4 address not set, BGP could work well and the + # actual BGP router id should be aligned with CONFIG_DB + duthost = duthosts[enum_frontend_dut_hostname] + neighbor_type = request.config.getoption("neighbor_type") + verify_bgp(enum_asic_index, duthost, CUSTOMIZED_BGP_ROUTER_ID, neighbor_type, nbrhosts) diff --git a/tests/common/plugins/conditional_mark/tests_mark_conditions.yaml b/tests/common/plugins/conditional_mark/tests_mark_conditions.yaml index 2d69c5096ab..6ebd88abb26 100644 --- a/tests/common/plugins/conditional_mark/tests_mark_conditions.yaml +++ b/tests/common/plugins/conditional_mark/tests_mark_conditions.yaml @@ -143,6 +143,12 @@ bgp/test_bgp_queue.py: conditions: - "topo_type in ['m0', 'mx']" +bgp/test_bgp_router_id.py: + skip: + reason: "Not supported on multi-asic" + conditions: + - "is_multi_asic==True" + bgp/test_bgp_slb.py: skip: reason: "Skip over topologies which doesn't support slb." diff --git a/tests/ip/test_mgmt_ipv6_only.py b/tests/ip/test_mgmt_ipv6_only.py index 0e2d7692204..a75a6a4a9f8 100644 --- a/tests/ip/test_mgmt_ipv6_only.py +++ b/tests/ip/test_mgmt_ipv6_only.py @@ -88,7 +88,7 @@ def test_bgp_facts_ipv6_only(duthosts, enum_frontend_dut_hostname, enum_asic_ind convert_and_restore_config_db_to_ipv6_only): # noqa F811 # Add a temporary debug log to see if DUTs are reachable via IPv6 mgmt-ip. Will remove later log_eth0_interface_info(duthosts) - run_bgp_facts(duthosts, enum_frontend_dut_hostname, enum_asic_index) + run_bgp_facts(duthosts[enum_frontend_dut_hostname], enum_asic_index) def test_show_features_ipv6_only(duthosts, enum_dut_hostname, convert_and_restore_config_db_to_ipv6_only): # noqa F811