Skip to content

Commit a5b78cf

Browse files
authored
[config][show] CLI support for interacting with muxcable (sonic-net#1221)
* Cli support for muxcable to integrate with hardware_proxy What is the motivation for this PR? To add the support for Cli for muxcable to be utilized for configuring and showing the status of all the Port/Ports on a muxcable. How did you do it? Added the changes inside sonic-utilities and tested it on the testbed How did you verify/test it? Ran the cli commands on an Arista7260cx3 testbed with Gemini cable Signed-off-by: vaibhav-dahiya [email protected]
1 parent 8f3b22e commit a5b78cf

13 files changed

+1271
-0
lines changed

config/main.py

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from . import feature
3131
from . import kube
3232
from . import mlnx
33+
from . import muxcable
3334
from . import nat
3435
from . import vlan
3536
from .config_mgmt import ConfigMgmtDPB
@@ -878,6 +879,7 @@ def config(ctx):
878879
config.add_command(console.console)
879880
config.add_command(feature.feature)
880881
config.add_command(kube.kubernetes)
882+
config.add_command(muxcable.muxcable)
881883
config.add_command(nat.nat)
882884
config.add_command(vlan.vlan)
883885

config/muxcable.py

+190
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
import json
2+
import os
3+
import sys
4+
5+
import click
6+
import utilities_common.cli as clicommon
7+
from sonic_py_common import multi_asic
8+
from swsssdk import ConfigDBConnector
9+
from swsscommon import swsscommon
10+
from tabulate import tabulate
11+
from utilities_common import platform_sfputil_helper
12+
13+
platform_sfputil = None
14+
15+
REDIS_TIMEOUT_MSECS = 0
16+
17+
CONFIG_SUCCESSFUL = 100
18+
CONFIG_FAIL = 1
19+
20+
21+
# Helper functions
22+
23+
24+
def get_value_for_key_in_dict(mdict, port, key, table_name):
25+
value = mdict.get(key, None)
26+
if value is None:
27+
click.echo("could not retrieve key {} value for port {} inside table {}".format(key, port, table_name))
28+
sys.exit(CONFIG_FAIL)
29+
return value
30+
31+
#
32+
# 'muxcable' command ("config muxcable")
33+
#
34+
35+
36+
def get_value_for_key_in_config_tbl(config_db, port, key, table):
37+
info_dict = {}
38+
info_dict = config_db.get_entry(table, port)
39+
if info_dict is None:
40+
click.echo("could not retrieve key {} value for port {} inside table {}".format(key, port, table))
41+
sys.exit(CONFIG_FAIL)
42+
43+
value = get_value_for_key_in_dict(info_dict, port, key, table)
44+
45+
return value
46+
47+
48+
@click.group(name='muxcable', cls=clicommon.AliasedGroup)
49+
def muxcable():
50+
"""SONiC command line - 'show muxcable' command"""
51+
52+
if os.geteuid() != 0:
53+
click.echo("Root privileges are required for this operation")
54+
sys.exit(CONFIG_FAIL)
55+
56+
global platform_sfputil
57+
# Load platform-specific sfputil class
58+
platform_sfputil_helper.load_platform_sfputil()
59+
60+
# Load port info
61+
platform_sfputil_helper.platform_sfputil_read_porttab_mappings()
62+
63+
platform_sfputil = platform_sfputil_helper.platform_sfputil
64+
65+
66+
def lookup_statedb_and_update_configdb(per_npu_statedb, config_db, port, state_cfg_val, port_status_dict):
67+
68+
muxcable_statedb_dict = per_npu_statedb.get_all(per_npu_statedb.STATE_DB, 'MUX_CABLE_TABLE|{}'.format(port))
69+
configdb_state = get_value_for_key_in_config_tbl(config_db, port, "state", "MUX_CABLE")
70+
ipv4_value = get_value_for_key_in_config_tbl(config_db, port, "server_ipv4", "MUX_CABLE")
71+
ipv6_value = get_value_for_key_in_config_tbl(config_db, port, "server_ipv6", "MUX_CABLE")
72+
73+
state = get_value_for_key_in_dict(muxcable_statedb_dict, port, "state", "MUX_CABLE_TABLE")
74+
if (state == "active" and configdb_state == "active") or (state == "standby" and configdb_state == "active") or (state == "unknown" and configdb_state == "active") :
75+
if state_cfg_val == "active":
76+
# status is already active, so right back error
77+
port_status_dict[port] = 'OK'
78+
if state_cfg_val == "auto":
79+
# display ok and write to cfgdb auto
80+
port_status_dict[port] = 'OK'
81+
config_db.set_entry("MUX_CABLE", port, {"state": "auto",
82+
"server_ipv4": ipv4_value, "server_ipv6": ipv6_value})
83+
elif state == "active" and configdb_state == "auto":
84+
if state_cfg_val == "active":
85+
# make the state active and write back OK
86+
config_db.set_entry("MUX_CABLE", port, {"state": "active",
87+
"server_ipv4": ipv4_value, "server_ipv6": ipv6_value})
88+
port_status_dict[port] = 'OK'
89+
if state_cfg_val == "auto":
90+
# dont write anything to db, write OK to user
91+
port_status_dict[port] = 'OK'
92+
93+
elif (state == "standby" and configdb_state == "auto") or (state == "unknown" and configdb_state == "auto"):
94+
if state_cfg_val == "active":
95+
# make the state active
96+
config_db.set_entry("MUX_CABLE", port, {"state": "active",
97+
"server_ipv4": ipv4_value, "server_ipv6": ipv6_value})
98+
port_status_dict[port] = 'INPROGRESS'
99+
if state_cfg_val == "auto":
100+
# dont write anything to db
101+
port_status_dict[port] = 'OK'
102+
103+
104+
# 'muxcable' command ("config muxcable mode <port|all> active|auto")
105+
@muxcable.command()
106+
@click.argument('state', metavar='<operation_status>', required=True, type=click.Choice(["active", "auto"]))
107+
@click.argument('port', metavar='<port_name>', required=True, default=None)
108+
@click.option('--json', 'json_output', required=False, is_flag=True, type=click.BOOL)
109+
def mode(state, port, json_output):
110+
"""Show muxcable summary information"""
111+
112+
port_table_keys = {}
113+
y_cable_asic_table_keys = {}
114+
per_npu_configdb = {}
115+
per_npu_statedb = {}
116+
mux_tbl_cfg_db = {}
117+
118+
# Getting all front asic namespace and correspding config and state DB connector
119+
120+
namespaces = multi_asic.get_front_end_namespaces()
121+
for namespace in namespaces:
122+
asic_id = multi_asic.get_asic_index_from_namespace(namespace)
123+
# replace these with correct macros
124+
per_npu_configdb[asic_id] = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace)
125+
per_npu_configdb[asic_id].connect()
126+
per_npu_statedb[asic_id] = swsscommon.SonicV2Connector(use_unix_socket_path=True, namespace=namespace)
127+
per_npu_statedb[asic_id].connect(per_npu_statedb[asic_id].STATE_DB)
128+
129+
mux_tbl_cfg_db[asic_id] = per_npu_configdb[asic_id].get_table("MUX_CABLE")
130+
131+
port_table_keys[asic_id] = per_npu_statedb[asic_id].keys(
132+
per_npu_statedb[asic_id].STATE_DB, 'MUX_CABLE_TABLE|*')
133+
134+
if port is not None and port != "all":
135+
136+
asic_index = None
137+
if platform_sfputil is not None:
138+
asic_index = platform_sfputil.get_asic_id_for_logical_port(port)
139+
if asic_index is None:
140+
# TODO this import is only for unit test purposes, and should be removed once sonic_platform_base
141+
# is fully mocked
142+
import sonic_platform_base.sonic_sfp.sfputilhelper
143+
asic_index = sonic_platform_base.sonic_sfp.sfputilhelper.SfpUtilHelper().get_asic_id_for_logical_port(port)
144+
if asic_index is None:
145+
click.echo("Got invalid asic index for port {}, cant retreive mux status".format(port))
146+
sys.exit(CONFIG_FAIL)
147+
148+
if per_npu_statedb[asic_index] is not None:
149+
y_cable_asic_table_keys = port_table_keys[asic_index]
150+
logical_key = "MUX_CABLE_TABLE"+"|"+port
151+
if logical_key in y_cable_asic_table_keys:
152+
port_status_dict = {}
153+
lookup_statedb_and_update_configdb(
154+
per_npu_statedb[asic_index], per_npu_configdb[asic_index], port, state, port_status_dict)
155+
156+
if json_output:
157+
click.echo("{}".format(json.dumps(port_status_dict, indent=4)))
158+
else:
159+
headers = ['port', 'state']
160+
data = sorted([(k, v) for k, v in port_status_dict.items()])
161+
click.echo(tabulate(data, headers=headers))
162+
163+
sys.exit(CONFIG_SUCCESSFUL)
164+
165+
else:
166+
click.echo("this is not a valid port present on mux_cable".format(port))
167+
sys.exit(CONFIG_FAIL)
168+
else:
169+
click.echo("there is not a valid asic table for this asic_index".format(asic_index))
170+
sys.exit(CONFIG_FAIL)
171+
172+
elif port == "all" and port is not None:
173+
174+
port_status_dict = {}
175+
for namespace in namespaces:
176+
asic_id = multi_asic.get_asic_index_from_namespace(namespace)
177+
for key in port_table_keys[asic_id]:
178+
logical_port = key.split("|")[1]
179+
lookup_statedb_and_update_configdb(
180+
per_npu_statedb[asic_id], per_npu_configdb[asic_id], logical_port, state, port_status_dict)
181+
182+
if json_output:
183+
click.echo("{}".format(json.dumps(port_status_dict, indent=4)))
184+
else:
185+
data = sorted([(k, v) for k, v in port_status_dict.items()])
186+
187+
headers = ['port', 'state']
188+
click.echo(tabulate(data, headers=headers))
189+
190+
sys.exit(CONFIG_SUCCESSFUL)

0 commit comments

Comments
 (0)