Skip to content

Commit 8c46f71

Browse files
authored
[sfputil] add support for sfputil debug tx-output/rx-output {port} enable/disable (sonic-net#162)
<!-- Please make sure you've read and understood our contributing guidelines: https://github.com/Azure/SONiC/blob/gh-pages/CONTRIBUTING.md failure_prs.log skip_prs.log Make sure all your commits include a signature generated with `git commit -s` ** If this is a bug fix, make sure your description includes "closes #xxxx", "fixes #xxxx" or "resolves #xxxx" so that GitHub automatically closes the related issue when the PR is merged. If you are adding/modifying/removing any command or utility script, please also make sure to add/modify/remove any unit tests from the tests directory as appropriate. If you are modifying or removing an existing 'show', 'config' or 'sonic-clear' subcommand, or you are adding a new subcommand, please make sure you also update the Command Line Reference Guide (doc/Command-Reference.md) to reflect your changes. Please provide the following information: --> ## **Summary of Changes** ### **What I did** - Added a new `debug.py` module to handle **SFP diagnostics and debugging**. - Replaced existing **utility methods** with functions from `utilities_common.platform_sfputil_helper`. - Registered `debug` as a subcommand in `sfputil/main.py`. ### **How I did it** - Created a new `debug.py` file under `sfputil/`. - Implemented the `debug` command group with the following subcommands: - `loopback`: Configures SFP loopback modes. - `tx-output`: Enables or disables TX output. - `rx-output`: Enables or disables RX output. - Used helper functions from `utilities_common.platform_sfputil_helper` for better modularity and maintainability. - Updated `sfputil/main.py` to integrate the new `debug` command. ### **How to verify it** UT and deploy changed on a testbed 1. Run the following command to check if `debug` is now available: ```sh sfputil debug --help 2. sfputil debug tx-output Ethernet0 enable 3. sfputil debug rx-output Ethernet0 disable ``` Usage: sfputil debug [OPTIONS] COMMAND [ARGS]... Try "sfputil debug --help" for help. Error: no such option: -h admin@sonic:~$ sudo sfputil debug --help Usage: sfputil debug [OPTIONS] COMMAND [ARGS]... Group for debugging and diagnostic control commands. This command group loads platform-specific utilities and prepares them for use in diagnostic commands. Options: --help Show this message and exit. Commands: loopback Set module diagnostic loopback mode. rx-output Enable or disable RX output on a port. tx-output Enable or disable TX output on a port. admin@sonic:~$ sudo sfputil debug rx-output --help Usage: sfputil debug rx-output [OPTIONS] PORT_NAME [enable|disable] Enable or disable RX output on a port. Options: --help Show this message and exit. admin@sonic:~$ ```
1 parent a9fb9ca commit 8c46f71

File tree

6 files changed

+566
-135
lines changed

6 files changed

+566
-135
lines changed

doc/Command-Reference.md

+37
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
* [CMIS firmware target mode commands](#cmis-firmware-target-mode-commands)
5151
* [CMIS debug](#cmis-debug)
5252
* [CMIS debug loopback](#cmis-debug-loopback)
53+
* [CMIS debug rx-output](#cmis-debug-rx-output)
54+
* [CMIS debug tx-output](#cmis-debug-tx-output)
5355
* [DHCP Relay](#dhcp-relay)
5456
* [DHCP Relay show commands](#dhcp-relay-show-commands)
5557
* [DHCP Relay clear commands](#dhcp-relay-clear-commands)
@@ -3246,6 +3248,41 @@ This command is the standard CMIS diagnostic control used for troubleshooting li
32463248
admin@sonic:~$ sfputil debug loopback Ethernet88 media-side-output disable
32473249
```
32483250

3251+
### CMIS debug rx-output
3252+
3253+
The command disables RX input by muting the optical receiver on the module, preventing it from detecting incoming signals.
3254+
3255+
**sfputil debug rx-ouput**
3256+
3257+
- Usage:
3258+
```
3259+
sfputil debug rx-output PORT_NAME <enable/disable>
3260+
3261+
```
3262+
3263+
- Example:
3264+
```
3265+
admin@sonic:~$ sfputil debug rx-output Ethernet88 enable
3266+
admin@sonic:~$ sfputil debug rx-output Ethernet88 disable
3267+
```
3268+
3269+
### CMIS debug tx-output
3270+
3271+
The command disables TX output by turning off the laser on the module, effectively blocking the optical signal.
3272+
3273+
**sfputil debug tx-ouput**
3274+
3275+
- Usage:
3276+
```
3277+
sfputil debug tx-output PORT_NAME <enable/disable>
3278+
3279+
```
3280+
3281+
- Example:
3282+
```
3283+
admin@sonic:~$ sfputil debug tx-output Ethernet88 enable
3284+
```
3285+
32493286
## DHCP Relay
32503287

32513288
### DHCP Relay show commands

sfputil/debug.py

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import sys
2+
import click
3+
import utilities_common.cli as clicommon
4+
from utilities_common import platform_sfputil_helper
5+
from utilities_common.platform_sfputil_helper import (
6+
get_subport,
7+
get_sfp_object,
8+
get_subport_lane_mask,
9+
get_media_lane_count,
10+
get_host_lane_count,
11+
)
12+
13+
EXIT_FAIL = -1
14+
EXIT_SUCCESS = 0
15+
ERROR_PERMISSIONS = 1
16+
ERROR_CHASSIS_LOAD = 2
17+
ERROR_SFPUTILHELPER_LOAD = 3
18+
ERROR_PORT_CONFIG_LOAD = 4
19+
ERROR_NOT_IMPLEMENTED = 5
20+
ERROR_INVALID_PORT = 6
21+
22+
23+
@click.group(cls=clicommon.AliasedGroup)
24+
def debug():
25+
"""
26+
Group for debugging and diagnostic control commands.
27+
28+
This command group loads platform-specific utilities and prepares them for use in diagnostic commands.
29+
"""
30+
platform_sfputil_helper.load_platform_sfputil()
31+
platform_sfputil_helper.load_chassis()
32+
platform_sfputil_helper.platform_sfputil_read_porttab_mappings()
33+
34+
35+
@debug.command()
36+
@click.argument('port_name', required=True)
37+
@click.argument(
38+
'loopback_mode',
39+
required=True,
40+
type=click.Choice(["host-side-input", "host-side-output", "media-side-input", "media-side-output"])
41+
)
42+
@click.argument('enable', required=True, type=click.Choice(["enable", "disable"]))
43+
def loopback(port_name, loopback_mode, enable):
44+
"""
45+
Set module diagnostic loopback mode.
46+
"""
47+
sfp = get_sfp_object(port_name)
48+
49+
try:
50+
api = sfp.get_xcvr_api()
51+
except NotImplementedError:
52+
click.echo(f"{port_name}: This functionality is not implemented")
53+
sys.exit(ERROR_NOT_IMPLEMENTED)
54+
55+
subport = get_subport(port_name)
56+
57+
host_lane_count = get_host_lane_count(port_name)
58+
59+
media_lane_count = get_media_lane_count(port_name)
60+
61+
lane_count = int(host_lane_count) if 'host-side' in loopback_mode else int(media_lane_count)
62+
lane_mask = get_subport_lane_mask(int(subport), lane_count)
63+
64+
try:
65+
status = api.set_loopback_mode(loopback_mode, lane_mask=lane_mask, enable=(enable == 'enable'))
66+
except AttributeError:
67+
click.echo(f"{port_name}: Set loopback mode is not applicable for this module")
68+
sys.exit(ERROR_NOT_IMPLEMENTED)
69+
except TypeError:
70+
click.echo(f"{port_name}: Set loopback mode failed. Parameter is not supported")
71+
sys.exit(EXIT_FAIL)
72+
73+
if status:
74+
click.echo(f"{port_name}: {enable} {loopback_mode} loopback")
75+
else:
76+
click.echo(f"{port_name}: {enable} {loopback_mode} loopback failed")
77+
sys.exit(EXIT_FAIL)
78+
79+
80+
def set_output(port_name, enable, direction):
81+
"""
82+
Enable or disable TX/RX output based on direction ('tx' or 'rx').
83+
"""
84+
sfp = get_sfp_object(port_name)
85+
86+
subport = get_subport(port_name)
87+
88+
media_lane_count = get_media_lane_count(port_name)
89+
90+
lane_mask = get_subport_lane_mask(int(subport), int(media_lane_count))
91+
92+
try:
93+
if direction == "tx":
94+
sfp.tx_disable_channel(lane_mask, enable == "disable")
95+
elif direction == "rx":
96+
sfp.rx_disable_channel(lane_mask, enable == "disable")
97+
98+
click.echo(
99+
f"{port_name}: {direction.upper()} output "
100+
f"{'disabled' if enable == 'disable' else 'enabled'} on subport {subport}"
101+
)
102+
103+
except AttributeError:
104+
click.echo(f"{port_name}: {direction.upper()} disable is not applicable for this module")
105+
sys.exit(ERROR_NOT_IMPLEMENTED)
106+
except Exception as e:
107+
click.echo(f"{port_name}: {direction.upper()} disable failed due to {str(e)}")
108+
sys.exit(EXIT_FAIL)
109+
110+
111+
@debug.command()
112+
@click.argument('port_name', required=True)
113+
@click.argument('enable', required=True, type=click.Choice(["enable", "disable"]))
114+
def tx_output(port_name, enable):
115+
"""Enable or disable TX output on a port."""
116+
set_output(port_name, enable, "tx")
117+
118+
119+
@debug.command()
120+
@click.argument('port_name', required=True)
121+
@click.argument('enable', required=True, type=click.Choice(["enable", "disable"]))
122+
def rx_output(port_name, enable):
123+
"""Enable or disable RX output on a port."""
124+
set_output(port_name, enable, "rx")

sfputil/main.py

+3-111
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import click
1818
import sonic_platform
1919
import sonic_platform_base.sonic_sfp.sfputilhelper
20+
from sfputil.debug import debug
2021
from sonic_platform_base.sfp_base import SfpBase
2122
from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector
2223
from natsort import natsorted
@@ -605,6 +606,8 @@ def cli():
605606
sys.exit(ERROR_PORT_CONFIG_LOAD)
606607

607608

609+
cli.add_command(debug)
610+
608611
# 'show' subgroup
609612
@cli.group()
610613
def show():
@@ -1965,116 +1968,5 @@ def get_overall_offset_sff8472(api, page, offset, size, wire_addr):
19651968
return page * PAGE_SIZE + offset + PAGE_SIZE_FOR_A0H
19661969

19671970

1968-
# 'debug' subgroup
1969-
@cli.group()
1970-
def debug():
1971-
"""Module debug and diagnostic control"""
1972-
pass
1973-
1974-
1975-
# 'loopback' subcommand
1976-
@debug.command()
1977-
@click.argument('port_name', required=True)
1978-
@click.argument('loopback_mode', required=True,
1979-
type=click.Choice(["host-side-input", "host-side-output",
1980-
"media-side-input", "media-side-output"]))
1981-
@click.argument('enable', required=True, type=click.Choice(["enable", "disable"]))
1982-
def loopback(port_name, loopback_mode, enable):
1983-
"""Set module diagnostic loopback mode
1984-
"""
1985-
physical_port = logical_port_to_physical_port_index(port_name)
1986-
sfp = platform_chassis.get_sfp(physical_port)
1987-
1988-
if is_port_type_rj45(port_name):
1989-
click.echo("{}: This functionality is not applicable for RJ45 port".format(port_name))
1990-
sys.exit(EXIT_FAIL)
1991-
1992-
if not is_sfp_present(port_name):
1993-
click.echo("{}: SFP EEPROM not detected".format(port_name))
1994-
sys.exit(EXIT_FAIL)
1995-
1996-
try:
1997-
api = sfp.get_xcvr_api()
1998-
except NotImplementedError:
1999-
click.echo("{}: This functionality is not implemented".format(port_name))
2000-
sys.exit(ERROR_NOT_IMPLEMENTED)
2001-
2002-
namespace = multi_asic.get_namespace_for_port(port_name)
2003-
config_db = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace)
2004-
if config_db is not None:
2005-
config_db.connect()
2006-
try:
2007-
subport = int(config_db.get(config_db.CONFIG_DB, f'PORT|{port_name}', 'subport'))
2008-
except TypeError:
2009-
click.echo(f"{port_name}: subport is not present in CONFIG_DB")
2010-
sys.exit(EXIT_FAIL)
2011-
2012-
# If subport is set to 0, assign a default value of 1 to ensure valid subport configuration
2013-
if subport == 0:
2014-
subport = 1
2015-
else:
2016-
click.echo(f"{port_name}: Failed to connect to CONFIG_DB")
2017-
sys.exit(EXIT_FAIL)
2018-
2019-
state_db = SonicV2Connector(use_unix_socket_path=False, namespace=namespace)
2020-
if state_db is not None:
2021-
state_db.connect(state_db.STATE_DB)
2022-
try:
2023-
host_lane_count = int(state_db.get(state_db.STATE_DB,
2024-
f'TRANSCEIVER_INFO|{port_name}',
2025-
'host_lane_count'))
2026-
except TypeError:
2027-
click.echo(f"{port_name}: host_lane_count is not present in STATE_DB")
2028-
sys.exit(EXIT_FAIL)
2029-
2030-
try:
2031-
media_lane_count = int(state_db.get(state_db.STATE_DB,
2032-
f'TRANSCEIVER_INFO|{port_name}',
2033-
'media_lane_count'))
2034-
except TypeError:
2035-
click.echo(f"{port_name}: media_lane_count is not present in STATE_DB")
2036-
sys.exit(EXIT_FAIL)
2037-
else:
2038-
click.echo(f"{port_name}: Failed to connect to STATE_DB")
2039-
sys.exit(EXIT_FAIL)
2040-
2041-
if 'host-side' in loopback_mode:
2042-
lane_mask = get_subport_lane_mask(subport, host_lane_count)
2043-
elif 'media-side' in loopback_mode:
2044-
lane_mask = get_subport_lane_mask(subport, media_lane_count)
2045-
else:
2046-
lane_mask = 0
2047-
2048-
try:
2049-
status = api.set_loopback_mode(loopback_mode,
2050-
lane_mask=lane_mask,
2051-
enable=enable == 'enable')
2052-
except AttributeError:
2053-
click.echo("{}: Set loopback mode is not applicable for this module".format(port_name))
2054-
sys.exit(ERROR_NOT_IMPLEMENTED)
2055-
except TypeError:
2056-
click.echo("{}: Set loopback mode failed. Parameter is not supported".format(port_name))
2057-
sys.exit(EXIT_FAIL)
2058-
2059-
if status:
2060-
click.echo("{}: {} {} loopback".format(port_name, enable, loopback_mode))
2061-
else:
2062-
click.echo("{}: {} {} loopback failed".format(port_name, enable, loopback_mode))
2063-
sys.exit(EXIT_FAIL)
2064-
2065-
2066-
def get_subport_lane_mask(subport, lane_count):
2067-
"""Get the lane mask for the given subport and lane count
2068-
2069-
Args:
2070-
subport (int): Subport number
2071-
lane_count (int): Lane count for the subport
2072-
2073-
Returns:
2074-
int: Lane mask for the given subport and lane count
2075-
"""
2076-
return ((1 << lane_count) - 1) << ((subport - 1) * lane_count)
2077-
2078-
20791971
if __name__ == '__main__':
20801972
cli()

0 commit comments

Comments
 (0)