Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add transceiver info CLI support to show output from TRANSCEIVER_INFO for ZR #2630

Merged
merged 8 commits into from
Feb 9, 2023
44 changes: 43 additions & 1 deletion doc/Command-Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -927,7 +927,7 @@ This command displays information for all the interfaces for the transceiver req

- Usage:
```
show interfaces transceiver (eeprom [-d|--dom] | lpmode | presence | error-status [-hw|--fetch-from-hardware] | pm) [<interface_name>]
show interfaces transceiver (eeprom [-d|--dom] | info | lpmode | presence | error-status [-hw|--fetch-from-hardware] | pm) [<interface_name>]
```

- Example (Decode and display information stored on the EEPROM of SFP transceiver connected to Ethernet0):
Expand Down Expand Up @@ -965,6 +965,48 @@ This command displays information for all the interfaces for the transceiver req
Vcc : 0.0000Volts
```

- Example (Decode and display information stored on the EEPROM of SFP transceiver connected to Ethernet16):
```
admin@sonic:~$ show interfaces transceiver info Ethernet16
Ethernet16: SFP EEPROM detected
Active Firmware: 61.20
Active application selected code assigned to host lane 1: 1
Active application selected code assigned to host lane 2: 1
Active application selected code assigned to host lane 3: 1
Active application selected code assigned to host lane 4: 1
Active application selected code assigned to host lane 5: 1
Active application selected code assigned to host lane 6: 1
Active application selected code assigned to host lane 7: 1
Active application selected code assigned to host lane 8: 1
Application Advertisement: 400GAUI-8 C2M (Annex 120E) - Host Assign (0x1) - 400ZR, DWDM, amplified - Media Assign (0x1)
400GAUI-8 C2M (Annex 120E) - Host Assign (0x1) - 400ZR, Single Wavelength, Unamplified - Media Assign (0x1)
100GAUI-2 C2M (Annex 135G) - Host Assign (0x55) - 400ZR, DWDM, amplified - Media Assign (0x1)
CMIS Rev: 4.1
Connector: LC
Encoding: N/A
Extended Identifier: Power Class 8 (20.0W Max)
Extended RateSelect Compliance: N/A
Host Lane Count: 8
Identifier: QSFP-DD Double Density 8X Pluggable Transceiver
Inactive Firmware: 61.20
Length Cable Assembly(m): 0.0
Media Interface Technology: 1550 nm DFB
Media Lane Count: 1
Module Hardware Rev: 49.49
Nominal Bit Rate(100Mbs): 0
Specification Compliance: sm_media_interface
Supported Max Laser Frequency: 196100
Supported Max TX Power: 4.0
Supported Min Laser Frequency: 191300
Supported Min TX Power: -22.9
Vendor Date Code(YYYY-MM-DD Lot): 2020-21-02 17
Vendor Name: Acacia Comm Inc.
Vendor OUI: 7c-b2-5c
Vendor PN: DP04QSDD-E20-00E
Vendor Rev: 01
Vendor SN: 210753986
```

- Example (Display status of low-power mode of SFP transceiver connected to Ethernet100):
```
admin@sonic:~$ show interfaces transceiver lpmode Ethernet100
Expand Down
46 changes: 42 additions & 4 deletions scripts/sfpshow
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ from natsort import natsorted
from sonic_py_common.interface import front_panel_prefix, backplane_prefix, inband_prefix, recirc_prefix
from sonic_py_common import multi_asic
from utilities_common.sfp_helper import covert_application_advertisement_to_output_string
from utilities_common.sfp_helper import QSFP_DATA_MAP
from utilities_common.sfp_helper import QSFP_DATA_MAP, CMIS_DATA_MAP
from tabulate import tabulate

# Mock the redis DB for unit test purposes
Expand Down Expand Up @@ -280,8 +280,26 @@ class SFPShow(object):
units)
return output

