From 62e44d9008b6a2a72684134a7795c2ddb167493b Mon Sep 17 00:00:00 2001 From: Mahesh Maddikayala <10645050+smaheshm@users.noreply.github.com> Date: Thu, 27 Aug 2020 11:32:52 -0700 Subject: [PATCH] [CLI][PFC] Add multi ASIC options for pfcstat and 'show pfc counters' (#1057) * Add multi ASIC options for pfcstat and 'show pfc counters' Options: -d, --display [all|frontend] Show internal interfaces [default:frontend] -n, --namespace [asic0|asic1|asic2|asic3|asic4|asic5] * Added unit tests for 'pfcstat' show commands. --- scripts/pfcstat | 102 +++++++++--- show/main.py | 11 +- tests/mock_tables/asic0/counters_db.json | 85 +++++++++- tests/mock_tables/counters_db.json | 54 ++++++- tests/multi_asic_intfutil_test.py | 56 +++---- tests/pfcstat_test.py | 197 +++++++++++++++++++++++ tests/utils.py | 18 +++ 7 files changed, 455 insertions(+), 68 deletions(-) create mode 100644 tests/pfcstat_test.py create mode 100644 tests/utils.py diff --git a/scripts/pfcstat b/scripts/pfcstat index 7649c07f4d98..eff3f09cfe77 100755 --- a/scripts/pfcstat +++ b/scripts/pfcstat @@ -2,11 +2,10 @@ ##################################################################### # -# pfcstat is a tool for summarizing Priority-based Flow Control (PFC) statistics. +# pfcstat is a tool for summarizing Priority-based Flow Control (PFC) statistics. # ##################################################################### -import swsssdk import sys import argparse import cPickle as pickle @@ -17,6 +16,24 @@ from collections import namedtuple, OrderedDict from natsort import natsorted from tabulate import tabulate +from sonic_py_common.multi_asic import get_external_ports +from utilities_common import multi_asic as multi_asic_util +from utilities_common import constants + +# 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 + mock_tables.dbconnector.load_namespace_config() + +except KeyError: + pass PStats = namedtuple("PStats", "pfc0, pfc1, pfc2, pfc3, pfc4, pfc5, pfc6, pfc7") header_Rx = ['Port Rx', 'PFC0', 'PFC1', 'PFC2', 'PFC3', 'PFC4', 'PFC5', 'PFC6', 'PFC7'] @@ -51,11 +68,14 @@ COUNTER_TABLE_PREFIX = "COUNTERS:" COUNTERS_PORT_NAME_MAP = "COUNTERS_PORT_NAME_MAP" class Pfcstat(object): - def __init__(self): - self.db = swsssdk.SonicV2Connector(host='127.0.0.1') - self.db.connect(self.db.COUNTERS_DB) - - def get_cnstat(self, rx): + def __init__(self, namespace, display): + self.multi_asic = multi_asic_util.MultiAsic(display, namespace) + self.db = None + self.config_db = None + self.cnstat_dict = OrderedDict() + + @multi_asic_util.run_on_multi_asic + def collect_cnstat(self, rx): """ Get the counters info from database. """ @@ -70,7 +90,9 @@ class Pfcstat(object): bucket_dict = counter_bucket_tx_dict for counter_name, pos in bucket_dict.iteritems(): full_table_id = COUNTER_TABLE_PREFIX + table_id - counter_data = self.db.get(self.db.COUNTERS_DB, full_table_id, counter_name) + counter_data = self.db.get( + self.db.COUNTERS_DB, full_table_id, counter_name + ) if counter_data is None: fields[pos] = STATUS_NA else: @@ -79,15 +101,34 @@ class Pfcstat(object): return cntr # Get the info from database - counter_port_name_map = self.db.get_all(self.db.COUNTERS_DB, COUNTERS_PORT_NAME_MAP) + counter_port_name_map = self.db.get_all( + self.db.COUNTERS_DB, COUNTERS_PORT_NAME_MAP + ) + if counter_port_name_map is None: + return + display_ports_set = set(counter_port_name_map.keys()) + if self.multi_asic.display_option == constants.DISPLAY_EXTERNAL: + display_ports_set = get_external_ports( + display_ports_set, self.multi_asic.current_namespace + ) # Build a dictionary of the stats cnstat_dict = OrderedDict() cnstat_dict['time'] = datetime.datetime.now() - if counter_port_name_map is None: - return cnstat_dict - for port in natsorted(counter_port_name_map): - cnstat_dict[port] = get_counters(counter_port_name_map[port]) - return cnstat_dict + if counter_port_name_map is not None: + for port in natsorted(counter_port_name_map): + if port in display_ports_set: + cnstat_dict[port] = get_counters( + counter_port_name_map[port] + ) + self.cnstat_dict.update(cnstat_dict) + + def get_cnstat(self, rx): + """ + Get the counters info from database. + """ + self.cnstat_dict.clear() + self.collect_cnstat(rx) + return self.cnstat_dict def cnstat_print(self, cnstat_dict, rx): """ @@ -163,10 +204,22 @@ Examples: pfcstat pfcstat -c pfcstat -d + pfcstat -n asic1 + pfcstat -s all -n asic0 """) - parser.add_argument('-c', '--clear', action='store_true', help='Clear previous stats and save new ones') - parser.add_argument('-d', '--delete', action='store_true', help='Delete saved stats') + parser.add_argument( '-c', '--clear', action='store_true', + help='Clear previous stats and save new ones' + ) + parser.add_argument( + '-d', '--delete', action='store_true', help='Delete saved stats' + ) + parser.add_argument('-s', '--show', default=constants.DISPLAY_EXTERNAL, + help='Display all interfaces or only external interfaces' + ) + parser.add_argument('-n', '--namespace', default=None, + help='Display interfaces for specific namespace' + ) args = parser.parse_args() save_fresh_stats = args.clear @@ -175,15 +228,20 @@ Examples: uid = str(os.getuid()) cnstat_file = uid - cnstat_dir = "/tmp/pfcstat-" + uid - cnstat_fqn_file_rx = cnstat_dir + "/" + cnstat_file + "rx" - cnstat_fqn_file_tx = cnstat_dir + "/" + cnstat_file + "tx" + cnstat_dir = os.path.join(os.sep, "tmp", "pfcstat-{}".format(uid)) + cnstat_fqn_file_rx = os.path.join(cnstat_dir, "{}rx".format(cnstat_file)) + cnstat_fqn_file_tx = os.path.join(cnstat_dir, "{}tx".format(cnstat_file)) + + # if '-c' option is provided get stats from all (frontend and backend) ports + if save_fresh_stats: + args.namespace = None + args.show = constants.DISPLAY_ALL - pfcstat = Pfcstat() + pfcstat = Pfcstat(args.namespace, args.show) if delete_all_stats: for file in os.listdir(cnstat_dir): - os.remove(cnstat_dir + "/" + file) + os.remove(os.path.join(cnstat_dir, file)) try: os.rmdir(cnstat_dir) @@ -235,7 +293,7 @@ Examples: else: pfcstat.cnstat_print(cnstat_dict_rx, True) - print() + print("") """ Print the counters of pfc tx counter diff --git a/show/main.py b/show/main.py index 9feb80ff2781..f63501dacb65 100755 --- a/show/main.py +++ b/show/main.py @@ -16,8 +16,10 @@ from swsssdk import ConfigDBConnector from swsssdk import SonicV2Connector from tabulate import tabulate -from utilities_common.db import Db + import utilities_common.cli as clicommon +from utilities_common.db import Db +from utilities_common.multi_asic import multi_asic_click_options import mlnx import feature @@ -366,11 +368,14 @@ def pfc(): # 'counters' subcommand ("show interfaces pfccounters") @pfc.command() +@multi_asic_click_options @click.option('--verbose', is_flag=True, help="Enable verbose output") -def counters(verbose): +def counters(namespace, display, verbose): """Show pfc counters""" - cmd = "pfcstat" + cmd = "pfcstat -s {}".format(display) + if namespace is not None: + cmd += " -n {}".format(namespace) run_command(cmd, display_cmd=verbose) diff --git a/tests/mock_tables/asic0/counters_db.json b/tests/mock_tables/asic0/counters_db.json index 2b2b6002808a..38d5e445d78b 100644 --- a/tests/mock_tables/asic0/counters_db.json +++ b/tests/mock_tables/asic0/counters_db.json @@ -123,19 +123,89 @@ "SAI_PORT_STAT_IF_IN_ERRORS": "10", "SAI_PORT_STAT_IF_IN_DISCARDS": "100", "SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "80", - "SAI_PORT_STAT_OUT_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS": "20" + "SAI_PORT_STAT_OUT_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS": "20", + "SAI_PORT_STAT_PFC_0_RX_PKTS": "20", + "SAI_PORT_STAT_PFC_1_RX_PKTS": "21", + "SAI_PORT_STAT_PFC_2_RX_PKTS": "22", + "SAI_PORT_STAT_PFC_3_RX_PKTS": "23", + "SAI_PORT_STAT_PFC_4_RX_PKTS": "24", + "SAI_PORT_STAT_PFC_5_RX_PKTS": "25", + "SAI_PORT_STAT_PFC_6_RX_PKTS": "26", + "SAI_PORT_STAT_PFC_7_RX_PKTS": "27", + "SAI_PORT_STAT_PFC_0_TX_PKTS": "400", + "SAI_PORT_STAT_PFC_1_TX_PKTS": "201", + "SAI_PORT_STAT_PFC_2_TX_PKTS": "202", + "SAI_PORT_STAT_PFC_3_TX_PKTS": "203", + "SAI_PORT_STAT_PFC_4_TX_PKTS": "204", + "SAI_PORT_STAT_PFC_5_TX_PKTS": "205", + "SAI_PORT_STAT_PFC_6_TX_PKTS": "206", + "SAI_PORT_STAT_PFC_7_TX_PKTS": "207" }, "COUNTERS:oid:0x1000000000004": { "SAI_PORT_STAT_IF_IN_ERRORS": "0", "SAI_PORT_STAT_IF_IN_DISCARDS": "1000", "SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "800", - "SAI_PORT_STAT_OUT_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS": "100" + "SAI_PORT_STAT_OUT_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS": "100", + "SAI_PORT_STAT_PFC_0_RX_PKTS": "40", + "SAI_PORT_STAT_PFC_1_RX_PKTS": "41", + "SAI_PORT_STAT_PFC_2_RX_PKTS": "42", + "SAI_PORT_STAT_PFC_3_RX_PKTS": "43", + "SAI_PORT_STAT_PFC_4_RX_PKTS": "44", + "SAI_PORT_STAT_PFC_5_RX_PKTS": "45", + "SAI_PORT_STAT_PFC_6_RX_PKTS": "46", + "SAI_PORT_STAT_PFC_7_RX_PKTS": "47", + "SAI_PORT_STAT_PFC_0_TX_PKTS": "400", + "SAI_PORT_STAT_PFC_1_TX_PKTS": "401", + "SAI_PORT_STAT_PFC_2_TX_PKTS": "402", + "SAI_PORT_STAT_PFC_3_TX_PKTS": "403", + "SAI_PORT_STAT_PFC_4_TX_PKTS": "404", + "SAI_PORT_STAT_PFC_5_TX_PKTS": "405", + "SAI_PORT_STAT_PFC_6_TX_PKTS": "406", + "SAI_PORT_STAT_PFC_7_TX_PKTS": "407" }, "COUNTERS:oid:0x1000000000006": { - "SAI_PORT_STAT_IF_IN_ERRORS": "100", - "SAI_PORT_STAT_IF_IN_DISCARDS": "10", - "SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "10", - "SAI_PORT_STAT_OUT_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS": "0" + "SAI_PORT_STAT_IF_IN_ERRORS": "0", + "SAI_PORT_STAT_IF_IN_DISCARDS": "1000", + "SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "800", + "SAI_PORT_STAT_OUT_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS": "100", + "SAI_PORT_STAT_PFC_0_RX_PKTS": "60", + "SAI_PORT_STAT_PFC_1_RX_PKTS": "61", + "SAI_PORT_STAT_PFC_2_RX_PKTS": "62", + "SAI_PORT_STAT_PFC_3_RX_PKTS": "63", + "SAI_PORT_STAT_PFC_4_RX_PKTS": "64", + "SAI_PORT_STAT_PFC_5_RX_PKTS": "65", + "SAI_PORT_STAT_PFC_6_RX_PKTS": "66", + "SAI_PORT_STAT_PFC_7_RX_PKTS": "67", + "SAI_PORT_STAT_PFC_0_TX_PKTS": "600", + "SAI_PORT_STAT_PFC_1_TX_PKTS": "601", + "SAI_PORT_STAT_PFC_2_TX_PKTS": "602", + "SAI_PORT_STAT_PFC_3_TX_PKTS": "603", + "SAI_PORT_STAT_PFC_4_TX_PKTS": "604", + "SAI_PORT_STAT_PFC_5_TX_PKTS": "605", + "SAI_PORT_STAT_PFC_6_TX_PKTS": "606", + "SAI_PORT_STAT_PFC_7_TX_PKTS": "607" + }, + "COUNTERS:oid:0x1000000000008": { + "SAI_PORT_STAT_IF_IN_ERRORS": "0", + "SAI_PORT_STAT_IF_IN_DISCARDS": "1000", + "SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "800", + "SAI_PORT_STAT_OUT_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS": "100", + "SAI_PORT_STAT_PFC_0_RX_PKTS": "80", + "SAI_PORT_STAT_PFC_1_RX_PKTS": "81", + "SAI_PORT_STAT_PFC_2_RX_PKTS": "82", + "SAI_PORT_STAT_PFC_3_RX_PKTS": "83", + "SAI_PORT_STAT_PFC_4_RX_PKTS": "84", + "SAI_PORT_STAT_PFC_5_RX_PKTS": "85", + "SAI_PORT_STAT_PFC_6_RX_PKTS": "86", + "SAI_PORT_STAT_PFC_7_RX_PKTS": "87", + "SAI_PORT_STAT_PFC_0_TX_PKTS": "800", + "SAI_PORT_STAT_PFC_1_TX_PKTS": "801", + "SAI_PORT_STAT_PFC_2_TX_PKTS": "802", + "SAI_PORT_STAT_PFC_3_TX_PKTS": "803", + "SAI_PORT_STAT_PFC_4_TX_PKTS": "804", + "SAI_PORT_STAT_PFC_5_TX_PKTS": "805", + "SAI_PORT_STAT_PFC_6_TX_PKTS": "806", + "SAI_PORT_STAT_PFC_7_TX_PKTS": "807" }, "COUNTERS:oid:0x21000000000000": { "SAI_SWITCH_STAT_IN_DROP_REASON_RANGE_BASE": "1000" @@ -143,7 +213,8 @@ "COUNTERS_PORT_NAME_MAP": { "Ethernet0": "oid:0x1000000000002", "Ethernet4": "oid:0x1000000000004", - "Ethernet8": "oid:0x1000000000006" + "Ethernet-BP0": "oid:0x1000000000006", + "Ethernet-BP4": "oid:0x1000000000008" }, "COUNTERS_LAG_NAME_MAP": { "PortChannel0001": "oid:0x60000000005a1", diff --git a/tests/mock_tables/counters_db.json b/tests/mock_tables/counters_db.json index 2b2b6002808a..6468f74ded56 100644 --- a/tests/mock_tables/counters_db.json +++ b/tests/mock_tables/counters_db.json @@ -123,19 +123,67 @@ "SAI_PORT_STAT_IF_IN_ERRORS": "10", "SAI_PORT_STAT_IF_IN_DISCARDS": "100", "SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "80", - "SAI_PORT_STAT_OUT_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS": "20" + "SAI_PORT_STAT_OUT_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS": "20", + "SAI_PORT_STAT_PFC_0_RX_PKTS": "0", + "SAI_PORT_STAT_PFC_1_RX_PKTS": "0", + "SAI_PORT_STAT_PFC_2_RX_PKTS": "0", + "SAI_PORT_STAT_PFC_3_RX_PKTS": "0", + "SAI_PORT_STAT_PFC_4_RX_PKTS": "0", + "SAI_PORT_STAT_PFC_5_RX_PKTS": "0", + "SAI_PORT_STAT_PFC_6_RX_PKTS": "0", + "SAI_PORT_STAT_PFC_7_RX_PKTS": "0", + "SAI_PORT_STAT_PFC_0_TX_PKTS": "0", + "SAI_PORT_STAT_PFC_1_TX_PKTS": "0", + "SAI_PORT_STAT_PFC_2_TX_PKTS": "0", + "SAI_PORT_STAT_PFC_3_TX_PKTS": "0", + "SAI_PORT_STAT_PFC_4_TX_PKTS": "0", + "SAI_PORT_STAT_PFC_5_TX_PKTS": "0", + "SAI_PORT_STAT_PFC_6_TX_PKTS": "0", + "SAI_PORT_STAT_PFC_7_TX_PKTS": "0" }, "COUNTERS:oid:0x1000000000004": { "SAI_PORT_STAT_IF_IN_ERRORS": "0", "SAI_PORT_STAT_IF_IN_DISCARDS": "1000", "SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "800", - "SAI_PORT_STAT_OUT_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS": "100" + "SAI_PORT_STAT_OUT_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS": "100", + "SAI_PORT_STAT_PFC_0_RX_PKTS": "40", + "SAI_PORT_STAT_PFC_1_RX_PKTS": "41", + "SAI_PORT_STAT_PFC_2_RX_PKTS": "42", + "SAI_PORT_STAT_PFC_3_RX_PKTS": "43", + "SAI_PORT_STAT_PFC_4_RX_PKTS": "44", + "SAI_PORT_STAT_PFC_5_RX_PKTS": "45", + "SAI_PORT_STAT_PFC_6_RX_PKTS": "46", + "SAI_PORT_STAT_PFC_7_RX_PKTS": "47", + "SAI_PORT_STAT_PFC_0_TX_PKTS": "400", + "SAI_PORT_STAT_PFC_1_TX_PKTS": "401", + "SAI_PORT_STAT_PFC_2_TX_PKTS": "402", + "SAI_PORT_STAT_PFC_3_TX_PKTS": "403", + "SAI_PORT_STAT_PFC_4_TX_PKTS": "404", + "SAI_PORT_STAT_PFC_5_TX_PKTS": "405", + "SAI_PORT_STAT_PFC_6_TX_PKTS": "406", + "SAI_PORT_STAT_PFC_7_TX_PKTS": "407" }, "COUNTERS:oid:0x1000000000006": { "SAI_PORT_STAT_IF_IN_ERRORS": "100", "SAI_PORT_STAT_IF_IN_DISCARDS": "10", "SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "10", - "SAI_PORT_STAT_OUT_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS": "0" + "SAI_PORT_STAT_OUT_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS": "0", + "SAI_PORT_STAT_PFC_0_RX_PKTS": "80", + "SAI_PORT_STAT_PFC_1_RX_PKTS": "81", + "SAI_PORT_STAT_PFC_2_RX_PKTS": "82", + "SAI_PORT_STAT_PFC_3_RX_PKTS": "83", + "SAI_PORT_STAT_PFC_4_RX_PKTS": "84", + "SAI_PORT_STAT_PFC_5_RX_PKTS": "85", + "SAI_PORT_STAT_PFC_6_RX_PKTS": "86", + "SAI_PORT_STAT_PFC_7_RX_PKTS": "87", + "SAI_PORT_STAT_PFC_0_TX_PKTS": "800", + "SAI_PORT_STAT_PFC_1_TX_PKTS": "801", + "SAI_PORT_STAT_PFC_2_TX_PKTS": "802", + "SAI_PORT_STAT_PFC_3_TX_PKTS": "803", + "SAI_PORT_STAT_PFC_4_TX_PKTS": "804", + "SAI_PORT_STAT_PFC_5_TX_PKTS": "805", + "SAI_PORT_STAT_PFC_6_TX_PKTS": "806", + "SAI_PORT_STAT_PFC_7_TX_PKTS": "807" }, "COUNTERS:oid:0x21000000000000": { "SAI_SWITCH_STAT_IN_DROP_REASON_RANGE_BASE": "1000" diff --git a/tests/multi_asic_intfutil_test.py b/tests/multi_asic_intfutil_test.py index 621828dd4ddc..da1875e7dd67 100644 --- a/tests/multi_asic_intfutil_test.py +++ b/tests/multi_asic_intfutil_test.py @@ -3,6 +3,8 @@ from click.testing import CliRunner +from utils import get_result_and_return_code + root_path = os.path.dirname(os.path.abspath(__file__)) modules_path = os.path.dirname(root_path) scripts_path = os.path.join(modules_path, "scripts") @@ -93,85 +95,73 @@ def setup_class(cls): def setUp(self): self.runner = CliRunner() - def get_result_and_return_code(self, cmd): - return_code = 0 - try: - output = subprocess.check_output( - cmd, stderr=subprocess.STDOUT, shell=True) - except subprocess.CalledProcessError as e: - return_code = e.returncode - #store only the error, no need for the traceback - output = e.output.strip().split("\n")[-1] - - return(return_code, output) - def test_multi_asic_interface_status_all(self): - return_code, result = self.get_result_and_return_code( 'intfutil -c status -d all') + return_code, result = get_result_and_return_code( 'intfutil -c status -d all') print("return_code: {}".format(return_code)) - print("result = {}".format(result)) + print("result = {}".format(result)) assert return_code == 0 assert result == intf_status_all def test_multi_asic_interface_status(self): - return_code, result = self.get_result_and_return_code('intfutil -c status') + return_code, result = get_result_and_return_code('intfutil -c status') print("return_code: {}".format(return_code)) - print("result = {}".format(result)) + print("result = {}".format(result)) assert return_code == 0 assert result == intf_status def test_multi_asic_interface_status_asic0_all(self): - return_code, result = self.get_result_and_return_code('intfutil -c status -n asic0 -d all') + return_code, result = get_result_and_return_code('intfutil -c status -n asic0 -d all') print("return_code: {}".format(return_code)) - print("result = {}".format(result)) + print("result = {}".format(result)) assert return_code == 0 assert result == intf_status_asic0_all def test_multi_asic_interface_status_asic0(self): - return_code, result = self.get_result_and_return_code('intfutil -c status -n asic0') + return_code, result = get_result_and_return_code('intfutil -c status -n asic0') print("return_code: {}".format(return_code)) - print("result = {}".format(result)) + print("result = {}".format(result)) assert return_code == 0 assert result == intf_status_asic0 def test_multi_asic_interface_desc(self): - return_code, result = self.get_result_and_return_code('intfutil -c description') + return_code, result = get_result_and_return_code('intfutil -c description') print("return_code: {}".format(return_code)) - print("result = {}".format(result)) + print("result = {}".format(result)) assert return_code == 0 assert result == intf_description def test_multi_asic_interface_desc_all(self): - return_code, result = self.get_result_and_return_code( 'intfutil -c description -d all') + return_code, result = get_result_and_return_code( 'intfutil -c description -d all') print("return_code: {}".format(return_code)) - print("result = {}".format(result)) + print("result = {}".format(result)) assert return_code == 0 assert result == intf_description_all def test_multi_asic_interface_asic0(self): - return_code, result = self.get_result_and_return_code( 'intfutil -c description -n asic0') + return_code, result = get_result_and_return_code( 'intfutil -c description -n asic0') print("return_code: {}".format(return_code)) - print("result = {}".format(result)) + print("result = {}".format(result)) assert return_code == 0 assert result == intf_description_asic0 def test_multi_asic_interface_desc_asic0_all(self): - return_code, result = self.get_result_and_return_code('intfutil -c description -n asic0 -d all') + return_code, result = get_result_and_return_code('intfutil -c description -n asic0 -d all') print("return_code: {}".format(return_code)) - print("result = {}".format(result)) + print("result = {}".format(result)) assert return_code == 0 assert result == intf_description_asic0_all - + def test_invalid_asic_name(self): - return_code, result = self.get_result_and_return_code('intfutil -c description -n asic99 -d all') + return_code, result = get_result_and_return_code('intfutil -c description -n asic99 -d all') print("return_code: {}".format(return_code)) - print("result = {}".format(result)) + print("result = {}".format(result)) assert return_code == 1 assert result == intf_invalid_asic_error def test_invalid_asic_name(self): - return_code, result = self.get_result_and_return_code('intfutil -c status -n asic99') + return_code, result = get_result_and_return_code('intfutil -c status -n asic99') print("return_code: {}".format(return_code)) - print("result = {}".format(result)) + print("result = {}".format(result)) assert return_code == 1 assert result == intf_invalid_asic_error diff --git a/tests/pfcstat_test.py b/tests/pfcstat_test.py new file mode 100644 index 000000000000..ee5abc724dad --- /dev/null +++ b/tests/pfcstat_test.py @@ -0,0 +1,197 @@ +import imp +import os +import shutil +import sys + +from click.testing import CliRunner + +import show.main as show + +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") + +show_pfc_counters_output = """\ + Port Rx PFC0 PFC1 PFC2 PFC3 PFC4 PFC5 PFC6 PFC7 +--------- ------ ------ ------ ------ ------ ------ ------ ------ +Ethernet0 0 0 0 0 0 0 0 0 +Ethernet4 400 401 402 403 404 405 406 407 +Ethernet8 800 801 802 803 804 805 806 807 + + Port Tx PFC0 PFC1 PFC2 PFC3 PFC4 PFC5 PFC6 PFC7 +--------- ------ ------ ------ ------ ------ ------ ------ ------ +Ethernet0 0 0 0 0 0 0 0 0 +Ethernet4 400 401 402 403 404 405 406 407 +Ethernet8 800 801 802 803 804 805 806 807 +""" + +show_pfc_counters_output_diff = """\ + Port Rx PFC0 PFC1 PFC2 PFC3 PFC4 PFC5 PFC6 PFC7 +--------- ------ ------ ------ ------ ------ ------ ------ ------ +Ethernet0 0 0 0 0 0 0 0 0 +Ethernet4 0 0 0 0 0 0 0 0 +Ethernet8 0 0 0 0 0 0 0 0 + + Port Tx PFC0 PFC1 PFC2 PFC3 PFC4 PFC5 PFC6 PFC7 +--------- ------ ------ ------ ------ ------ ------ ------ ------ +Ethernet0 0 0 0 0 0 0 0 0 +Ethernet4 0 0 0 0 0 0 0 0 +Ethernet8 0 0 0 0 0 0 0 0 +""" + +show_pfc_counters_all = """\ + Port Rx PFC0 PFC1 PFC2 PFC3 PFC4 PFC5 PFC6 PFC7 +------------ ------ ------ ------ ------ ------ ------ ------ ------ + Ethernet0 400 201 202 203 204 205 206 207 + Ethernet4 400 401 402 403 404 405 406 407 +Ethernet-BP0 600 601 602 603 604 605 606 607 +Ethernet-BP4 800 801 802 803 804 805 806 807 + + Port Tx PFC0 PFC1 PFC2 PFC3 PFC4 PFC5 PFC6 PFC7 +------------ ------ ------ ------ ------ ------ ------ ------ ------ + Ethernet0 400 201 202 203 204 205 206 207 + Ethernet4 400 401 402 403 404 405 406 407 +Ethernet-BP0 600 601 602 603 604 605 606 607 +Ethernet-BP4 800 801 802 803 804 805 806 807 +""" + +show_pfc_counters_asic0_frontend = """\ + Port Rx PFC0 PFC1 PFC2 PFC3 PFC4 PFC5 PFC6 PFC7 +--------- ------ ------ ------ ------ ------ ------ ------ ------ +Ethernet0 400 201 202 203 204 205 206 207 +Ethernet4 400 401 402 403 404 405 406 407 + + Port Tx PFC0 PFC1 PFC2 PFC3 PFC4 PFC5 PFC6 PFC7 +--------- ------ ------ ------ ------ ------ ------ ------ ------ +Ethernet0 400 201 202 203 204 205 206 207 +Ethernet4 400 401 402 403 404 405 406 407 +""" + +show_pfc_counters_msaic_output_diff = """\ + Port Rx PFC0 PFC1 PFC2 PFC3 PFC4 PFC5 PFC6 PFC7 +------------ ------ ------ ------ ------ ------ ------ ------ ------ + Ethernet0 0 0 0 0 0 0 0 0 + Ethernet4 0 0 0 0 0 0 0 0 +Ethernet-BP0 0 0 0 0 0 0 0 0 +Ethernet-BP4 0 0 0 0 0 0 0 0 + + Port Tx PFC0 PFC1 PFC2 PFC3 PFC4 PFC5 PFC6 PFC7 +------------ ------ ------ ------ ------ ------ ------ ------ ------ + Ethernet0 0 0 0 0 0 0 0 0 + Ethernet4 0 0 0 0 0 0 0 0 +Ethernet-BP0 0 0 0 0 0 0 0 0 +Ethernet-BP4 0 0 0 0 0 0 0 0 +""" + + +def pfc_clear(expected_output): + counters_file_list = ['0tx', '0rx'] + uid = str(os.getuid()) + cnstat_dir = os.path.join(os.sep, "tmp", "pfcstat-{}".format(uid)) + shutil.rmtree(cnstat_dir, ignore_errors=True, onerror=None) + + return_code, result = get_result_and_return_code( + 'pfcstat -c' + ) + + # verify that files are created with stats + cnstat_fqn_file_rx = "{}rx".format(uid) + cnstat_fqn_file_tx = "{}tx".format(uid) + file_list = [cnstat_fqn_file_tx, cnstat_fqn_file_rx] + file_list.sort() + files = os.listdir(cnstat_dir) + files.sort() + assert files == file_list + + return_code, result = get_result_and_return_code( + 'pfcstat -s all' + ) + result_stat = [s for s in result.split("\n") if "Last cached" not in s] + expected = expected_output.split("\n") + # this will also verify the saved counters are correct since the + # expected counters are all '0s' + assert result_stat == expected + shutil.rmtree(cnstat_dir, ignore_errors=True, onerror=None) + + +class TestPfcstat(object): + @classmethod + def setup_class(cls): + print("SETUP") + os.environ["PATH"] += os.pathsep + scripts_path + os.environ["UTILITIES_UNIT_TESTING"] = "2" + + def test_pfc_counters(self): + runner = CliRunner() + result = runner.invoke( + show.cli.commands["pfc"].commands["counters"], + [] + ) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_pfc_counters_output + + def test_pfc_clear(self): + pfc_clear(show_pfc_counters_output_diff) + + @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" + + +class TestMultiAsicPfcstat(object): + @classmethod + def setup_class(cls): + print("SETUP") + os.environ["PATH"] += os.pathsep + scripts_path + os.environ["UTILITIES_UNIT_TESTING"] = "2" + os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = "multi_asic" + imp.reload(show) + + def test_pfc_counters_all(self): + runner = CliRunner() + result = runner.invoke( + show.cli.commands["pfc"].commands["counters"], + ) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_pfc_counters_all + + def test_pfc_counters_frontend(self): + return_code, result = get_result_and_return_code( + 'pfcstat -s frontend' + ) + assert return_code == 0 + assert result == show_pfc_counters_asic0_frontend + + def test_pfc_counters_asic(self): + return_code, result = get_result_and_return_code( + 'pfcstat -n asic0' + ) + assert return_code == 0 + assert result == show_pfc_counters_asic0_frontend + + def test_pfc_counters_asic_all(self): + return_code, result = get_result_and_return_code( + 'pfcstat -n asic0 -s all' + ) + assert return_code == 0 + assert result == show_pfc_counters_all + + def test_pfc_clear(self): + pfc_clear(show_pfc_counters_msaic_output_diff) + + @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" + os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = "" diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 000000000000..92ee422e9c95 --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,18 @@ + +""" Common test utilities """ + +import subprocess + + +def get_result_and_return_code(cmd): + return_code = 0 + try: + output = subprocess.check_output( + cmd, stderr=subprocess.STDOUT, shell=True) + except subprocess.CalledProcessError as e: + return_code = e.returncode + # store only the error, no need for the traceback + output = e.output.strip().split("\n")[-1] + + print(output) + return(return_code, output)