From 1b1402f5dc0fd01e5f0d25679e7382fc96226a40 Mon Sep 17 00:00:00 2001 From: Nazarii Hnydyn Date: Fri, 1 Dec 2023 21:04:15 +0200 Subject: [PATCH] [hash]: Add ECMP/LAG hash algorithm CLI (#3036) **HLD:** https://github.com/sonic-net/SONiC/pull/1501 #### What I did * Implemented CLI for Generic Hash feature #### How I did it * Integrated Generic Hash interface into `config` and `show` CLI root #### How to verify it * Run Generic Hash CLI UTs #### Previous command output (if the output of a command-line utility has changed) ``` root@sonic:/home/admin# show switch-hash global ECMP HASH LAG HASH ----------------- ----------------- DST_MAC DST_MAC SRC_MAC SRC_MAC ETHERTYPE ETHERTYPE IP_PROTOCOL IP_PROTOCOL DST_IP DST_IP SRC_IP SRC_IP L4_DST_PORT L4_DST_PORT L4_SRC_PORT L4_SRC_PORT INNER_DST_MAC INNER_DST_MAC INNER_SRC_MAC INNER_SRC_MAC INNER_ETHERTYPE INNER_ETHERTYPE INNER_IP_PROTOCOL INNER_IP_PROTOCOL INNER_DST_IP INNER_DST_IP INNER_SRC_IP INNER_SRC_IP INNER_L4_DST_PORT INNER_L4_DST_PORT INNER_L4_SRC_PORT INNER_L4_SRC_PORT ``` #### New command output (if the output of a command-line utility has changed) ``` root@sonic:/home/admin# show switch-hash global +--------+-------------------------------------+ | Hash | Configuration | +========+=====================================+ | ECMP | +-------------------+-------------+ | | | | Hash Field | Algorithm | | | | |-------------------+-------------| | | | | DST_MAC | CRC | | | | | SRC_MAC | | | | | | ETHERTYPE | | | | | | IP_PROTOCOL | | | | | | DST_IP | | | | | | SRC_IP | | | | | | L4_DST_PORT | | | | | | L4_SRC_PORT | | | | | | INNER_DST_MAC | | | | | | INNER_SRC_MAC | | | | | | INNER_ETHERTYPE | | | | | | INNER_IP_PROTOCOL | | | | | | INNER_DST_IP | | | | | | INNER_SRC_IP | | | | | | INNER_L4_DST_PORT | | | | | | INNER_L4_SRC_PORT | | | | | +-------------------+-------------+ | +--------+-------------------------------------+ | LAG | +-------------------+-------------+ | | | | Hash Field | Algorithm | | | | |-------------------+-------------| | | | | DST_MAC | CRC | | | | | SRC_MAC | | | | | | ETHERTYPE | | | | | | IP_PROTOCOL | | | | | | DST_IP | | | | | | SRC_IP | | | | | | L4_DST_PORT | | | | | | L4_SRC_PORT | | | | | | INNER_DST_MAC | | | | | | INNER_SRC_MAC | | | | | | INNER_ETHERTYPE | | | | | | INNER_IP_PROTOCOL | | | | | | INNER_DST_IP | | | | | | INNER_SRC_IP | | | | | | INNER_L4_DST_PORT | | | | | | INNER_L4_SRC_PORT | | | | | +-------------------+-------------+ | +--------+-------------------------------------+ ``` --- config/plugins/sonic-hash.py | 159 ++++- doc/Command-Reference.md | 153 +++- show/plugins/sonic-hash.py | 167 ++++- tests/hash_input/assert_show_output.py | 673 ++++++++++++++---- tests/hash_input/mock_config/ecmp.json | 3 +- .../hash_input/mock_config/ecmp_and_lag.json | 4 +- tests/hash_input/mock_config/lag.json | 3 +- tests/hash_input/mock_state/ecmp.json | 6 +- tests/hash_input/mock_state/ecmp_and_lag.json | 6 +- tests/hash_input/mock_state/empty.json | 6 +- tests/hash_input/mock_state/lag.json | 6 +- .../mock_state/no_capabilities.json | 6 +- .../hash_input/mock_state/not_applicable.json | 6 +- tests/hash_test.py | 141 +++- utilities_common/switch_hash.py | 20 +- 15 files changed, 1158 insertions(+), 201 deletions(-) diff --git a/config/plugins/sonic-hash.py b/config/plugins/sonic-hash.py index 1d3c65c537..75a787722d 100644 --- a/config/plugins/sonic-hash.py +++ b/config/plugins/sonic-hash.py @@ -10,11 +10,16 @@ CFG_SWITCH_HASH, STATE_SWITCH_CAPABILITY, SW_CAP_HASH_FIELD_LIST_KEY, - SW_CAP_ECMP_HASH_KEY, - SW_CAP_LAG_HASH_KEY, + SW_CAP_ECMP_HASH_ALGORITHM_KEY, + SW_CAP_LAG_HASH_ALGORITHM_KEY, + SW_CAP_ECMP_HASH_CAPABLE_KEY, + SW_CAP_LAG_HASH_CAPABLE_KEY, + SW_CAP_ECMP_HASH_ALGORITHM_CAPABLE_KEY, + SW_CAP_LAG_HASH_ALGORITHM_CAPABLE_KEY, SW_HASH_KEY, SW_CAP_KEY, HASH_FIELD_LIST, + HASH_ALGORITHM, SYSLOG_IDENTIFIER, get_param, get_param_hint, @@ -47,6 +52,22 @@ def hash_field_validator(ctx, param, value): return list(value) +def hash_algorithm_validator(ctx, param, value): + """ + Check if hash algorithm argument is valid + Args: + ctx: click context + param: click parameter context + value: value of parameter + Returns: + str: validated parameter + """ + + click.Choice(HASH_ALGORITHM).convert(value, param, ctx) + + return value + + def ecmp_hash_validator(ctx, db, ecmp_hash): """ Check if ECMP hash argument is valid @@ -66,9 +87,9 @@ def ecmp_hash_validator(ctx, db, ecmp_hash): entry = db.get_all(db.STATE_DB, "{}|{}".format(STATE_SWITCH_CAPABILITY, SW_CAP_KEY)) entry.setdefault(SW_CAP_HASH_FIELD_LIST_KEY, 'N/A') - entry.setdefault(SW_CAP_ECMP_HASH_KEY, 'false') + entry.setdefault(SW_CAP_ECMP_HASH_CAPABLE_KEY, 'false') - if entry[SW_CAP_ECMP_HASH_KEY] == 'false': + if entry[SW_CAP_ECMP_HASH_CAPABLE_KEY] == 'false': raise click.UsageError("Failed to configure {}: operation is not supported".format( get_param_hint(ctx, "ecmp_hash")), ctx ) @@ -106,9 +127,9 @@ def lag_hash_validator(ctx, db, lag_hash): entry = db.get_all(db.STATE_DB, "{}|{}".format(STATE_SWITCH_CAPABILITY, SW_CAP_KEY)) entry.setdefault(SW_CAP_HASH_FIELD_LIST_KEY, 'N/A') - entry.setdefault(SW_CAP_LAG_HASH_KEY, 'false') + entry.setdefault(SW_CAP_LAG_HASH_CAPABLE_KEY, 'false') - if entry[SW_CAP_LAG_HASH_KEY] == 'false': + if entry[SW_CAP_LAG_HASH_CAPABLE_KEY] == 'false': raise click.UsageError("Failed to configure {}: operation is not supported".format( get_param_hint(ctx, "lag_hash")), ctx ) @@ -126,6 +147,72 @@ def lag_hash_validator(ctx, db, lag_hash): for hash_field in lag_hash: click.Choice(cap_list).convert(hash_field, get_param(ctx, "lag_hash"), ctx) + +def ecmp_hash_algorithm_validator(ctx, db, ecmp_hash_algorithm): + """ + Check if ECMP hash algorithm argument is valid + + Args: + ctx: click context + db: State DB connector object + ecmp_hash_algorithm: ECMP hash algorithm + """ + + entry = db.get_all(db.STATE_DB, "{}|{}".format(STATE_SWITCH_CAPABILITY, SW_CAP_KEY)) + + entry.setdefault(SW_CAP_ECMP_HASH_ALGORITHM_KEY, 'N/A') + entry.setdefault(SW_CAP_ECMP_HASH_ALGORITHM_CAPABLE_KEY, 'false') + + if entry[SW_CAP_ECMP_HASH_ALGORITHM_CAPABLE_KEY] == 'false': + raise click.UsageError("Failed to configure {}: operation is not supported".format( + get_param_hint(ctx, "ecmp_hash_algorithm")), ctx + ) + + if not entry[SW_CAP_ECMP_HASH_ALGORITHM_KEY]: + raise click.UsageError("Failed to configure {}: no hash algorithm capabilities".format( + get_param_hint(ctx, "ecmp_hash_algorithm")), ctx + ) + + if entry[SW_CAP_ECMP_HASH_ALGORITHM_KEY] == 'N/A': + return + + cap_list = entry[SW_CAP_ECMP_HASH_ALGORITHM_KEY].split(',') + + click.Choice(cap_list).convert(ecmp_hash_algorithm, get_param(ctx, "ecmp_hash_algorithm"), ctx) + + +def lag_hash_algorithm_validator(ctx, db, lag_hash_algorithm): + """ + Check if LAG hash algorithm argument is valid + + Args: + ctx: click context + db: State DB connector object + lag_hash_algorithm: LAG hash algorithm + """ + + entry = db.get_all(db.STATE_DB, "{}|{}".format(STATE_SWITCH_CAPABILITY, SW_CAP_KEY)) + + entry.setdefault(SW_CAP_LAG_HASH_ALGORITHM_KEY, 'N/A') + entry.setdefault(SW_CAP_LAG_HASH_ALGORITHM_CAPABLE_KEY, 'false') + + if entry[SW_CAP_LAG_HASH_ALGORITHM_CAPABLE_KEY] == 'false': + raise click.UsageError("Failed to configure {}: operation is not supported".format( + get_param_hint(ctx, "lag_hash_algorithm")), ctx + ) + + if not entry[SW_CAP_LAG_HASH_ALGORITHM_KEY]: + raise click.UsageError("Failed to configure {}: no hash algorithm capabilities".format( + get_param_hint(ctx, "lag_hash_algorithm")), ctx + ) + + if entry[SW_CAP_LAG_HASH_ALGORITHM_KEY] == 'N/A': + return + + cap_list = entry[SW_CAP_LAG_HASH_ALGORITHM_KEY].split(',') + + click.Choice(cap_list).convert(lag_hash_algorithm, get_param(ctx, "lag_hash_algorithm"), ctx) + # # Hash DB interface --------------------------------------------------------------------------------------------------- # @@ -258,6 +345,66 @@ def SWITCH_HASH_GLOBAL_lag_hash(ctx, db, lag_hash): ctx.fail(str(err)) +@SWITCH_HASH_GLOBAL.command( + name="ecmp-hash-algorithm" +) +@click.argument( + "ecmp-hash-algorithm", + nargs=1, + required=True, + callback=hash_algorithm_validator, +) +@clicommon.pass_db +@click.pass_context +def SWITCH_HASH_GLOBAL_ecmp_hash_algorithm(ctx, db, ecmp_hash_algorithm): + """ Hash algorithm for hashing packets going through ECMP """ + + ecmp_hash_algorithm_validator(ctx, db.db, ecmp_hash_algorithm) + + table = CFG_SWITCH_HASH + key = SW_HASH_KEY + data = { + "ecmp_hash_algorithm": ecmp_hash_algorithm, + } + + try: + update_entry_validated(db.cfgdb, table, key, data, create_if_not_exists=True) + log.log_notice("Configured switch global ECMP hash algorithm: {}".format(ecmp_hash_algorithm)) + except Exception as e: + log.log_error("Failed to configure switch global ECMP hash algorithm: {}".format(str(e))) + ctx.fail(str(e)) + + +@SWITCH_HASH_GLOBAL.command( + name="lag-hash-algorithm" +) +@click.argument( + "lag-hash-algorithm", + nargs=1, + required=True, + callback=hash_algorithm_validator, +) +@clicommon.pass_db +@click.pass_context +def SWITCH_HASH_GLOBAL_lag_hash_algorithm(ctx, db, lag_hash_algorithm): + """ Hash algorithm for hashing packets going through LAG """ + + lag_hash_algorithm_validator(ctx, db.db, lag_hash_algorithm) + + table = CFG_SWITCH_HASH + key = SW_HASH_KEY + data = { + "lag_hash_algorithm": lag_hash_algorithm, + } + + try: + update_entry_validated(db.cfgdb, table, key, data, create_if_not_exists=True) + log.log_notice("Configured switch global LAG hash algorithm: {}".format(lag_hash_algorithm)) + except Exception as e: + log.log_error("Failed to configure switch global LAG hash algorithm: {}".format(str(e))) + ctx.fail(str(e)) + + def register(cli): """ Register new CLI nodes in root CLI. diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index d39e64ec82..e8e24e5aad 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -4248,34 +4248,130 @@ This command displays switch hash global configuration. show switch-hash global ``` +- Options: + - _-j,--json_: display in JSON format + - Example: ```bash admin@sonic:~$ show switch-hash global - ECMP HASH LAG HASH - ----------------- ----------------- - DST_MAC DST_MAC - SRC_MAC SRC_MAC - ETHERTYPE ETHERTYPE - IP_PROTOCOL IP_PROTOCOL - DST_IP DST_IP - SRC_IP SRC_IP - L4_DST_PORT L4_DST_PORT - L4_SRC_PORT L4_SRC_PORT - INNER_DST_MAC INNER_DST_MAC - INNER_SRC_MAC INNER_SRC_MAC - INNER_ETHERTYPE INNER_ETHERTYPE - INNER_IP_PROTOCOL INNER_IP_PROTOCOL - INNER_DST_IP INNER_DST_IP - INNER_SRC_IP INNER_SRC_IP - INNER_L4_DST_PORT INNER_L4_DST_PORT - INNER_L4_SRC_PORT INNER_L4_SRC_PORT + +--------+-------------------------------------+ + | Hash | Configuration | + +========+=====================================+ + | ECMP | +-------------------+-------------+ | + | | | Hash Field | Algorithm | | + | | |-------------------+-------------| | + | | | DST_MAC | CRC | | + | | | SRC_MAC | | | + | | | ETHERTYPE | | | + | | | IP_PROTOCOL | | | + | | | DST_IP | | | + | | | SRC_IP | | | + | | | L4_DST_PORT | | | + | | | L4_SRC_PORT | | | + | | | INNER_DST_MAC | | | + | | | INNER_SRC_MAC | | | + | | | INNER_ETHERTYPE | | | + | | | INNER_IP_PROTOCOL | | | + | | | INNER_DST_IP | | | + | | | INNER_SRC_IP | | | + | | | INNER_L4_DST_PORT | | | + | | | INNER_L4_SRC_PORT | | | + | | +-------------------+-------------+ | + +--------+-------------------------------------+ + | LAG | +-------------------+-------------+ | + | | | Hash Field | Algorithm | | + | | |-------------------+-------------| | + | | | DST_MAC | CRC | | + | | | SRC_MAC | | | + | | | ETHERTYPE | | | + | | | IP_PROTOCOL | | | + | | | DST_IP | | | + | | | SRC_IP | | | + | | | L4_DST_PORT | | | + | | | L4_SRC_PORT | | | + | | | INNER_DST_MAC | | | + | | | INNER_SRC_MAC | | | + | | | INNER_ETHERTYPE | | | + | | | INNER_IP_PROTOCOL | | | + | | | INNER_DST_IP | | | + | | | INNER_SRC_IP | | | + | | | INNER_L4_DST_PORT | | | + | | | INNER_L4_SRC_PORT | | | + | | +-------------------+-------------+ | + +--------+-------------------------------------+ + ``` + +**show switch-hash capabilities** + +This command displays switch hash capabilities. + +- Usage: + ```bash + show switch-hash capabilities + ``` + +- Options: + - _-j,--json_: display in JSON format + +- Example: + ```bash + admin@sonic:~$ show switch-hash capabilities + +--------+-------------------------------------+ + | Hash | Capabilities | + +========+=====================================+ + | ECMP | +-------------------+-------------+ | + | | | Hash Field | Algorithm | | + | | |-------------------+-------------| | + | | | IN_PORT | CRC | | + | | | DST_MAC | XOR | | + | | | SRC_MAC | RANDOM | | + | | | ETHERTYPE | CRC_32LO | | + | | | VLAN_ID | CRC_32HI | | + | | | IP_PROTOCOL | CRC_CCITT | | + | | | DST_IP | CRC_XOR | | + | | | SRC_IP | | | + | | | L4_DST_PORT | | | + | | | L4_SRC_PORT | | | + | | | INNER_DST_MAC | | | + | | | INNER_SRC_MAC | | | + | | | INNER_ETHERTYPE | | | + | | | INNER_IP_PROTOCOL | | | + | | | INNER_DST_IP | | | + | | | INNER_SRC_IP | | | + | | | INNER_L4_DST_PORT | | | + | | | INNER_L4_SRC_PORT | | | + | | +-------------------+-------------+ | + +--------+-------------------------------------+ + | LAG | +-------------------+-------------+ | + | | | Hash Field | Algorithm | | + | | |-------------------+-------------| | + | | | IN_PORT | CRC | | + | | | DST_MAC | XOR | | + | | | SRC_MAC | RANDOM | | + | | | ETHERTYPE | CRC_32LO | | + | | | VLAN_ID | CRC_32HI | | + | | | IP_PROTOCOL | CRC_CCITT | | + | | | DST_IP | CRC_XOR | | + | | | SRC_IP | | | + | | | L4_DST_PORT | | | + | | | L4_SRC_PORT | | | + | | | INNER_DST_MAC | | | + | | | INNER_SRC_MAC | | | + | | | INNER_ETHERTYPE | | | + | | | INNER_IP_PROTOCOL | | | + | | | INNER_DST_IP | | | + | | | INNER_SRC_IP | | | + | | | INNER_L4_DST_PORT | | | + | | | INNER_L4_SRC_PORT | | | + | | +-------------------+-------------+ | + +--------+-------------------------------------+ ``` ### Hash Config Commands This subsection explains how to configure switch hash. -**config switch-hash global** +**config switch-hash global ecmp/lag hash** This command is used to manage switch hash global configuration. @@ -4326,6 +4422,25 @@ This command is used to manage switch hash global configuration. 'INNER_L4_SRC_PORT' ``` +**config switch-hash global ecmp/lag hash algorithm** + +This command is used to manage switch hash algorithm global configuration. + +- Usage: + ```bash + config switch-hash global ecmp-hash-algorithm + config switch-hash global lag-hash-algorithm + ``` + +- Parameters: + - _hash_algorithm_: hash algorithm for hashing packets going through ECMP/LAG + +- Examples: + ```bash + admin@sonic:~$ config switch-hash global ecmp-hash-algorithm 'CRC' + admin@sonic:~$ config switch-hash global lag-hash-algorithm 'CRC' + ``` + ## Interfaces ### Interface Show Commands diff --git a/show/plugins/sonic-hash.py b/show/plugins/sonic-hash.py index 467b124c32..3744067744 100644 --- a/show/plugins/sonic-hash.py +++ b/show/plugins/sonic-hash.py @@ -4,14 +4,19 @@ import click import tabulate +import json import utilities_common.cli as clicommon from utilities_common.switch_hash import ( CFG_SWITCH_HASH, STATE_SWITCH_CAPABILITY, SW_CAP_HASH_FIELD_LIST_KEY, - SW_CAP_ECMP_HASH_KEY, - SW_CAP_LAG_HASH_KEY, + SW_CAP_ECMP_HASH_ALGORITHM_KEY, + SW_CAP_LAG_HASH_ALGORITHM_KEY, + SW_CAP_ECMP_HASH_CAPABLE_KEY, + SW_CAP_LAG_HASH_CAPABLE_KEY, + SW_CAP_ECMP_HASH_ALGORITHM_CAPABLE_KEY, + SW_CAP_LAG_HASH_ALGORITHM_CAPABLE_KEY, SW_HASH_KEY, SW_CAP_KEY, ) @@ -53,24 +58,52 @@ def SWITCH_HASH(): @SWITCH_HASH.command( name="global" ) +@click.option( + "-j", "--json", "json_format", + help="Display in JSON format", + is_flag=True, + default=False +) @clicommon.pass_db -def SWITCH_HASH_GLOBAL(db): +@click.pass_context +def SWITCH_HASH_GLOBAL(ctx, db, json_format): """ Show switch hash global configuration """ header = [ - "ECMP HASH", - "LAG HASH", + "Hash", + "Configuration", ] body = [] + sub_header = [ + "Hash Field", + "Algorithm" + ] + ecmp_body = [] + lag_body = [] + table = db.cfgdb.get_table(CFG_SWITCH_HASH) entry = table.get(SW_HASH_KEY, {}) if not entry: - click.echo(tabulate.tabulate(body, header)) - return + click.echo("No configuration is present in CONFIG DB") + ctx.exit(0) - row = [ + if json_format: + json_dict = { + "ecmp": { + "hash_field": entry["ecmp_hash"] if "ecmp_hash" in entry else "N/A", + "algorithm": entry["ecmp_hash_algorithm"] if "ecmp_hash_algorithm" in entry else "N/A" + }, + "lag": { + "hash_field": entry["lag_hash"] if "lag_hash" in entry else "N/A", + "algorithm": entry["lag_hash_algorithm"] if "lag_hash_algorithm" in entry else "N/A" + } + } + click.echo(json.dumps(json_dict, indent=4)) + ctx.exit(0) + + ecmp_row = [ format_attr_value( entry, { @@ -78,6 +111,17 @@ def SWITCH_HASH_GLOBAL(db): 'is-leaf-list': True } ), + format_attr_value( + entry, + { + 'name': 'ecmp_hash_algorithm', + 'is-leaf-list': False + } + ), + ] + ecmp_body.append(ecmp_row) + + lag_row = [ format_attr_value( entry, { @@ -85,59 +129,138 @@ def SWITCH_HASH_GLOBAL(db): 'is-leaf-list': True } ), + format_attr_value( + entry, + { + 'name': 'lag_hash_algorithm', + 'is-leaf-list': False + } + ), ] - body.append(row) + lag_body.append(lag_row) + + body.append(["ECMP", tabulate.tabulate(ecmp_body, sub_header, "psql")]) + body.append(["LAG", tabulate.tabulate(lag_body, sub_header, "psql")]) - click.echo(tabulate.tabulate(body, header)) + click.echo(tabulate.tabulate(body, header, "grid")) @SWITCH_HASH.command( name="capabilities" ) +@click.option( + "-j", "--json", "json_format", + help="Display in JSON format", + is_flag=True, + default=False +) @clicommon.pass_db -def SWITCH_HASH_CAPABILITIES(db): +@click.pass_context +def SWITCH_HASH_CAPABILITIES(ctx, db, json_format): """ Show switch hash capabilities """ header = [ - "ECMP HASH", - "LAG HASH", + "Hash", + "Capabilities", ] body = [] + sub_header = [ + "Hash Field", + "Algorithm" + ] + ecmp_body = [] + lag_body = [] + entry = db.db.get_all(db.db.STATE_DB, "{}|{}".format(STATE_SWITCH_CAPABILITY, SW_CAP_KEY)) if not entry: - click.echo(tabulate.tabulate(body, header)) - return + ctx.fail("No data is present in STATE DB") entry.setdefault(SW_CAP_HASH_FIELD_LIST_KEY, 'N/A') - entry.setdefault(SW_CAP_ECMP_HASH_KEY, 'false') - entry.setdefault(SW_CAP_LAG_HASH_KEY, 'false') + entry.setdefault(SW_CAP_ECMP_HASH_ALGORITHM_KEY, 'N/A') + entry.setdefault(SW_CAP_LAG_HASH_ALGORITHM_KEY, 'N/A') + entry.setdefault(SW_CAP_ECMP_HASH_CAPABLE_KEY, 'false') + entry.setdefault(SW_CAP_LAG_HASH_CAPABLE_KEY, 'false') + entry.setdefault(SW_CAP_ECMP_HASH_ALGORITHM_CAPABLE_KEY, 'false') + entry.setdefault(SW_CAP_LAG_HASH_ALGORITHM_CAPABLE_KEY, 'false') if not entry[SW_CAP_HASH_FIELD_LIST_KEY]: entry[SW_CAP_HASH_FIELD_LIST_KEY] = "no capabilities" + if not entry[SW_CAP_ECMP_HASH_ALGORITHM_KEY]: + entry[SW_CAP_ECMP_HASH_ALGORITHM_KEY] = "no capabilities" + + if not entry[SW_CAP_LAG_HASH_ALGORITHM_KEY]: + entry[SW_CAP_LAG_HASH_ALGORITHM_KEY] = "no capabilities" + + if json_format: + if entry[SW_CAP_HASH_FIELD_LIST_KEY] not in ["N/A", "no capabilities"]: + entry[SW_CAP_HASH_FIELD_LIST_KEY] = entry[SW_CAP_HASH_FIELD_LIST_KEY].split(',') + + if entry[SW_CAP_ECMP_HASH_ALGORITHM_KEY] not in ["N/A", "no capabilities"]: + entry[SW_CAP_ECMP_HASH_ALGORITHM_KEY] = entry[SW_CAP_ECMP_HASH_ALGORITHM_KEY].split(',') + + if entry[SW_CAP_LAG_HASH_ALGORITHM_KEY] not in ["N/A", "no capabilities"]: + entry[SW_CAP_LAG_HASH_ALGORITHM_KEY] = entry[SW_CAP_LAG_HASH_ALGORITHM_KEY].split(',') + + json_dict = { + "ecmp": { + "hash_field": entry[SW_CAP_HASH_FIELD_LIST_KEY] if entry[SW_CAP_ECMP_HASH_CAPABLE_KEY] == 'true' else 'not supported', + "algorithm": entry[SW_CAP_ECMP_HASH_ALGORITHM_KEY] if entry[SW_CAP_ECMP_HASH_ALGORITHM_CAPABLE_KEY] == 'true' else 'not supported' + }, + "lag": { + "hash_field": entry[SW_CAP_HASH_FIELD_LIST_KEY] if entry[SW_CAP_LAG_HASH_CAPABLE_KEY] == 'true' else 'not supported', + "algorithm": entry[SW_CAP_LAG_HASH_ALGORITHM_KEY] if entry[SW_CAP_LAG_HASH_ALGORITHM_CAPABLE_KEY] == 'true' else 'not supported' + } + } + click.echo(json.dumps(json_dict, indent=4)) + ctx.exit(0) + entry[SW_CAP_HASH_FIELD_LIST_KEY] = entry[SW_CAP_HASH_FIELD_LIST_KEY].split(',') + entry[SW_CAP_ECMP_HASH_ALGORITHM_KEY] = entry[SW_CAP_ECMP_HASH_ALGORITHM_KEY].split(',') + entry[SW_CAP_LAG_HASH_ALGORITHM_KEY] = entry[SW_CAP_LAG_HASH_ALGORITHM_KEY].split(',') - row = [ + ecmp_row = [ format_attr_value( entry, { 'name': SW_CAP_HASH_FIELD_LIST_KEY, 'is-leaf-list': True } - ) if entry[SW_CAP_ECMP_HASH_KEY] == 'true' else 'not supported', + ) if entry[SW_CAP_ECMP_HASH_CAPABLE_KEY] == 'true' else 'not supported', + format_attr_value( + entry, + { + 'name': SW_CAP_ECMP_HASH_ALGORITHM_KEY, + 'is-leaf-list': True + } + ) if entry[SW_CAP_ECMP_HASH_ALGORITHM_CAPABLE_KEY] == 'true' else 'not supported', + ] + ecmp_body.append(ecmp_row) + + lag_row = [ format_attr_value( entry, { 'name': SW_CAP_HASH_FIELD_LIST_KEY, 'is-leaf-list': True } - ) if entry[SW_CAP_LAG_HASH_KEY] == 'true' else 'not supported', + ) if entry[SW_CAP_LAG_HASH_CAPABLE_KEY] == 'true' else 'not supported', + format_attr_value( + entry, + { + 'name': SW_CAP_LAG_HASH_ALGORITHM_KEY, + 'is-leaf-list': True + } + ) if entry[SW_CAP_LAG_HASH_ALGORITHM_CAPABLE_KEY] == 'true' else 'not supported', ] - body.append(row) + lag_body.append(lag_row) + + body.append(["ECMP", tabulate.tabulate(ecmp_body, sub_header, "psql")]) + body.append(["LAG", tabulate.tabulate(lag_body, sub_header, "psql")]) - click.echo(tabulate.tabulate(body, header)) + click.echo(tabulate.tabulate(body, header, "grid")) def register(cli): diff --git a/tests/hash_input/assert_show_output.py b/tests/hash_input/assert_show_output.py index 80edc1cf17..8a1e1fa2dc 100644 --- a/tests/hash_input/assert_show_output.py +++ b/tests/hash_input/assert_show_output.py @@ -3,156 +3,579 @@ """ show_hash_empty="""\ -ECMP HASH LAG HASH ------------ ---------- +No configuration is present in CONFIG DB """ show_hash_ecmp="""\ -ECMP HASH LAG HASH ------------------ ---------- -DST_MAC N/A -SRC_MAC -ETHERTYPE -IP_PROTOCOL -DST_IP -SRC_IP -L4_DST_PORT -L4_SRC_PORT -INNER_DST_MAC -INNER_SRC_MAC -INNER_ETHERTYPE -INNER_IP_PROTOCOL -INNER_DST_IP -INNER_SRC_IP -INNER_L4_DST_PORT -INNER_L4_SRC_PORT ++--------+-------------------------------------+ +| Hash | Configuration | ++========+=====================================+ +| ECMP | +-------------------+-------------+ | +| | | Hash Field | Algorithm | | +| | |-------------------+-------------| | +| | | DST_MAC | CRC | | +| | | SRC_MAC | | | +| | | ETHERTYPE | | | +| | | IP_PROTOCOL | | | +| | | DST_IP | | | +| | | SRC_IP | | | +| | | L4_DST_PORT | | | +| | | L4_SRC_PORT | | | +| | | INNER_DST_MAC | | | +| | | INNER_SRC_MAC | | | +| | | INNER_ETHERTYPE | | | +| | | INNER_IP_PROTOCOL | | | +| | | INNER_DST_IP | | | +| | | INNER_SRC_IP | | | +| | | INNER_L4_DST_PORT | | | +| | | INNER_L4_SRC_PORT | | | +| | +-------------------+-------------+ | ++--------+-------------------------------------+ +| LAG | +--------------+-------------+ | +| | | Hash Field | Algorithm | | +| | |--------------+-------------| | +| | | N/A | N/A | | +| | +--------------+-------------+ | ++--------+-------------------------------------+ +""" +show_hash_ecmp_json="""\ +{ + "ecmp": { + "hash_field": [ + "DST_MAC", + "SRC_MAC", + "ETHERTYPE", + "IP_PROTOCOL", + "DST_IP", + "SRC_IP", + "L4_DST_PORT", + "L4_SRC_PORT", + "INNER_DST_MAC", + "INNER_SRC_MAC", + "INNER_ETHERTYPE", + "INNER_IP_PROTOCOL", + "INNER_DST_IP", + "INNER_SRC_IP", + "INNER_L4_DST_PORT", + "INNER_L4_SRC_PORT" + ], + "algorithm": "CRC" + }, + "lag": { + "hash_field": "N/A", + "algorithm": "N/A" + } +} """ show_hash_lag="""\ -ECMP HASH LAG HASH ------------ ----------------- -N/A DST_MAC - SRC_MAC - ETHERTYPE - IP_PROTOCOL - DST_IP - SRC_IP - L4_DST_PORT - L4_SRC_PORT - INNER_DST_MAC - INNER_SRC_MAC - INNER_ETHERTYPE - INNER_IP_PROTOCOL - INNER_DST_IP - INNER_SRC_IP - INNER_L4_DST_PORT - INNER_L4_SRC_PORT ++--------+-------------------------------------+ +| Hash | Configuration | ++========+=====================================+ +| ECMP | +--------------+-------------+ | +| | | Hash Field | Algorithm | | +| | |--------------+-------------| | +| | | N/A | N/A | | +| | +--------------+-------------+ | ++--------+-------------------------------------+ +| LAG | +-------------------+-------------+ | +| | | Hash Field | Algorithm | | +| | |-------------------+-------------| | +| | | DST_MAC | XOR | | +| | | SRC_MAC | | | +| | | ETHERTYPE | | | +| | | IP_PROTOCOL | | | +| | | DST_IP | | | +| | | SRC_IP | | | +| | | L4_DST_PORT | | | +| | | L4_SRC_PORT | | | +| | | INNER_DST_MAC | | | +| | | INNER_SRC_MAC | | | +| | | INNER_ETHERTYPE | | | +| | | INNER_IP_PROTOCOL | | | +| | | INNER_DST_IP | | | +| | | INNER_SRC_IP | | | +| | | INNER_L4_DST_PORT | | | +| | | INNER_L4_SRC_PORT | | | +| | +-------------------+-------------+ | ++--------+-------------------------------------+ +""" +show_hash_lag_json="""\ +{ + "ecmp": { + "hash_field": "N/A", + "algorithm": "N/A" + }, + "lag": { + "hash_field": [ + "DST_MAC", + "SRC_MAC", + "ETHERTYPE", + "IP_PROTOCOL", + "DST_IP", + "SRC_IP", + "L4_DST_PORT", + "L4_SRC_PORT", + "INNER_DST_MAC", + "INNER_SRC_MAC", + "INNER_ETHERTYPE", + "INNER_IP_PROTOCOL", + "INNER_DST_IP", + "INNER_SRC_IP", + "INNER_L4_DST_PORT", + "INNER_L4_SRC_PORT" + ], + "algorithm": "XOR" + } +} """ show_hash_ecmp_and_lag="""\ -ECMP HASH LAG HASH ------------------ ----------------- -DST_MAC DST_MAC -SRC_MAC SRC_MAC -ETHERTYPE ETHERTYPE -IP_PROTOCOL IP_PROTOCOL -DST_IP DST_IP -SRC_IP SRC_IP -L4_DST_PORT L4_DST_PORT -L4_SRC_PORT L4_SRC_PORT -INNER_DST_MAC INNER_DST_MAC -INNER_SRC_MAC INNER_SRC_MAC -INNER_ETHERTYPE INNER_ETHERTYPE -INNER_IP_PROTOCOL INNER_IP_PROTOCOL -INNER_DST_IP INNER_DST_IP -INNER_SRC_IP INNER_SRC_IP -INNER_L4_DST_PORT INNER_L4_DST_PORT -INNER_L4_SRC_PORT INNER_L4_SRC_PORT ++--------+-------------------------------------+ +| Hash | Configuration | ++========+=====================================+ +| ECMP | +-------------------+-------------+ | +| | | Hash Field | Algorithm | | +| | |-------------------+-------------| | +| | | DST_MAC | CRC | | +| | | SRC_MAC | | | +| | | ETHERTYPE | | | +| | | IP_PROTOCOL | | | +| | | DST_IP | | | +| | | SRC_IP | | | +| | | L4_DST_PORT | | | +| | | L4_SRC_PORT | | | +| | | INNER_DST_MAC | | | +| | | INNER_SRC_MAC | | | +| | | INNER_ETHERTYPE | | | +| | | INNER_IP_PROTOCOL | | | +| | | INNER_DST_IP | | | +| | | INNER_SRC_IP | | | +| | | INNER_L4_DST_PORT | | | +| | | INNER_L4_SRC_PORT | | | +| | +-------------------+-------------+ | ++--------+-------------------------------------+ +| LAG | +-------------------+-------------+ | +| | | Hash Field | Algorithm | | +| | |-------------------+-------------| | +| | | DST_MAC | XOR | | +| | | SRC_MAC | | | +| | | ETHERTYPE | | | +| | | IP_PROTOCOL | | | +| | | DST_IP | | | +| | | SRC_IP | | | +| | | L4_DST_PORT | | | +| | | L4_SRC_PORT | | | +| | | INNER_DST_MAC | | | +| | | INNER_SRC_MAC | | | +| | | INNER_ETHERTYPE | | | +| | | INNER_IP_PROTOCOL | | | +| | | INNER_DST_IP | | | +| | | INNER_SRC_IP | | | +| | | INNER_L4_DST_PORT | | | +| | | INNER_L4_SRC_PORT | | | +| | +-------------------+-------------+ | ++--------+-------------------------------------+ +""" +show_hash_ecmp_and_lag_json="""\ +{ + "ecmp": { + "hash_field": [ + "DST_MAC", + "SRC_MAC", + "ETHERTYPE", + "IP_PROTOCOL", + "DST_IP", + "SRC_IP", + "L4_DST_PORT", + "L4_SRC_PORT", + "INNER_DST_MAC", + "INNER_SRC_MAC", + "INNER_ETHERTYPE", + "INNER_IP_PROTOCOL", + "INNER_DST_IP", + "INNER_SRC_IP", + "INNER_L4_DST_PORT", + "INNER_L4_SRC_PORT" + ], + "algorithm": "CRC" + }, + "lag": { + "hash_field": [ + "DST_MAC", + "SRC_MAC", + "ETHERTYPE", + "IP_PROTOCOL", + "DST_IP", + "SRC_IP", + "L4_DST_PORT", + "L4_SRC_PORT", + "INNER_DST_MAC", + "INNER_SRC_MAC", + "INNER_ETHERTYPE", + "INNER_IP_PROTOCOL", + "INNER_DST_IP", + "INNER_SRC_IP", + "INNER_L4_DST_PORT", + "INNER_L4_SRC_PORT" + ], + "algorithm": "XOR" + } +} """ show_hash_capabilities_no="""\ -ECMP HASH LAG HASH ---------------- --------------- -no capabilities no capabilities ++--------+---------------------------------------+ +| Hash | Capabilities | ++========+=======================================+ +| ECMP | +-----------------+-----------------+ | +| | | Hash Field | Algorithm | | +| | |-----------------+-----------------| | +| | | no capabilities | no capabilities | | +| | +-----------------+-----------------+ | ++--------+---------------------------------------+ +| LAG | +-----------------+-----------------+ | +| | | Hash Field | Algorithm | | +| | |-----------------+-----------------| | +| | | no capabilities | no capabilities | | +| | +-----------------+-----------------+ | ++--------+---------------------------------------+ +""" +show_hash_capabilities_no_json="""\ +{ + "ecmp": { + "hash_field": "no capabilities", + "algorithm": "no capabilities" + }, + "lag": { + "hash_field": "no capabilities", + "algorithm": "no capabilities" + } +} """ show_hash_capabilities_na="""\ -ECMP HASH LAG HASH ------------ ---------- -N/A N/A ++--------+--------------------------------+ +| Hash | Capabilities | ++========+================================+ +| ECMP | +--------------+-------------+ | +| | | Hash Field | Algorithm | | +| | |--------------+-------------| | +| | | N/A | N/A | | +| | +--------------+-------------+ | ++--------+--------------------------------+ +| LAG | +--------------+-------------+ | +| | | Hash Field | Algorithm | | +| | |--------------+-------------| | +| | | N/A | N/A | | +| | +--------------+-------------+ | ++--------+--------------------------------+ +""" +show_hash_capabilities_na_json="""\ +{ + "ecmp": { + "hash_field": "N/A", + "algorithm": "N/A" + }, + "lag": { + "hash_field": "N/A", + "algorithm": "N/A" + } +} """ show_hash_capabilities_empty="""\ -ECMP HASH LAG HASH -------------- ------------- -not supported not supported ++--------+-----------------------------------+ +| Hash | Capabilities | ++========+===================================+ +| ECMP | +---------------+---------------+ | +| | | Hash Field | Algorithm | | +| | |---------------+---------------| | +| | | not supported | not supported | | +| | +---------------+---------------+ | ++--------+-----------------------------------+ +| LAG | +---------------+---------------+ | +| | | Hash Field | Algorithm | | +| | |---------------+---------------| | +| | | not supported | not supported | | +| | +---------------+---------------+ | ++--------+-----------------------------------+ +""" +show_hash_capabilities_empty_json="""\ +{ + "ecmp": { + "hash_field": "not supported", + "algorithm": "not supported" + }, + "lag": { + "hash_field": "not supported", + "algorithm": "not supported" + } +} """ show_hash_capabilities_ecmp="""\ -ECMP HASH LAG HASH ------------------ ------------- -IN_PORT not supported -DST_MAC -SRC_MAC -ETHERTYPE -VLAN_ID -IP_PROTOCOL -DST_IP -SRC_IP -L4_DST_PORT -L4_SRC_PORT -INNER_DST_MAC -INNER_SRC_MAC -INNER_ETHERTYPE -INNER_IP_PROTOCOL -INNER_DST_IP -INNER_SRC_IP -INNER_L4_DST_PORT -INNER_L4_SRC_PORT ++--------+-------------------------------------+ +| Hash | Capabilities | ++========+=====================================+ +| ECMP | +-------------------+-------------+ | +| | | Hash Field | Algorithm | | +| | |-------------------+-------------| | +| | | IN_PORT | CRC | | +| | | DST_MAC | XOR | | +| | | SRC_MAC | RANDOM | | +| | | ETHERTYPE | CRC_32LO | | +| | | VLAN_ID | CRC_32HI | | +| | | IP_PROTOCOL | CRC_CCITT | | +| | | DST_IP | CRC_XOR | | +| | | SRC_IP | | | +| | | L4_DST_PORT | | | +| | | L4_SRC_PORT | | | +| | | INNER_DST_MAC | | | +| | | INNER_SRC_MAC | | | +| | | INNER_ETHERTYPE | | | +| | | INNER_IP_PROTOCOL | | | +| | | INNER_DST_IP | | | +| | | INNER_SRC_IP | | | +| | | INNER_L4_DST_PORT | | | +| | | INNER_L4_SRC_PORT | | | +| | +-------------------+-------------+ | ++--------+-------------------------------------+ +| LAG | +---------------+---------------+ | +| | | Hash Field | Algorithm | | +| | |---------------+---------------| | +| | | not supported | not supported | | +| | +---------------+---------------+ | ++--------+-------------------------------------+ +""" +show_hash_capabilities_ecmp_json="""\ +{ + "ecmp": { + "hash_field": [ + "IN_PORT", + "DST_MAC", + "SRC_MAC", + "ETHERTYPE", + "VLAN_ID", + "IP_PROTOCOL", + "DST_IP", + "SRC_IP", + "L4_DST_PORT", + "L4_SRC_PORT", + "INNER_DST_MAC", + "INNER_SRC_MAC", + "INNER_ETHERTYPE", + "INNER_IP_PROTOCOL", + "INNER_DST_IP", + "INNER_SRC_IP", + "INNER_L4_DST_PORT", + "INNER_L4_SRC_PORT" + ], + "algorithm": [ + "CRC", + "XOR", + "RANDOM", + "CRC_32LO", + "CRC_32HI", + "CRC_CCITT", + "CRC_XOR" + ] + }, + "lag": { + "hash_field": "not supported", + "algorithm": "not supported" + } +} """ show_hash_capabilities_lag="""\ -ECMP HASH LAG HASH -------------- ----------------- -not supported IN_PORT - DST_MAC - SRC_MAC - ETHERTYPE - VLAN_ID - IP_PROTOCOL - DST_IP - SRC_IP - L4_DST_PORT - L4_SRC_PORT - INNER_DST_MAC - INNER_SRC_MAC - INNER_ETHERTYPE - INNER_IP_PROTOCOL - INNER_DST_IP - INNER_SRC_IP - INNER_L4_DST_PORT - INNER_L4_SRC_PORT ++--------+-------------------------------------+ +| Hash | Capabilities | ++========+=====================================+ +| ECMP | +---------------+---------------+ | +| | | Hash Field | Algorithm | | +| | |---------------+---------------| | +| | | not supported | not supported | | +| | +---------------+---------------+ | ++--------+-------------------------------------+ +| LAG | +-------------------+-------------+ | +| | | Hash Field | Algorithm | | +| | |-------------------+-------------| | +| | | IN_PORT | CRC | | +| | | DST_MAC | XOR | | +| | | SRC_MAC | RANDOM | | +| | | ETHERTYPE | CRC_32LO | | +| | | VLAN_ID | CRC_32HI | | +| | | IP_PROTOCOL | CRC_CCITT | | +| | | DST_IP | CRC_XOR | | +| | | SRC_IP | | | +| | | L4_DST_PORT | | | +| | | L4_SRC_PORT | | | +| | | INNER_DST_MAC | | | +| | | INNER_SRC_MAC | | | +| | | INNER_ETHERTYPE | | | +| | | INNER_IP_PROTOCOL | | | +| | | INNER_DST_IP | | | +| | | INNER_SRC_IP | | | +| | | INNER_L4_DST_PORT | | | +| | | INNER_L4_SRC_PORT | | | +| | +-------------------+-------------+ | ++--------+-------------------------------------+ +""" +show_hash_capabilities_lag_json="""\ +{ + "ecmp": { + "hash_field": "not supported", + "algorithm": "not supported" + }, + "lag": { + "hash_field": [ + "IN_PORT", + "DST_MAC", + "SRC_MAC", + "ETHERTYPE", + "VLAN_ID", + "IP_PROTOCOL", + "DST_IP", + "SRC_IP", + "L4_DST_PORT", + "L4_SRC_PORT", + "INNER_DST_MAC", + "INNER_SRC_MAC", + "INNER_ETHERTYPE", + "INNER_IP_PROTOCOL", + "INNER_DST_IP", + "INNER_SRC_IP", + "INNER_L4_DST_PORT", + "INNER_L4_SRC_PORT" + ], + "algorithm": [ + "CRC", + "XOR", + "RANDOM", + "CRC_32LO", + "CRC_32HI", + "CRC_CCITT", + "CRC_XOR" + ] + } +} """ show_hash_capabilities_ecmp_and_lag="""\ -ECMP HASH LAG HASH ------------------ ----------------- -IN_PORT IN_PORT -DST_MAC DST_MAC -SRC_MAC SRC_MAC -ETHERTYPE ETHERTYPE -VLAN_ID VLAN_ID -IP_PROTOCOL IP_PROTOCOL -DST_IP DST_IP -SRC_IP SRC_IP -L4_DST_PORT L4_DST_PORT -L4_SRC_PORT L4_SRC_PORT -INNER_DST_MAC INNER_DST_MAC -INNER_SRC_MAC INNER_SRC_MAC -INNER_ETHERTYPE INNER_ETHERTYPE -INNER_IP_PROTOCOL INNER_IP_PROTOCOL -INNER_DST_IP INNER_DST_IP -INNER_SRC_IP INNER_SRC_IP -INNER_L4_DST_PORT INNER_L4_DST_PORT -INNER_L4_SRC_PORT INNER_L4_SRC_PORT ++--------+-------------------------------------+ +| Hash | Capabilities | ++========+=====================================+ +| ECMP | +-------------------+-------------+ | +| | | Hash Field | Algorithm | | +| | |-------------------+-------------| | +| | | IN_PORT | CRC | | +| | | DST_MAC | XOR | | +| | | SRC_MAC | RANDOM | | +| | | ETHERTYPE | CRC_32LO | | +| | | VLAN_ID | CRC_32HI | | +| | | IP_PROTOCOL | CRC_CCITT | | +| | | DST_IP | CRC_XOR | | +| | | SRC_IP | | | +| | | L4_DST_PORT | | | +| | | L4_SRC_PORT | | | +| | | INNER_DST_MAC | | | +| | | INNER_SRC_MAC | | | +| | | INNER_ETHERTYPE | | | +| | | INNER_IP_PROTOCOL | | | +| | | INNER_DST_IP | | | +| | | INNER_SRC_IP | | | +| | | INNER_L4_DST_PORT | | | +| | | INNER_L4_SRC_PORT | | | +| | +-------------------+-------------+ | ++--------+-------------------------------------+ +| LAG | +-------------------+-------------+ | +| | | Hash Field | Algorithm | | +| | |-------------------+-------------| | +| | | IN_PORT | CRC | | +| | | DST_MAC | XOR | | +| | | SRC_MAC | RANDOM | | +| | | ETHERTYPE | CRC_32LO | | +| | | VLAN_ID | CRC_32HI | | +| | | IP_PROTOCOL | CRC_CCITT | | +| | | DST_IP | CRC_XOR | | +| | | SRC_IP | | | +| | | L4_DST_PORT | | | +| | | L4_SRC_PORT | | | +| | | INNER_DST_MAC | | | +| | | INNER_SRC_MAC | | | +| | | INNER_ETHERTYPE | | | +| | | INNER_IP_PROTOCOL | | | +| | | INNER_DST_IP | | | +| | | INNER_SRC_IP | | | +| | | INNER_L4_DST_PORT | | | +| | | INNER_L4_SRC_PORT | | | +| | +-------------------+-------------+ | ++--------+-------------------------------------+ +""" +show_hash_capabilities_ecmp_and_lag_json="""\ +{ + "ecmp": { + "hash_field": [ + "IN_PORT", + "DST_MAC", + "SRC_MAC", + "ETHERTYPE", + "VLAN_ID", + "IP_PROTOCOL", + "DST_IP", + "SRC_IP", + "L4_DST_PORT", + "L4_SRC_PORT", + "INNER_DST_MAC", + "INNER_SRC_MAC", + "INNER_ETHERTYPE", + "INNER_IP_PROTOCOL", + "INNER_DST_IP", + "INNER_SRC_IP", + "INNER_L4_DST_PORT", + "INNER_L4_SRC_PORT" + ], + "algorithm": [ + "CRC", + "XOR", + "RANDOM", + "CRC_32LO", + "CRC_32HI", + "CRC_CCITT", + "CRC_XOR" + ] + }, + "lag": { + "hash_field": [ + "IN_PORT", + "DST_MAC", + "SRC_MAC", + "ETHERTYPE", + "VLAN_ID", + "IP_PROTOCOL", + "DST_IP", + "SRC_IP", + "L4_DST_PORT", + "L4_SRC_PORT", + "INNER_DST_MAC", + "INNER_SRC_MAC", + "INNER_ETHERTYPE", + "INNER_IP_PROTOCOL", + "INNER_DST_IP", + "INNER_SRC_IP", + "INNER_L4_DST_PORT", + "INNER_L4_SRC_PORT" + ], + "algorithm": [ + "CRC", + "XOR", + "RANDOM", + "CRC_32LO", + "CRC_32HI", + "CRC_CCITT", + "CRC_XOR" + ] + } +} """ diff --git a/tests/hash_input/mock_config/ecmp.json b/tests/hash_input/mock_config/ecmp.json index 329af0be66..a488aca226 100644 --- a/tests/hash_input/mock_config/ecmp.json +++ b/tests/hash_input/mock_config/ecmp.json @@ -1,5 +1,6 @@ { "SWITCH_HASH|GLOBAL": { - "ecmp_hash@": "DST_MAC,SRC_MAC,ETHERTYPE,IP_PROTOCOL,DST_IP,SRC_IP,L4_DST_PORT,L4_SRC_PORT,INNER_DST_MAC,INNER_SRC_MAC,INNER_ETHERTYPE,INNER_IP_PROTOCOL,INNER_DST_IP,INNER_SRC_IP,INNER_L4_DST_PORT,INNER_L4_SRC_PORT" + "ecmp_hash@": "DST_MAC,SRC_MAC,ETHERTYPE,IP_PROTOCOL,DST_IP,SRC_IP,L4_DST_PORT,L4_SRC_PORT,INNER_DST_MAC,INNER_SRC_MAC,INNER_ETHERTYPE,INNER_IP_PROTOCOL,INNER_DST_IP,INNER_SRC_IP,INNER_L4_DST_PORT,INNER_L4_SRC_PORT", + "ecmp_hash_algorithm": "CRC" } } diff --git a/tests/hash_input/mock_config/ecmp_and_lag.json b/tests/hash_input/mock_config/ecmp_and_lag.json index caa095ac4e..e1c0018e4f 100644 --- a/tests/hash_input/mock_config/ecmp_and_lag.json +++ b/tests/hash_input/mock_config/ecmp_and_lag.json @@ -1,6 +1,8 @@ { "SWITCH_HASH|GLOBAL": { "ecmp_hash@": "DST_MAC,SRC_MAC,ETHERTYPE,IP_PROTOCOL,DST_IP,SRC_IP,L4_DST_PORT,L4_SRC_PORT,INNER_DST_MAC,INNER_SRC_MAC,INNER_ETHERTYPE,INNER_IP_PROTOCOL,INNER_DST_IP,INNER_SRC_IP,INNER_L4_DST_PORT,INNER_L4_SRC_PORT", - "lag_hash@": "DST_MAC,SRC_MAC,ETHERTYPE,IP_PROTOCOL,DST_IP,SRC_IP,L4_DST_PORT,L4_SRC_PORT,INNER_DST_MAC,INNER_SRC_MAC,INNER_ETHERTYPE,INNER_IP_PROTOCOL,INNER_DST_IP,INNER_SRC_IP,INNER_L4_DST_PORT,INNER_L4_SRC_PORT" + "lag_hash@": "DST_MAC,SRC_MAC,ETHERTYPE,IP_PROTOCOL,DST_IP,SRC_IP,L4_DST_PORT,L4_SRC_PORT,INNER_DST_MAC,INNER_SRC_MAC,INNER_ETHERTYPE,INNER_IP_PROTOCOL,INNER_DST_IP,INNER_SRC_IP,INNER_L4_DST_PORT,INNER_L4_SRC_PORT", + "ecmp_hash_algorithm": "CRC", + "lag_hash_algorithm": "XOR" } } diff --git a/tests/hash_input/mock_config/lag.json b/tests/hash_input/mock_config/lag.json index 69b63bd0ab..14f262810c 100644 --- a/tests/hash_input/mock_config/lag.json +++ b/tests/hash_input/mock_config/lag.json @@ -1,5 +1,6 @@ { "SWITCH_HASH|GLOBAL": { - "lag_hash@": "DST_MAC,SRC_MAC,ETHERTYPE,IP_PROTOCOL,DST_IP,SRC_IP,L4_DST_PORT,L4_SRC_PORT,INNER_DST_MAC,INNER_SRC_MAC,INNER_ETHERTYPE,INNER_IP_PROTOCOL,INNER_DST_IP,INNER_SRC_IP,INNER_L4_DST_PORT,INNER_L4_SRC_PORT" + "lag_hash@": "DST_MAC,SRC_MAC,ETHERTYPE,IP_PROTOCOL,DST_IP,SRC_IP,L4_DST_PORT,L4_SRC_PORT,INNER_DST_MAC,INNER_SRC_MAC,INNER_ETHERTYPE,INNER_IP_PROTOCOL,INNER_DST_IP,INNER_SRC_IP,INNER_L4_DST_PORT,INNER_L4_SRC_PORT", + "lag_hash_algorithm": "XOR" } } diff --git a/tests/hash_input/mock_state/ecmp.json b/tests/hash_input/mock_state/ecmp.json index e9f7cf436d..9e79484d28 100644 --- a/tests/hash_input/mock_state/ecmp.json +++ b/tests/hash_input/mock_state/ecmp.json @@ -2,6 +2,10 @@ "SWITCH_CAPABILITY|switch": { "ECMP_HASH_CAPABLE": "true", "LAG_HASH_CAPABLE": "false", - "HASH|NATIVE_HASH_FIELD_LIST": "IN_PORT,DST_MAC,SRC_MAC,ETHERTYPE,VLAN_ID,IP_PROTOCOL,DST_IP,SRC_IP,L4_DST_PORT,L4_SRC_PORT,INNER_DST_MAC,INNER_SRC_MAC,INNER_ETHERTYPE,INNER_IP_PROTOCOL,INNER_DST_IP,INNER_SRC_IP,INNER_L4_DST_PORT,INNER_L4_SRC_PORT" + "ECMP_HASH_ALGORITHM_CAPABLE": "true", + "LAG_HASH_ALGORITHM_CAPABLE": "false", + "HASH|NATIVE_HASH_FIELD_LIST": "IN_PORT,DST_MAC,SRC_MAC,ETHERTYPE,VLAN_ID,IP_PROTOCOL,DST_IP,SRC_IP,L4_DST_PORT,L4_SRC_PORT,INNER_DST_MAC,INNER_SRC_MAC,INNER_ETHERTYPE,INNER_IP_PROTOCOL,INNER_DST_IP,INNER_SRC_IP,INNER_L4_DST_PORT,INNER_L4_SRC_PORT", + "ECMP_HASH_ALGORITHM": "CRC,XOR,RANDOM,CRC_32LO,CRC_32HI,CRC_CCITT,CRC_XOR", + "LAG_HASH_ALGORITHM": "CRC,XOR,RANDOM,CRC_32LO,CRC_32HI,CRC_CCITT,CRC_XOR" } } diff --git a/tests/hash_input/mock_state/ecmp_and_lag.json b/tests/hash_input/mock_state/ecmp_and_lag.json index ee0e94169b..27a8d84a13 100644 --- a/tests/hash_input/mock_state/ecmp_and_lag.json +++ b/tests/hash_input/mock_state/ecmp_and_lag.json @@ -2,6 +2,10 @@ "SWITCH_CAPABILITY|switch": { "ECMP_HASH_CAPABLE": "true", "LAG_HASH_CAPABLE": "true", - "HASH|NATIVE_HASH_FIELD_LIST": "IN_PORT,DST_MAC,SRC_MAC,ETHERTYPE,VLAN_ID,IP_PROTOCOL,DST_IP,SRC_IP,L4_DST_PORT,L4_SRC_PORT,INNER_DST_MAC,INNER_SRC_MAC,INNER_ETHERTYPE,INNER_IP_PROTOCOL,INNER_DST_IP,INNER_SRC_IP,INNER_L4_DST_PORT,INNER_L4_SRC_PORT" + "ECMP_HASH_ALGORITHM_CAPABLE": "true", + "LAG_HASH_ALGORITHM_CAPABLE": "true", + "HASH|NATIVE_HASH_FIELD_LIST": "IN_PORT,DST_MAC,SRC_MAC,ETHERTYPE,VLAN_ID,IP_PROTOCOL,DST_IP,SRC_IP,L4_DST_PORT,L4_SRC_PORT,INNER_DST_MAC,INNER_SRC_MAC,INNER_ETHERTYPE,INNER_IP_PROTOCOL,INNER_DST_IP,INNER_SRC_IP,INNER_L4_DST_PORT,INNER_L4_SRC_PORT", + "ECMP_HASH_ALGORITHM": "CRC,XOR,RANDOM,CRC_32LO,CRC_32HI,CRC_CCITT,CRC_XOR", + "LAG_HASH_ALGORITHM": "CRC,XOR,RANDOM,CRC_32LO,CRC_32HI,CRC_CCITT,CRC_XOR" } } diff --git a/tests/hash_input/mock_state/empty.json b/tests/hash_input/mock_state/empty.json index 9e221a2fb7..240080f1e0 100644 --- a/tests/hash_input/mock_state/empty.json +++ b/tests/hash_input/mock_state/empty.json @@ -2,6 +2,10 @@ "SWITCH_CAPABILITY|switch": { "ECMP_HASH_CAPABLE": "false", "LAG_HASH_CAPABLE": "false", - "HASH|NATIVE_HASH_FIELD_LIST": "IN_PORT,DST_MAC,SRC_MAC,ETHERTYPE,VLAN_ID,IP_PROTOCOL,DST_IP,SRC_IP,L4_DST_PORT,L4_SRC_PORT,INNER_DST_MAC,INNER_SRC_MAC,INNER_ETHERTYPE,INNER_IP_PROTOCOL,INNER_DST_IP,INNER_SRC_IP,INNER_L4_DST_PORT,INNER_L4_SRC_PORT" + "ECMP_HASH_ALGORITHM_CAPABLE": "false", + "LAG_HASH_ALGORITHM_CAPABLE": "false", + "HASH|NATIVE_HASH_FIELD_LIST": "IN_PORT,DST_MAC,SRC_MAC,ETHERTYPE,VLAN_ID,IP_PROTOCOL,DST_IP,SRC_IP,L4_DST_PORT,L4_SRC_PORT,INNER_DST_MAC,INNER_SRC_MAC,INNER_ETHERTYPE,INNER_IP_PROTOCOL,INNER_DST_IP,INNER_SRC_IP,INNER_L4_DST_PORT,INNER_L4_SRC_PORT", + "ECMP_HASH_ALGORITHM": "CRC,XOR,RANDOM,CRC_32LO,CRC_32HI,CRC_CCITT,CRC_XOR", + "LAG_HASH_ALGORITHM": "CRC,XOR,RANDOM,CRC_32LO,CRC_32HI,CRC_CCITT,CRC_XOR" } } diff --git a/tests/hash_input/mock_state/lag.json b/tests/hash_input/mock_state/lag.json index 3fb0008a18..ad4e472534 100644 --- a/tests/hash_input/mock_state/lag.json +++ b/tests/hash_input/mock_state/lag.json @@ -2,6 +2,10 @@ "SWITCH_CAPABILITY|switch": { "ECMP_HASH_CAPABLE": "false", "LAG_HASH_CAPABLE": "true", - "HASH|NATIVE_HASH_FIELD_LIST": "IN_PORT,DST_MAC,SRC_MAC,ETHERTYPE,VLAN_ID,IP_PROTOCOL,DST_IP,SRC_IP,L4_DST_PORT,L4_SRC_PORT,INNER_DST_MAC,INNER_SRC_MAC,INNER_ETHERTYPE,INNER_IP_PROTOCOL,INNER_DST_IP,INNER_SRC_IP,INNER_L4_DST_PORT,INNER_L4_SRC_PORT" + "ECMP_HASH_ALGORITHM_CAPABLE": "false", + "LAG_HASH_ALGORITHM_CAPABLE": "true", + "HASH|NATIVE_HASH_FIELD_LIST": "IN_PORT,DST_MAC,SRC_MAC,ETHERTYPE,VLAN_ID,IP_PROTOCOL,DST_IP,SRC_IP,L4_DST_PORT,L4_SRC_PORT,INNER_DST_MAC,INNER_SRC_MAC,INNER_ETHERTYPE,INNER_IP_PROTOCOL,INNER_DST_IP,INNER_SRC_IP,INNER_L4_DST_PORT,INNER_L4_SRC_PORT", + "ECMP_HASH_ALGORITHM": "CRC,XOR,RANDOM,CRC_32LO,CRC_32HI,CRC_CCITT,CRC_XOR", + "LAG_HASH_ALGORITHM": "CRC,XOR,RANDOM,CRC_32LO,CRC_32HI,CRC_CCITT,CRC_XOR" } } diff --git a/tests/hash_input/mock_state/no_capabilities.json b/tests/hash_input/mock_state/no_capabilities.json index 68f5962ca4..5e4652cc63 100644 --- a/tests/hash_input/mock_state/no_capabilities.json +++ b/tests/hash_input/mock_state/no_capabilities.json @@ -2,6 +2,10 @@ "SWITCH_CAPABILITY|switch": { "ECMP_HASH_CAPABLE": "true", "LAG_HASH_CAPABLE": "true", - "HASH|NATIVE_HASH_FIELD_LIST": "" + "ECMP_HASH_ALGORITHM_CAPABLE": "true", + "LAG_HASH_ALGORITHM_CAPABLE": "true", + "HASH|NATIVE_HASH_FIELD_LIST": "", + "ECMP_HASH_ALGORITHM": "", + "LAG_HASH_ALGORITHM": "" } } diff --git a/tests/hash_input/mock_state/not_applicable.json b/tests/hash_input/mock_state/not_applicable.json index de390e8961..6495b58582 100644 --- a/tests/hash_input/mock_state/not_applicable.json +++ b/tests/hash_input/mock_state/not_applicable.json @@ -2,6 +2,10 @@ "SWITCH_CAPABILITY|switch": { "ECMP_HASH_CAPABLE": "true", "LAG_HASH_CAPABLE": "true", - "HASH|NATIVE_HASH_FIELD_LIST": "N/A" + "ECMP_HASH_ALGORITHM_CAPABLE": "true", + "LAG_HASH_ALGORITHM_CAPABLE": "true", + "HASH|NATIVE_HASH_FIELD_LIST": "N/A", + "ECMP_HASH_ALGORITHM": "N/A", + "LAG_HASH_ALGORITHM": "N/A" } } diff --git a/tests/hash_test.py b/tests/hash_test.py index 96e8558d19..f9de27e999 100644 --- a/tests/hash_test.py +++ b/tests/hash_test.py @@ -39,6 +39,16 @@ "INNER_L4_SRC_PORT" ] +HASH_ALGORITHM = [ + "CRC", + "XOR", + "RANDOM", + "CRC_32LO", + "CRC_32HI", + "CRC_CCITT", + "CRC_XOR" +] + SUCCESS = 0 ERROR2 = 2 @@ -147,6 +157,59 @@ def test_config_hash_neg(self, hash, args, pattern): assert pattern in result.output assert result.exit_code == ERROR2 + @pytest.mark.parametrize( + "hash", [ + "ecmp-hash-algorithm", + "lag-hash-algorithm" + ] + ) + @pytest.mark.parametrize( + "arg", HASH_ALGORITHM + ) + def test_config_hash_algorithm(self, hash, arg): + db = Db() + runner = CliRunner() + + result = runner.invoke( + config.config.commands["switch-hash"].commands["global"]. + commands[hash], arg, obj=db + ) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + + assert result.exit_code == SUCCESS + + @pytest.mark.parametrize( + "hash", [ + "ecmp-hash-algorithm", + "lag-hash-algorithm" + ] + ) + @pytest.mark.parametrize( + "arg,pattern", [ + pytest.param( + "CRC1", + "invalid choice: CRC1.", + id="INVALID" + ) + ] + ) + def test_config_hash_algorithm_neg(self, hash, arg, pattern): + db = Db() + runner = CliRunner() + + result = runner.invoke( + config.config.commands["switch-hash"].commands["global"]. + commands[hash], arg, obj=db + ) + + logger.debug("\n" + result.output) + logger.debug(result.exit_code) + + assert pattern in result.output + assert result.exit_code == ERROR2 + ########## SHOW SWITCH-HASH GLOBAL ########## @@ -155,90 +218,132 @@ def test_config_hash_neg(self, hash, args, pattern): "cfgdb,output", [ pytest.param( os.path.join(mock_config_path, "empty"), - assert_show_output.show_hash_empty, + { + "plain": assert_show_output.show_hash_empty, + "json": assert_show_output.show_hash_empty + }, id="empty" ), pytest.param( os.path.join(mock_config_path, "ecmp"), - assert_show_output.show_hash_ecmp, + { + "plain": assert_show_output.show_hash_ecmp, + "json": assert_show_output.show_hash_ecmp_json + }, id="ecmp" ), pytest.param( os.path.join(mock_config_path, "lag"), - assert_show_output.show_hash_lag, + { + "plain": assert_show_output.show_hash_lag, + "json": assert_show_output.show_hash_lag_json + }, id="lag" ), pytest.param( os.path.join(mock_config_path, "ecmp_and_lag"), - assert_show_output.show_hash_ecmp_and_lag, + { + "plain": assert_show_output.show_hash_ecmp_and_lag, + "json": assert_show_output.show_hash_ecmp_and_lag_json + }, id="all" ) ] ) - def test_show_hash(self, cfgdb, output): + @pytest.mark.parametrize( + "format", [ + "plain", + "json", + ] + ) + def test_show_hash(self, cfgdb, output, format): dbconnector.dedicated_dbs["CONFIG_DB"] = cfgdb db = Db() runner = CliRunner() result = runner.invoke( - show.cli.commands["switch-hash"]. - commands["global"], [], obj=db + show.cli.commands["switch-hash"].commands["global"], + [] if format == "plain" else ["--json"], obj=db ) logger.debug("\n" + result.output) logger.debug(result.exit_code) - assert result.output == output + assert result.output == output[format] assert result.exit_code == SUCCESS @pytest.mark.parametrize( "statedb,output", [ pytest.param( os.path.join(mock_state_path, "no_capabilities"), - assert_show_output.show_hash_capabilities_no, + { + "plain": assert_show_output.show_hash_capabilities_no, + "json": assert_show_output.show_hash_capabilities_no_json + }, id="no" ), pytest.param( os.path.join(mock_state_path, "not_applicable"), - assert_show_output.show_hash_capabilities_na, + { + "plain": assert_show_output.show_hash_capabilities_na, + "json": assert_show_output.show_hash_capabilities_na_json + }, id="na" ), pytest.param( os.path.join(mock_state_path, "empty"), - assert_show_output.show_hash_capabilities_empty, + { + "plain": assert_show_output.show_hash_capabilities_empty, + "json": assert_show_output.show_hash_capabilities_empty_json + }, id="empty" ), pytest.param( os.path.join(mock_state_path, "ecmp"), - assert_show_output.show_hash_capabilities_ecmp, + { + "plain": assert_show_output.show_hash_capabilities_ecmp, + "json": assert_show_output.show_hash_capabilities_ecmp_json + }, id="ecmp" ), pytest.param( os.path.join(mock_state_path, "lag"), - assert_show_output.show_hash_capabilities_lag, + { + "plain": assert_show_output.show_hash_capabilities_lag, + "json": assert_show_output.show_hash_capabilities_lag_json + }, id="lag" ), pytest.param( os.path.join(mock_state_path, "ecmp_and_lag"), - assert_show_output.show_hash_capabilities_ecmp_and_lag, + { + "plain": assert_show_output.show_hash_capabilities_ecmp_and_lag, + "json": assert_show_output.show_hash_capabilities_ecmp_and_lag_json + }, id="all" ) ] ) - def test_show_hash_capabilities(self, statedb, output): + @pytest.mark.parametrize( + "format", [ + "plain", + "json", + ] + ) + def test_show_hash_capabilities(self, statedb, output, format): dbconnector.dedicated_dbs["STATE_DB"] = statedb db = Db() runner = CliRunner() result = runner.invoke( - show.cli.commands["switch-hash"]. - commands["capabilities"], [], obj=db + show.cli.commands["switch-hash"].commands["capabilities"], + [] if format == "plain" else ["--json"], obj=db ) logger.debug("\n" + result.output) logger.debug(result.exit_code) - assert result.output == output + assert result.output == output[format] assert result.exit_code == SUCCESS diff --git a/utilities_common/switch_hash.py b/utilities_common/switch_hash.py index 1f29ca4e30..93e11b0615 100644 --- a/utilities_common/switch_hash.py +++ b/utilities_common/switch_hash.py @@ -8,8 +8,14 @@ # SW_CAP_HASH_FIELD_LIST_KEY = "HASH|NATIVE_HASH_FIELD_LIST" -SW_CAP_ECMP_HASH_KEY = "ECMP_HASH_CAPABLE" -SW_CAP_LAG_HASH_KEY = "LAG_HASH_CAPABLE" + +SW_CAP_ECMP_HASH_CAPABLE_KEY = "ECMP_HASH_CAPABLE" +SW_CAP_LAG_HASH_CAPABLE_KEY = "LAG_HASH_CAPABLE" + +SW_CAP_ECMP_HASH_ALGORITHM_KEY = "ECMP_HASH_ALGORITHM" +SW_CAP_ECMP_HASH_ALGORITHM_CAPABLE_KEY = "ECMP_HASH_ALGORITHM_CAPABLE" +SW_CAP_LAG_HASH_ALGORITHM_KEY = "LAG_HASH_ALGORITHM" +SW_CAP_LAG_HASH_ALGORITHM_CAPABLE_KEY = "LAG_HASH_ALGORITHM_CAPABLE" SW_HASH_KEY = "GLOBAL" SW_CAP_KEY = "switch" @@ -35,6 +41,16 @@ "INNER_L4_SRC_PORT" ] +HASH_ALGORITHM = [ + "CRC", + "XOR", + "RANDOM", + "CRC_32LO", + "CRC_32HI", + "CRC_CCITT", + "CRC_XOR" +] + SYSLOG_IDENTIFIER = "switch_hash" #