# Convert sfp info in DB to cli output string
def convert_sfp_info_to_output_string(self, sfp_info_dict):
# Convert cmis sfp info in DB to cli output string
def convert_cmis_sfp_info_to_output_string(self, sfp_info_dict):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like, convert_cmis_sfp_info_to_output_string and convert_non_cmis_sfp_info_to_output_string have quite some code in common.
would it better to combine them into one function to reuse the code more?
something like:

    def convert_sfp_info_to_output_string(self, sfp_info_dict):
        indent = ' ' * 8
        output = ''
        
        data_map = CMIS_DATA_MAP if is_cmis else QSFP_DATA_MAP
        sorted_data_map_keys = sorted(data_map, key= data_map.get)
        for key in sorted_data_map_keys:
            if key == 'cable_type':
                output += '{}{}: {}\n'.format(indent, sfp_info_dict['cable_type'], sfp_info_dict['cable_length'])
            elif key == 'cable_length':
                pass
            elif key == 'specification_compliance':
                if not is_cmis:
                     ....
                else:
                     ...
            elif key == 'application_advertisement':
                output += covert_application_advertisement_to_output_string(indent, sfp_info_dict)
            else:
                output += '{}{}: {}\n'.format(indent, data_map[key], sfp_info_dict[key])

        return output

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, addressed this now

indent = ' ' * 8
output = ''

sorted_cmis_data_map_keys = sorted(CMIS_DATA_MAP, key=CMIS_DATA_MAP.get)
for key in sorted_cmis_data_map_keys:
if key == 'cable_type':
output += '{}{}: {}\n'.format(indent, sfp_info_dict['cable_type'], sfp_info_dict['cable_length'])
elif key == 'cable_length':
pass
elif key == 'application_advertisement':
output += covert_application_advertisement_to_output_string(indent, sfp_info_dict)
else:
output += '{}{}: {}\n'.format(indent, CMIS_DATA_MAP[key], sfp_info_dict[key])

return output

# Convert non cmis sfp info in DB to cli output string
def convert_non_cmis_sfp_info_to_output_string(self, sfp_info_dict):
indent = ' ' * 8
output = ''

Expand Down Expand Up @@ -420,7 +438,10 @@ class SFPShow(object):
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)
if 'cmis_rev' in sfp_info_dict:
sfp_info_output = self.convert_cmis_sfp_info_to_output_string(sfp_info_dict)
else:
sfp_info_output = self.convert_non_cmis_sfp_info_to_output_string(sfp_info_dict)
output += sfp_info_output

if dump_dom:
Expand Down Expand Up @@ -587,6 +608,23 @@ def eeprom(port, dump_dom, namespace):
sfp.get_eeprom()
sfp.display_eeprom()

# 'eeprom' subcommand
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i thought this "info" does not come under as subcommand of 'eeprom' since we will remove 'eeprom' command in future?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is a typo in comment. I have corrected it now.


@cli.command()
@click.option('-p', '--port', metavar='<port_name>', help="Display SFP EEPROM data for port <port_name> only")
@click.option('-n', '--namespace', default=None, help="Display interfaces for specific namespace")
def info(port, namespace):
if port and multi_asic.is_multi_asic() and namespace is None:
try:
namespace = multi_asic.get_namespace_for_port(port)
except Exception:
display_invalid_intf_eeprom(port)
sys.exit(1)

sfp = SFPShow(port, namespace)
sfp.get_eeprom()
sfp.display_eeprom()

# 'presence' subcommand


Expand Down
22 changes: 22 additions & 0 deletions show/interfaces/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,28 @@ def pm(interfacename, namespace, verbose):

clicommon.run_command(cmd, display_cmd=verbose)

@transceiver.command()
@click.argument('interfacename', required=False)
@click.option('--namespace', '-n', 'namespace', default=None, show_default=True,
type=click.Choice(multi_asic_util.multi_asic_ns_choices()), help='Namespace name or all')
@click.option('--verbose', is_flag=True, help="Enable verbose output")
def info(interfacename, namespace, verbose):
"""Show interface transceiver information"""

ctx = click.get_current_context()

cmd = "sfpshow info"

if interfacename is not None:
interfacename = try_convert_interfacename_from_alias(ctx, interfacename)

