Skip to content

Commit

Permalink
Add Transceiver PM basic CLI support to show output from TRANSCEIVER_…
Browse files Browse the repository at this point in the history
…PM table for ZR (#2655)
  • Loading branch information
longhuan-cisco authored Feb 9, 2023
1 parent 496a077 commit 0ffb4b6
Show file tree
Hide file tree
Showing 5 changed files with 373 additions and 8 deletions.
26 changes: 25 additions & 1 deletion doc/Command-Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -12468,7 +12468,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]) [<interface_name>]
show interfaces transceiver (eeprom [-d|--dom] | 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 @@ -12531,6 +12531,30 @@ This command displays information for all the interfaces for the transceiver req
Ethernet100 OK
```

- Example (Display performance monitoring info of SFP transceiver connected to Ethernet100):
```
admin@sonic:~$ show interfaces transceiver pm Ethernet100
Ethernet100:
Parameter Unit Min Avg Max Threshold Threshold Threshold Threshold Threshold Threshold
High High Crossing Low Low Crossing
Alarm Warning Alert-High Alarm Warning Alert-Low
--------------- ------ -------- -------- -------- ----------- ----------- ------------ ----------- ----------- -----------
Tx Power dBm -8.22 -8.23 -8.24 -5.0 -6.0 False -16.99 -16.003 False
Rx Total Power dBm -10.61 -10.62 -10.62 2.0 0.0 False -21.0 -18.0 False
Rx Signal Power dBm -40.0 0.0 40.0 13.0 10.0 True -18.0 -15.0 True
CD-short link ps/nm 0.0 0.0 0.0 1000.0 500.0 False -1000.0 -500.0 False
PDL dB 0.5 0.6 0.6 4.0 4.0 False 0.0 0.0 False
OSNR dB 36.5 36.5 36.5 99.0 99.0 False 0.0 0.0 False
eSNR dB 30.5 30.5 30.5 99.0 99.0 False 0.0 0.0 False
CFO MHz 54.0 70.0 121.0 3800.0 3800.0 False -3800.0 -3800.0 False
DGD ps 5.37 5.56 5.81 7.0 7.0 False 0.0 0.0 False
SOPMD ps^2 0.0 0.0 0.0 655.35 655.35 False 0.0 0.0 False
SOP ROC krad/s 1.0 1.0 2.0 N/A N/A N/A N/A N/A N/A
Pre-FEC BER N/A 4.58E-04 4.66E-04 5.76E-04 1.25E-02 1.10E-02 0.0 0.0 0.0 0.0
Post-FEC BER N/A 0.0 0.0 0.0 1000.0 1.0 False 0.0 0.0 False
EVM % 100.0 100.0 100.0 N/A N/A N/A N/A N/A N/A
```

Go Back To [Beginning of the document](#) or [Beginning of this section](#basic-show-commands)

## AAA & TACACS+
Expand Down
129 changes: 129 additions & 0 deletions scripts/sfpshow
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,36 @@ QSFP_DD_DOM_VALUE_UNIT_MAP = {
'voltage': 'Volts'
}

ZR_PM_HEADER = ['Parameter', 'Unit', 'Min', 'Avg', 'Max',
'Threshold\nHigh\nAlarm', 'Threshold\nHigh\nWarning',
'Threshold\nCrossing\nAlert-High',
'Threshold\nLow\nAlarm', 'Threshold\nLow\nWarning',
'Threshold\nCrossing\nAlert-Low']

ZR_PM_VALUE_KEY_SUFFIXS = ['min', 'avg', 'max']

ZR_PM_THRESHOLD_KEY_SUFFIXS = ['highalarm',
'highwarning', 'lowalarm', 'lowwarning']

# mapping from parameter_name to [unit, parameter_key_prefix]
ZR_PM_INFO_MAP = {
'Tx Power': ['dBm', 'tx_power'],
'Rx Total Power': ['dBm', 'rx_tot_power'],
'Rx Signal Power': ['dBm', 'rx_sig_power'],
'CD-short link': ['ps/nm', 'cd'],
'PDL': ['dB', 'pdl'],
'OSNR': ['dB', 'osnr'],
'eSNR': ['dB', 'esnr'],
'CFO': ['MHz', 'cfo'],
'DGD': ['ps', 'dgd'],
'SOPMD': ['ps^2', 'sopmd'],
'SOP ROC': ['krad/s', 'soproc'],
'Pre-FEC BER': ['N/A', 'prefec_ber'],
'Post-FEC BER': ['N/A', 'uncorr_frames'],
'EVM': ['%', 'evm']
}

ZR_PM_NOT_APPLICABLE_STR = 'Transceiver performance monitoring not applicable'

def display_invalid_intf_eeprom(intf_name):
output = intf_name + ': SFP EEPROM Not detected\n'
Expand All @@ -215,6 +245,10 @@ def display_invalid_intf_presence(intf_name):
click.echo(tabulate(port_table, header))


def display_invalid_intf_pm(intf_name):
output = intf_name + ': %s\n' % ZR_PM_NOT_APPLICABLE_STR
click.echo(output)

class SFPShow(object):
def __init__(self, intf_name, namespace_option, dump_dom=False):
super(SFPShow, self).__init__()
Expand All @@ -223,6 +257,7 @@ class SFPShow(object):
self.dump_dom = dump_dom
self.table = []
self.intf_eeprom: Dict[str, str] = {}
self.intf_pm: Dict[str, str] = {}
self.multi_asic = multi_asic_util.MultiAsic(namespace_option=namespace_option)

# Convert dict values to cli output string
Expand Down Expand Up @@ -402,6 +437,66 @@ class SFPShow(object):

return output

def convert_pm_prefix_to_threshold_prefix(self, pm_prefix):
if pm_prefix == 'uncorr_frames':
return 'postfecber'
elif pm_prefix == 'cd':
return 'cdshort'
else:
return pm_prefix.replace('_', '')

def beautify_pm_field(self, prefix, field):
if field is None:
return 'N/A'
elif prefix in {'prefec_ber'}:
return "{:.2E}".format(field) if field != 0 else '0.0'
else:
return str(field)

def convert_interface_sfp_pm_to_cli_output_string(self, state_db, interface_name):
sfp_pm_dict = state_db.get_all(
self.db.STATE_DB, 'TRANSCEIVER_PM|{}'.format(interface_name))
sfp_threshold_dict = state_db.get_all(
state_db.STATE_DB, 'TRANSCEIVER_DOM_THRESHOLD|{}'.format(interface_name))
table = []
indent_num = 4
indent = ' ' * indent_num
if sfp_pm_dict:
output = '\n' + indent
for param_name, (unit, prefix) in ZR_PM_INFO_MAP.items():
row = [param_name, unit]
values = []
for suffix in ZR_PM_VALUE_KEY_SUFFIXS:
key = prefix + '_' + suffix
values.append(
float(sfp_pm_dict[key]) if key in sfp_pm_dict else None)

thresholds = []
for suffix in ZR_PM_THRESHOLD_KEY_SUFFIXS:
key = self.convert_pm_prefix_to_threshold_prefix(
prefix) + suffix
thresholds.append(
float(sfp_threshold_dict[key]) if key in sfp_threshold_dict else None)

tca_high, tca_low = None, None
if values[2] is not None and thresholds[0] is not None:
# TCA-High: max > high_alarm
tca_high = values[2] > thresholds[0]
if values[0] is not None and thresholds[2] is not None:
# TCA-low: min < low_alarm
tca_low = values[0] < thresholds[2]

for field in values + thresholds[:2] + [tca_high] + thresholds[2:] + [tca_low]:
row.append(self.beautify_pm_field(prefix, field))
table.append(row)

output += tabulate(table,
ZR_PM_HEADER, disable_numparse=True).replace('\n', '\n' + indent)
output += '\n'
else:
output = ZR_PM_NOT_APPLICABLE_STR + '\n'
return output

@multi_asic_util.run_on_multi_asic
def get_eeprom(self):
if self.intf_name is not None:
Expand Down Expand Up @@ -441,6 +536,19 @@ class SFPShow(object):

self.table += port_table

@multi_asic_util.run_on_multi_asic
def get_pm(self):
if self.intf_name is not None:
self.intf_pm[self.intf_name] = self.convert_interface_sfp_pm_to_cli_output_string(
self.db, self.intf_name)
else:
port_table_keys = self.db.keys(self.db.APPL_DB, "PORT_TABLE:*")
for i in port_table_keys:
interface = re.split(':', i, maxsplit=1)[-1].strip()
if interface and interface.startswith(front_panel_prefix()) and not interface.startswith((backplane_prefix(), inband_prefix(), recirc_prefix())):
self.intf_pm[interface] = self.convert_interface_sfp_pm_to_cli_output_string(
self.db, interface)

def display_eeprom(self):
click.echo("\n".join([f"{k}: {v}" for k, v in natsorted(self.intf_eeprom.items())]))

Expand All @@ -449,6 +557,9 @@ class SFPShow(object):
sorted_port_table = natsorted(self.table)
click.echo(tabulate(sorted_port_table, header))

def display_pm(self):
click.echo(
"\n".join([f"{k}: {v}" for k, v in natsorted(self.intf_pm.items())]))
# This is our main entrypoint - the main 'sfpshow' command


Expand Down Expand Up @@ -494,6 +605,24 @@ def presence(port, namespace):
sfp.get_presence()
sfp.display_presence()

# 'pm' subcommand


@cli.command()
@click.option('-p', '--port', metavar='<port_name>', help="Display SFP PM for port <port_name> only")
@click.option('-n', '--namespace', default=None, help="Display interfaces for specific namespace")
def pm(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_pm(port)
sys.exit(1)

sfp = SFPShow(port, namespace)
sfp.get_pm()
sfp.display_pm()


if __name__ == "__main__":
cli()
23 changes: 23 additions & 0 deletions show/interfaces/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,29 @@ def eeprom(interfacename, dump_dom, 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 pm(interfacename, namespace, verbose):
"""Show interface transceiver performance monitoring information"""

ctx = click.get_current_context()

cmd = "sfpshow pm"

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
Loading

0 comments on commit 0ffb4b6

Please sign in to comment.