diff --git a/config/main.py b/config/main.py index 1639dd6fc0..70632720cc 100644 --- a/config/main.py +++ b/config/main.py @@ -7660,6 +7660,7 @@ def ecn(profile, rmax, rmin, ymax, ymin, gmax, gmin, rdrop, ydrop, gdrop, verbos @click.option('-p', metavar='', type=str, required=True, help="Profile name") @click.option('-a', metavar='', type=click.IntRange(-8,8), help="Set alpha for profile type dynamic") @click.option('-s', metavar='', type=click.IntRange(min=0), help="Set staticth for profile type static") +@click.option('-t', type=click.Choice(["on", "off"]), help="Set packet trimming eligibility") @click.option('--verbose', '-vv', is_flag=True, help="Enable verbose output") @click.option('--namespace', '-n', @@ -7669,12 +7670,14 @@ def ecn(profile, rmax, rmin, ymax, ymin, gmax, gmin, rdrop, ydrop, gdrop, verbos show_default=True, help='Namespace name or all', callback=multi_asic_util.multi_asic_namespace_validation_callback) -def mmu(p, a, s, namespace, verbose): +def mmu(p, a, s, t, namespace, verbose): """mmuconfig configuration tasks""" log.log_info("'mmuconfig -p {}' executing...".format(p)) command = ['mmuconfig', '-p', str(p)] if a is not None: command += ['-a', str(a)] if s is not None: command += ['-s', str(s)] + if t is not None: + command += ['-t', str(t)] if namespace is not None: command += ['-n', str(namespace)] if verbose: diff --git a/config/plugins/sonic-trimming.py b/config/plugins/sonic-trimming.py new file mode 100644 index 0000000000..91e029653e --- /dev/null +++ b/config/plugins/sonic-trimming.py @@ -0,0 +1,228 @@ +""" +This CLI plugin was auto-generated by using 'sonic-cli-gen' utility +""" + +import click +import utilities_common.cli as clicommon + +from sonic_py_common import logger +from utilities_common.switch_trimming import ( + CFG_SWITCH_TRIMMING, + STATE_SWITCH_CAPABILITY, + STATE_CAP_TRIMMING_CAPABLE_KEY, + STATE_CAP_QUEUE_MODE_KEY, + STATE_CAP_QUEUE_MODE_DYNAMIC, + STATE_CAP_QUEUE_MODE_STATIC, + CFG_TRIM_QUEUE_INDEX_DYNAMIC, + CFG_TRIM_KEY, + STATE_CAP_KEY, + UINT32_MAX, + UINT8_MAX, + SYSLOG_IDENTIFIER, + get_db, + to_str, +) + + +log = logger.Logger(SYSLOG_IDENTIFIER) +log.set_min_log_priority_info() + + +# +# Validators ---------------------------------------------------------------------------------------------------------- +# + + +class SizeTypeValidator(click.ParamType): + """ Size option validator """ + name = "integer" + + def convert(self, value, param, ctx): + click.IntRange(0, UINT32_MAX).convert(value, param, ctx) + return value + + +class DscpTypeValidator(click.ParamType): + """ Dscp option validator """ + name = "integer" + + def convert(self, value, param, ctx): + click.IntRange(0, UINT8_MAX).convert(value, param, ctx) + return value + + +class QueueTypeValidator(click.ParamType): + """ Queue index option validator """ + name = "text" + + def get_metavar(self, param): + db = get_db(click.get_current_context()) + + entry = db.get_all(db.STATE_DB, "{}|{}".format(STATE_SWITCH_CAPABILITY, STATE_CAP_KEY)) + entry.setdefault(STATE_CAP_QUEUE_MODE_KEY, "N/A") + + cap_list = entry[STATE_CAP_QUEUE_MODE_KEY].split(',') + + if cap_list.count(STATE_CAP_QUEUE_MODE_DYNAMIC) == len(cap_list): + return "dynamic" + elif cap_list.count(STATE_CAP_QUEUE_MODE_STATIC) == len(cap_list): + return "INTEGER" + + return "[INTEGER|dynamic]" + + def convert(self, value, param, ctx): + db = get_db(ctx) + + entry = db.get_all(db.STATE_DB, "{}|{}".format(STATE_SWITCH_CAPABILITY, STATE_CAP_KEY)) + + entry.setdefault(STATE_CAP_TRIMMING_CAPABLE_KEY, "false") + entry.setdefault(STATE_CAP_QUEUE_MODE_KEY, "N/A") + + if entry[STATE_CAP_TRIMMING_CAPABLE_KEY] == "false": + raise click.UsageError("Failed to configure {}: operation is not supported".format( + param.get_error_hint(ctx)), ctx + ) + + if not entry[STATE_CAP_QUEUE_MODE_KEY]: + raise click.UsageError("Failed to configure {}: no queue resolution mode capabilities".format( + param.get_error_hint(ctx)), ctx + ) + + verify_cap = True + + if entry[STATE_CAP_QUEUE_MODE_KEY] == "N/A": + verify_cap = False + + cap_list = entry[STATE_CAP_QUEUE_MODE_KEY].split(',') + + if value == CFG_TRIM_QUEUE_INDEX_DYNAMIC: + if verify_cap and (STATE_CAP_QUEUE_MODE_DYNAMIC not in cap_list): + self.fail("dynamic queue resolution mode is not supported", param, ctx) + else: + if verify_cap and (STATE_CAP_QUEUE_MODE_STATIC not in cap_list): + self.fail("static queue resolution mode is not supported", param, ctx) + + click.IntRange(0, UINT8_MAX).convert(value, param, ctx) + + return value + + +# +# DB interface -------------------------------------------------------------------------------------------------------- +# + + +def update_entry_validated(db, table, key, data, create_if_not_exists=False): + """ Update entry in table and validate configuration. + If attribute value in data is None, the attribute is deleted. + + Args: + db (swsscommon.ConfigDBConnector): Config DB connector object. + table (str): Table name to add new entry to. + key (Union[str, Tuple]): Key name in the table. + data (Dict): Entry data. + create_if_not_exists (bool): + In case entry does not exists already a new entry + is not created if this flag is set to False and + creates a new entry if flag is set to True. + Raises: + Exception: when cfg does not satisfy YANG schema. + """ + + cfg = db.get_config() + cfg.setdefault(table, {}) + + if not data: + raise click.ClickException(f"No field/values to update {key}") + + if create_if_not_exists: + cfg[table].setdefault(key, {}) + + if key not in cfg[table]: + raise click.ClickException(f"{key} does not exist") + + entry_changed = False + for attr, value in data.items(): + if value == cfg[table][key].get(attr): + continue + if value is not None: + cfg[table][key][attr] = value + entry_changed = True + + if not entry_changed: + return + + db.set_entry(table, key, cfg[table][key]) + + +# +# CLI ----------------------------------------------------------------------------------------------------------------- +# + + +@click.group( + name="switch-trimming", + cls=clicommon.AliasedGroup +) +def SWITCH_TRIMMING(): + """ Configure switch trimming feature """ + + pass + + +@SWITCH_TRIMMING.command( + name="global" +) +@click.option( + "-s", "--size", "size", + help="Configures size (in bytes) to trim eligible packet", + type=SizeTypeValidator(), +) +@click.option( + "-d", "--dscp", "dscp", + help="Configures DSCP value assigned to a packet after trimming", + type=DscpTypeValidator(), +) +@click.option( + "-q", "--queue", "queue", + help="Configures queue index to use for transmission of a packet after trimming", + type=QueueTypeValidator(), +) +@clicommon.pass_db +@click.pass_context +def SWITCH_TRIMMING_GLOBAL(ctx, db, size, dscp, queue): + """ Configure switch trimming global """ + + if not (size or dscp or queue): + raise click.UsageError("Failed to configure switch trimming global: no options are provided", ctx) + + table = CFG_SWITCH_TRIMMING + key = CFG_TRIM_KEY + + data = { + "size": size, + "dscp_value": dscp, + "queue_index": queue, + } + + try: + update_entry_validated(db.cfgdb, table, key, data, create_if_not_exists=True) + log.log_notice("Configured switch trimming global: {}".format(to_str(data))) + except Exception as e: + log.log_error("Failed to configure switch trimming global: {}".format(str(e))) + ctx.fail(str(e)) + + +def register(cli): + """ Register new CLI nodes in root CLI. + + Args: + cli: Root CLI node. + Raises: + Exception: when root CLI already has a command + we are trying to register. + """ + cli_node = SWITCH_TRIMMING + if cli_node.name in cli.commands: + raise Exception(f"{cli_node.name} already exists in CLI") + cli.add_command(SWITCH_TRIMMING) diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index e0613f1ff8..3e7275aa9d 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -129,6 +129,9 @@ * [Muxcable](#muxcable) * [Muxcable Show commands](#muxcable-show-commands) * [Muxcable Config commands](#muxcable-config-commands) +* [MMU](#mmu) + * [MMU Show commands](#mmu-show-commands) + * [NAT Config commands](#mmu-config-commands) * [NAT](#nat) * [NAT Show commands](#nat-show-commands) * [NAT Config commands](#nat-config-commands) @@ -154,6 +157,9 @@ * [PortChannels](#portchannels) * [PortChannel Show commands](#portchannel-show-commands) * [PortChannel Config commands](#portchannel-config-commands) +* [Packet Trimming](#packet-trimming) + * [Packet Trimming Show commands](#packet-trimming-show-commands) + * [Packet Trimming Config commands](#packet-trimming-config-commands) * [QoS](#qos) * [QoS Show commands](#qos-show-commands) * [PFC](#pfc) @@ -5051,6 +5057,7 @@ Optional argument "-p" specify a period (in seconds) with which to gather counte show interfaces counters fec-histogram [-i ] show interfaces counters fec-stats show interfaces counters detailed + show interfaces counters trim [interface_name] [-p|--period ] [-j|--json] ``` - Example: @@ -5258,6 +5265,17 @@ The "fec-stats" subcommand is used to disply the interface fec related statistic Ethernet16 U 0 0 0 1.77e-20 0.00e+00 ``` +The "trim" subcommand is used to display the interface packet trimming related statistic. + +- Example: + ``` + admin@sonic:~$ show interfaces counters trim + IFACE STATE TRIM_PKTS + ---------- ------- ----------- + Ethernet0 U 0 + Ethernet8 U 100 + Ethernet16 U 200 + ``` **show interfaces description** @@ -8088,6 +8106,140 @@ While adding a new SPAN session, users need to configure the following fields th Go Back To [Beginning of the document](#) or [Beginning of this section](#mirroring) +## MMU + +### MMU Show commands + +This subsection explains how to display switch Memory Management Unit (MMU) configuration. + +**show mmu** + +This command displays MMU configuration. + +- Usage: + ```bash + show mmu [OPTIONS] + ``` + +- Options: + - _-n,--namespace_: namespace name or all + - _-vv,--verbose_: enable verbose output + +- Example: + ```bash + admin@sonic:~$ show mmu + Pool: ingress_lossless_pool + ---- -------- + xoff 4194112 + type ingress + mode dynamic + size 10875072 + ---- -------- + + Pool: egress_lossless_pool + ---- -------- + type egress + mode static + size 15982720 + ---- -------- + + Pool: egress_lossy_pool + ---- ------- + type egress + mode dynamic + size 9243812 + ---- ------- + + Profile: egress_lossy_profile + ---------- ------------------------------- + dynamic_th 3 + pool [BUFFER_POOL|egress_lossy_pool] + size 1518 + ---------- ------------------------------- + + Profile: pg_lossless_100000_300m_profile + ---------- ----------------------------------- + xon_offset 2288 + dynamic_th -3 + xon 2288 + xoff 268736 + pool [BUFFER_POOL|ingress_lossless_pool] + size 1248 + ---------- ----------------------------------- + + Profile: egress_lossless_profile + --------- ---------------------------------- + static_th 3995680 + pool [BUFFER_POOL|egress_lossless_pool] + size 1518 + --------- ---------------------------------- + + Profile: pg_lossless_100000_40m_profile + ---------- ----------------------------------- + xon_offset 2288 + dynamic_th -3 + xon 2288 + xoff 177632 + pool [BUFFER_POOL|ingress_lossless_pool] + size 1248 + ---------- ----------------------------------- + + Profile: ingress_lossy_profile + ---------- ----------------------------------- + dynamic_th 3 + pool [BUFFER_POOL|ingress_lossless_pool] + size 0 + ---------- ----------------------------------- + + Profile: pg_lossless_40000_40m_profile + ---------- ----------------------------------- + xon_offset 2288 + dynamic_th -3 + xon 2288 + xoff 71552 + pool [BUFFER_POOL|ingress_lossless_pool] + size 1248 + ---------- ----------------------------------- + + Profile: q_lossy_profile + --------------------- ----------------- + packet_discard_action drop + dynamic_th 0 + pool egress_lossy_pool + size 0 + --------------------- ----------------- + ``` + +### MMU Config commands + +This subsection explains how to configure switch Memory Management Unit (MMU). + +**config mmu** + +This command is used to manage switch MMU configuration. + +- Usage: + ```bash + config mmu [OPTIONS] + ``` + +- Options: + - _-p_: profile name + - _-a_: set alpha for profile type dynamic + - _-s_: set staticth for profile type static + - _-t_: set packet trimming eligibility + - _-n,--namespace_: namespace name or all + - _-vv,--verbose_: enable verbose output + +- Examples: + ```bash + config mmu -p alpha_profile -a 2 + config mmu -p ingress_lossless_profile -s 12121215 + config mmu -p q_lossy_profile -t on + ``` + +Go Back To [Beginning of the document](#) or [Beginning of this section](#mmu) + ## NAT ### NAT Show commands @@ -8987,6 +9139,82 @@ This command adds or deletes a member port to/from the already created portchann Go Back To [Beginning of the document](#) or [Beginning of this section](#portchannels) +# Packet Trimming + +This section explains the various show commands and configuration commands available for users. + +### Packet Trimming Show commands + +This subsection explains how to display switch trimming configuration. + +**show switch-trimming global** + +This command displays switch trimming global configuration. + +- Usage: + ```bash + show switch-trimming global [OPTIONS] + ``` + +- Options: + - _-j,--json_: display in JSON format + +- Example: + ```bash + admin@sonic:~$ show switch-trimming global + +-----------------------------+---------+ + | Configuration | Value | + +=============================+=========+ + | Packet trimming size | 200 | + +-----------------------------+---------+ + | Packet trimming DSCP value | 20 | + +-----------------------------+---------+ + | Packet trimming queue index | 2 | + +-----------------------------+---------+ + + admin@sonic:~$ show switch-trimming global --json + { + "size": "200", + "dscp_value": "20", + "queue_index": "2" + } + ``` + +### Packet Trimming Config commands + +This subsection explains how to configure switch trimming. + +**config switch-trimming global** + +This command is used to manage switch trimming global configuration. + +- Usage: + ```bash + config switch-trimming global [OPTIONS] + ``` + +- Options: + - _-s,--size_: size (in bytes) to trim eligible packet + - _-d,--dscp_: dscp value assigned to a packet after trimming + - _-q,--queue_: queue index to use for transmission of a packet after trimming + +- Examples: + ```bash + admin@sonic:~$ config switch-trimming global \ + --size '128' \ + --dscp '48' \ + --queue '6' + ``` + +- Note: + - At least one option must be provided + - When `--queue` value is set to `dynamic`, the `--dscp` value is used for mapping to the queue + ```bash + admin@sonic:~$ config switch-trimming global --queue dynamic + ``` + +Go Back To [Beginning of the document](#) or [Beginning of this section](#packet-trimming) + ## NVGRE This section explains the various show commands and configuration commands available for users. @@ -9551,9 +9779,19 @@ This command can be used to clear the counters for all queues of all ports. Note - Usage: ``` - show queue counters [] + show queue counters [OPTIONS] [interface_name] ``` +- Parameters: + - _interface_name_: display counters for interface name only + +- Options: + - _-a,--all_: display all counters + - _-T,--trim_: display trimming counters only + - _-V,--voq_: display VOQ counters only + - _-nz,--nonzero_: display non zero counters + - _-j,--json_: display counters in JSON format + - Example: ``` admin@sonic:~$ show queue counters @@ -9604,6 +9842,30 @@ This command can be used to clear the counters for all queues of all ports. Note Ethernet4 MC9 0 0 0 0 ... + + admin@sonic:~$ show queue counters --trim + Port TxQ Trim/pkts + --------- ----- ----------- + Ethernet0 UC0 0 + Ethernet0 UC1 0 + Ethernet0 UC2 0 + Ethernet0 UC3 0 + Ethernet0 UC4 0 + Ethernet0 UC5 0 + Ethernet0 UC6 0 + Ethernet0 UC7 0 + Ethernet0 UC8 0 + Ethernet0 UC9 0 + Ethernet0 MC0 N/A + Ethernet0 MC1 N/A + Ethernet0 MC2 N/A + Ethernet0 MC3 N/A + Ethernet0 MC4 N/A + Ethernet0 MC5 N/A + Ethernet0 MC6 N/A + Ethernet0 MC7 N/A + Ethernet0 MC8 N/A + Ethernet0 MC9 N/A ``` Optionally, you can specify an interface name in order to display only that particular interface @@ -11362,92 +11624,6 @@ This command displays the system-wide memory utilization information – just a Swap: 0B 0B 0B ``` -**show mmu** - -This command displays virtual address to the physical address translation status of the Memory Management Unit (MMU). - -- Usage: - ``` - show mmu - ``` - -- Example: - ``` - admin@sonic:~$ show mmu - Pool: ingress_lossless_pool - ---- -------- - xoff 4194112 - type ingress - mode dynamic - size 10875072 - ---- -------- - - Pool: egress_lossless_pool - ---- -------- - type egress - mode static - size 15982720 - ---- -------- - - Pool: egress_lossy_pool - ---- ------- - type egress - mode dynamic - size 9243812 - ---- ------- - - Profile: egress_lossy_profile - ---------- ------------------------------- - dynamic_th 3 - pool [BUFFER_POOL|egress_lossy_pool] - size 1518 - ---------- ------------------------------- - - Profile: pg_lossless_100000_300m_profile - ---------- ----------------------------------- - xon_offset 2288 - dynamic_th -3 - xon 2288 - xoff 268736 - pool [BUFFER_POOL|ingress_lossless_pool] - size 1248 - ---------- ----------------------------------- - - Profile: egress_lossless_profile - --------- ---------------------------------- - static_th 3995680 - pool [BUFFER_POOL|egress_lossless_pool] - size 1518 - --------- ---------------------------------- - - Profile: pg_lossless_100000_40m_profile - ---------- ----------------------------------- - xon_offset 2288 - dynamic_th -3 - xon 2288 - xoff 177632 - pool [BUFFER_POOL|ingress_lossless_pool] - size 1248 - ---------- ----------------------------------- - - Profile: ingress_lossy_profile - ---------- ----------------------------------- - dynamic_th 3 - pool [BUFFER_POOL|ingress_lossless_pool] - size 0 - ---------- ----------------------------------- - - Profile: pg_lossless_40000_40m_profile - ---------- ----------------------------------- - xon_offset 2288 - dynamic_th -3 - xon 2288 - xoff 71552 - pool [BUFFER_POOL|ingress_lossless_pool] - size 1248 - ---------- ----------------------------------- - ``` - Go Back To [Beginning of the document](#) or [Beginning of this section](#System-State) ### System-Health diff --git a/scripts/mmuconfig b/scripts/mmuconfig index 3986f3ba1b..17101ba6fd 100755 --- a/scripts/mmuconfig +++ b/scripts/mmuconfig @@ -35,10 +35,9 @@ DYNAMIC_THRESHOLD_MIN = -8 DYNAMIC_THRESHOLD_MAX = 8 STATIC_THRESHOLD = "static_th" STATIC_THRESHOLD_MIN = 0 -BUFFER_PROFILE_FIELDS = { - "alpha": DYNAMIC_THRESHOLD, - "staticth" : STATIC_THRESHOLD -} +PACKET_TRIMMING = "packet_discard_action" +PACKET_TRIMMING_ENABLED = "on" +PACKET_TRIMMING_DISABLED = "off" # mock the redis for unit test purposes # try: @@ -129,12 +128,11 @@ class MmuConfig(object): print(f"No buffer profile information available{namespace_str}") @multi_asic_util.run_on_multi_asic - def set(self, profile, field_alias, value): + def set(self, profile, field, value): namespace_str = f" for namespace {self.multi_asic.current_namespace}" if multi_asic.is_multi_asic() else '' if os.geteuid() != 0 and os.environ.get("UTILITIES_UNIT_TESTING", "0") != "2": sys.exit("Root privileges required for this operation") - field = BUFFER_PROFILE_FIELDS[field_alias] buf_profs = self.config_db.get_table(BUFFER_PROFILE_TABLE_NAME) if field == DYNAMIC_THRESHOLD: if profile in buf_profs and DYNAMIC_THRESHOLD not in buf_profs[profile]: @@ -142,6 +140,9 @@ class MmuConfig(object): elif field == STATIC_THRESHOLD: if profile in buf_profs and STATIC_THRESHOLD not in buf_profs[profile]: sys.exit("%s not using static threshold" % (profile)) + elif field == PACKET_TRIMMING: + # Packet trimming eligibility configuration works for both dynamic/static buffer model + pass else: sys.exit("Set field %s not supported" % (field)) @@ -153,15 +154,30 @@ class MmuConfig(object): with open(self.filename, "w") as fd: json.dump(self.updated_profile_table, fd) + +class TrimTypeValidator(click.ParamType): + """ Trim option validator """ + name = "text" + + def get_metavar(self, param): + return "[on|off]" + + def convert(self, value, param, ctx): + click.Choice([PACKET_TRIMMING_ENABLED, PACKET_TRIMMING_DISABLED]).convert(value, param, ctx) + + return "trim" if value == PACKET_TRIMMING_ENABLED else "drop" + + @click.command(help='Show and change: mmu configuration') @click.option('-l', '--list', 'show_config', is_flag=True, help='show mmu configuration') @click.option('-p', '--profile', type=str, help='specify buffer profile name', default=None) @click.option('-a', '--alpha', type=click.IntRange(DYNAMIC_THRESHOLD_MIN, DYNAMIC_THRESHOLD_MAX), help='set n for dyanmic threshold alpha 2^(n)', default=None) @click.option('-s', '--staticth', type=click.IntRange(min=STATIC_THRESHOLD_MIN), help='set static threshold', default=None) +@click.option('-t', '--trim', 'trim', type=TrimTypeValidator(), help='Configures packet trimming eligibility') @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.version_option(version='1.0') -def main(show_config, profile, alpha, staticth, namespace, verbose): +def main(show_config, profile, alpha, staticth, trim, namespace, verbose): # A test file created for unit test purposes filename=None if os.environ.get("UTILITIES_UNIT_TESTING", "0") == "2": @@ -181,9 +197,11 @@ def main(show_config, profile, alpha, staticth, namespace, verbose): # Buffershow cannot modify profiles elif config and profile: if alpha: - mmu_cfg.set(profile, "alpha", alpha) + mmu_cfg.set(profile, DYNAMIC_THRESHOLD, alpha) elif staticth: - mmu_cfg.set(profile, "staticth", staticth) + mmu_cfg.set(profile, STATIC_THRESHOLD, staticth) + elif trim: + mmu_cfg.set(profile, PACKET_TRIMMING, trim) else: ctx = click.get_current_context() click.echo(ctx.get_help()) diff --git a/scripts/portstat b/scripts/portstat index f006cb6b1c..58a2d678d1 100755 --- a/scripts/portstat +++ b/scripts/portstat @@ -15,14 +15,14 @@ from collections import OrderedDict # mock the redis for unit test purposes # try: - if os.environ["UTILITIES_UNIT_TESTING"] == "2": + if os.environ.get("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_IS_SUP"] == "1": + if os.environ.get("UTILITIES_UNIT_TESTING_IS_SUP", "") == "1": import mock import sonic_py_common from swsscommon.swsscommon import SonicV2Connector @@ -33,7 +33,7 @@ try: else: sonic_py_common.device_info.is_voq_chassis = mock.MagicMock(return_value=True) SonicV2Connector.delete_all_by_pattern = mock.MagicMock() - if os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] == "multi_asic": + if os.environ.get("UTILITIES_UNIT_TESTING_TOPOLOGY", "") == "multi_asic": import mock_tables.mock_multi_asic mock_tables.dbconnector.load_namespace_config() @@ -73,6 +73,7 @@ Examples: parser.add_argument('-j', '--json', action='store_true', help='Display in JSON format') parser.add_argument('-r', '--raw', action='store_true', help='Raw stats (unmodified output of netstat)') parser.add_argument('-R', '--rate', action='store_true', help='Display interface rates') + parser.add_argument('-T', '--trim', action='store_true', help='Display trimming related statistics') parser.add_argument('-t', '--tag', type=str, help='Save stats with name TAG', default=None) parser.add_argument('-p', '--period', type=int, help='Display stats over a specified period (in seconds).', default=0) parser.add_argument('-i', '--interface', type=str, help='Display stats for interface lists.', default=None) @@ -90,6 +91,7 @@ Examples: rates_only = args.rate use_json = args.json raw_stats = args.raw + trim_stats_only = args.trim tag_name = args.tag wait_time_in_seconds = args.period print_all = args.all @@ -123,7 +125,7 @@ Examples: # Now decide what information to display if raw_stats: - portstat.cnstat_print(cnstat_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, fec_stats_only, rates_only) + portstat.cnstat_print(cnstat_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, fec_stats_only, rates_only, trim_stats_only) sys.exit(0) if save_fresh_stats: @@ -142,7 +144,7 @@ Examples: cnstat_cached_dict = json.load(open(cnstat_fqn_file, 'r')) if not detail: print("Last cached time was " + str(cnstat_cached_dict.get('time'))) - portstat.cnstat_diff_print(cnstat_dict, cnstat_cached_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, fec_stats_only, rates_only, detail) + portstat.cnstat_diff_print(cnstat_dict, cnstat_cached_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, fec_stats_only, rates_only, trim_stats_only, detail) except IOError as e: print(e.errno, e) else: @@ -150,13 +152,13 @@ Examples: print("\nFile '%s' does not exist" % cnstat_fqn_file) print("Did you run 'portstat -c -t %s' to record the counters via tag %s?\n" % (tag_name, tag_name)) else: - portstat.cnstat_print(cnstat_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, fec_stats_only, rates_only, detail) + portstat.cnstat_print(cnstat_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, fec_stats_only, rates_only, trim_stats_only, detail) else: #wait for the specified time and then gather the new stats and output the difference. time.sleep(wait_time_in_seconds) print("The rates are calculated within %s seconds period" % wait_time_in_seconds) cnstat_new_dict, ratestat_new_dict = portstat.get_cnstat_dict() - portstat.cnstat_diff_print(cnstat_new_dict, cnstat_dict, ratestat_new_dict, intf_list, use_json, print_all, errors_only, fec_stats_only, rates_only, detail) + portstat.cnstat_diff_print(cnstat_new_dict, cnstat_dict, ratestat_new_dict, intf_list, use_json, print_all, errors_only, fec_stats_only, rates_only, trim_stats_only, detail) if __name__ == "__main__": main() diff --git a/scripts/queuestat b/scripts/queuestat index befe8acc30..d18d8ee91d 100755 --- a/scripts/queuestat +++ b/scripts/queuestat @@ -37,9 +37,11 @@ from utilities_common.cli import json_serial, UserCache from utilities_common import constants import utilities_common.multi_asic as multi_asic_util -QueueStats = namedtuple("QueueStats", "queueindex, queuetype, totalpacket, totalbytes, droppacket, dropbytes") +QueueStats = namedtuple("QueueStats", "queueindex, queuetype, totalpacket, totalbytes, droppacket, dropbytes, trimpacket") VoqStats = namedtuple("VoqStats", "queueindex, queuetype, totalpacket, totalbytes, droppacket, dropbytes, creditWDpkts") -header = ['Port', 'TxQ', 'Counter/pkts', 'Counter/bytes', 'Drop/pkts', 'Drop/bytes'] +std_header = ['Port', 'TxQ', 'Counter/pkts', 'Counter/bytes', 'Drop/pkts', 'Drop/bytes'] +all_header = ['Port', 'TxQ', 'Counter/pkts', 'Counter/bytes', 'Drop/pkts', 'Drop/bytes', 'Trim/pkts'] +trim_header = ['Port', 'TxQ', 'Trim/pkts'] voq_header = ['Port', 'Voq', 'Counter/pkts', 'Counter/bytes', 'Drop/pkts', 'Drop/bytes', 'Credit-WD-Del/pkts'] counter_bucket_dict = { @@ -48,6 +50,9 @@ counter_bucket_dict = { 'SAI_QUEUE_STAT_DROPPED_PACKETS': 4, 'SAI_QUEUE_STAT_DROPPED_BYTES': 5, } +trim_counter_bucket_dict = { + 'SAI_QUEUE_STAT_TRIM_PACKETS': 6, +} voq_counter_bucket_dict = { 'SAI_QUEUE_STAT_CREDIT_WD_DELETED_PACKETS': 6 } @@ -77,7 +82,7 @@ cnstat_dir = 'N/A' cnstat_fqn_file = 'N/A' -def build_json(port, cnstat, voq=False): +def build_json(port, cnstat, all=False, trim=False, voq=False): def ports_stats(k): p = {} if voq: @@ -89,12 +94,25 @@ def build_json(port, cnstat, voq=False): "creditWDPkts": k[6] } else: - p[k[1]] = { - "totalpacket": k[2], - "totalbytes": k[3], - "droppacket": k[4], - "dropbytes": k[5] - } + if all: # All statistics + p[k[1]] = { + "totalpacket": k[2], + "totalbytes": k[3], + "droppacket": k[4], + "dropbytes": k[5], + "trimpacket": k[6], + } + elif trim: # Packet Trimming related statistics + p[k[1]] = { + "trimpacket": k[2], + } + else: # Generic statistics + p[k[1]] = { + "totalpacket": k[2], + "totalbytes": k[3], + "droppacket": k[4], + "dropbytes": k[5], + } return p out = {} @@ -104,8 +122,10 @@ def build_json(port, cnstat, voq=False): class QueuestatWrapper(object): """A wrapper to execute queuestat cmd over the correct namespaces""" - def __init__(self, namespace, voq): + def __init__(self, namespace, all, trim, voq): self.namespace = namespace + self.all = all + self.trim = trim self.voq = voq # Initialize the multi-asic namespace @@ -114,7 +134,7 @@ class QueuestatWrapper(object): @multi_asic_util.run_on_multi_asic def run(self, save_fresh_stats, port_to_show_stats, json_opt, non_zero): - queuestat = Queuestat(self.multi_asic.current_namespace, self.db, self.voq) + queuestat = Queuestat(self.multi_asic.current_namespace, self.db, self.all, self.trim, self.voq) if save_fresh_stats: queuestat.save_fresh_stats() return @@ -126,8 +146,10 @@ class QueuestatWrapper(object): class Queuestat(object): - def __init__(self, namespace, db, voq=False): + def __init__(self, namespace, db, all=False, trim=False, voq=False): self.db = db + self.all = all + self.trim = trim self.voq = voq self.namespace = namespace self.namespace_str = f" for {namespace}" if namespace else '' @@ -205,17 +227,16 @@ class Queuestat(object): print(f"Queue Type is invalid{self.namespace_str}:", table_id, queue_type) sys.exit(1) + counter_dict = { **counter_bucket_dict } + fields = [ get_queue_index(table_id), get_queue_type(table_id) ] + if self.voq: - fields = ["0","0","0","0","0","0","0"] + counter_dict.update(voq_counter_bucket_dict) else: - fields = ["0","0","0","0","0","0"] - fields[0] = get_queue_index(table_id) - fields[1] = get_queue_type(table_id) + counter_dict.update(trim_counter_bucket_dict) - counter_dict = {} - counter_dict.update(counter_bucket_dict) - if self.voq: - counter_dict.update(voq_counter_bucket_dict) + # Layout is per QueueStats/VoqStats type definition + fields.extend(["0"]*len(counter_dict)) for counter_name, pos in counter_dict.items(): full_table_id = COUNTER_TABLE_PREFIX + table_id @@ -231,7 +252,7 @@ class Queuestat(object): cntr = QueueStats._make(fields)._asdict() return cntr - # Build a dictionary of the stats + # Build a dictionary of the stats cnstat_dict = OrderedDict() cnstat_dict['time'] = datetime.datetime.now() if queue_map is None: @@ -260,17 +281,49 @@ class Queuestat(object): data['totalpacket'], data['totalbytes'], data['droppacket'], data['dropbytes'], data['creditWDpkts'])) else: - if not non_zero or data['totalpacket'] != '0' or data['totalbytes'] != '0' or \ - data['droppacket'] != '0' or data['dropbytes'] != '0': - table.append((port, data['queuetype'] + str(data['queueindex']), - data['totalpacket'], data['totalbytes'], - data['droppacket'], data['dropbytes'])) + queuetag = data['queuetype'] + str(data['queueindex']) + + if self.all: # All statistics + if not non_zero or \ + data['totalpacket'] != '0' or data['totalbytes'] != '0' or \ + data['droppacket'] != '0' or data['dropbytes'] != '0' or \ + data['trimpacket'] != '0': + table.append(( + port, queuetag, + data['totalpacket'], data['totalbytes'], + data['droppacket'], data['dropbytes'], + data['trimpacket'] + )) + elif self.trim: # Packet Trimming related statistics + if not non_zero or \ + data['trimpacket'] != '0': + table.append(( + port, queuetag, + data['trimpacket'] + )) + else: # Generic statistics + if not non_zero or \ + data['totalpacket'] != '0' or data['totalbytes'] != '0' or \ + data['droppacket'] != '0' or data['dropbytes'] != '0': + table.append(( + port, queuetag, + data['totalpacket'], data['totalbytes'], + data['droppacket'], data['dropbytes'] + )) if json_opt: - json_output[port].update(build_json(port, table, self.voq)) + json_output[port].update(build_json(port, table, self.all, self.trim, self.voq)) return json_output else: - hdr = voq_header if self.voq else header + if self.voq: + hdr = voq_header + elif self.all: + hdr = all_header + elif self.trim: + hdr = trim_header + else: + hdr = std_header + if table: print(f"For namespace {self.namespace}:") print(tabulate(table, hdr, tablefmt='simple', stralign='right')) @@ -306,20 +359,61 @@ class Queuestat(object): ns_diff(cntr['dropbytes'], old_cntr['dropbytes']), ns_diff(cntr['creditWDpkts'], old_cntr['creditWDpkts']))) else: - if not non_zero or ns_diff(cntr['totalpacket'], old_cntr['totalpacket']) != '0' or \ - ns_diff(cntr['totalbytes'], old_cntr['totalbytes']) != '0' or \ - ns_diff(cntr['droppacket'], old_cntr['droppacket']) != '0' or \ - ns_diff(cntr['dropbytes'], old_cntr['dropbytes']) != '0': - table.append((port, cntr['queuetype'] + str(cntr['queueindex']), - ns_diff(cntr['totalpacket'], old_cntr['totalpacket']), - ns_diff(cntr['totalbytes'], old_cntr['totalbytes']), - ns_diff(cntr['droppacket'], old_cntr['droppacket']), - ns_diff(cntr['dropbytes'], old_cntr['dropbytes']))) + queuetag = cntr['queuetype'] + str(cntr['queueindex']) + + if self.all: # All statistics + totalpacket = ns_diff(cntr['totalpacket'], old_cntr['totalpacket']) + totalbytes = ns_diff(cntr['totalbytes'], old_cntr['totalbytes']) + droppacket = ns_diff(cntr['droppacket'], old_cntr['droppacket']) + dropbytes = ns_diff(cntr['dropbytes'], old_cntr['dropbytes']) + trimpacket = ns_diff(cntr['trimpacket'], old_cntr['trimpacket']) + + if not non_zero or \ + totalpacket != '0' or totalbytes != '0' or \ + droppacket != '0' or dropbytes != '0' or \ + trimpacket != '0': + table.append(( + port, queuetag, + totalpacket, totalbytes, + droppacket, dropbytes, + trimpacket + )) + elif self.trim: # Packet Trimming related statistics + trimpacket = ns_diff(cntr['trimpacket'], old_cntr['trimpacket']) + + if not non_zero or \ + trimpacket != '0': + table.append(( + port, queuetag, + trimpacket + )) + else: # Generic statistics + totalpacket = ns_diff(cntr['totalpacket'], old_cntr['totalpacket']) + totalbytes = ns_diff(cntr['totalbytes'], old_cntr['totalbytes']) + droppacket = ns_diff(cntr['droppacket'], old_cntr['droppacket']) + dropbytes = ns_diff(cntr['dropbytes'], old_cntr['dropbytes']) + + if not non_zero or \ + totalpacket != '0' or totalbytes != '0' or \ + droppacket != '0' or dropbytes != '0': + table.append(( + port, queuetag, + totalpacket, totalbytes, + droppacket, dropbytes + )) if json_opt: - json_output[port].update(build_json(port, table, self.voq)) + json_output[port].update(build_json(port, table, self.all, self.trim, self.voq)) return json_output else: - hdr = voq_header if self.voq else header + if self.voq: + hdr = voq_header + elif self.all: + hdr = all_header + elif self.trim: + hdr = trim_header + else: + hdr = std_header + if table: print(port + f" Last cached time{self.namespace_str} was " + str(cnstat_old_dict.get('time'))) print(tabulate(table, hdr, tablefmt='simple', stralign='right')) @@ -416,11 +510,13 @@ class Queuestat(object): @click.option('-c', '--clear', is_flag=True, default=False, help='Clear previous stats and save new ones') @click.option('-d', '--delete', is_flag=True, default=False, help='Delete saved stats') @click.option('-j', '--json_opt', is_flag=True, default=False, help='Print in JSON format') +@click.option('-a', '--all', is_flag=True, default=False, help='Display all the stats counters') +@click.option('-T', '--trim', is_flag=True, default=False, help='Display trimming related statistics') @click.option('-V', '--voq', is_flag=True, default=False, help='display voq stats') @click.option('-nz','--non_zero', is_flag=True, default=False, help='Display non-zero queue counters') @click.option('-n', '--namespace', type=click.Choice(multi_asic.get_namespace_list()), help='Display queuecounters for a specific namespace name or skip for all', default=None) @click.version_option(version='1.0') -def main(port, clear, delete, json_opt, voq, non_zero, namespace): +def main(port, clear, delete, json_opt, all, trim, voq, non_zero, namespace): """ Examples: queuestat @@ -446,7 +542,7 @@ def main(port, clear, delete, json_opt, voq, non_zero, namespace): if delete_stats: cache.remove() - queuestat_wrapper = QueuestatWrapper(namespace, voq) + queuestat_wrapper = QueuestatWrapper(namespace, all, trim, voq) queuestat_wrapper.run(save_fresh_stats, port_to_show_stats, json_opt, non_zero) sys.exit(0) diff --git a/show/interfaces/__init__.py b/show/interfaces/__init__.py index 4b349c340c..c50720de6a 100644 --- a/show/interfaces/__init__.py +++ b/show/interfaces/__init__.py @@ -753,14 +753,16 @@ def error_status(db, interfacename, fetch_from_hardware, namespace, verbose): # # counters group ("show interfaces counters ...") # + @interfaces.group(invoke_without_command=True) -@click.option('-a', '--printall', is_flag=True) -@click.option('-p', '--period') -@click.option('-i', '--interface') @multi_asic_util.multi_asic_click_options +@click.option('-i', '--interface', help="Filter by interface name") +@click.option('-a', '--printall', is_flag=True, help="Show all counters") +@click.option('-p', '--period', type=click.INT, help="Display statistics over a specified period (in seconds)") +@click.option('-j', '--json', 'json_fmt', is_flag=True, help="Print in JSON format") @click.option('--verbose', is_flag=True, help="Enable verbose output") @click.pass_context -def counters(ctx, verbose, period, interface, printall, namespace, display): +def counters(ctx, namespace, display, interface, printall, period, json_fmt, verbose): """Show interface counters""" if ctx.invoked_subcommand is None: @@ -777,18 +779,23 @@ def counters(ctx, verbose, period, interface, printall, namespace, display): cmd += ['-s', str(display)] if namespace is not None: cmd += ['-n', str(namespace)] + if json_fmt: + cmd += ['-j'] clicommon.run_command(cmd, display_cmd=verbose) # 'errors' subcommand ("show interfaces counters errors") @counters.command() -@click.option('-p', '--period') @multi_asic_util.multi_asic_click_options +@click.option('-p', '--period', type=click.INT, help="Display statistics over a specified period (in seconds)") +@click.option('-j', '--json', 'json_fmt', is_flag=True, help="Print in JSON format") @click.option('--verbose', is_flag=True, help="Enable verbose output") -def errors(verbose, period, namespace, display): +def errors(namespace, display, period, json_fmt, verbose): # noqa: F811 """Show interface counters errors""" + cmd = ['portstat', '-e'] + if period is not None: cmd += ['-p', str(period)] @@ -796,17 +803,23 @@ def errors(verbose, period, namespace, display): if namespace is not None: cmd += ['-n', str(namespace)] + if json_fmt: + cmd += ['-j'] + clicommon.run_command(cmd, display_cmd=verbose) # 'fec-stats' subcommand ("show interfaces counters errors") @counters.command('fec-stats') -@click.option('-p', '--period') @multi_asic_util.multi_asic_click_options +@click.option('-p', '--period', type=click.INT, help="Display statistics over a specified period (in seconds)") +@click.option('-j', '--json', 'json_fmt', is_flag=True, help="Print in JSON format") @click.option('--verbose', is_flag=True, help="Enable verbose output") -def fec_stats(verbose, period, namespace, display): +def fec_stats(namespace, display, period, json_fmt, verbose): """Show interface counters fec-stats""" + cmd = ['portstat', '-f'] + if period is not None: cmd += ['-p', str(period)] @@ -814,6 +827,9 @@ def fec_stats(verbose, period, namespace, display): if namespace is not None: cmd += ['-n', str(namespace)] + if json_fmt: + cmd += ['-j'] + clicommon.run_command(cmd, display_cmd=verbose) @@ -874,49 +890,83 @@ def fec_histogram(db, interfacename, namespace, display): # 'rates' subcommand ("show interfaces counters rates") @counters.command() -@click.option('-p', '--period') @multi_asic_util.multi_asic_click_options +@click.option('-p', '--period', type=click.INT, help="Display statistics over a specified period (in seconds)") +@click.option('-j', '--json', 'json_fmt', is_flag=True, help="Print in JSON format") @click.option('--verbose', is_flag=True, help="Enable verbose output") -def rates(verbose, period, namespace, display): +def rates(namespace, display, period, json_fmt, verbose): """Show interface counters rates""" + cmd = ['portstat', '-R'] + if period is not None: cmd += ['-p', str(period)] cmd += ['-s', str(display)] if namespace is not None: cmd += ['-n', str(namespace)] + if json_fmt: + cmd += ['-j'] + clicommon.run_command(cmd, display_cmd=verbose) # 'counters' subcommand ("show interfaces counters rif") @counters.command() -@click.argument('interface', metavar='', required=False, type=str) -@click.option('-p', '--period') +@click.argument('interface', metavar='[INTERFACE_NAME]', required=False, type=str) +@click.option('-p', '--period', type=click.INT, help="Display statistics over a specified period (in seconds)") +@click.option('-j', '--json', 'json_fmt', is_flag=True, help="Print in JSON format") @click.option('--verbose', is_flag=True, help="Enable verbose output") -def rif(interface, period, verbose): - """Show interface counters""" +@click.pass_context +def rif(ctx, interface, period, json_fmt, verbose): + """Show interface counters rif""" + + cmd = ['intfstat'] - ctx = click.get_current_context() - cmd = ["intfstat"] if period is not None: cmd += ['-p', str(period)] if interface is not None: interface = try_convert_interfacename_from_alias(ctx, interface) cmd += ['-i', str(interface)] + if json_fmt: + cmd += ['-j'] + + clicommon.run_command(cmd, display_cmd=verbose) + + +# 'counters' subcommand ("show interfaces counters trim") +@counters.command() +@click.argument('interface', metavar='[INTERFACE_NAME]', required=False, type=str) +@click.option('-p', '--period', type=click.INT, help="Display statistics over a specified period (in seconds)") +@click.option('-j', '--json', 'json_fmt', is_flag=True, help="Print in JSON format") +@click.option('--verbose', is_flag=True, help="Enable verbose output") +@click.pass_context +def trim(ctx, interface, period, json_fmt, verbose): + """Show interface counters trim""" + + cmd = ['portstat', '-T'] + + if interface is not None: + interface = try_convert_interfacename_from_alias(ctx, interface) + cmd += ['-i', str(interface)] + if period is not None: + cmd += ['-p', str(period)] + if json_fmt: + cmd += ['-j'] clicommon.run_command(cmd, display_cmd=verbose) # 'counters' subcommand ("show interfaces counters detailed") @counters.command() -@click.argument('interface', metavar='', required=True, type=str) -@click.option('-p', '--period', help="Display statistics over a specified period (in seconds)") +@click.argument('interface', metavar='', required=True, type=str) +@click.option('-p', '--period', type=click.INT, help="Display statistics over a specified period (in seconds)") @click.option('--verbose', is_flag=True, help="Enable verbose output") -def detailed(interface, period, verbose): +@click.pass_context +def detailed(ctx, interface, period, verbose): """Show interface counters detailed""" - ctx = click.get_current_context() cmd = ['portstat', '-l'] + if period is not None: cmd += ['-p', str(period)] if interface is not None: diff --git a/show/main.py b/show/main.py index 3f7a1bca8b..9c0aa1b3af 100755 --- a/show/main.py +++ b/show/main.py @@ -742,15 +742,18 @@ def queue(): """Show details of the queues """ pass + # 'counters' subcommand ("show queue counters") @queue.command() -@click.argument('interfacename', required=False) +@click.argument('interfacename', metavar='[INTERFACE_NAME]', required=False) @multi_asic_util.multi_asic_click_options +@click.option('-a', '--all', is_flag=True, help="All counters") +@click.option('-T', '--trim', is_flag=True, help="Trimming counters") +@click.option('-V', '--voq', is_flag=True, help="VOQ counters") +@click.option('-nz', '--nonzero', is_flag=True, help="Non Zero Counters") +@click.option('-j', '--json', is_flag=True, help="JSON output") @click.option('--verbose', is_flag=True, help="Enable verbose output") -@click.option('--json', is_flag=True, help="JSON output") -@click.option('--voq', is_flag=True, help="VOQ counters") -@click.option('--nonzero', is_flag=True, help="Non Zero Counters") -def counters(interfacename, namespace, display, verbose, json, voq, nonzero): +def counters(interfacename, namespace, display, all, trim, voq, nonzero, json, verbose): # noqa: F811 """Show queue counters""" cmd = ["queuestat"] @@ -765,8 +768,11 @@ def counters(interfacename, namespace, display, verbose, json, voq, nonzero): if namespace is not None: cmd += ['-n', str(namespace)] - if json: - cmd += ["-j"] + if all: + cmd += ["-a"] + + if trim: + cmd += ["-T"] if voq: cmd += ["-V"] @@ -774,6 +780,9 @@ def counters(interfacename, namespace, display, verbose, json, voq, nonzero): if nonzero: cmd += ["-nz"] + if json: + cmd += ["-j"] + run_command(cmd, display_cmd=verbose) diff --git a/show/plugins/sonic-trimming.py b/show/plugins/sonic-trimming.py new file mode 100644 index 0000000000..f0fff9be1b --- /dev/null +++ b/show/plugins/sonic-trimming.py @@ -0,0 +1,140 @@ +""" +This CLI plugin was auto-generated by using 'sonic-cli-gen' utility +""" + +import click +import tabulate +import json +import utilities_common.cli as clicommon + +from utilities_common.switch_trimming import ( + CFG_SWITCH_TRIMMING, + CFG_TRIM_KEY +) + + +# +# Helpers ------------------------------------------------------------------------------------------------------------- +# + + +def format_attr_value(entry, attr): + """ Helper that formats attribute to be presented in the table output. + + Args: + entry (Dict[str, str]): CONFIG DB entry configuration. + attr (Dict): Attribute metadata. + + Returns: + str: formatted attribute value. + """ + + if attr["is-leaf-list"]: + value = entry.get(attr["name"], []) + return "\n".join(value) if value else "N/A" + return entry.get(attr["name"], "N/A") + + +# +# CLI ----------------------------------------------------------------------------------------------------------------- +# + + +@click.group( + name="switch-trimming", + cls=clicommon.AliasedGroup +) +def SWITCH_TRIMMING(): + """ Show switch trimming feature configuration """ + + pass + + +@SWITCH_TRIMMING.command( + name="global" +) +@click.option( + "-j", "--json", "json_format", + help="Display in JSON format", + is_flag=True, + default=False +) +@clicommon.pass_db +@click.pass_context +def SWITCH_TRIMMING_GLOBAL(ctx, db, json_format): + """ Show switch trimming global configuration """ + + header = [ + "Configuration", + "Value" + ] + body = [] + + table = db.cfgdb.get_table(CFG_SWITCH_TRIMMING) + entry = table.get(CFG_TRIM_KEY, {}) + + if not entry: + click.echo("No configuration is present in CONFIG DB") + ctx.exit(0) + + if json_format: + json_dict = { + "size": entry.get("size", "N/A"), + "dscp_value": entry.get("dscp_value", "N/A"), + "queue_index": entry.get("queue_index", "N/A") + } + click.echo(json.dumps(json_dict, indent=4)) + ctx.exit(0) + + row = [ + "Packet trimming size", + format_attr_value( + entry, + { + 'name': 'size', + 'is-leaf-list': False + } + ) + ] + body.append(row) + + row = [ + "Packet trimming DSCP value", + format_attr_value( + entry, + { + 'name': 'dscp_value', + 'is-leaf-list': False + } + ) + ] + body.append(row) + + row = [ + "Packet trimming queue index", + format_attr_value( + entry, + { + 'name': 'queue_index', + 'is-leaf-list': False + } + ) + ] + body.append(row) + + click.echo(tabulate.tabulate(body, header, "grid")) + + +def register(cli): + """ Register new CLI nodes in root CLI. + + Args: + cli (click.core.Command): Root CLI node. + Raises: + Exception: when root CLI already has a command + we are trying to register. + """ + cli_node = SWITCH_TRIMMING + if cli_node.name in cli.commands: + raise Exception(f"{cli_node.name} already exists in CLI") + cli.add_command(SWITCH_TRIMMING) diff --git a/tests/buffer_input/buffer_test_vectors.py b/tests/buffer_input/buffer_test_vectors.py index baa99608a1..a38d341b6e 100644 --- a/tests/buffer_input/buffer_test_vectors.py +++ b/tests/buffer_input/buffer_test_vectors.py @@ -81,6 +81,14 @@ size 0 ---------- ----------------- +Profile: q_lossy_profile +--------------------- ----------------- +packet_discard_action drop +dynamic_th 0 +pool egress_lossy_pool +size 0 +--------------------- ----------------- + """ show_buffer_information_output="""\ diff --git a/tests/mmuconfig_input/mmuconfig_test_vectors.py b/tests/mmuconfig_input/mmuconfig_test_vectors.py index 1d72ed6725..b1d3a92231 100644 --- a/tests/mmuconfig_input/mmuconfig_test_vectors.py +++ b/tests/mmuconfig_input/mmuconfig_test_vectors.py @@ -81,6 +81,14 @@ size 0 ---------- ----------------- +Profile: q_lossy_profile +--------------------- ----------------- +packet_discard_action drop +dynamic_th 0 +pool egress_lossy_pool +size 0 +--------------------- ----------------- + """ show_mmu_config_asic0 = """\ @@ -275,6 +283,22 @@ '\nError: Invalid value for "-a": 12 is not in the ' 'valid range of -8 to 8.\n') }, + 'mmu_cfg_trim': { + 'cmd': ['config'], + 'args': ['-p', 'q_lossy_profile', '-t', 'on'], + 'rc': 0, + 'db_table': 'BUFFER_PROFILE', + 'cmp_args': [',q_lossy_profile,packet_discard_action,trim'], + 'rc_msg': '' + }, + 'mmu_cfg_trim_invalid': { + 'cmd': ['config'], + 'args': ['-p', 'q_lossy_profile', '-t', 'INVALID_VALUE'], + 'rc': 2, + 'rc_msg': ('Usage: mmu [OPTIONS]\nTry "mmu --help" for help.\n' + '\nError: Invalid value for "-t": invalid choice: INVALID_VALUE. ' + '(choose from on, off)\n') + }, 'mmu_cfg_list_one_masic': {'cmd': ['show'], 'args': ['-n', 'asic0'], 'rc': 0, diff --git a/tests/mmuconfig_test.py b/tests/mmuconfig_test.py index 03a849eed5..ad257f7a8b 100644 --- a/tests/mmuconfig_test.py +++ b/tests/mmuconfig_test.py @@ -86,3 +86,9 @@ def test_mmu_alpha_invalid_config(self): def test_mmu_staticth_config(self): self.executor(testData['mmu_cfg_static_th']) + + def test_mmu_trim_config(self): + self.executor(testData['mmu_cfg_trim']) + + def test_mmu_trim_invalid_config(self): + self.executor(testData['mmu_cfg_trim_invalid']) diff --git a/tests/mock_tables/asic0/counters_db.json b/tests/mock_tables/asic0/counters_db.json index 610662a019..c61a00e192 100644 --- a/tests/mock_tables/asic0/counters_db.json +++ b/tests/mock_tables/asic0/counters_db.json @@ -1822,7 +1822,8 @@ "SAI_PORT_STAT_PFC_4_TX_PKTS": "214", "SAI_PORT_STAT_PFC_5_TX_PKTS": "215", "SAI_PORT_STAT_PFC_6_TX_PKTS": "216", - "SAI_PORT_STAT_PFC_7_TX_PKTS": "217" + "SAI_PORT_STAT_PFC_7_TX_PKTS": "217", + "SAI_PORT_STAT_TRIM_PACKETS": "0" }, "COUNTERS:oid:0x1000000000004": { "SAI_PORT_STAT_IF_IN_UCAST_PKTS": "4", @@ -1850,7 +1851,8 @@ "SAI_PORT_STAT_PFC_4_TX_PKTS": "414", "SAI_PORT_STAT_PFC_5_TX_PKTS": "415", "SAI_PORT_STAT_PFC_6_TX_PKTS": "416", - "SAI_PORT_STAT_PFC_7_TX_PKTS": "417" + "SAI_PORT_STAT_PFC_7_TX_PKTS": "417", + "SAI_PORT_STAT_TRIM_PACKETS": "100" }, "COUNTERS:oid:0x1000000000006": { "SAI_PORT_STAT_IF_IN_UCAST_PKTS": "6", diff --git a/tests/mock_tables/config_db.json b/tests/mock_tables/config_db.json index 7f0a9a2189..d715f28444 100644 --- a/tests/mock_tables/config_db.json +++ b/tests/mock_tables/config_db.json @@ -2039,6 +2039,12 @@ "pool": "egress_lossy_pool", "size": "0" }, + "BUFFER_PROFILE|q_lossy_profile": { + "packet_discard_action": "drop", + "dynamic_th": "0", + "pool": "egress_lossy_pool", + "size": "0" + }, "BUFFER_PG|Ethernet0|3-4": { "profile": "NULL" }, diff --git a/tests/mock_tables/counters_db.json b/tests/mock_tables/counters_db.json index 3293e74f64..bd04150159 100644 --- a/tests/mock_tables/counters_db.json +++ b/tests/mock_tables/counters_db.json @@ -8,7 +8,8 @@ "SAI_QUEUE_STAT_WRED_ECN_MARKED_BYTES": "0", "SAI_QUEUE_STAT_WRED_ECN_MARKED_PACKETS": "0", "SAI_QUEUE_STAT_WRED_DROPPED_PACKETS": "0", - "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES": "61" + "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES": "61", + "SAI_QUEUE_STAT_TRIM_PACKETS": "0" }, "COUNTERS:oid:0x15000000000358": { "SAI_QUEUE_STAT_WRED_DROPPED_BYTES": "43", @@ -19,7 +20,8 @@ "SAI_QUEUE_STAT_DROPPED_BYTES": "1", "SAI_QUEUE_STAT_DROPPED_PACKETS": "39", "SAI_QUEUE_STAT_PACKETS": "60", - "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES": "88" + "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES": "88", + "SAI_QUEUE_STAT_TRIM_PACKETS": "100" }, "COUNTERS:oid:0x15000000000359": { "SAI_QUEUE_STAT_WRED_DROPPED_BYTES": "7", @@ -217,7 +219,8 @@ "SAI_QUEUE_STAT_DROPPED_BYTES": "98", "SAI_QUEUE_STAT_DROPPED_PACKETS": "70", "SAI_QUEUE_STAT_PACKETS": "41", - "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES": "46" + "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES": "46", + "SAI_QUEUE_STAT_TRIM_PACKETS": "0" }, "COUNTERS:oid:0x15000000000380": { "SAI_QUEUE_STAT_WRED_DROPPED_BYTES": "49", @@ -228,7 +231,8 @@ "SAI_QUEUE_STAT_DROPPED_BYTES": "36", "SAI_QUEUE_STAT_DROPPED_PACKETS": "63", "SAI_QUEUE_STAT_PACKETS": "18", - "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES": "18" + "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES": "18", + "SAI_QUEUE_STAT_TRIM_PACKETS": "100" }, "COUNTERS:oid:0x15000000000381": { "SAI_QUEUE_STAT_WRED_DROPPED_BYTES": "90", @@ -426,7 +430,8 @@ "SAI_QUEUE_STAT_DROPPED_BYTES": "0", "SAI_QUEUE_STAT_DROPPED_PACKETS": "0", "SAI_QUEUE_STAT_PACKETS": "0", - "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES": "65" + "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES": "65", + "SAI_QUEUE_STAT_TRIM_PACKETS": "0" }, "COUNTERS:oid:0x150000000003a8": { "SAI_QUEUE_STAT_WRED_DROPPED_BYTES": "17", @@ -437,7 +442,8 @@ "SAI_QUEUE_STAT_DROPPED_BYTES": "91", "SAI_QUEUE_STAT_DROPPED_PACKETS": "68", "SAI_QUEUE_STAT_PACKETS": "38", - "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES": "94" + "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES": "94", + "SAI_QUEUE_STAT_TRIM_PACKETS": "100" }, "COUNTERS:oid:0x150000000003a9": { "SAI_QUEUE_STAT_WRED_DROPPED_BYTES": "65", @@ -1211,21 +1217,22 @@ "SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES": "3", "SAI_PORT_STAT_IF_IN_FEC_SYMBOL_ERRORS": "4", "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S0": "1000000", - "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S1": "900000", - "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S2": "800000", - "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S3": "700000", - "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S4": "600000", - "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S5": "500000", - "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S6": "400000", - "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S7": "300000", - "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S8": "0", - "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S9": "0", - "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S10": "0", - "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S11": "0", - "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S12": "0", - "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S13": "0", - "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S14": "0", - "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S15": "0" + "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S1": "900000", + "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S2": "800000", + "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S3": "700000", + "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S4": "600000", + "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S5": "500000", + "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S6": "400000", + "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S7": "300000", + "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S8": "0", + "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S9": "0", + "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S10": "0", + "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S11": "0", + "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S12": "0", + "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S13": "0", + "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S14": "0", + "SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S15": "0", + "SAI_PORT_STAT_TRIM_PACKETS": "0" }, "COUNTERS:oid:0x1000000000013": { "SAI_PORT_STAT_IF_IN_UCAST_PKTS": "4", @@ -1286,10 +1293,11 @@ "SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES": "110412", "SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES": "1", "SAI_PORT_STAT_IF_IN_FEC_SYMBOL_ERRORS": "0", - "SAI_PORT_STAT_GREEN_WRED_DROPPED_PACKETS":"17", - "SAI_PORT_STAT_YELLOW_WRED_DROPPED_PACKETS":"33", - "SAI_PORT_STAT_RED_WRED_DROPPED_PACKETS":"51", - "SAI_PORT_STAT_WRED_DROPPED_PACKETS":"101" + "SAI_PORT_STAT_GREEN_WRED_DROPPED_PACKETS":"17", + "SAI_PORT_STAT_YELLOW_WRED_DROPPED_PACKETS":"33", + "SAI_PORT_STAT_RED_WRED_DROPPED_PACKETS":"51", + "SAI_PORT_STAT_WRED_DROPPED_PACKETS":"101", + "SAI_PORT_STAT_TRIM_PACKETS": "100" }, "COUNTERS:oid:0x1000000000014": { "SAI_PORT_STAT_IF_IN_UCAST_PKTS": "6", diff --git a/tests/portstat_input/assert_show_output.py b/tests/portstat_input/assert_show_output.py new file mode 100644 index 0000000000..4a71c9a93e --- /dev/null +++ b/tests/portstat_input/assert_show_output.py @@ -0,0 +1,96 @@ +""" +Module holding the correct values for show CLI command outputs for the portstat_test.py +""" + +trim_counters_all = """\ + IFACE STATE TRIM_PKTS +--------- ------- ----------- +Ethernet0 D 0 +Ethernet4 N/A 100 +Ethernet8 N/A N/A +""" +trim_counters_all_json = """\ +{ + "Ethernet0": { + "STATE": "D", + "TRIM_PKTS": "0" + }, + "Ethernet4": { + "STATE": "N/A", + "TRIM_PKTS": "100" + }, + "Ethernet8": { + "STATE": "N/A", + "TRIM_PKTS": "N/A" + } +} +""" + +trim_eth0_counters = """\ + IFACE STATE TRIM_PKTS +--------- ------- ----------- +Ethernet0 D 0 +""" +trim_eth0_counters_json = """\ +{ + "Ethernet0": { + "STATE": "D", + "TRIM_PKTS": "0" + } +} +""" + +trim_eth4_counters = """\ + IFACE STATE TRIM_PKTS +--------- ------- ----------- +Ethernet4 N/A 100 +""" +trim_eth4_counters_json = """\ +{ + "Ethernet4": { + "STATE": "N/A", + "TRIM_PKTS": "100" + } +} +""" + +trim_eth8_counters = """\ + IFACE STATE TRIM_PKTS +--------- ------- ----------- +Ethernet8 N/A N/A +""" +trim_eth8_counters_json = """\ +{ + "Ethernet8": { + "STATE": "N/A", + "TRIM_PKTS": "N/A" + } +} +""" + +trim_counters_period = """\ +The rates are calculated within 3 seconds period + IFACE STATE TRIM_PKTS +--------- ------- ----------- +Ethernet0 D 0 +Ethernet4 N/A 0 +Ethernet8 N/A N/A +""" + +trim_counters_clear_msg = """\ +Cleared counters +""" +trim_counters_clear_stat = """\ + IFACE STATE TRIM_PKTS +--------- ------- ----------- +Ethernet0 D 0 +Ethernet4 N/A 0 +Ethernet8 N/A N/A +""" +trim_counters_clear_raw = """\ + IFACE STATE TRIM_PKTS +--------- ------- ----------- +Ethernet0 D 0 +Ethernet4 N/A 100 +Ethernet8 N/A N/A +""" diff --git a/tests/portstat_test.py b/tests/portstat_test.py index 153281bcef..c731763335 100644 --- a/tests/portstat_test.py +++ b/tests/portstat_test.py @@ -1,3 +1,6 @@ +import pytest +import logging + import os import shutil @@ -6,12 +9,17 @@ import clear.main as clear import show.main as show from .utils import get_result_and_return_code +from .portstat_input import assert_show_output from utilities_common.cli import UserCache test_path = os.path.dirname(os.path.abspath(__file__)) modules_path = os.path.dirname(test_path) scripts_path = os.path.join(modules_path, "scripts") +logger = logging.getLogger(__name__) + +SUCCESS = 0 + intf_counters_before_clear = """\ IFACE STATE RX_OK RX_BPS RX_UTIL RX_ERR RX_DRP RX_OVR TX_OK TX_BPS TX_UTIL TX_ERR TX_DRP TX_OVR --------- ------- ------- ------------ --------- -------- -------- -------- ------- ------------ --------- -------- -------- -------- @@ -27,12 +35,12 @@ """ intf_counters_all = """\ - IFACE STATE RX_OK RX_BPS RX_PPS RX_UTIL RX_ERR RX_DRP RX_OVR TX_OK TX_BPS TX_PPS TX_UTIL TX_ERR TX_DRP TX_OVR ---------- ------- ------- ------------ ----------- --------- -------- -------- -------- ------- ------------ ----------- --------- -------- -------- -------- -Ethernet0 D 8 2000.00 MB/s 247000.00/s 64.00% 10 100 N/A 10 1500.00 MB/s 183000.00/s 48.00% N/A N/A N/A -Ethernet4 N/A 4 204.80 KB/s 200.00/s N/A 0 1,000 N/A 40 204.85 KB/s 201.00/s N/A N/A N/A N/A -Ethernet8 N/A 6 1350.00 KB/s 9000.00/s N/A 100 10 N/A 60 13.37 MB/s 9000.00/s N/A N/A N/A N/A -""" + IFACE STATE RX_OK RX_BPS RX_PPS RX_UTIL RX_ERR RX_DRP RX_OVR TX_OK TX_BPS TX_PPS TX_UTIL TX_ERR TX_DRP TX_OVR TRIM +--------- ------- ------- ------------ ----------- --------- -------- -------- -------- ------- ------------ ----------- --------- -------- -------- -------- ------ +Ethernet0 D 8 2000.00 MB/s 247000.00/s 64.00% 10 100 N/A 10 1500.00 MB/s 183000.00/s 48.00% N/A N/A N/A 0 +Ethernet4 N/A 4 204.80 KB/s 200.00/s N/A 0 1,000 N/A 40 204.85 KB/s 201.00/s N/A N/A N/A N/A 100 +Ethernet8 N/A 6 1350.00 KB/s 9000.00/s N/A 100 10 N/A 60 13.37 MB/s 9000.00/s N/A N/A N/A N/A N/A +""" # noqa: E501 intf_fec_counters = """\ IFACE STATE FEC_CORR FEC_UNCORR FEC_SYMBOL_ERR FEC_PRE_BER FEC_POST_BER @@ -127,40 +135,40 @@ """ multi_asic_external_intf_counters_printall = """\ - IFACE STATE RX_OK RX_BPS RX_PPS RX_UTIL RX_ERR RX_DRP RX_OVR TX_OK TX_BPS TX_PPS TX_UTIL TX_ERR TX_DRP TX_OVR ---------- ------- ------- -------- -------- --------- -------- -------- -------- ------- -------- -------- --------- -------- -------- -------- -Ethernet0 U 8 0.00 B/s 0.00/s 0.00% 10 100 N/A 10 0.00 B/s 0.00/s 0.00% N/A N/A N/A -Ethernet4 U 4 0.00 B/s 0.00/s 0.00% 0 1,000 N/A 40 0.00 B/s 0.00/s 0.00% N/A N/A N/A + IFACE STATE RX_OK RX_BPS RX_PPS RX_UTIL RX_ERR RX_DRP RX_OVR TX_OK TX_BPS TX_PPS TX_UTIL TX_ERR TX_DRP TX_OVR TRIM +--------- ------- ------- -------- -------- --------- -------- -------- -------- ------- -------- -------- --------- -------- -------- -------- ------ +Ethernet0 U 8 0.00 B/s 0.00/s 0.00% 10 100 N/A 10 0.00 B/s 0.00/s 0.00% N/A N/A N/A 0 +Ethernet4 U 4 0.00 B/s 0.00/s 0.00% 0 1,000 N/A 40 0.00 B/s 0.00/s 0.00% N/A N/A N/A 100 Reminder: Please execute 'show interface counters -d all' to include internal links -""" +""" # noqa: E501 multi_asic_intf_counters_printall = """\ - IFACE STATE RX_OK RX_BPS RX_PPS RX_UTIL RX_ERR RX_DRP RX_OVR TX_OK TX_BPS TX_PPS TX_UTIL TX_ERR TX_DRP TX_OVR --------------- ------- ------- -------- -------- --------- -------- -------- -------- ------- -------- -------- --------- -------- -------- -------- - Ethernet0 U 8 0.00 B/s 0.00/s 0.00% 10 100 N/A 10 0.00 B/s 0.00/s 0.00% N/A N/A N/A - Ethernet4 U 4 0.00 B/s 0.00/s 0.00% 0 1,000 N/A 40 0.00 B/s 0.00/s 0.00% N/A N/A N/A - Ethernet-BP0 U 6 0.00 B/s 0.00/s 0.00% 0 1,000 N/A 60 0.00 B/s 0.00/s 0.00% N/A N/A N/A - Ethernet-BP4 U 8 0.00 B/s 0.00/s 0.00% 0 1,000 N/A 80 0.00 B/s 0.00/s 0.00% N/A N/A N/A -Ethernet-BP256 U 8 0.00 B/s 0.00/s 0.00% 10 100 N/A 10 0.00 B/s 0.00/s 0.00% N/A N/A N/A -Ethernet-BP260 U 4 0.00 B/s 0.00/s 0.00% 0 1,000 N/A 40 0.00 B/s 0.00/s 0.00% N/A N/A N/A + IFACE STATE RX_OK RX_BPS RX_PPS RX_UTIL RX_ERR RX_DRP RX_OVR TX_OK TX_BPS TX_PPS TX_UTIL TX_ERR TX_DRP TX_OVR TRIM +-------------- ------- ------- -------- -------- --------- -------- -------- -------- ------- -------- -------- --------- -------- -------- -------- ------ + Ethernet0 U 8 0.00 B/s 0.00/s 0.00% 10 100 N/A 10 0.00 B/s 0.00/s 0.00% N/A N/A N/A 0 + Ethernet4 U 4 0.00 B/s 0.00/s 0.00% 0 1,000 N/A 40 0.00 B/s 0.00/s 0.00% N/A N/A N/A 100 + Ethernet-BP0 U 6 0.00 B/s 0.00/s 0.00% 0 1,000 N/A 60 0.00 B/s 0.00/s 0.00% N/A N/A N/A N/A + Ethernet-BP4 U 8 0.00 B/s 0.00/s 0.00% 0 1,000 N/A 80 0.00 B/s 0.00/s 0.00% N/A N/A N/A N/A +Ethernet-BP256 U 8 0.00 B/s 0.00/s 0.00% 10 100 N/A 10 0.00 B/s 0.00/s 0.00% N/A N/A N/A N/A +Ethernet-BP260 U 4 0.00 B/s 0.00/s 0.00% 0 1,000 N/A 40 0.00 B/s 0.00/s 0.00% N/A N/A N/A N/A Reminder: Please execute 'show interface counters -d all' to include internal links -""" +""" # noqa: E501 multi_asic_intf_counters_asic0_printall = """\ - IFACE STATE RX_OK RX_BPS RX_PPS RX_UTIL RX_ERR RX_DRP RX_OVR TX_OK TX_BPS TX_PPS TX_UTIL TX_ERR TX_DRP TX_OVR ------------- ------- ------- -------- -------- --------- -------- -------- -------- ------- -------- -------- --------- -------- -------- -------- - Ethernet0 U 8 0.00 B/s 0.00/s 0.00% 10 100 N/A 10 0.00 B/s 0.00/s 0.00% N/A N/A N/A - Ethernet4 U 4 0.00 B/s 0.00/s 0.00% 0 1,000 N/A 40 0.00 B/s 0.00/s 0.00% N/A N/A N/A -Ethernet-BP0 U 6 0.00 B/s 0.00/s 0.00% 0 1,000 N/A 60 0.00 B/s 0.00/s 0.00% N/A N/A N/A -Ethernet-BP4 U 8 0.00 B/s 0.00/s 0.00% 0 1,000 N/A 80 0.00 B/s 0.00/s 0.00% N/A N/A N/A + IFACE STATE RX_OK RX_BPS RX_PPS RX_UTIL RX_ERR RX_DRP RX_OVR TX_OK TX_BPS TX_PPS TX_UTIL TX_ERR TX_DRP TX_OVR TRIM +------------ ------- ------- -------- -------- --------- -------- -------- -------- ------- -------- -------- --------- -------- -------- -------- ------ + Ethernet0 U 8 0.00 B/s 0.00/s 0.00% 10 100 N/A 10 0.00 B/s 0.00/s 0.00% N/A N/A N/A 0 + Ethernet4 U 4 0.00 B/s 0.00/s 0.00% 0 1,000 N/A 40 0.00 B/s 0.00/s 0.00% N/A N/A N/A 100 +Ethernet-BP0 U 6 0.00 B/s 0.00/s 0.00% 0 1,000 N/A 60 0.00 B/s 0.00/s 0.00% N/A N/A N/A N/A +Ethernet-BP4 U 8 0.00 B/s 0.00/s 0.00% 0 1,000 N/A 80 0.00 B/s 0.00/s 0.00% N/A N/A N/A N/A Reminder: Please execute 'show interface counters -d all' to include internal links -""" +""" # noqa: E501 multi_asic_intf_counters_period = """\ The rates are calculated within 3 seconds period IFACE STATE RX_OK RX_BPS RX_UTIL RX_ERR RX_DRP RX_OVR TX_OK TX_BPS TX_UTIL TX_ERR TX_DRP TX_OVR @@ -258,6 +266,7 @@ WRED Red Dropped Packets....................... 51 WRED Total Dropped Packets..................... 101 +Packets Trimmed................................ 100 Time Since Counters Last Cleared............... None """ @@ -618,6 +627,187 @@ def teardown_class(cls): .format(os.path.join(test_path, "mock_tables/counters_db.json"))) +class TestPortTrimStat(object): + @classmethod + def setup_class(cls): + logger.info("SETUP") + os.environ["PATH"] += os.pathsep + scripts_path + os.environ["UTILITIES_UNIT_TESTING"] = "2" + remove_tmp_cnstat_file() + + @classmethod + def teardown_class(cls): + logger.info("TEARDOWN") + os.environ["PATH"] = os.pathsep.join( + os.environ["PATH"].split(os.pathsep)[:-1]) + os.environ["UTILITIES_UNIT_TESTING"] = "0" + remove_tmp_cnstat_file() + + @pytest.mark.parametrize( + "output", [ + pytest.param( + { + "plain": assert_show_output.trim_counters_all, + "json": assert_show_output.trim_counters_all_json + }, + id="all" + ) + ] + ) + @pytest.mark.parametrize( + "format", [ + "plain", + "json", + ] + ) + def test_show_port_trim_counters(self, format, output): + runner = CliRunner() + + result = runner.invoke( + show.cli.commands["interfaces"].commands["counters"].commands["trim"], + [] if format == "plain" else ["--json"] + ) + logger.debug("result:\n{}".format(result.output)) + logger.debug("return_code:\n{}".format(result.exit_code)) + + assert result.output == output[format] + assert result.exit_code == SUCCESS + + cmd = ['portstat', '--trim'] + + if format == "json": + cmd.append('-j') + + return_code, result = get_result_and_return_code(cmd) + logger.debug("result:\n{}".format(result)) + logger.debug("return_code:\n{}".format(return_code)) + + assert result == output[format] + assert return_code == SUCCESS + + @pytest.mark.parametrize( + "intf,output", [ + pytest.param( + "Ethernet0", + { + "plain": assert_show_output.trim_eth0_counters, + "json": assert_show_output.trim_eth0_counters_json + }, + id="eth0" + ), + pytest.param( + "Ethernet4", + { + "plain": assert_show_output.trim_eth4_counters, + "json": assert_show_output.trim_eth4_counters_json + }, + id="eth4" + ), + pytest.param( + "Ethernet8", + { + "plain": assert_show_output.trim_eth8_counters, + "json": assert_show_output.trim_eth8_counters_json + }, + id="eth8" + ) + ] + ) + @pytest.mark.parametrize( + "format", [ + "plain", + "json", + ] + ) + def test_show_port_trim_counters_intf(self, format, intf, output): + runner = CliRunner() + + result = runner.invoke( + show.cli.commands["interfaces"].commands["counters"].commands["trim"], + [intf] if format == "plain" else [intf, "--json"] + ) + logger.debug("result:\n{}".format(result.output)) + logger.debug("return_code:\n{}".format(result.exit_code)) + + assert result.output == output[format] + assert result.exit_code == SUCCESS + + cmd = ['portstat', '--trim', '-i', intf] + + if format == "json": + cmd.append('-j') + + return_code, result = get_result_and_return_code(cmd) + logger.debug("result:\n{}".format(result)) + logger.debug("return_code:\n{}".format(return_code)) + + assert result == output[format] + assert return_code == SUCCESS + + def test_show_port_trim_counters_period(self): + runner = CliRunner() + + result = runner.invoke( + show.cli.commands["interfaces"].commands["counters"].commands["trim"], + ["-p", str(TEST_PERIOD)] + ) + logger.debug("result:\n{}".format(result.output)) + logger.debug("return_code:\n{}".format(result.exit_code)) + + assert result.output == assert_show_output.trim_counters_period + assert result.exit_code == SUCCESS + + return_code, result = get_result_and_return_code( + ['portstat', '--trim', '-p', str(TEST_PERIOD)] + ) + logger.debug("result:\n{}".format(result)) + logger.debug("return_code:\n{}".format(return_code)) + + assert result == assert_show_output.trim_counters_period + assert return_code == SUCCESS + + def test_clear_port_trim_counters(self): + # Clear counters + return_code, result = get_result_and_return_code( + ['portstat', '-c'] + ) + logger.debug("result:\n{}".format(result)) + logger.debug("return_code:\n{}".format(return_code)) + + assert result == assert_show_output.trim_counters_clear_msg + assert return_code == SUCCESS + + # Verify updated stats + return_code, result = get_result_and_return_code( + ['portstat', '--trim'] + ) + logger.debug("result:\n{}".format(result)) + logger.debug("return_code:\n{}".format(return_code)) + + verify_after_clear(result, assert_show_output.trim_counters_clear_stat.rstrip()) + assert return_code == SUCCESS + + # Verify raw stats + return_code, result = get_result_and_return_code( + ['portstat', '--trim', '--raw'] + ) + logger.debug("result:\n{}".format(result)) + logger.debug("return_code:\n{}".format(return_code)) + + assert result == assert_show_output.trim_counters_all + assert return_code == SUCCESS + + # Verify stats after snapshot cleanup + return_code, result = get_result_and_return_code( + ['portstat', '--trim', '-d'] + ) + logger.debug("result:\n{}".format(result)) + logger.debug("return_code:\n{}".format(return_code)) + + assert result == assert_show_output.trim_counters_all + assert return_code == SUCCESS + + class TestMultiAsicPortStat(object): @classmethod def setup_class(cls): diff --git a/tests/queuestat_input/assert_show_output.py b/tests/queuestat_input/assert_show_output.py new file mode 100644 index 0000000000..2a446591be --- /dev/null +++ b/tests/queuestat_input/assert_show_output.py @@ -0,0 +1,1638 @@ +""" +Module holding the correct values for show CLI command outputs for the queuestat_test.py +""" + +counters_all = """\ +For namespace : + Port TxQ Counter/pkts Counter/bytes Drop/pkts Drop/bytes Trim/pkts +--------- ----- -------------- --------------- ----------- ------------ ----------- +Ethernet0 UC0 0 0 0 0 0 +Ethernet0 UC1 60 43 39 1 100 +Ethernet0 UC2 82 7 39 21 N/A +Ethernet0 UC3 52 70 19 76 N/A +Ethernet0 UC4 11 59 12 94 N/A +Ethernet0 UC5 36 62 35 40 N/A +Ethernet0 UC6 49 91 2 88 N/A +Ethernet0 UC7 33 17 94 74 N/A +Ethernet0 UC8 40 71 95 33 N/A +Ethernet0 UC9 54 8 93 78 N/A +Ethernet0 MC10 83 96 74 9 N/A +Ethernet0 MC11 15 60 61 31 N/A +Ethernet0 MC12 45 52 82 94 N/A +Ethernet0 MC13 55 88 89 52 N/A +Ethernet0 MC14 14 70 95 79 N/A +Ethernet0 MC15 68 60 66 81 N/A +Ethernet0 MC16 63 4 48 76 N/A +Ethernet0 MC17 41 73 77 74 N/A +Ethernet0 MC18 60 21 56 54 N/A +Ethernet0 MC19 57 31 12 39 N/A +Ethernet0 ALL20 N/A N/A N/A N/A N/A +Ethernet0 ALL21 N/A N/A N/A N/A N/A +Ethernet0 ALL22 N/A N/A N/A N/A N/A +Ethernet0 ALL23 N/A N/A N/A N/A N/A +Ethernet0 ALL24 N/A N/A N/A N/A N/A +Ethernet0 ALL25 N/A N/A N/A N/A N/A +Ethernet0 ALL26 N/A N/A N/A N/A N/A +Ethernet0 ALL27 N/A N/A N/A N/A N/A +Ethernet0 ALL28 N/A N/A N/A N/A N/A +Ethernet0 ALL29 N/A N/A N/A N/A N/A + +For namespace : + Port TxQ Counter/pkts Counter/bytes Drop/pkts Drop/bytes Trim/pkts +--------- ----- -------------- --------------- ----------- ------------ ----------- +Ethernet4 UC0 41 96 70 98 0 +Ethernet4 UC1 18 49 63 36 100 +Ethernet4 UC2 99 90 3 15 N/A +Ethernet4 UC3 60 89 48 41 N/A +Ethernet4 UC4 8 84 82 94 N/A +Ethernet4 UC5 83 15 75 92 N/A +Ethernet4 UC6 84 26 50 71 N/A +Ethernet4 UC7 27 19 49 80 N/A +Ethernet4 UC8 13 89 13 33 N/A +Ethernet4 UC9 43 48 86 31 N/A +Ethernet4 MC10 50 1 57 82 N/A +Ethernet4 MC11 67 99 84 59 N/A +Ethernet4 MC12 4 58 27 5 N/A +Ethernet4 MC13 74 5 57 39 N/A +Ethernet4 MC14 21 59 4 14 N/A +Ethernet4 MC15 24 61 19 53 N/A +Ethernet4 MC16 51 15 15 32 N/A +Ethernet4 MC17 98 18 23 15 N/A +Ethernet4 MC18 41 34 9 57 N/A +Ethernet4 MC19 57 7 18 99 N/A +Ethernet4 ALL20 N/A N/A N/A N/A N/A +Ethernet4 ALL21 N/A N/A N/A N/A N/A +Ethernet4 ALL22 N/A N/A N/A N/A N/A +Ethernet4 ALL23 N/A N/A N/A N/A N/A +Ethernet4 ALL24 N/A N/A N/A N/A N/A +Ethernet4 ALL25 N/A N/A N/A N/A N/A +Ethernet4 ALL26 N/A N/A N/A N/A N/A +Ethernet4 ALL27 N/A N/A N/A N/A N/A +Ethernet4 ALL28 N/A N/A N/A N/A N/A +Ethernet4 ALL29 N/A N/A N/A N/A N/A + +For namespace : + Port TxQ Counter/pkts Counter/bytes Drop/pkts Drop/bytes Trim/pkts +--------- ----- -------------- --------------- ----------- ------------ ----------- +Ethernet8 UC0 0 0 0 0 0 +Ethernet8 UC1 38 17 68 91 100 +Ethernet8 UC2 16 65 79 51 N/A +Ethernet8 UC3 11 97 63 72 N/A +Ethernet8 UC4 54 89 62 62 N/A +Ethernet8 UC5 13 84 30 59 N/A +Ethernet8 UC6 49 67 99 85 N/A +Ethernet8 UC7 2 63 38 88 N/A +Ethernet8 UC8 0 82 93 43 N/A +Ethernet8 UC9 80 17 91 61 N/A +Ethernet8 MC10 81 63 76 73 N/A +Ethernet8 MC11 29 16 29 66 N/A +Ethernet8 MC12 32 12 61 35 N/A +Ethernet8 MC13 79 17 72 93 N/A +Ethernet8 MC14 23 21 67 50 N/A +Ethernet8 MC15 37 10 97 14 N/A +Ethernet8 MC16 30 17 74 43 N/A +Ethernet8 MC17 0 63 54 84 N/A +Ethernet8 MC18 69 88 24 79 N/A +Ethernet8 MC19 20 12 84 3 N/A +Ethernet8 ALL20 N/A N/A N/A N/A N/A +Ethernet8 ALL21 N/A N/A N/A N/A N/A +Ethernet8 ALL22 N/A N/A N/A N/A N/A +Ethernet8 ALL23 N/A N/A N/A N/A N/A +Ethernet8 ALL24 N/A N/A N/A N/A N/A +Ethernet8 ALL25 N/A N/A N/A N/A N/A +Ethernet8 ALL26 N/A N/A N/A N/A N/A +Ethernet8 ALL27 N/A N/A N/A N/A N/A +Ethernet8 ALL28 N/A N/A N/A N/A N/A +Ethernet8 ALL29 N/A N/A N/A N/A N/A + +""" +counters_all_json = """\ +{ + "Ethernet0": { + "ALL20": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL21": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL22": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL23": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL24": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL25": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL26": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL27": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL28": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL29": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "MC10": { + "dropbytes": "9", + "droppacket": "74", + "totalbytes": "96", + "totalpacket": "83", + "trimpacket": "N/A" + }, + "MC11": { + "dropbytes": "31", + "droppacket": "61", + "totalbytes": "60", + "totalpacket": "15", + "trimpacket": "N/A" + }, + "MC12": { + "dropbytes": "94", + "droppacket": "82", + "totalbytes": "52", + "totalpacket": "45", + "trimpacket": "N/A" + }, + "MC13": { + "dropbytes": "52", + "droppacket": "89", + "totalbytes": "88", + "totalpacket": "55", + "trimpacket": "N/A" + }, + "MC14": { + "dropbytes": "79", + "droppacket": "95", + "totalbytes": "70", + "totalpacket": "14", + "trimpacket": "N/A" + }, + "MC15": { + "dropbytes": "81", + "droppacket": "66", + "totalbytes": "60", + "totalpacket": "68", + "trimpacket": "N/A" + }, + "MC16": { + "dropbytes": "76", + "droppacket": "48", + "totalbytes": "4", + "totalpacket": "63", + "trimpacket": "N/A" + }, + "MC17": { + "dropbytes": "74", + "droppacket": "77", + "totalbytes": "73", + "totalpacket": "41", + "trimpacket": "N/A" + }, + "MC18": { + "dropbytes": "54", + "droppacket": "56", + "totalbytes": "21", + "totalpacket": "60", + "trimpacket": "N/A" + }, + "MC19": { + "dropbytes": "39", + "droppacket": "12", + "totalbytes": "31", + "totalpacket": "57", + "trimpacket": "N/A" + }, + "UC0": { + "dropbytes": "0", + "droppacket": "0", + "totalbytes": "0", + "totalpacket": "0", + "trimpacket": "0" + }, + "UC1": { + "dropbytes": "1", + "droppacket": "39", + "totalbytes": "43", + "totalpacket": "60", + "trimpacket": "100" + }, + "UC2": { + "dropbytes": "21", + "droppacket": "39", + "totalbytes": "7", + "totalpacket": "82", + "trimpacket": "N/A" + }, + "UC3": { + "dropbytes": "76", + "droppacket": "19", + "totalbytes": "70", + "totalpacket": "52", + "trimpacket": "N/A" + }, + "UC4": { + "dropbytes": "94", + "droppacket": "12", + "totalbytes": "59", + "totalpacket": "11", + "trimpacket": "N/A" + }, + "UC5": { + "dropbytes": "40", + "droppacket": "35", + "totalbytes": "62", + "totalpacket": "36", + "trimpacket": "N/A" + }, + "UC6": { + "dropbytes": "88", + "droppacket": "2", + "totalbytes": "91", + "totalpacket": "49", + "trimpacket": "N/A" + }, + "UC7": { + "dropbytes": "74", + "droppacket": "94", + "totalbytes": "17", + "totalpacket": "33", + "trimpacket": "N/A" + }, + "UC8": { + "dropbytes": "33", + "droppacket": "95", + "totalbytes": "71", + "totalpacket": "40", + "trimpacket": "N/A" + }, + "UC9": { + "dropbytes": "78", + "droppacket": "93", + "totalbytes": "8", + "totalpacket": "54", + "trimpacket": "N/A" + } + }, + "Ethernet4": { + "ALL20": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL21": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL22": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL23": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL24": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL25": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL26": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL27": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL28": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL29": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "MC10": { + "dropbytes": "82", + "droppacket": "57", + "totalbytes": "1", + "totalpacket": "50", + "trimpacket": "N/A" + }, + "MC11": { + "dropbytes": "59", + "droppacket": "84", + "totalbytes": "99", + "totalpacket": "67", + "trimpacket": "N/A" + }, + "MC12": { + "dropbytes": "5", + "droppacket": "27", + "totalbytes": "58", + "totalpacket": "4", + "trimpacket": "N/A" + }, + "MC13": { + "dropbytes": "39", + "droppacket": "57", + "totalbytes": "5", + "totalpacket": "74", + "trimpacket": "N/A" + }, + "MC14": { + "dropbytes": "14", + "droppacket": "4", + "totalbytes": "59", + "totalpacket": "21", + "trimpacket": "N/A" + }, + "MC15": { + "dropbytes": "53", + "droppacket": "19", + "totalbytes": "61", + "totalpacket": "24", + "trimpacket": "N/A" + }, + "MC16": { + "dropbytes": "32", + "droppacket": "15", + "totalbytes": "15", + "totalpacket": "51", + "trimpacket": "N/A" + }, + "MC17": { + "dropbytes": "15", + "droppacket": "23", + "totalbytes": "18", + "totalpacket": "98", + "trimpacket": "N/A" + }, + "MC18": { + "dropbytes": "57", + "droppacket": "9", + "totalbytes": "34", + "totalpacket": "41", + "trimpacket": "N/A" + }, + "MC19": { + "dropbytes": "99", + "droppacket": "18", + "totalbytes": "7", + "totalpacket": "57", + "trimpacket": "N/A" + }, + "UC0": { + "dropbytes": "98", + "droppacket": "70", + "totalbytes": "96", + "totalpacket": "41", + "trimpacket": "0" + }, + "UC1": { + "dropbytes": "36", + "droppacket": "63", + "totalbytes": "49", + "totalpacket": "18", + "trimpacket": "100" + }, + "UC2": { + "dropbytes": "15", + "droppacket": "3", + "totalbytes": "90", + "totalpacket": "99", + "trimpacket": "N/A" + }, + "UC3": { + "dropbytes": "41", + "droppacket": "48", + "totalbytes": "89", + "totalpacket": "60", + "trimpacket": "N/A" + }, + "UC4": { + "dropbytes": "94", + "droppacket": "82", + "totalbytes": "84", + "totalpacket": "8", + "trimpacket": "N/A" + }, + "UC5": { + "dropbytes": "92", + "droppacket": "75", + "totalbytes": "15", + "totalpacket": "83", + "trimpacket": "N/A" + }, + "UC6": { + "dropbytes": "71", + "droppacket": "50", + "totalbytes": "26", + "totalpacket": "84", + "trimpacket": "N/A" + }, + "UC7": { + "dropbytes": "80", + "droppacket": "49", + "totalbytes": "19", + "totalpacket": "27", + "trimpacket": "N/A" + }, + "UC8": { + "dropbytes": "33", + "droppacket": "13", + "totalbytes": "89", + "totalpacket": "13", + "trimpacket": "N/A" + }, + "UC9": { + "dropbytes": "31", + "droppacket": "86", + "totalbytes": "48", + "totalpacket": "43", + "trimpacket": "N/A" + } + }, + "Ethernet8": { + "ALL20": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL21": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL22": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL23": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL24": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL25": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL26": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL27": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL28": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "ALL29": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A", + "trimpacket": "N/A" + }, + "MC10": { + "dropbytes": "73", + "droppacket": "76", + "totalbytes": "63", + "totalpacket": "81", + "trimpacket": "N/A" + }, + "MC11": { + "dropbytes": "66", + "droppacket": "29", + "totalbytes": "16", + "totalpacket": "29", + "trimpacket": "N/A" + }, + "MC12": { + "dropbytes": "35", + "droppacket": "61", + "totalbytes": "12", + "totalpacket": "32", + "trimpacket": "N/A" + }, + "MC13": { + "dropbytes": "93", + "droppacket": "72", + "totalbytes": "17", + "totalpacket": "79", + "trimpacket": "N/A" + }, + "MC14": { + "dropbytes": "50", + "droppacket": "67", + "totalbytes": "21", + "totalpacket": "23", + "trimpacket": "N/A" + }, + "MC15": { + "dropbytes": "14", + "droppacket": "97", + "totalbytes": "10", + "totalpacket": "37", + "trimpacket": "N/A" + }, + "MC16": { + "dropbytes": "43", + "droppacket": "74", + "totalbytes": "17", + "totalpacket": "30", + "trimpacket": "N/A" + }, + "MC17": { + "dropbytes": "84", + "droppacket": "54", + "totalbytes": "63", + "totalpacket": "0", + "trimpacket": "N/A" + }, + "MC18": { + "dropbytes": "79", + "droppacket": "24", + "totalbytes": "88", + "totalpacket": "69", + "trimpacket": "N/A" + }, + "MC19": { + "dropbytes": "3", + "droppacket": "84", + "totalbytes": "12", + "totalpacket": "20", + "trimpacket": "N/A" + }, + "UC0": { + "dropbytes": "0", + "droppacket": "0", + "totalbytes": "0", + "totalpacket": "0", + "trimpacket": "0" + }, + "UC1": { + "dropbytes": "91", + "droppacket": "68", + "totalbytes": "17", + "totalpacket": "38", + "trimpacket": "100" + }, + "UC2": { + "dropbytes": "51", + "droppacket": "79", + "totalbytes": "65", + "totalpacket": "16", + "trimpacket": "N/A" + }, + "UC3": { + "dropbytes": "72", + "droppacket": "63", + "totalbytes": "97", + "totalpacket": "11", + "trimpacket": "N/A" + }, + "UC4": { + "dropbytes": "62", + "droppacket": "62", + "totalbytes": "89", + "totalpacket": "54", + "trimpacket": "N/A" + }, + "UC5": { + "dropbytes": "59", + "droppacket": "30", + "totalbytes": "84", + "totalpacket": "13", + "trimpacket": "N/A" + }, + "UC6": { + "dropbytes": "85", + "droppacket": "99", + "totalbytes": "67", + "totalpacket": "49", + "trimpacket": "N/A" + }, + "UC7": { + "dropbytes": "88", + "droppacket": "38", + "totalbytes": "63", + "totalpacket": "2", + "trimpacket": "N/A" + }, + "UC8": { + "dropbytes": "43", + "droppacket": "93", + "totalbytes": "82", + "totalpacket": "0", + "trimpacket": "N/A" + }, + "UC9": { + "dropbytes": "61", + "droppacket": "91", + "totalbytes": "17", + "totalpacket": "80", + "trimpacket": "N/A" + } + } +} +""" + +trim_counters_all = """\ +For namespace : + Port TxQ Trim/pkts +--------- ----- ----------- +Ethernet0 UC0 0 +Ethernet0 UC1 100 +Ethernet0 UC2 N/A +Ethernet0 UC3 N/A +Ethernet0 UC4 N/A +Ethernet0 UC5 N/A +Ethernet0 UC6 N/A +Ethernet0 UC7 N/A +Ethernet0 UC8 N/A +Ethernet0 UC9 N/A +Ethernet0 MC10 N/A +Ethernet0 MC11 N/A +Ethernet0 MC12 N/A +Ethernet0 MC13 N/A +Ethernet0 MC14 N/A +Ethernet0 MC15 N/A +Ethernet0 MC16 N/A +Ethernet0 MC17 N/A +Ethernet0 MC18 N/A +Ethernet0 MC19 N/A +Ethernet0 ALL20 N/A +Ethernet0 ALL21 N/A +Ethernet0 ALL22 N/A +Ethernet0 ALL23 N/A +Ethernet0 ALL24 N/A +Ethernet0 ALL25 N/A +Ethernet0 ALL26 N/A +Ethernet0 ALL27 N/A +Ethernet0 ALL28 N/A +Ethernet0 ALL29 N/A + +For namespace : + Port TxQ Trim/pkts +--------- ----- ----------- +Ethernet4 UC0 0 +Ethernet4 UC1 100 +Ethernet4 UC2 N/A +Ethernet4 UC3 N/A +Ethernet4 UC4 N/A +Ethernet4 UC5 N/A +Ethernet4 UC6 N/A +Ethernet4 UC7 N/A +Ethernet4 UC8 N/A +Ethernet4 UC9 N/A +Ethernet4 MC10 N/A +Ethernet4 MC11 N/A +Ethernet4 MC12 N/A +Ethernet4 MC13 N/A +Ethernet4 MC14 N/A +Ethernet4 MC15 N/A +Ethernet4 MC16 N/A +Ethernet4 MC17 N/A +Ethernet4 MC18 N/A +Ethernet4 MC19 N/A +Ethernet4 ALL20 N/A +Ethernet4 ALL21 N/A +Ethernet4 ALL22 N/A +Ethernet4 ALL23 N/A +Ethernet4 ALL24 N/A +Ethernet4 ALL25 N/A +Ethernet4 ALL26 N/A +Ethernet4 ALL27 N/A +Ethernet4 ALL28 N/A +Ethernet4 ALL29 N/A + +For namespace : + Port TxQ Trim/pkts +--------- ----- ----------- +Ethernet8 UC0 0 +Ethernet8 UC1 100 +Ethernet8 UC2 N/A +Ethernet8 UC3 N/A +Ethernet8 UC4 N/A +Ethernet8 UC5 N/A +Ethernet8 UC6 N/A +Ethernet8 UC7 N/A +Ethernet8 UC8 N/A +Ethernet8 UC9 N/A +Ethernet8 MC10 N/A +Ethernet8 MC11 N/A +Ethernet8 MC12 N/A +Ethernet8 MC13 N/A +Ethernet8 MC14 N/A +Ethernet8 MC15 N/A +Ethernet8 MC16 N/A +Ethernet8 MC17 N/A +Ethernet8 MC18 N/A +Ethernet8 MC19 N/A +Ethernet8 ALL20 N/A +Ethernet8 ALL21 N/A +Ethernet8 ALL22 N/A +Ethernet8 ALL23 N/A +Ethernet8 ALL24 N/A +Ethernet8 ALL25 N/A +Ethernet8 ALL26 N/A +Ethernet8 ALL27 N/A +Ethernet8 ALL28 N/A +Ethernet8 ALL29 N/A + +""" +trim_counters_all_json = """\ +{ + "Ethernet0": { + "ALL20": { + "trimpacket": "N/A" + }, + "ALL21": { + "trimpacket": "N/A" + }, + "ALL22": { + "trimpacket": "N/A" + }, + "ALL23": { + "trimpacket": "N/A" + }, + "ALL24": { + "trimpacket": "N/A" + }, + "ALL25": { + "trimpacket": "N/A" + }, + "ALL26": { + "trimpacket": "N/A" + }, + "ALL27": { + "trimpacket": "N/A" + }, + "ALL28": { + "trimpacket": "N/A" + }, + "ALL29": { + "trimpacket": "N/A" + }, + "MC10": { + "trimpacket": "N/A" + }, + "MC11": { + "trimpacket": "N/A" + }, + "MC12": { + "trimpacket": "N/A" + }, + "MC13": { + "trimpacket": "N/A" + }, + "MC14": { + "trimpacket": "N/A" + }, + "MC15": { + "trimpacket": "N/A" + }, + "MC16": { + "trimpacket": "N/A" + }, + "MC17": { + "trimpacket": "N/A" + }, + "MC18": { + "trimpacket": "N/A" + }, + "MC19": { + "trimpacket": "N/A" + }, + "UC0": { + "trimpacket": "0" + }, + "UC1": { + "trimpacket": "100" + }, + "UC2": { + "trimpacket": "N/A" + }, + "UC3": { + "trimpacket": "N/A" + }, + "UC4": { + "trimpacket": "N/A" + }, + "UC5": { + "trimpacket": "N/A" + }, + "UC6": { + "trimpacket": "N/A" + }, + "UC7": { + "trimpacket": "N/A" + }, + "UC8": { + "trimpacket": "N/A" + }, + "UC9": { + "trimpacket": "N/A" + } + }, + "Ethernet4": { + "ALL20": { + "trimpacket": "N/A" + }, + "ALL21": { + "trimpacket": "N/A" + }, + "ALL22": { + "trimpacket": "N/A" + }, + "ALL23": { + "trimpacket": "N/A" + }, + "ALL24": { + "trimpacket": "N/A" + }, + "ALL25": { + "trimpacket": "N/A" + }, + "ALL26": { + "trimpacket": "N/A" + }, + "ALL27": { + "trimpacket": "N/A" + }, + "ALL28": { + "trimpacket": "N/A" + }, + "ALL29": { + "trimpacket": "N/A" + }, + "MC10": { + "trimpacket": "N/A" + }, + "MC11": { + "trimpacket": "N/A" + }, + "MC12": { + "trimpacket": "N/A" + }, + "MC13": { + "trimpacket": "N/A" + }, + "MC14": { + "trimpacket": "N/A" + }, + "MC15": { + "trimpacket": "N/A" + }, + "MC16": { + "trimpacket": "N/A" + }, + "MC17": { + "trimpacket": "N/A" + }, + "MC18": { + "trimpacket": "N/A" + }, + "MC19": { + "trimpacket": "N/A" + }, + "UC0": { + "trimpacket": "0" + }, + "UC1": { + "trimpacket": "100" + }, + "UC2": { + "trimpacket": "N/A" + }, + "UC3": { + "trimpacket": "N/A" + }, + "UC4": { + "trimpacket": "N/A" + }, + "UC5": { + "trimpacket": "N/A" + }, + "UC6": { + "trimpacket": "N/A" + }, + "UC7": { + "trimpacket": "N/A" + }, + "UC8": { + "trimpacket": "N/A" + }, + "UC9": { + "trimpacket": "N/A" + } + }, + "Ethernet8": { + "ALL20": { + "trimpacket": "N/A" + }, + "ALL21": { + "trimpacket": "N/A" + }, + "ALL22": { + "trimpacket": "N/A" + }, + "ALL23": { + "trimpacket": "N/A" + }, + "ALL24": { + "trimpacket": "N/A" + }, + "ALL25": { + "trimpacket": "N/A" + }, + "ALL26": { + "trimpacket": "N/A" + }, + "ALL27": { + "trimpacket": "N/A" + }, + "ALL28": { + "trimpacket": "N/A" + }, + "ALL29": { + "trimpacket": "N/A" + }, + "MC10": { + "trimpacket": "N/A" + }, + "MC11": { + "trimpacket": "N/A" + }, + "MC12": { + "trimpacket": "N/A" + }, + "MC13": { + "trimpacket": "N/A" + }, + "MC14": { + "trimpacket": "N/A" + }, + "MC15": { + "trimpacket": "N/A" + }, + "MC16": { + "trimpacket": "N/A" + }, + "MC17": { + "trimpacket": "N/A" + }, + "MC18": { + "trimpacket": "N/A" + }, + "MC19": { + "trimpacket": "N/A" + }, + "UC0": { + "trimpacket": "0" + }, + "UC1": { + "trimpacket": "100" + }, + "UC2": { + "trimpacket": "N/A" + }, + "UC3": { + "trimpacket": "N/A" + }, + "UC4": { + "trimpacket": "N/A" + }, + "UC5": { + "trimpacket": "N/A" + }, + "UC6": { + "trimpacket": "N/A" + }, + "UC7": { + "trimpacket": "N/A" + }, + "UC8": { + "trimpacket": "N/A" + }, + "UC9": { + "trimpacket": "N/A" + } + } +} +""" + +trim_eth0_counters = """\ +For namespace : + Port TxQ Trim/pkts +--------- ----- ----------- +Ethernet0 UC0 0 +Ethernet0 UC1 100 +Ethernet0 UC2 N/A +Ethernet0 UC3 N/A +Ethernet0 UC4 N/A +Ethernet0 UC5 N/A +Ethernet0 UC6 N/A +Ethernet0 UC7 N/A +Ethernet0 UC8 N/A +Ethernet0 UC9 N/A +Ethernet0 MC10 N/A +Ethernet0 MC11 N/A +Ethernet0 MC12 N/A +Ethernet0 MC13 N/A +Ethernet0 MC14 N/A +Ethernet0 MC15 N/A +Ethernet0 MC16 N/A +Ethernet0 MC17 N/A +Ethernet0 MC18 N/A +Ethernet0 MC19 N/A +Ethernet0 ALL20 N/A +Ethernet0 ALL21 N/A +Ethernet0 ALL22 N/A +Ethernet0 ALL23 N/A +Ethernet0 ALL24 N/A +Ethernet0 ALL25 N/A +Ethernet0 ALL26 N/A +Ethernet0 ALL27 N/A +Ethernet0 ALL28 N/A +Ethernet0 ALL29 N/A + +""" +trim_eth0_counters_json = """\ +{ + "Ethernet0": { + "ALL20": { + "trimpacket": "N/A" + }, + "ALL21": { + "trimpacket": "N/A" + }, + "ALL22": { + "trimpacket": "N/A" + }, + "ALL23": { + "trimpacket": "N/A" + }, + "ALL24": { + "trimpacket": "N/A" + }, + "ALL25": { + "trimpacket": "N/A" + }, + "ALL26": { + "trimpacket": "N/A" + }, + "ALL27": { + "trimpacket": "N/A" + }, + "ALL28": { + "trimpacket": "N/A" + }, + "ALL29": { + "trimpacket": "N/A" + }, + "MC10": { + "trimpacket": "N/A" + }, + "MC11": { + "trimpacket": "N/A" + }, + "MC12": { + "trimpacket": "N/A" + }, + "MC13": { + "trimpacket": "N/A" + }, + "MC14": { + "trimpacket": "N/A" + }, + "MC15": { + "trimpacket": "N/A" + }, + "MC16": { + "trimpacket": "N/A" + }, + "MC17": { + "trimpacket": "N/A" + }, + "MC18": { + "trimpacket": "N/A" + }, + "MC19": { + "trimpacket": "N/A" + }, + "UC0": { + "trimpacket": "0" + }, + "UC1": { + "trimpacket": "100" + }, + "UC2": { + "trimpacket": "N/A" + }, + "UC3": { + "trimpacket": "N/A" + }, + "UC4": { + "trimpacket": "N/A" + }, + "UC5": { + "trimpacket": "N/A" + }, + "UC6": { + "trimpacket": "N/A" + }, + "UC7": { + "trimpacket": "N/A" + }, + "UC8": { + "trimpacket": "N/A" + }, + "UC9": { + "trimpacket": "N/A" + } + } +} +""" + +trim_eth4_counters = """\ +For namespace : + Port TxQ Trim/pkts +--------- ----- ----------- +Ethernet4 UC0 0 +Ethernet4 UC1 100 +Ethernet4 UC2 N/A +Ethernet4 UC3 N/A +Ethernet4 UC4 N/A +Ethernet4 UC5 N/A +Ethernet4 UC6 N/A +Ethernet4 UC7 N/A +Ethernet4 UC8 N/A +Ethernet4 UC9 N/A +Ethernet4 MC10 N/A +Ethernet4 MC11 N/A +Ethernet4 MC12 N/A +Ethernet4 MC13 N/A +Ethernet4 MC14 N/A +Ethernet4 MC15 N/A +Ethernet4 MC16 N/A +Ethernet4 MC17 N/A +Ethernet4 MC18 N/A +Ethernet4 MC19 N/A +Ethernet4 ALL20 N/A +Ethernet4 ALL21 N/A +Ethernet4 ALL22 N/A +Ethernet4 ALL23 N/A +Ethernet4 ALL24 N/A +Ethernet4 ALL25 N/A +Ethernet4 ALL26 N/A +Ethernet4 ALL27 N/A +Ethernet4 ALL28 N/A +Ethernet4 ALL29 N/A + +""" +trim_eth4_counters_json = """\ +{ + "Ethernet4": { + "ALL20": { + "trimpacket": "N/A" + }, + "ALL21": { + "trimpacket": "N/A" + }, + "ALL22": { + "trimpacket": "N/A" + }, + "ALL23": { + "trimpacket": "N/A" + }, + "ALL24": { + "trimpacket": "N/A" + }, + "ALL25": { + "trimpacket": "N/A" + }, + "ALL26": { + "trimpacket": "N/A" + }, + "ALL27": { + "trimpacket": "N/A" + }, + "ALL28": { + "trimpacket": "N/A" + }, + "ALL29": { + "trimpacket": "N/A" + }, + "MC10": { + "trimpacket": "N/A" + }, + "MC11": { + "trimpacket": "N/A" + }, + "MC12": { + "trimpacket": "N/A" + }, + "MC13": { + "trimpacket": "N/A" + }, + "MC14": { + "trimpacket": "N/A" + }, + "MC15": { + "trimpacket": "N/A" + }, + "MC16": { + "trimpacket": "N/A" + }, + "MC17": { + "trimpacket": "N/A" + }, + "MC18": { + "trimpacket": "N/A" + }, + "MC19": { + "trimpacket": "N/A" + }, + "UC0": { + "trimpacket": "0" + }, + "UC1": { + "trimpacket": "100" + }, + "UC2": { + "trimpacket": "N/A" + }, + "UC3": { + "trimpacket": "N/A" + }, + "UC4": { + "trimpacket": "N/A" + }, + "UC5": { + "trimpacket": "N/A" + }, + "UC6": { + "trimpacket": "N/A" + }, + "UC7": { + "trimpacket": "N/A" + }, + "UC8": { + "trimpacket": "N/A" + }, + "UC9": { + "trimpacket": "N/A" + } + } +} +""" + +trim_eth8_counters = """\ +For namespace : + Port TxQ Trim/pkts +--------- ----- ----------- +Ethernet8 UC0 0 +Ethernet8 UC1 100 +Ethernet8 UC2 N/A +Ethernet8 UC3 N/A +Ethernet8 UC4 N/A +Ethernet8 UC5 N/A +Ethernet8 UC6 N/A +Ethernet8 UC7 N/A +Ethernet8 UC8 N/A +Ethernet8 UC9 N/A +Ethernet8 MC10 N/A +Ethernet8 MC11 N/A +Ethernet8 MC12 N/A +Ethernet8 MC13 N/A +Ethernet8 MC14 N/A +Ethernet8 MC15 N/A +Ethernet8 MC16 N/A +Ethernet8 MC17 N/A +Ethernet8 MC18 N/A +Ethernet8 MC19 N/A +Ethernet8 ALL20 N/A +Ethernet8 ALL21 N/A +Ethernet8 ALL22 N/A +Ethernet8 ALL23 N/A +Ethernet8 ALL24 N/A +Ethernet8 ALL25 N/A +Ethernet8 ALL26 N/A +Ethernet8 ALL27 N/A +Ethernet8 ALL28 N/A +Ethernet8 ALL29 N/A + +""" +trim_eth8_counters_json = """\ +{ + "Ethernet8": { + "ALL20": { + "trimpacket": "N/A" + }, + "ALL21": { + "trimpacket": "N/A" + }, + "ALL22": { + "trimpacket": "N/A" + }, + "ALL23": { + "trimpacket": "N/A" + }, + "ALL24": { + "trimpacket": "N/A" + }, + "ALL25": { + "trimpacket": "N/A" + }, + "ALL26": { + "trimpacket": "N/A" + }, + "ALL27": { + "trimpacket": "N/A" + }, + "ALL28": { + "trimpacket": "N/A" + }, + "ALL29": { + "trimpacket": "N/A" + }, + "MC10": { + "trimpacket": "N/A" + }, + "MC11": { + "trimpacket": "N/A" + }, + "MC12": { + "trimpacket": "N/A" + }, + "MC13": { + "trimpacket": "N/A" + }, + "MC14": { + "trimpacket": "N/A" + }, + "MC15": { + "trimpacket": "N/A" + }, + "MC16": { + "trimpacket": "N/A" + }, + "MC17": { + "trimpacket": "N/A" + }, + "MC18": { + "trimpacket": "N/A" + }, + "MC19": { + "trimpacket": "N/A" + }, + "UC0": { + "trimpacket": "0" + }, + "UC1": { + "trimpacket": "100" + }, + "UC2": { + "trimpacket": "N/A" + }, + "UC3": { + "trimpacket": "N/A" + }, + "UC4": { + "trimpacket": "N/A" + }, + "UC5": { + "trimpacket": "N/A" + }, + "UC6": { + "trimpacket": "N/A" + }, + "UC7": { + "trimpacket": "N/A" + }, + "UC8": { + "trimpacket": "N/A" + }, + "UC9": { + "trimpacket": "N/A" + } + } +} +""" + +trim_counters_clear_msg = """\ +Clear and update saved counters for Ethernet0 +Clear and update saved counters for Ethernet4 +Clear and update saved counters for Ethernet8 +""" +trim_counters_clear_stat = """\ + Port TxQ Trim/pkts +--------- ----- ----------- +Ethernet0 UC0 0 +Ethernet0 UC1 0 +Ethernet0 UC2 N/A +Ethernet0 UC3 N/A +Ethernet0 UC4 N/A +Ethernet0 UC5 N/A +Ethernet0 UC6 N/A +Ethernet0 UC7 N/A +Ethernet0 UC8 N/A +Ethernet0 UC9 N/A +Ethernet0 MC10 N/A +Ethernet0 MC11 N/A +Ethernet0 MC12 N/A +Ethernet0 MC13 N/A +Ethernet0 MC14 N/A +Ethernet0 MC15 N/A +Ethernet0 MC16 N/A +Ethernet0 MC17 N/A +Ethernet0 MC18 N/A +Ethernet0 MC19 N/A +Ethernet0 ALL20 N/A +Ethernet0 ALL21 N/A +Ethernet0 ALL22 N/A +Ethernet0 ALL23 N/A +Ethernet0 ALL24 N/A +Ethernet0 ALL25 N/A +Ethernet0 ALL26 N/A +Ethernet0 ALL27 N/A +Ethernet0 ALL28 N/A +Ethernet0 ALL29 N/A + + Port TxQ Trim/pkts +--------- ----- ----------- +Ethernet4 UC0 0 +Ethernet4 UC1 0 +Ethernet4 UC2 N/A +Ethernet4 UC3 N/A +Ethernet4 UC4 N/A +Ethernet4 UC5 N/A +Ethernet4 UC6 N/A +Ethernet4 UC7 N/A +Ethernet4 UC8 N/A +Ethernet4 UC9 N/A +Ethernet4 MC10 N/A +Ethernet4 MC11 N/A +Ethernet4 MC12 N/A +Ethernet4 MC13 N/A +Ethernet4 MC14 N/A +Ethernet4 MC15 N/A +Ethernet4 MC16 N/A +Ethernet4 MC17 N/A +Ethernet4 MC18 N/A +Ethernet4 MC19 N/A +Ethernet4 ALL20 N/A +Ethernet4 ALL21 N/A +Ethernet4 ALL22 N/A +Ethernet4 ALL23 N/A +Ethernet4 ALL24 N/A +Ethernet4 ALL25 N/A +Ethernet4 ALL26 N/A +Ethernet4 ALL27 N/A +Ethernet4 ALL28 N/A +Ethernet4 ALL29 N/A + + Port TxQ Trim/pkts +--------- ----- ----------- +Ethernet8 UC0 0 +Ethernet8 UC1 0 +Ethernet8 UC2 N/A +Ethernet8 UC3 N/A +Ethernet8 UC4 N/A +Ethernet8 UC5 N/A +Ethernet8 UC6 N/A +Ethernet8 UC7 N/A +Ethernet8 UC8 N/A +Ethernet8 UC9 N/A +Ethernet8 MC10 N/A +Ethernet8 MC11 N/A +Ethernet8 MC12 N/A +Ethernet8 MC13 N/A +Ethernet8 MC14 N/A +Ethernet8 MC15 N/A +Ethernet8 MC16 N/A +Ethernet8 MC17 N/A +Ethernet8 MC18 N/A +Ethernet8 MC19 N/A +Ethernet8 ALL20 N/A +Ethernet8 ALL21 N/A +Ethernet8 ALL22 N/A +Ethernet8 ALL23 N/A +Ethernet8 ALL24 N/A +Ethernet8 ALL25 N/A +Ethernet8 ALL26 N/A +Ethernet8 ALL27 N/A +Ethernet8 ALL28 N/A +Ethernet8 ALL29 N/A +""" diff --git a/tests/queuestat_test.py b/tests/queuestat_test.py new file mode 100644 index 0000000000..4d05bff2e0 --- /dev/null +++ b/tests/queuestat_test.py @@ -0,0 +1,268 @@ +import pytest + +import os +import json +import logging + +import show.main as show + +from click.testing import CliRunner + +from utilities_common.cli import UserCache +from utilities_common.cli import json_dump + +from .utils import get_result_and_return_code +from .queuestat_input import assert_show_output + + +test_path = os.path.dirname(os.path.abspath(__file__)) +modules_path = os.path.dirname(test_path) +scripts_path = os.path.join(modules_path, "scripts") + +logger = logging.getLogger(__name__) + +SUCCESS = 0 + + +def remove_tmp_cnstat_file(): + cache = UserCache("queuestat") + cache.remove_all() + + +def remove_timestamp(result): + result_json = json.loads(result) + for key in result_json.keys(): + result_json[key].pop("time", None) + return json_dump(result_json) + + +def verify_after_clear(output, expected_out): + # ignore lines containing 'Last cached time was' as it has time stamp and is diffcult to compare + lines = [x for x in output.splitlines() if 'Last cached time was' not in x] + new_output = '\n'.join(lines) + assert new_output == expected_out + + +class TestQueueStat(object): + @classmethod + def setup_class(cls): + logger.info("SETUP") + os.environ["PATH"] += os.pathsep + scripts_path + os.environ['UTILITIES_UNIT_TESTING'] = "2" + + @classmethod + def teardown_class(cls): + logger.info("TEARDOWN") + os.environ["PATH"] = os.pathsep.join(os.environ["PATH"].split(os.pathsep)[:-1]) + os.environ['UTILITIES_UNIT_TESTING'] = "0" + + @pytest.mark.parametrize( + "output", [ + pytest.param( + { + "plain": assert_show_output.counters_all, + "json": assert_show_output.counters_all_json + }, + id="all" + ) + ] + ) + @pytest.mark.parametrize( + "format", [ + "plain", + "json", + ] + ) + def test_queue_counters(self, format, output): + runner = CliRunner() + + result = runner.invoke( + show.cli.commands["queue"].commands["counters"], + ["--all"] if format == "plain" else ["--all", "--json"] + ) + logger.debug("result:\n{}".format(result.output)) + logger.debug("return_code:\n{}".format(result.exit_code)) + + if format == "json": + assert "{}\n".format(remove_timestamp(result.output)) == output[format] + else: + assert result.output == output[format] + + assert result.exit_code == SUCCESS + + cmd = ['queuestat', '--all'] + + if format == "json": + cmd.append('-j') + + return_code, result = get_result_and_return_code(cmd) + logger.debug("result:\n{}".format(result)) + logger.debug("return_code:\n{}".format(return_code)) + + if format == "json": + result = "{}\n".format(remove_timestamp(result)) + + assert result == output[format] + assert return_code == SUCCESS + + +class TestQueueTrimStat(object): + @classmethod + def setup_class(cls): + logger.info("SETUP") + remove_tmp_cnstat_file() + os.environ["PATH"] += os.pathsep + scripts_path + os.environ['UTILITIES_UNIT_TESTING'] = "2" + + @classmethod + def teardown_class(cls): + logger.info("TEARDOWN") + remove_tmp_cnstat_file() + os.environ["PATH"] = os.pathsep.join(os.environ["PATH"].split(os.pathsep)[:-1]) + os.environ['UTILITIES_UNIT_TESTING'] = "0" + + @pytest.mark.parametrize( + "output", [ + pytest.param( + { + "plain": assert_show_output.trim_counters_all, + "json": assert_show_output.trim_counters_all_json + }, + id="all" + ) + ] + ) + @pytest.mark.parametrize( + "format", [ + "plain", + "json", + ] + ) + def test_show_queue_trim_counters(self, format, output): + runner = CliRunner() + + result = runner.invoke( + show.cli.commands["queue"].commands["counters"], + ["--trim"] if format == "plain" else ["--trim", "--json"] + ) + logger.debug("result:\n{}".format(result.output)) + logger.debug("return_code:\n{}".format(result.exit_code)) + + if format == "json": + assert "{}\n".format(remove_timestamp(result.output)) == output[format] + else: + assert result.output == output[format] + + assert result.exit_code == SUCCESS + + cmd = ['queuestat', '--trim'] + + if format == "json": + cmd.append('-j') + + return_code, result = get_result_and_return_code(cmd) + logger.debug("result:\n{}".format(result)) + logger.debug("return_code:\n{}".format(return_code)) + + if format == "json": + result = "{}\n".format(remove_timestamp(result)) + + assert result == output[format] + assert return_code == SUCCESS + + @pytest.mark.parametrize( + "intf,output", [ + pytest.param( + "Ethernet0", + { + "plain": assert_show_output.trim_eth0_counters, + "json": assert_show_output.trim_eth0_counters_json + }, + id="eth0" + ), + pytest.param( + "Ethernet4", + { + "plain": assert_show_output.trim_eth4_counters, + "json": assert_show_output.trim_eth4_counters_json + }, + id="eth4" + ), + pytest.param( + "Ethernet8", + { + "plain": assert_show_output.trim_eth8_counters, + "json": assert_show_output.trim_eth8_counters_json + }, + id="eth8" + ) + ] + ) + @pytest.mark.parametrize( + "format", [ + "plain", + "json", + ] + ) + def test_show_queue_trim_counters_intf(self, format, intf, output): + runner = CliRunner() + + result = runner.invoke( + show.cli.commands["queue"].commands["counters"], + [intf, "--trim"] if format == "plain" else [intf, "--trim", "--json"] + ) + logger.debug("result:\n{}".format(result.output)) + logger.debug("return_code:\n{}".format(result.exit_code)) + + if format == "json": + assert "{}\n".format(remove_timestamp(result.output)) == output[format] + else: + assert result.output == output[format] + + assert result.exit_code == SUCCESS + + cmd = ['queuestat', '--trim', '-p', intf] + + if format == "json": + cmd.append('-j') + + return_code, result = get_result_and_return_code(cmd) + logger.debug("result:\n{}".format(result)) + logger.debug("return_code:\n{}".format(return_code)) + + if format == "json": + result = "{}\n".format(remove_timestamp(result)) + + assert result == output[format] + assert return_code == SUCCESS + + def test_clear_queue_trim_counters(self): + # Clear counters + return_code, result = get_result_and_return_code( + ['queuestat', '-c'] + ) + logger.debug("result:\n{}".format(result)) + logger.debug("return_code:\n{}".format(return_code)) + + assert result == assert_show_output.trim_counters_clear_msg + assert return_code == SUCCESS + + # Verify updated stats + return_code, result = get_result_and_return_code( + ['queuestat', '--trim'] + ) + logger.debug("result:\n{}".format(result)) + logger.debug("return_code:\n{}".format(return_code)) + + verify_after_clear(result, assert_show_output.trim_counters_clear_stat) + assert return_code == SUCCESS + + # Verify stats after snapshot cleanup + return_code, result = get_result_and_return_code( + ['queuestat', '--trim', '-d'] + ) + logger.debug("result:\n{}".format(result)) + logger.debug("return_code:\n{}".format(return_code)) + + assert result == assert_show_output.trim_counters_all + assert return_code == SUCCESS diff --git a/tests/trimming_input/assert_show_output.py b/tests/trimming_input/assert_show_output.py new file mode 100644 index 0000000000..de58b5b0d0 --- /dev/null +++ b/tests/trimming_input/assert_show_output.py @@ -0,0 +1,64 @@ +""" +Module holding the correct values for show CLI command outputs for the trimming_test.py +""" + +show_trim_empty = """\ +No configuration is present in CONFIG DB +""" + +show_trim_partial = """\ ++-----------------------------+---------+ +| Configuration | Value | ++=============================+=========+ +| Packet trimming size | 200 | ++-----------------------------+---------+ +| Packet trimming DSCP value | 20 | ++-----------------------------+---------+ +| Packet trimming queue index | N/A | ++-----------------------------+---------+ +""" +show_trim_partial_json = """\ +{ + "size": "200", + "dscp_value": "20", + "queue_index": "N/A" +} +""" + +show_trim_queue_static = """\ ++-----------------------------+---------+ +| Configuration | Value | ++=============================+=========+ +| Packet trimming size | 200 | ++-----------------------------+---------+ +| Packet trimming DSCP value | 20 | ++-----------------------------+---------+ +| Packet trimming queue index | 2 | ++-----------------------------+---------+ +""" +show_trim_queue_static_json = """\ +{ + "size": "200", + "dscp_value": "20", + "queue_index": "2" +} +""" + +show_trim_queue_dynamic = """\ ++-----------------------------+---------+ +| Configuration | Value | ++=============================+=========+ +| Packet trimming size | 200 | ++-----------------------------+---------+ +| Packet trimming DSCP value | 20 | ++-----------------------------+---------+ +| Packet trimming queue index | dynamic | ++-----------------------------+---------+ +""" +show_trim_queue_dynamic_json = """\ +{ + "size": "200", + "dscp_value": "20", + "queue_index": "dynamic" +} +""" diff --git a/tests/trimming_input/mock_config/empty.json b/tests/trimming_input/mock_config/empty.json new file mode 100644 index 0000000000..e913c3f583 --- /dev/null +++ b/tests/trimming_input/mock_config/empty.json @@ -0,0 +1,5 @@ +{ + "SWITCH_TRIMMING|GLOBAL": { + "NULL": "NULL" + } +} diff --git a/tests/trimming_input/mock_config/partial.json b/tests/trimming_input/mock_config/partial.json new file mode 100644 index 0000000000..a47775ad41 --- /dev/null +++ b/tests/trimming_input/mock_config/partial.json @@ -0,0 +1,6 @@ +{ + "SWITCH_TRIMMING|GLOBAL": { + "size": "200", + "dscp_value": "20" + } +} diff --git a/tests/trimming_input/mock_config/queue_dynamic.json b/tests/trimming_input/mock_config/queue_dynamic.json new file mode 100644 index 0000000000..5d259260e2 --- /dev/null +++ b/tests/trimming_input/mock_config/queue_dynamic.json @@ -0,0 +1,7 @@ +{ + "SWITCH_TRIMMING|GLOBAL": { + "size": "200", + "dscp_value": "20", + "queue_index": "dynamic" + } +} diff --git a/tests/trimming_input/mock_config/queue_static.json b/tests/trimming_input/mock_config/queue_static.json new file mode 100644 index 0000000000..58ae476c57 --- /dev/null +++ b/tests/trimming_input/mock_config/queue_static.json @@ -0,0 +1,7 @@ +{ + "SWITCH_TRIMMING|GLOBAL": { + "size": "200", + "dscp_value": "20", + "queue_index": "2" + } +} diff --git a/tests/trimming_input/mock_state/all.json b/tests/trimming_input/mock_state/all.json new file mode 100644 index 0000000000..2d77c510fe --- /dev/null +++ b/tests/trimming_input/mock_state/all.json @@ -0,0 +1,6 @@ +{ + "SWITCH_CAPABILITY|switch": { + "SWITCH_TRIMMING_CAPABLE": "true", + "SWITCH|PACKET_TRIMMING_QUEUE_RESOLUTION_MODE": "STATIC,DYNAMIC" + } +} diff --git a/tests/trimming_input/mock_state/no_capabilities.json b/tests/trimming_input/mock_state/no_capabilities.json new file mode 100644 index 0000000000..a3e92a9639 --- /dev/null +++ b/tests/trimming_input/mock_state/no_capabilities.json @@ -0,0 +1,6 @@ +{ + "SWITCH_CAPABILITY|switch": { + "SWITCH_TRIMMING_CAPABLE": "true", + "SWITCH|PACKET_TRIMMING_QUEUE_RESOLUTION_MODE": "" + } +} diff --git a/tests/trimming_input/mock_state/not_supported.json b/tests/trimming_input/mock_state/not_supported.json new file mode 100644 index 0000000000..f92c451a44 --- /dev/null +++ b/tests/trimming_input/mock_state/not_supported.json @@ -0,0 +1,6 @@ +{ + "SWITCH_CAPABILITY|switch": { + "SWITCH_TRIMMING_CAPABLE": "false", + "SWITCH|PACKET_TRIMMING_QUEUE_RESOLUTION_MODE": "" + } +} diff --git a/tests/trimming_input/mock_state/queue_dynamic.json b/tests/trimming_input/mock_state/queue_dynamic.json new file mode 100644 index 0000000000..aa4a704bc6 --- /dev/null +++ b/tests/trimming_input/mock_state/queue_dynamic.json @@ -0,0 +1,6 @@ +{ + "SWITCH_CAPABILITY|switch": { + "SWITCH_TRIMMING_CAPABLE": "true", + "SWITCH|PACKET_TRIMMING_QUEUE_RESOLUTION_MODE": "DYNAMIC" + } +} diff --git a/tests/trimming_input/mock_state/queue_static.json b/tests/trimming_input/mock_state/queue_static.json new file mode 100644 index 0000000000..f103608fa1 --- /dev/null +++ b/tests/trimming_input/mock_state/queue_static.json @@ -0,0 +1,6 @@ +{ + "SWITCH_CAPABILITY|switch": { + "SWITCH_TRIMMING_CAPABLE": "true", + "SWITCH|PACKET_TRIMMING_QUEUE_RESOLUTION_MODE": "STATIC" + } +} diff --git a/tests/trimming_test.py b/tests/trimming_test.py new file mode 100644 index 0000000000..11c2741872 --- /dev/null +++ b/tests/trimming_test.py @@ -0,0 +1,188 @@ +import pytest +import os +import logging +import show.main as show +import config.main as config + +from click.testing import CliRunner +from utilities_common.db import Db +from .mock_tables import dbconnector +from .trimming_input import assert_show_output + + +test_path = os.path.dirname(os.path.abspath(__file__)) +input_path = os.path.join(test_path, "trimming_input") +mock_config_path = os.path.join(input_path, "mock_config") +mock_state_path = os.path.join(input_path, "mock_state") + +logger = logging.getLogger(__name__) + + +SUCCESS = 0 +ERROR2 = 2 + + +class TestTrimming: + @classmethod + def setup_class(cls): + logger.info("Setup class: {}".format(cls.__name__)) + os.environ['UTILITIES_UNIT_TESTING'] = "1" + dbconnector.dedicated_dbs["STATE_DB"] = os.path.join(mock_state_path, "all") + + @classmethod + def teardown_class(cls): + logger.info("Teardown class: {}".format(cls.__name__)) + os.environ['UTILITIES_UNIT_TESTING'] = "0" + dbconnector.dedicated_dbs.clear() + + # ---------- CONFIG CONFIG SWITCH-TRIMMING GLOBAL ---------- # + + @pytest.mark.parametrize( + "size,dscp,queue", [ + pytest.param( + "200", "20", "2", + id="queue-static" + ), + pytest.param( + "300", "30", "dynamic", + id="queue-dynamic" + ) + ] + ) + def test_config_trimming(self, size, dscp, queue): + db = Db() + runner = CliRunner() + + result = runner.invoke( + config.config.commands["switch-trimming"].commands["global"], + ["--size", size, "--dscp", dscp, "--queue", queue], obj=db + ) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + + assert result.exit_code == SUCCESS + + @pytest.mark.parametrize( + "statedb,option,pattern", [ + pytest.param( + os.path.join(mock_state_path, "all"), + ["--size", "-1"], + "is not in the valid range of", + id="size-out-of-bound" + ), + pytest.param( + os.path.join(mock_state_path, "all"), + ["--dscp", "-1"], + "is not in the valid range of", + id="dscp-out-of-bound" + ), + pytest.param( + os.path.join(mock_state_path, "all"), + ["--queue", "-1"], + "is not in the valid range of", + id="queue-out-of-bound" + ), + pytest.param( + os.path.join(mock_state_path, "queue_static"), + ["--queue", "dynamic"], + "dynamic queue resolution mode is not supported", + id="queue-static-only" + ), + pytest.param( + os.path.join(mock_state_path, "queue_dynamic"), + ["--queue", "2"], + "static queue resolution mode is not supported", + id="queue-dynamic-only" + ), + pytest.param( + os.path.join(mock_state_path, "no_capabilities"), + ["--queue", "dynamic"], + "no queue resolution mode capabilities", + id="no-capabilities" + ), + pytest.param( + os.path.join(mock_state_path, "not_supported"), + ["--queue", "2"], + "operation is not supported", + id="not-supported" + ) + ] + ) + def test_config_trimming_neg(self, statedb, option, pattern): + dbconnector.dedicated_dbs["STATE_DB"] = statedb + + db = Db() + runner = CliRunner() + + result = runner.invoke( + config.config.commands["switch-trimming"].commands["global"], + option, obj=db + ) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + + assert pattern in result.output + assert result.exit_code == ERROR2 + + # ---------- SHOW SWITCH-TRIMMING GLOBAL ---------- # + + @pytest.mark.parametrize( + "cfgdb,output", [ + pytest.param( + os.path.join(mock_config_path, "empty"), + { + "plain": assert_show_output.show_trim_empty, + "json": assert_show_output.show_trim_empty + }, + id="empty" + ), + pytest.param( + os.path.join(mock_config_path, "partial"), + { + "plain": assert_show_output.show_trim_partial, + "json": assert_show_output.show_trim_partial_json + }, + id="partial" + ), + pytest.param( + os.path.join(mock_config_path, "queue_static"), + { + "plain": assert_show_output.show_trim_queue_static, + "json": assert_show_output.show_trim_queue_static_json + }, + id="queue-static" + ), + pytest.param( + os.path.join(mock_config_path, "queue_dynamic"), + { + "plain": assert_show_output.show_trim_queue_dynamic, + "json": assert_show_output.show_trim_queue_dynamic_json + }, + id="queue-dynamic" + ) + ] + ) + @pytest.mark.parametrize( + "format", [ + "plain", + "json", + ] + ) + def test_show_trimming(self, cfgdb, output, format): + dbconnector.dedicated_dbs["CONFIG_DB"] = cfgdb + + db = Db() + runner = CliRunner() + + result = runner.invoke( + show.cli.commands["switch-trimming"].commands["global"], + [] if format == "plain" else ["--json"], obj=db + ) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + + assert result.output == output[format] + assert result.exit_code == SUCCESS diff --git a/tests/wred_queue_counter_test.py b/tests/wred_queue_counter_test.py index c120af415f..2cb2eaa632 100644 --- a/tests/wred_queue_counter_test.py +++ b/tests/wred_queue_counter_test.py @@ -7,6 +7,7 @@ import clear.main as clear import show.main as show from utilities_common.cli import json_dump +from utilities_common.cli import UserCache test_path = os.path.dirname(os.path.abspath(__file__)) modules_path = os.path.dirname(test_path) @@ -1148,11 +1149,18 @@ }""" +def remove_tmp_cnstat_file(): + # remove the tmp wredstat + cache = UserCache("wredstat") + cache.remove_all() + + class TestWredQueue(object): @classmethod def setup_class(cls): os.environ["PATH"] += os.pathsep + scripts_path os.environ['UTILITIES_UNIT_TESTING'] = "2" + remove_tmp_cnstat_file() print("SETUP") def test_queue_counters(self): diff --git a/utilities_common/portstat.py b/utilities_common/portstat.py index 8e40b37125..a69078bf9f 100644 --- a/utilities_common/portstat.py +++ b/utilities_common/portstat.py @@ -28,14 +28,16 @@ tx_uca, tx_mca, tx_bca, tx_all,\ rx_jbr, rx_frag, rx_usize, rx_ovrrun,\ fec_corr, fec_uncorr, fec_symbol_err,\ - wred_grn_drp_pkt, wred_ylw_drp_pkt, wred_red_drp_pkt, wred_tot_drp_pkt") + wred_grn_drp_pkt, wred_ylw_drp_pkt, wred_red_drp_pkt, wred_tot_drp_pkt,\ + trim") header_all = ['IFACE', 'STATE', 'RX_OK', 'RX_BPS', 'RX_PPS', 'RX_UTIL', 'RX_ERR', 'RX_DRP', 'RX_OVR', - 'TX_OK', 'TX_BPS', 'TX_PPS', 'TX_UTIL', 'TX_ERR', 'TX_DRP', 'TX_OVR'] + 'TX_OK', 'TX_BPS', 'TX_PPS', 'TX_UTIL', 'TX_ERR', 'TX_DRP', 'TX_OVR', 'TRIM'] header_std = ['IFACE', 'STATE', 'RX_OK', 'RX_BPS', 'RX_UTIL', 'RX_ERR', 'RX_DRP', 'RX_OVR', 'TX_OK', 'TX_BPS', 'TX_UTIL', 'TX_ERR', 'TX_DRP', 'TX_OVR'] header_errors_only = ['IFACE', 'STATE', 'RX_ERR', 'RX_DRP', 'RX_OVR', 'TX_ERR', 'TX_DRP', 'TX_OVR'] header_fec_only = ['IFACE', 'STATE', 'FEC_CORR', 'FEC_UNCORR', 'FEC_SYMBOL_ERR', 'FEC_PRE_BER', 'FEC_POST_BER'] header_rates_only = ['IFACE', 'STATE', 'RX_OK', 'RX_BPS', 'RX_PPS', 'RX_UTIL', 'TX_OK', 'TX_BPS', 'TX_PPS', 'TX_UTIL'] +header_trim_only = ['IFACE', 'STATE', 'TRIM_PKTS'] rates_key_list = ['RX_BPS', 'RX_PPS', 'RX_UTIL', 'TX_BPS', 'TX_PPS', 'TX_UTIL', 'FEC_PRE_BER', 'FEC_POST_BER'] ratestat_fields = ("rx_bps", "rx_pps", "rx_util", "tx_bps", "tx_pps", "tx_util", "fec_pre_ber", "fec_post_ber") @@ -45,7 +47,7 @@ The order and count of statistics mentioned below needs to be in sync with the values in portstat script So, any fields added/deleted in here should be reflected in portstat script also """ -BUCKET_NUM = 49 +BUCKET_NUM = 50 wred_green_pkt_stat_capable = "false" wred_yellow_pkt_stat_capable = "false" @@ -105,7 +107,8 @@ 45: ['SAI_PORT_STAT_GREEN_WRED_DROPPED_PACKETS'], 46: ['SAI_PORT_STAT_YELLOW_WRED_DROPPED_PACKETS'], 47: ['SAI_PORT_STAT_RED_WRED_DROPPED_PACKETS'], - 48: ['SAI_PORT_STAT_WRED_DROPPED_PACKETS'] + 48: ['SAI_PORT_STAT_WRED_DROPPED_PACKETS'], + 49: ['SAI_PORT_STAT_TRIM_PACKETS'], } STATUS_NA = 'N/A' @@ -380,7 +383,7 @@ def get_port_state(self, port_name): return STATUS_NA def cnstat_print(self, cnstat_dict, ratestat_dict, intf_list, use_json, print_all, - errors_only, fec_stats_only, rates_only, detail=False): + errors_only, fec_stats_only, rates_only, trim_stats_only, detail=False): """ Print the cnstat. """ @@ -418,7 +421,8 @@ def cnstat_print(self, cnstat_dict, ratestat_dict, intf_list, use_json, print_al if rates.tx_util == STATUS_NA else format_util_directly(rates.tx_util), format_number_with_comma(data["tx_err"]), format_number_with_comma(data["tx_drop"]), - format_number_with_comma(data["tx_ovr"]))) + format_number_with_comma(data["tx_ovr"]), + format_number_with_comma(data["trim"]))) elif errors_only: header = header_errors_only table.append((key, self.get_port_state(key), @@ -449,6 +453,10 @@ def cnstat_print(self, cnstat_dict, ratestat_dict, intf_list, use_json, print_al format_prate(rates.tx_pps), format_util(rates.tx_bps, port_speed) if rates.tx_util == STATUS_NA else format_util_directly(rates.tx_util))) + elif trim_stats_only: # Packet Trimming related statistics + header = header_trim_only + table.append((key, self.get_port_state(key), + format_number_with_comma(data['trim']))) else: header = header_std table.append((key, self.get_port_state(key), @@ -603,12 +611,15 @@ def cnstat_intf_diff_print(self, cnstat_new_dict, cnstat_old_dict, intf_list): ) print("") + print("Packets Trimmed................................ {}".format(ns_diff(cntr['trim'], + old_cntr['trim']))) + print("Time Since Counters Last Cleared............... " + str(cnstat_old_dict.get('time'))) def cnstat_diff_print(self, cnstat_new_dict, cnstat_old_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, fec_stats_only, - rates_only, detail=False): + rates_only, trim_stats_only, detail=False): """ Print the difference between two cnstat results. """ @@ -653,7 +664,8 @@ def cnstat_diff_print(self, cnstat_new_dict, cnstat_old_dict, if rates.tx_util == STATUS_NA else format_util_directly(rates.tx_util), ns_diff(cntr["tx_err"], old_cntr["tx_err"]), ns_diff(cntr["tx_drop"], old_cntr["tx_drop"]), - ns_diff(cntr["tx_ovr"], old_cntr["tx_ovr"]))) + ns_diff(cntr["tx_ovr"], old_cntr["tx_ovr"]), + ns_diff(cntr["trim"], old_cntr["trim"]))) else: table.append((key, self.get_port_state(key), format_number_with_comma(cntr["rx_ok"]), @@ -671,7 +683,8 @@ def cnstat_diff_print(self, cnstat_new_dict, cnstat_old_dict, if rates.tx_util == STATUS_NA else format_util_directly(rates.tx_util), format_number_with_comma(cntr["tx_err"]), format_number_with_comma(cntr["tx_drop"]), - format_number_with_comma(cntr["tx_ovr"]))) + format_number_with_comma(cntr["tx_ovr"]), + format_number_with_comma(cntr["trim"]))) elif errors_only: header = header_errors_only if old_cntr is not None: @@ -731,6 +744,14 @@ def cnstat_diff_print(self, cnstat_new_dict, cnstat_old_dict, format_prate(rates.tx_pps), format_util(rates.tx_bps, port_speed) if rates.tx_util == STATUS_NA else format_util_directly(rates.tx_util))) + elif trim_stats_only: # Packet Trimming related statistics + header = header_trim_only + if old_cntr is not None: + table.append((key, self.get_port_state(key), + ns_diff(cntr['trim'], old_cntr['trim']))) + else: + table.append((key, self.get_port_state(key), + format_number_with_comma(cntr['trim']))) else: header = header_std if old_cntr is not None: diff --git a/utilities_common/switch_trimming.py b/utilities_common/switch_trimming.py new file mode 100644 index 0000000000..761969b7c8 --- /dev/null +++ b/utilities_common/switch_trimming.py @@ -0,0 +1,41 @@ +from utilities_common import db + +from swsscommon.swsscommon import CFG_SWITCH_TRIMMING_TABLE_NAME as CFG_SWITCH_TRIMMING # noqa: F401 +from swsscommon.swsscommon import STATE_SWITCH_CAPABILITY_TABLE_NAME as STATE_SWITCH_CAPABILITY # noqa: F401 + + +# +# Constants ----------------------------------------------------------------------------------------------------------- +# + + +STATE_CAP_TRIMMING_CAPABLE_KEY = "SWITCH_TRIMMING_CAPABLE" +STATE_CAP_QUEUE_MODE_KEY = "SWITCH|PACKET_TRIMMING_QUEUE_RESOLUTION_MODE" + +STATE_CAP_QUEUE_MODE_DYNAMIC = "DYNAMIC" +STATE_CAP_QUEUE_MODE_STATIC = "STATIC" + +CFG_TRIM_QUEUE_INDEX_DYNAMIC = "dynamic" + +CFG_TRIM_KEY = "GLOBAL" +STATE_CAP_KEY = "switch" + +UINT32_MAX = 4294967295 +UINT8_MAX = 255 + +SYSLOG_IDENTIFIER = "switch_trimming" + + +# +# Helpers ------------------------------------------------------------------------------------------------------------- +# + + +def get_db(ctx): + """ Get DB object """ + return ctx.find_object(db.Db).db + + +def to_str(obj_dict): + """ Convert dict to comma-separated representation """ + return ", ".join(["{}={}".format(k, v) for k, v in obj_dict.items() if v])