Skip to content

Commit ea2fb57

Browse files
committed
Transceiver CLI changes to support DOM and STATUS table related changes
<!-- Please make sure you've read and understood our contributing guidelines: https://github.com/Azure/SONiC/blob/gh-pages/CONTRIBUTING.md ** 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: --> #### What I did 1. With the recent transceiver DOM and STATUS table related changes, the handles of the following CLIs need to be modified to ensure that the CLI function inline to the existing behavior 1. show int transceiver eeprom -d 2. show int transceiver status 3. show int transceiver pm 4. sfputil show eeprom -d -p EthernetXX 2. Also, all the diagnostics related tables will not be present only for the first subport for breakout ports. Hence, made the corresponding CLI handler related changes to support this 3. Modified the `sfputil show error-status` CLI handler to use `TRANSCEIVER_STATUS_SW` instead of `TRANSCEIVER_STATUS` table #### How I did it 1. Replaced function call for get_transceiver_bulk_status with get_transceiver_dom_real_value 5. As part of displaying o/p for "show int transceiver status" CLI, we are now fetching data from TRANSCEIVER_DOM_FLAG, VDM flag tables and TRANSCEIVER_STATUS_FLAG tables as well since the relevant fields have been moved to these new tables 6. As part of display o/p for "show int transceiver pm" CLI, we are now fetching data from VDM threshold tables as well since the relevant fields have been moved to these new tables Related PRs sonic-net/SONiC#1954 [[cmis] Separate Flag-Specific Fields for DOM and Status APIs by mihirpat1 · Pull Request #556 · sonic-net/sonic-platform-common](sonic-net/sonic-platform-common#556) [[xcvrd] Re-organize transceiver DOM and STATUS tables by mihirpat1 · Pull Request #604 · sonic-net/sonic-platform-daemons](sonic-net/sonic-platform-daemons#604) #### How to verify it Following tests were attempted and confirmed to pass 1. Diagnostic table related verification 1. Compare tables with nightly image and ensure non-CMIS transceivers do not change their mapping for DOM and status tables 2. Compare DOM sensor, threshold and status tables between current and nightly. Ensure units are not shown 3. Dump TRANSCEIVER_INFO table for CMIS transceiver to ensure is_replaceable field is present 2. CLI verification 1. sudo sfputil show error-status - This should dump output for all the ports and the behavior should not change between nightly and private image 2. Use invalid port name (Ethernet2033) to dump show int transceiver info, status CLI. Ensure to note new errors in PR description for invalid ports 3. For breakout scenario, keep 2 subports in different CMIS_STATES and ensure the CLI o/p is different for status CLI for both subports 3. Run transceiver onboarding test due to TRANSCEIVER_INFO table deletion change 4. XCVRD stop/start 1. Stop xcvrd and ensure all diagnostic tables are deleted 2. Start xcvrd and ensure all diagnostic tables are populated 5. Link flap event handling 1. Change link flap count for different types of transceivers and ensure no crash is seen. This is to ensure that the link event handler code does not cause any crash for non-CMIS transceivers 2. Shutdown peer port and ensure the tables get updated which are handled via link event update handler. Also, execute sfputil reset on 10 different physical ports and ensure that all the corresponding flags on the remote side are updated for all 8 lanes of a DR8 optic Specifically, we need to ensure that the following flags are set wherein X represents the lane number 1. LOSFlagRxX (page 11, byte 147) 2. CDRLOLFlagRxX (page 11, byte 148) 3. OpticalPowerLowAlarmFlagRxX (page 11, byte 150) 4. OpticalPowerLowWarningFlagRxX (page 11, byte 152) 6. Transceiver OIR test 1. Remove transceiver and ensure all diagnostic related tables are deleted 2. Insert transceiver and ensure all diagnostic tables are populated 3. Stop xcvrd and remove transceiver and ensure TRANSCEIVER_INFO table is present. Start xcvrd and ensure that the TRANSCEIVER_INFO table is now deleted 4. Insert the transceiver now 7. Ensure firmware upgrade is successfull and TRANSCEIVER_FIRMWARE_INFO table changes only for the primary subport eventhough, the upgrade was triggered for a non-primary subport 8. Ensure SFF manager crash is seen with nightly and is resolved with this PR (since the PR has the fix for [SffLoggerForPortUpdateEvent class does not have log_debug function · Issue #605 · sonic-net/sonic-platform-daemons](sonic-net/sonic-platform-daemons#605)) #### Previous command output (if the output of a command-line utility has changed) The output of show int transceiver info and show int transceiver eeprom will change if an invalid port name is passed to the CLI ``` root@str4-sn5600-3:/home/admin# show int transceiver info Ethernet8 Ethernet8: SFP EEPROM Not detected root@str4-sn5600-3:/home/admin# ``` #### New command output (if the output of a command-line utility has changed) ``` root@str4-sn5600-3:/home/admin# show int transceiver info Ethernet8 Error: Found KeyError while getting first subport for Ethernet8 Error: Unable to get first subport for Ethernet8 while converting SFP info Ethernet8: SFP EEPROM Not detected root@str4-sn5600-3:/home/admin# ``` MSFT ADO - 32238285
1 parent ba955a9 commit ea2fb57

