diff --git a/show/main.py b/show/main.py index 8e14c641e..5a47a9065 100755 --- a/show/main.py +++ b/show/main.py @@ -132,6 +132,24 @@ def get_cmd_output(cmd): proc = subprocess.Popen(cmd, text=True, stdout=subprocess.PIPE) return proc.communicate()[0], proc.returncode +def get_config_json_by_namespace(namespace): + cmd = ['sonic-cfggen', '-d', '--print-data'] + if namespace is not None and namespace != multi_asic.DEFAULT_NAMESPACE: + cmd += ['-n', namespace] + + stdout, rc = get_cmd_output(cmd) + if rc: + click.echo("Failed to get cmd output '{}':rc {}".format(cmd, rc)) + raise click.Abort() + + try: + config_json = json.loads(stdout) + except JSONDecodeError as e: + click.echo("Failed to load output '{}':{}".format(cmd, e)) + raise click.Abort() + + return config_json + # Lazy global class instance for SONiC interface name to alias conversion iface_alias_converter = lazy_object_proxy.Proxy(lambda: clicommon.InterfaceAliasConverter()) @@ -1247,25 +1265,24 @@ def runningconfiguration(): @click.option('--verbose', is_flag=True, help="Enable verbose output") def all(verbose): """Show full running configuration""" - cmd = ['sonic-cfggen', '-d', '--print-data'] - stdout, rc = get_cmd_output(cmd) - if rc: - click.echo("Failed to get cmd output '{}':rc {}".format(cmd, rc)) - raise click.Abort() + output = {} + bgpraw_cmd = "show running-config" - try: - output = json.loads(stdout) - except JSONDecodeError as e: - click.echo("Failed to load output '{}':{}".format(cmd, e)) - raise click.Abort() + import utilities_common.bgp_util as bgp_util + # In multiaisc, the namespace is changed to 'localhost' by design + host_config = get_config_json_by_namespace(multi_asic.DEFAULT_NAMESPACE) + output['localhost'] = host_config - if not multi_asic.is_multi_asic(): - bgpraw_cmd = [constants.RVTYSH_COMMAND, '-c', 'show running-config'] - bgpraw, rc = get_cmd_output(bgpraw_cmd) - if rc: - bgpraw = "" - output['bgpraw'] = bgpraw - click.echo(json.dumps(output, indent=4)) + if multi_asic.is_multi_asic(): + ns_list = multi_asic.get_namespace_list() + for ns in ns_list: + ns_config = get_config_json_by_namespace(ns) + ns_config['bgpraw'] = bgp_util.run_bgp_show_command(bgpraw_cmd, ns) + output[ns] = ns_config + click.echo(json.dumps(output, indent=4)) + else: + host_config['bgpraw'] = bgp_util.run_bgp_show_command(bgpraw_cmd) + click.echo(json.dumps(output['localhost'], indent=4)) # 'acl' subcommand ("show runningconfiguration acl") diff --git a/tests/show_test.py b/tests/show_test.py index ddb59078b..c44a27953 100644 --- a/tests/show_test.py +++ b/tests/show_test.py @@ -1,7 +1,9 @@ import os import sys import pytest +import importlib import show.main as show +import utilities_common.bgp_util as bgp_util from click.testing import CliRunner from unittest import mock from unittest.mock import call, MagicMock, patch @@ -19,6 +21,12 @@ class TestShowRunAllCommands(object): def setup_class(cls): print("SETUP") os.environ["UTILITIES_UNIT_TESTING"] = "1" + cls._old_run_bgp_command = bgp_util.run_bgp_command + bgp_util.run_bgp_command = mock.MagicMock( + return_value=cls.mock_run_bgp_command()) + + def mock_run_bgp_command(): + return "" def test_show_runningconfiguration_all_json_loads_failure(self): def get_cmd_output_side_effect(*args, **kwargs): @@ -42,16 +50,62 @@ def get_cmd_output_side_effect(*args, **kwargs): with mock.patch('show.main.get_cmd_output', mock.MagicMock(side_effect=get_cmd_output_side_effect)) as mock_get_cmd_output: result = CliRunner().invoke(show.cli.commands['runningconfiguration'].commands['all'], []) - assert mock_get_cmd_output.call_count == 2 + assert result.exit_code == 0 + assert mock_get_cmd_output.call_count == 1 + assert mock_get_cmd_output.call_args_list == [ + call(['sonic-cfggen', '-d', '--print-data'])] + + @classmethod + def teardown_class(cls): + print("TEARDOWN") + bgp_util.run_bgp_command = cls._old_run_bgp_command + os.environ["PATH"] = os.pathsep.join(os.environ["PATH"].split(os.pathsep)[:-1]) + os.environ["UTILITIES_UNIT_TESTING"] = "0" + + +class TestShowRunAllCommandsMasic(object): + @classmethod + def setup_class(cls): + print("SETUP") + os.environ['UTILITIES_UNIT_TESTING'] = "2" + os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = "multi_asic" + cls._old_run_bgp_command = bgp_util.run_bgp_command + bgp_util.run_bgp_command = mock.MagicMock( + return_value=cls.mock_run_bgp_command()) + # change to multi asic config + from .mock_tables import dbconnector + from .mock_tables import mock_multi_asic + importlib.reload(mock_multi_asic) + dbconnector.load_namespace_config() + + def mock_run_bgp_command(): + return "" + + def test_show_runningconfiguration_all_masic(self): + def get_cmd_output_side_effect(*args, **kwargs): + return "{}", 0 + with mock.patch('show.main.get_cmd_output', + mock.MagicMock(side_effect=get_cmd_output_side_effect)) as mock_get_cmd_output: + result = CliRunner().invoke(show.cli.commands['runningconfiguration'].commands['all'], []) + assert result.exit_code == 0 + assert mock_get_cmd_output.call_count == 3 assert mock_get_cmd_output.call_args_list == [ call(['sonic-cfggen', '-d', '--print-data']), - call(['rvtysh', '-c', 'show running-config'])] + call(['sonic-cfggen', '-d', '--print-data', '-n', 'asic0']), + call(['sonic-cfggen', '-d', '--print-data', '-n', 'asic1'])] @classmethod def teardown_class(cls): print("TEARDOWN") + bgp_util.run_bgp_command = cls._old_run_bgp_command os.environ["PATH"] = os.pathsep.join(os.environ["PATH"].split(os.pathsep)[:-1]) os.environ["UTILITIES_UNIT_TESTING"] = "0" + # change back to single asic config + from .mock_tables import dbconnector + from .mock_tables import mock_single_asic + importlib.reload(mock_single_asic) + dbconnector.load_namespace_config() + @patch('show.main.run_command') @pytest.mark.parametrize(