From 5e51c9b66b20460d6058e09d415247dc0fc90a0e Mon Sep 17 00:00:00 2001 From: JoshYangEC Date: Fri, 23 Aug 2024 17:35:39 +0800 Subject: [PATCH] fix errors of "show ip bgp summary" and "show ip bgp neigh" under alias In alias mode, the output of "show ip/ipv6 bgp summary" and "show ip/ipv6 bgp neigh" return errors. This PR is used to fix these errors. --- .../bgp_neighbor_test_vector.py | 10 ++ tests/bgp_commands_test.py | 92 +++++++++++++++++++ tests/conftest.py | 9 ++ .../ip_bgp_summary_alias_empty.json | 1 + tests/mock_tables/ipv4_bgp_summary_alias.json | 61 ++++++++++++ tests/mock_tables/ipv6_bgp_summary_alias.json | 61 ++++++++++++ tests/show_bgp_neighbor_test.py | 9 ++ utilities_common/bgp_util.py | 85 ++++++++++++++--- utilities_common/cli.py | 3 +- 9 files changed, 316 insertions(+), 15 deletions(-) create mode 100644 tests/mock_tables/ip_bgp_summary_alias_empty.json create mode 100644 tests/mock_tables/ipv4_bgp_summary_alias.json create mode 100644 tests/mock_tables/ipv6_bgp_summary_alias.json diff --git a/tests/bgp_commands_input/bgp_neighbor_test_vector.py b/tests/bgp_commands_input/bgp_neighbor_test_vector.py index a5766c2a..9a816f82 100644 --- a/tests/bgp_commands_input/bgp_neighbor_test_vector.py +++ b/tests/bgp_commands_input/bgp_neighbor_test_vector.py @@ -651,6 +651,11 @@ def mock_show_bgp_neighbor_single_asic(request): 'rc': 0, 'rc_output': bgp_v4_neighbors_output }, + 'bgp_v4_alias_neighbors': { + 'args': [], + 'rc': 0, + 'rc_output': bgp_v4_neighbors_output + }, 'bgp_v4_neighbor_ip_address': { 'args': ['10.0.0.57'], 'rc': 0, @@ -681,6 +686,11 @@ def mock_show_bgp_neighbor_single_asic(request): 'rc': 0, 'rc_output': bgp_v6_neighbors_output }, + 'bgp_v6_alias_neighbors': { + 'args': [], + 'rc': 0, + 'rc_output': bgp_v6_neighbors_output + }, 'bgp_v6_neighbor_ip_address': { 'args': ['fc00::72'], 'rc': 0, diff --git a/tests/bgp_commands_test.py b/tests/bgp_commands_test.py index a60ba8c8..5e7e2388 100644 --- a/tests/bgp_commands_test.py +++ b/tests/bgp_commands_test.py @@ -55,6 +55,44 @@ Total number of neighbors 24 """ + +show_bgp_summary_alias_v4 = """\ + +IPv4 Unicast Summary: +BGP router identifier 10.1.0.32, local AS number 65100 vrf-id 0 +BGP table version 12811 +RIB entries 12817, using 2358328 bytes of memory +Peers 2, using 502080 KiB of memory +Peer groups 1, using 256 bytes of memory + + +Neighbhor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd NeighborName +----------- --- ----- --------- --------- -------- ----- ------ --------- -------------- -------------- +etp1 4 65200 5919 2717 0 0 0 1d21h11m 6402 NotAvailable +etp2 4 65200 5916 2714 0 0 0 1d21h10m 6402 NotAvailable + +Total number of neighbors 2 +""" + +show_bgp_summary_alias_v6 = """\ + +IPv6 Unicast Summary: +BGP router identifier 10.1.0.32, local AS number 65100 vrf-id 0 +BGP table version 12811 +RIB entries 12817, using 2358328 bytes of memory +Peers 2, using 502080 KiB of memory +Peer groups 1, using 256 bytes of memory + + +Neighbhor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd NeighborName +----------- --- ----- --------- --------- -------- ----- ------ --------- -------------- -------------- +etp1 4 65200 5919 2717 0 0 0 1d21h11m 6402 NotAvailable +etp2 4 65200 5916 2714 0 0 0 1d21h10m 6402 NotAvailable + +Total number of neighbors 2 +""" + + show_bgp_summary_v6 = """\ IPv6 Unicast Summary: @@ -362,6 +400,60 @@ def test_bgp_summary_v4( assert result.exit_code == 0 assert result.output == show_bgp_summary_v4 + @pytest.mark.parametrize('setup_single_bgp_instance', + ['alias_v4'], indirect=['setup_single_bgp_instance']) + def test_bgp_summary_alias_v4( + self, + setup_bgp_commands, + setup_single_bgp_instance): + show = setup_bgp_commands + runner = CliRunner() + os.environ['SONIC_CLI_IFACE_MODE'] = "alias" + result = runner.invoke( + show.cli.commands["ip"].commands["bgp"].commands["summary"], []) + os.environ['SONIC_CLI_IFACE_MODE'] = "default" + print("{}".format(result.output)) + assert result.exit_code == 0 + assert result.output == show_bgp_summary_alias_v4 + + @pytest.mark.parametrize('setup_single_bgp_instance', + ['alias_v6'], indirect=['setup_single_bgp_instance']) + def test_bgp_summary_alias_v6( + self, + setup_bgp_commands, + setup_single_bgp_instance): + show = setup_bgp_commands + runner = CliRunner() + os.environ['SONIC_CLI_IFACE_MODE'] = "alias" + result = runner.invoke( + show.cli.commands["ipv6"].commands["bgp"].commands["summary"], []) + os.environ['SONIC_CLI_IFACE_MODE'] = "default" + print("{}".format(result.output)) + assert result.exit_code == 0 + assert result.output == show_bgp_summary_alias_v6 + + @pytest.mark.parametrize('setup_single_bgp_instance', + ['alias_empty'], indirect=['setup_single_bgp_instance']) + def test_bgp_summary_alias_empty( + self, + setup_bgp_commands, + setup_single_bgp_instance): + show = setup_bgp_commands + runner = CliRunner() + os.environ['SONIC_CLI_IFACE_MODE'] = "alias" + result = runner.invoke( + show.cli.commands["ip"].commands["bgp"].commands["summary"], []) + os.environ['SONIC_CLI_IFACE_MODE'] = "default" + print("{}".format(result.output)) + assert result.exit_code == 2 + + os.environ['SONIC_CLI_IFACE_MODE'] = "alias" + result = runner.invoke( + show.cli.commands["ipv6"].commands["bgp"].commands["summary"], []) + os.environ['SONIC_CLI_IFACE_MODE'] = "default" + print("{}".format(result.output)) + assert result.exit_code == 2 + @pytest.mark.parametrize('setup_single_bgp_instance', ['v6'], indirect=['setup_single_bgp_instance']) def test_bgp_summary_v6( diff --git a/tests/conftest.py b/tests/conftest.py index fd859ccc..3d4af210 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -167,6 +167,15 @@ def setup_single_bgp_instance(request): if request.param == 'v4': bgp_mocked_json = os.path.join( test_path, 'mock_tables', 'ipv4_bgp_summary.json') + elif request.param == 'alias_v4': + bgp_mocked_json = os.path.join( + test_path, 'mock_tables', 'ipv4_bgp_summary_alias.json') + elif request.param == 'alias_v6': + bgp_mocked_json = os.path.join( + test_path, 'mock_tables', 'ipv6_bgp_summary_alias.json') + elif request.param == 'alias_empty': + bgp_mocked_json = os.path.join( + test_path, 'mock_tables', 'ip_bgp_summary_alias_empty.json') elif request.param == 'v6': bgp_mocked_json = os.path.join( test_path, 'mock_tables', 'ipv6_bgp_summary.json') diff --git a/tests/mock_tables/ip_bgp_summary_alias_empty.json b/tests/mock_tables/ip_bgp_summary_alias_empty.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/tests/mock_tables/ip_bgp_summary_alias_empty.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/mock_tables/ipv4_bgp_summary_alias.json b/tests/mock_tables/ipv4_bgp_summary_alias.json new file mode 100644 index 00000000..09fa2b9e --- /dev/null +++ b/tests/mock_tables/ipv4_bgp_summary_alias.json @@ -0,0 +1,61 @@ +{ + "ipv4Unicast":{ + "routerId":"10.1.0.32", + "as":65100, + "vrfId":0, + "vrfName":"default", + "tableVersion":12811, + "ribCount":12817, + "ribMemory":2358328, + "peerCount":2, + "peerMemory":502080, + "peerGroupCount":1, + "peerGroupMemory":256, + "peers":{ + "Ethernet0":{ + "remoteAs":65200, + "version":4, + "msgRcvd":5919, + "msgSent":2717, + "tableVersion":0, + "outq":0, + "inq":0, + "peerUptime":"1d21h11m", + "peerUptimeMsec":162683000, + "peerUptimeEstablishedEpoch":1597732920, + "prefixReceivedCount":6402, + "pfxRcd":6402, + "pfxSnt":1, + "state":"Established", + "connectionsEstablished":1, + "connectionsDropped":0, + "idType":"interface" + }, + "Ethernet4":{ + "remoteAs":65200, + "version":4, + "msgRcvd":5916, + "msgSent":2714, + "tableVersion":0, + "outq":0, + "inq":0, + "peerUptime":"1d21h10m", + "peerUptimeMsec":162638000, + "peerUptimeEstablishedEpoch":1597732965, + "prefixReceivedCount":6402, + "pfxRcd":6402, + "pfxSnt":1, + "state":"Established", + "connectionsEstablished":1, + "connectionsDropped":0, + "idType":"interface" + } + }, + "failedPeers":0, + "totalPeers":2, + "dynamicPeers":0, + "bestPath":{ + "multiPathRelax":"true" + } + } +} \ No newline at end of file diff --git a/tests/mock_tables/ipv6_bgp_summary_alias.json b/tests/mock_tables/ipv6_bgp_summary_alias.json new file mode 100644 index 00000000..a16aecf0 --- /dev/null +++ b/tests/mock_tables/ipv6_bgp_summary_alias.json @@ -0,0 +1,61 @@ +{ + "ipv6Unicast":{ + "routerId":"10.1.0.32", + "as":65100, + "vrfId":0, + "vrfName":"default", + "tableVersion":12811, + "ribCount":12817, + "ribMemory":2358328, + "peerCount":2, + "peerMemory":502080, + "peerGroupCount":1, + "peerGroupMemory":256, + "peers":{ + "Ethernet0":{ + "remoteAs":65200, + "version":4, + "msgRcvd":5919, + "msgSent":2717, + "tableVersion":0, + "outq":0, + "inq":0, + "peerUptime":"1d21h11m", + "peerUptimeMsec":162683000, + "peerUptimeEstablishedEpoch":1597732920, + "prefixReceivedCount":6402, + "pfxRcd":6402, + "pfxSnt":1, + "state":"Established", + "connectionsEstablished":1, + "connectionsDropped":0, + "idType":"interface" + }, + "Ethernet4":{ + "remoteAs":65200, + "version":4, + "msgRcvd":5916, + "msgSent":2714, + "tableVersion":0, + "outq":0, + "inq":0, + "peerUptime":"1d21h10m", + "peerUptimeMsec":162638000, + "peerUptimeEstablishedEpoch":1597732965, + "prefixReceivedCount":6402, + "pfxRcd":6402, + "pfxSnt":1, + "state":"Established", + "connectionsEstablished":1, + "connectionsDropped":0, + "idType":"interface" + } + }, + "failedPeers":0, + "totalPeers":2, + "dynamicPeers":0, + "bestPath":{ + "multiPathRelax":"true" + } + } +} \ No newline at end of file diff --git a/tests/show_bgp_neighbor_test.py b/tests/show_bgp_neighbor_test.py index de842230..d36ec019 100644 --- a/tests/show_bgp_neighbor_test.py +++ b/tests/show_bgp_neighbor_test.py @@ -10,6 +10,10 @@ def executor(test_vector, show): runner = CliRunner() input = testData[test_vector] + + if "alias" in test_vector: + os.environ['SONIC_CLI_IFACE_MODE'] = "alias" + if test_vector.startswith('bgp_v6'): exec_cmd = show.cli.commands["ipv6"].commands["bgp"].commands["neighbors"] else: @@ -17,6 +21,9 @@ def executor(test_vector, show): result = runner.invoke(exec_cmd, input['args']) + if "alias" in test_vector: + os.environ['SONIC_CLI_IFACE_MODE'] = "default" + print(result.exit_code) print(result.output) @@ -50,6 +57,7 @@ def setup_class(cls): @pytest.mark.parametrize('setup_single_bgp_instance, test_vector', [ ('bgp_v4_neighbors_output', 'bgp_v4_neighbors'), + ('bgp_v4_neighbors_output', 'bgp_v4_alias_neighbors'), ('bgp_v4_neighbors_output', 'bgp_v4_neighbor_ip_address'), ('bgp_v4_neighbor_invalid_neigh', @@ -61,6 +69,7 @@ def setup_class(cls): ('bgp_v4_neighbor_output_recv_routes', 'bgp_v4_neighbor_recv_routes'), ('bgp_v6_neighbors_output', 'bgp_v6_neighbors'), + ('bgp_v6_neighbors_output', 'bgp_v6_alias_neighbors'), ('bgp_v6_neighbors_output', 'bgp_v6_neighbor_ip_address'), ('bgp_v6_neighbor_invalid', diff --git a/utilities_common/bgp_util.py b/utilities_common/bgp_util.py index 64054662..ac83fb58 100644 --- a/utilities_common/bgp_util.py +++ b/utilities_common/bgp_util.py @@ -206,25 +206,82 @@ def run_bgp_command(vtysh_cmd, bgp_namespace=multi_asic.DEFAULT_NAMESPACE, vtysh return output +def get_alias_route_converter(vtysh_output): + iface_alias_converter = clicommon.InterfaceAliasConverter() + route_info = json.loads(vtysh_output) + for route, info in route_info.items(): + for i in range(0, len(info)): + if 'nexthops' in info[i]: + for j in range(0, len(info[i]['nexthops'])): + intf_name = "" + if 'interfaceName' in info[i]['nexthops'][j]: + intf_name = info[i]['nexthops'][j]['interfaceName'] + alias = iface_alias_converter.name_to_alias(intf_name) + if alias is not None: + info[i]['nexthops'][j]['interfaceName'] = alias + output = json.dumps(route_info) + return output + + +def get_alias_bgp_summary_converter(key): + def converter(vtysh_output): + iface_alias_converter = clicommon.InterfaceAliasConverter() + cmd_json_output = json.loads(vtysh_output) + + if cmd_json_output: + for iface_name in list(cmd_json_output[key]['peers'].keys()): + port_alias = iface_alias_converter.name_to_alias(iface_name) + if port_alias is not None: + cmd_json_output[key]['peers'][port_alias] = cmd_json_output[key]['peers'].pop(iface_name) + return json.dumps(cmd_json_output) + return converter + + +def get_alias_neighbor_converter(vtysh_output): + iface_alias_converter = clicommon.InterfaceAliasConverter() + output = "" + for vtysh_line in vtysh_output.split("\n"): + m = re.match(r'^BGP neighbor on (Ethernet[0-9]*):', vtysh_line) + + if m: + iface_name = m[1] + port_alias = iface_alias_converter.name_to_alias(iface_name) + output += vtysh_line.replace(iface_name, port_alias) + "\n" + else: + output += vtysh_line + "\n" + return output + + +vtysh_alias_dict = { + "show (ip|ipv6) route": get_alias_route_converter, + "show ip bgp summary": lambda vtysh_output: get_alias_bgp_summary_converter('ipv4Unicast')(vtysh_output), + "show bgp ipv6 summary": lambda vtysh_output: get_alias_bgp_summary_converter('ipv6Unicast')(vtysh_output), + "show (ip bgp|bgp ipv6) neighbor": get_alias_neighbor_converter, + "show ip bgp json": None +} + + +def is_vtysh_cmd(vtysh_cmd): + return any(re.search(cmd_key, vtysh_cmd) for cmd_key in vtysh_alias_dict) + + +def get_vtysh_alias_converter(vtysh_output, vtysh_cmd): + for cmd_key, vtysh_alias_fun in vtysh_alias_dict.items(): + if re.search(cmd_key, vtysh_cmd): + if vtysh_alias_fun is None: + return vtysh_output + return vtysh_alias_fun(vtysh_output) + return vtysh_output def run_bgp_show_command(vtysh_cmd, bgp_namespace=multi_asic.DEFAULT_NAMESPACE): output = run_bgp_command(vtysh_cmd, bgp_namespace, constants.RVTYSH_COMMAND) # handle the the alias mode in the following code + if output == '': + return '' + if output is not None: - if clicommon.get_interface_naming_mode() == "alias" and re.search("show ip|ipv6 route", vtysh_cmd): - iface_alias_converter = clicommon.InterfaceAliasConverter() - route_info =json.loads(output) - for route, info in route_info.items(): - for i in range(0, len(info)): - if 'nexthops' in info[i]: - for j in range(0, len(info[i]['nexthops'])): - intf_name = "" - if 'interfaceName' in info[i]['nexthops'][j]: - intf_name = info[i]['nexthops'][j]['interfaceName'] - alias = iface_alias_converter.name_to_alias(intf_name) - if alias is not None: - info[i]['nexthops'][j]['interfaceName'] = alias - output= json.dumps(route_info) + if clicommon.get_interface_naming_mode() == "alias": + output = get_vtysh_alias_converter(output, vtysh_cmd) return output diff --git a/utilities_common/cli.py b/utilities_common/cli.py index 9d3cdae7..b152585a 100644 --- a/utilities_common/cli.py +++ b/utilities_common/cli.py @@ -10,6 +10,7 @@ import json import lazy_object_proxy import netaddr +import utilities_common.bgp_util as bgp_util from natsort import natsorted from sonic_py_common import multi_asic @@ -538,7 +539,7 @@ def run_command(command, display_cmd=False, ignore_error=False, return_cmd=False # both SONiC interface name and alias name for all interfaces. # IP route table cannot be handled in function run_command_in_alias_mode since it is in JSON format # with a list for next hops - if get_interface_naming_mode() == "alias" and not command_str.startswith("intfutil") and not re.search("show ip|ipv6 route", command_str): + if get_interface_naming_mode() == "alias" and not command_str.startswith("intfutil") and not bgp_util.is_vtysh_cmd(command_str): run_command_in_alias_mode(command, shell=shell) sys.exit(0)