From 67170da5e1dd5dea1bee9818a73e6f3940108dcf Mon Sep 17 00:00:00 2001 From: junchao Date: Mon, 12 Apr 2021 16:20:49 +0800 Subject: [PATCH 1/5] Add bitmap support for SFP error event --- .../mlnx-platform-api/sonic_platform/sfp_event.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py index ba3ab68a9576..53e846bd005b 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py @@ -24,13 +24,14 @@ SDK_SFP_STATE_DIS = 0x4 # SFP status that will be handled by XCVRD -STATUS_PLUGIN = '1' -STATUS_PLUGOUT = '0' -STATUS_ERR_I2C_STUCK = '2' -STATUS_ERR_BAD_EEPROM = '3' -STATUS_ERR_UNSUPPORTED_CABLE = '4' -STATUS_ERR_HIGH_TEMP = '5' -STATUS_ERR_BAD_CABLE = '6' +STATUS_PLUGIN = '1' # 00000001 +STATUS_PLUGOUT = '0' # 00000000 +# SFP error status always come with STATUS_PLUGIN, so the last bit is always 1 +STATUS_ERR_I2C_STUCK = '3' # 00000011 +STATUS_ERR_BAD_EEPROM = '5' # 00000101 +STATUS_ERR_UNSUPPORTED_CABLE = '9' # 00001001 +STATUS_ERR_HIGH_TEMP = '17' # 00010001 +STATUS_ERR_BAD_CABLE = '33' # 00100001 # SFP status used in this file only, will not expose to XCVRD # STATUS_ERROR will be mapped to different status according to the error code From 152c0986f7a16d00b52e77c9a3156ad0306b62d4 Mon Sep 17 00:00:00 2001 From: junchao Date: Wed, 14 Apr 2021 11:44:42 +0800 Subject: [PATCH 2/5] Add unit test for sfp error bitmap --- .../sonic_platform/sfp_event.py | 9 ++++- .../mlnx-platform-api/tests/test_sfp_event.py | 36 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 platform/mellanox/mlnx-platform-api/tests/test_sfp_event.py diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py index 53e846bd005b..2164f4e18cf2 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py @@ -8,7 +8,14 @@ import os import time import select -from python_sdk_api.sx_api import * +if 'MLNX_PLATFORM_API_UNIT_TESTING' not in os.environ: + from python_sdk_api.sx_api import * +else: + from mock import MagicMock + class MockSxFd(object): + fd = 99 + new_sx_fd_t_p = MagicMock(return_value=MockSxFd()) + new_sx_user_channel_t_p = MagicMock() from sonic_py_common.logger import Logger # SFP status from PMAOS register diff --git a/platform/mellanox/mlnx-platform-api/tests/test_sfp_event.py b/platform/mellanox/mlnx-platform-api/tests/test_sfp_event.py new file mode 100644 index 000000000000..a21d2d5d8318 --- /dev/null +++ b/platform/mellanox/mlnx-platform-api/tests/test_sfp_event.py @@ -0,0 +1,36 @@ +import os +import select +import sys + +from mock import MagicMock + +test_path = os.path.dirname(os.path.abspath(__file__)) +modules_path = os.path.dirname(test_path) +sys.path.insert(0, modules_path) + +class TestSfpEvent(object): + @classmethod + def setup_class(cls): + os.environ["MLNX_PLATFORM_API_UNIT_TESTING"] = "1" + select.select = MagicMock(return_value=([99], None, None)) + + def test_check_sfp_status(self): + from sonic_platform.sfp_event import SDK_SFP_STATE_IN, SDK_SFP_STATE_OUT, SDK_SFP_STATE_ERR + from sonic_platform.sfp_event import STATUS_PLUGIN, STATUS_PLUGOUT + from sonic_platform.sfp_event import sdk_sfp_err_type_dict + + self.executor(SDK_SFP_STATE_IN, None, STATUS_PLUGIN) + self.executor(SDK_SFP_STATE_OUT, None, STATUS_PLUGOUT) + for error_type, error_status in sdk_sfp_err_type_dict.items(): + self.executor(SDK_SFP_STATE_ERR, error_type, error_status) + + def executor(self, mock_module_state, mock_error_type, expect_status): + from sonic_platform.sfp_event import sfp_event + + event = sfp_event() + event.on_pmpe = MagicMock(return_value=(True, [0,1], mock_module_state, mock_error_type)) + port_change = {} + found = event.check_sfp_status(port_change, 0) + assert found + assert 1 in port_change and port_change[1] == expect_status + assert 2 in port_change and port_change[2] == expect_status From 661507d8800913b1cb8c0128353dba1bcf8f0109 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Mon, 26 Apr 2021 02:51:35 +0000 Subject: [PATCH 3/5] Support handling the error status of SFP modules 1. Update get_change_event - Return event in a bitmap format in case there is an error - Return an extra map indexed by sfp_error in case there is a Mellanox-specific error - Indicate whether the error is a blocking error 2. Expose the error status of SFP modules to CLI - A platform API calls SDK API to fetch the error status - Display the SFP module state: plugged, unplugged and plugged with error. - The SFP error state is displayed only if the SFP module state is plugged with error - The CLI will call platform API and provide user-friendly output 3. Redirect stderr to /dev/null in order to eliminate errors in case SFP module is not plugged 4. Beautify the code, making all the state/error/descriptions defined in a uniform place Signed-off-by: Stephen Sun --- .../sonic_platform/chassis.py | 10 ++- .../mlnx-platform-api/sonic_platform/sfp.py | 83 ++++++++++++++++++- .../sonic_platform/sfp_event.py | 76 +++++++++++------ .../mlnx-platform-api/tests/test_sfp.py | 43 +++++++++- .../mlnx-platform-api/tests/test_sfp_event.py | 30 ++++--- 5 files changed, 200 insertions(+), 42 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py index 1167f56368db..88ec916a33ea 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py @@ -571,18 +571,22 @@ def get_change_event(self, timeout=0): wait_for_ever = (timeout == 0) port_dict = {} + error_dict = {} if wait_for_ever: timeout = MAX_SELECT_DELAY while True: - status = self.sfp_event.check_sfp_status(port_dict, timeout) + status = self.sfp_event.check_sfp_status(port_dict, error_dict, timeout) if bool(port_dict): break else: - status = self.sfp_event.check_sfp_status(port_dict, timeout) + status = self.sfp_event.check_sfp_status(port_dict, error_dict, timeout) if status: self.reinit_sfps(port_dict) - return True, {'sfp':port_dict} + result_dict = {'sfp':port_dict} + if error_dict: + result_dict['sfp_error'] = error_dict + return True, result_dict else: return True, {'sfp':{}} diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py index 76a83a4472c9..0bd26340b448 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py @@ -10,6 +10,7 @@ try: import subprocess + import os from sonic_platform_base.sfp_base import SfpBase from sonic_platform_base.sonic_eeprom import eeprom_dts from sonic_platform_base.sonic_sfp.sff8472 import sff8472InterfaceId @@ -35,6 +36,18 @@ except ImportError as e: pass +try: + if os.environ["PLATFORM_API_UNIT_TESTING"] == "1": + # Unable to import SDK constants under unit test + # Define them here + SX_PORT_MODULE_STATUS_INITIALIZING = 0 + SX_PORT_MODULE_STATUS_PLUGGED = 1 + SX_PORT_MODULE_STATUS_UNPLUGGED = 2 + SX_PORT_MODULE_STATUS_PLUGGED_WITH_ERROR = 3 + SX_PORT_MODULE_STATUS_PLUGGED_DISABLED = 4 +except KeyError: + pass + # definitions of the offset and width for values in XCVR info eeprom XCVR_INTFACE_BULK_OFFSET = 0 XCVR_INTFACE_BULK_WIDTH_QSFP = 20 @@ -330,6 +343,11 @@ def __exit__(self, exc_type, exc_val, exc_tb): class SFP(SfpBase): """Platform-specific SFP class""" + SFP_MLNX_ERROR_DESCRIPTION_LONGRANGE_NON_MLNX_CABLE = 'Long range for non-Mellanox cable or module' + SFP_MLNX_ERROR_DESCRIPTION_ENFORCE_PART_NUMBER_LIST = 'Enforce part number list' + SFP_MLNX_ERROR_DESCRIPTION_PMD_TYPE_NOT_ENABLED = 'PMD type not enabled' + SFP_MLNX_ERROR_DESCRIPTION_PCIE_POWER_SLOT_EXCEEDED = 'PCIE system power slot exceeded' + def __init__(self, sfp_index, sfp_type, sdk_handle_getter, platform): SfpBase.__init__(self) self.index = sfp_index + 1 @@ -388,7 +406,7 @@ def get_presence(self): # Read out any bytes from any offset def _read_eeprom_specific_bytes(self, offset, num_bytes): eeprom_raw = [] - ethtool_cmd = "ethtool -m sfp{} hex on offset {} length {}".format(self.index, offset, num_bytes) + ethtool_cmd = "ethtool -m sfp{} hex on offset {} length {} 2>/dev/null".format(self.index, offset, num_bytes) try: output = subprocess.check_output(ethtool_cmd, shell=True, @@ -2158,3 +2176,66 @@ def is_replaceable(self): bool: True if it is replaceable. """ return True + + def _get_error_code(self): + """ + Get error code of the SFP module + + Returns: + The error code fetch from SDK API + """ + module_id_info_list = new_sx_mgmt_module_id_info_t_arr(1) + module_info_list = new_sx_mgmt_phy_module_info_t_arr(1) + + module_id_info = sx_mgmt_module_id_info_t() + module_id_info.slot_id = 0 + module_id_info.module_id = self.sdk_index + sx_mgmt_module_id_info_t_arr_setitem(module_id_info_list, 0, module_id_info) + + rc = sx_mgmt_phy_module_info_get(self.sdk_handle, module_id_info_list, 1, module_info_list) + assert SX_STATUS_SUCCESS == rc, "sx_mgmt_phy_module_info_get failed, error code {}".format(rc) + + mod_info = sx_mgmt_phy_module_info_t_arr_getitem(module_info_list, 0) + return mod_info.module_state.oper_state, mod_info.module_state.error_type + + @classmethod + def _get_error_description_dict(cls): + return {0: cls.SFP_ERROR_DESCRIPTION_POWER_BUDGET_EXCEEDED, + 1: cls.SFP_MLNX_ERROR_DESCRIPTION_LONGRANGE_NON_MLNX_CABLE, + 2: cls.SFP_ERROR_DESCRIPTION_I2C_STUCK, + 3: cls.SFP_ERROR_DESCRIPTION_BAD_EEPROM, + 4: cls.SFP_MLNX_ERROR_DESCRIPTION_ENFORCE_PART_NUMBER_LIST, + 5: cls.SFP_ERROR_DESCRIPTION_UNSUPPORTED_CABLE, + 6: cls.SFP_ERROR_DESCRIPTION_HIGH_TEMP, + 7: cls.SFP_ERROR_DESCRIPTION_BAD_CABLE, + 8: cls.SFP_MLNX_ERROR_DESCRIPTION_PMD_TYPE_NOT_ENABLED, + 12: cls.SFP_MLNX_ERROR_DESCRIPTION_PCIE_POWER_SLOT_EXCEEDED, + 255: cls.SFP_STATUS_OK + } + + def get_error_description(self): + """ + Get error description + + Args: + error_code: The error code returned by _get_error_code + + Returns: + The error description + """ + oper_status, error_code = self._get_error_code() + if oper_status == SX_PORT_MODULE_STATUS_INITIALIZING: + error_description = self.SFP_STATUS_INITIALIZING + elif oper_status == SX_PORT_MODULE_STATUS_PLUGGED: + error_description = self.SFP_STATUS_OK + elif oper_status == SX_PORT_MODULE_STATUS_UNPLUGGED: + error_description = self.SFP_STATUS_UNPLUGGED + elif oper_status == SX_PORT_MODULE_STATUS_PLUGGED_DISABLED: + error_description = self.SFP_STATUS_DISABLED + elif oper_status == SX_PORT_MODULE_STATUS_PLUGGED_WITH_ERROR: + error_description_dict = self._get_error_description_dict() + if error_code in error_description_dict: + error_description = error_description_dict[error_code] + else: + error_description = "Unknown error ({})".format(error_code) + return error_description diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py index 2164f4e18cf2..6856d49f1ca2 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py @@ -17,6 +17,7 @@ class MockSxFd(object): new_sx_fd_t_p = MagicMock(return_value=MockSxFd()) new_sx_user_channel_t_p = MagicMock() from sonic_py_common.logger import Logger +from sonic_platform.sfp import SFP # SFP status from PMAOS register # 0x1 plug in @@ -30,16 +31,6 @@ class MockSxFd(object): SDK_SFP_STATE_ERR = 0x3 SDK_SFP_STATE_DIS = 0x4 -# SFP status that will be handled by XCVRD -STATUS_PLUGIN = '1' # 00000001 -STATUS_PLUGOUT = '0' # 00000000 -# SFP error status always come with STATUS_PLUGIN, so the last bit is always 1 -STATUS_ERR_I2C_STUCK = '3' # 00000011 -STATUS_ERR_BAD_EEPROM = '5' # 00000101 -STATUS_ERR_UNSUPPORTED_CABLE = '9' # 00001001 -STATUS_ERR_HIGH_TEMP = '17' # 00010001 -STATUS_ERR_BAD_CABLE = '33' # 00100001 - # SFP status used in this file only, will not expose to XCVRD # STATUS_ERROR will be mapped to different status according to the error code STATUS_UNKNOWN = '-1' @@ -69,19 +60,38 @@ class MockSxFd(object): ''' # SFP errors that will block eeprom accessing -sdk_sfp_err_type_dict = { - 0x2: STATUS_ERR_I2C_STUCK, - 0x3: STATUS_ERR_BAD_EEPROM, - 0x5: STATUS_ERR_UNSUPPORTED_CABLE, - 0x6: STATUS_ERR_HIGH_TEMP, - 0x7: STATUS_ERR_BAD_CABLE +SDK_SFP_BLOCKING_ERRORS = [ + 0x2, # SFP.SFP_ERROR_BIT_I2C_STUCK, + 0x3, # SFP.SFP_ERROR_BIT_BAD_EEPROM, + 0x5, # SFP.SFP_ERROR_BIT_UNSUPPORTED_CABLE, + 0x6, # SFP.SFP_ERROR_BIT_HIGH_TEMP, + 0x7, # SFP.SFP_ERROR_BIT_BAD_CABLE +] + +SDK_ERRORS_TO_ERROR_BITS = { + 0x0: SFP.SFP_ERROR_BIT_POWER_BUDGET_EXCEEDED, + 0x2: SFP.SFP_ERROR_BIT_I2C_STUCK, + 0x3: SFP.SFP_ERROR_BIT_BAD_EEPROM, + 0x5: SFP.SFP_ERROR_BIT_UNSUPPORTED_CABLE, + 0x6: SFP.SFP_ERROR_BIT_HIGH_TEMP, + 0x7: SFP.SFP_ERROR_BIT_BAD_CABLE, + 0x1: 0x00010000, + 0x4: 0x00020000, + 0x8: 0x00040000, + 0xc: 0x00080000 +} +SDK_ERRORS_TO_DESCRIPTION = { + 0x1: SFP.SFP_MLNX_ERROR_DESCRIPTION_LONGRANGE_NON_MLNX_CABLE, + 0x4: SFP.SFP_MLNX_ERROR_DESCRIPTION_ENFORCE_PART_NUMBER_LIST, + 0x8: SFP.SFP_MLNX_ERROR_DESCRIPTION_PMD_TYPE_NOT_ENABLED, + 0xc: SFP.SFP_MLNX_ERROR_DESCRIPTION_PCIE_POWER_SLOT_EXCEEDED } sfp_value_status_dict = { - SDK_SFP_STATE_IN: STATUS_PLUGIN, - SDK_SFP_STATE_OUT: STATUS_PLUGOUT, + SDK_SFP_STATE_IN: str(SFP.SFP_STATUS_BIT_INSERTED), + SDK_SFP_STATE_OUT: str(SFP.SFP_STATUS_BIT_REMOVED), SDK_SFP_STATE_ERR: STATUS_ERROR, - SDK_SFP_STATE_DIS: STATUS_PLUGOUT, + SDK_SFP_STATE_DIS: str(SFP.SFP_STATUS_BIT_REMOVED), } # system level event/error @@ -204,7 +214,7 @@ def deinitialize(self): delete_sx_fd_t_p(self.rx_fd_p) delete_sx_user_channel_t_p(self.user_channel_p) - def check_sfp_status(self, port_change, timeout): + def check_sfp_status(self, port_change, error_dict, timeout): """ the meaning of timeout is aligned with select.select, which has the following meaning: 0: poll, returns without blocked @@ -242,6 +252,7 @@ def check_sfp_status(self, port_change, timeout): break sfp_state = sfp_value_status_dict.get(module_state, STATUS_UNKNOWN) + error_description = None if sfp_state == STATUS_UNKNOWN: # in the following sequence, STATUS_UNKNOWN can be returned. # so we shouldn't raise exception here. @@ -256,18 +267,29 @@ def check_sfp_status(self, port_change, timeout): # If get SFP status error(0x3) from SDK, then need to read the error_type to get the detailed error if sfp_state == STATUS_ERROR: - if error_type in sdk_sfp_err_type_dict.keys(): - # In SFP at error status case, need to overwrite the sfp_state with the exact error code - sfp_state = sdk_sfp_err_type_dict[error_type] - else: - # For errors don't block the eeprom accessing, we don't report it to XCVRD - logger.log_info("SFP error on port but not blocking eeprom read, error_type {}".format(error_type)) - found +=1 + sfp_state_bits = SDK_ERRORS_TO_ERROR_BITS.get(error_type) + if sfp_state_bits is None: + logger.log_error("Unrecognized error {} detected on ports {}".format(error_type, port_list)) + found += 1 continue + if error_type in SDK_SFP_BLOCKING_ERRORS: + # In SFP at error status case, need to overwrite the sfp_state with the exact error code + sfp_state_bits |= SFP.SFP_ERROR_BIT_BLOCKING + + # An error should be always set along with 'INSERTED' + sfp_state_bits |= SFP.SFP_STATUS_BIT_INSERTED + + # For vendor specific errors, the description should be returned as well + error_description = SDK_ERRORS_TO_DESCRIPTION.get(error_type) + + sfp_state = str(sfp_state_bits) + for port in port_list: logger.log_info("SFP on port {} state {}".format(port, sfp_state)) port_change[port+1] = sfp_state + if error_description: + error_dict[port+1] = error_description found += 1 return found != 0 diff --git a/platform/mellanox/mlnx-platform-api/tests/test_sfp.py b/platform/mellanox/mlnx-platform-api/tests/test_sfp.py index 0c24eb83354e..405a48a77b79 100644 --- a/platform/mellanox/mlnx-platform-api/tests/test_sfp.py +++ b/platform/mellanox/mlnx-platform-api/tests/test_sfp.py @@ -8,8 +8,11 @@ modules_path = os.path.dirname(test_path) sys.path.insert(0, modules_path) +os.environ["PLATFORM_API_UNIT_TESTING"] = "1" + from sonic_py_common import device_info -from sonic_platform.sfp import SFP +from sonic_platform.sfp import SFP, SX_PORT_MODULE_STATUS_INITIALIZING, SX_PORT_MODULE_STATUS_PLUGGED, SX_PORT_MODULE_STATUS_UNPLUGGED, SX_PORT_MODULE_STATUS_PLUGGED_WITH_ERROR, SX_PORT_MODULE_STATUS_PLUGGED_DISABLED + from sonic_platform.chassis import Chassis @@ -26,8 +29,14 @@ def mock_get_sdk_handle(self): self.sdk_handle = 1 return self.sdk_handle + +def mock_get_sfp_error_code(self): + return self.oper_code, self.error_code + + device_info.get_platform = mock_get_platform SFP._read_eeprom_specific_bytes = mock_read_eeprom_specific_bytes +SFP._get_error_code = mock_get_sfp_error_code Chassis.get_sdk_handle = mock_get_sdk_handle @@ -82,3 +91,35 @@ def test_sfp_full_initialize_without_partial(): # Verify when get_sfp is called, the SFP modules won't be initialized again sfp1 = allsfp[0] assert sfp1 == chassis.get_sfp(1) + + +def test_sfp_get_error_status(): + chassis = Chassis() + + # Fetch an SFP module to test + sfp = chassis.get_sfp(1) + + description_dict = sfp._get_error_description_dict() + + sfp.oper_code = SX_PORT_MODULE_STATUS_PLUGGED_WITH_ERROR + for error in description_dict.keys(): + sfp.error_code = error + description = sfp.get_error_description() + + assert description == description_dict[sfp.error_code] + + sfp.error_code = -1 + description = sfp.get_error_description() + assert description == "Unknown error (-1)" + + expected_description_list = [ + (SX_PORT_MODULE_STATUS_INITIALIZING, "Initializing"), + (SX_PORT_MODULE_STATUS_PLUGGED, "OK"), + (SX_PORT_MODULE_STATUS_UNPLUGGED, "Unplugged"), + (SX_PORT_MODULE_STATUS_PLUGGED_DISABLED, "Disabled") + ] + for oper_code, expected_description in expected_description_list: + sfp.oper_code = oper_code + description = sfp.get_error_description() + + assert description == expected_description diff --git a/platform/mellanox/mlnx-platform-api/tests/test_sfp_event.py b/platform/mellanox/mlnx-platform-api/tests/test_sfp_event.py index a21d2d5d8318..3edcc362a5e6 100644 --- a/platform/mellanox/mlnx-platform-api/tests/test_sfp_event.py +++ b/platform/mellanox/mlnx-platform-api/tests/test_sfp_event.py @@ -8,6 +8,8 @@ modules_path = os.path.dirname(test_path) sys.path.insert(0, modules_path) +from sonic_platform_base.sfp_base import SfpBase + class TestSfpEvent(object): @classmethod def setup_class(cls): @@ -16,21 +18,29 @@ def setup_class(cls): def test_check_sfp_status(self): from sonic_platform.sfp_event import SDK_SFP_STATE_IN, SDK_SFP_STATE_OUT, SDK_SFP_STATE_ERR - from sonic_platform.sfp_event import STATUS_PLUGIN, STATUS_PLUGOUT - from sonic_platform.sfp_event import sdk_sfp_err_type_dict + from sonic_platform.sfp_event import SDK_ERRORS_TO_ERROR_BITS, SDK_ERRORS_TO_DESCRIPTION, SDK_SFP_BLOCKING_ERRORS - self.executor(SDK_SFP_STATE_IN, None, STATUS_PLUGIN) - self.executor(SDK_SFP_STATE_OUT, None, STATUS_PLUGOUT) - for error_type, error_status in sdk_sfp_err_type_dict.items(): - self.executor(SDK_SFP_STATE_ERR, error_type, error_status) + self.executor(SDK_SFP_STATE_IN, None, SfpBase.SFP_STATUS_BIT_INSERTED) + self.executor(SDK_SFP_STATE_OUT, None, SfpBase.SFP_STATUS_BIT_REMOVED) + for error_type, error_status in SDK_ERRORS_TO_ERROR_BITS.items(): + description = SDK_ERRORS_TO_DESCRIPTION.get(error_type) + if error_type in SDK_SFP_BLOCKING_ERRORS: + error_status |= SfpBase.SFP_ERROR_BIT_BLOCKING + error_status |= SfpBase.SFP_STATUS_BIT_INSERTED + self.executor(SDK_SFP_STATE_ERR, error_type, error_status, description) - def executor(self, mock_module_state, mock_error_type, expect_status): + def executor(self, mock_module_state, mock_error_type, expect_status, description=None): from sonic_platform.sfp_event import sfp_event event = sfp_event() event.on_pmpe = MagicMock(return_value=(True, [0,1], mock_module_state, mock_error_type)) port_change = {} - found = event.check_sfp_status(port_change, 0) + error_dict = {} + found = event.check_sfp_status(port_change, error_dict, 0) assert found - assert 1 in port_change and port_change[1] == expect_status - assert 2 in port_change and port_change[2] == expect_status + expect_status_str = str(expect_status) + assert 1 in port_change and port_change[1] == expect_status_str + assert 2 in port_change and port_change[2] == expect_status_str + if description: + assert 1 in error_dict and error_dict[1] == description + assert 2 in error_dict and error_dict[2] == description From cb8bfdc423592b0f554cdb590ea32276241f5ad3 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Fri, 4 Jun 2021 10:53:12 +0000 Subject: [PATCH 4/5] Address comments Signed-off-by: Stephen Sun --- .../mellanox/mlnx-platform-api/sonic_platform/sfp.py | 7 +++++++ .../mlnx-platform-api/sonic_platform/sfp_event.py | 11 ++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py index 0bd26340b448..8222ead5081f 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py @@ -348,6 +348,11 @@ class SFP(SfpBase): SFP_MLNX_ERROR_DESCRIPTION_PMD_TYPE_NOT_ENABLED = 'PMD type not enabled' SFP_MLNX_ERROR_DESCRIPTION_PCIE_POWER_SLOT_EXCEEDED = 'PCIE system power slot exceeded' + SFP_MLNX_ERROR_BIT_LONGRANGE_NON_MLNX_CABLE = 0x00010000 + SFP_MLNX_ERROR_BIT_ENFORCE_PART_NUMBER_LIST = 0x00020000 + SFP_MLNX_ERROR_BIT_PMD_TYPE_NOT_ENABLED = 0x00040000 + SFP_MLNX_ERROR_BIT_PCIE_POWER_SLOT_EXCEEDED = 0x00080000 + def __init__(self, sfp_index, sfp_type, sdk_handle_getter, platform): SfpBase.__init__(self) self.index = sfp_index + 1 @@ -2238,4 +2243,6 @@ def get_error_description(self): error_description = error_description_dict[error_code] else: error_description = "Unknown error ({})".format(error_code) + else: + error_description = "Unknow SFP module status ({})".format(oper_status) return error_description diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py index 6856d49f1ca2..289a210482f1 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py @@ -17,7 +17,7 @@ class MockSxFd(object): new_sx_fd_t_p = MagicMock(return_value=MockSxFd()) new_sx_user_channel_t_p = MagicMock() from sonic_py_common.logger import Logger -from sonic_platform.sfp import SFP +from .sfp import SFP # SFP status from PMAOS register # 0x1 plug in @@ -70,16 +70,17 @@ class MockSxFd(object): SDK_ERRORS_TO_ERROR_BITS = { 0x0: SFP.SFP_ERROR_BIT_POWER_BUDGET_EXCEEDED, + 0x1: SFP.SFP_MLNX_ERROR_BIT_LONGRANGE_NON_MLNX_CABLE, 0x2: SFP.SFP_ERROR_BIT_I2C_STUCK, 0x3: SFP.SFP_ERROR_BIT_BAD_EEPROM, + 0x4: SFP.SFP_MLNX_ERROR_BIT_ENFORCE_PART_NUMBER_LIST, 0x5: SFP.SFP_ERROR_BIT_UNSUPPORTED_CABLE, 0x6: SFP.SFP_ERROR_BIT_HIGH_TEMP, 0x7: SFP.SFP_ERROR_BIT_BAD_CABLE, - 0x1: 0x00010000, - 0x4: 0x00020000, - 0x8: 0x00040000, - 0xc: 0x00080000 + 0x8: SFP.SFP_MLNX_ERROR_BIT_PMD_TYPE_NOT_ENABLED, + 0xc: SFP.SFP_MLNX_ERROR_BIT_PCIE_POWER_SLOT_EXCEEDED } + SDK_ERRORS_TO_DESCRIPTION = { 0x1: SFP.SFP_MLNX_ERROR_DESCRIPTION_LONGRANGE_NON_MLNX_CABLE, 0x4: SFP.SFP_MLNX_ERROR_DESCRIPTION_ENFORCE_PART_NUMBER_LIST, From d4b63df00f9c54d6b3b4c26020c7483d60b5aceb Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Mon, 7 Jun 2021 10:08:44 +0000 Subject: [PATCH 5/5] Address review comment Signed-off-by: Stephen Sun --- platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py index 8222ead5081f..f8f8bb402ced 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py @@ -347,11 +347,13 @@ class SFP(SfpBase): SFP_MLNX_ERROR_DESCRIPTION_ENFORCE_PART_NUMBER_LIST = 'Enforce part number list' SFP_MLNX_ERROR_DESCRIPTION_PMD_TYPE_NOT_ENABLED = 'PMD type not enabled' SFP_MLNX_ERROR_DESCRIPTION_PCIE_POWER_SLOT_EXCEEDED = 'PCIE system power slot exceeded' + SFP_MLNX_ERROR_DESCRIPTION_RESERVED = 'Reserved' SFP_MLNX_ERROR_BIT_LONGRANGE_NON_MLNX_CABLE = 0x00010000 SFP_MLNX_ERROR_BIT_ENFORCE_PART_NUMBER_LIST = 0x00020000 SFP_MLNX_ERROR_BIT_PMD_TYPE_NOT_ENABLED = 0x00040000 SFP_MLNX_ERROR_BIT_PCIE_POWER_SLOT_EXCEEDED = 0x00080000 + SFP_MLNX_ERROR_BIT_RESERVED = 0x80000000 def __init__(self, sfp_index, sfp_type, sdk_handle_getter, platform): SfpBase.__init__(self) @@ -2215,7 +2217,7 @@ def _get_error_description_dict(cls): 7: cls.SFP_ERROR_DESCRIPTION_BAD_CABLE, 8: cls.SFP_MLNX_ERROR_DESCRIPTION_PMD_TYPE_NOT_ENABLED, 12: cls.SFP_MLNX_ERROR_DESCRIPTION_PCIE_POWER_SLOT_EXCEEDED, - 255: cls.SFP_STATUS_OK + 255: cls.SFP_MLNX_ERROR_DESCRIPTION_RESERVED } def get_error_description(self):