[Mellanox] Enhance Platform API to support SN2201 - RJ45 ports and new components mgmt. (#10377)

* Support new platform SN2201 and RJ45 port

Signed-off-by: Kebo Liu <kebol@nvidia.com>

* remove unused import and redundant function

Signed-off-by: Kebo Liu <kebol@nvidia.com>

* fix error introduced by rebase

Signed-off-by: Kebo Liu <kebol@nvidia.com>

* Revert the special handling of RJ45 ports (#56)

* Revert the special handling of RJ45 ports

sfp.py
sfp_event.py
chassis.py

Signed-off-by: Stephen Sun <stephens@nvidia.com>

* Remove deadcode

Signed-off-by: Stephen Sun <stephens@nvidia.com>

* Support CPLD update for SN2201

A new class is introduced, deriving from ComponentCPLD and overloading _install_firmware
Change _install_firmware from private (starting with __) to protected, making it overloadable

Signed-off-by: Stephen Sun <stephens@nvidia.com>

* Initialize component BIOS/CPLD

Signed-off-by: Stephen Sun <stephens@nvidia.com>

* Remove swb_amb which doesn't on DVT board any more

Signed-off-by: Stephen Sun <stephens@nvidia.com>

* Remove the unexisted sensor - switch board ambient - from platform.json

Signed-off-by: Stephen Sun <stephens@nvidia.com>

* Do not report error on receiving unknown status on RJ45 ports

Translate it to disconnect for RJ45 ports
Report error for xSFP ports

Signed-off-by: Stephen Sun <stephens@nvidia.com>

* Add reinit for RJ45 to avoid exception

Signed-off-by: Stephen Sun <stephens@nvidia.com>

Co-authored-by: Stephen Sun <5379172+stephenxs@users.noreply.github.com>
Co-authored-by: Stephen Sun <stephens@nvidia.com>
This commit is contained in:
Kebo Liu 2022-06-21 10:12:20 +08:00 committed by Ying Xie
parent 71f47ed15b
commit 2f59460fc4
15 changed files with 569 additions and 65 deletions

View File

@ -90,9 +90,6 @@
{
"name": "Ambient CPU Board Temp"
},
{
"name": "Ambient Switch Board Temp"
},
{
"name": "CPU Pack Temp"
},

View File

@ -4,6 +4,7 @@
"component": {
"ONIE": { },
"SSD": { },
"BIOS": { },
"CPLD1": { },
"CPLD2": { }
}

View File

@ -27,14 +27,17 @@ try:
from sonic_py_common.logger import Logger
import os
from functools import reduce
from .utils import extract_RJ45_ports_index
from . import utils
from .device_data import DeviceDataManager
from .sfp import SFP, RJ45Port, deinitialize_sdk_handle
except ImportError as e:
raise ImportError (str(e) + "- required module not found")
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"
@ -106,6 +109,10 @@ class Chassis(ChassisBase):
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
self.RJ45_port_list = extract_RJ45_ports_index()
logger.log_info("Chassis loaded successfully")
def __del__(self):
@ -242,7 +249,10 @@ class Chassis(ChassisBase):
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] = RJ45Port(index)
else:
self._sfp_list[index] = SFP(index)
self.sfp_initialized_count += 1
def initialize_sfp(self):
@ -250,14 +260,20 @@ class Chassis(ChassisBase):
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 = RJ45Port(index)
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] = RJ45Port(index)
else:
self._sfp_list[index] = SFP(index)
self.sfp_initialized_count = len(self._sfp_list)
def get_num_sfps(self):
@ -324,7 +340,7 @@ class Chassis(ChassisBase):
# 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)
@ -340,7 +356,8 @@ class Chassis(ChassisBase):
status = self.sfp_event.check_sfp_status(port_dict, error_dict, timeout)
if status:
self.reinit_sfps(port_dict)
if port_dict:
self.reinit_sfps(port_dict)
result_dict = {'sfp':port_dict}
if error_dict:
result_dict['sfp_error'] = error_dict
@ -515,8 +532,8 @@ class Chassis(ChassisBase):
from .component import ComponentONIE, ComponentSSD, ComponentBIOS, ComponentCPLD
self._component_list.append(ComponentONIE())
self._component_list.append(ComponentSSD())
self._component_list.append(ComponentBIOS())
self._component_list.extend(ComponentCPLD.get_component_list())
self._component_list.append(DeviceDataManager.get_bios_component())
self._component_list.extend(DeviceDataManager.get_cpld_component_list())
def get_num_components(self):
"""

View File

@ -29,6 +29,7 @@ try:
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,17 @@ 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'
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 +347,9 @@ class ONIEUpdater(object):
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))
@ -698,6 +712,37 @@ class ComponentBIOS(Component):
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'
@ -744,7 +789,7 @@ class ComponentCPLD(Component):
return mst_dev_list[0]
def __install_firmware(self, image_path):
def _install_firmware(self, image_path):
if not self._check_file_validity(image_path):
return False
@ -830,9 +875,9 @@ class ComponentCPLD(Component):
burn_firmware = mpfa.get_metadata().get('firmware', 'burn')
print("INFO: Processing {} burn file: firmware install".format(self.name))
return self.__install_firmware(os.path.join(mpfa.get_path(), burn_firmware))
return self._install_firmware(os.path.join(mpfa.get_path(), burn_firmware))
else:
return self.__install_firmware(image_path)
return self._install_firmware(image_path)
def update_firmware(self, image_path):
with MPFAManager(image_path) as mpfa:
@ -845,11 +890,11 @@ class ComponentCPLD(Component):
refresh_firmware = mpfa.get_metadata().get('firmware', 'refresh')
print("INFO: Processing {} burn file: firmware install".format(self.name))
if not self.__install_firmware(os.path.join(mpfa.get_path(), burn_firmware)):
if not self._install_firmware(os.path.join(mpfa.get_path(), burn_firmware)):
return
print("INFO: Processing {} refresh file: firmware update".format(self.name))
self.__install_firmware(os.path.join(mpfa.get_path(), refresh_firmware))
self._install_firmware(os.path.join(mpfa.get_path(), refresh_firmware))
@classmethod
def get_component_list(cls):
@ -862,3 +907,19 @@ class ComponentCPLD(Component):
component_list.append(cls(cpld_idx))
return component_list
class ComponentCPLDSN2201(ComponentCPLD):
CPLD_FIRMWARE_UPDATE_COMMAND = 'cpldupdate --gpio {} --uncustomized --print-progress'
def _install_firmware(self, image_path):
cmd = self.CPLD_FIRMWARE_UPDATE_COMMAND.format(image_path)
try:
print("INFO: Installing {} firmware update: path={}".format(self.name, image_path))
subprocess.check_call(cmd.split(), universal_newlines=True)
except subprocess.CalledProcessError as e:
print("ERROR: Failed to update {} firmware: {}".format(self.name, str(e)))
return False
return True

View File

@ -158,8 +158,7 @@ DEVICE_DATA = {
'thermal': {
"capability": {
"comex_amb": False,
"cpu_amb": True,
"swb_amb": True
"cpu_amb": True
}
}
},
@ -281,3 +280,21 @@ class DeviceDataManager:
return None, None
return thermal_data.get('cpu_threshold', (None, None))
@classmethod
def get_bios_component(cls):
from .component import ComponentBIOS, ComponentBIOSSN2201
if cls.get_platform_name() in ['x86_64-nvidia_sn2201-r0']:
# For SN2201, special chass is required for handle BIOS
# Currently, only fetching BIOS version is supported
return ComponentBIOSSN2201()
return ComponentBIOS()
@classmethod
def get_cpld_component_list(cls):
from .component import ComponentCPLD, ComponentCPLDSN2201
if cls.get_platform_name() in ['x86_64-nvidia_sn2201-r0']:
# For SN2201, special chass is required for handle BIOS
# Currently, only fetching BIOS version is supported
return ComponentCPLDSN2201.get_component_list()
return ComponentCPLD.get_component_list()

View File

@ -80,6 +80,8 @@ QSFP_DD_TYPE_CODE_LIST = [
'18' # QSFP-DD Double Density 8X Pluggable Transceiver
]
RJ45_TYPE = "RJ45"
#variables for sdk
REGISTER_NUM = 1
DEVICE_ID = 1
@ -235,7 +237,44 @@ class SdkHandleContext(object):
deinitialize_sdk_handle(self.sdk_handle)
class SFP(SfpOptoeBase):
class NvidiaSFPCommon(SfpOptoeBase):
def __init__(self, sfp_index):
super(NvidiaSFPCommon, self).__init__()
self.index = sfp_index + 1
self.sdk_index = sfp_index
@property
def sdk_handle(self):
if not SFP.shared_sdk_handle:
SFP.shared_sdk_handle = initialize_sdk_handle()
if not SFP.shared_sdk_handle:
logger.log_error('Failed to open SDK handle')
return SFP.shared_sdk_handle
@classmethod
def _get_module_info(self, sdk_handle, sdk_index):
"""
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 = 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(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
class SFP(NvidiaSFPCommon):
"""Platform-specific SFP class"""
shared_sdk_handle = None
SFP_MLNX_ERROR_DESCRIPTION_LONGRANGE_NON_MLNX_CABLE = 'Long range for non-Mellanox cable or module'
@ -250,13 +289,11 @@ class SFP(SfpOptoeBase):
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):
super(SFP, self).__init__()
def __init__(self, sfp_index, sfp_type=None, slot_id=0, linecard_port_count=0, lc_name=None):
super(SFP, self).__init__(sfp_index)
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)
else: # For modular chassis
@ -281,6 +318,7 @@ class SFP(SfpOptoeBase):
logger.log_error("Failed to find mst PCI device rc={} err.msg={}".format(e.returncode, e.output))
return device_name
'''
@property
def sdk_handle(self):
if not SFP.shared_sdk_handle:
@ -288,6 +326,7 @@ class SFP(SfpOptoeBase):
if not SFP.shared_sdk_handle:
logger.log_error('Failed to open SDK handle')
return SFP.shared_sdk_handle
'''
def reinit(self):
"""
@ -512,7 +551,7 @@ class SFP(SfpOptoeBase):
@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()
@ -520,12 +559,19 @@ class SFP(SfpOptoeBase):
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(sdk_handle, log_port);
admin_state == SX_PORT_ADMIN_STATUS_UP
@classmethod
@ -663,27 +709,6 @@ class SFP(SfpOptoeBase):
"""
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,
@ -704,12 +729,12 @@ class SFP(SfpOptoeBase):
Get error description
Args:
error_code: The error code returned by _get_error_code
error_code: The error code returned by _get_module_info
Returns:
The error description
"""
oper_status, error_code = self._get_error_code()
oper_status, error_code = self._get_module_info(self.sdk_handle, self.sdk_index)
if oper_status == SX_PORT_MODULE_STATUS_INITIALIZING:
error_description = self.SFP_STATUS_INITIALIZING
elif oper_status == SX_PORT_MODULE_STATUS_PLUGGED:
@ -727,3 +752,271 @@ class SFP(SfpOptoeBase):
else:
error_description = "Unknow SFP module status ({})".format(oper_status)
return error_description
class RJ45Port(NvidiaSFPCommon):
"""class derived from SFP, representing RJ45 ports"""
def __init__(self, sfp_index):
super(RJ45Port, self).__init__(sfp_index)
self.sfp_type = RJ45_TYPE
@classmethod
def _get_presence(cls, sdk_handle, sdk_index):
"""Class level method to get low power mode.
Args:
sdk_handle: SDK handle
sdk_index (integer): SDK port index
slot_id (integer): Slot ID
Returns:
[boolean]: True if low power mode is on else off
"""
oper_status, _ = cls._get_module_info(sdk_handle, sdk_index)
return print(oper_status == SX_PORT_MODULE_STATUS_PLUGGED)
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
"""
if utils.is_host():
# To avoid performance issue,
# call class level method to avoid initialize the whole sonic platform API
get_presence_code = 'from sonic_platform import sfp;\n' \
'with sfp.SdkHandleContext() as sdk_handle:' \
'print(sfp.RJ45Port._get_presence(sdk_handle, {}))'.format(self.sdk_index)
presence_cmd = "docker exec pmon python3 -c \"{}\"".format(get_presence_code)
try:
output = subprocess.check_output(presence_cmd, shell=True, universal_newlines=True)
return 'True' in output
except subprocess.CalledProcessError as e:
print("Error! Unable to get presence for {}, rc = {}, err msg: {}".format(self.sdk_index, e.returncode, e.output))
return False
else:
oper_status, _ = self._get_module_info(self.sdk_handle, self.sdk_index);
return (oper_status == SX_PORT_MODULE_STATUS_PLUGGED)
def get_transceiver_info(self):
"""
Retrieves transceiver info of this port.
For RJ45, all fields are N/A
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_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
return transceiver_info_dict
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: Always false on SN2201
Returns:
The error description
"""
return False
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):
"""
Retrieves the reset status of SFP
Returns:
A Boolean, True if reset enabled, False if disabled
for QSFP, originally I would like to make use of Initialization complete flag bit
which is at Page a0 offset 6 bit 0 to test whether reset is complete.
However as unit testing was carried out I find this approach may fail because:
1. we make use of ethtool to read data on I2C bus rather than to read directly
2. ethtool is unable to access I2C during QSFP module being reset
In other words, whenever the flag is able to be retrived, the value is always be 1
As a result, it doesn't make sense to retrieve that flag. Just treat successfully
retrieving data as "data ready".
for SFP it seems that there is not flag indicating whether reset succeed. However,
we can also do it in the way for QSFP.
"""
return False
def read_eeprom(self, offset, num_bytes):
return None
def reinit(self):
"""
Nothing to do for RJ45. Just provide it to avoid exception
:return:
"""
return

