Skip to content

Commit ebb17ca

Browse files
authored
[dualtor] Support reset heartbeat suspend timer on active-standby ports (#3776)
* [dualtor] Support reset heartbeat suspend timer on active-standby ports Signed-off-by: Longxiang Lyu <lolv@microsoft.com>
1 parent 2aa7a82 commit ebb17ca

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-0
lines changed

config/muxcable.py

+80
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import click
77
import re
8+
from natsort import natsorted
89
import utilities_common.cli as clicommon
910
from sonic_py_common import multi_asic
1011
from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector
@@ -1269,3 +1270,82 @@ def telemetry(db, state):
12691270
else:
12701271
click.echo("ERR: Unable to set ycabled telemetry state to {}".format(state))
12711272
sys.exit(CONFIG_FAIL)
1273+
1274+
1275+
# 'muxcable' command ("config muxcable reset-heartbeat-suspend <port|all>")
1276+
@muxcable.command()
1277+
@click.argument('port', metavar='<port_name>', required=True, default=None)
1278+
@clicommon.pass_db
1279+
def reset_heartbeat_suspend(db, port):
1280+
"""Reset the mux port heartbeat suspend."""
1281+
1282+
if port is None:
1283+
click.echo("no port provided")
1284+
sys.exit(CONFIG_FAIL)
1285+
1286+
port = platform_sfputil_helper.get_interface_name(port, db)
1287+
asic_index = multi_asic.get_asic_index_from_namespace(EMPTY_NAMESPACE)
1288+
config_dbs = {}
1289+
mux_linkmgrd_tables = {}
1290+
mux_config_tables = {}
1291+
1292+
# Getting all front asic namespace and correspding config DB connector
1293+
namespaces = multi_asic.get_front_end_namespaces()
1294+
for namespace in namespaces:
1295+
asic_index = multi_asic.get_asic_index_from_namespace(namespace)
1296+
config_dbs[asic_index] = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace)
1297+
config_dbs[asic_index].connect()
1298+
mux_linkmgrd_tables[asic_index] = config_dbs[asic_index].get_table("MUX_LINKMGR")
1299+
mux_config_tables[asic_index] = config_dbs[asic_index].get_table("MUX_CABLE")
1300+
1301+
mux_ports = []
1302+
if port == "all":
1303+
for asic_index, mux_config_table in mux_config_tables.items():
1304+
config_db = config_dbs[asic_index]
1305+
mux_linkmgrd_table = mux_linkmgrd_tables[asic_index]
1306+
mux_ports = [p for p, c in mux_config_table.items()
1307+
if c.get("cable_type", "active-standby") == "active-standby"]
1308+
if mux_ports:
1309+
# trigger one-shot heartbeat suspend reset
1310+
config_db.mod_entry("MUX_LINKMGR", "LINK_PROBER", {"reset_suspend_timer": ",".join(mux_ports)})
1311+
# restore config db to the original
1312+
config_db.set_entry("MUX_LINKMGR", "LINK_PROBER", mux_linkmgrd_table.get("LINK_PROBER", None))
1313+
else:
1314+
asic_index = None
1315+
if platform_sfputil is not None:
1316+
asic_index = platform_sfputil.get_asic_id_for_logical_port(port)
1317+
if asic_index is None:
1318+
# TODO this import is only for unit test purposes, and should be removed once sonic_platform_base
1319+
# is fully mocked
1320+
import sonic_platform_base.sonic_sfp.sfputilhelper
1321+
asic_index = sonic_platform_base.sonic_sfp.sfputilhelper.SfpUtilHelper().get_asic_id_for_logical_port(port)
1322+
if asic_index is None:
1323+
click.echo("Got invalid asic index for port {}, can't reset heartbeat suspend".format(port))
1324+
sys.exit(CONFIG_FAIL)
1325+
if asic_index in config_dbs:
1326+
config_db = config_dbs[asic_index]
1327+
mux_linkmgrd_table = mux_linkmgrd_tables[asic_index]
1328+
mux_config_table = mux_config_tables[asic_index]
1329+
if port not in mux_config_table:
1330+
click.echo("Got invalid port {}, can't reset heartbeat suspend'".format(port))
1331+
sys.exit(CONFIG_FAIL)
1332+
elif mux_config_table[port].get("cable_type", "active-standby") != "active-standby":
1333+
click.echo(
1334+
"Got invalid port {}, can't reset heartbeat suspend on active-active mux port".format(port)
1335+
)
1336+
sys.exit(CONFIG_FAIL)
1337+
mux_ports.append(port)
1338+
# trigger one-shot heartbeat suspend reset
1339+
config_db.mod_entry("MUX_LINKMGR", "LINK_PROBER", {"reset_suspend_timer": port})
1340+
# restore config db to the original
1341+
config_db.set_entry("MUX_LINKMGR", "LINK_PROBER", mux_linkmgrd_table.get("LINK_PROBER", None))
1342+
else:
1343+
click.echo("Got invalid asic index for port {}, can't reset heartbeat suspend'".format(port))
1344+
sys.exit(CONFIG_FAIL)
1345+
1346+
if not mux_ports:
1347+
click.echo("No mux ports found to reset heartbeat suspend")
1348+
sys.exit(CONFIG_FAIL)
1349+
1350+
mux_ports = natsorted(mux_ports)
1351+
click.echo("Success in resetting heartbeat suspend for mux ports: {}".format(", ".join(mux_ports)))

