From 913220a97d3e352eb9ad6940453498aaee754d64 Mon Sep 17 00:00:00 2001 From: bktsim Date: Thu, 9 Nov 2023 11:23:32 -0800 Subject: [PATCH] Fixes dropstat multi-asic behaviour by using multi-asic helpers and ensuring that dropstat iterates through correct namespaces when 'show' command is run. --- scripts/dropstat | 25 ++++++++++--- show/dropcounters.py | 7 +++- tests/multi_asic_dropstat_test.py | 59 +++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 tests/multi_asic_dropstat_test.py diff --git a/scripts/dropstat b/scripts/dropstat index 4e9f5bb4d03..e0cdf512ea0 100755 --- a/scripts/dropstat +++ b/scripts/dropstat @@ -20,6 +20,8 @@ import sys from collections import OrderedDict from natsort import natsorted from tabulate import tabulate +from sonic_py_common import multi_asic +from utilities_common.general import load_db_config # mock the redis for unit test purposes # try: @@ -28,9 +30,12 @@ try: test_path = os.path.join(modules_path, "tests") sys.path.insert(0, modules_path) sys.path.insert(0, test_path) - import mock_tables.dbconnector + from tests.mock_tables import dbconnector socket.gethostname = lambda: 'sonic_drops_test' os.getuid = lambda: 27 + if os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] == "multi_asic": + import tests.mock_tables.mock_multi_asic + dbconnector.load_namespace_config() except KeyError: pass @@ -85,11 +90,11 @@ def get_dropstat_dir(): class DropStat(object): - def __init__(self): - self.config_db = ConfigDBConnector() + def __init__(self, namespace): + self.config_db = ConfigDBConnector(namespace=namespace, use_unix_socket_path=True if namespace else False) self.config_db.connect() - self.db = SonicV2Connector(use_unix_socket_path=False) + self.db = SonicV2Connector(namespace=namespace, use_unix_socket_path=True if namespace else False) self.db.connect(self.db.COUNTERS_DB) self.db.connect(self.db.ASIC_DB) self.db.connect(self.db.APPL_DB) @@ -403,6 +408,7 @@ Examples: # Variables parser.add_argument('-g', '--group', type=str, help='The group of the target drop counter', default=None) parser.add_argument('-t', '--type', type=str, help='The type of the target drop counter', default=None) + parser.add_argument('-n', '--namespace', type=str, help='Namespace name', default=None) args = parser.parse_args() @@ -410,8 +416,17 @@ Examples: group = args.group counter_type = args.type + namespace = args.namespace - dcstat = DropStat() + load_db_config() + namespaces = multi_asic.get_namespace_list() + if namespace and namespace not in namespaces: + raise Exception("Input arguments error. Namespaces must be one of", *namespaces) + + if multi_asic.is_multi_asic() and not namespace: + raise Exception("Input arguments error. Namespace must be specified for multi-asic devices.") + + dcstat = DropStat(namespace) if command == 'clear': dcstat.clear_drop_counts() elif command == 'show': diff --git a/show/dropcounters.py b/show/dropcounters.py index 30779b9364c..0f2916a8b47 100644 --- a/show/dropcounters.py +++ b/show/dropcounters.py @@ -1,5 +1,6 @@ import click import utilities_common.cli as clicommon +import utilities_common.multi_asic as multi_asic_util # @@ -41,7 +42,8 @@ def capabilities(verbose): @click.option('-g', '--group', required=False) @click.option('-t', '--counter_type', required=False) @click.option('--verbose', is_flag=True, help="Enable verbose output") -def counts(group, counter_type, verbose): +@multi_asic_util.multi_asic_click_option_namespace +def counts(group, counter_type, verbose, namespace): """Show drop counts""" cmd = ['dropstat', '-c', 'show'] @@ -50,5 +52,8 @@ def counts(group, counter_type, verbose): if counter_type: cmd += ['-t', str(counter_type)] + + if namespace: + cmd += ['-n', str(namespace)] clicommon.run_command(cmd, display_cmd=verbose) diff --git a/tests/multi_asic_dropstat_test.py b/tests/multi_asic_dropstat_test.py new file mode 100644 index 00000000000..5ced0c3a929 --- /dev/null +++ b/tests/multi_asic_dropstat_test.py @@ -0,0 +1,59 @@ +import os +import sys +import shutil +from utils import get_result_and_return_code + +test_path = os.path.dirname(os.path.abspath(__file__)) +modules_path = os.path.dirname(test_path) +scripts_path = os.path.join(modules_path, "scripts") +sys.path.insert(0, test_path) +sys.path.insert(0, modules_path) + +dropstat_path = "/tmp/dropstat-27" + +dropstat_masic_result="""\ + IFACE STATE RX_ERR RX_DROPS TX_ERR TX_DROPS DEBUG_0 DEBUG_2 +------------ ------- -------- ---------- -------- ---------- --------- --------- + Ethernet0 U 0 0 0 0 0 0 + Ethernet4 U 0 0 0 0 0 0 +Ethernet-BP0 U 0 1000 0 0 800 100 +Ethernet-BP4 U 0 1000 0 0 800 100 + + DEVICE DEBUG_1 +---------------- --------- +sonic_drops_test 1000 +""" + +class TestMultiAsicDropstat(object): + @classmethod + def setup_class(cls): + if os.path.exists(dropstat_path): + shutil.rmtree(dropstat_path) + os.environ["PATH"] += os.pathsep + scripts_path + os.environ["UTILITIES_UNIT_TESTING"] = "1" + os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = "multi_asic" + print("SETUP") + + def test_show_pg_drop_masic(self): + return_code, result = get_result_and_return_code([ + 'dropstat', '-c', 'show', '-n', 'asic0' + ]) + print("return_code: {}".format(return_code)) + print("result = {}".format(result)) + assert return_code == 0 + assert result == dropstat_masic_result + + def test_show_pg_drop_masic_invalid_ns(self): + return_code, result = get_result_and_return_code([ + 'dropstat', '-c', 'show', '-n', 'asic5' + ]) + print("return_code: {}".format(return_code)) + print("result = {}".format(result)) + assert return_code == 1 + + @classmethod + def teardown_class(cls): + os.environ["PATH"] = os.pathsep.join(os.environ["PATH"].split(os.pathsep)[:-1]) + os.environ['UTILITIES_UNIT_TESTING'] = "0" + os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = "" + print("TEARDOWN")