Skip to content

Commit ff380e0

Browse files
authored
[hash]: Implement GH frontend (sonic-net#2580)
HLD sonic-net/SONiC#1101 - 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 Signed-off-by: Nazarii Hnydyn <[email protected]>
1 parent 61bad06 commit ff380e0

16 files changed

+1055
-0
lines changed

config/plugins/sonic-hash.py

+273
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
"""
2+
This CLI plugin was auto-generated by using 'sonic-cli-gen' utility
3+
"""
4+
5+
import click
6+
import utilities_common.cli as clicommon
7+
8+
from sonic_py_common import logger
9+
from utilities_common.switch_hash import (
10+
CFG_SWITCH_HASH,
11+
STATE_SWITCH_CAPABILITY,
12+
SW_CAP_HASH_FIELD_LIST_KEY,
13+
SW_CAP_ECMP_HASH_KEY,
14+
SW_CAP_LAG_HASH_KEY,
15+
SW_HASH_KEY,
16+
SW_CAP_KEY,
17+
HASH_FIELD_LIST,
18+
SYSLOG_IDENTIFIER,
19+
get_param,
20+
get_param_hint,
21+
get_dupes,
22+
to_str,
23+
)
24+
25+
26+
log = logger.Logger(SYSLOG_IDENTIFIER)
27+
log.set_min_log_priority_info()
28+
29+
#
30+
# Hash validators -----------------------------------------------------------------------------------------------------
31+
#
32+
33+
def hash_field_validator(ctx, param, value):
34+
"""
35+
Check if hash field list argument is valid
36+
Args:
37+
ctx: click context
38+
param: click parameter context
39+
value: value of parameter
40+
Returns:
41+
str: validated parameter
42+
"""
43+
44+
for hash_field in value:
45+
click.Choice(HASH_FIELD_LIST).convert(hash_field, param, ctx)
46+
47+
return list(value)
48+
49+
50+
def ecmp_hash_validator(ctx, db, ecmp_hash):
51+
"""
52+
Check if ECMP hash argument is valid
53+
54+
Args:
55+
ctx: click context
56+
db: State DB connector object
57+
ecmp_hash: ECMP hash field list
58+
"""
59+
60+
dup_list = get_dupes(ecmp_hash)
61+
if dup_list:
62+
raise click.UsageError("Invalid value for {}: {} has a duplicate hash field(s) {}".format(
63+
get_param_hint(ctx, "ecmp_hash"), to_str(ecmp_hash), to_str(dup_list)), ctx
64+
)
65+
66+
entry = db.get_all(db.STATE_DB, "{}|{}".format(STATE_SWITCH_CAPABILITY, SW_CAP_KEY))
67+
68+
entry.setdefault(SW_CAP_HASH_FIELD_LIST_KEY, 'N/A')
69+
entry.setdefault(SW_CAP_ECMP_HASH_KEY, 'false')
70+
71+
if entry[SW_CAP_ECMP_HASH_KEY] == 'false':
72+
raise click.UsageError("Failed to configure {}: operation is not supported".format(
73+
get_param_hint(ctx, "ecmp_hash")), ctx
74+
)
75+
76+
if not entry[SW_CAP_HASH_FIELD_LIST_KEY]:
77+
raise click.UsageError("Failed to configure {}: no hash field capabilities".format(
78+
get_param_hint(ctx, "ecmp_hash")), ctx
79+
)
80+
81+
if entry[SW_CAP_HASH_FIELD_LIST_KEY] == 'N/A':
82+
return
83+
84+
cap_list = entry[SW_CAP_HASH_FIELD_LIST_KEY].split(',')
85+
86+
for hash_field in ecmp_hash:
87+
click.Choice(cap_list).convert(hash_field, get_param(ctx, "ecmp_hash"), ctx)
88+
89+
90+
def lag_hash_validator(ctx, db, lag_hash):
91+
"""
92+
Check if LAG hash argument is valid
93+
94+
Args:
95+
ctx: click context
96+
db: State DB connector object
97+
lag_hash: LAG hash field list
98+
"""
99+
100+
dup_list = get_dupes(lag_hash)
101+
if dup_list:
102+
raise click.UsageError("Invalid value for {}: {} has a duplicate hash field(s) {}".format(
103+
get_param_hint(ctx, "lag_hash"), to_str(lag_hash), to_str(dup_list)), ctx
104+
)
105+
106+
entry = db.get_all(db.STATE_DB, "{}|{}".format(STATE_SWITCH_CAPABILITY, SW_CAP_KEY))
107+
108+
entry.setdefault(SW_CAP_HASH_FIELD_LIST_KEY, 'N/A')
109+
entry.setdefault(SW_CAP_LAG_HASH_KEY, 'false')
110+
111+
if entry[SW_CAP_LAG_HASH_KEY] == 'false':
112+
raise click.UsageError("Failed to configure {}: operation is not supported".format(
113+
get_param_hint(ctx, "lag_hash")), ctx
114+
)
115+
116+
if not entry[SW_CAP_HASH_FIELD_LIST_KEY]:
117+
raise click.UsageError("Failed to configure {}: no hash field capabilities".format(
118+
get_param_hint(ctx, "lag_hash")), ctx
119+
)
120+
121+
if entry[SW_CAP_HASH_FIELD_LIST_KEY] == 'N/A':
122+
return
123+
124+
cap_list = entry[SW_CAP_HASH_FIELD_LIST_KEY].split(',')
125+
126+
for hash_field in lag_hash:
127+
click.Choice(cap_list).convert(hash_field, get_param(ctx, "lag_hash"), ctx)
128+
129+
#
130+
# Hash DB interface ---------------------------------------------------------------------------------------------------
131+
#
132+
133+
def update_entry_validated(db, table, key, data, create_if_not_exists=False):
134+
""" Update entry in table and validate configuration.
135+
If attribute value in data is None, the attribute is deleted.
136+
137+
Args:
138+
db (swsscommon.ConfigDBConnector): Config DB connector object.
139+
table (str): Table name to add new entry to.
140+
key (Union[str, Tuple]): Key name in the table.
141+
data (Dict): Entry data.
142+
create_if_not_exists (bool):
143+
In case entry does not exists already a new entry
144+
is not created if this flag is set to False and
145+
creates a new entry if flag is set to True.
146+
Raises:
147+
Exception: when cfg does not satisfy YANG schema.
148+
"""
149+
150+
cfg = db.get_config()
151+
cfg.setdefault(table, {})
152+
153+
if not data:
154+
raise click.ClickException(f"No field/values to update {key}")
155+
156+
if create_if_not_exists:
157+
cfg[table].setdefault(key, {})
158+
159+
if key not in cfg[table]:
160+
raise click.ClickException(f"{key} does not exist")
161+
162+
entry_changed = False
163+
for attr, value in data.items():
164+
if value == cfg[table][key].get(attr):
165+
continue
166+
entry_changed = True
167+
if value is None:
168+
cfg[table][key].pop(attr, None)
169+
else:
170+
cfg[table][key][attr] = value
171+
172+
if not entry_changed:
173+
return
174+
175+
db.set_entry(table, key, cfg[table][key])
176+
177+
#
178+
# Hash CLI ------------------------------------------------------------------------------------------------------------
179+
#
180+
181+
@click.group(
182+
name="switch-hash",
183+
cls=clicommon.AliasedGroup
184+
)
185+
def SWITCH_HASH():
186+
""" Configure switch hash feature """
187+
188+
pass
189+
190+
191+
@SWITCH_HASH.group(
192+
name="global",
193+
cls=clicommon.AliasedGroup
194+
)
195+
def SWITCH_HASH_GLOBAL():
196+
""" Configure switch hash global """
197+
198+
pass
199+
200+
201+
@SWITCH_HASH_GLOBAL.command(
202+
name="ecmp-hash"
203+
)
204+
@click.argument(
205+
"ecmp-hash",
206+
nargs=-1,
207+
required=True,
208+
callback=hash_field_validator,
209+
)
210+
@clicommon.pass_db
211+
@click.pass_context
212+
def SWITCH_HASH_GLOBAL_ecmp_hash(ctx, db, ecmp_hash):
213+
""" Hash fields for hashing packets going through ECMP """
214+
215+
ecmp_hash_validator(ctx, db.db, ecmp_hash)
216+
217+
table = CFG_SWITCH_HASH
218+
key = SW_HASH_KEY
219+
data = {
220+
"ecmp_hash": ecmp_hash,
221+
}
222+
223+
try:
224+
update_entry_validated(db.cfgdb, table, key, data, create_if_not_exists=True)
225+
log.log_notice("Configured switch global ECMP hash: {}".format(to_str(ecmp_hash)))
226+
except Exception as e:
227+
log.log_error("Failed to configure switch global ECMP hash: {}".format(str(e)))
228+
ctx.fail(str(e))
229+
230+
231+
@SWITCH_HASH_GLOBAL.command(
232+
name="lag-hash"
233+
)
234+
@click.argument(
235+
"lag-hash",
236+
nargs=-1,
237+
required=True,
238+
callback=hash_field_validator,
239+
)
240+
@clicommon.pass_db
241+
@click.pass_context
242+
def SWITCH_HASH_GLOBAL_lag_hash(ctx, db, lag_hash):
243+
""" Hash fields for hashing packets going through LAG """
244+
245+
lag_hash_validator(ctx, db.db, lag_hash)
246+
247+
table = CFG_SWITCH_HASH
248+
key = SW_HASH_KEY
249+
data = {
250+
"lag_hash": lag_hash,
251+
}
252+
253+
try:
254+
update_entry_validated(db.cfgdb, table, key, data, create_if_not_exists=True)
255+
log.log_notice("Configured switch global LAG hash: {}".format(to_str(lag_hash)))
256+
except Exception as err:
257+
log.log_error("Failed to configure switch global LAG hash: {}".format(str(err)))
258+
ctx.fail(str(err))
259+
260+
261+
def register(cli):
262+
""" Register new CLI nodes in root CLI.
263+
264+
Args:
265+
cli: Root CLI node.
266+
Raises:
267+
Exception: when root CLI already has a command
268+
we are trying to register.
269+
"""
270+
cli_node = SWITCH_HASH
271+
if cli_node.name in cli.commands:
272+
raise Exception(f"{cli_node.name} already exists in CLI")
273+
cli.add_command(SWITCH_HASH)

doc/Command-Reference.md

+98
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@
6464
* [Flow Counters config commands](#flow-counters-config-commands)
6565
* [Gearbox](#gearbox)
6666
* [Gearbox show commands](#gearbox-show-commands)
67+
* [Hash](#hash)
68+
* [Hash show commands](#hash-show-commands)
69+
* [Hash config commands](#hash-config-commands)
6770
* [Interfaces](#interfaces)
6871
* [Interface Show Commands](#interface-show-commands)
6972
* [Interface Config Commands](#interface-config-commands)
@@ -4087,6 +4090,101 @@ This command is used to change device hostname without traffic being impacted.
40874090
Please note loaded setting will be lost after system reboot. To preserve setting, run `config save`.
40884091
```
40894092
4093+
## Hash
4094+
4095+
This section explains the various show and configuration commands available for user.
4096+
4097+
### Hash Show Commands
4098+
4099+
This subsection explains how to display switch hash configuration.
4100+
4101+
**show switch-hash global**
4102+
4103+
This command displays switch hash global configuration.
4104+
4105+
- Usage:
4106+
```bash
4107+
show switch-hash global
4108+
```
4109+
4110+
- Example:
4111+
```bash
4112+
admin@sonic:~$ show switch-hash global
4113+
ECMP HASH LAG HASH
4114+
----------------- -----------------
4115+
DST_MAC DST_MAC
4116+
SRC_MAC SRC_MAC
4117+
ETHERTYPE ETHERTYPE
4118+
IP_PROTOCOL IP_PROTOCOL
4119+
DST_IP DST_IP
4120+
SRC_IP SRC_IP
4121+
L4_DST_PORT L4_DST_PORT
4122+
L4_SRC_PORT L4_SRC_PORT
4123+
INNER_DST_MAC INNER_DST_MAC
4124+
INNER_SRC_MAC INNER_SRC_MAC
4125+
INNER_ETHERTYPE INNER_ETHERTYPE
4126+
INNER_IP_PROTOCOL INNER_IP_PROTOCOL
4127+
INNER_DST_IP INNER_DST_IP
4128+
INNER_SRC_IP INNER_SRC_IP
4129+
INNER_L4_DST_PORT INNER_L4_DST_PORT
4130+
INNER_L4_SRC_PORT INNER_L4_SRC_PORT
4131+
```
4132+
4133+
### Hash Config Commands
4134+
4135+
This subsection explains how to configure switch hash.
4136+
4137+
**config switch-hash global**
4138+
4139+
This command is used to manage switch hash global configuration.
4140+
4141+
- Usage:
4142+
```bash
4143+
config switch-hash global ecmp-hash <hash_field_list>
4144+
config switch-hash global lag-hash <hash_field_list>
4145+
```
4146+
4147+
- Parameters:
4148+
- _hash_field_list_: hash fields for hashing packets going through ECMP/LAG
4149+
4150+
- Examples:
4151+
```bash
4152+
admin@sonic:~$ config switch-hash global ecmp-hash \
4153+
'DST_MAC' \
4154+
'SRC_MAC' \
4155+
'ETHERTYPE' \
4156+
'IP_PROTOCOL' \
4157+
'DST_IP' \
4158+
'SRC_IP' \
4159+
'L4_DST_PORT' \
4160+
'L4_SRC_PORT' \
4161+
'INNER_DST_MAC' \
4162+
'INNER_SRC_MAC' \
4163+
'INNER_ETHERTYPE' \
4164+
'INNER_IP_PROTOCOL' \
4165+
'INNER_DST_IP' \
4166+
'INNER_SRC_IP' \
4167+
'INNER_L4_DST_PORT' \
4168+
'INNER_L4_SRC_PORT'
4169+
admin@sonic:~$ config switch-hash global lag-hash \
4170+
'DST_MAC' \
4171+
'SRC_MAC' \
4172+
'ETHERTYPE' \
4173+
'IP_PROTOCOL' \
4174+
'DST_IP' \
4175+
'SRC_IP' \
4176+
'L4_DST_PORT' \
4177+
'L4_SRC_PORT' \
4178+
'INNER_DST_MAC' \
4179+
'INNER_SRC_MAC' \
4180+
'INNER_ETHERTYPE' \
4181+
'INNER_IP_PROTOCOL' \
4182+
'INNER_DST_IP' \
4183+
'INNER_SRC_IP' \
4184+
'INNER_L4_DST_PORT' \
4185+
'INNER_L4_SRC_PORT'
4186+
```
4187+
40904188
## Interfaces
40914189

40924190
### Interface Show Commands

0 commit comments

Comments
 (0)