From fc3c49bf83dde28ce76f3abdcf823e8132fc4944 Mon Sep 17 00:00:00 2001 From: Kebo Liu Date: Tue, 14 Dec 2021 22:21:27 +0800 Subject: [PATCH 01/20] change platform API to support RJ45 ports Signed-off-by: Kebo Liu --- .../sonic_platform/chassis.py | 27 +++++++-- .../mlnx-platform-api/sonic_platform/sfp.py | 44 ++++++++++++--- .../mlnx-platform-api/sonic_platform/utils.py | 56 +++++++++++++++++++ 3 files changed, 114 insertions(+), 13 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py index 6a712af68ff2..1025dccd0525 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py @@ -27,7 +27,7 @@ from sonic_py_common.logger import Logger import os from functools import reduce - + from .utils import load_json_file, extract_RJ45_ports_index from . import utils from .device_data import DeviceDataManager from .sfp import SFP, deinitialize_sdk_handle @@ -36,6 +36,8 @@ MAX_SELECT_DELAY = 3600 +RJ45_TYPE = "RJ45" + DMI_FILE = '/sys/firmware/dmi/entries/2-0/raw' DMI_HEADER_LEN = 15 DMI_PRODUCT_NAME = "Product Name" @@ -107,6 +109,14 @@ def __init__(self): self.sfp_initialized_count = 0 self.sfp_event = None self.reboot_cause_initialized = False + + # Build the RJ45 port list from platform.json and hwsku.json + try: + if os.environ["PLATFORM_API_UNIT_TESTING"] == "1": + self.RJ45_port_list = None + except KeyError: + self.RJ45_port_list = extract_RJ45_ports_index() + logger.log_info("Chassis loaded successfully") def __del__(self): @@ -241,7 +251,10 @@ def initialize_single_sfp(self, index): if not self._sfp_list[index]: from .sfp import SFP - self._sfp_list[index] = SFP(index) + if self.RJ45_port_list and index in self.RJ45_port_list: + self._sfp_list[index] = SFP(index, RJ45_TYPE) + else: + self._sfp_list[index] = SFP(index) self.sfp_initialized_count += 1 def initialize_sfp(self): @@ -249,14 +262,20 @@ def initialize_sfp(self): from .sfp import SFP sfp_count = self.get_num_sfps() for index in range(sfp_count): - sfp_module = SFP(index) + if self.RJ45_port_list and index in self.RJ45_port_list: + sfp_module = SFP(index, RJ45_TYPE) + else: + sfp_module = SFP(index) self._sfp_list.append(sfp_module) self.sfp_initialized_count = sfp_count elif self.sfp_initialized_count != len(self._sfp_list): from .sfp import SFP for index in range(len(self._sfp_list)): if self._sfp_list[index] is None: - self._sfp_list[index] = SFP(index) + if self.RJ45_port_list and index in self.RJ45_port_list: + self._sfp_list[index] = SFP(index, RJ45_TYPE) + else: + self._sfp_list[index] = SFP(index) self.sfp_initialized_count = len(self._sfp_list) def get_num_sfps(self): diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py index 59f71bebc9a5..3341acd08cdb 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py @@ -276,6 +276,7 @@ QSFP_TYPE = "QSFP" OSFP_TYPE = "OSFP" QSFP_DD_TYPE = "QSFP_DD" +RJ45_TYPE = "RJ45" #variables for sdk REGISTER_NUM = 1 @@ -386,15 +387,17 @@ class SFP(SfpBase): SFP_MLNX_ERROR_BIT_PCIE_POWER_SLOT_EXCEEDED = 0x00080000 SFP_MLNX_ERROR_BIT_RESERVED = 0x80000000 - def __init__(self, sfp_index, slot_id=0, linecard_port_count=0, lc_name=None): + def __init__(self, sfp_index, sfp_type=None, slot_id=0, linecard_port_count=0, lc_name=None): super(SFP, self).__init__() + self._sfp_type = sfp_type if slot_id == 0: # For non-modular chassis self.index = sfp_index + 1 self.sdk_index = sfp_index - from .thermal import initialize_sfp_thermal - self._thermal_list = initialize_sfp_thermal(sfp_index) + if self._sfp_type != RJ45_TYPE: + from .thermal import initialize_sfp_thermal + self._thermal_list = initialize_sfp_thermal(sfp_index) else: # For modular chassis # (slot_id % MAX_LC_CONUNT - 1) * MAX_PORT_COUNT + (sfp_index + 1) * (MAX_PORT_COUNT / LC_PORT_COUNT) max_linecard_count = DeviceDataManager.get_linecard_count() @@ -402,11 +405,11 @@ def __init__(self, sfp_index, slot_id=0, linecard_port_count=0, lc_name=None): self.index = (slot_id % max_linecard_count - 1) * max_linecard_port_count + sfp_index * (max_linecard_port_count / linecard_port_count) + 1 self.sdk_index = sfp_index - from .thermal import initialize_linecard_sfp_thermal - self._thermal_list = initialize_linecard_sfp_thermal(lc_name, slot_id, sfp_index) + if self._sfp_type != RJ45_TYPE: + from .thermal import initialize_linecard_sfp_thermal + self._thermal_list = initialize_linecard_sfp_thermal(lc_name, slot_id, sfp_index) self.slot_id = slot_id - self._sfp_type = None self._sfp_capability = None @property @@ -627,7 +630,8 @@ def reinit(self): Re-initialize this SFP object when a new SFP inserted :return: """ - self._sfp_type = None + if self.sfp_type != RJ45_TYPE: + self.sfp_type = None self._sfp_capability = None def get_presence(self): @@ -637,6 +641,9 @@ def get_presence(self): Returns: bool: True if device is present, False if not """ + if self.sfp_type == RJ45_TYPE: + return True + presence = False ethtool_cmd = "ethtool -m sfp{} hex on offset 0 length 1 2>/dev/null".format(self.index) try: @@ -907,6 +914,25 @@ def get_transceiver_info(self): transceiver_info_dict['nominal_bit_rate'] = "Not supported for CMIS cables" transceiver_info_dict['application_advertisement'] = host_media_list + elif self.sfp_type == RJ45_TYPE: + transceiver_info_dict['type'] = self.sfp_type + transceiver_info_dict['manufacturer'] = 'N/A' + transceiver_info_dict['model'] = 'N/A' + transceiver_info_dict['vendor_rev'] = 'N/A' + transceiver_info_dict['serial'] = 'N/A' + transceiver_info_dict['vendor_oui'] = 'N/A' + transceiver_info_dict['vendor_date'] = 'N/A' + transceiver_info_dict['connector'] = 'N/A' + transceiver_info_dict['encoding'] = 'N/A' + transceiver_info_dict['ext_identifier'] = 'N/A' + transceiver_info_dict['ext_rateselect_compliance'] = 'N/A' + transceiver_info_dict['cable_type'] = 'N/A' + transceiver_info_dict['cable_length'] = 'N/A' + transceiver_info_dict['specification_compliance'] = 'N/A' + transceiver_info_dict['nominal_bit_rate'] = 'N/A' + transceiver_info_dict['application_advertisement'] = 'N/A' + return transceiver_info_dict + else: offset = 0 vendor_rev_width = XCVR_HW_REV_WIDTH_SFP @@ -1043,7 +1069,7 @@ def get_transceiver_bulk_status(self): ] transceiver_dom_info_dict = dict.fromkeys(dom_info_dict_keys, 'N/A') - if self.sfp_type == OSFP_TYPE: + if self.sfp_type == OSFP_TYPE or self.sfp_type == RJ45_TYPE: pass elif self.sfp_type == QSFP_TYPE: @@ -1242,7 +1268,7 @@ def get_transceiver_threshold_info(self): ] transceiver_dom_threshold_info_dict = dict.fromkeys(dom_info_dict_keys, 'N/A') - if self.sfp_type == OSFP_TYPE: + if self.sfp_type == OSFP_TYPE or self.sfp_type == RJ45_TYPE: pass elif self.sfp_type == QSFP_TYPE: diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/utils.py b/platform/mellanox/mlnx-platform-api/sonic_platform/utils.py index 0650d9af1a1c..1e90f4f96846 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/utils.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/utils.py @@ -16,8 +16,19 @@ # import functools import subprocess +import ast +import json +import sys +import os +from sonic_py_common import device_info from sonic_py_common.logger import Logger +HWSKU_JSON = 'hwsku.json' + +PORT_INDEX_KEY = "index" +PORT_TYPE_KEY = "port_type" +RJ45_PORT_TYPE = "RJ45" + logger = Logger() @@ -194,3 +205,48 @@ def _impl(*args, **kwargs): return return_value return _impl return wrapper + + +def load_json_file(filename): + # load 'platform.json' or 'hwsku.json' file + try: + with open(filename) as fp: + try: + data = json.load(fp) + except json.JSONDecodeError: + print("Json file does not exist") + data_dict = ast.literal_eval(json.dumps(data)) + return data_dict + except Exception as e: + print("error occurred while parsing json: {}".format(sys.exc_info()[1])) + return None + + +def extract_RJ45_ports_index(): + # Cross check 'platform.json' and 'hwsku.json' to extract the RJ45 port index if exists. + hwsku_path = device_info.get_path_to_hwsku_dir() + platform_file = device_info.get_path_to_port_config_file() + platform_dict = load_json_file(platform_file)['interfaces'] + hwsku_file = os.path.join(hwsku_path, HWSKU_JSON) + hwsku_dict = load_json_file(hwsku_file)['interfaces'] + port_name_to_index_map_dict = {} + RJ45_port_index_list = [] + + # Compose a interface name to index mapping from 'platform.json' + for i, (key, value) in enumerate(platform_dict.items()): + if PORT_INDEX_KEY in value: + index_raw = value[PORT_INDEX_KEY] + # The index could be "1" or "1, 1, 1, 1" + index = index_raw.split(',')[0] + port_name_to_index_map_dict[key] = index + + if not bool(port_name_to_index_map_dict): + return None + + # Check if "port_type" specified as "RJ45", if yes, add the port index to the list. + for i, (key, value) in enumerate(hwsku_dict.items()): + if key in port_name_to_index_map_dict and PORT_TYPE_KEY in value and value[PORT_TYPE_KEY] == RJ45_PORT_TYPE: + RJ45_port_index_list.append(int(port_name_to_index_map_dict[key])-1) + + return RJ45_port_index_list if bool(RJ45_port_index_list) else None + From a1aa3d7242bc35353410a30a8763cf1b0669f1a2 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Wed, 5 Jan 2022 16:37:55 +0800 Subject: [PATCH 02/20] Support ONIE upgrade for SN2201 Do not check ONIE version on SN2201. In legacy switches, upgrading firmware from ONIE was supported from a certain version while it is supported from the very beginning on SN2201. So we do not need to check ONIE version on SN2201 Signed-off-by: Stephen Sun --- .../mlnx-platform-api/sonic_platform/component.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py index 37f04f9a1dee..bacf2cbefa10 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py @@ -29,6 +29,7 @@ import glob import tempfile import subprocess + from sonic_py_common import device_info if sys.version_info[0] > 2: import configparser else: @@ -136,7 +137,13 @@ class ONIEUpdater(object): ONIE_IMAGE_INFO_COMMAND = '/bin/bash {} -q -i' + PLATFORM_ALWAYS_SUPPORT_UPGRADE = ['x86_64-nvidia_sn2201-r0'] + BIOS_UPDATE_FILE_EXT = '.rom' + + + def __init__(self): + self.platform = device_info.get_platform() def __add_prefix(self, image_path): if self.BIOS_UPDATE_FILE_EXT not in image_path: @@ -336,6 +343,9 @@ def update_firmware(self, image_path, allow_reboot=True): raise def is_non_onie_firmware_update_supported(self): + if self.platform in self.PLATFORM_ALWAYS_SUPPORT_UPGRADE: + return True + current_version = self.get_onie_version() _, _, major1, minor1, release1, _ = self.parse_onie_version(current_version) version1 = int("{}{}{}".format(major1, minor1, release1)) From b05bd1f3ad3b8be21c2c68c11ee1e1669da0dd9c Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Thu, 13 Jan 2022 16:13:52 +0800 Subject: [PATCH 03/20] Adjust mlnx-ssd-fw-update.sh for SN2201 Signed-off-by: Stephen Sun --- platform/mellanox/mlnx-ssd-fw-update.sh | 56 +++++++++++++++++++++---- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/platform/mellanox/mlnx-ssd-fw-update.sh b/platform/mellanox/mlnx-ssd-fw-update.sh index 7a180bde7bc7..4e3b95db9c65 100755 --- a/platform/mellanox/mlnx-ssd-fw-update.sh +++ b/platform/mellanox/mlnx-ssd-fw-update.sh @@ -21,9 +21,8 @@ #= Global variable # #= #===== -VERSION="1.5" +VERSION="1.6" #===== -SWITCH_SSD_DEV="/dev/sda" UTIL_TITLE="This is MLNX SSD firmware update utility to read and write SSD FW. Version ${VERSION}" DEPENDECIES=("smartctl" "sha256sum" "tar" "/bin/bash" "gpg" "sed" "realpath" "dirname") TRUE="0" @@ -37,6 +36,7 @@ DEBUG_MSG="DEBUG" # remove all instance after script is ready. #===== PKG_EXTRACTED=$FALSE LOGGER_UTIL=$FALSE +SSD_DEV_NAME="" SSD_FW_VER="" SSD_DEVICE_MODEL="" SSD_SERIAL="" @@ -230,7 +230,7 @@ function get_ssd_fw_version() { [ $1 ] || { LOG_MSG_AND_EXIT "Wrong usage - ${FUNCNAME[0]}()"; } local device_fw_version - device_fw_version=$(smartctl -i $SWITCH_SSD_DEV | grep -Po "Firmware Version: +\K[^,]+") + device_fw_version=$(smartctl -i $SSD_DEV_NAME | grep -Po "Firmware Version: +\K[^,]+") LOG_MSG "device_fw_version: $device_fw_version" ${DEBUG_MSG} eval $1='$device_fw_version' } @@ -242,7 +242,7 @@ function get_ssd_device_model() { [ $1 ] || { LOG_MSG_AND_EXIT "Wrong usage - ${FUNCNAME[0]}()"; } local device_model_name - device_model_name=$(smartctl -i $SWITCH_SSD_DEV | grep -Po "Device Model: +\K[^,]+") + device_model_name=$(smartctl -i $SSD_DEV_NAME | grep -E "Device Model:|Model Number:" | awk '{$1=$2="";print $0}'| sed 's/^ *//g') LOG_MSG "device_model_name: $device_model_name" ${DEBUG_MSG} eval $1='$device_model_name' } @@ -254,7 +254,7 @@ function get_ssd_size() { [ $1 ] || { LOG_MSG_AND_EXIT "Wrong usage - ${FUNCNAME[0]}()"; } local device_size - device_size=$(smartctl -i $SWITCH_SSD_DEV | grep -Po "User Capacity:.+bytes \[\K[^ ]+") + device_size=$(smartctl -i $SSD_DEV_NAME | grep -E "User Capacity:|Size/Capacity" | awk -F '\[|\]' '{print $2}' | awk '{print $1}') LOG_MSG "device_size: $device_size" ${DEBUG_MSG} eval $1='$device_size' } @@ -266,16 +266,56 @@ function get_ssd_serial() { [ $1 ] || { LOG_MSG_AND_EXIT "Wrong usage - ${FUNCNAME[0]}()"; } local device_serial - device_serial=$(smartctl -i $SWITCH_SSD_DEV | grep -Po "Serial Number: +\K[^,]+") + device_serial=$(smartctl -i $SSD_DEV_NAME | grep -Po "Serial Number: +\K[^,]+") LOG_MSG "device_serial: $device_serial" ${DEBUG_MSG} eval $1='$device_serial' } #==============================================================================# -#= This function check if given argument is valid and return boolean result. # +# This function check SSD device name # +# +function get_ssd_device_name() { + [ $1 ] || { LOG_MSG_AND_EXIT "Wrong usage - ${FUNCNAME[0]}()"; } + + non_rem_mount_disks="" + non_rem_mount_disks_count=0 + mount_parts=$(cat /proc/partitions | grep -v "^major" | grep -v ram | awk '{{print $4}}') + for blk_dev_name in ${mount_parts} + do + blk_dev_link=$(find /sys/bus /sys/class /sys/block/ -name ${blk_dev_name}) + for first_blk_dev_link in ${blk_dev_link} + do + if ls -l ${first_blk_dev_link} | grep -q virtual; then + continue + fi + if [ -e ${first_blk_dev_link}/removable ] ; then + if [ "0" = $(cat ${first_blk_dev_link}/removable) ] ; then + let non_rem_mount_disks_count=${non_rem_mount_disks_count}+1 + if [ "1" == "${non_rem_mount_disks_count}" ] ; then + non_rem_mount_disks="${blk_dev_name}" + else + non_rem_mount_disks="${non_rem_mount_disks} ${blk_dev_name}" + fi + fi + fi + break + done + done + if [ "1" == "${non_rem_mount_disks_count}" ] ; then + device_name="/dev/${non_rem_mount_disks}" + else + $1="/dev/sda" + fi + LOG_MSG "device_name: $device_name" ${DEBUG_MSG} + eval $1='$device_name' +} + +#==============================================================================# +#= This function check if given argument. # #= function get_ssd_info() { LOG_MSG "func: ${FUNCNAME[0]}()" ${DEBUG_MSG} + get_ssd_device_name SSD_DEV_NAME get_ssd_fw_version SSD_FW_VER get_ssd_device_model SSD_DEVICE_MODEL get_ssd_serial SSD_SERIAL @@ -554,7 +594,7 @@ function print_ssd_info() { LOG_MSG "Device Model\t : $SSD_DEVICE_MODEL" LOG_MSG "Serial Number\t : $SSD_SERIAL" LOG_MSG "User Capacity\t : $SSD_SIZE GB" - LOG_MSG "Firmware Version : $SSD_FW_VER" + LOG_MSG "Firmware Version : $SSD_FW_VER" fi } From 07370efb5f67a9bfd68a770ed864de189e37ced9 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Fri, 14 Jan 2022 08:17:50 +0000 Subject: [PATCH 04/20] Skip ComponentBIOS on SN2201 Signed-off-by: Stephen Sun --- platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py index 1025dccd0525..5a66172acfea 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py @@ -533,7 +533,9 @@ def initialize_components(self): from .component import ComponentONIE, ComponentSSD, ComponentBIOS, ComponentCPLD self._component_list.append(ComponentONIE()) self._component_list.append(ComponentSSD()) - self._component_list.append(ComponentBIOS()) + # Upgrading BIOS is not supported on SN2201 + if DeviceDataManager.get_platform_name() not in ['x86_64-nvidia_sn2201-r0']: + self._component_list.append(ComponentBIOS()) self._component_list.extend(ComponentCPLD.get_component_list()) def get_num_components(self): From bdb90542fac1fc460def4a3424cec814878db926 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Sat, 22 Jan 2022 17:55:49 +0800 Subject: [PATCH 05/20] Support RJ45 port 1. A derived class representing RJ45 ports is introduced. get_presence always returns True 2. SFP error event is leveraged to represent "unknown" and "not present" states. By doing so, most of code change for supporting RJ45 is restricted in platform API and CLI xcvrd won't need to be updated for this Signed-off-by: Stephen Sun --- .../sonic_platform/chassis.py | 36 ++- .../mlnx-platform-api/sonic_platform/sfp.py | 267 ++++++++++++++++-- 2 files changed, 273 insertions(+), 30 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py index 5a66172acfea..8a7f28da5e65 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py @@ -30,7 +30,7 @@ from .utils import load_json_file, extract_RJ45_ports_index from . import utils from .device_data import DeviceDataManager - from .sfp import SFP, deinitialize_sdk_handle + from .sfp import SFP, RJ45Port, deinitialize_sdk_handle except ImportError as e: raise ImportError (str(e) + "- required module not found") @@ -252,7 +252,7 @@ def initialize_single_sfp(self, index): if not self._sfp_list[index]: from .sfp import SFP if self.RJ45_port_list and index in self.RJ45_port_list: - self._sfp_list[index] = SFP(index, RJ45_TYPE) + self._sfp_list[index] = RJ45Port(index) else: self._sfp_list[index] = SFP(index) self.sfp_initialized_count += 1 @@ -263,7 +263,7 @@ def initialize_sfp(self): sfp_count = self.get_num_sfps() for index in range(sfp_count): if self.RJ45_port_list and index in self.RJ45_port_list: - sfp_module = SFP(index, RJ45_TYPE) + sfp_module = RJ45Port(index) #SFP(index, RJ45_TYPE) else: sfp_module = SFP(index) self._sfp_list.append(sfp_module) @@ -273,7 +273,7 @@ def initialize_sfp(self): for index in range(len(self._sfp_list)): if self._sfp_list[index] is None: if self.RJ45_port_list and index in self.RJ45_port_list: - self._sfp_list[index] = SFP(index, RJ45_TYPE) + self._sfp_list[index] = RJ45Port(index) # SFP(index, RJ45_TYPE) else: self._sfp_list[index] = SFP(index) self.sfp_initialized_count = len(self._sfp_list) @@ -358,7 +358,33 @@ def get_change_event(self, timeout=0): status = self.sfp_event.check_sfp_status(port_dict, error_dict, timeout) if status: - self.reinit_sfps(port_dict) + if self.RJ45_port_list: + # Translate any plugin/plugout event into error dict + unknown_port = set() + unplug_port = set() + for index, event in port_dict.items(): + if index in self.RJ45_port_list: + # Remove it from port event + # check if it's unknown event + if event == '0': # Remove event + unplug_port.add(index) + elif event != '1': + unknown_port.add(index) + # This is to leverage TRANSCEIVER_STATUS table to represent 'Not present' and 'Unknown' state + # The event should satisfies: + # - Vendor specific error bit + # - Non-blocking bit + # Currently, the 2 MSBs are reserved for this since they are most unlikely to be used in the near future + for index in unknown_port: + # Bit 31 for unknown + port_dict[index] = '2147483648' + error_dict[index] = 'Unknown' + for index in unplug_port: + # Bit 30 for not present + port_dict[index] = '1073741824' + error_dict[index] = 'Not present' + if port_dict: + self.reinit_sfps(port_dict) result_dict = {'sfp':port_dict} if error_dict: result_dict['sfp_error'] = error_dict diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py index 3341acd08cdb..dec92e2f47b1 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py @@ -641,9 +641,6 @@ def get_presence(self): Returns: bool: True if device is present, False if not """ - if self.sfp_type == RJ45_TYPE: - return True - presence = False ethtool_cmd = "ethtool -m sfp{} hex on offset 0 length 1 2>/dev/null".format(self.index) try: @@ -914,25 +911,6 @@ def get_transceiver_info(self): transceiver_info_dict['nominal_bit_rate'] = "Not supported for CMIS cables" transceiver_info_dict['application_advertisement'] = host_media_list - elif self.sfp_type == RJ45_TYPE: - transceiver_info_dict['type'] = self.sfp_type - transceiver_info_dict['manufacturer'] = 'N/A' - transceiver_info_dict['model'] = 'N/A' - transceiver_info_dict['vendor_rev'] = 'N/A' - transceiver_info_dict['serial'] = 'N/A' - transceiver_info_dict['vendor_oui'] = 'N/A' - transceiver_info_dict['vendor_date'] = 'N/A' - transceiver_info_dict['connector'] = 'N/A' - transceiver_info_dict['encoding'] = 'N/A' - transceiver_info_dict['ext_identifier'] = 'N/A' - transceiver_info_dict['ext_rateselect_compliance'] = 'N/A' - transceiver_info_dict['cable_type'] = 'N/A' - transceiver_info_dict['cable_length'] = 'N/A' - transceiver_info_dict['specification_compliance'] = 'N/A' - transceiver_info_dict['nominal_bit_rate'] = 'N/A' - transceiver_info_dict['application_advertisement'] = 'N/A' - return transceiver_info_dict - else: offset = 0 vendor_rev_width = XCVR_HW_REV_WIDTH_SFP @@ -1268,6 +1246,9 @@ def get_transceiver_threshold_info(self): ] transceiver_dom_threshold_info_dict = dict.fromkeys(dom_info_dict_keys, 'N/A') + if not self.dom_supported: + return transceiver_dom_threshold_info_dict + if self.sfp_type == OSFP_TYPE or self.sfp_type == RJ45_TYPE: pass @@ -1836,6 +1817,9 @@ def get_tx_bias(self): for channel 0 to channel 4. Ex. ['110.09', '111.12', '108.21', '112.09'] """ + if not self.dom_supported: + return None + tx_bias_list = [] if self.sfp_type == QSFP_TYPE: offset = 0 @@ -1904,6 +1888,9 @@ def get_rx_power(self): power in mW for channel 0 to channel 4. Ex. ['1.77', '1.71', '1.68', '1.70'] """ + if not self.dom_supported: + return None + rx_power_list = [] if self.sfp_type == OSFP_TYPE: # OSFP not supported on our platform yet. @@ -1981,6 +1968,9 @@ def get_tx_power(self): for channel 0 to channel 4. Ex. ['1.86', '1.86', '1.86', '1.86'] """ + if not self.dom_supported: + return None + tx_power_list = [] if self.sfp_type == OSFP_TYPE: # OSFP not supported on our platform yet. @@ -2135,7 +2125,7 @@ def is_cpu(cls, port): @classmethod - def is_port_admin_status_up(cls, sdk_handle, log_port): + def _fetch_port_status(cls, sdk_handle, log_port): oper_state_p = new_sx_port_oper_state_t_p() admin_state_p = new_sx_port_admin_state_t_p() module_state_p = new_sx_port_module_state_t_p() @@ -2143,12 +2133,19 @@ def is_port_admin_status_up(cls, sdk_handle, log_port): assert rc == SXD_STATUS_SUCCESS, "sx_api_port_state_get failed, rc = %d" % rc admin_state = sx_port_admin_state_t_p_value(admin_state_p) - + oper_state = sx_port_oper_state_t_p_value(oper_state_p) + delete_sx_port_oper_state_t_p(oper_state_p) delete_sx_port_admin_state_t_p(admin_state_p) delete_sx_port_module_state_t_p(module_state_p) - return admin_state == SX_PORT_ADMIN_STATUS_UP + return oper_state, admin_state + + + @classmethod + def is_port_admin_status_up(cls, sdk_handle, log_port): + _, admin_state = cls._fetch_port_status(cls, sdk_handle, log_port); + admin_state == SX_PORT_ADMIN_STATUS_UP @classmethod @@ -2372,3 +2369,223 @@ def get_error_description(self): else: error_description = "Unknow SFP module status ({})".format(oper_status) return error_description + + +class RJ45Port(SFP): + """class derived from SFP, representing RJ45 ports""" + + def __init__(self, sfp_index): + super(RJ45Port, self).__init__(sfp_index, RJ45_TYPE) + self._sfp_type = RJ45_TYPE + + # Reuse sdk_handle + + @property + def sfp_type(self): + return self._sfp_type + + def get_presence(self): + """ + Retrieves the presence of the device + + Returns: + bool: True if device is present, False if not + """ + # Reimplement it via using SDK API + return True + + @property + def dom_supported(self): + return False + + def get_transceiver_info(self): + """ + Retrieves transceiver info of this SFP + + Returns: + A dict which contains following keys/values : + ================================================================================ + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + type |1*255VCHAR |type of SFP + vendor_rev |1*255VCHAR |vendor revision of SFP + serial |1*255VCHAR |serial number of the SFP + manufacturer |1*255VCHAR |SFP vendor name + model |1*255VCHAR |SFP model name + connector |1*255VCHAR |connector information + encoding |1*255VCHAR |encoding information + ext_identifier |1*255VCHAR |extend identifier + ext_rateselect_compliance |1*255VCHAR |extended rateSelect compliance + cable_length |INT |cable length in m + mominal_bit_rate |INT |nominal bit rate by 100Mbs + specification_compliance |1*255VCHAR |specification compliance + vendor_date |1*255VCHAR |vendor date + vendor_oui |1*255VCHAR |vendor OUI + application_advertisement |1*255VCHAR |supported applications advertisement + ================================================================================ + """ + transceiver_info_dict = {} + + transceiver_info_dict['type'] = self.sfp_type + transceiver_info_dict['manufacturer'] = 'N/A' + transceiver_info_dict['model'] = 'N/A' + transceiver_info_dict['vendor_rev'] = 'N/A' + transceiver_info_dict['serial'] = 'N/A' + transceiver_info_dict['vendor_oui'] = 'N/A' + transceiver_info_dict['vendor_date'] = 'N/A' + transceiver_info_dict['connector'] = 'N/A' + transceiver_info_dict['encoding'] = 'N/A' + transceiver_info_dict['ext_identifier'] = 'N/A' + transceiver_info_dict['ext_rateselect_compliance'] = 'N/A' + transceiver_info_dict['cable_type'] = 'N/A' + transceiver_info_dict['cable_length'] = 'N/A' + transceiver_info_dict['specification_compliance'] = 'N/A' + transceiver_info_dict['nominal_bit_rate'] = 'N/A' + transceiver_info_dict['application_advertisement'] = 'N/A' + + return transceiver_info_dict + + def get_transceiver_bulk_status(self): + """ + Retrieves transceiver bulk status of this SFP + + Returns: + A dict which contains following keys/values : + ======================================================================== + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + RX LOS |BOOLEAN |RX lost-of-signal status, + | |True if has RX los, False if not. + TX FAULT |BOOLEAN |TX fault status, + | |True if has TX fault, False if not. + Reset status |BOOLEAN |reset status, + | |True if SFP in reset, False if not. + LP mode |BOOLEAN |low power mode status, + | |True in lp mode, False if not. + TX disable |BOOLEAN |TX disable status, + | |True TX disabled, False if not. + TX disabled channel |HEX |disabled TX channles in hex, + | |bits 0 to 3 represent channel 0 + | |to channel 3. + Temperature |INT |module temperature in Celsius + Voltage |INT |supply voltage in mV + TX bias |INT |TX Bias Current in mA + RX power |INT |received optical power in mW + TX power |INT |TX output power in mW + ======================================================================== + """ + transceiver_dom_info_dict = {} + dom_info_dict_keys = ['temperature', 'voltage', + 'rx1power', 'rx2power', + 'rx3power', 'rx4power', + 'rx5power', 'rx6power', + 'rx7power', 'rx8power', + 'tx1bias', 'tx2bias', + 'tx3bias', 'tx4bias', + 'tx5bias', 'tx6bias', + 'tx7bias', 'tx8bias', + 'tx1power', 'tx2power', + 'tx3power', 'tx4power', + 'tx5power', 'tx6power', + 'tx7power', 'tx8power' + ] + transceiver_dom_info_dict = dict.fromkeys(dom_info_dict_keys, 'N/A') + + return transceiver_dom_info_dict + + def get_transceiver_threshold_info(self): + """ + Retrieves transceiver threshold info of this SFP + + Returns: + A dict which contains following keys/values : + ======================================================================== + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + temphighalarm |FLOAT |High Alarm Threshold value of temperature in Celsius. + templowalarm |FLOAT |Low Alarm Threshold value of temperature in Celsius. + temphighwarning |FLOAT |High Warning Threshold value of temperature in Celsius. + templowwarning |FLOAT |Low Warning Threshold value of temperature in Celsius. + vcchighalarm |FLOAT |High Alarm Threshold value of supply voltage in mV. + vcclowalarm |FLOAT |Low Alarm Threshold value of supply voltage in mV. + vcchighwarning |FLOAT |High Warning Threshold value of supply voltage in mV. + vcclowwarning |FLOAT |Low Warning Threshold value of supply voltage in mV. + rxpowerhighalarm |FLOAT |High Alarm Threshold value of received power in dBm. + rxpowerlowalarm |FLOAT |Low Alarm Threshold value of received power in dBm. + rxpowerhighwarning |FLOAT |High Warning Threshold value of received power in dBm. + rxpowerlowwarning |FLOAT |Low Warning Threshold value of received power in dBm. + txpowerhighalarm |FLOAT |High Alarm Threshold value of transmit power in dBm. + txpowerlowalarm |FLOAT |Low Alarm Threshold value of transmit power in dBm. + txpowerhighwarning |FLOAT |High Warning Threshold value of transmit power in dBm. + txpowerlowwarning |FLOAT |Low Warning Threshold value of transmit power in dBm. + txbiashighalarm |FLOAT |High Alarm Threshold value of tx Bias Current in mA. + txbiaslowalarm |FLOAT |Low Alarm Threshold value of tx Bias Current in mA. + txbiashighwarning |FLOAT |High Warning Threshold value of tx Bias Current in mA. + txbiaslowwarning |FLOAT |Low Warning Threshold value of tx Bias Current in mA. + ======================================================================== + """ + transceiver_dom_threshold_info_dict = {} + + dom_info_dict_keys = ['temphighalarm', 'temphighwarning', + 'templowalarm', 'templowwarning', + 'vcchighalarm', 'vcchighwarning', + 'vcclowalarm', 'vcclowwarning', + 'rxpowerhighalarm', 'rxpowerhighwarning', + 'rxpowerlowalarm', 'rxpowerlowwarning', + 'txpowerhighalarm', 'txpowerhighwarning', + 'txpowerlowalarm', 'txpowerlowwarning', + 'txbiashighalarm', 'txbiashighwarning', + 'txbiaslowalarm', 'txbiaslowwarning' + ] + transceiver_dom_threshold_info_dict = dict.fromkeys(dom_info_dict_keys, 'N/A') + + return transceiver_dom_threshold_info_dict + + def get_reset_status(self): + return False + + def get_lpmode(self): + """ + Retrieves the lpmode (low power mode) status of this SFP + + Returns: + A Boolean, True if lpmode is enabled, False if disabled + """ + return False + + def reset(self): + """ + Reset SFP and return all user module settings to their default state. + + Returns: + A boolean, True if successful, False if not + + refer plugins/sfpreset.py + """ + return False + + def set_lpmode(self, lpmode): + """ + Sets the lpmode (low power mode) of SFP + + Args: + lpmode: A Boolean, True to enable lpmode, False to disable it + Note : lpmode can be overridden by set_power_override + + Returns: + A boolean, True if lpmode is set successfully, False if not + """ + return False + + def get_error_description(self): + """ + Get error description + + Args: + error_code: The error code returned by _get_error_code + + Returns: + The error description + """ + return False + From cbe8dcda01b6a30095e10b6989eb6c203cdd083c Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Fri, 11 Feb 2022 09:18:12 +0000 Subject: [PATCH 06/20] Fix error in sfp.reinit Signed-off-by: Stephen Sun --- platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py | 2 +- 1 file changed, 1 insertion(+), 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 dec92e2f47b1..edaf85035eab 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py @@ -631,7 +631,7 @@ def reinit(self): :return: """ if self.sfp_type != RJ45_TYPE: - self.sfp_type = None + self._sfp_type = None self._sfp_capability = None def get_presence(self): From b4c68971da30308253b0e0e338c1f36542c7b259 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Fri, 18 Feb 2022 03:24:09 +0000 Subject: [PATCH 07/20] Support initialization of RJ45 port status - Hide the logic to convert port_change to error_dict inside sfp_event - In case there are some RJ45 ports are down (not present), report them as sfp_event during the first round of get_change_event This is because SDK/FW will never report port change occurred before init We must make compensation for it in platform API Signed-off-by: Stephen Sun --- .../sonic_platform/chassis.py | 27 +------- .../sonic_platform/sfp_event.py | 69 ++++++++++++++++++- 2 files changed, 69 insertions(+), 27 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py index 8a7f28da5e65..b0cfd5359deb 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py @@ -342,7 +342,7 @@ def get_change_event(self, timeout=0): # Initialize SFP event first if not self.sfp_event: from .sfp_event import sfp_event - self.sfp_event = sfp_event() + self.sfp_event = sfp_event(self.RJ45_port_list) self.sfp_event.initialize() wait_for_ever = (timeout == 0) @@ -358,31 +358,6 @@ def get_change_event(self, timeout=0): status = self.sfp_event.check_sfp_status(port_dict, error_dict, timeout) if status: - if self.RJ45_port_list: - # Translate any plugin/plugout event into error dict - unknown_port = set() - unplug_port = set() - for index, event in port_dict.items(): - if index in self.RJ45_port_list: - # Remove it from port event - # check if it's unknown event - if event == '0': # Remove event - unplug_port.add(index) - elif event != '1': - unknown_port.add(index) - # This is to leverage TRANSCEIVER_STATUS table to represent 'Not present' and 'Unknown' state - # The event should satisfies: - # - Vendor specific error bit - # - Non-blocking bit - # Currently, the 2 MSBs are reserved for this since they are most unlikely to be used in the near future - for index in unknown_port: - # Bit 31 for unknown - port_dict[index] = '2147483648' - error_dict[index] = 'Unknown' - for index in unplug_port: - # Bit 30 for not present - port_dict[index] = '1073741824' - error_dict[index] = 'Not present' if port_dict: self.reinit_sfps(port_dict) result_dict = {'sfp':port_dict} 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 06948af3286b..4bdd5b7eeffb 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py @@ -115,6 +115,10 @@ class MockSxFd(object): SDK_SFP_STATE_DIS: str(SFP.SFP_STATUS_BIT_REMOVED), } +# RJ45 ports events definition +RJ45_UNPLUG_EVENT = '1073741824' +RJ45_UNKNOWN_EVENT = '2147483648' + # system level event/error EVENT_ON_ALL_SFP = '-1' SYSTEM_NOT_READY = 'system_not_ready' @@ -134,7 +138,7 @@ class sfp_event: SX_OPEN_TIMEOUT = 5 SELECT_TIMEOUT = 1 - def __init__(self): + def __init__(self, rj45_port_list=None): self.swid = 0 self.handle = None @@ -142,6 +146,10 @@ def __init__(self): self.rx_fd_p = new_sx_fd_t_p() self.user_channel_p = new_sx_user_channel_t_p() + self.RJ45_port_list = rj45_port_list + if rj45_port_list: + self.absent_ports_before_init = [] + def initialize(self): swid_cnt_p = None @@ -205,6 +213,30 @@ def initialize(self): if rc != SX_STATUS_SUCCESS: raise RuntimeError("sx_api_host_ifc_trap_id_register_set failed with rc {}, exiting...".format(rc)) + + if self.RJ45_port_list: + # Fetch the present state of each RJ45 port. + 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) + absent_port_list = [] + + for i in self.RJ45_port_list: + module_id_info = sx_mgmt_module_id_info_t() + module_id_info.slot_id = 0 + module_id_info.module_id = i + sx_mgmt_module_id_info_t_arr_setitem(module_id_info_list, 0, module_id_info) + + rc = sx_mgmt_phy_module_info_get(self.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) + if mod_info.module_state.oper_state not in [SX_PORT_MODULE_STATUS_PLUGGED, SX_PORT_MODULE_STATUS_PLUGGED_DISABLED]: + absent_port_list.append(i) + + self.absent_ports_before_init = absent_port_list + + delete_sx_mgmt_module_id_info_t_arr(module_id_info_list) + delete_sx_mgmt_phy_module_info_t_arr(module_info_list) except Exception as e: logger.log_error("sfp_event initialization failed due to {}, exiting...".format(repr(e))) if swid_cnt_p is not None: @@ -254,6 +286,15 @@ def check_sfp_status(self, port_change, error_dict, timeout): to repeat calling it with timeout = 0 in a loop until no new notification read (in this case it returns false). by doing so all the notifications in the fd can be retrieved through a single call to get_change_event. """ + if self.absent_ports_before_init: + # This is the first time this method is called. + # We should return the ports that are not present during initialization + for i in self.absent_ports_before_init: + error_dict[i + 1] = 'Not present' + port_change[i + 1] = RJ45_UNPLUG_EVENT + self.absent_ports_before_init = [] + return True + found = 0 try: @@ -315,6 +356,32 @@ def check_sfp_status(self, port_change, error_dict, timeout): error_dict[port+1] = error_description found += 1 + if self.RJ45_port_list: + # Translate any plugin/plugout event into error dict for RJ45 ports + unknown_port = set() + unplug_port = set() + for index, event in port_change.items(): + if index in self.RJ45_port_list: + # Remove it from port event + # check if it's unknown event + if event == '0': # Remove event + unplug_port.add(index) + elif event != '1': + unknown_port.add(index) + # This is to leverage TRANSCEIVER_STATUS table to represent 'Not present' and 'Unknown' state + # The event should satisfies: + # - Vendor specific error bit + # - Non-blocking bit + # Currently, the 2 MSBs are reserved for this since they are most unlikely to be used in the near future + for index in unknown_port: + # Bit 31 for unknown + port_change[index] = RJ45_UNKNOWN_EVENT + error_dict[index] = 'Unknown' + for index in unplug_port: + # Bit 30 for not present + port_change[index] = RJ45_UNPLUG_EVENT + error_dict[index] = 'Not present' + return found != 0 def on_pmpe(self, fd_p): From df3c249d03d4d5936e466247237e34886e383a48 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Fri, 18 Feb 2022 07:01:38 +0000 Subject: [PATCH 08/20] Fix issue: - Report unknown event for RJ45 ports - Fix syntax error Signed-off-by: Stephen Sun --- .../mlnx-platform-api/sonic_platform/sfp_event.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 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 4bdd5b7eeffb..59f7b2b93ccb 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py @@ -147,8 +147,7 @@ def __init__(self, rj45_port_list=None): self.user_channel_p = new_sx_user_channel_t_p() self.RJ45_port_list = rj45_port_list - if rj45_port_list: - self.absent_ports_before_init = [] + self.absent_ports_before_init = [] def initialize(self): swid_cnt_p = None @@ -325,7 +324,16 @@ def check_sfp_status(self, port_change, error_dict, timeout): # 3. and then the sfp module is removed # 4. sfp_event starts to try fetching the change event # in this case found is increased so that True will be returned - logger.log_info("unknown module state {}, maybe the port suffers two adjacent insertion/removal".format(module_state)) + # For RJ45 ports, we will report "unknown" event anyway since it's legal + reported = 0 + if self.RJ45_port_list: + for port in port_list: + if port in self.RJ45_port_list: + port_change[port+1] = sfp_state + reported += 1 + + if not reported: + logger.log_info("unknown module state {}, maybe the port suffers two adjacent insertion/removal".format(module_state)) found += 1 continue From 5f404087881f7ba652223689341402a6975b66f3 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Fri, 18 Feb 2022 07:03:20 +0000 Subject: [PATCH 09/20] Add test cases for SFP event Signed-off-by: Stephen Sun --- .../mlnx-platform-api/tests/test_sfp_event.py | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) 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 ef4820ecfd8f..4781201c0206 100644 --- a/platform/mellanox/mlnx-platform-api/tests/test_sfp_event.py +++ b/platform/mellanox/mlnx-platform-api/tests/test_sfp_event.py @@ -31,7 +31,7 @@ def setup_class(cls): os.environ["MLNX_PLATFORM_API_UNIT_TESTING"] = "1" @patch('select.select', MagicMock(return_value=([99], None, None))) - def test_check_sfp_status(self): + def test_check_sfp_status_xsfp(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 SDK_ERRORS_TO_ERROR_BITS, SDK_ERRORS_TO_DESCRIPTION, SDK_SFP_BLOCKING_ERRORS @@ -59,3 +59,47 @@ def executor(self, mock_module_state, mock_error_type, expect_status, descriptio if description: assert 1 in error_dict and error_dict[1] == description assert 2 in error_dict and error_dict[2] == description + + @patch('select.select', MagicMock(return_value=([99], None, None))) + def test_check_sfp_status_rj45(self): + from sonic_platform.sfp_event import sfp_event + from sonic_platform.sfp_event import SDK_SFP_STATE_IN, SDK_SFP_STATE_OUT, SDK_SFP_STATE_ERR + from sonic_platform.sfp_event import SDK_ERRORS_TO_ERROR_BITS, SDK_ERRORS_TO_DESCRIPTION, SDK_SFP_BLOCKING_ERRORS + from sonic_platform.sfp_event import RJ45_UNPLUG_EVENT, RJ45_UNKNOWN_EVENT + + # Verify absent ports before initialization + event = sfp_event([0, 1, 2, 3]) + event.absent_ports_before_init = [0,1] + port_change = {} + error_dict = {} + found = event.check_sfp_status(port_change, error_dict, 0) + assert found + assert port_change == {1: RJ45_UNPLUG_EVENT, 2: RJ45_UNPLUG_EVENT} + assert error_dict == {1: 'Not present', 2: 'Not present'} + + # Unplug event + event.on_pmpe = MagicMock(return_value=(True, [2], SDK_SFP_STATE_OUT, None)) + port_change.clear() + error_dict.clear() + found = event.check_sfp_status(port_change, error_dict, 0) + assert found + assert port_change == {3: RJ45_UNPLUG_EVENT} + assert error_dict == {3: 'Not present'} + + # Plug event + event.on_pmpe = MagicMock(return_value=(True, [2], SDK_SFP_STATE_IN, None)) + port_change.clear() + error_dict.clear() + found = event.check_sfp_status(port_change, error_dict, 0) + assert found + assert port_change == {3: str(SDK_SFP_STATE_IN)} + assert error_dict == {} + + # Unknown event + event.on_pmpe = MagicMock(return_value=(True, [2], -1, None)) + port_change.clear() + error_dict.clear() + found = event.check_sfp_status(port_change, error_dict, 0) + assert found + assert port_change == {3: RJ45_UNKNOWN_EVENT} + assert error_dict == {3: 'Unknown'} From 43c23b64f415824a5615be3819145f7655f42094 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Fri, 18 Feb 2022 07:19:10 +0000 Subject: [PATCH 10/20] Remove unused local variables Signed-off-by: Stephen Sun --- platform/mellanox/mlnx-platform-api/tests/test_sfp_event.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 4781201c0206..ce3022df2506 100644 --- a/platform/mellanox/mlnx-platform-api/tests/test_sfp_event.py +++ b/platform/mellanox/mlnx-platform-api/tests/test_sfp_event.py @@ -63,8 +63,7 @@ def executor(self, mock_module_state, mock_error_type, expect_status, descriptio @patch('select.select', MagicMock(return_value=([99], None, None))) def test_check_sfp_status_rj45(self): from sonic_platform.sfp_event import sfp_event - from sonic_platform.sfp_event import SDK_SFP_STATE_IN, SDK_SFP_STATE_OUT, SDK_SFP_STATE_ERR - from sonic_platform.sfp_event import SDK_ERRORS_TO_ERROR_BITS, SDK_ERRORS_TO_DESCRIPTION, SDK_SFP_BLOCKING_ERRORS + from sonic_platform.sfp_event import SDK_SFP_STATE_IN, SDK_SFP_STATE_OUT from sonic_platform.sfp_event import RJ45_UNPLUG_EVENT, RJ45_UNKNOWN_EVENT # Verify absent ports before initialization From 4f3e5c5fc33e07c25c576727d3039e3e6d4bac19 Mon Sep 17 00:00:00 2001 From: Kebo Liu Date: Fri, 18 Feb 2022 17:05:34 +0800 Subject: [PATCH 11/20] recover hwsku.json Signed-off-by: Kebo Liu --- device/mellanox/x86_64-nvidia_sn2201-r0/ACS-SN2201/hwsku.json | 1 + 1 file changed, 1 insertion(+) diff --git a/device/mellanox/x86_64-nvidia_sn2201-r0/ACS-SN2201/hwsku.json b/device/mellanox/x86_64-nvidia_sn2201-r0/ACS-SN2201/hwsku.json index 435135cef434..aef116f5e50e 100644 --- a/device/mellanox/x86_64-nvidia_sn2201-r0/ACS-SN2201/hwsku.json +++ b/device/mellanox/x86_64-nvidia_sn2201-r0/ACS-SN2201/hwsku.json @@ -206,3 +206,4 @@ } } } + From 8aa1ddeeca47f74a16135d455a65cf714a254c07 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Mon, 21 Feb 2022 10:56:06 +0000 Subject: [PATCH 12/20] Fix comments Signed-off-by: Stephen Sun --- .../sonic_platform/chassis.py | 5 +- .../sonic_platform/component.py | 4 + .../mlnx-platform-api/sonic_platform/sfp.py | 104 +----------------- .../sonic_platform/sfp_event.py | 11 +- 4 files changed, 21 insertions(+), 103 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py index b0cfd5359deb..6656d2c5a629 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py @@ -263,7 +263,7 @@ def initialize_sfp(self): sfp_count = self.get_num_sfps() for index in range(sfp_count): if self.RJ45_port_list and index in self.RJ45_port_list: - sfp_module = RJ45Port(index) #SFP(index, RJ45_TYPE) + sfp_module = RJ45Port(index) else: sfp_module = SFP(index) self._sfp_list.append(sfp_module) @@ -273,7 +273,7 @@ def initialize_sfp(self): for index in range(len(self._sfp_list)): if self._sfp_list[index] is None: if self.RJ45_port_list and index in self.RJ45_port_list: - self._sfp_list[index] = RJ45Port(index) # SFP(index, RJ45_TYPE) + self._sfp_list[index] = RJ45Port(index) else: self._sfp_list[index] = SFP(index) self.sfp_initialized_count = len(self._sfp_list) @@ -536,6 +536,7 @@ def initialize_components(self): self._component_list.append(ComponentSSD()) # Upgrading BIOS is not supported on SN2201 if DeviceDataManager.get_platform_name() not in ['x86_64-nvidia_sn2201-r0']: + logger.log_notice("Updating BIOS is not supported on SN2201") self._component_list.append(ComponentBIOS()) self._component_list.extend(ComponentCPLD.get_component_list()) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py index bacf2cbefa10..0e619c98cf0d 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py @@ -137,6 +137,10 @@ class ONIEUpdater(object): ONIE_IMAGE_INFO_COMMAND = '/bin/bash {} -q -i' + # Upgrading fireware from ONIE is not supported from the beginning on some platforms, like SN2700. + # There is a logic to check the ONIE version in order to know whether it is supported. + # If it is not supported, we will not proceed and print some error message. + # For SN2201, upgrading fireware from ONIE is supported from day one so we do not need to check it. PLATFORM_ALWAYS_SUPPORT_UPGRADE = ['x86_64-nvidia_sn2201-r0'] BIOS_UPDATE_FILE_EXT = '.rom' diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py index edaf85035eab..7866735721e6 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py @@ -396,6 +396,7 @@ def __init__(self, sfp_index, sfp_type=None, slot_id=0, linecard_port_count=0, l self.sdk_index = sfp_index if self._sfp_type != RJ45_TYPE: + # No thermal sensors for RJ45 ports so we do not need to initiallize them. from .thermal import initialize_sfp_thermal self._thermal_list = initialize_sfp_thermal(sfp_index) else: # For modular chassis @@ -2378,8 +2379,6 @@ def __init__(self, sfp_index): super(RJ45Port, self).__init__(sfp_index, RJ45_TYPE) self._sfp_type = RJ45_TYPE - # Reuse sdk_handle - @property def sfp_type(self): return self._sfp_type @@ -2387,11 +2386,11 @@ def sfp_type(self): def get_presence(self): """ Retrieves the presence of the device + For RJ45 ports, it always return True Returns: bool: True if device is present, False if not """ - # Reimplement it via using SDK API return True @property @@ -2400,7 +2399,8 @@ def dom_supported(self): def get_transceiver_info(self): """ - Retrieves transceiver info of this SFP + Retrieves transceiver info of this port. + For RJ45, all fields are N/A Returns: A dict which contains following keys/values : @@ -2445,102 +2445,6 @@ def get_transceiver_info(self): return transceiver_info_dict - def get_transceiver_bulk_status(self): - """ - Retrieves transceiver bulk status of this SFP - - Returns: - A dict which contains following keys/values : - ======================================================================== - keys |Value Format |Information - ---------------------------|---------------|---------------------------- - RX LOS |BOOLEAN |RX lost-of-signal status, - | |True if has RX los, False if not. - TX FAULT |BOOLEAN |TX fault status, - | |True if has TX fault, False if not. - Reset status |BOOLEAN |reset status, - | |True if SFP in reset, False if not. - LP mode |BOOLEAN |low power mode status, - | |True in lp mode, False if not. - TX disable |BOOLEAN |TX disable status, - | |True TX disabled, False if not. - TX disabled channel |HEX |disabled TX channles in hex, - | |bits 0 to 3 represent channel 0 - | |to channel 3. - Temperature |INT |module temperature in Celsius - Voltage |INT |supply voltage in mV - TX bias |INT |TX Bias Current in mA - RX power |INT |received optical power in mW - TX power |INT |TX output power in mW - ======================================================================== - """ - transceiver_dom_info_dict = {} - dom_info_dict_keys = ['temperature', 'voltage', - 'rx1power', 'rx2power', - 'rx3power', 'rx4power', - 'rx5power', 'rx6power', - 'rx7power', 'rx8power', - 'tx1bias', 'tx2bias', - 'tx3bias', 'tx4bias', - 'tx5bias', 'tx6bias', - 'tx7bias', 'tx8bias', - 'tx1power', 'tx2power', - 'tx3power', 'tx4power', - 'tx5power', 'tx6power', - 'tx7power', 'tx8power' - ] - transceiver_dom_info_dict = dict.fromkeys(dom_info_dict_keys, 'N/A') - - return transceiver_dom_info_dict - - def get_transceiver_threshold_info(self): - """ - Retrieves transceiver threshold info of this SFP - - Returns: - A dict which contains following keys/values : - ======================================================================== - keys |Value Format |Information - ---------------------------|---------------|---------------------------- - temphighalarm |FLOAT |High Alarm Threshold value of temperature in Celsius. - templowalarm |FLOAT |Low Alarm Threshold value of temperature in Celsius. - temphighwarning |FLOAT |High Warning Threshold value of temperature in Celsius. - templowwarning |FLOAT |Low Warning Threshold value of temperature in Celsius. - vcchighalarm |FLOAT |High Alarm Threshold value of supply voltage in mV. - vcclowalarm |FLOAT |Low Alarm Threshold value of supply voltage in mV. - vcchighwarning |FLOAT |High Warning Threshold value of supply voltage in mV. - vcclowwarning |FLOAT |Low Warning Threshold value of supply voltage in mV. - rxpowerhighalarm |FLOAT |High Alarm Threshold value of received power in dBm. - rxpowerlowalarm |FLOAT |Low Alarm Threshold value of received power in dBm. - rxpowerhighwarning |FLOAT |High Warning Threshold value of received power in dBm. - rxpowerlowwarning |FLOAT |Low Warning Threshold value of received power in dBm. - txpowerhighalarm |FLOAT |High Alarm Threshold value of transmit power in dBm. - txpowerlowalarm |FLOAT |Low Alarm Threshold value of transmit power in dBm. - txpowerhighwarning |FLOAT |High Warning Threshold value of transmit power in dBm. - txpowerlowwarning |FLOAT |Low Warning Threshold value of transmit power in dBm. - txbiashighalarm |FLOAT |High Alarm Threshold value of tx Bias Current in mA. - txbiaslowalarm |FLOAT |Low Alarm Threshold value of tx Bias Current in mA. - txbiashighwarning |FLOAT |High Warning Threshold value of tx Bias Current in mA. - txbiaslowwarning |FLOAT |Low Warning Threshold value of tx Bias Current in mA. - ======================================================================== - """ - transceiver_dom_threshold_info_dict = {} - - dom_info_dict_keys = ['temphighalarm', 'temphighwarning', - 'templowalarm', 'templowwarning', - 'vcchighalarm', 'vcchighwarning', - 'vcclowalarm', 'vcclowwarning', - 'rxpowerhighalarm', 'rxpowerhighwarning', - 'rxpowerlowalarm', 'rxpowerlowwarning', - 'txpowerhighalarm', 'txpowerhighwarning', - 'txpowerlowalarm', 'txpowerlowwarning', - 'txbiashighalarm', 'txbiashighwarning', - 'txbiaslowalarm', 'txbiaslowwarning' - ] - transceiver_dom_threshold_info_dict = dict.fromkeys(dom_info_dict_keys, 'N/A') - - return transceiver_dom_threshold_info_dict - def get_reset_status(self): return False 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 59f7b2b93ccb..2d7efa1b070c 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp_event.py @@ -115,7 +115,16 @@ class MockSxFd(object): SDK_SFP_STATE_DIS: str(SFP.SFP_STATUS_BIT_REMOVED), } -# RJ45 ports events definition +""" +RJ45 ports events definition +For RJ45, get_present always returns True. +In case an unplug / unknown event is reported for a port, error status is leveraged for representing them. +The following events will be used. +- Unknown: 2147483648 => 0x80000000 +- Unplug: 1073741824 => 0x40000000 +According to the error status design, the upper half (bits 16 ~ 31) are the events are encoded from bit 16 (0x0001000), +using those numbers will avoid conflict as much as possible +""" RJ45_UNPLUG_EVENT = '1073741824' RJ45_UNKNOWN_EVENT = '2147483648' From 53b63092104663d7547d4cf7a4f82f3d89659fcd Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Wed, 2 Mar 2022 08:08:38 +0000 Subject: [PATCH 13/20] Fix review comments: do a mock i unit test and make extract_RJ45_ports_index_return [] Signed-off-by: Stephen Sun --- .../mellanox/mlnx-platform-api/sonic_platform/chassis.py | 6 +----- platform/mellanox/mlnx-platform-api/tests/test_chassis.py | 2 ++ platform/mellanox/mlnx-platform-api/tests/test_eeprom.py | 6 +----- platform/mellanox/mlnx-platform-api/tests/test_led.py | 1 + platform/mellanox/mlnx-platform-api/tests/test_module.py | 2 ++ platform/mellanox/mlnx-platform-api/tests/test_sfp.py | 1 + platform/mellanox/mlnx-platform-api/tests/test_thermal.py | 4 +++- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py index 6656d2c5a629..aee0ffb39500 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py @@ -111,11 +111,7 @@ def __init__(self): self.reboot_cause_initialized = False # Build the RJ45 port list from platform.json and hwsku.json - try: - if os.environ["PLATFORM_API_UNIT_TESTING"] == "1": - self.RJ45_port_list = None - except KeyError: - self.RJ45_port_list = extract_RJ45_ports_index() + self.RJ45_port_list = extract_RJ45_ports_index() logger.log_info("Chassis loaded successfully") diff --git a/platform/mellanox/mlnx-platform-api/tests/test_chassis.py b/platform/mellanox/mlnx-platform-api/tests/test_chassis.py index cfa2d8224718..dae686f6d3d2 100644 --- a/platform/mellanox/mlnx-platform-api/tests/test_chassis.py +++ b/platform/mellanox/mlnx-platform-api/tests/test_chassis.py @@ -28,9 +28,11 @@ modules_path = os.path.dirname(test_path) sys.path.insert(0, modules_path) +import sonic_platform.chassis from sonic_platform.chassis import Chassis from sonic_platform.device_data import DeviceDataManager +sonic_platform.chassis.extract_RJ45_ports_index = mock.MagicMock(return_value=[]) class TestChassis: """Test class to test chassis.py. The test cases covers: diff --git a/platform/mellanox/mlnx-platform-api/tests/test_eeprom.py b/platform/mellanox/mlnx-platform-api/tests/test_eeprom.py index 2797d62a70f3..5f0a30dbf519 100644 --- a/platform/mellanox/mlnx-platform-api/tests/test_eeprom.py +++ b/platform/mellanox/mlnx-platform-api/tests/test_eeprom.py @@ -30,11 +30,11 @@ from sonic_platform.chassis import Chassis from sonic_platform.eeprom import Eeprom, EepromContentVisitor - class TestEeprom: @patch('os.path.exists', MagicMock(return_value=True)) @patch('os.path.islink', MagicMock(return_value=True)) @patch('sonic_platform.eeprom.Eeprom.get_system_eeprom_info') + @patch('sonic_platform.chassis.extract_RJ45_ports_index', MagicMock(return_value=[])) def test_chassis_eeprom(self, mock_eeprom_info): mock_eeprom_info.return_value = { hex(Eeprom._TLV_CODE_PRODUCT_NAME): 'MSN3420', @@ -102,7 +102,3 @@ def test_eeprom_content_visitor(self): v.visit_tlv('tlv3', Eeprom._TLV_CODE_VENDOR_EXT, 4, 'ext2') assert content[hex(Eeprom._TLV_CODE_PRODUCT_NAME)] == 'MSN3420' assert content[hex(Eeprom._TLV_CODE_VENDOR_EXT)] == ['ext1', 'ext2'] - - - - diff --git a/platform/mellanox/mlnx-platform-api/tests/test_led.py b/platform/mellanox/mlnx-platform-api/tests/test_led.py index 7a9ebaf056a5..1544ae35fb70 100644 --- a/platform/mellanox/mlnx-platform-api/tests/test_led.py +++ b/platform/mellanox/mlnx-platform-api/tests/test_led.py @@ -35,6 +35,7 @@ class TestLed: @mock.patch('sonic_platform.led.Led._wait_files_ready', mock.MagicMock(return_value=True)) + @mock.patch('sonic_platform.chassis.extract_RJ45_ports_index', mock.MagicMock(return_value=True)) def test_chassis_led(self): chassis = Chassis() assert chassis._led is None diff --git a/platform/mellanox/mlnx-platform-api/tests/test_module.py b/platform/mellanox/mlnx-platform-api/tests/test_module.py index 8213aa5a986a..4cba90ac95f4 100644 --- a/platform/mellanox/mlnx-platform-api/tests/test_module.py +++ b/platform/mellanox/mlnx-platform-api/tests/test_module.py @@ -26,6 +26,7 @@ modules_path = os.path.dirname(test_path) sys.path.insert(0, modules_path) +import sonic_platform.chassis from sonic_platform import utils from sonic_platform.chassis import ModularChassis from sonic_platform.device_data import DeviceDataManager @@ -37,6 +38,7 @@ class TestModule: def setup_class(cls): DeviceDataManager.get_linecard_sfp_count = mock.MagicMock(return_value=2) DeviceDataManager.get_linecard_count = mock.MagicMock(return_value=2) + sonic_platform.chassis.extract_RJ45_ports_index = mock.MagicMock(return_value=[]) def test_chassis_get_num_sfp(self): chassis = ModularChassis() diff --git a/platform/mellanox/mlnx-platform-api/tests/test_sfp.py b/platform/mellanox/mlnx-platform-api/tests/test_sfp.py index 0ad9537430b9..8e5444d38ad0 100644 --- a/platform/mellanox/mlnx-platform-api/tests/test_sfp.py +++ b/platform/mellanox/mlnx-platform-api/tests/test_sfp.py @@ -52,6 +52,7 @@ def test_sfp_index(self, mock_max_port): @mock.patch('sonic_platform.sfp.SFP._read_eeprom_specific_bytes', mock.MagicMock(return_value=None)) @mock.patch('sonic_platform.sfp.SFP._get_error_code') @mock.patch('sonic_platform.chassis.Chassis.get_num_sfps', mock.MagicMock(return_value=2)) + @mock.patch('sonic_platform.chassis.extract_RJ45_ports_index', mock.MagicMock(return_value=[])) def test_sfp_get_error_status(self, mock_get_error_code): chassis = Chassis() diff --git a/platform/mellanox/mlnx-platform-api/tests/test_thermal.py b/platform/mellanox/mlnx-platform-api/tests/test_thermal.py index 89c940d89260..48bebf3d5337 100644 --- a/platform/mellanox/mlnx-platform-api/tests/test_thermal.py +++ b/platform/mellanox/mlnx-platform-api/tests/test_thermal.py @@ -28,9 +28,11 @@ modules_path = os.path.dirname(test_path) sys.path.insert(0, modules_path) +import sonic_platform.chassis from sonic_platform.chassis import Chassis from sonic_platform.device_data import DeviceDataManager +sonic_platform.chassis.extract_RJ45_ports_index = mock.MagicMock(return_value=[]) class TestThermal: @mock.patch('os.path.exists', mock.MagicMock(return_value=True)) @@ -334,4 +336,4 @@ def test_get_cooling_level(self, mock_read_file): mock_read_file.side_effect = ValueError('') with pytest.raises(RuntimeError): - Thermal.get_cooling_level() \ No newline at end of file + Thermal.get_cooling_level() From 3f8c6943babf42b75d708fa7fde3026358febf31 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Wed, 2 Mar 2022 11:06:22 +0000 Subject: [PATCH 14/20] Fix log issue Signed-off-by: Stephen Sun --- platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py index aee0ffb39500..e1b62b410fbf 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py @@ -532,8 +532,9 @@ def initialize_components(self): self._component_list.append(ComponentSSD()) # Upgrading BIOS is not supported on SN2201 if DeviceDataManager.get_platform_name() not in ['x86_64-nvidia_sn2201-r0']: - logger.log_notice("Updating BIOS is not supported on SN2201") self._component_list.append(ComponentBIOS()) + else: + logger.log_notice("Updating BIOS is not supported on SN2201") self._component_list.extend(ComponentCPLD.get_component_list()) def get_num_components(self): From 57567645b4921af92698e07543333a72ba80bd59 Mon Sep 17 00:00:00 2001 From: Kebo Liu Date: Sun, 6 Mar 2022 15:18:31 +0800 Subject: [PATCH 15/20] simplify json file parsing code logic, remove redundant spaces from ssd fw update scripts Signed-off-by: Kebo Liu --- .../mlnx-platform-api/sonic_platform/utils.py | 13 +++++++------ platform/mellanox/mlnx-ssd-fw-update.sh | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/utils.py b/platform/mellanox/mlnx-platform-api/sonic_platform/utils.py index 1e90f4f96846..4434771cc116 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/utils.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/utils.py @@ -16,7 +16,6 @@ # import functools import subprocess -import ast import json import sys import os @@ -207,18 +206,20 @@ def _impl(*args, **kwargs): return wrapper -def load_json_file(filename): +def load_json_file(filename, log_func=logger.log_error): # load 'platform.json' or 'hwsku.json' file + data = None try: with open(filename) as fp: try: data = json.load(fp) except json.JSONDecodeError: - print("Json file does not exist") - data_dict = ast.literal_eval(json.dumps(data)) - return data_dict + if log_func: + log_func("failed to decode Json file.") + return data except Exception as e: - print("error occurred while parsing json: {}".format(sys.exc_info()[1])) + if log_func: + log_func("error occurred while parsing json file: {}".format(sys.exc_info()[1])) return None diff --git a/platform/mellanox/mlnx-ssd-fw-update.sh b/platform/mellanox/mlnx-ssd-fw-update.sh index 4e3b95db9c65..fd0d4b66973a 100755 --- a/platform/mellanox/mlnx-ssd-fw-update.sh +++ b/platform/mellanox/mlnx-ssd-fw-update.sh @@ -594,7 +594,7 @@ function print_ssd_info() { LOG_MSG "Device Model\t : $SSD_DEVICE_MODEL" LOG_MSG "Serial Number\t : $SSD_SERIAL" LOG_MSG "User Capacity\t : $SSD_SIZE GB" - LOG_MSG "Firmware Version : $SSD_FW_VER" + LOG_MSG "Firmware Version : $SSD_FW_VER" fi } From 4107b99e4dcbea49c556df6aeb6b41ac1cf6407b Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Mon, 7 Mar 2022 10:10:58 +0800 Subject: [PATCH 16/20] Support BIOS version Signed-off-by: Stephen Sun --- .../sonic_platform/chassis.py | 4 +-- .../sonic_platform/component.py | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py index e1b62b410fbf..1866a4359d37 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py @@ -527,14 +527,14 @@ def initialize_components(self): return if not self._component_list: # Initialize component list - from .component import ComponentONIE, ComponentSSD, ComponentBIOS, ComponentCPLD + from .component import ComponentONIE, ComponentSSD, ComponentBIOS, ComponentCPLD, ComponentBIOSSN2201 self._component_list.append(ComponentONIE()) self._component_list.append(ComponentSSD()) # Upgrading BIOS is not supported on SN2201 if DeviceDataManager.get_platform_name() not in ['x86_64-nvidia_sn2201-r0']: self._component_list.append(ComponentBIOS()) else: - logger.log_notice("Updating BIOS is not supported on SN2201") + self._component_list.append(ComponentBIOSSN2201()) self._component_list.extend(ComponentCPLD.get_component_list()) def get_num_components(self): diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py index 0e619c98cf0d..4009762d0410 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py @@ -712,6 +712,37 @@ def update_firmware(self, image_path): self.__install_firmware(image_path) +class ComponentBIOSSN2201(Component): + COMPONENT_NAME = 'BIOS' + COMPONENT_DESCRIPTION = 'BIOS - Basic Input/Output System' + + BIOS_VERSION_COMMAND = 'dmidecode -t0' + + def __init__(self): + super(ComponentBIOSSN2201, self).__init__() + + self.name = self.COMPONENT_NAME + self.description = self.COMPONENT_DESCRIPTION + + def get_firmware_version(self): + cmd = self.BIOS_VERSION_COMMAND + + try: + output = subprocess.check_output(cmd.split(), + stderr=subprocess.STDOUT, + universal_newlines=True).rstrip('\n') + except subprocess.CalledProcessError as e: + raise RuntimeError("Failed to get {} version: {}".format(self.name, str(e))) + + match = re.search('Version: (.*)', output) + if match: + version = match.group(1) + else: + version = 'Unknown version' + + return version + + class ComponentCPLD(Component): COMPONENT_NAME = 'CPLD{}' COMPONENT_DESCRIPTION = 'CPLD - Complex Programmable Logic Device' From 0d4f4bdce9846f5825657afeeb5dfdb170776d99 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Tue, 8 Mar 2022 09:43:45 +0000 Subject: [PATCH 17/20] Update platform_component.json Signed-off-by: Stephen Sun --- device/mellanox/x86_64-nvidia_sn2201-r0/platform_components.json | 1 + 1 file changed, 1 insertion(+) diff --git a/device/mellanox/x86_64-nvidia_sn2201-r0/platform_components.json b/device/mellanox/x86_64-nvidia_sn2201-r0/platform_components.json index e28c19087875..88d4851f7176 100644 --- a/device/mellanox/x86_64-nvidia_sn2201-r0/platform_components.json +++ b/device/mellanox/x86_64-nvidia_sn2201-r0/platform_components.json @@ -4,6 +4,7 @@ "component": { "ONIE": { }, "SSD": { }, + "BIOS": { }, "CPLD1": { }, "CPLD2": { } } From 91f2cd7024057ce0b8277c4c95008cbb10f32aa8 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Tue, 8 Mar 2022 10:03:34 +0000 Subject: [PATCH 18/20] Fix sfp review comments Signed-off-by: Stephen Sun --- .../mlnx-platform-api/sonic_platform/sfp.py | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py index 7866735721e6..5c43ae3d9e7b 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py @@ -2424,24 +2424,23 @@ def get_transceiver_info(self): application_advertisement |1*255VCHAR |supported applications advertisement ================================================================================ """ - transceiver_info_dict = {} - + transceiver_info_keys = ['manufacturer', + 'model', + 'vendor_rev', + 'serial', + 'vendor_oui', + 'vendor_date', + 'connector', + 'encoding', + 'ext_identifier', + 'ext_rateselect_compliance', + 'cable_type', + 'cable_length', + 'specification_compliance', + 'nominal_bit_rate', + 'application_advertisement'] + transceiver_info_dict = dict.fromkeys(transceiver_info_keys, 'N/A') transceiver_info_dict['type'] = self.sfp_type - transceiver_info_dict['manufacturer'] = 'N/A' - transceiver_info_dict['model'] = 'N/A' - transceiver_info_dict['vendor_rev'] = 'N/A' - transceiver_info_dict['serial'] = 'N/A' - transceiver_info_dict['vendor_oui'] = 'N/A' - transceiver_info_dict['vendor_date'] = 'N/A' - transceiver_info_dict['connector'] = 'N/A' - transceiver_info_dict['encoding'] = 'N/A' - transceiver_info_dict['ext_identifier'] = 'N/A' - transceiver_info_dict['ext_rateselect_compliance'] = 'N/A' - transceiver_info_dict['cable_type'] = 'N/A' - transceiver_info_dict['cable_length'] = 'N/A' - transceiver_info_dict['specification_compliance'] = 'N/A' - transceiver_info_dict['nominal_bit_rate'] = 'N/A' - transceiver_info_dict['application_advertisement'] = 'N/A' return transceiver_info_dict From 079ca89e7376b15fb7d9c928ee143ff6cc996bd8 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Wed, 9 Mar 2022 07:28:03 +0000 Subject: [PATCH 19/20] Fix comments in ComponentBIOS Signed-off-by: Stephen Sun --- .../mlnx-platform-api/sonic_platform/chassis.py | 11 +++++------ .../mlnx-platform-api/sonic_platform/device_data.py | 9 +++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py index 1866a4359d37..d5e5bf8d7166 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py @@ -527,14 +527,13 @@ def initialize_components(self): return if not self._component_list: # Initialize component list - from .component import ComponentONIE, ComponentSSD, ComponentBIOS, ComponentCPLD, ComponentBIOSSN2201 + from .component import ComponentONIE, ComponentSSD, ComponentBIOS, ComponentCPLD self._component_list.append(ComponentONIE()) self._component_list.append(ComponentSSD()) - # Upgrading BIOS is not supported on SN2201 - if DeviceDataManager.get_platform_name() not in ['x86_64-nvidia_sn2201-r0']: - self._component_list.append(ComponentBIOS()) - else: - self._component_list.append(ComponentBIOSSN2201()) + biosComponent = DeviceDataManager.get_bios_component() + if not biosComponent: + biosComponent = ComponentBIOS() + self._component_list.append(biosComponent) self._component_list.extend(ComponentCPLD.get_component_list()) def get_num_components(self): diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/device_data.py b/platform/mellanox/mlnx-platform-api/sonic_platform/device_data.py index b4610fe045f0..a029f08fb8bf 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/device_data.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/device_data.py @@ -263,3 +263,12 @@ def get_linecard_max_port_count(cls): if not sfp_data: return 0 return sfp_data.get('max_port_per_line_card', 0) + + @classmethod + def get_bios_component(cls): + if cls.get_platform_name() in ['x86_64-nvidia_sn2201-r0']: + from .component import ComponentBIOSSN2201 + # For SN2201, special chass is required for handle BIOS + # Currently, only fetching BIOS version is supported + return ComponentBIOSSN2201() + return None From 723226429bda97a8aec6034ca6ccf51680f8e7c1 Mon Sep 17 00:00:00 2001 From: Kebo Liu Date: Mon, 21 Mar 2022 16:24:13 +0800 Subject: [PATCH 20/20] remove unnecessary line --- device/mellanox/x86_64-nvidia_sn2201-r0/ACS-SN2201/hwsku.json | 1 - 1 file changed, 1 deletion(-) diff --git a/device/mellanox/x86_64-nvidia_sn2201-r0/ACS-SN2201/hwsku.json b/device/mellanox/x86_64-nvidia_sn2201-r0/ACS-SN2201/hwsku.json index aef116f5e50e..435135cef434 100644 --- a/device/mellanox/x86_64-nvidia_sn2201-r0/ACS-SN2201/hwsku.json +++ b/device/mellanox/x86_64-nvidia_sn2201-r0/ACS-SN2201/hwsku.json @@ -206,4 +206,3 @@ } } } -