View File

@ -50,6 +50,7 @@ SDK_SFP_STATE_IN = 0x1
SDK_SFP_STATE_OUT = 0x2
SDK_SFP_STATE_ERR = 0x3
SDK_SFP_STATE_DIS = 0x4
SDK_SFP_STATE_UNKNOWN = 0x5
# 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
@ -134,13 +135,17 @@ 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
# Allocate SDK fd and user channel structures
self.rx_fd_p = new_sx_fd_t_p()
self.user_channel_p = new_sx_user_channel_t_p()
if rj45_port_list:
self.RJ45_port_set = set(rj45_port_list)
else:
self.RJ45_port_set = set()
def initialize(self):
swid_cnt_p = None
@ -340,6 +345,7 @@ class sfp_event:
status = False
else:
status = True
unknown = False
pmpe_t = recv_info_p.event_info.pmpe
port_list_size = pmpe_t.list_size
logical_port_list = pmpe_t.log_port_list
@ -354,8 +360,11 @@ class sfp_event:
logger.log_info("Receive PMPE disable event on module {}: status {}".format(module_id, module_state))
elif module_state == SDK_SFP_STATE_IN or module_state == SDK_SFP_STATE_OUT:
logger.log_info("Receive PMPE plug in/out event on module {}: status {}".format(module_id, module_state))
elif module_state == SDK_SFP_STATE_UNKNOWN:
unknown = True
else:
logger.log_error("Receive PMPE unknown event on module {}: status {}".format(module_id, module_state))
for i in range(port_list_size):
logical_port = sx_port_log_id_t_arr_getitem(logical_port_list, i)
rc = sx_api_port_device_get(self.handle, 1 , 0, port_attributes_list, port_cnt_p)
@ -369,6 +378,14 @@ class sfp_event:
if label_port is not None:
label_port_list.append(label_port)
if unknown:
SFP_ports_with_unknown_event = set(label_port_list) - self.RJ45_port_set
if SFP_ports_with_unknown_event:
logger.log_error("Receive PMPE unknown event on module {}: status {}".format(module_id, module_state))
else:
# For RJ45 ports, we treat unknown as disconnect
module_state = SDK_SFP_STATE_DIS
delete_uint32_t_p(pkt_size_p)
delete_uint8_t_arr(pkt)
delete_sx_receive_info_t_p(recv_info_p)

