Skip to content

Commit

Permalink
[hash]: Implement GH frontend.
Browse files Browse the repository at this point in the history
Signed-off-by: Nazarii Hnydyn <[email protected]>
  • Loading branch information
nazariig committed Jun 30, 2023
1 parent 73d8d63 commit 79f444b
Show file tree
Hide file tree
Showing 15 changed files with 957 additions and 0 deletions.
273 changes: 273 additions & 0 deletions config/plugins/sonic-hash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
"""
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_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_HASH_KEY,
SW_CAP_KEY,
HASH_FIELD_LIST,
SYSLOG_IDENTIFIER,
get_param,
get_param_hint,
get_dupes,
to_str,
)


log = logger.Logger(SYSLOG_IDENTIFIER)
log.set_min_log_priority_info()

#
# Hash validators -----------------------------------------------------------------------------------------------------
#

def hash_field_validator(ctx, param, value):
"""
Check if hash field list argument is valid
Args:
ctx: click context
param: click parameter context
value: value of parameter
Returns:
str: validated parameter
"""

for hash_field in value:
click.Choice(HASH_FIELD_LIST).convert(hash_field, param, ctx)

return list(value)


def ecmp_hash_validator(ctx, db, ecmp_hash):
"""
Check if ECMP hash argument is valid
Args:
ctx: click context
db: State DB connector object
ecmp_hash: ECMP hash field list
"""

dup_list = get_dupes(ecmp_hash)
if dup_list:
raise click.UsageError("Invalid value for {}: {} has a duplicate hash field(s) {}".format(
get_param_hint(ctx, "ecmp_hash"), to_str(ecmp_hash), to_str(dup_list)), ctx
)

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')

if entry[SW_CAP_ECMP_HASH_KEY] == 'false':
raise click.UsageError("Failed to configure {}: operation is not supported".format(
get_param_hint(ctx, "ecmp_hash")), ctx
)

if not entry[SW_CAP_HASH_FIELD_LIST_KEY]:
raise click.UsageError("Failed to configure {}: no hash field capabilities".format(
get_param_hint(ctx, "ecmp_hash")), ctx
)

if entry[SW_CAP_HASH_FIELD_LIST_KEY] == 'N/A':
return

cap_list = entry[SW_CAP_HASH_FIELD_LIST_KEY].split(',')

for hash_field in ecmp_hash:
click.Choice(cap_list).convert(hash_field, get_param(ctx, "ecmp_hash"), ctx)


def lag_hash_validator(ctx, db, lag_hash):
"""
Check if LAG hash argument is valid
Args:
ctx: click context
db: State DB connector object
lag_hash: LAG hash field list
"""

dup_list = get_dupes(lag_hash)
if dup_list:
raise click.UsageError("Invalid value for {}: {} has a duplicate hash field(s) {}".format(
get_param_hint(ctx, "lag_hash"), to_str(lag_hash), to_str(dup_list)), ctx
)

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')

if entry[SW_CAP_LAG_HASH_KEY] == 'false':
raise click.UsageError("Failed to configure {}: operation is not supported".format(
get_param_hint(ctx, "lag_hash")), ctx
)

if not entry[SW_CAP_HASH_FIELD_LIST_KEY]:
raise click.UsageError("Failed to configure {}: no hash field capabilities".format(
get_param_hint(ctx, "lag_hash")), ctx
)

if entry[SW_CAP_HASH_FIELD_LIST_KEY] == 'N/A':
return

cap_list = entry[SW_CAP_HASH_FIELD_LIST_KEY].split(',')

for hash_field in lag_hash:
click.Choice(cap_list).convert(hash_field, get_param(ctx, "lag_hash"), ctx)

#
# Hash 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
entry_changed = True
if value is None:
cfg[table][key].pop(attr, None)
else:
cfg[table][key][attr] = value

if not entry_changed:
return

db.set_entry(table, key, cfg[table][key])

#
# Hash CLI ------------------------------------------------------------------------------------------------------------
#

@click.group(
name="switch-hash",
cls=clicommon.AliasedGroup
)
def SWITCH_HASH():
""" Configure switch hash feature """

pass


@SWITCH_HASH.group(
name="global",
cls=clicommon.AliasedGroup
)
def SWITCH_HASH_GLOBAL():
""" Configure switch hash global """

pass


@SWITCH_HASH_GLOBAL.command(
name="ecmp-hash"
)
@click.argument(
"ecmp-hash",
nargs=-1,
required=True,
callback=hash_field_validator,
)
@clicommon.pass_db
@click.pass_context
def SWITCH_HASH_GLOBAL_ecmp_hash(ctx, db, ecmp_hash):
""" Hash fields for hashing packets going through ECMP """

ecmp_hash_validator(ctx, db.db, ecmp_hash)

table = CFG_SWITCH_HASH
key = SW_HASH_KEY
data = {
"ecmp_hash": ecmp_hash,
}

try:
update_entry_validated(db.cfgdb, table, key, data, create_if_not_exists=True)
log.log_notice("Configured switch global ECMP hash: {}".format(to_str(ecmp_hash)))
except Exception as e:
log.log_error("Failed to configure switch global ECMP hash: {}".format(str(e)))
ctx.fail(str(e))


@SWITCH_HASH_GLOBAL.command(
name="lag-hash"
)
@click.argument(
"lag-hash",
nargs=-1,
required=True,
callback=hash_field_validator,
)
@clicommon.pass_db
@click.pass_context
def SWITCH_HASH_GLOBAL_lag_hash(ctx, db, lag_hash):
""" Hash fields for hashing packets going through LAG """

lag_hash_validator(ctx, db.db, lag_hash)

table = CFG_SWITCH_HASH
key = SW_HASH_KEY
data = {
"lag_hash": lag_hash,
}

try:
update_entry_validated(db.cfgdb, table, key, data, create_if_not_exists=True)
log.log_notice("Configured switch global LAG hash: {}".format(to_str(lag_hash)))
except Exception as err:
log.log_error("Failed to configure switch global LAG hash: {}".format(str(err)))
ctx.fail(str(err))


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_HASH
if cli_node.name in cli.commands:
raise Exception(f"{cli_node.name} already exists in CLI")
cli.add_command(SWITCH_HASH)
Loading

0 comments on commit 79f444b

Please sign in to comment.