From e9c73e834a6b08c056b916c46878f9b96c3f6a35 Mon Sep 17 00:00:00 2001 From: Dev Ojha <47282568+devadityaojha@users.noreply.github.com> Date: Wed, 28 Jul 2021 16:00:12 -0700 Subject: [PATCH] [CLI][MPLS][Show] Added multi ASIC support for 'show mpls command'. * Also added unit tests to test show command with various options. --- config/main.py | 2 +- show/interfaces/__init__.py | 75 +++++++---- show/main.py | 18 +++ tests/mock_tables/asic0/appl_db.json | 14 +- tests/mock_tables/asic1/appl_db.json | 9 ++ tests/mpls_input/appl_db.json | 25 +--- tests/mpls_test.py | 191 +++++++++++++++++++++++---- 7 files changed, 260 insertions(+), 74 deletions(-) diff --git a/config/main.py b/config/main.py index e680f65274c3..6c7547a5ee13 100644 --- a/config/main.py +++ b/config/main.py @@ -4031,7 +4031,7 @@ def add(ctx, interface_name): config_db.set_entry(table_name, interface_name, {"mpls": "enable"}) # -# 'del' subcommand +# 'remove' subcommand # @mpls.command() diff --git a/show/interfaces/__init__.py b/show/interfaces/__init__.py index 204ba84e6cf5..458d3cc25116 100644 --- a/show/interfaces/__init__.py +++ b/show/interfaces/__init__.py @@ -321,36 +321,65 @@ def expected(db, interfacename): click.echo(tabulate(body, header)) -# 'mpls' subcommand ("show interfaces mpls") @interfaces.command() @click.argument('interfacename', required=False) +@click.option('--namespace', '-n', 'namespace', default=None, + type=str, show_default=True, help='Namespace name or all', + callback=multi_asic_util.multi_asic_namespace_validation_callback) +@click.option('--display', '-d', 'display', default=None, show_default=False, + type=str, help='all|frontend') @click.pass_context -def mpls(ctx, interfacename): +def mpls(ctx, interfacename, namespace, display): """Show Interface MPLS status""" + + #Edge case: Force show frontend interfaces on single asic + if not (multi_asic.is_multi_asic()): + if (display == 'frontend' or display == 'all' or display is None): + display = None + else: + print("Error: Invalid display option command for single asic") + return + + masic = multi_asic_util.MultiAsic(display_option=display, namespace_option=namespace) + ns_list = masic.get_ns_list_based_on_options() + intfs_data = {} - appl_db = SonicV2Connector() - appl_db.connect(appl_db.APPL_DB) + for ns in ns_list: - if interfacename is not None: - interfacename = try_convert_interfacename_from_alias(ctx, interfacename) + appl_db = multi_asic.connect_to_all_dbs_for_ns(namespace=ns) - # Fetching data from appl_db for intfs - keys = appl_db.keys(appl_db.APPL_DB, "INTF_TABLE:*") - intfs_data = {} - for key in keys if keys else []: - tokens = key.split(":") - # Skip INTF_TABLE entries with address information - if len(tokens) != 2: - continue - - if (interfacename is not None) and (interfacename != tokens[1]): - continue - - mpls = appl_db.get(appl_db.APPL_DB, key, 'mpls') - if mpls is None or mpls == '': - intfs_data.update({tokens[1]: 'disable'}) - else: - intfs_data.update({tokens[1]: mpls}) + if interfacename is not None: + interfacename = try_convert_interfacename_from_alias(ctx, interfacename) + + # Fetching data from appl_db for intfs + keys = appl_db.keys(appl_db.APPL_DB, "INTF_TABLE:*") + for key in keys if keys else []: + tokens = key.split(":") + ifname = tokens[1] + # Skip INTF_TABLE entries with address information + if len(tokens) != 2: + continue + + if (interfacename is not None) and (interfacename != tokens[1]): + continue + + if (display != "all"): + if ("Loopback" in tokens[1]): + continue + + if ifname.startswith("Ethernet") and multi_asic.is_port_internal(ifname, ns): + continue + + if ifname.startswith("PortChannel") and multi_asic.is_port_channel_internal(ifname, ns): + continue + + + mpls_intf = appl_db.get_all(appl_db.APPL_DB, key) + + if 'mpls' not in mpls_intf or mpls_intf['mpls'] == 'disable': + intfs_data.update({tokens[1]: 'disable'}) + else: + intfs_data.update({tokens[1]: mpls_intf['mpls']}) header = ['Interface', 'MPLS State'] body = [] diff --git a/show/main.py b/show/main.py index c7f83920f688..024f48801031 100755 --- a/show/main.py +++ b/show/main.py @@ -7,6 +7,7 @@ import click import utilities_common.cli as clicommon import utilities_common.multi_asic as multi_asic_util +from importlib import reload from natsort import natsorted from sonic_py_common import device_info from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector @@ -16,6 +17,23 @@ import utilities_common.constants as constants from utilities_common.general import load_db_config +# mock the redis for unit test purposes # +try: + if os.environ["UTILITIES_UNIT_TESTING"] == "2": + modules_path = os.path.join(os.path.dirname(__file__), "..") + tests_path = os.path.join(modules_path, "tests") + sys.path.insert(0, modules_path) + sys.path.insert(0, tests_path) + import mock_tables.dbconnector + if os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] == "multi_asic": + import mock_tables.mock_multi_asic + reload(mock_tables.mock_multi_asic) + reload(mock_tables.dbconnector) + mock_tables.dbconnector.load_namespace_config() + +except KeyError: + pass + from . import acl from . import bgp_common from . import chassis_modules diff --git a/tests/mock_tables/asic0/appl_db.json b/tests/mock_tables/asic0/appl_db.json index 875a89fc8bc1..a708aa7fd69b 100644 --- a/tests/mock_tables/asic0/appl_db.json +++ b/tests/mock_tables/asic0/appl_db.json @@ -80,5 +80,17 @@ "admin_status": "up", "mtu": "9100", "oper_status": "up" + }, + "INTF_TABLE:Ethernet0": { + "mpls":"enable" + }, + "INTF_TABLE:Ethernet4": { + "mpls":"disable" + }, + "INTF_TABLE:Ethernet-BP0": { + "mpls":"enable" + }, + "INTF_TABLE:Ethernet-BP4": { + "mpls":"disable" } -} \ No newline at end of file +} diff --git a/tests/mock_tables/asic1/appl_db.json b/tests/mock_tables/asic1/appl_db.json index ed5fbb2c3c50..37e992c415a2 100644 --- a/tests/mock_tables/asic1/appl_db.json +++ b/tests/mock_tables/asic1/appl_db.json @@ -55,5 +55,14 @@ }, "LAG_MEMBER_TABLE:PortChannel4009:Ethernet-BP260": { "status": "enabled" + }, + "INTF_TABLE:Ethernet64": { + "mpls":"enable" + }, + "INTF_TABLE:Ethernet-BP256": { + "mpls":"disable" + }, + "INTF_TABLE:Ethernet-BP260": { + "mpls":"enable" } } diff --git a/tests/mpls_input/appl_db.json b/tests/mpls_input/appl_db.json index 0bdb59a3ff1d..4ba95edf0f27 100644 --- a/tests/mpls_input/appl_db.json +++ b/tests/mpls_input/appl_db.json @@ -1,18 +1,15 @@ { - "INTF_TABLE:Ethernet16": { +"INTF_TABLE:Ethernet16": { "NULL": "NULL" }, "INTF_TABLE:Ethernet16:192.168.16.1/24": { "NULL": "NULL" }, - "INTF_TABLE:Ethernet2": { + "INTF_TABLE:Ethernet0": { "mpls": "enable" }, - "INTF_TABLE:Ethernet2:192.168.2.1/24": { - "NULL": "NULL" - }, "INTF_TABLE:Ethernet4": { - "NULL": "NULL" + "mpls": "enable" }, "INTF_TABLE:Ethernet4:192.168.4.1/24": { "NULL": "NULL" @@ -26,22 +23,10 @@ "INTF_TABLE:Ethernet8:192.168.8.1/24": { "NULL": "NULL" }, - "INTF_TABLE:Loopback0": { - "NULL": "NULL" - }, - "INTF_TABLE:Loopback0:192.168.0.1/24": { - "NULL": "NULL" - }, - "INTF_TABLE:PortChannel2": { + "INTF_TABLE:Ethernet12": { "mpls": "disable" }, - "INTF_TABLE:PortChannel2:10.0.0.56/31": { - "NULL": "NULL" - }, - "INTF_TABLE:Vlan2": { + "INTF_TABLE:Ethernet20": { "mpls": "enable" - }, - "INTF_TABLE:Vlan2:192.168.1.1/21": { - "NULL": "NULL" } } diff --git a/tests/mpls_test.py b/tests/mpls_test.py index a833ef61d37e..b0c2d4fb805d 100644 --- a/tests/mpls_test.py +++ b/tests/mpls_test.py @@ -1,4 +1,5 @@ import os +import importlib import sys import traceback from unittest import mock @@ -11,83 +12,215 @@ import show.main as show from utilities_common.db import Db -show_interfaces_mpls_output="""\ -Interface MPLS State ------------- ------------ -Ethernet2 enable -Ethernet4 disable -Ethernet8 disable -Ethernet16 disable -Loopback0 disable -PortChannel2 disable -Vlan2 enable +show_interfaces_mpls_output_frontend="""\ +Interface MPLS State +----------- ------------ +Ethernet0 enable +Ethernet4 enable +Ethernet8 disable +Ethernet12 disable +Ethernet16 disable +Ethernet20 enable """ -show_interfaces_mpls_specific_output="""\ +show_interfaces_mpls_masic_output_frontend="""\ Interface MPLS State ----------- ------------ -Ethernet2 enable +Ethernet0 enable +Ethernet4 disable +""" + +show_interfaces_mpls_masic_output_all="""\ +Interface MPLS State +-------------- ------------ +Ethernet0 enable +Ethernet4 disable +Ethernet64 enable +Ethernet-BP0 enable +Ethernet-BP4 disable +Ethernet-BP256 disable +Ethernet-BP260 enable +""" +show_interfaces_mpls_masic_output_asic_all="""\ +Interface MPLS State +------------ ------------ +Ethernet0 enable +Ethernet4 disable +Ethernet-BP0 enable +Ethernet-BP4 disable """ + modules_path = os.path.join(os.path.dirname(__file__), "..") test_path = os.path.join(modules_path, "tests") +scripts_path = os.path.join(modules_path, "scripts") sys.path.insert(0, modules_path) -sys.path.insert(0, test_path) mock_db_path = os.path.join(test_path, "mpls_input") - class TestMpls(object): @classmethod def setup_class(cls): print("SETUP") os.environ['UTILITIES_UNIT_TESTING'] = "1" + jsonfile = os.path.join(mock_db_path, 'appl_db') + dbconnector.dedicated_dbs['APPL_DB'] = jsonfile def test_config_mpls_add(self): runner = CliRunner() db = Db() obj = {'config_db':db.cfgdb} - result = runner.invoke(config.config.commands["interface"].commands["mpls"].commands["add"], ["Ethernet4"], obj=obj) + result = runner.invoke( + config.config.commands["interface"].commands["mpls"].commands["add"], + ["Ethernet8"], obj=obj + ) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert db.cfgdb.get_entry("INTERFACE", "Ethernet8") == {"mpls": "enable"} + + def test_show_interfaces_mpls_frontend(self): + + runner = CliRunner() + result = runner.invoke( + show.cli.commands["interfaces"].commands["mpls"], + ["-dfrontend"] + ) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_interfaces_mpls_output_frontend + + def test_show_interfaces_mpls(self): + runner = CliRunner() + result = runner.invoke( + show.cli.commands["interfaces"].commands["mpls"], [] + ) print(result.exit_code) print(result.output) assert result.exit_code == 0 - assert db.cfgdb.get_entry("INTERFACE", "Ethernet4") == {"mpls": "enable"} + assert result.output == show_interfaces_mpls_output_frontend + + def test_show_interfaces_mpls_dall(self): + runner = CliRunner() + result = runner.invoke( + show.cli.commands["interfaces"].commands["mpls"], + ["-dall"] + ) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_interfaces_mpls_output_frontend def test_config_mpls_remove(self): runner = CliRunner() db = Db() obj = {'config_db':db.cfgdb} - result = runner.invoke(config.config.commands["interface"].commands["mpls"].commands["remove"], ["Ethernet4"], obj=obj) + result = runner.invoke( + config.config.commands["interface"].commands["mpls"].commands["remove"], + ["Ethernet8"], obj=obj + ) print(result.exit_code) print(result.output) assert result.exit_code == 0 - assert db.cfgdb.get_entry("INTERFACE", "Ethernet4") == {"mpls": "disable"} + assert db.cfgdb.get_entry("INTERFACE", "Ethernet8") == {"mpls": "disable"} - def test_show_interfaces_mpls(self): - jsonfile = os.path.join(mock_db_path, 'appl_db') - dbconnector.dedicated_dbs['APPL_DB'] = jsonfile + @classmethod + def teardown_class(cls): + print("TEARDOWN") + os.environ['UTILITIES_UNIT_TESTING'] = "0" + from .mock_tables import mock_single_asic + importlib.reload(mock_single_asic) + from .mock_tables import dbconnector + dbconnector.load_database_config() + dbconnector.dedicated_dbs['APPL_DB'] = {} + +class TestMplsMasic(object): + @classmethod + def setup_class(cls): + print("SETUP") + from .mock_tables import mock_multi_asic + importlib.reload(mock_multi_asic) + from .mock_tables import dbconnector + dbconnector.load_namespace_config() + + def test_config_mpls_masic_add(self): runner = CliRunner() - result = runner.invoke(show.cli.commands["interfaces"].commands["mpls"], []) + db = Db() + obj = {'config_db':db.cfgdb, 'namespace':'asic0'} + + result = runner.invoke( + config.config.commands["interface"].commands["mpls"].commands["add"], + ["Ethernet8"], obj=obj + ) print(result.exit_code) print(result.output) assert result.exit_code == 0 - assert result.output == show_interfaces_mpls_output + assert db.cfgdb.get_entry("INTERFACE", "Ethernet8") == {"mpls": "enable"} - def test_show_interfaces_mpls_specific(self): - jsonfile = os.path.join(mock_db_path, 'appl_db') - dbconnector.dedicated_dbs['APPL_DB'] = jsonfile + def test_show_interfaces_mpls_masic_frontend(self): runner = CliRunner() - result = runner.invoke(show.cli.commands["interfaces"].commands["mpls"], ["Ethernet2"]) + result = runner.invoke( + show.cli.commands["interfaces"].commands["mpls"], + ["-dfrontend"] + ) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_interfaces_mpls_masic_output_frontend + + def test_show_interfaces_mpls_masic_all(self): + runner = CliRunner() + result = runner.invoke( + show.cli.commands["interfaces"].commands["mpls"], + ["-dall"] + ) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_interfaces_mpls_masic_output_all + + def test_show_interfaces_mpls_masic_asic(self): + runner = CliRunner() + result = runner.invoke( + show.cli.commands["interfaces"].commands["mpls"], + ["-nasic0"] + ) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_interfaces_mpls_masic_output_frontend + + def test_show_interfaces_mpls_masic_asic_all(self): + runner = CliRunner() + result = runner.invoke( + show.cli.commands["interfaces"].commands["mpls"], + ["-nasic0", "-dall"] + ) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_interfaces_mpls_masic_output_asic_all + + def test_config_mpls_masic_remove(self): + runner = CliRunner() + db = Db() + obj = {'config_db':db.cfgdb, 'namespace':'asic0'} + + result = runner.invoke( + config.config.commands["interface"].commands["mpls"].commands["remove"], + ["Ethernet8"], obj=obj + ) print(result.exit_code) print(result.output) assert result.exit_code == 0 - assert result.output == show_interfaces_mpls_specific_output + assert db.cfgdb.get_entry("INTERFACE", "Ethernet8") == {"mpls": "disable"} @classmethod def teardown_class(cls): print("TEARDOWN") os.environ['UTILITIES_UNIT_TESTING'] = "0" - dbconnector.dedicated_dbs['APPL_DB'] = None + from .mock_tables import mock_single_asic + importlib.reload(mock_single_asic) + from .mock_tables import dbconnector + dbconnector.load_database_config()