File tree

8 files changed

+756
-443
lines changed

8 files changed

+756
-443
lines changed

scripts/sfpshow

Lines changed: 88 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ from typing import Dict
1414

1515
import click
1616
from natsort import natsorted
17+
from utilities_common import platform_sfputil_helper
1718
from sonic_py_common import multi_asic
1819
from utilities_common.general import load_db_config
1920
from utilities_common.sfp_helper import covert_application_advertisement_to_output_string
@@ -23,7 +24,10 @@ from utilities_common.sfp_helper import (
2324
C_CMIS_DATA_MAP,
2425
QSFP_STATUS_MAP,
2526
CMIS_STATUS_MAP,
27+
CMIS_VDM_TO_LEGACY_STATUS_MAP,
2628
CCMIS_STATUS_MAP,
29+
CCMIS_VDM_TO_LEGACY_STATUS_MAP,
30+
CCMIS_VDM_THRESHOLD_TO_LEGACY_DOM_THRESHOLD_MAP
2731
)
2832
from tabulate import tabulate
2933

@@ -453,8 +457,14 @@ class SFPShow(object):
453457
def convert_interface_sfp_info_to_cli_output_string(self, state_db, interface_name, dump_dom):
454458
output = ''
455459

460+
first_subport = platform_sfputil_helper.get_first_subport(interface_name)
461+
if first_subport is None:
462+
click.echo("Error: Unable to get first subport for {} while converting SFP info".format(interface_name))
463+
output = "SFP EEPROM Not detected\n"
464+
return output
465+
456466
sfp_info_dict = state_db.get_all(state_db.STATE_DB, 'TRANSCEIVER_INFO|{}'.format(interface_name))
457-
sfp_firmware_info_dict = state_db.get_all(state_db.STATE_DB, 'TRANSCEIVER_FIRMWARE_INFO|{}'.format(interface_name))
467+
sfp_firmware_info_dict = state_db.get_all(state_db.STATE_DB, 'TRANSCEIVER_FIRMWARE_INFO|{}'.format(first_subport))
458468
if sfp_info_dict:
459469
if sfp_info_dict['type'] == RJ45_PORT_TYPE:
460470
output = 'SFP EEPROM is not applicable for RJ45 port\n'
@@ -465,8 +475,8 @@ class SFPShow(object):
465475

466476
if dump_dom:
467477
sfp_type = sfp_info_dict['type']
468-
dom_info_dict = state_db.get_all(state_db.STATE_DB, 'TRANSCEIVER_DOM_SENSOR|{}'.format(interface_name))
469-
dom_info_dict.update(state_db.get_all(state_db.STATE_DB, 'TRANSCEIVER_DOM_THRESHOLD|{}'.format(interface_name)))
478+
dom_info_dict = state_db.get_all(state_db.STATE_DB, 'TRANSCEIVER_DOM_SENSOR|{}'.format(first_subport)) or {}
479+
dom_info_dict.update(state_db.get_all(state_db.STATE_DB, 'TRANSCEIVER_DOM_THRESHOLD|{}'.format(first_subport)) or {})
470480
dom_output = self.convert_dom_to_output_string(sfp_type, dom_info_dict)
471481
output += dom_output
472482
else:
@@ -477,17 +487,75 @@ class SFPShow(object):
477487

