diff --git a/config/main.py b/config/main.py index 4a46efda5e..7509628a67 100644 --- a/config/main.py +++ b/config/main.py @@ -6399,7 +6399,8 @@ def remove_reasons(counter_name, reasons, verbose): @click.option('-ydrop', metavar='', type=click.IntRange(0, 100), help="Set yellow drop probability") @click.option('-gdrop', metavar='', type=click.IntRange(0, 100), help="Set green drop probability") @click.option('-v', '--verbose', is_flag=True, help="Enable verbose output") -def ecn(profile, rmax, rmin, ymax, ymin, gmax, gmin, rdrop, ydrop, gdrop, verbose): +@multi_asic_util.multi_asic_click_option_namespace +def ecn(profile, rmax, rmin, ymax, ymin, gmax, gmin, rdrop, ydrop, gdrop, verbose, namespace): """ECN-related configuration tasks""" log.log_info("'ecn -profile {}' executing...".format(profile)) command = ['ecnconfig', '-p', str(profile)] @@ -6413,6 +6414,8 @@ def ecn(profile, rmax, rmin, ymax, ymin, gmax, gmin, rdrop, ydrop, gdrop, verbos if ydrop is not None: command += ['-ydrop', str(ydrop)] if gdrop is not None: command += ['-gdrop', str(gdrop)] if verbose: command += ["-vv"] + if namespace is not None: + command += ['-n', str(namespace)] clicommon.run_command(command, display_cmd=verbose) diff --git a/scripts/ecnconfig b/scripts/ecnconfig index e3b08d2bd3..9b2deab4dc 100755 --- a/scripts/ecnconfig +++ b/scripts/ecnconfig @@ -5,7 +5,7 @@ ecnconfig is the utility to 1) show and change ECN configuration -usage: ecnconfig [-h] [-v] [-l] [-p PROFILE] [-gmin GREEN_MIN] +usage: ecnconfig [-h] [-v] [-l] [-p PROFILE] [-gmin GREEN_MIN] [-n NAMESPACE] [-gmax GREEN_MAX] [-ymin YELLOW_MIN] [-ymax YELLOW_MAX] [-rmin RED_MIN] [-rmax RED_MAX] [-gdrop GREEN_DROP_PROB] [-ydrop YELLOW_DROP_PROB] [-rdrop RED_DROP_PROB] [-vv] @@ -16,6 +16,7 @@ optional arguments: -vv --verbose verbose output -l --list show ECN WRED configuration -p --profile specify WRED profile name + -n --namespace show ECN configuration for specified namespace -gmin --green-min set min threshold for packets marked green -gmax --green-max set max threshold for packets marked green -ymin --yellow-min set min threshold for packets marked yellow @@ -47,7 +48,7 @@ $ecnconfig -q 3 ECN status: queue 3: on """ -import argparse +import click import json import os import sys @@ -62,12 +63,17 @@ try: 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 from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector +from sonic_py_common import multi_asic +from utilities_common import multi_asic as multi_asic_util +from utilities_common.general import load_db_config WRED_PROFILE_TABLE_NAME = "WRED_PROFILE" WRED_CONFIG_FIELDS = { @@ -82,7 +88,6 @@ WRED_CONFIG_FIELDS = { "rdrop": "red_drop_probability" } -PORT_TABLE_NAME = "PORT" QUEUE_TABLE_NAME = "QUEUE" DEVICE_NEIGHBOR_TABLE_NAME = "DEVICE_NEIGHBOR" FIELD = "wred_profile" @@ -96,18 +101,25 @@ class EcnConfig(object): """ Process ecnconfig """ - def __init__(self, filename, verbose): + def __init__(self, test_filename, verbose, namespace): self.ports = [] self.queues = [] - self.filename = filename self.verbose = verbose + self.namespace = namespace + self.multi_asic = multi_asic_util.MultiAsic(namespace_option=namespace) + self.config_db = None + self.num_wred_profiles = 0 - # Set up db connections - self.db = ConfigDBConnector() - self.db.connect() + # For unit testing + self.test_filename = test_filename + self.updated_profile_tables = {} + @multi_asic_util.run_on_multi_asic def list(self): - wred_profiles = self.db.get_table(WRED_PROFILE_TABLE_NAME) + """ + List all WRED profiles. + """ + wred_profiles = self.config_db.get_table(WRED_PROFILE_TABLE_NAME) for name, data in wred_profiles.items(): profile_name = name profile_data = data @@ -117,12 +129,18 @@ class EcnConfig(object): line = [field, value] config.append(line) print(tabulate(config) + "\n") - if self.verbose: - print("Total profiles: %d" % len(wred_profiles)) + self.num_wred_profiles += len(wred_profiles) - # get parameters of a WRED profile def get_profile_data(self, profile): - wred_profiles = self.db.get_table(WRED_PROFILE_TABLE_NAME) + """ + Get parameters of a WRED profile + """ + if self.namespace or not multi_asic.is_multi_asic(): + db = ConfigDBConnector(namespace=self.namespace) + db.connect() + wred_profiles = db.get_table(WRED_PROFILE_TABLE_NAME) + else: + wred_profiles = multi_asic.get_table(WRED_PROFILE_TABLE_NAME) for profile_name, profile_data in wred_profiles.items(): if profile_name == profile: @@ -131,6 +149,9 @@ class EcnConfig(object): return None def validate_profile_data(self, profile_data): + """ + Validate threshold, probability and color values. + """ result = True # check if thresholds are non-negative integers @@ -168,73 +189,116 @@ class EcnConfig(object): return result + @multi_asic_util.run_on_multi_asic def set_wred_threshold(self, profile, threshold, value): + """ + Single asic behaviour: + Set threshold value on default namespace + + Multi asic behaviour: + Set threshold value on the specified namespace. + If no namespace is provided, set on all namespaces. + """ chk_exec_privilege() + # Modify the threshold field = WRED_CONFIG_FIELDS[threshold] if self.verbose: - print("Setting %s value to %s" % (field, value)) - self.db.mod_entry(WRED_PROFILE_TABLE_NAME, profile, {field: value}) - if self.filename is not None: - prof_table = self.db.get_table(WRED_PROFILE_TABLE_NAME) - with open(self.filename, "w") as fd: - json.dump(prof_table, fd) + namespace_str = f" for namespace {self.multi_asic.current_namespace}" if multi_asic.is_multi_asic() else '' + print("Setting %s value to %s%s" % (field, value, namespace_str)) + self.config_db.mod_entry(WRED_PROFILE_TABLE_NAME, profile, {field: value}) + + # Record the change for unit testing + if self.test_filename: + profile_table = self.config_db.get_table(WRED_PROFILE_TABLE_NAME) + if self.multi_asic.current_namespace in self.updated_profile_tables.keys(): + self.updated_profile_tables[self.multi_asic.current_namespace][profile][threshold] = value + else: + self.updated_profile_tables[self.multi_asic.current_namespace] = profile_table + @multi_asic_util.run_on_multi_asic def set_wred_prob(self, profile, drop_color, value): + """ + Single asic behaviour: + Set drop probability on default namespace + + Multi asic behaviour: + Set drop probability value on the specified namespace. + If no namespace is provided, set on all namespaces. + """ chk_exec_privilege() + # Modify the drop probability field = WRED_CONFIG_FIELDS[drop_color] if self.verbose: - print("Setting %s value to %s%%" % (field, value)) - self.db.mod_entry(WRED_PROFILE_TABLE_NAME, profile, {field: value}) - if self.filename is not None: - prof_table = self.db.get_table(WRED_PROFILE_TABLE_NAME) - with open(self.filename, "w") as fd: - json.dump(prof_table, fd) + namespace_str = f" for namespace {self.multi_asic.current_namespace}" if multi_asic.is_multi_asic() else '' + print("Setting %s value to %s%%%s" % (field, value, namespace_str)) + self.config_db.mod_entry(WRED_PROFILE_TABLE_NAME, profile, {field: value}) + + # Record the change for unit testing + if self.test_filename: + profile_table = self.config_db.get_table(WRED_PROFILE_TABLE_NAME) + if self.multi_asic.current_namespace in self.updated_profile_tables.keys(): + self.updated_profile_tables[self.multi_asic.current_namespace][profile][field] = value + else: + self.updated_profile_tables[self.multi_asic.current_namespace] = profile_table class EcnQ(object): """ Process ecn on/off on queues """ - def __init__(self, queues, filename, verbose): + def __init__(self, queues, test_filename, verbose, namespace): self.ports_key = [] self.queues = queues.split(',') - self.filename = filename self.verbose = verbose + self.namespace = namespace + self.multi_asic = multi_asic_util.MultiAsic(namespace_option=namespace) + self.config_db = None + self.db = None - # Set up db connections - self.config_db = ConfigDBConnector() - self.config_db.connect() - - self.db = SonicV2Connector(use_unix_socket_path=False) - self.db.connect(self.db.CONFIG_DB) - - self.gen_ports_key() + # For unit testing + self.test_filename = test_filename + self.updated_q_table = {} def gen_ports_key(self): - if self.ports_key is not None: - port_table = self.config_db.get_table(DEVICE_NEIGHBOR_TABLE_NAME) - self.ports_key = list(port_table.keys()) + port_table = self.config_db.get_table(DEVICE_NEIGHBOR_TABLE_NAME) + self.ports_key = list(port_table.keys()) - # Verify at least one port is available - if len(self.ports_key) == 0: - raise Exception("No active ports detected in table '{}'".format(DEVICE_NEIGHBOR_TABLE_NAME)) + # Verify at least one port is available + if len(self.ports_key) == 0: + raise Exception("No active ports detected in table '{}'".format(DEVICE_NEIGHBOR_TABLE_NAME)) - # In multi-ASIC platforms backend ethernet ports are identified as - # 'Ethernet-BPxy'. Add 1024 to sort backend ports to the end. - self.ports_key.sort( - key = lambda k: int(k[8:]) if "BP" not in k else int(k[11:]) + 1024 - ) + # In multi-ASIC platforms backend ethernet ports are identified as + # 'Ethernet-BPxy'. Add 1024 to sort backend ports to the end. + self.ports_key.sort( + key = lambda k: int(k[8:]) if "BP" not in k else int(k[11:]) + 1024 + ) def dump_table_info(self): - if self.filename is not None: + """ + A function to dump updated queue tables. + These JSON dumps are used exclusively by unit tests. + The tables are organized by namespaces for multi-asic support. + """ + if self.test_filename is not None: q_table = self.config_db.get_table(QUEUE_TABLE_NAME) - with open(self.filename, "w") as fd: - json.dump({repr(x):y for x, y in q_table.items()}, fd) + with open(self.test_filename, "w") as fd: + self.updated_q_table[self.multi_asic.current_namespace] = {repr(x):y for x, y in q_table.items()} + json.dump(self.updated_q_table, fd) + @multi_asic_util.run_on_multi_asic def set(self, enable): + """ + Single asic behaviour: + Enable or disable queues on default namespace + + Multi asic behaviour: + Enable or disable queues on a specified namespace. + If no namespace is provided, set on all namespaces. + """ chk_exec_privilege() + self.gen_ports_key() for queue in self.queues: if self.verbose: print("%s ECN on %s queue %s" % ("Enable" if enable else "Disable", ','.join(self.ports_key), queue)) @@ -252,10 +316,24 @@ class EcnQ(object): self.config_db.mod_entry(QUEUE_TABLE_NAME, key, None) else: self.config_db.set_entry(QUEUE_TABLE_NAME, key, entry) + # For unit testing self.dump_table_info() + @multi_asic_util.run_on_multi_asic def get(self): - print("ECN status:") + """ + Single asic behaviour: + Get status of queues on default namespace + + Multi asic behaviour: + Get status of queues on a specified namespace. + If no namespace is provided, get queue status on all namespaces. + """ + self.gen_ports_key() + namespace = self.multi_asic.current_namespace + namespace_str = f" for namespace {namespace}" if namespace else '' + print(f"ECN status{namespace_str}:") + for queue in self.queues: out = ' '.join(['queue', queue]) if self.verbose: @@ -270,81 +348,77 @@ class EcnQ(object): print("%s: on" % (out)) else: print("%s: off" % (out)) + # For unit testing self.dump_table_info() -def main(): - parser = argparse.ArgumentParser(description='Show and change:\n' - '1) ECN WRED configuration\n' - '2) ECN on/off status on queues', - formatter_class=argparse.RawTextHelpFormatter) - - parser.add_argument('-l', '--list', action='store_true', help='show ECN WRED configuration') - parser.add_argument('-p', '--profile', type=str, help='specify WRED profile name', default=None) - parser.add_argument('-gmin', '--green-min', type=str, help='set min threshold for packets marked \'green\'', default=None) - parser.add_argument('-gmax', '--green-max', type=str, help='set max threshold for packets marked \'green\'', default=None) - parser.add_argument('-ymin', '--yellow-min', type=str, help='set min threshold for packets marked \'yellow\'', default=None) - parser.add_argument('-ymax', '--yellow-max', type=str, help='set max threshold for packets marked \'yellow\'', default=None) - parser.add_argument('-rmin', '--red-min', type=str, help='set min threshold for packets marked \'red\'', default=None) - parser.add_argument('-rmax', '--red-max', type=str, help='set max threshold for packets marked \'red\'', default=None) - parser.add_argument('-gdrop', '--green-drop-prob', type=str, help='set max drop/mark probability for packets marked \'green\'', default=None) - parser.add_argument('-ydrop', '--yellow-drop-prob', type=str, help='set max drop/mark probability for packets marked \'yellow\'', default=None) - parser.add_argument('-rdrop', '--red-drop-prob', type=str, help='set max drop/mark probability for packets marked \'red\'', default=None) - parser.add_argument('-v', '--version', action='version', version='%(prog)s 1.0') - parser.add_argument('-vv', '--verbose', action='store_true', help='Verbose output', default=False) - - parser.add_argument('command', nargs='?', choices=['on', 'off'], type=str, help='turn on/off ecn', default=None) - parser.add_argument('-q', '--queue', type=str, help='specify queue index list: 3,4', default=None) - parser.add_argument('-f', '--filename', help='file used by mock tests', type=str, default=None) - +@click.command(help='Show and change: ECN WRED configuration\nECN on/off status on queues') +@click.argument('command', type=click.Choice(['on', 'off'], case_sensitive=False), required=False, default=None) +@click.option('-l', '--list', 'show_config', is_flag=True, help='show ECN WRED configuration') +@click.option('-p', '--profile', type=str, help='specify WRED profile name', default=None) +@click.option('-gmin', '--green-min', type=str, help='set min threshold for packets marked \'green\'', default=None) +@click.option('-gmax', '--green-max', type=str, help='set max threshold for packets marked \'green\'', default=None) +@click.option('-ymin', '--yellow-min', type=str, help='set min threshold for packets marked \'yellow\'', default=None) +@click.option('-ymax', '--yellow-max', type=str, help='set max threshold for packets marked \'yellow\'', default=None) +@click.option('-rmin', '--red-min', type=str, help='set min threshold for packets marked \'red\'', default=None) +@click.option('-rmax', '--red-max', type=str, help='set max threshold for packets marked \'red\'', default=None) +@click.option('-gdrop', '--green-drop-prob', type=str, help='set max drop/mark probability for packets marked \'green\'', default=None) +@click.option('-ydrop', '--yellow-drop-prob', type=str, help='set max drop/mark probability for packets marked \'yellow\'', default=None) +@click.option('-rdrop', '--red-drop-prob', type=str, help='set max drop/mark probability for packets marked \'red\'', default=None) +@click.option('-n', '--namespace', type=click.Choice(multi_asic.get_namespace_list()), help='Namespace name or skip for all', default=None) +@click.option('-vv', '--verbose', is_flag=True, help='Verbose output', default=False) +@click.option('-q', '--queue', type=str, help='specify queue index list: 3,4', default=None) +@click.version_option(version='1.0') +def main(command, show_config, profile, green_min, + green_max, yellow_min, yellow_max, red_min, + red_max, green_drop_prob, yellow_drop_prob, + red_drop_prob, namespace, verbose, queue): + test_filename = None if os.environ.get("UTILITIES_UNIT_TESTING", "0") == "2": - sys.argv.extend(['-f', '/tmp/ecnconfig']) - - args = parser.parse_args() + test_filename = '/tmp/ecnconfig' try: - if args.list or args.profile: - prof_cfg = EcnConfig(args.filename, args.verbose) - if args.list: - arg_len_max = 2 - if args.verbose: - arg_len_max += 1 - if args.filename: - arg_len_max += 2 - if len(sys.argv) > arg_len_max: + load_db_config() + if show_config or profile: + # Check if a set option has been provided + setOption = (green_min or green_max or yellow_min or yellow_max or red_min or red_max + or green_drop_prob or yellow_drop_prob or red_drop_prob) + + prof_cfg = EcnConfig(test_filename, verbose, namespace) + if show_config: + if setOption: raise Exception("Input arguments error. No set options allowed when -l[ist] specified") + prof_cfg.list() - elif args.profile: - arg_len_min = 4 - if args.verbose: - arg_len_min += 1 - if args.filename: - arg_len_min += 2 - if len(sys.argv) < arg_len_min: + if verbose: + print("Total profiles: %d" % prof_cfg.num_wred_profiles) + + elif profile: + if not setOption: raise Exception("Input arguments error. Specify at least one threshold parameter to set") # get current configuration data - wred_profile_data = prof_cfg.get_profile_data(args.profile) + wred_profile_data = prof_cfg.get_profile_data(profile) if wred_profile_data is None: - raise Exception("Input arguments error. Invalid WRED profile %s" % (args.profile)) - - if args.green_max: - wred_profile_data[WRED_CONFIG_FIELDS["gmax"]] = args.green_max - if args.green_min: - wred_profile_data[WRED_CONFIG_FIELDS["gmin"]] = args.green_min - if args.yellow_max: - wred_profile_data[WRED_CONFIG_FIELDS["ymax"]] = args.yellow_max - if args.yellow_min: - wred_profile_data[WRED_CONFIG_FIELDS["ymin"]] = args.yellow_min - if args.red_max: - wred_profile_data[WRED_CONFIG_FIELDS["rmax"]] = args.red_max - if args.red_min: - wred_profile_data[WRED_CONFIG_FIELDS["rmin"]] = args.red_min - if args.green_drop_prob: - wred_profile_data[WRED_CONFIG_FIELDS["gdrop"]] = args.green_drop_prob - if args.yellow_drop_prob: - wred_profile_data[WRED_CONFIG_FIELDS["ydrop"]] = args.yellow_drop_prob - if args.red_drop_prob: - wred_profile_data[WRED_CONFIG_FIELDS["rdrop"]] = args.red_drop_prob + raise Exception("Input arguments error. Invalid WRED profile %s for namespace %s" % (profile, namespace)) + + if green_max: + wred_profile_data[WRED_CONFIG_FIELDS["gmax"]] = green_max + if green_min: + wred_profile_data[WRED_CONFIG_FIELDS["gmin"]] = green_min + if yellow_max: + wred_profile_data[WRED_CONFIG_FIELDS["ymax"]] = yellow_max + if yellow_min: + wred_profile_data[WRED_CONFIG_FIELDS["ymin"]] = yellow_min + if red_max: + wred_profile_data[WRED_CONFIG_FIELDS["rmax"]] = red_max + if red_min: + wred_profile_data[WRED_CONFIG_FIELDS["rmin"]] = red_min + if green_drop_prob: + wred_profile_data[WRED_CONFIG_FIELDS["gdrop"]] = green_drop_prob + if yellow_drop_prob: + wred_profile_data[WRED_CONFIG_FIELDS["ydrop"]] = yellow_drop_prob + if red_drop_prob: + wred_profile_data[WRED_CONFIG_FIELDS["rdrop"]] = red_drop_prob # validate new configuration data if prof_cfg.validate_profile_data(wred_profile_data) == False: @@ -352,41 +426,39 @@ def main(): # apply new configuration # the following parameters can be combined in one run - if args.green_max: - prof_cfg.set_wred_threshold(args.profile, "gmax", args.green_max) - if args.green_min: - prof_cfg.set_wred_threshold(args.profile, "gmin", args.green_min) - if args.yellow_max: - prof_cfg.set_wred_threshold(args.profile, "ymax", args.yellow_max) - if args.yellow_min: - prof_cfg.set_wred_threshold(args.profile, "ymin", args.yellow_min) - if args.red_max: - prof_cfg.set_wred_threshold(args.profile, "rmax", args.red_max) - if args.red_min: - prof_cfg.set_wred_threshold(args.profile, "rmin", args.red_min) - if args.green_drop_prob: - prof_cfg.set_wred_prob(args.profile, "gdrop", args.green_drop_prob) - if args.yellow_drop_prob: - prof_cfg.set_wred_prob(args.profile, "ydrop", args.yellow_drop_prob) - if args.red_drop_prob: - prof_cfg.set_wred_prob(args.profile, "rdrop", args.red_drop_prob) - - elif args.queue: - arg_len_min = 3 - if args.filename: - arg_len_min += 1 - if args.verbose: - arg_len_min += 1 - if len(sys.argv) < arg_len_min: + if green_max: + prof_cfg.set_wred_threshold(profile, "gmax", green_max) + if green_min: + prof_cfg.set_wred_threshold(profile, "gmin", green_min) + if yellow_max: + prof_cfg.set_wred_threshold(profile, "ymax", yellow_max) + if yellow_min: + prof_cfg.set_wred_threshold(profile, "ymin", yellow_min) + if red_max: + prof_cfg.set_wred_threshold(profile, "rmax", red_max) + if red_min: + prof_cfg.set_wred_threshold(profile, "rmin", red_min) + if green_drop_prob: + prof_cfg.set_wred_prob(profile, "gdrop", green_drop_prob) + if yellow_drop_prob: + prof_cfg.set_wred_prob(profile, "ydrop", yellow_drop_prob) + if red_drop_prob: + prof_cfg.set_wred_prob(profile, "rdrop", red_drop_prob) + + # Dump the current config in the file for unit tests + if test_filename: + with open(test_filename, "w") as fd: + json.dump(prof_cfg.updated_profile_tables, fd) + + elif queue: + if queue.split(',') == ['']: raise Exception("Input arguments error. Specify at least one queue by index") - - q_ecn = EcnQ(args.queue, args.filename, args.verbose) - if not args.command: + q_ecn = EcnQ(queue, test_filename, verbose, namespace) + if command is None: q_ecn.get() else: - q_ecn.set(enable = True if args.command == 'on' else False) + q_ecn.set(enable = True if command == 'on' else False) else: - parser.print_help() sys.exit(1) except Exception as e: diff --git a/show/main.py b/show/main.py index 06114eb79f..c9e5e2086c 100755 --- a/show/main.py +++ b/show/main.py @@ -2008,10 +2008,13 @@ def policer(policer_name, verbose): # 'ecn' command ("show ecn") # @cli.command('ecn') +@multi_asic_util.multi_asic_click_option_namespace @click.option('--verbose', is_flag=True, help="Enable verbose output") -def ecn(verbose): +def ecn(namespace, verbose): """Show ECN configuration""" cmd = ['ecnconfig', '-l'] + if namespace is not None: + cmd += ['-n', str(namespace)] run_command(cmd, display_cmd=verbose) diff --git a/tests/ecn_input/ecn_test_vectors.py b/tests/ecn_input/ecn_test_vectors.py index c53bf48a24..fe47f0b7a3 100644 --- a/tests/ecn_input/ecn_test_vectors.py +++ b/tests/ecn_input/ecn_test_vectors.py @@ -18,205 +18,356 @@ """ +ecn_show_config_output_specific_namespace = """\ +Profile: AZURE_LOSSLESS +----------------------- ------- +red_max_threshold 2097152 +ecn ecn_all +green_min_threshold 1048576 +red_min_threshold 1048576 +yellow_min_threshold 1048576 +green_max_threshold 2097152 +green_drop_probability 5 +yellow_max_threshold 2097152 +yellow_drop_probability 5 +red_drop_probability 5 +----------------------- ------- + +""" + +ecn_show_config_output_multi = """\ +Profile: AZURE_LOSSLESS +----------------------- ------- +red_max_threshold 2097152 +ecn ecn_all +green_min_threshold 1048576 +red_min_threshold 1048576 +yellow_min_threshold 1048576 +green_max_threshold 2097152 +green_drop_probability 5 +yellow_max_threshold 2097152 +yellow_drop_probability 5 +red_drop_probability 5 +----------------------- ------- + +Profile: AZURE_LOSSY +----------------------- ----- +red_max_threshold 32760 +red_min_threshold 4095 +yellow_max_threshold 32760 +yellow_min_threshold 4095 +green_max_threshold 32760 +green_min_threshold 4095 +yellow_drop_probability 2 +----------------------- ----- + +""" + testData = { - 'ecn_show_config' : {'cmd' : ['show'], - 'args' : [], - 'rc' : 0, - 'rc_output': ecn_show_config_output + 'ecn_show_config': {'cmd': ['show'], + 'args': [], + 'rc': 0, + 'rc_output': ecn_show_config_output }, - 'ecn_show_config_verbose' : {'cmd' : ['q_cmd'], - 'args' : ['-l', '-vv'], - 'rc' : 0, - 'rc_output': ecn_show_config_output + 'Total profiles: 1\n' + 'ecn_show_config_verbose': {'cmd': ['q_cmd'], + 'args': ['-l', '-vv'], + 'rc': 0, + 'rc_output': ecn_show_config_output + 'Total profiles: 1\n' }, - 'ecn_cfg_gmin' : {'cmd' : ['config'], - 'args' : ['-profile', 'AZURE_LOSSLESS', '-gmin', '1048600'], - 'rc' : 0, - 'cmp_args' : ['AZURE_LOSSLESS,green_min_threshold,1048600'] + 'ecn_cfg_gmin': {'cmd': ['config'], + 'args': ['-profile', 'AZURE_LOSSLESS', '-gmin', '1048600'], + 'rc': 0, + 'cmp_args': [',AZURE_LOSSLESS,green_min_threshold,1048600'] }, - 'ecn_cfg_gmin_verbose' : {'cmd' : ['config'], - 'args' : ['-profile', 'AZURE_LOSSLESS', '-gmin', '1048600', '-vv'], - 'rc' : 0, - 'cmp_args' : ['AZURE_LOSSLESS,green_min_threshold,1048600'], - 'rc_output' : 'Running command: ecnconfig -p AZURE_LOSSLESS -gmin 1048600 -vv\nSetting green_min_threshold value to 1048600\n' + 'ecn_cfg_gmin_verbose': {'cmd': ['config'], + 'args': ['-profile', 'AZURE_LOSSLESS', '-gmin', '1048600', '-vv'], + 'rc': 0, + 'cmp_args': [',AZURE_LOSSLESS,green_min_threshold,1048600'], + 'rc_output': ('Running command: ecnconfig -p AZURE_LOSSLESS -gmin 1048600 -vv\n' + 'Setting green_min_threshold value to 1048600\n') }, - 'ecn_cfg_gmax' : {'cmd' : ['config'], - 'args' : ['-profile', 'AZURE_LOSSLESS', '-gmax', '2097153'], - 'rc' : 0, - 'cmp_args' : ['AZURE_LOSSLESS,green_max_threshold,2097153'] + 'ecn_cfg_gmax': {'cmd': ['config'], + 'args': ['-profile', 'AZURE_LOSSLESS', '-gmax', '2097153'], + 'rc': 0, + 'cmp_args': [',AZURE_LOSSLESS,green_max_threshold,2097153'] }, - 'ecn_cfg_ymin' : {'cmd' : ['config'], - 'args' : ['-profile', 'AZURE_LOSSLESS', '-ymin', '1048600'], - 'rc' : 0, - 'cmp_args' : ['AZURE_LOSSLESS,yellow_min_threshold,1048600'] + 'ecn_cfg_ymin': {'cmd': ['config'], + 'args': ['-profile', 'AZURE_LOSSLESS', '-ymin', '1048600'], + 'rc': 0, + 'cmp_args': [',AZURE_LOSSLESS,yellow_min_threshold,1048600'] }, - 'ecn_cfg_ymax' : {'cmd' : ['config'], - 'args' : ['-profile', 'AZURE_LOSSLESS', '-ymax', '2097153'], - 'rc' : 0, - 'cmp_args' : ['AZURE_LOSSLESS,yellow_max_threshold,2097153'] + 'ecn_cfg_ymax': {'cmd': ['config'], + 'args': ['-profile', 'AZURE_LOSSLESS', '-ymax', '2097153'], + 'rc': 0, + 'cmp_args': [',AZURE_LOSSLESS,yellow_max_threshold,2097153'] }, - 'ecn_cfg_rmin' : {'cmd' : ['config'], - 'args' : ['-profile', 'AZURE_LOSSLESS', '-rmin', '1048600'], - 'rc' : 0, - 'cmp_args' : ['AZURE_LOSSLESS,red_min_threshold,1048600'] + 'ecn_cfg_rmin': {'cmd': ['config'], + 'args': ['-profile', 'AZURE_LOSSLESS', '-rmin', '1048600'], + 'rc': 0, + 'cmp_args': [',AZURE_LOSSLESS,red_min_threshold,1048600'] }, - 'ecn_cfg_rmax' : {'cmd' : ['config'], - 'args' : ['-profile', 'AZURE_LOSSLESS', '-rmax', '2097153'], - 'rc' : 0, - 'cmp_args' : ['AZURE_LOSSLESS,red_max_threshold,2097153'] + 'ecn_cfg_rmax': {'cmd': ['config'], + 'args': ['-profile', 'AZURE_LOSSLESS', '-rmax', '2097153'], + 'rc': 0, + 'cmp_args': [',AZURE_LOSSLESS,red_max_threshold,2097153'] }, - 'ecn_cfg_rdrop' : {'cmd' : ['config'], - 'args' : ['-profile', 'AZURE_LOSSLESS', '-rdrop', '10'], - 'rc' : 0, - 'cmp_args' : ['AZURE_LOSSLESS,red_drop_probability,10'] + 'ecn_cfg_rdrop': {'cmd': ['config'], + 'args': ['-profile', 'AZURE_LOSSLESS', '-rdrop', '10'], + 'rc': 0, + 'cmp_args': [',AZURE_LOSSLESS,red_drop_probability,10'] }, - 'ecn_cfg_ydrop' : {'cmd' : ['config'], - 'args' : ['-profile', 'AZURE_LOSSLESS', '-ydrop', '11'], - 'rc' : 0, - 'cmp_args' : ['AZURE_LOSSLESS,yellow_drop_probability,11'] + 'ecn_cfg_ydrop': {'cmd': ['config'], + 'args': ['-profile', 'AZURE_LOSSLESS', '-ydrop', '11'], + 'rc': 0, + 'cmp_args': [',AZURE_LOSSLESS,yellow_drop_probability,11'] }, - 'ecn_cfg_gdrop' : {'cmd' : ['config'], - 'args' : ['-profile', 'AZURE_LOSSLESS', '-gdrop', '12'], - 'rc' : 0, - 'cmp_args' : ['AZURE_LOSSLESS,green_drop_probability,12'] + 'ecn_cfg_gdrop': {'cmd': ['config'], + 'args': ['-profile', 'AZURE_LOSSLESS', '-gdrop', '12'], + 'rc': 0, + 'cmp_args': [',AZURE_LOSSLESS,green_drop_probability,12'] }, - 'ecn_cfg_gdrop_verbose' : {'cmd' : ['config'], - 'args' : ['-profile', 'AZURE_LOSSLESS', '-gdrop', '12', '-vv'], - 'rc' : 0, - 'cmp_args' : ['AZURE_LOSSLESS,green_drop_probability,12'], - 'rc_output' : 'Running command: ecnconfig -p AZURE_LOSSLESS -gdrop 12 -vv\nSetting green_drop_probability value to 12%\n' + 'ecn_cfg_gdrop_verbose': {'cmd': ['config'], + 'args': ['-profile', 'AZURE_LOSSLESS', '-gdrop', '12', '-vv'], + 'rc': 0, + 'cmp_args': [',AZURE_LOSSLESS,green_drop_probability,12'], + 'rc_output': ('Running command: ecnconfig -p AZURE_LOSSLESS -gdrop 12 -vv\n' + 'Setting green_drop_probability value to 12%\n') }, - 'ecn_cfg_multi_set' : {'cmd' : ['config'], - 'args' : ['-profile', 'AZURE_LOSSLESS', '-gdrop', '12', '-gmax', '2097153'], - 'rc' : 0, - 'cmp_args' : ['AZURE_LOSSLESS,green_drop_probability,12', - 'AZURE_LOSSLESS,green_max_threshold,2097153' - ] + 'ecn_cfg_multi_set': {'cmd': ['config'], + 'args': ['-profile', 'AZURE_LOSSLESS', '-gdrop', '12', '-gmax', '2097153'], + 'rc': 0, + 'cmp_args': [',AZURE_LOSSLESS,green_drop_probability,12', + ',AZURE_LOSSLESS,green_max_threshold,2097153'] }, - 'ecn_cfg_gmin_gmax_invalid' : {'cmd' : ['config'], - 'args' : ['-profile', 'AZURE_LOSSLESS', '-gmax', '2097153', '-gmin', '2097154'], - 'rc' : 1, - 'rc_msg' : 'Invalid gmin (2097154) and gmax (2097153). gmin should be smaller than gmax' + 'ecn_cfg_gmin_gmax_invalid': {'cmd': ['config'], + 'args': ['-profile', 'AZURE_LOSSLESS', '-gmax', + '2097153', '-gmin', '2097154'], + 'rc': 1, + 'rc_msg': ('Invalid gmin (2097154) and gmax (2097153).' + ' gmin should be smaller than gmax') }, - 'ecn_cfg_ymin_ymax_invalid' : {'cmd' : ['config'], - 'args' : ['-profile', 'AZURE_LOSSLESS', '-ymax', '2097153', '-ymin', '2097154'], - 'rc' : 1, - 'rc_msg' : 'Invalid ymin (2097154) and ymax (2097153). ymin should be smaller than ymax' + 'ecn_cfg_ymin_ymax_invalid': {'cmd': ['config'], + 'args': ['-profile', 'AZURE_LOSSLESS', '-ymax', + '2097153', '-ymin', '2097154'], + 'rc': 1, + 'rc_msg': ('Invalid ymin (2097154) and ymax (2097153).' + ' ymin should be smaller than ymax') }, - 'ecn_cfg_rmin_rmax_invalid' : {'cmd' : ['config'], - 'args' : ['-profile', 'AZURE_LOSSLESS', '-rmax', '2097153', '-rmin', '2097154'], - 'rc' : 1, - 'rc_msg' : 'Invalid rmin (2097154) and rmax (2097153). rmin should be smaller than rmax' + 'ecn_cfg_rmin_rmax_invalid': {'cmd': ['config'], + 'args': ['-profile', 'AZURE_LOSSLESS', '-rmax', + '2097153', '-rmin', '2097154'], + 'rc': 1, + 'rc_msg': ('Invalid rmin (2097154) and rmax (2097153).' + ' rmin should be smaller than rmax') }, - 'ecn_cfg_rmax_invalid' : {'cmd' : ['config'], - 'args' : ['-profile', 'AZURE_LOSSLESS', '-rmax', '-2097153'], - 'rc' : 1, - 'rc_msg' : 'Invalid rmax (-2097153). rmax should be an non-negative integer' - }, - 'ecn_cfg_rdrop_invalid' : {'cmd' : ['config'], - 'args' : ['-profile', 'AZURE_LOSSLESS', '-rdrop', '105'], - 'rc' : 1, - 'rc_msg' : 'Invalid value for "-rdrop": 105 is not in the valid range of 0 to 100' + 'ecn_cfg_rmax_invalid': {'cmd': ['config'], + 'args': ['-profile', 'AZURE_LOSSLESS', '-rmax', '-2097153'], + 'rc': 1, + 'rc_msg': 'Invalid rmax (-2097153). rmax should be an non-negative integer' }, - 'ecn_q_get' : {'cmd' : ['q_cmd'], - 'args' : ['-q', '3'], - 'rc' : 0, - 'rc_msg' : 'ECN status:\nqueue 3: on\n', - 'cmp_args' : ['wred_profile,AZURE_LOSSLESS'], - 'cmp_q_args' : ['3', '4'] + 'ecn_cfg_rdrop_invalid': {'cmd': ['config'], + 'args': ['-profile', 'AZURE_LOSSLESS', '-rdrop', '105'], + 'rc': 1, + 'rc_msg': 'Invalid value for "-rdrop": 105 is not in the valid range of 0 to 100' + }, + 'ecn_q_get': {'cmd': ['q_cmd'], + 'args': ['-q', '3'], + 'rc': 0, + 'rc_msg': 'ECN status:\nqueue 3: on\n', + 'cmp_args': [',wred_profile,AZURE_LOSSLESS'], + 'cmp_q_args': ['3', '4'] }, - 'ecn_q_get_verbose' : {'cmd' : ['q_cmd'], - 'args' : ['-q', '3', '-vv'], - 'rc' : 0, - 'rc_msg' : 'ECN status:\n{0} queue 3: on\n', - 'cmp_args' : ['wred_profile,AZURE_LOSSLESS'], - 'cmp_q_args' : ['3', '4'], - 'db_table' : 'DEVICE_NEIGHBOR' + 'ecn_q_get_verbose': {'cmd': ['q_cmd'], + 'args': ['-q', '3', '-vv'], + 'rc': 0, + 'rc_msg': 'ECN status:\n{0} queue 3: on\n', + 'cmp_args': [',wred_profile,AZURE_LOSSLESS'], + 'cmp_q_args': ['3', '4'], + 'db_table': 'DEVICE_NEIGHBOR' }, - 'ecn_lossy_q_get' : {'cmd' : ['q_cmd'], - 'args' : ['-q', '2'], - 'rc' : 0, - 'rc_msg' : 'ECN status:\nqueue 2: off\n', - 'cmp_args' : [None], - 'cmp_q_args' : ['2'] + 'ecn_lossy_q_get': {'cmd': ['q_cmd'], + 'args': ['-q', '2'], + 'rc': 0, + 'rc_msg': 'ECN status:\nqueue 2: off\n', + 'cmp_args': [',None,None'], + 'cmp_q_args': ['2'] }, - 'ecn_q_all_get_verbose' : {'cmd' : ['q_cmd'], - 'args' : ['-q', '3,4', '-vv'], - 'rc' : 0, - 'rc_msg' : 'ECN status:\n{0} queue 3: on\n{0} queue 4: on\n', - 'cmp_args' : ['wred_profile,AZURE_LOSSLESS'], - 'cmp_q_args' : ['3', '4'], - 'db_table' : 'DEVICE_NEIGHBOR' + 'ecn_q_all_get_verbose': {'cmd': ['q_cmd'], + 'args': ['-q', '3,4', '-vv'], + 'rc': 0, + 'rc_msg': 'ECN status:\n{0} queue 3: on\n{0} queue 4: on\n', + 'cmp_args': [',wred_profile,AZURE_LOSSLESS'], + 'cmp_q_args': ['3', '4'], + 'db_table': 'DEVICE_NEIGHBOR' }, - 'ecn_q_all_get' : {'cmd' : ['q_cmd'], - 'args' : ['-q', '3,4'], - 'rc' : 0, - 'rc_msg' : 'ECN status:\nqueue 3: on\nqueue 4: on\n', - 'cmp_args' : ['wred_profile,AZURE_LOSSLESS'], - 'cmp_q_args' : ['3', '4'] + 'ecn_q_all_get': {'cmd': ['q_cmd'], + 'args': ['-q', '3,4'], + 'rc': 0, + 'rc_msg': 'ECN status:\nqueue 3: on\nqueue 4: on\n', + 'cmp_args': [',wred_profile,AZURE_LOSSLESS'], + 'cmp_q_args': ['3', '4'] }, - 'ecn_cfg_q_all_off' : {'cmd' : ['q_cmd'], - 'args' : ['-q', '3,4', 'off'], - 'rc' : 0, - 'cmp_args' : [None], - 'cmp_q_args' : ['3', '4'] - }, - 'ecn_cfg_q_all_off_verbose' : {'cmd' : ['q_cmd'], - 'args' : ['-q', '3,4', 'off', '-vv'], - 'rc' : 0, - 'cmp_args' : [None], - 'cmp_q_args' : ['3', '4'], - 'db_table' : 'DEVICE_NEIGHBOR', - 'rc_msg' : 'Disable ECN on {0} queue 3\nDisable ECN on {0} queue 4' + 'ecn_cfg_q_all_off': {'cmd': ['q_cmd'], + 'args': ['-q', '3,4', 'off'], + 'rc': 0, + 'cmp_args': [',None,None'], + 'cmp_q_args': ['3', '4'] + }, + 'ecn_cfg_q_all_off_verbose': {'cmd': ['q_cmd'], + 'args': ['-q', '3,4', 'off', '-vv'], + 'rc': 0, + 'cmp_args': [',None,None'], + 'cmp_q_args': ['3', '4'], + 'db_table': 'DEVICE_NEIGHBOR', + 'rc_msg': 'Disable ECN on {0} queue 3\nDisable ECN on {0} queue 4' }, - 'ecn_cfg_q_off' : {'cmd' : ['q_cmd'], - 'args' : ['-q', '3', 'off'], - 'rc' : 0, - 'cmp_args' : [None, 'wred_profile,AZURE_LOSSLESS'], - 'cmp_q_args' : ['3'], - 'other_q' : ['4'] + 'ecn_cfg_q_off': {'cmd': ['q_cmd'], + 'args': ['-q', '3', 'off'], + 'rc': 0, + 'cmp_args': [',None,None', ',wred_profile,AZURE_LOSSLESS'], + 'cmp_q_args': ['3'], + 'other_q': ['4'] }, - 'ecn_cfg_q_off_verbose' : {'cmd' : ['q_cmd'], - 'args' : ['-q', '3', 'off', '-vv'], - 'rc' : 0, - 'cmp_args' : [None, 'wred_profile,AZURE_LOSSLESS'], - 'cmp_q_args' : ['3'], - 'other_q' : ['4'], - 'db_table' : 'DEVICE_NEIGHBOR', - 'rc_msg' : 'Disable ECN on {0} queue 3' + 'ecn_cfg_q_off_verbose': {'cmd': ['q_cmd'], + 'args': ['-q', '3', 'off', '-vv'], + 'rc': 0, + 'cmp_args': [',None,None', ',wred_profile,AZURE_LOSSLESS'], + 'cmp_q_args': ['3'], + 'other_q': ['4'], + 'db_table': 'DEVICE_NEIGHBOR', + 'rc_msg': 'Disable ECN on {0} queue 3' }, - 'ecn_cfg_q_all_on' : {'cmd' : ['q_cmd'], - 'args' : ['-q', '3,4', 'on'], - 'rc' : 0, - 'cmp_args' : ['wred_profile,AZURE_LOSSLESS'], - 'cmp_q_args' : ['3', '4'] + 'ecn_cfg_q_all_on': {'cmd': ['q_cmd'], + 'args': ['-q', '3,4', 'on'], + 'rc': 0, + 'cmp_args': [',wred_profile,AZURE_LOSSLESS'], + 'cmp_q_args': ['3', '4'] }, - 'ecn_cfg_q_all_on_verbose' : {'cmd' : ['q_cmd'], - 'args' : ['-q', '3,4', 'on', '-vv'], - 'rc' : 0, - 'cmp_args' : ['wred_profile,AZURE_LOSSLESS'], - 'cmp_q_args' : ['3', '4'], - 'db_table' : 'DEVICE_NEIGHBOR', - 'rc_msg' : 'Enable ECN on {0} queue 3\nEnable ECN on {0} queue 4' + 'ecn_cfg_q_all_on_verbose': {'cmd': ['q_cmd'], + 'args': ['-q', '3,4', 'on', '-vv'], + 'rc': 0, + 'cmp_args': [',wred_profile,AZURE_LOSSLESS'], + 'cmp_q_args': ['3', '4'], + 'db_table': 'DEVICE_NEIGHBOR', + 'rc_msg': 'Enable ECN on {0} queue 3\nEnable ECN on {0} queue 4' }, - 'ecn_cfg_q_on' : {'cmd' : ['q_cmd'], - 'args' : ['-q', '4', 'on'], - 'rc' : 0, - 'cmp_args' : ['wred_profile,AZURE_LOSSLESS'], - 'cmp_q_args' : ['3', '4'] + 'ecn_cfg_q_on': {'cmd': ['q_cmd'], + 'args': ['-q', '4', 'on'], + 'rc': 0, + 'cmp_args': [',wred_profile,AZURE_LOSSLESS'], + 'cmp_q_args': ['3', '4'] }, - 'ecn_cfg_q_on_verbose' : {'cmd' : ['q_cmd'], - 'args' : ['-q', '4', 'on', '-vv'], - 'rc' : 0, - 'cmp_args' : ['wred_profile,AZURE_LOSSLESS'], - 'cmp_q_args' : ['3', '4'], - 'db_table' : 'DEVICE_NEIGHBOR', - 'rc_msg' : 'Enable ECN on {0} queue 4' + 'ecn_cfg_q_on_verbose': {'cmd': ['q_cmd'], + 'args': ['-q', '4', 'on', '-vv'], + 'rc': 0, + 'cmp_args': [',wred_profile,AZURE_LOSSLESS'], + 'cmp_q_args': ['3', '4'], + 'db_table': 'DEVICE_NEIGHBOR', + 'rc_msg': 'Enable ECN on {0} queue 4' }, - 'ecn_cfg_lossy_q_on' : {'cmd' : ['q_cmd'], - 'args' : ['-q', '0,1,2,5,6,7', 'on'], - 'rc' : 0, - 'cmp_args' : ['wred_profile,AZURE_LOSSLESS'], - 'cmp_q_args' : ['0', '1', '2', '5', '6', '7'] - } + 'ecn_cfg_lossy_q_on': {'cmd': ['q_cmd'], + 'args': ['-q', '0,1,2,5,6,7', 'on'], + 'rc': 0, + 'cmp_args': [',wred_profile,AZURE_LOSSLESS'], + 'cmp_q_args': ['0', '1', '2', '5', '6', '7'] + }, + 'ecn_show_config_masic': {'cmd': ['show_masic'], + 'args': ['-l'], + 'rc': 0, + 'rc_output': ecn_show_config_output_multi, + }, + 'test_ecn_show_config_verbose_masic': {'cmd': ['show_masic'], + 'args': ['-l', '-vv'], + 'rc': 0, + 'rc_output': ecn_show_config_output_multi + 'Total profiles: 2\n', + }, + 'test_ecn_show_config_namespace': {'cmd': ['show_masic'], + 'args': ['-l', '-n', 'asic0'], + 'rc': 0, + 'rc_output': ecn_show_config_output_specific_namespace, + }, + 'test_ecn_show_config_namespace_verbose': {'cmd': ['show_masic'], + 'args': ['-l', '-n', 'asic0', '-vv'], + 'rc': 0, + 'rc_output': ecn_show_config_output_specific_namespace + + 'Total profiles: 1\n', + }, + 'ecn_cfg_threshold_masic': {'cmd': ['config_masic'], + 'args': ['-p', 'AZURE_LOSSY', '-gmax', '35000', '-n', 'asic1'], + 'rc': 0, + 'cmp_args': ['asic1,AZURE_LOSSY,green_max_threshold,35000'] + }, + 'ecn_cfg_probability_masic': {'cmd': ['config_masic'], + 'args': ['-p', 'AZURE_LOSSY', '-ydrop', '3', '-n', 'asic1'], + 'rc': 0, + 'cmp_args': ['asic1,AZURE_LOSSY,yellow_drop_probability,3'] + }, + 'ecn_cfg_gdrop_verbose_all_masic': {'cmd': ['config_masic'], + 'args': ['-p', 'AZURE_LOSSLESS', '-gdrop', '12', '-vv'], + 'rc': 0, + 'cmp_args': ['asic0-asic1,AZURE_LOSSLESS,green_drop_probability,12'], + 'rc_output': ('Setting green_drop_probability value to 12% ' + 'for namespace asic0\n' + 'Setting green_drop_probability value to 12% ' + 'for namespace asic1\n') + }, + 'ecn_cfg_multi_set_verbose_all_masic': {'cmd': ['config_masic'], + 'args': ['-p', 'AZURE_LOSSLESS', '-gdrop', + '14', '-gmax', '2097153', '-vv'], + 'rc': 0, + 'cmp_args': [('asic0-asic1,AZURE_LOSSLESS,' + 'green_drop_probability,14'), + ('asic0-asic1,AZURE_LOSSLESS,' + 'green_max_threshold,2097153')], + 'rc_output': ('Setting green_max_threshold value to 2097153 ' + 'for namespace asic0\n' + 'Setting green_max_threshold value to 2097153 ' + 'for namespace asic1\n' + 'Setting green_drop_probability value to 14% ' + 'for namespace asic0\n' + 'Setting green_drop_probability value to 14% ' + 'for namespace asic1\n') + }, + 'ecn_q_get_masic': {'cmd': ['q_cmd'], + 'args': ['-q', '1', '-n', 'asic0'], + 'rc': 0, + 'rc_msg': 'ECN status for namespace asic0:\nqueue 1: on\n', + 'cmp_args': ['asic0,wred_profile,AZURE_LOSSLESS'], + 'cmp_q_args': ['1'] + }, + 'ecn_q_get_verbose_masic': {'cmd': ['q_cmd'], + 'args': ['-q', '1', '-vv', '-n', 'asic0'], + 'rc': 0, + 'rc_msg': 'ECN status for namespace asic0:\nEthernet4 queue 1: on\n', + 'cmp_args': ['asic0,wred_profile,AZURE_LOSSLESS'], + 'cmp_q_args': ['1'], + 'db_table': 'DEVICE_NEIGHBOR' + }, + 'ecn_q_get_all_ns_masic': {'cmd': ['q_cmd'], + 'args': ['-q', '0'], + 'rc': 0, + 'rc_msg': ('ECN status for namespace asic0:\nqueue 0: off\n' + 'ECN status for namespace asic1:\nqueue 0: on\n') + }, + 'ecn_q_get_all_ns_verbose_masic': {'cmd': ['q_cmd'], + 'args': ['-q', '0', '-vv'], + 'rc': 0, + 'rc_msg': ('ECN status for namespace asic0:\nEthernet4 queue 0: off\n' + 'ECN status for namespace asic1:\nEthernet0 queue 0: on\n') + }, + 'ecn_cfg_q_all_ns_off_masic': {'cmd': ['q_cmd'], + 'args': ['-q', '0,1', 'off'], + 'rc': 0, + 'cmp_args': ['asic0-asic1,None,None'], + 'cmp_q_args': ['0', '1'] + }, + 'ecn_cfg_q_one_ns_off_verbose_masic': {'cmd': ['q_cmd'], + 'args': ['-q', '1', 'on', '-n', 'asic1', '-vv'], + 'rc': 0, + 'rc_msg': 'Enable ECN on Ethernet0 queue 1\n', + 'cmp_args': ['asic1,wred_profile,AZURE_LOSSLESS', + 'asic1,wred_profile,AZURE_LOSSLESS'], + 'cmp_q_args': ['0'], + 'other_q': ['1'] + } } diff --git a/tests/ecn_test.py b/tests/ecn_test.py index 13474b12e8..5d2ac36011 100644 --- a/tests/ecn_test.py +++ b/tests/ecn_test.py @@ -6,11 +6,15 @@ from click.testing import CliRunner import config.main as config -from .ecn_input.ecn_test_vectors import * +from .ecn_input.ecn_test_vectors import testData from .utils import get_result_and_return_code from utilities_common.db import Db import show.main as show +# Constants +ARGS_DELIMITER = ',' +NAMESPACE_DELIMITER = '-' + test_path = os.path.dirname(os.path.abspath(__file__)) modules_path = os.path.dirname(test_path) scripts_path = os.path.join(modules_path, "scripts") @@ -18,13 +22,107 @@ sys.path.insert(0, modules_path) -class TestEcnConfig(object): +class TestEcnConfigBase(object): @classmethod def setup_class(cls): + print("SETUP") os.environ["PATH"] += os.pathsep + scripts_path os.environ['UTILITIES_UNIT_TESTING'] = "2" - print("SETUP") + def process_cmp_args(self, cmp_args): + """ + The arguments are a string marked by delimiters + Arguments marked as 'None', are treated as None objects + First arg is always a collection of namespaces + """ + + args = cmp_args.split(ARGS_DELIMITER) + args = [None if arg == "None" else arg for arg in args] + args[0] = args[0].split(NAMESPACE_DELIMITER) + return args + + def verify_profile(self, queue_db_entry, profile, value): + if profile is not None: + assert queue_db_entry[profile] == value + else: + assert profile not in queue_db_entry,\ + "Profile needs to be fully removed from table to propagate NULL OID to SAI" + + def executor(self, input): + runner = CliRunner() + + if 'db_table' in input: + db = Db() + data_list = list(db.cfgdb.get_table(input['db_table'])) + input['rc_msg'] = input['rc_msg'].format(",".join(data_list)) + + if 'show' in input['cmd']: + exec_cmd = show.cli.commands["ecn"] + result = runner.invoke(exec_cmd, input['args']) + exit_code = result.exit_code + output = result.output + elif 'q_cmd' in input['cmd'] or 'show_masic' in input['cmd'] or 'config_masic' in input['cmd']: + exit_code, output = get_result_and_return_code(["ecnconfig"] + input['args']) + else: + exec_cmd = config.config.commands["ecn"] + result = runner.invoke(exec_cmd, input['args']) + exit_code = result.exit_code + output = result.output + + print(exit_code) + print(output) + + if input['rc'] == 0: + assert exit_code == 0 + else: + assert exit_code != 0 + + if 'cmp_args' in input: + fd = open('/tmp/ecnconfig', 'r') + cmp_data = json.load(fd) + + # Verify queue assignments + if 'cmp_q_args' in input: + namespaces, profile, value = self.process_cmp_args(input['cmp_args'][0]) + for namespace in namespaces: + for key in cmp_data[namespace]: + queue_idx = ast.literal_eval(key)[-1] + if queue_idx in input['cmp_q_args']: + self.verify_profile(cmp_data[namespace][key], profile, value) + + # other_q helps verify two different queue assignments + if 'other_q' in input: + namespaces1, profile1, value1 = self.process_cmp_args(input['cmp_args'][-1]) + for namespace1 in namespaces1: + for key in cmp_data[namespace1]: + queue_idx = ast.literal_eval(key)[-1] + if 'other_q' in input and queue_idx in input['other_q']: + self.verify_profile(cmp_data[namespace1][key], profile1, value1) + # Verify non-queue related assignments + else: + for args in input['cmp_args']: + namespaces, profile, name, value = self.process_cmp_args(args) + for namespace in namespaces: + assert(cmp_data[namespace][profile][name] == value) + fd.close() + + if 'rc_msg' in input: + assert input['rc_msg'] in output + + if 'rc_output' in input: + assert output == input['rc_output'] + + @classmethod + def teardown_class(cls): + os.environ['PATH'] = os.pathsep.join(os.environ['PATH'].split(os.pathsep)[:-1]) + os.environ['UTILITIES_UNIT_TESTING'] = "0" + + if os.path.isfile('/tmp/ecnconfig'): + os.remove('/tmp/ecnconfig') + print("TEARDOWN") + + +class TestEcnConfig(TestEcnConfigBase): def test_ecn_show_config(self): self.executor(testData['ecn_show_config']) @@ -123,77 +221,3 @@ def test_ecn_queue_set_all_on_verbose(self): def test_ecn_queue_set_lossy_q_on(self): self.executor(testData['ecn_cfg_lossy_q_on']) - - def process_cmp_args(self, cmp_args): - if cmp_args is None: - return (None, None) - return cmp_args.split(',') - - def verify_profile(self, queue_db_entry, profile, value): - if profile != None: - assert queue_db_entry[profile] == value - else: - assert profile not in queue_db_entry,\ - "Profile needs to be fully removed from table to propagate NULL OID to SAI" - - def executor(self, input): - runner = CliRunner() - - if 'db_table' in input: - db = Db() - data_list = list(db.cfgdb.get_table(input['db_table'])) - input['rc_msg'] = input['rc_msg'].format(",".join(data_list)) - - if 'show' in input['cmd']: - exec_cmd = show.cli.commands["ecn"] - result = runner.invoke(exec_cmd, input['args']) - exit_code = result.exit_code - output = result.output - elif 'q_cmd' in input['cmd'] : - exit_code, output = get_result_and_return_code(["ecnconfig"] + input['args']) - else: - exec_cmd = config.config.commands["ecn"] - result = runner.invoke(exec_cmd, input['args']) - exit_code = result.exit_code - output = result.output - - print(exit_code) - print(output) - - if input['rc'] == 0: - assert exit_code == 0 - else: - assert exit_code != 0 - - if 'cmp_args' in input: - fd = open('/tmp/ecnconfig', 'r') - cmp_data = json.load(fd) - if 'cmp_q_args' in input: - profile, value = self.process_cmp_args(input['cmp_args'][0]) - if 'other_q' in input: - profile1, value1 = self.process_cmp_args(input['cmp_args'][-1]) - for key in cmp_data: - queue_idx = ast.literal_eval(key)[-1] - if queue_idx in input['cmp_q_args']: - self.verify_profile(cmp_data[key], profile, value) - if 'other_q' in input and queue_idx in input['other_q']: - self.verify_profile(cmp_data[key], profile1, value1) - else: - for args in input['cmp_args']: - profile, name, value = args.split(',') - assert(cmp_data[profile][name] == value) - fd.close() - - if 'rc_msg' in input: - assert input['rc_msg'] in output - - if 'rc_output' in input: - assert output == input['rc_output'] - - @classmethod - def teardown_class(cls): - os.environ['PATH'] = os.pathsep.join(os.environ['PATH'].split(os.pathsep)[:-1]) - os.environ['UTILITIES_UNIT_TESTING'] = "0" - if os.path.isfile('/tmp/ecnconfig'): - os.remove('/tmp/ecnconfig') - print("TEARDOWN") diff --git a/tests/mock_tables/asic0/config_db.json b/tests/mock_tables/asic0/config_db.json index 8b867bdc96..da38af13dd 100644 --- a/tests/mock_tables/asic0/config_db.json +++ b/tests/mock_tables/asic0/config_db.json @@ -303,5 +303,28 @@ "SYSLOG_CONFIG_FEATURE|database": { "rate_limit_interval": "222", "rate_limit_burst": "22222" + }, + "WRED_PROFILE|AZURE_LOSSLESS": { + "red_max_threshold": "2097152", + "ecn": "ecn_all", + "green_min_threshold": "1048576", + "red_min_threshold": "1048576", + "yellow_min_threshold": "1048576", + "green_max_threshold": "2097152", + "green_drop_probability": "5", + "yellow_max_threshold": "2097152", + "yellow_drop_probability": "5", + "red_drop_probability": "5" + }, + "DEVICE_NEIGHBOR|Ethernet4": { + "name": "Serverss0", + "port": "eth0" + }, + "QUEUE|Ethernet4|0": { + "scheduler": "[SCHEDULAR|scheduler.0]" + }, + "QUEUE|Ethernet4|1": { + "scheduler": "[SCHEDULAR|scheduler.0]", + "wred_profile": "AZURE_LOSSLESS" } } diff --git a/tests/mock_tables/asic1/config_db.json b/tests/mock_tables/asic1/config_db.json index 56823ae113..1bcd812ef2 100644 --- a/tests/mock_tables/asic1/config_db.json +++ b/tests/mock_tables/asic1/config_db.json @@ -242,5 +242,25 @@ "SYSLOG_CONFIG_FEATURE|database": { "rate_limit_interval": "555", "rate_limit_burst": "55555" + }, + "WRED_PROFILE|AZURE_LOSSY": { + "red_max_threshold":"32760", + "red_min_threshold":"4095", + "yellow_max_threshold":"32760", + "yellow_min_threshold":"4095", + "green_max_threshold": "32760", + "green_min_threshold": "4095", + "yellow_drop_probability": "2" + }, + "DEVICE_NEIGHBOR|Ethernet0": { + "name": "Servers", + "port": "eth0" + }, + "QUEUE|Ethernet0|0": { + "scheduler": "[SCHEDULAR|scheduler.0]", + "wred_profile": "AZURE_LOSSLESS" + }, + "QUEUE|Ethernet0|1": { + "scheduler": "[SCHEDULAR|scheduler.0]" } } diff --git a/tests/multi_asic_ecnconfig_test.py b/tests/multi_asic_ecnconfig_test.py new file mode 100644 index 0000000000..034a517ace --- /dev/null +++ b/tests/multi_asic_ecnconfig_test.py @@ -0,0 +1,64 @@ +import os +import sys +from .ecn_test import TestEcnConfigBase +from .ecn_input.ecn_test_vectors import testData + +root_path = os.path.dirname(os.path.abspath(__file__)) +modules_path = os.path.dirname(root_path) +scripts_path = os.path.join(modules_path, "scripts") +sys.path.insert(0, root_path) +sys.path.insert(0, modules_path) + + +class TestEcnConfigMultiAsic(TestEcnConfigBase): + @classmethod + def setup_class(cls): + super().setup_class() + os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = "multi_asic" + + def test_ecn_show_config_all_masic(self): + self.executor(testData['ecn_show_config_masic']) + + def test_ecn_show_config_all_verbose_masic(self): + self.executor(testData['test_ecn_show_config_verbose_masic']) + + def test_ecn_show_config_one_masic(self): + self.executor(testData['test_ecn_show_config_namespace']) + + def test_ecn_show_config_one_verbose_masic(self): + self.executor(testData['test_ecn_show_config_namespace_verbose']) + + def test_ecn_config_change_other_threshold_masic(self): + self.executor(testData['ecn_cfg_threshold_masic']) + + def test_ecn_config_change_other_prob_masic(self): + self.executor(testData['ecn_cfg_probability_masic']) + + def test_ecn_config_change_gdrop_verbose_all_masic(self): + self.executor(testData['ecn_cfg_gdrop_verbose_all_masic']) + + def test_ecn_config_multi_set_verbose_all_masic(self): + self.executor(testData['ecn_cfg_multi_set_verbose_all_masic']) + + def test_ecn_queue_get_masic(self): + self.executor(testData['ecn_q_get_masic']) + + def test_ecn_queue_get_verbose_masic(self): + self.executor(testData['ecn_q_get_verbose_masic']) + + def test_ecn_queue_get_all_masic(self): + self.executor(testData['ecn_q_get_all_ns_masic']) + + def test_ecn_queue_get_all_verbose_masic(self): + self.executor(testData['ecn_q_get_all_ns_verbose_masic']) + + def test_ecn_q_set_off_all_masic(self): + self.executor(testData['ecn_cfg_q_all_ns_off_masic']) + + def test_ecn_q_set_off_one_masic(self): + self.executor(testData['ecn_cfg_q_one_ns_off_verbose_masic']) + + @classmethod + def teardown_class(cls): + super().teardown_class() + os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = ""