From fe5791a181b4daba01ece18032117e536e490ca0 Mon Sep 17 00:00:00 2001 From: JoshYangEC Date: Thu, 25 Jul 2024 16:31:57 +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. --- tests/bgp_commands_test.py | 90 +++++++++++++++++++ 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 +++++++++++++ utilities_common/bgp_util.py | 81 ++++++++++++++--- utilities_common/cli.py | 4 +- 7 files changed, 291 insertions(+), 16 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_test.py b/tests/bgp_commands_test.py index 11415e8727..deca373887 100644 --- a/tests/bgp_commands_test.py +++ b/tests/bgp_commands_test.py @@ -55,6 +55,42 @@ 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 +398,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 5dd31d523a..667b5d8d35 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -174,6 +174,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 0000000000..9e26dfeeb6 --- /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 0000000000..09fa2b9ebc --- /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 0000000000..70af203ef0 --- /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" + } + } +} diff --git a/utilities_common/bgp_util.py b/utilities_common/bgp_util.py index cb49123c4b..c062f1e7c2 100644 --- a/utilities_common/bgp_util.py +++ b/utilities_common/bgp_util.py @@ -217,24 +217,77 @@ def run_bgp_command(vtysh_cmd, bgp_namespace=multi_asic.DEFAULT_NAMESPACE, 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, exit_on_fail=True): output = run_bgp_command(vtysh_cmd, bgp_namespace, constants.RVTYSH_COMMAND, exit_on_fail) # 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 c8a314b704..6e92db06d5 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 @@ -729,8 +730,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)): return run_command_in_alias_mode(command, shell=shell) proc = subprocess.Popen(command, shell=shell, text=True, stdout=subprocess.PIPE)