Skip to content

Commit 2cb8cc6

Browse files
authored
[sfputil] Configure the debug loopback mode only on the relevant lanes of the logical port (sonic-net#3485)
* [sfputil] Configure the debug loopback mode only on the relevant lanes of the logical port Signed-off-by: xinyu <[email protected]>
1 parent 1aac5e2 commit 2cb8cc6

File tree

3 files changed

+135
-20
lines changed

3 files changed

+135
-20
lines changed

doc/Command-Reference.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -3144,19 +3144,19 @@ This command is the standard CMIS diagnostic control used for troubleshooting li
31443144

31453145
- Usage:
31463146
```
3147-
sfputil debug loopback PORT_NAME LOOPBACK_MODE
3147+
sfputil debug loopback PORT_NAME LOOPBACK_MODE <enable/disable>
31483148
3149-
Set the loopback mode
3149+
Valid values for loopback mode
31503150
host-side-input: host side input loopback mode
31513151
host-side-output: host side output loopback mode
31523152
media-side-input: media side input loopback mode
31533153
media-side-output: media side output loopback mode
3154-
none: disable loopback mode
31553154
```
31563155

31573156
- Example:
31583157
```
3159-
admin@sonic:~$ sfputil debug loopback Ethernet88 host-side-input
3158+
admin@sonic:~$ sfputil debug loopback Ethernet88 host-side-input enable
3159+
admin@sonic:~$ sfputil debug loopback Ethernet88 media-side-output disable
31603160
```
31613161

31623162
## DHCP Relay

sfputil/main.py

+74-8
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import sonic_platform
1919
import sonic_platform_base.sonic_sfp.sfputilhelper
2020
from sonic_platform_base.sfp_base import SfpBase
21-
from swsscommon.swsscommon import SonicV2Connector
21+
from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector
2222
from natsort import natsorted
2323
from sonic_py_common import device_info, logger, multi_asic
2424
from utilities_common.sfp_helper import covert_application_advertisement_to_output_string
@@ -1967,11 +1967,12 @@ def debug():
19671967

19681968
# 'loopback' subcommand
19691969
@debug.command()
1970-
@click.argument('port_name', required=True, default=None)
1971-
@click.argument('loopback_mode', required=True, default="none",
1972-
type=click.Choice(["none", "host-side-input", "host-side-output",
1970+
@click.argument('port_name', required=True)
1971+
@click.argument('loopback_mode', required=True,
1972+
type=click.Choice(["host-side-input", "host-side-output",
19731973
"media-side-input", "media-side-output"]))
1974-
def loopback(port_name, loopback_mode):
1974+
@click.argument('enable', required=True, type=click.Choice(["enable", "disable"]))
1975+
def loopback(port_name, loopback_mode, enable):
19751976
"""Set module diagnostic loopback mode
19761977
"""
19771978
physical_port = logical_port_to_physical_port_index(port_name)
@@ -1991,17 +1992,82 @@ def loopback(port_name, loopback_mode):
19911992
click.echo("{}: This functionality is not implemented".format(port_name))
19921993
sys.exit(ERROR_NOT_IMPLEMENTED)
19931994

1995+
namespace = multi_asic.get_namespace_for_port(port_name)
1996+
config_db = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace)
1997+
if config_db is not None:
1998+
config_db.connect()
1999+
try:
2000+
subport = int(config_db.get(config_db.CONFIG_DB, f'PORT|{port_name}', 'subport'))
2001+
except TypeError:
2002+
click.echo(f"{port_name}: subport is not present in CONFIG_DB")
2003+
sys.exit(EXIT_FAIL)
2004+
2005+
# If subport is set to 0, assign a default value of 1 to ensure valid subport configuration
2006+
if subport == 0:
2007+
subport = 1
2008+
else:
2009+
click.echo(f"{port_name}: Failed to connect to CONFIG_DB")
2010+
sys.exit(EXIT_FAIL)
2011+
2012+
state_db = SonicV2Connector(use_unix_socket_path=False, namespace=namespace)
2013+
if state_db is not None:
2014+
state_db.connect(state_db.STATE_DB)
2015+
try:
2016+
host_lane_count = int(state_db.get(state_db.STATE_DB,
2017+
f'TRANSCEIVER_INFO|{port_name}',
2018+
'host_lane_count'))
2019+
except TypeError:
2020+
click.echo(f"{port_name}: host_lane_count is not present in STATE_DB")
2021+
sys.exit(EXIT_FAIL)
2022+
2023+
try:
2024+
media_lane_count = int(state_db.get(state_db.STATE_DB,
2025+
f'TRANSCEIVER_INFO|{port_name}',
2026+
'media_lane_count'))
2027+
except TypeError:
2028+
click.echo(f"{port_name}: media_lane_count is not present in STATE_DB")
2029+
sys.exit(EXIT_FAIL)
2030+
else:
2031+
click.echo(f"{port_name}: Failed to connect to STATE_DB")
2032+
sys.exit(EXIT_FAIL)
2033+
2034+
if 'host-side' in loopback_mode:
2035+
lane_mask = get_subport_lane_mask(subport, host_lane_count)
2036+
elif 'media-side' in loopback_mode:
2037+
lane_mask = get_subport_lane_mask(subport, media_lane_count)
2038+
else:
2039+
lane_mask = 0
2040+
19942041
try:
1995-
status = api.set_loopback_mode(loopback_mode)
2042+
status = api.set_loopback_mode(loopback_mode,
2043+
lane_mask=lane_mask,
2044+
enable=enable == 'enable')
19962045
except AttributeError:
19972046
click.echo("{}: Set loopback mode is not applicable for this module".format(port_name))
19982047
sys.exit(ERROR_NOT_IMPLEMENTED)
2048+
except TypeError:
2049+
click.echo("{}: Set loopback mode failed. Parameter is not supported".format(port_name))
2050+
sys.exit(EXIT_FAIL)
19992051

20002052
if status:
2001-
click.echo("{}: Set {} loopback".format(port_name, loopback_mode))
2053+
click.echo("{}: {} {} loopback".format(port_name, enable, loopback_mode))
20022054
else:
2003-
click.echo("{}: Set {} loopback failed".format(port_name, loopback_mode))
2055+
click.echo("{}: {} {} loopback failed".format(port_name, enable, loopback_mode))
20042056
sys.exit(EXIT_FAIL)
20052057

2058+
2059+
def get_subport_lane_mask(subport, lane_count):
2060+
"""Get the lane mask for the given subport and lane count
2061+
2062+
Args:
2063+
subport (int): Subport number
2064+
lane_count (int): Lane count for the subport
2065+
2066+
Returns:
2067+
int: Lane mask for the given subport and lane count
2068+
"""
2069+
return ((1 << lane_count) - 1) << ((subport - 1) * lane_count)
2070+
2071+
20062072
if __name__ == '__main__':
20072073
cli()

tests/sfputil_test.py

+57-8
Original file line numberDiff line numberDiff line change
@@ -1631,43 +1631,92 @@ def test_load_port_config(self, mock_is_multi_asic):
16311631

16321632
@patch('sfputil.main.is_port_type_rj45', MagicMock(return_value=False))
16331633
@patch('sfputil.main.platform_chassis')
1634+
@patch('sfputil.main.ConfigDBConnector')
1635+
@patch('sfputil.main.SonicV2Connector')
16341636
@patch('sfputil.main.platform_sfputil', MagicMock(is_logical_port=MagicMock(return_value=1)))
16351637
@patch('sfputil.main.logical_port_to_physical_port_index', MagicMock(return_value=1))
1636-
def test_debug_loopback(self, mock_chassis):
1638+
@patch('sonic_py_common.multi_asic.get_front_end_namespaces', MagicMock(return_value=['']))
1639+
def test_debug_loopback(self, mock_sonic_v2_connector, mock_config_db_connector, mock_chassis):
16371640
mock_sfp = MagicMock()
16381641
mock_api = MagicMock()
1642+
mock_config_db_connector.return_value = MagicMock()
1643+
mock_sonic_v2_connector.return_value = MagicMock()
16391644
mock_chassis.get_sfp = MagicMock(return_value=mock_sfp)
16401645
mock_sfp.get_presence.return_value = True
16411646
mock_sfp.get_xcvr_api = MagicMock(return_value=mock_api)
16421647

16431648
runner = CliRunner()
16441649
mock_sfp.get_presence.return_value = False
16451650
result = runner.invoke(sfputil.cli.commands['debug'].commands['loopback'],
1646-
["Ethernet0", "host-side-input"])
1651+
["Ethernet0", "host-side-input", "enable"])
16471652
assert result.output == 'Ethernet0: SFP EEPROM not detected\n'
16481653
mock_sfp.get_presence.return_value = True
16491654

16501655
mock_sfp.get_xcvr_api = MagicMock(side_effect=NotImplementedError)
16511656
result = runner.invoke(sfputil.cli.commands['debug'].commands['loopback'],
1652-
["Ethernet0", "host-side-input"])
1657+
["Ethernet0", "host-side-input", "enable"])
16531658
assert result.output == 'Ethernet0: This functionality is not implemented\n'
16541659
assert result.exit_code == ERROR_NOT_IMPLEMENTED
16551660

16561661
mock_sfp.get_xcvr_api = MagicMock(return_value=mock_api)
16571662
result = runner.invoke(sfputil.cli.commands['debug'].commands['loopback'],
1658-
["Ethernet0", "host-side-input"])
1659-
assert result.output == 'Ethernet0: Set host-side-input loopback\n'
1663+
["Ethernet0", "host-side-input", "enable"])
1664+
assert result.output == 'Ethernet0: enable host-side-input loopback\n'
1665+
assert result.exit_code != ERROR_NOT_IMPLEMENTED
1666+
1667+
mock_sfp.get_xcvr_api = MagicMock(return_value=mock_api)
1668+
result = runner.invoke(sfputil.cli.commands['debug'].commands['loopback'],
1669+
["Ethernet0", "media-side-input", "enable"])
1670+
assert result.output == 'Ethernet0: enable media-side-input loopback\n'
16601671
assert result.exit_code != ERROR_NOT_IMPLEMENTED
16611672