478488
return output
479489

490+
def convert_vdm_fields_to_legacy_fields(self, state_db, interface_name, dict_to_be_updated, vdm_to_legacy_field_map, vdm_field_type):
491+
"""
492+
Converts VDM fields from the database into legacy field names
493+
and updates the provided dictionary with the converted values.
494+
495+
This function ensures backward compatibility by mapping VDM fields to their corresponding
496+
legacy field names based on the specified field type ('FLAG' or 'THRESHOLD').
497+
498+
Args:
499+
state_db: The database connection object used to retrieve VDM data.
500+
interface_name (str): The name of the interface for which VDM data is being retrieved.
501+
dict_to_be_updated (dict): The dictionary to be updated with the converted legacy field names and values.
502+
vdm_to_legacy_field_map (dict): A mapping of VDM field names to their corresponding legacy field name prefixes.
503+
vdm_field_type (str): Specifies the type of VDM fields to process. It can be either 'FLAG' or 'THRESHOLD'.
504+
"""
505+
if dict_to_be_updated is None:
506+
return
507+
if vdm_field_type not in ['FLAG', 'THRESHOLD']:
508+
return
509+
510+
# Retrieve VDM data from the database
511+
vdm_halarm_db_dict = state_db.get_all(state_db.STATE_DB, f'TRANSCEIVER_VDM_HALARM_{vdm_field_type}|{interface_name}') or {}
512+
vdm_lalarm_db_dict = state_db.get_all(state_db.STATE_DB, f'TRANSCEIVER_VDM_LALARM_{vdm_field_type}|{interface_name}') or {}
513+
vdm_hwarning_db_dict = state_db.get_all(state_db.STATE_DB, f'TRANSCEIVER_VDM_HWARN_{vdm_field_type}|{interface_name}') or {}
514+
vdm_lwarning_db_dict = state_db.get_all(state_db.STATE_DB, f'TRANSCEIVER_VDM_LWARN_{vdm_field_type}|{interface_name}') or {}
515+
516+
# Define threshold types and their corresponding dictionaries
517+
VDM_THRESHOLD_TYPES = {
518+
'highalarm': vdm_halarm_db_dict,
519+
'lowalarm': vdm_lalarm_db_dict,
520+
'highwarning': vdm_hwarning_db_dict,
521+
'lowwarning': vdm_lwarning_db_dict
522+
}
523+
524+
# Process each VDM field and update the dictionary
525+
for vdm_field, legacy_field_prefix in vdm_to_legacy_field_map.items():
526+
for threshold_type, vdm_db_dict in VDM_THRESHOLD_TYPES.items():
527+
if vdm_field in vdm_db_dict:
528+
if vdm_field_type == 'FLAG':
529+
legacy_field_name = f"{legacy_field_prefix}{threshold_type}_flag"
530+
else:
531+
legacy_field_name = f"{legacy_field_prefix}{threshold_type}"
532+
dict_to_be_updated[legacy_field_name] = vdm_db_dict[vdm_field]
533+
480534
# Convert sfp status info in DB to cli output string
481535
def convert_interface_sfp_status_to_cli_output_string(self, state_db, interface_name):
482-
sfp_status_dict = state_db.get_all(state_db.STATE_DB, 'TRANSCEIVER_STATUS|{}'.format(interface_name))
536+
first_subport = platform_sfputil_helper.get_first_subport(interface_name)
537+
if first_subport is None:
538+
click.echo("Error: Unable to get first subport for {} while converting SFP status".format(interface_name))
539+
output = QSFP_STATUS_NOT_APPLICABLE_STR + '\n'
540+
return output
541+
542+
sfp_status_dict = state_db.get_all(state_db.STATE_DB, 'TRANSCEIVER_STATUS|{}'.format(first_subport))
543+
if sfp_status_dict:
544+
# Additional handling to ensure that the CLI output remains the same
545+
# after restructuring the diagnostic data in the state DB
546+
sfp_status_dict.update(state_db.get_all(state_db.STATE_DB, 'TRANSCEIVER_STATUS_SW|{}'.format(interface_name)) or {})
547+
sfp_status_dict.update(state_db.get_all(state_db.STATE_DB, 'TRANSCEIVER_STATUS_FLAG|{}'.format(first_subport)) or {})
548+
sfp_status_dict.update(state_db.get_all(state_db.STATE_DB, 'TRANSCEIVER_DOM_FLAG|{}'.format(first_subport)) or {})
483549
if sfp_status_dict and len(sfp_status_dict) > 2:
484550
# common section
485551
output = '\n' + self.convert_sfp_status_to_output_string(sfp_status_dict, QSFP_STATUS_MAP)
486552
# CMIS specific section
487553
if 'module_state' in sfp_status_dict:
554+
self.convert_vdm_fields_to_legacy_fields(state_db, first_subport, sfp_status_dict, CMIS_VDM_TO_LEGACY_STATUS_MAP, 'FLAG')
488555
output += self.convert_sfp_status_to_output_string(sfp_status_dict, CMIS_STATUS_MAP)
489556
# C-CMIS specific section
490557
if 'tuning_in_progress' in sfp_status_dict:
558+
self.convert_vdm_fields_to_legacy_fields(state_db, first_subport, sfp_status_dict, CCMIS_VDM_TO_LEGACY_STATUS_MAP, 'FLAG')
491559
output += self.convert_sfp_status_to_output_string(sfp_status_dict, CCMIS_STATUS_MAP)
492560
else:
493561
output = QSFP_STATUS_NOT_APPLICABLE_STR + '\n'
@@ -510,10 +578,18 @@ class SFPShow(object):
510578
return str(field)
511579