tests/muxcable_test.py

+55
Original file line numberDiff line numberDiff line change
@@ -2125,6 +2125,61 @@ def test_show_mux_resetcause_json(self):
21252125
["Ethernet0", "--json"], obj=db)
21262126
assert result.output == show_muxcable_resetcause_expected_port_output_json
21272127

2128+
def test_config_muxcable_reset_heartbeat_suspend_all(self):
2129+
runner = CliRunner()
2130+
db = Db()
2131+
2132+
os.environ['SONIC_CLI_IFACE_MODE'] = "alias"
2133+
result = runner.invoke(config.config.commands["muxcable"].commands["reset-heartbeat-suspend"],
2134+
["all"], obj=db)
2135+
os.environ['SONIC_CLI_IFACE_MODE'] = "default"
2136+
2137+
assert result.exit_code == 0
2138+
assert result.output == ("Success in resetting heartbeat suspend for mux ports: "
2139+
"Ethernet0, Ethernet4, Ethernet8, Ethernet12, Ethernet16, Ethernet28\n")
2140+
2141+
@mock.patch('sonic_platform_base.sonic_sfp.sfputilhelper.SfpUtilHelper.get_asic_id_for_logical_port',
2142+
mock.MagicMock(return_value=0))
2143+
def test_config_muxcable_reset_heartbeat_suspend_active_standby_port(self):
2144+
runner = CliRunner()
2145+
db = Db()
2146+
2147+
os.environ['SONIC_CLI_IFACE_MODE'] = "alias"
2148+
result = runner.invoke(config.config.commands["muxcable"].commands["reset-heartbeat-suspend"],
2149+
["Ethernet28"], obj=db)
2150+
os.environ['SONIC_CLI_IFACE_MODE'] = "default"
2151+
2152+
assert result.exit_code == 0
2153+
assert result.output == "Success in resetting heartbeat suspend for mux ports: Ethernet28\n"
2154+
2155+
@mock.patch('sonic_platform_base.sonic_sfp.sfputilhelper.SfpUtilHelper.get_asic_id_for_logical_port',
2156+
mock.MagicMock(return_value=0))
2157+
def test_config_muxcable_reset_heartbeat_suspend_active_active_port(self):
2158+
runner = CliRunner()
2159+
db = Db()
2160+
2161+
os.environ['SONIC_CLI_IFACE_MODE'] = "alias"
2162+
result = runner.invoke(config.config.commands["muxcable"].commands["reset-heartbeat-suspend"],
2163+
["Ethernet32"], obj=db)
2164+
os.environ['SONIC_CLI_IFACE_MODE'] = "default"
2165+
2166+
assert result.exit_code == 1
2167+
assert result.output == \
2168+
"Got invalid port Ethernet32, can't reset heartbeat suspend on active-active mux port\n"
2169+
2170+
@mock.patch('sonic_platform_base.sonic_sfp.sfputilhelper.SfpUtilHelper.get_asic_id_for_logical_port',
2171+
mock.MagicMock(return_value=0))
2172+
def test_config_muxcable_reset_heartbeat_suspend_invalid_port(self):
2173+
runner = CliRunner()
2174+
db = Db()
2175+
2176+
os.environ['SONIC_CLI_IFACE_MODE'] = "alias"
2177+
result = runner.invoke(config.config.commands["muxcable"].commands["reset-heartbeat-suspend"],
2178+
["Ethernet40"], obj=db)
2179+
os.environ['SONIC_CLI_IFACE_MODE'] = "default"
2180+
2181+
assert result.exit_code == 1
2182+
assert result.output == "Got invalid port Ethernet40, can't reset heartbeat suspend'\n"
21282183

21292184
@classmethod
21302185
def teardown_class(cls):

0 commit comments

Comments
 (0)