Skip to content

Commit

Permalink
[sfpshow/sfputil] Enhance sfpshow and sfputil to behavior correctly o…
Browse files Browse the repository at this point in the history
…n RJ45 ports (#2111)

* enhance show interface transceiver eeprom logic with RJ45 port support

Signed-off-by: Kebo Liu <kebol@nvidia.com>

* enhance sfputil to support RJ45 port, exclude error status

* fix sfputil issue on RJ45 port

Signed-off-by: Kebo Liu <kebol@nvidia.com>

* [RJ45] change the way to judge port type and add more UT test case

Signed-off-by: Kebo Liu <kebol@nvidia.com>

* [sfputil] simplity the logic for RJ45 support

Signed-off-by: Kebo Liu <kebol@nvidia.com>

* Support sfputil show present

Signed-off-by: Stephen Sun <stephens@nvidia.com>

* Support rj45 in sfpshow

Signed-off-by: Stephen Sun <stephens@nvidia.com>

* Add test case for sfputil with RJ45 supported

Signed-off-by: Stephen Sun <stephens@nvidia.com>

* Add mock data for RJ45 ports into STATE_DB

Signed-off-by: Stephen Sun <stephens@nvidia.com>

* Add test for sfputil show for RJ45 ports

Signed-off-by: Stephen Sun <stephens@nvidia.com>

* remove debug code in sfputil test case

Signed-off-by: Kebo Liu <kebol@nvidia.com>

* remove unnecessary argument for format()

Signed-off-by: Kebo Liu <kebol@nvidia.com>

* Revert the logic to fetch presence status from error status for RJ45 port (#17)

* Revert the logic to fetch presence status from error status

Signed-off-by: Stephen Sun <stephens@nvidia.com>

* Unit test

Signed-off-by: Stephen Sun <stephens@nvidia.com>

* Fix error

Signed-off-by: Stephen Sun <stephens@nvidia.com>

* Add test cases to cover lpmode and error status

Signed-off-by: Stephen Sun <stephens@nvidia.com>

* add comments to describe the usage of functions to judge the port type

Signed-off-by: Kebo Liu <kebol@nvidia.com>

* add more testcase for sfputil

Signed-off-by: Kebo Liu <kebol@nvidia.com>

* fix typo in testcase name

Signed-off-by: Kebo Liu <kebol@nvidia.com>

Co-authored-by: Stephen Sun <stephens@nvidia.com>
Co-authored-by: Stephen Sun <5379172+stephenxs@users.noreply.github.com>
  • Loading branch information
3 people committed Jun 24, 2022
1 parent 2f6a547 commit f64d280
Show file tree
Hide file tree
Showing 5 changed files with 349 additions and 34 deletions.
23 changes: 14 additions & 9 deletions scripts/sfpshow
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ QSFP_DD_DOM_VALUE_UNIT_MAP = {
'voltage': 'Volts'
}

RJ45_PORT_TYPE = 'RJ45'


def display_invalid_intf_eeprom(intf_name):
output = intf_name + ': SFP EEPROM Not detected\n'
Expand Down Expand Up @@ -392,15 +394,18 @@ class SFPShow(object):
output = ''

sfp_info_dict = state_db.get_all(state_db.STATE_DB, 'TRANSCEIVER_INFO|{}'.format(interface_name))
output = 'SFP EEPROM detected\n'
sfp_info_output = self.convert_sfp_info_to_output_string(sfp_info_dict)
output += sfp_info_output

if dump_dom:
sfp_type = sfp_info_dict['type']
dom_info_dict = state_db.get_all(state_db.STATE_DB, 'TRANSCEIVER_DOM_SENSOR|{}'.format(interface_name))
dom_output = self.convert_dom_to_output_string(sfp_type, dom_info_dict)
output += dom_output
if sfp_info_dict['type'] == RJ45_PORT_TYPE:
output = 'SFP EEPROM is not applicable for RJ45 port\n'
else:
output = 'SFP EEPROM detected\n'
sfp_info_output = self.convert_sfp_info_to_output_string(sfp_info_dict)
output += sfp_info_output

if dump_dom:
sfp_type = sfp_info_dict['type']
dom_info_dict = state_db.get_all(state_db.STATE_DB, 'TRANSCEIVER_DOM_SENSOR|{}'.format(interface_name))
dom_output = self.convert_dom_to_output_string(sfp_type, dom_info_dict)
output += dom_output

return output

Expand Down
113 changes: 89 additions & 24 deletions sfputil/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@
'voltage': 'Volts'
}

RJ45_PORT_TYPE = 'RJ45'

# Global platform-specific Chassis class instance
platform_chassis = None
Expand All @@ -289,6 +290,34 @@ def is_sfp_present(port_name):

return bool(presence)


# Below defined two flavors of functions to determin whether a port is a RJ45 port.
# They serve different types of SFP utilities. One type of SFP utility consume the
# info stored in the STATE_DB, these utilities shall call 'is_rj45_port_from_db'
# to judge the port type. Another type of utilities will call the platform API
# directly to access SFP, for them shall use 'is_rj45_port_from_api'.
def is_rj45_port_from_db(port_name, db):
intf_type = db.get(db.STATE_DB, 'TRANSCEIVER_INFO|{}'.format(port_name), 'type')
return intf_type == RJ45_PORT_TYPE


def is_rj45_port_from_api(port_name):
physical_port = logical_port_to_physical_port_index(port_name)
sfp = platform_chassis.get_sfp(physical_port)

try:
port_type = sfp.get_transceiver_info()['type']
except NotImplementedError:
click.echo("Not able to judge the port type due to get_transceiver_info not implemented!", err=True)
sys.exit(ERROR_NOT_IMPLEMENTED)

return port_type == RJ45_PORT_TYPE


def skip_if_port_is_rj45(port_name):
if is_rj45_port_from_api(port_name):
click.echo("This functionality is not applicable for RJ45 port {}.".format(port_name))
sys.exit(EXIT_FAIL)
# ========================== Methods for formatting output ==========================

# Convert dict values to cli output string
Expand Down Expand Up @@ -630,6 +659,11 @@ def eeprom(port, dump_dom, namespace):
for physical_port in physical_port_list:
port_name = get_physical_port_name(logical_port_name, i, ganged)

if is_rj45_port_from_api(port_name):
output += "{}: SFP EEPROM is not applicable for RJ45 port\n".format(port_name)
output += '\n'
continue

try:
presence = platform_chassis.get_sfp(physical_port).get_presence()
except NotImplementedError:
Expand Down Expand Up @@ -783,7 +817,10 @@ def fetch_error_status_from_platform_api(port):
physical_port_list = logical_port_name_to_physical_port_list(logical_port_name)
port_name = get_physical_port_name(logical_port_name, 1, False)

output.append([port_name, output_dict.get(physical_port_list[0])])
if is_rj45_port_from_api(logical_port_name):
output.append([port_name, "N/A"])
else:
output.append([port_name, output_dict.get(physical_port_list[0])])

return output

Expand All @@ -806,15 +843,18 @@ def fetch_error_status_from_state_db(port, state_db):
sorted_ports = natsort.natsorted(status)
output = []
for port in sorted_ports:
statestring = status[port].get('status')
description = status[port].get('error')
if statestring == '1':
description = 'OK'
elif statestring == '0':
description = 'Unplugged'
elif description == 'N/A':
log.log_error("Inconsistent state found for port {}: state is {} but error description is N/A".format(port, statestring))
description = 'Unknown state: {}'.format(statestring)
if is_rj45_port_from_db(port, state_db):
description = "N/A"
else:
statestring = status[port].get('status')
description = status[port].get('error')
if statestring == '1':
description = 'OK'
elif statestring == '0':
description = 'Unplugged'
elif description == 'N/A':
log.log_error("Inconsistent state found for port {}: state is {} but error description is N/A".format(port, statestring))
description = 'Unknown state: {}'.format(statestring)

output.append([port, description])

Expand Down Expand Up @@ -879,24 +919,27 @@ def lpmode(port):
click.echo("Error: No physical ports found for logical port '{}'".format(logical_port_name))
return

if len(physical_port_list) > 1:
ganged = True
if is_rj45_port_from_api(logical_port_name):
output_table.append([logical_port_name, "N/A"])
else:
if len(physical_port_list) > 1:
ganged = True

for physical_port in physical_port_list:
port_name = get_physical_port_name(logical_port_name, i, ganged)
for physical_port in physical_port_list:
port_name = get_physical_port_name(logical_port_name, i, ganged)

try:
lpmode = platform_chassis.get_sfp(physical_port).get_lpmode()
except NotImplementedError:
click.echo("This functionality is currently not implemented for this platform")
sys.exit(ERROR_NOT_IMPLEMENTED)
try:
lpmode = platform_chassis.get_sfp(physical_port).get_lpmode()
except NotImplementedError:
click.echo("This functionality is currently not implemented for this platform")
sys.exit(ERROR_NOT_IMPLEMENTED)

if lpmode:
output_table.append([port_name, "On"])
else:
output_table.append([port_name, "Off"])
if lpmode:
output_table.append([port_name, "On"])
else:
output_table.append([port_name, "Off"])

i += 1
i += 1

click.echo(tabulate(output_table, table_header, tablefmt='simple'))

Expand All @@ -919,6 +962,10 @@ def fwversion(port_name):
physical_port = logical_port_to_physical_port_index(port_name)
sfp = platform_chassis.get_sfp(physical_port)

if is_rj45_port_from_api(port_name):
click.echo("Show firmware version is not applicable for RJ45 port {}.".format(port_name))
sys.exit(EXIT_FAIL)

try:
presence = sfp.get_presence()
except NotImplementedError:
Expand Down Expand Up @@ -954,6 +1001,10 @@ def set_lpmode(logical_port, enable):
click.echo("Error: No physical ports found for logical port '{}'".format(logical_port))
return

if is_rj45_port_from_api(logical_port):
click.echo("{} low-power mode is not applicable for RJ45 port {}.".format("Enabling" if enable else "Disabling", logical_port))
sys.exit(EXIT_FAIL)

if len(physical_port_list) > 1:
ganged = True

Expand Down Expand Up @@ -1010,6 +1061,10 @@ def reset(port_name):
click.echo("Error: No physical ports found for logical port '{}'".format(port_name))
return

if is_rj45_port_from_api(port_name):
click.echo("Reset is not applicable for RJ45 port {}.".format(port_name))
sys.exit(EXIT_FAIL)

if len(physical_port_list) > 1:
ganged = True

Expand Down Expand Up @@ -1175,6 +1230,8 @@ def run(port_name, mode):
click.echo("{}: SFP EEPROM not detected\n".format(port_name))
sys.exit(EXIT_FAIL)

skip_if_port_is_rj45(port_name)

status = run_firmware(port_name, int(mode))
if status != 1:
click.echo('Failed to run firmware in mode={}! CDB status: {}'.format(mode, status))
Expand All @@ -1192,6 +1249,8 @@ def commit(port_name):
click.echo("{}: SFP EEPROM not detected\n".format(port_name))
sys.exit(EXIT_FAIL)

skip_if_port_is_rj45(port_name)

status = commit_firmware(port_name)
if status != 1:
click.echo('Failed to commit firmware! CDB status: {}'.format(status))
Expand All @@ -1212,6 +1271,8 @@ def upgrade(port_name, filepath):
click.echo("{}: SFP EEPROM not detected\n".format(port_name))
sys.exit(EXIT_FAIL)

skip_if_port_is_rj45(port_name)

show_firmware_version(physical_port)

status = download_firmware(port_name, filepath)
Expand Down Expand Up @@ -1246,6 +1307,8 @@ def download(port_name, filepath):
click.echo("{}: SFP EEPROM not detected\n".format(port_name))
sys.exit(EXIT_FAIL)

skip_if_port_is_rj45(port_name)

start = time.time()
status = download_firmware(port_name, filepath)
if status == 1:
Expand All @@ -1266,6 +1329,8 @@ def unlock(port_name, password):
physical_port = logical_port_to_physical_port_index(port_name)
sfp = platform_chassis.get_sfp(physical_port)

skip_if_port_is_rj45(port_name)

if not is_sfp_present(port_name):
click.echo("{}: SFP EEPROM not detected\n".format(port_name))
sys.exit(EXIT_FAIL)
Expand Down
12 changes: 12 additions & 0 deletions tests/mock_tables/state_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,18 @@
"status": "255",
"error": "N/A"
},
"TRANSCEIVER_STATUS|Ethernet16": {
"status": "0",
"error": "N/A"
},
"TRANSCEIVER_STATUS|Ethernet28": {
"status": "0",
"error": "N/A"
},
"TRANSCEIVER_STATUS|Ethernet36": {
"status": "255",
"error": "Unknown"
},
"CHASSIS_INFO|chassis 1": {
"psu_num": "2"
},
Expand Down
31 changes: 31 additions & 0 deletions tests/sfp_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,30 @@ def test_sfp_presence(self):
expected = """Port Presence
----------- -----------
Ethernet200 Not present
"""
assert result.exit_code == 0
assert result.output == expected

result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["presence"], ["Ethernet16"])
expected = """Port Presence
---------- ----------
Ethernet16 Present
"""
assert result.exit_code == 0
assert result.output == expected

result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["presence"], ["Ethernet28"])
expected = """Port Presence
---------- ----------
Ethernet28 Present
"""
assert result.exit_code == 0
assert result.output == expected

result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["presence"], ["Ethernet36"])
expected = """Port Presence
---------- ----------
Ethernet36 Present
"""
assert result.exit_code == 0
assert result.output == expected
Expand Down Expand Up @@ -377,6 +401,13 @@ def test_qsfp_dd_eeprom(self):
assert result.exit_code == 0
assert "result.output == test_qsfp_dd_eeprom_output"

def test_rj45_eeprom(self):
runner = CliRunner()
result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["eeprom"], ["Ethernet36"])
result_lines = result.output.strip('\n')
expected = "Ethernet36: SFP EEPROM is not applicable for RJ45 port"
assert result_lines == expected

@classmethod
def teardown_class(cls):
print("TEARDOWN")
Expand Down
Loading

0 comments on commit f64d280

Please sign in to comment.