512580
def convert_interface_sfp_pm_to_cli_output_string(self, state_db, interface_name):
581+
first_subport = platform_sfputil_helper.get_first_subport(interface_name)
582+
if first_subport is None:
583+
click.echo("Error: Unable to get first subport for {} while converting SFP PM".format(interface_name))
584+
output = ZR_PM_NOT_APPLICABLE_STR + '\n'
585+
return output
586+
513587
sfp_pm_dict = state_db.get_all(
514-
self.db.STATE_DB, 'TRANSCEIVER_PM|{}'.format(interface_name))
588+
self.db.STATE_DB, 'TRANSCEIVER_PM|{}'.format(first_subport))
515589
sfp_threshold_dict = state_db.get_all(
516-
state_db.STATE_DB, 'TRANSCEIVER_DOM_THRESHOLD|{}'.format(interface_name))
590+
state_db.STATE_DB, 'TRANSCEIVER_DOM_THRESHOLD|{}'.format(first_subport))
591+
# Convert VDM THRESHOLD fields to legacy DOM THRESHOLD fields
592+
self.convert_vdm_fields_to_legacy_fields(state_db, first_subport, sfp_threshold_dict, CCMIS_VDM_THRESHOLD_TO_LEGACY_DOM_THRESHOLD_MAP, 'THRESHOLD')
517593
table = []
518594
indent_num = 4
519595
indent = ' ' * indent_num
@@ -645,7 +721,12 @@ class SFPShow(object):
645721
@click.group()
646722
def cli():
647723
"""sfpshow - Command line utility for display SFP transceivers information"""
648-
pass
724+
# Load platform-specific sfputil class
725+
platform_sfputil_helper.load_platform_sfputil()
726+
727+
# Load port info
728+
platform_sfputil_helper.platform_sfputil_read_porttab_mappings()
729+
649730

650731
# 'eeprom' subcommand
651732

sfputil/main.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector
2323
from natsort import natsorted
2424
from sonic_py_common import device_info, logger, multi_asic
25+
from utilities_common import platform_sfputil_helper
26+
from utilities_common.platform_sfputil_helper import (
27+
get_first_subport
28+
)
2529
from utilities_common.sfp_helper import covert_application_advertisement_to_output_string
2630
from utilities_common.sfp_helper import QSFP_DATA_MAP
2731
from tabulate import tabulate
@@ -605,6 +609,11 @@ def cli():
605609
if not load_port_config():
606610
sys.exit(ERROR_PORT_CONFIG_LOAD)
607611

