From cda43d63a75421945822d808fbecf96582fc536d Mon Sep 17 00:00:00 2001 From: HP Date: Thu, 5 Sep 2024 13:56:01 -0700 Subject: [PATCH] Fix multi-asic support to PFC config/show (#3521) * Add multi-asic support to pfc - Add namespace arg for show and config cmds for pfc - Replace test DB with JSON to support verification of multiple namespaces in unit test - Add unit tests for multi-asic behaviour - Added a test vector file for better test organization * Fix linter errors --- config/main.py | 18 +- pfc/main.py | 132 +++++++------ show/main.py | 10 +- tests/multi_asic_pfc_test.py | 133 +++++++++++++ tests/pfc_input/pfc_test_vectors.py | 286 ++++++++++++++++++++++++++++ tests/pfc_test.py | 48 +++-- 6 files changed, 554 insertions(+), 73 deletions(-) create mode 100644 tests/multi_asic_pfc_test.py create mode 100644 tests/pfc_input/pfc_test_vectors.py diff --git a/config/main.py b/config/main.py index 3370cf3f11..90f42a077b 100644 --- a/config/main.py +++ b/config/main.py @@ -6446,8 +6446,9 @@ def pfc(ctx): @pfc.command() @click.argument('interface_name', metavar='', required=True) @click.argument('status', type=click.Choice(['on', 'off'])) +@multi_asic_util.multi_asic_click_option_namespace @click.pass_context -def asymmetric(ctx, interface_name, status): +def asymmetric(ctx, interface_name, status, namespace): """Set asymmetric PFC configuration.""" # Get the config_db connector config_db = ctx.obj['config_db'] @@ -6457,7 +6458,11 @@ def asymmetric(ctx, interface_name, status): if interface_name is None: ctx.fail("'interface_name' is None!") - clicommon.run_command(['pfc', 'config', 'asymmetric', str(status), str(interface_name)]) + cmd = ['pfc', 'config', 'asymmetric', str(status), str(interface_name)] + if namespace is not None: + cmd += ['-n', str(namespace)] + + clicommon.run_command(cmd) # # 'pfc priority' command ('config interface pfc priority ...') @@ -6467,8 +6472,9 @@ def asymmetric(ctx, interface_name, status): @click.argument('interface_name', metavar='', required=True) @click.argument('priority', type=click.Choice([str(x) for x in range(8)])) @click.argument('status', type=click.Choice(['on', 'off'])) +@multi_asic_util.multi_asic_click_option_namespace @click.pass_context -def priority(ctx, interface_name, priority, status): +def priority(ctx, interface_name, priority, status, namespace): """Set PFC priority configuration.""" # Get the config_db connector config_db = ctx.obj['config_db'] @@ -6478,7 +6484,11 @@ def priority(ctx, interface_name, priority, status): if interface_name is None: ctx.fail("'interface_name' is None!") - clicommon.run_command(['pfc', 'config', 'priority', str(status), str(interface_name), str(priority)]) + cmd = ['pfc', 'config', 'priority', str(status), str(interface_name), str(priority)] + if namespace is not None: + cmd += ['-n', str(namespace)] + + clicommon.run_command(cmd) # # 'buffer' group ('config buffer ...') diff --git a/pfc/main.py b/pfc/main.py index f0b376e242..071b4a304e 100644 --- a/pfc/main.py +++ b/pfc/main.py @@ -1,39 +1,63 @@ #!/usr/bin/env python3 +import os import click -from swsscommon.swsscommon import ConfigDBConnector +import json +from sonic_py_common import multi_asic from tabulate import tabulate from natsort import natsorted +from utilities_common import multi_asic as multi_asic_util +# Constants ALL_PRIORITIES = [str(x) for x in range(8)] PRIORITY_STATUS = ['on', 'off'] +PORT_TABLE_NAME = "PORT" +PORT_QOS_MAP_TABLE_NAME = "PORT_QOS_MAP" class Pfc(object): - def __init__(self, cfgdb=None): - self.cfgdb = cfgdb + def __init__(self, namespace=None): + self.multi_asic = multi_asic_util.MultiAsic(namespace_option=namespace) + self.config_db = None + # For unit testing + self.updated_port_tables = {} + self.test_filename = '/tmp/pfc_testdata.json' + + def dump_config_to_json(self, table_name, namespace): + """ + This function dumps the current config in a JSON file for unit testing. + """ + # Only dump files in unit testing mode + if os.environ["UTILITIES_UNIT_TESTING"] != "2": + return + + if namespace not in self.updated_port_tables.keys(): + self.updated_port_tables[namespace] = {} + + self.updated_port_tables[namespace][table_name] = self.config_db.get_table(table_name) + with open(self.test_filename, "w") as fd: + json.dump(self.updated_port_tables, fd) + + @multi_asic_util.run_on_multi_asic def configPfcAsym(self, interface, pfc_asym): """ PFC handler to configure asymmetric PFC. """ - configdb = ConfigDBConnector() if self.cfgdb is None else self.cfgdb - configdb.connect() - - configdb.mod_entry("PORT", interface, {'pfc_asym': pfc_asym}) + self.config_db.mod_entry(PORT_TABLE_NAME, interface, {'pfc_asym': pfc_asym}) + self.dump_config_to_json(PORT_TABLE_NAME, self.multi_asic.current_namespace) + @multi_asic_util.run_on_multi_asic def showPfcAsym(self, interface): """ PFC handler to display asymmetric PFC information. """ + namespace_str = f"Namespace {self.multi_asic.current_namespace}" if multi_asic.is_multi_asic() else '' header = ('Interface', 'Asymmetric') - configdb = ConfigDBConnector() if self.cfgdb is None else self.cfgdb - configdb.connect() - if interface: - db_keys = configdb.keys(configdb.CONFIG_DB, 'PORT|{0}'.format(interface)) + db_keys = self.config_db.keys(self.config_db.CONFIG_DB, 'PORT|{0}'.format(interface)) else: - db_keys = configdb.keys(configdb.CONFIG_DB, 'PORT|*') + db_keys = self.config_db.keys(self.config_db.CONFIG_DB, 'PORT|*') table = [] @@ -43,36 +67,35 @@ def showPfcAsym(self, interface): key = i.split('|')[-1] if key and key.startswith('Ethernet'): - entry = configdb.get_entry('PORT', key) + entry = self.config_db.get_entry(PORT_TABLE_NAME, key) table.append([key, entry.get('pfc_asym', 'N/A')]) sorted_table = natsorted(table) - click.echo() + click.echo(namespace_str) click.echo(tabulate(sorted_table, headers=header, tablefmt="simple", missingval="")) click.echo() + @multi_asic_util.run_on_multi_asic def configPfcPrio(self, status, interface, priority): - configdb = ConfigDBConnector() if self.cfgdb is None else self.cfgdb - configdb.connect() - - if interface not in configdb.get_keys('PORT_QOS_MAP'): + if interface not in self.config_db.get_keys(PORT_QOS_MAP_TABLE_NAME): click.echo('Cannot find interface {0}'.format(interface)) return """Current lossless priorities on the interface""" - entry = configdb.get_entry('PORT_QOS_MAP', interface) + entry = self.config_db.get_entry('PORT_QOS_MAP', interface) enable_prio = entry.get('pfc_enable').split(',') """Avoid '' in enable_prio""" enable_prio = [x.strip() for x in enable_prio if x.strip()] + namespace_str = f" for namespace {self.multi_asic.current_namespace}" if multi_asic.is_multi_asic() else '' if status == 'on' and priority in enable_prio: - click.echo('Priority {0} has already been enabled on {1}'.format(priority, interface)) + click.echo('Priority {0} has already been enabled on {1}{2}'.format(priority, interface, namespace_str)) return if status == 'off' and priority not in enable_prio: - click.echo('Priority {0} is not enabled on {1}'.format(priority, interface)) + click.echo('Priority {0} is not enabled on {1}{2}'.format(priority, interface, namespace_str)) return if status == 'on': @@ -82,11 +105,10 @@ def configPfcPrio(self, status, interface, priority): enable_prio.remove(priority) enable_prio.sort() - configdb.mod_entry("PORT_QOS_MAP", interface, {'pfc_enable': ','.join(enable_prio)}) + self.config_db.mod_entry(PORT_QOS_MAP_TABLE_NAME, interface, {'pfc_enable': ','.join(enable_prio)}) + self.dump_config_to_json(PORT_QOS_MAP_TABLE_NAME, self.multi_asic.current_namespace) - """Show the latest PFC configuration""" - self.showPfcPrio(interface) - + @multi_asic_util.run_on_multi_asic def showPfcPrio(self, interface): """ PFC handler to display PFC enabled priority information. @@ -94,80 +116,82 @@ def showPfcPrio(self, interface): header = ('Interface', 'Lossless priorities') table = [] - configdb = ConfigDBConnector() if self.cfgdb is None else self.cfgdb - configdb.connect() - """Get all the interfaces with QoS map information""" - intfs = configdb.get_keys('PORT_QOS_MAP') + intfs = self.config_db.get_keys('PORT_QOS_MAP') """The user specifies an interface but we cannot find it""" + namespace_str = f"Namespace {self.multi_asic.current_namespace}" if multi_asic.is_multi_asic() else '' if interface and interface not in intfs: - click.echo('Cannot find interface {0}'.format(interface)) + if multi_asic.is_multi_asic(): + click.echo('Cannot find interface {0} for {1}'.format(interface, namespace_str)) + else: + click.echo('Cannot find interface {0}'.format(interface)) return if interface: intfs = [interface] for intf in intfs: - entry = configdb.get_entry('PORT_QOS_MAP', intf) + entry = self.config_db.get_entry('PORT_QOS_MAP', intf) table.append([intf, entry.get('pfc_enable', 'N/A')]) sorted_table = natsorted(table) - click.echo() + click.echo(namespace_str) click.echo(tabulate(sorted_table, headers=header, tablefmt="simple", missingval="")) click.echo() - + + @click.group() -@click.pass_context -def cli(ctx): +def cli(): """PFC Command Line""" - # Use the cfgdb object if given as input. - cfgdb = None if ctx.obj is None else ctx.obj.cfgdb - ctx.obj = {'pfc': Pfc(cfgdb)} @cli.group() -@click.pass_context -def config(ctx): +def config(): """Config PFC""" pass + @cli.group() -@click.pass_context -def show(ctx): +def show(): """Show PFC information""" pass + @click.command() @click.argument('status', type=click.Choice(PRIORITY_STATUS)) @click.argument('interface', type=click.STRING) -@click.pass_context -def configAsym(ctx, status, interface): +@multi_asic_util.multi_asic_click_option_namespace +def configAsym(status, interface, namespace): """Configure asymmetric PFC on a given port.""" - ctx.obj['pfc'].configPfcAsym(interface, status) + Pfc(namespace).configPfcAsym(interface, status) + @click.command() @click.argument('status', type=click.Choice(PRIORITY_STATUS)) @click.argument('interface', type=click.STRING) @click.argument('priority', type=click.Choice(ALL_PRIORITIES)) -@click.pass_context -def configPrio(ctx, status, interface, priority): +@multi_asic_util.multi_asic_click_option_namespace +def configPrio(status, interface, priority, namespace): """Configure PFC on a given priority.""" - ctx.obj['pfc'].configPfcPrio(status, interface, priority) + Pfc(namespace).configPfcPrio(status, interface, priority) + @click.command() @click.argument('interface', type=click.STRING, required=False) -@click.pass_context -def showAsym(ctx, interface): +@multi_asic_util.multi_asic_click_option_namespace +def showAsym(interface, namespace): """Show asymmetric PFC information""" - ctx.obj['pfc'].showPfcAsym(interface) + Pfc(namespace).showPfcAsym(interface) + @click.command() @click.argument('interface', type=click.STRING, required=False) -@click.pass_context -def showPrio(ctx, interface): +@multi_asic_util.multi_asic_click_option_namespace +def showPrio(interface, namespace): """Show PFC priority information""" - ctx.obj['pfc'].showPfcPrio(interface) + Pfc(namespace).showPfcPrio(interface) + config.add_command(configAsym, "asymmetric") config.add_command(configPrio, "priority") diff --git a/show/main.py b/show/main.py index 6e5fd8ab59..655a775502 100755 --- a/show/main.py +++ b/show/main.py @@ -645,7 +645,8 @@ def counters(namespace, display, verbose): @pfc.command() @click.argument('interface', type=click.STRING, required=False) -def priority(interface): +@multi_asic_util.multi_asic_click_option_namespace +def priority(interface, namespace): """Show pfc priority""" cmd = ['pfc', 'show', 'priority'] if interface is not None and clicommon.get_interface_naming_mode() == "alias": @@ -653,12 +654,15 @@ def priority(interface): if interface is not None: cmd += [str(interface)] + if namespace is not None: + cmd += ['-n', str(namespace)] run_command(cmd) @pfc.command() @click.argument('interface', type=click.STRING, required=False) -def asymmetric(interface): +@multi_asic_util.multi_asic_click_option_namespace +def asymmetric(interface, namespace): """Show asymmetric pfc""" cmd = ['pfc', 'show', 'asymmetric'] if interface is not None and clicommon.get_interface_naming_mode() == "alias": @@ -666,6 +670,8 @@ def asymmetric(interface): if interface is not None: cmd += [str(interface)] + if namespace is not None: + cmd += ['-n', str(namespace)] run_command(cmd) diff --git a/tests/multi_asic_pfc_test.py b/tests/multi_asic_pfc_test.py new file mode 100644 index 0000000000..52bfcf4982 --- /dev/null +++ b/tests/multi_asic_pfc_test.py @@ -0,0 +1,133 @@ +import os +import sys +import json +import importlib +import pfc.main as pfc +from .pfc_test import TestPfcBase +from click.testing import CliRunner +from .pfc_input.pfc_test_vectors import testData + +test_path = os.path.dirname(os.path.abspath(__file__)) +modules_path = os.path.dirname(test_path) +scripts_path = os.path.join(modules_path, "pfc") +sys.path.insert(0, test_path) +sys.path.insert(0, modules_path) + + +class TestPfcMultiAsic(TestPfcBase): + @classmethod + def setup_class(cls): + super().setup_class() + os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = "multi_asic" + + # Multi-asic utils rely on the database that is loaded + # We reload the multi_asic database and update the multi-asic utils + # Pfc uses click cmds that use multi_asic utils, hence we reload pfc too + + import mock_tables.mock_multi_asic + importlib.reload(mock_tables.mock_multi_asic) + mock_tables.dbconnector.load_namespace_config() + + import utilities_common + importlib.reload(utilities_common.multi_asic) + importlib.reload(pfc) + + def executor(self, input): + runner = CliRunner() + result = runner.invoke(pfc.cli, input['cmd']) + exit_code = result.exit_code + output = result.output + + print(exit_code) + print(output) + + assert exit_code == input['rc'] + + # For config commands we dump modified value in a tmp JSON file for testing + if 'cmp_args' in input: + fd = open('/tmp/pfc_testdata.json', 'r') + cmp_data = json.load(fd) + + # Verify assignments + for args in input['cmp_args']: + namespace, table, key, field, expected_val = args + assert(cmp_data[namespace][table][key][field] == expected_val) + fd.close() + + if 'rc_msg' in input: + assert input['rc_msg'] in output + + if 'rc_output' in input: + assert output == input['rc_output'] + + def test_pfc_show_asymmetric_all_asic0_masic(self): + self.executor(testData['pfc_show_asymmetric_all_asic0_masic']) + + def test_pfc_show_asymmetric_all_asic1_masic(self): + self.executor(testData['pfc_show_asymmetric_all_asic1_masic']) + + def test_pfc_show_asymmetric_all_masic(self): + self.executor(testData['pfc_show_asymmetric_all_masic']) + + def test_pfc_show_asymmetric_intf_one_masic(self): + self.executor(testData['pfc_show_asymmetric_intf_one_masic']) + + def test_pfc_show_asymmetric_intf_all_masic(self): + self.executor(testData['pfc_show_asymmetric_intf_all_masic']) + + def test_pfc_show_asymmetric_intf_fake_one_masic(self): + self.executor(testData['pfc_show_asymmetric_intf_fake_one_masic']) + + def test_pfc_show_priority_all_asic0_masic(self): + self.executor(testData['pfc_show_priority_all_asic0_masic']) + + def test_pfc_show_priority_all_asic1_masic(self): + self.executor(testData['pfc_show_priority_all_asic1_masic']) + + def test_pfc_show_priority_all_masic(self): + self.executor(testData['pfc_show_priority_all_masic']) + + def test_pfc_show_priority_intf_one_masic(self): + self.executor(testData['pfc_show_priority_intf_one_masic']) + + def test_pfc_show_priority_intf_all_masic(self): + self.executor(testData['pfc_show_priority_intf_all_masic']) + + def test_pfc_show_priority_intf_fake_one_masic(self): + self.executor(testData['pfc_show_priority_intf_fake_one_masic']) + + def test_pfc_show_priority_intf_fake_all_masic(self): + self.executor(testData['pfc_show_priority_intf_fake_all_masic']) + + def test_pfc_config_asymmetric_one_masic(self): + self.executor(testData['pfc_config_asymmetric_one_masic']) + + def test_pfc_config_asymmetric_invalid_one_masic(self): + self.executor(testData['pfc_config_asymmetric_invalid_one_masic']) + + def test_pfc_config_asymmetric_all_masic(self): + self.executor(testData['pfc_config_asymmetric_all_masic']) + + def test_pfc_config_asymmetric_invalid_all_masic(self): + self.executor(testData['pfc_config_asymmetric_invalid_all_masic']) + + def test_pfc_config_priority_one_masic(self): + self.executor(testData['pfc_config_priority_one_masic']) + + def test_pfc_config_priority_invalid_one_masic(self): + self.executor(testData['pfc_config_priority_invalid_one_masic']) + + def test_pfc_config_priority_all_masic(self): + self.executor(testData['pfc_config_priority_all_masic']) + + def test_pfc_config_priority_invalid_all_masic(self): + self.executor(testData['pfc_config_priority_invalid_all_masic']) + + @classmethod + def teardown_class(cls): + # Reset the database to mock single-asic state + import mock_tables.mock_single_asic + mock_tables.dbconnector.load_database_config() + + super().teardown_class() + os.environ.pop("UTILITIES_UNIT_TESTING_TOPOLOGY") diff --git a/tests/pfc_input/pfc_test_vectors.py b/tests/pfc_input/pfc_test_vectors.py new file mode 100644 index 0000000000..20d6b59af3 --- /dev/null +++ b/tests/pfc_input/pfc_test_vectors.py @@ -0,0 +1,286 @@ +# Golden outputs +show_asym_all_asic0_masic = """\ +Namespace asic0 +Interface Asymmetric +------------ ------------ +Ethernet0 off +Ethernet4 off +Ethernet16 off +Ethernet-BP0 off +Ethernet-BP4 off + +""" + +show_asym_all_asic1_masic = """\ +Namespace asic1 +Interface Asymmetric +-------------- ------------ +Ethernet64 off +Ethernet-BP256 off +Ethernet-BP260 off + +""" + +show_asym_all_masic = """\ +Namespace asic0 +Interface Asymmetric +------------ ------------ +Ethernet0 off +Ethernet4 off +Ethernet16 off +Ethernet-BP0 off +Ethernet-BP4 off + +Namespace asic1 +Interface Asymmetric +-------------- ------------ +Ethernet64 off +Ethernet-BP256 off +Ethernet-BP260 off + +""" + +show_asym_intf_one_masic = """\ +Namespace asic0 +Interface Asymmetric +----------- ------------ +Ethernet0 off + +""" + +show_asym_intf_all_masic = """\ +Namespace asic0 +Interface Asymmetric +----------- ------------ +Ethernet0 off + +Namespace asic1 +Interface Asymmetric +----------- ------------ + +""" + +show_asym_intf_fake_one_masic = """\ +Namespace asic0 +Interface Asymmetric +----------- ------------ + +""" + +show_prio_all_asic0_masic = """\ +Namespace asic0 +Interface Lossless priorities +-------------- --------------------- +Ethernet0 3,4 +Ethernet4 3,4 +Ethernet8 3,4 +Ethernet-BP0 3,4 +Ethernet-BP4 3,4 +Ethernet-BP256 3,4 +Ethernet-BP260 3,4 + +""" + +show_prio_all_asic1_masic = """\ +Namespace asic1 +Interface Lossless priorities +-------------- --------------------- +Ethernet0 3,4 +Ethernet4 3,4 +Ethernet8 3,4 +Ethernet-BP0 3,4 +Ethernet-BP4 3,4 +Ethernet-BP256 3,4 + +""" + +show_prio_all_masic = """\ +Namespace asic0 +Interface Lossless priorities +-------------- --------------------- +Ethernet0 3,4 +Ethernet4 3,4 +Ethernet8 3,4 +Ethernet-BP0 3,4 +Ethernet-BP4 3,4 +Ethernet-BP256 3,4 +Ethernet-BP260 3,4 + +Namespace asic1 +Interface Lossless priorities +-------------- --------------------- +Ethernet0 3,4 +Ethernet4 3,4 +Ethernet8 3,4 +Ethernet-BP0 3,4 +Ethernet-BP4 3,4 +Ethernet-BP256 3,4 + +""" + +show_prio_intf_one_masic = """\ +Namespace asic0 +Interface Lossless priorities +----------- --------------------- +Ethernet0 3,4 + +""" + +show_prio_intf_all_masic = """\ +Namespace asic0 +Interface Lossless priorities +----------- --------------------- +Ethernet0 3,4 + +Namespace asic1 +Interface Lossless priorities +----------- --------------------- +Ethernet0 3,4 + +""" + +show_prio_intf_fake_one_masic = """\ +Cannot find interface Ethernet1234 for Namespace asic0 +""" + +show_prio_intf_fake_all_masic = """\ +Cannot find interface Ethernet1234 for Namespace asic0 +Cannot find interface Ethernet1234 for Namespace asic1 +""" + +testData = { + 'pfc_show_asymmetric_all_asic0_masic': {'cmd': ['show', 'asymmetric', + '--namespace', 'asic0'], + 'rc': 0, + 'rc_output': show_asym_all_asic0_masic + }, + 'pfc_show_asymmetric_all_asic1_masic': {'cmd': ['show', 'asymmetric', + '--namespace', 'asic1'], + 'rc': 0, + 'rc_output': show_asym_all_asic1_masic + }, + 'pfc_show_asymmetric_all_masic': {'cmd': ['show', 'asymmetric'], + 'rc': 0, + 'rc_output': show_asym_all_masic + }, + 'pfc_show_asymmetric_intf_one_masic': {'cmd': ['show', 'asymmetric', + 'Ethernet0', '--namespace', + 'asic0'], + 'rc': 0, + 'rc_output': show_asym_intf_one_masic + }, + 'pfc_show_asymmetric_intf_all_masic': {'cmd': ['show', 'asymmetric', + 'Ethernet0'], + 'rc': 0, + 'rc_output': show_asym_intf_all_masic + }, + 'pfc_show_asymmetric_intf_fake_one_masic': {'cmd': ['show', 'asymmetric', + 'Ethernet1234', '--namespace', + 'asic0'], + 'rc': 0, + 'rc_output': show_asym_intf_fake_one_masic + }, + 'pfc_show_priority_all_asic0_masic': {'cmd': ['show', 'priority', + '--namespace', 'asic0'], + 'rc': 0, + 'rc_output': show_prio_all_asic0_masic + }, + 'pfc_show_priority_all_asic1_masic': {'cmd': ['show', 'priority', + '--namespace', 'asic1'], + 'rc': 0, + 'rc_output': show_prio_all_asic1_masic + }, + 'pfc_show_priority_all_masic': {'cmd': ['show', 'priority'], + 'rc': 0, + 'rc_output': show_prio_all_masic + }, + 'pfc_show_priority_intf_one_masic': {'cmd': ['show', 'priority', + 'Ethernet0', '--namespace', + 'asic0'], + 'rc': 0, + 'rc_output': show_prio_intf_one_masic + }, + 'pfc_show_priority_intf_all_masic': {'cmd': ['show', 'priority', + 'Ethernet0'], + 'rc': 0, + 'rc_output': show_prio_intf_all_masic + }, + 'pfc_show_priority_intf_fake_one_masic': {'cmd': ['show', 'priority', + 'Ethernet1234', '--namespace', + 'asic0'], + 'rc': 0, + 'rc_output': show_prio_intf_fake_one_masic + }, + 'pfc_show_priority_intf_fake_all_masic': {'cmd': ['show', 'priority', + 'Ethernet1234'], + 'rc': 0, + 'rc_output': show_prio_intf_fake_all_masic + }, + 'pfc_config_asymmetric_one_masic': {'cmd': ['config', 'asymmetric', + 'on', 'Ethernet0', '--namespace', + 'asic0'], + 'rc': 0, + 'cmp_args': [['asic0', 'PORT', 'Ethernet0', 'pfc_asym', 'on']] + }, + 'pfc_config_asymmetric_invalid_one_masic': {'cmd': ['config', 'asymmetric', + 'onn', 'Ethernet0', '--namespace', + 'asic0'], + 'rc': 2, + 'rc_msg': ('Usage: cli config asymmetric [OPTIONS] ' + '[on|off] INTERFACE\nTry "cli config ' + 'asymmetric --help" for help.\n\n' + 'Error: Invalid value for "[on|off]": ' + 'invalid choice: onn. (choose from on, off)') + }, + 'pfc_config_asymmetric_all_masic': {'cmd': ['config', 'asymmetric', + 'on', 'Ethernet0'], + 'rc': 0, + 'cmp_args': [['asic0', 'PORT', 'Ethernet0', 'pfc_asym', 'on'], + ['asic1', 'PORT', 'Ethernet0', 'pfc_asym', 'on']] + }, + 'pfc_config_asymmetric_invalid_all_masic': {'cmd': ['config', 'asymmetric', + 'onn', 'Ethernet0'], + 'rc': 2, + 'rc_msg': ('Usage: cli config asymmetric [OPTIONS] ' + '[on|off] INTERFACE\nTry "cli config ' + 'asymmetric --help" for help.\n\n' + 'Error: Invalid value for "[on|off]": ' + 'invalid choice: onn. (choose from on, off)') + }, + 'pfc_config_priority_one_masic': {'cmd': ['config', 'priority', + 'on', 'Ethernet0', '5', + '--namespace', 'asic0'], + 'rc': 0, + 'cmp_args': [['asic0', 'PORT_QOS_MAP', 'Ethernet0', + 'pfc_enable', '3,4,5']] + }, + 'pfc_config_priority_invalid_one_masic': {'cmd': ['config', 'priority', + 'onn', 'Ethernet0', '5', + '--namespace', 'asic0'], + 'rc': 2, + 'rc_msg': ('Usage: cli config priority [OPTIONS] ' + '[on|off] INTERFACE [0|1|2|3|4|5|6|7]\n' + 'Try "cli config priority --help" for ' + 'help.\n\nError: Invalid value for ' + '"[on|off]": invalid choice: onn. ' + '(choose from on, off)') + }, + 'pfc_config_priority_all_masic': {'cmd': ['config', 'priority', + 'on', 'Ethernet0', '5'], + 'rc': 0, + 'cmp_args': [['asic0', 'PORT_QOS_MAP', 'Ethernet0', + 'pfc_enable', '3,4,5'], + ['asic1', 'PORT_QOS_MAP', 'Ethernet0', + 'pfc_enable', '3,4,5']] + }, + 'pfc_config_priority_invalid_all_masic': {'cmd': ['config', 'priority', + 'onn', 'Ethernet0', '5'], + 'rc': 2, + 'rc_msg': ('Usage: cli config priority [OPTIONS] ' + '[on|off] INTERFACE [0|1|2|3|4|5|6|7]\n' + 'Try "cli config priority --help" for ' + 'help.\n\nError: Invalid value for ' + '"[on|off]": invalid choice: onn. ' + '(choose from on, off)') + }, +} diff --git a/tests/pfc_test.py b/tests/pfc_test.py index 101aa476cc..136dab2623 100644 --- a/tests/pfc_test.py +++ b/tests/pfc_test.py @@ -1,10 +1,10 @@ import os import sys +import json import pfc.main as pfc from .pfc_input.assert_show_output import pfc_cannot_find_intf, pfc_show_asymmetric_all, \ pfc_show_asymmetric_intf, pfc_show_priority_all, pfc_show_priority_intf, \ pfc_config_priority_on, pfc_asym_cannot_find_intf -from utilities_common.db import Db from click.testing import CliRunner from importlib import reload @@ -17,11 +17,15 @@ class TestPfcBase(object): + @classmethod + def setup_class(cls): + print("SETUP") + os.environ["PATH"] += os.pathsep + scripts_path + os.environ["UTILITIES_UNIT_TESTING"] = "2" - def executor(self, cliobj, command, expected_rc=0, expected_output=None, expected_cfgdb_entry=None, + def executor(self, cliobj, command, expected_rc=0, expected_output=None, expected_cfgdb_entries=None, runner=CliRunner()): - db = Db() - result = runner.invoke(cliobj, command, obj=db) + result = runner.invoke(cliobj, command) print(result.exit_code) print(result.output) @@ -32,21 +36,37 @@ def executor(self, cliobj, command, expected_rc=0, expected_output=None, expecte if expected_output: assert result.output == expected_output - if expected_cfgdb_entry: - (table, key, field, expected_val) = expected_cfgdb_entry - configdb = db.cfgdb - entry = configdb.get_entry(table, key) - assert entry.get(field) == expected_val + if expected_cfgdb_entries: + fd = open('/tmp/pfc_testdata.json', 'r') + cmp_data = json.load(fd) + for expected_cfgdb_entry in expected_cfgdb_entries: + (namespace, table, key, field, expected_val) = expected_cfgdb_entry + entry = cmp_data[namespace][table][key][field] + assert entry == expected_val + @classmethod + def teardown_class(cls): + print("TEARDOWN") + os.environ["PATH"] = os.pathsep.join( + os.environ["PATH"].split(os.pathsep)[:-1] + ) + os.environ.pop("UTILITIES_UNIT_TESTING") + if os.path.isfile('/tmp/pfc_testdata.json'): + os.remove('/tmp/pfc_testdata.json') class TestPfc(TestPfcBase): - @classmethod def setup_class(cls): + super().setup_class() + from mock_tables import dbconnector from mock_tables import mock_single_asic reload(mock_single_asic) - dbconnector.load_namespace_config() + dbconnector.load_database_config() + + import utilities_common + reload(utilities_common.multi_asic) + reload(pfc) def test_pfc_show_asymmetric_all(self): self.executor(pfc.cli, ['show', 'asymmetric'], @@ -74,8 +94,10 @@ def test_pfc_show_priority_intf_fake(self): def test_pfc_config_asymmetric(self): self.executor(pfc.cli, ['config', 'asymmetric', 'on', 'Ethernet0'], - expected_cfgdb_entry=('PORT', 'Ethernet0', 'pfc_asym', 'on')) + # namespace, table, key, field, expected_val + expected_cfgdb_entries=[('', 'PORT', 'Ethernet0', 'pfc_asym', 'on')]) def test_pfc_config_priority(self): self.executor(pfc.cli, ['config', 'priority', 'on', 'Ethernet0', '5'], - expected_output=pfc_config_priority_on) + # namespace, table, key, field, expected_val + expected_cfgdb_entries=[('', 'PORT_QOS_MAP', 'Ethernet0', 'pfc_enable', '3,4,5')])