From 5e84f25258feb7fcff84bb94091ee57721165ee9 Mon Sep 17 00:00:00 2001 From: jingwenxie Date: Tue, 31 Jan 2023 00:46:30 +0800 Subject: [PATCH] [202205][show] Add bgpraw to show run all (#2624) What I did #2537 #2623 #2628 202205 branch doesn't have from sonic_py_common import multi_asic, which results in test failure and build error. The only difference between this PR and original PR is just one line of import. How to verify it Tested locally on 202205 build and pass the build process. Test to make sure it pass GCU tests. --- generic_config_updater/gu_common.py | 4 +- show/main.py | 28 ++++++++-- .../generic_config_updater/gu_common_test.py | 14 ++++- tests/show_test.py | 51 +++++++++++++++++++ 4 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 tests/show_test.py diff --git a/generic_config_updater/gu_common.py b/generic_config_updater/gu_common.py index 0cfaa73943..87fb4adfd7 100644 --- a/generic_config_updater/gu_common.py +++ b/generic_config_updater/gu_common.py @@ -48,7 +48,9 @@ def __init__(self, yang_dir = YANG_DIR): def get_config_db_as_json(self): text = self._get_config_db_as_text() - return json.loads(text) + config_db_json = json.loads(text) + config_db_json.pop("bgpraw", None) + return config_db_json def _get_config_db_as_text(self): # TODO: Getting configs from CLI is very slow, need to get it from sonic-cffgen directly diff --git a/show/main.py b/show/main.py index fc453ab899..74c5c9d899 100755 --- a/show/main.py +++ b/show/main.py @@ -10,7 +10,7 @@ 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 sonic_py_common import device_info, multi_asic from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector from tabulate import tabulate from utilities_common import util_base @@ -18,6 +18,7 @@ from datetime import datetime import utilities_common.constants as constants from utilities_common.general import load_db_config +from json.decoder import JSONDecodeError # mock the redis for unit test purposes # try: @@ -127,6 +128,10 @@ def run_command(command, display_cmd=False, return_cmd=False): if rc != 0: sys.exit(rc) +def get_cmd_output(cmd): + proc = subprocess.Popen(cmd, text=True, stdout=subprocess.PIPE) + return proc.communicate()[0], proc.returncode + # Lazy global class instance for SONiC interface name to alias conversion iface_alias_converter = lazy_object_proxy.Proxy(lambda: clicommon.InterfaceAliasConverter()) @@ -1229,8 +1234,25 @@ 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" - run_command(cmd, display_cmd=verbose) + 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() + + try: + output = json.loads(stdout) + except JSONDecodeError as e: + click.echo("Failed to load output '{}':{}".format(cmd, e)) + raise click.Abort() + + 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)) # 'acl' subcommand ("show runningconfiguration acl") diff --git a/tests/generic_config_updater/gu_common_test.py b/tests/generic_config_updater/gu_common_test.py index 6ba4923664..a767e641a5 100644 --- a/tests/generic_config_updater/gu_common_test.py +++ b/tests/generic_config_updater/gu_common_test.py @@ -3,12 +3,24 @@ import jsonpatch import sonic_yang import unittest -from unittest.mock import MagicMock, Mock +from unittest.mock import MagicMock, Mock, patch from .gutest_helpers import create_side_effect_dict, Files import generic_config_updater.gu_common as gu_common class TestDryRunConfigWrapper(unittest.TestCase): + @patch('generic_config_updater.gu_common.subprocess.Popen') + def test_get_config_db_as_json(self, mock_popen): + config_wrapper = gu_common.DryRunConfigWrapper() + mock_proc = MagicMock() + mock_proc.communicate = MagicMock( + return_value=('{"PORT": {}, "bgpraw": ""}', None)) + mock_proc.returncode = 0 + mock_popen.return_value = mock_proc + actual = config_wrapper.get_config_db_as_json() + expected = {"PORT": {}} + self.assertDictEqual(actual, expected) + def test_get_config_db_as_json__returns_imitated_config_db(self): # Arrange config_wrapper = gu_common.DryRunConfigWrapper(Files.CONFIG_DB_AS_JSON) diff --git a/tests/show_test.py b/tests/show_test.py new file mode 100644 index 0000000000..87c1b5a17e --- /dev/null +++ b/tests/show_test.py @@ -0,0 +1,51 @@ +import os +import sys +import show.main as show +from click.testing import CliRunner +from unittest import mock +from unittest.mock import call, MagicMock + +test_path = os.path.dirname(os.path.abspath(__file__)) +modules_path = os.path.dirname(test_path) +sys.path.insert(0, test_path) +sys.path.insert(0, modules_path) + + +class TestShowRunAllCommands(object): + @classmethod + def setup_class(cls): + print("SETUP") + os.environ["UTILITIES_UNIT_TESTING"] = "1" + + def test_show_runningconfiguration_all_json_loads_failure(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 + + def test_show_runningconfiguration_all_get_cmd_ouput_failure(self): + def get_cmd_output_side_effect(*args, **kwargs): + return "{}", 2 + 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 + + def test_show_runningconfiguration_all(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 mock_get_cmd_output.call_count == 2 + assert mock_get_cmd_output.call_args_list == [ + call(['sonic-cfggen', '-d', '--print-data']), + call(['rvtysh', '-c', 'show running-config'])] + + @classmethod + def teardown_class(cls): + print("TEARDOWN") + os.environ["PATH"] = os.pathsep.join(os.environ["PATH"].split(os.pathsep)[:-1]) + os.environ["UTILITIES_UNIT_TESTING"] = "0"