16621673
mock_api.set_loopback_mode.return_value = False
16631674
result = runner.invoke(sfputil.cli.commands['debug'].commands['loopback'],
1664-
["Ethernet0", "none"])
1665-
assert result.output == 'Ethernet0: Set none loopback failed\n'
1675+
["Ethernet0", "media-side-output", "enable"])
1676+
assert result.output == 'Ethernet0: enable media-side-output loopback failed\n'
16661677
assert result.exit_code == EXIT_FAIL
16671678

16681679
mock_api.set_loopback_mode.return_value = True
16691680
mock_api.set_loopback_mode.side_effect = AttributeError
16701681
result = runner.invoke(sfputil.cli.commands['debug'].commands['loopback'],
1671-
["Ethernet0", "none"])
1682+
["Ethernet0", "host-side-input", "enable"])
16721683
assert result.output == 'Ethernet0: Set loopback mode is not applicable for this module\n'
16731684
assert result.exit_code == ERROR_NOT_IMPLEMENTED
1685+
1686+
mock_api.set_loopback_mode.side_effect = [TypeError, True]
1687+
result = runner.invoke(sfputil.cli.commands['debug'].commands['loopback'],
1688+
["Ethernet0", "host-side-input", "enable"])
1689+
assert result.output == 'Ethernet0: Set loopback mode failed. Parameter is not supported\n'
1690+
assert result.exit_code == EXIT_FAIL
1691+
1692+
mock_config_db = MagicMock()
1693+
mock_config_db.get.side_effect = TypeError
1694+
mock_config_db_connector.return_value = mock_config_db
1695+
result = runner.invoke(sfputil.cli.commands['debug'].commands['loopback'],
1696+
["Ethernet0", "media-side-input", "enable"])
1697+
assert result.output == 'Ethernet0: subport is not present in CONFIG_DB\n'
1698+
assert result.exit_code == EXIT_FAIL
1699+
1700+
mock_config_db_connector.return_value = None
1701+
result = runner.invoke(sfputil.cli.commands['debug'].commands['loopback'],
1702+
["Ethernet0", "media-side-input", "enable"])
1703+
assert result.output == 'Ethernet0: Failed to connect to CONFIG_DB\n'
1704+
assert result.exit_code == EXIT_FAIL
1705+
1706+
mock_config_db_connector.return_value = MagicMock()
1707+
mock_sonic_v2_connector.return_value = None
1708+
result = runner.invoke(sfputil.cli.commands['debug'].commands['loopback'],
1709+
["Ethernet0", "media-side-input", "enable"])
1710+
assert result.output == 'Ethernet0: Failed to connect to STATE_DB\n'
1711+
assert result.exit_code == EXIT_FAIL
1712+
1713+
@pytest.mark.parametrize("subport, lane_count, expected_mask", [
1714+
(1, 1, 0x1),
1715+
(1, 4, 0xf),
1716+
(2, 1, 0x2),
1717+
(2, 4, 0xf0),
1718+
(3, 2, 0x30),
1719+
(4, 1, 0x8),
1720+
])
1721+
def test_get_subport_lane_mask(self, subport, lane_count, expected_mask):
1722+
assert sfputil.get_subport_lane_mask(subport, lane_count) == expected_mask

0 commit comments

Comments
 (0)