View File

@ -16,8 +16,18 @@
#
import functools
import subprocess
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()
@ -206,4 +216,51 @@ def run_command(command):
process = subprocess.Popen(command, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
return process.communicate()[0].strip()
except Exception:
return None
return None
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:
if log_func:
log_func("failed to decode Json file.")
return data
except Exception as e:
if log_func:
log_func("error occurred while parsing json file: {}".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

View File

@ -32,6 +32,7 @@ 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:

View File

@ -30,11 +30,11 @@ sys.path.insert(0, modules_path)
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 @@ class TestEeprom:
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']

View File

@ -35,6 +35,7 @@ from sonic_platform.psu import FixedPsu, Psu
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

View File

@ -26,6 +26,7 @@ test_path = os.path.dirname(os.path.abspath(__file__))
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()

View File

@ -53,8 +53,10 @@ class TestSfp:
assert sfp.index == 5
@mock.patch('sonic_platform.sfp.SFP.read_eeprom', mock.MagicMock(return_value=None))
@mock.patch('sonic_platform.sfp.SFP._get_error_code')
@mock.patch('sonic_platform.sfp.SFP.shared_sdk_handle', mock.MagicMock(return_value=2))
@mock.patch('sonic_platform.sfp.SFP._get_module_info')
@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()

View File

@ -28,9 +28,11 @@ test_path = os.path.dirname(os.path.abspath(__file__))
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 @@ class TestThermal:
mock_read_file.side_effect = ValueError('')
with pytest.raises(RuntimeError):
Thermal.get_cooling_level()
Thermal.get_cooling_level()

View File

@ -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