cmd += " -p {}".format(interfacename)

if namespace is not None:
cmd += " -n {}".format(namespace)

clicommon.run_command(cmd, display_cmd=verbose)

@transceiver.command()
@click.argument('interfacename', required=False)
@click.option('--verbose', is_flag=True, help="Enable verbose output")
Expand Down
37 changes: 37 additions & 0 deletions tests/mock_tables/asic0/state_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,43 @@
"vcclowalarm": "2.9700",
"vcclowwarning": "3.1349"
},
"TRANSCEIVER_INFO|Ethernet48": {
"type" : "QSFP-DD Double Density 8X Pluggable Transceiver",
"hardware_rev" : "1.1",
"serial" : "214455197",
"manufacturer" : "Acacia Comm Inc.",
"model" : "DP04QSDD-E20-001",
"connector" : "LC",
"encoding" : "N/A",
"ext_identifier" : "Power Class 8 (20.0W Max)",
"ext_rateselect_compliance" : "N/A",
"cable_type" : "Length Cable Assembly(m)",
"cable_length" : "0.0",
"nominal_bit_rate" : "0",
"specification_compliance" : "sm_media_interface",
"vendor_date" : "2021-11-19",
"vendor_oui" : "7c-b2-5c",
"application_advertisement" : "{1: {'host_electrical_interface_id': '400GAUI-8 C2M (Annex 120E)', 'module_media_interface_id': '400ZR, DWDM, amplified', 'media_lane_count': 1, 'host_lane_count': 8, 'host_lane_assignment_options': 1, 'media_lane_assignment_options': 1}, 2: {'host_electrical_interface_id': '400GAUI-8 C2M (Annex 120E)', 'module_media_interface_id': '400ZR, Single Wavelength, Unamplified', 'media_lane_count': 1, 'host_lane_count': 8, 'host_lane_assignment_options': 1, 'media_lane_assignment_options': 1}, 3: {'host_electrical_interface_id': '100GAUI-2 C2M (Annex 135G)', 'module_media_interface_id': '400ZR, DWDM, amplified', 'media_lane_count': 1, 'host_lane_count': 2, 'host_lane_assignment_options': 85, 'media_lane_assignment_options': 1}}",
"host_lane_count" : "8",
"media_lane_count" : "1",
"active_apsel_hostlane1" : "1",
"active_apsel_hostlane2" : "1",
"active_apsel_hostlane3" : "1",
"active_apsel_hostlane4" : "1",
"active_apsel_hostlane5" : "1",
"active_apsel_hostlane6" : "1",
"active_apsel_hostlane7" : "1",
"active_apsel_hostlane8" : "1",
"media_interface_technology" : "1550 nm DFB",
"vendor_rev" : "A",
"cmis_rev" : "4.1",
"active_firmware" : "61.20",
"inactive_firmware" : "161.10",
"supported_max_tx_power" : "4.0",
"supported_min_tx_power" : "-22.9",
"supported_max_laser_freq" : "196100",
"supported_min_laser_freq" : "191300"
},
"CHASSIS_INFO|chassis 1": {
"psu_num": "2"
},
Expand Down
37 changes: 37 additions & 0 deletions tests/mock_tables/state_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,43 @@
"rx_sig_power_min": "-40",
"rx_sig_power_max": "40"
},
"TRANSCEIVER_INFO|Ethernet48": {
"type" : "QSFP-DD Double Density 8X Pluggable Transceiver",
"hardware_rev" : "1.1",
"serial" : "214455197",
"manufacturer" : "Acacia Comm Inc.",
"model" : "DP04QSDD-E20-001",
"connector" : "LC",
"encoding" : "N/A",
"ext_identifier" : "Power Class 8 (20.0W Max)",
"ext_rateselect_compliance" : "N/A",
"cable_type" : "Length Cable Assembly(m)",
"cable_length" : "0.0",
"nominal_bit_rate" : "0",
"specification_compliance" : "sm_media_interface",
"vendor_date" : "2021-11-19",
"vendor_oui" : "7c-b2-5c",
"application_advertisement" : "{1: {'host_electrical_interface_id': '400GAUI-8 C2M (Annex 120E)', 'module_media_interface_id': '400ZR, DWDM, amplified', 'media_lane_count': 1, 'host_lane_count': 8, 'host_lane_assignment_options': 1, 'media_lane_assignment_options': 1}, 2: {'host_electrical_interface_id': '400GAUI-8 C2M (Annex 120E)', 'module_media_interface_id': '400ZR, Single Wavelength, Unamplified', 'media_lane_count': 1, 'host_lane_count': 8, 'host_lane_assignment_options': 1, 'media_lane_assignment_options': 1}, 3: {'host_electrical_interface_id': '100GAUI-2 C2M (Annex 135G)', 'module_media_interface_id': '400ZR, DWDM, amplified', 'media_lane_count': 1, 'host_lane_count': 2, 'host_lane_assignment_options': 85, 'media_lane_assignment_options': 1}}",
"host_lane_count" : "8",
"media_lane_count" : "1",
"active_apsel_hostlane1" : "1",
"active_apsel_hostlane2" : "1",
"active_apsel_hostlane3" : "1",
"active_apsel_hostlane4" : "1",
"active_apsel_hostlane5" : "1",
"active_apsel_hostlane6" : "1",
"active_apsel_hostlane7" : "1",
"active_apsel_hostlane8" : "1",
"media_interface_technology" : "1550 nm DFB",
"vendor_rev" : "A",
"cmis_rev" : "4.1",
"active_firmware" : "61.20",
"inactive_firmware" : "161.10",
"supported_max_tx_power" : "4.0",
"supported_min_tx_power" : "-22.9",
"supported_max_laser_freq" : "196100",
"supported_min_laser_freq" : "191300"
},
"TRANSCEIVER_STATUS|Ethernet0": {
"status": "67",
"error": "Blocking Error|High temperature"
Expand Down
52 changes: 52 additions & 0 deletions tests/sfp_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,46 @@
EVM % 100.0 100.0 100.0 N/A N/A N/A N/A N/A N/A
"""

test_cmis_eeprom_output = """\
Ethernet48: SFP EEPROM detected
Active Firmware: 61.20
Active application selected code assigned to host lane 1: 1
Active application selected code assigned to host lane 2: 1
Active application selected code assigned to host lane 3: 1
Active application selected code assigned to host lane 4: 1
Active application selected code assigned to host lane 5: 1
Active application selected code assigned to host lane 6: 1
Active application selected code assigned to host lane 7: 1
Active application selected code assigned to host lane 8: 1
Application Advertisement: 400GAUI-8 C2M (Annex 120E) - Host Assign (0x1) - 400ZR, DWDM, amplified - Media Assign (0x1)
400GAUI-8 C2M (Annex 120E) - Host Assign (0x1) - 400ZR, Single Wavelength, Unamplified - Media Assign (0x1)
100GAUI-2 C2M (Annex 135G) - Host Assign (0x55) - 400ZR, DWDM, amplified - Media Assign (0x1)
CMIS Rev: 4.1
Connector: LC
Encoding: N/A
Extended Identifier: Power Class 8 (20.0W Max)
Extended RateSelect Compliance: N/A
Host Lane Count: 8
Identifier: QSFP-DD Double Density 8X Pluggable Transceiver
Inactive Firmware: 161.10
Length Cable Assembly(m): 0.0
Media Interface Technology: 1550 nm DFB
Media Lane Count: 1
Module Hardware Rev: 1.1
Nominal Bit Rate(100Mbs): 0
Specification Compliance: sm_media_interface
Supported Max Laser Frequency: 196100
Supported Max TX Power: 4.0
Supported Min Laser Frequency: 191300
Supported Min TX Power: -22.9
Vendor Date Code(YYYY-MM-DD Lot): 2021-11-19
Vendor Name: Acacia Comm Inc.
Vendor OUI: 7c-b2-5c
Vendor PN: DP04QSDD-E20-001
Vendor Rev: A
Vendor SN: 214455197
"""

test_sfp_eeprom_dom_all_output = """\
Ethernet0: SFP EEPROM detected
Application Advertisement: N/A
Expand Down Expand Up @@ -464,6 +504,12 @@ def test_qsfp_dd_eeprom_adv_app(self):
print(result.output)
assert result.output == test_qsfp_dd_eeprom_adv_app_output

def test_cmis_info(self):
runner = CliRunner()
result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["info"], ["Ethernet48"])
assert result.exit_code == 0
assert result.output == test_cmis_eeprom_output