612+
# Generic way to load platform-specific sfputil
613+
# and chassis classes
614+
platform_sfputil_helper.load_platform_sfputil()
615+
platform_sfputil_helper.load_chassis()
616+
platform_sfputil_helper.platform_sfputil_read_porttab_mappings()
608617

609618
cli.add_command(debug)
610619

@@ -691,9 +700,10 @@ def eeprom(port, dump_dom, namespace):
691700
output += "DOM values not supported for flat memory module\n"
692701
continue
693702
try:
694-
xcvr_dom_info = platform_chassis.get_sfp(physical_port).get_transceiver_bulk_status()
703+
xcvr_dom_info = platform_chassis.get_sfp(physical_port).get_transceiver_dom_real_value()
695704
except NotImplementedError:
696-
click.echo("Sfp.get_transceiver_bulk_status() is currently not implemented for this platform")
705+
click.echo("Sfp.get_transceiver_dom_real_value() is currently not implemented "
706+
"for this platform")
697707
sys.exit(ERROR_NOT_IMPLEMENTED)
698708

699709
try:
@@ -1073,9 +1083,9 @@ def fetch_error_status_from_state_db(port, state_db):
10731083
"""
10741084
status = {}
10751085
if port:
1076-
status[port] = state_db.get_all(state_db.STATE_DB, 'TRANSCEIVER_STATUS|{}'.format(port))
1086+
status[port] = state_db.get_all(state_db.STATE_DB, 'TRANSCEIVER_STATUS_SW|{}'.format(port))
10771087
else:
1078-
ports = state_db.keys(state_db.STATE_DB, 'TRANSCEIVER_STATUS|*')
1088+
ports = state_db.keys(state_db.STATE_DB, 'TRANSCEIVER_STATUS_SW|*')
10791089
for key in ports:
10801090
status[key.split('|')[1]] = state_db.get_all(state_db.STATE_DB, key)
10811091

@@ -1380,7 +1390,11 @@ def enable(port_name):
13801390

13811391

13821392
def update_firmware_info_to_state_db(port_name):
1383-
physical_port = logical_port_to_physical_port_index(port_name)
1393+
first_subport = get_first_subport(port_name)
1394+
if first_subport is None:
1395+
click.echo("Error: Unable to get first subport for {} while updating FW info to DB".format(port_name))
1396+
return
1397+
physical_port = logical_port_to_physical_port_index(first_subport)
13841398

13851399
namespaces = multi_asic.get_front_end_namespaces()
13861400
for namespace in namespaces:
@@ -1390,7 +1404,7 @@ def update_firmware_info_to_state_db(port_name):
13901404
transceiver_firmware_info_dict = platform_chassis.get_sfp(physical_port).get_transceiver_info_firmware_versions()
13911405
if transceiver_firmware_info_dict is not None:
13921406
for key, value in transceiver_firmware_info_dict.items():
1393-
state_db.set(state_db.STATE_DB, 'TRANSCEIVER_FIRMWARE_INFO|{}'.format(port_name), key, value)
1407+
state_db.set(state_db.STATE_DB, 'TRANSCEIVER_FIRMWARE_INFO|{}'.format(first_subport), key, value)
13941408

13951409
# 'firmware' subgroup
13961410
@cli.group()

tests/mock_platform_sfputil/mock_platform_sfputil.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,17 @@ def mock_platform_sfputil_read_porttab_mappings():
3333
portMap = jsonobj['portMap']
3434
RJ45Ports = jsonobj['RJ45Ports']
3535

36+
37+
def mock_get_first_subport(logical_port_name):
38+
"""
39+
Mock function to get the first subport of a logical port.
40+
"""
41+
return logical_port_name
42+
3643
def mock_platform_sfputil_helper():
3744
platform_sfputil_helper.platform_chassis = mock_Chassis()
3845
platform_sfputil_helper.platform_sfputil = True
3946
platform_sfputil_helper.platform_porttab_mapping_read = False
4047
platform_sfputil_helper.platform_sfputil_read_porttab_mappings = mock_platform_sfputil_read_porttab_mappings
4148
platform_sfputil_helper.logical_port_name_to_physical_port_list = mock_logical_port_name_to_physical_port_list
49+
platform_sfputil_helper.get_first_subport = mock_get_first_subport

0 commit comments

Comments
 (0)