def test_rj45_eeprom(self):
runner = CliRunner()
result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["eeprom"], ["Ethernet36"])
Expand Down Expand Up @@ -545,6 +591,12 @@ def test_qsfp_dd_pm_with_ns(self):
expected = "Ethernet0: Transceiver performance monitoring not applicable"
assert result_lines == expected

def test_cmis_sfp_info_with_ns(self):
runner = CliRunner()
result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["info"], ["Ethernet48 -n asic0"])
assert result.exit_code == 0
assert "\n".join([ l.rstrip() for l in result.output.split('\n')]) == test_cmis_eeprom_output

def test_sfp_eeprom_all(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to add test_cmis_info_all testcase for "show int tran info"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a testcase now.

runner = CliRunner()
result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["eeprom"])
Expand Down
37 changes: 37 additions & 0 deletions utilities_common/sfp_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,43 @@
'application_advertisement': 'Application Advertisement'
}

CMIS_DATA_MAP = {
Copy link
Contributor

@longhuan-cisco longhuan-cisco Feb 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CMIS_DATA_MAP seems to be just a super set of QSFP_DATA_MAP.
Would it be better to only list the CMIS specific data here (the delta), then later get the entire cmis_data_map by doing xxx_map.update(CMIS_SPECIFIC_DATA_MAP)? (the naming for CMIS_SPECIFC_DATA_MAP might not be perfect, you may choose whatever naming you feel suited it better)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, have modified based on this now.

'model': 'Vendor PN',
'vendor_oui': 'Vendor OUI',
'vendor_date': 'Vendor Date Code(YYYY-MM-DD Lot)',
'manufacturer': 'Vendor Name',
'vendor_rev': 'Vendor Rev',
'serial': 'Vendor SN',
'type': 'Identifier',
'ext_identifier': 'Extended Identifier',
'ext_rateselect_compliance': 'Extended RateSelect Compliance',
'cable_length': 'cable_length',
'cable_type': 'Length',
'nominal_bit_rate': 'Nominal Bit Rate(100Mbs)',
'specification_compliance': 'Specification Compliance',
'encoding': 'Encoding',
'connector': 'Connector',
'application_advertisement': 'Application Advertisement',
'host_lane_count': 'Host Lane Count',
'media_lane_count': 'Media Lane Count',
'active_apsel_hostlane1': 'Active application selected code assigned to host lane 1',
'active_apsel_hostlane2': 'Active application selected code assigned to host lane 2',
'active_apsel_hostlane3': 'Active application selected code assigned to host lane 3',
'active_apsel_hostlane4': 'Active application selected code assigned to host lane 4',
'active_apsel_hostlane5': 'Active application selected code assigned to host lane 5',
'active_apsel_hostlane6': 'Active application selected code assigned to host lane 6',
'active_apsel_hostlane7': 'Active application selected code assigned to host lane 7',
'active_apsel_hostlane8': 'Active application selected code assigned to host lane 8',
'media_interface_technology': 'Media Interface Technology',
'hardware_rev': 'Module Hardware Rev',
'cmis_rev': 'CMIS Rev',
'active_firmware': 'Active Firmware',
'inactive_firmware': 'Inactive Firmware',
'supported_max_tx_power': 'Supported Max TX Power',
'supported_min_tx_power': 'Supported Min TX Power',
'supported_max_laser_freq': 'Supported Max Laser Frequency',
'supported_min_laser_freq': 'Supported Min Laser Frequency'
}

def covert_application_advertisement_to_output_string(indent, sfp_info_dict):
key = 'application_advertisement'
Expand Down