Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 37 additions & 5 deletions platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from sonic_py_common import device_info
from functools import reduce
from .utils import extract_RJ45_ports_index
from .utils import extract_cpo_ports_index
from . import module_host_mgmt_initializer
from . import utils
from .device_data import DeviceDataManager
Expand Down Expand Up @@ -120,6 +121,10 @@ def __init__(self):
self._RJ45_port_inited = False
self._RJ45_port_list = None

# Build the CPO port list from platform.json and hwsku.json
self._cpo_port_inited = False
self._cpo_port_list = None

Chassis.chassis_instance = self

self.module_host_mgmt_initializer = module_host_mgmt_initializer.ModuleHostMgmtInitializer()
Expand All @@ -139,6 +144,13 @@ def RJ45_port_list(self):
self._RJ45_port_inited = True
return self._RJ45_port_list

@property
def cpo_port_list(self):
if not self._cpo_port_inited:
self._cpo_port_list = extract_cpo_ports_index()
self._cpo_port_inited = True
return self._cpo_port_list

##############################################
# PSU methods
##############################################
Expand All @@ -147,6 +159,9 @@ def initialize_psu(self):
if not self._psu_list:
from .psu import Psu, FixedPsu
psu_count = DeviceDataManager.get_psu_count()
if psu_count == 0:
# For system with no PSU, for example, PDU system.
return
hot_swapable = DeviceDataManager.is_psu_hotswapable()

# Initialize PSU list
Expand Down Expand Up @@ -202,9 +217,11 @@ def initialize_fan(self):
if not self._fan_drawer_list:
from .fan import Fan
from .fan_drawer import RealDrawer, VirtualDrawer

hot_swapable = DeviceDataManager.is_fan_hotswapable()
drawer_num = DeviceDataManager.get_fan_drawer_count()
if drawer_num == 0:
# For system with no fan, for example, liquid cooling system.
return
hot_swapable = DeviceDataManager.is_fan_hotswapable()
fan_num = DeviceDataManager.get_fan_count()
fan_num_per_drawer = fan_num // drawer_num
drawer_ctor = RealDrawer if hot_swapable else VirtualDrawer
Expand Down Expand Up @@ -277,6 +294,8 @@ def initialize_single_sfp(self, index):
sfp_module = self._import_sfp_module()
if self.RJ45_port_list and index in self.RJ45_port_list:
self._sfp_list[index] = sfp_module.RJ45Port(index)
elif self.cpo_port_list and index in self.cpo_port_list:
self._sfp_list[index] = sfp_module.CpoPort(index)
else:
self._sfp_list[index] = sfp_module.SFP(index)
self.sfp_initialized_count += 1
Expand All @@ -294,6 +313,8 @@ def initialize_sfp(self):
for index in range(sfp_count):
if self.RJ45_port_list and index in self.RJ45_port_list:
sfp_object = sfp_module.RJ45Port(index)
elif self.cpo_port_list and index in self.cpo_port_list:
sfp_object = sfp_module.CpoPort(index)
else:
sfp_object = sfp_module.SFP(index)
self._sfp_list.append(sfp_object)
Expand All @@ -304,6 +325,8 @@ def initialize_sfp(self):
if self._sfp_list[index] is None:
if self.RJ45_port_list and index in self.RJ45_port_list:
self._sfp_list[index] = sfp_module.RJ45Port(index)
elif self.cpo_port_list and index in self.cpo_port_list:
self._sfp_list[index] = sfp_module.CpoPort(index)
else:
self._sfp_list[index] = sfp_module.SFP(index)
self.sfp_initialized_count = len(self._sfp_list)
Expand All @@ -315,13 +338,22 @@ def get_num_sfps(self):
Returns:
An integer, the number of sfps available on this chassis
"""
num_sfps = 0
if not self._RJ45_port_inited:
self._RJ45_port_list = extract_RJ45_ports_index()
self._RJ45_port_inited = True

if not self._cpo_port_inited:
self._cpo_port_list = extract_cpo_ports_index()
self._cpo_port_inited = True

num_sfps = DeviceDataManager.get_sfp_count()
if self._RJ45_port_list is not None:
return DeviceDataManager.get_sfp_count() + len(self._RJ45_port_list)
else:
return DeviceDataManager.get_sfp_count()
num_sfps += len(self._RJ45_port_list)
if self._cpo_port_list is not None:
num_sfps += len(self._cpo_port_list)

return num_sfps

def get_all_sfps(self):
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,22 @@ def get_simx_version(cls):
version = check_output_pipe(["lspci", "-vv"], ["grep", "SimX"])
parsed_version = re.search("([0-9]+\\.[0-9]+-[0-9]+)", version)
return parsed_version.group(1) if parsed_version else "N/A"

@classmethod
@utils.read_only_cache()
def get_fan_drawer_sysfs_count(cls):
return len(glob.glob('/run/hw-management/thermal/fan*_status'))

@classmethod
@utils.read_only_cache()
def get_fan_drawer_count(cls):
# Here we don't read from /run/hw-management/config/hotplug_fans because the value in it is not
# always correct.
return len(glob.glob('/run/hw-management/thermal/fan*_status')) if cls.is_fan_hotswapable() else 1
fan_status_count = cls.get_fan_drawer_sysfs_count()
if fan_status_count == 0:
# For system with no fan, for example, liquid cooling system.
return 0
return fan_status_count if cls.is_fan_hotswapable() else 1

@classmethod
@utils.read_only_cache()
Expand Down
52 changes: 45 additions & 7 deletions platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@
from .device_data import DeviceDataManager
from sonic_platform_base.sonic_xcvr.sfp_optoe_base import SfpOptoeBase
from sonic_platform_base.sonic_xcvr.fields import consts
from sonic_platform_base.sonic_xcvr.api.public import cmis, sff8636, sff8436
from sonic_platform_base.sonic_xcvr.api.public import sff8636, sff8436

from sonic_platform_base.sonic_xcvr.api.public import cmis as cmis_api
from sonic_platform_base.sonic_xcvr.codes.public import cmis as cmis_codes
from sonic_platform_base.sonic_xcvr.mem_maps.public import cmis as cmis_mem

except ImportError as e:
raise ImportError (str(e) + "- required module not found")
Expand Down Expand Up @@ -72,6 +76,7 @@
]

RJ45_TYPE = "RJ45"
CPO_TYPE = "CPO"

#variables for sdk
REGISTER_NUM = 1
Expand Down Expand Up @@ -461,18 +466,18 @@ def wait_sfp_eeprom_ready(cls, sfp_list, wait_time):
for s in not_ready_list:
logger.log_error(f'SFP {s.sdk_index} eeprom is not ready')

# read eeprom specfic bytes beginning from offset with size as num_bytes
# read eeprom specific bytes beginning from offset with size as num_bytes
def read_eeprom(self, offset, num_bytes):
"""
Read eeprom specfic bytes beginning from a random offset with size as num_bytes
Read eeprom specific bytes beginning from a random offset with size as num_bytes
Returns:
bytearray, if raw sequence of bytes are read correctly from the offset of size num_bytes
None, if the read_eeprom fails
"""
return self._read_eeprom(offset, num_bytes)

def _read_eeprom(self, offset, num_bytes, log_on_error=True):
"""Read eeprom specfic bytes beginning from a random offset with size as num_bytes
"""Read eeprom specific bytes beginning from a random offset with size as num_bytes

Args:
offset (int): read offset
Expand Down Expand Up @@ -520,10 +525,10 @@ def _read_eeprom(self, offset, num_bytes, log_on_error=True):

return bytearray(result)

# write eeprom specfic bytes beginning from offset with size as num_bytes
# write eeprom specific bytes beginning from offset with size as num_bytes
def write_eeprom(self, offset, num_bytes, write_buffer):
"""
write eeprom specfic bytes beginning from a random offset with size as num_bytes
write eeprom specific bytes beginning from a random offset with size as num_bytes
and write_buffer as the required bytes
Returns:
Boolean, true if the write succeeded and false if it did not succeed.
Expand Down Expand Up @@ -1086,7 +1091,7 @@ def is_cmis_api(self, xcvr_api):
Returns:
bool: True if the api is of type CMIS
"""
return isinstance(xcvr_api, cmis.CmisApi)
return isinstance(xcvr_api, cmis_api.CmisApi)

def is_sff_api(self, xcvr_api):
"""Check if the api type is SFF
Expand Down Expand Up @@ -1811,3 +1816,36 @@ def get_module_status(self):
"""
status = super().get_module_status()
return SFP_STATUS_REMOVED if status == SFP_STATUS_UNKNOWN else status


class CpoPort(SFP):
"""class derived from SFP, representing CPO ports"""

def __init__(self, sfp_index):
super(CpoPort, self).__init__(sfp_index)
self._sfp_type_str = None
self.sfp_type = CPO_TYPE

def get_transceiver_info(self):
transceiver_info_dict = super().get_transceiver_info()
transceiver_info_dict['type'] = self.sfp_type
return transceiver_info_dict

def get_xcvr_api(self):
if self._xcvr_api is None:
self._xcvr_api = self._xcvr_api_factory._create_api(cmis_codes.CmisCodes, cmis_mem.CmisMemMap, cmis_api.CmisApi)
return self._xcvr_api

def get_presence(self):
file_path = SFP_SDK_MODULE_SYSFS_ROOT_TEMPLATE.format(self.sdk_index) + SFP_SYSFS_PRESENT
present = utils.read_int_from_file(file_path)
return present == 1

def reinit(self):
"""
Nothing to do for cpo. Just provide it to avoid exception
:return:
"""
return


15 changes: 13 additions & 2 deletions platform/mellanox/mlnx-platform-api/sonic_platform/thermal.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,13 @@
},
{
"name": "Ambient Port Side Temp",
"temperature": "port_amb"
"temperature": "port_amb",
"default_present": True
},
{
"name": "Ambient Fan Side Temp",
"temperature": "fan_amb"
"temperature": "fan_amb",
"default_present": True
},
{
"name": "Ambient COMEX Temp",
Expand Down Expand Up @@ -139,6 +141,15 @@
"search_pattern": '/run/hw-management/thermal/sodimm*_temp_input',
'index_pattern': r'sodimm(\d+)_temp_input',
"type": "discrete",
},
{
"name": "PMIC {} Temp",
"temperature": "voltmon{}_temp1_input",
"high_threshold": "voltmon{}_temp1_max",
"high_critical_threshold": "voltmon{}_temp1_crit",
"search_pattern": '/run/hw-management/thermal/voltmon*_temp1_input',
'index_pattern': r'voltmon(\d+)_temp1_input',
"type": "discrete",
}
],
'linecard thermals': {
Expand Down
47 changes: 34 additions & 13 deletions platform/mellanox/mlnx-platform-api/sonic_platform/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
PORT_INDEX_KEY = "index"
PORT_TYPE_KEY = "port_type"
RJ45_PORT_TYPE = "RJ45"
CPO_PORT_TYPE = "CPO"

logger = Logger()

Expand Down Expand Up @@ -239,19 +240,14 @@ def load_json_file(filename, log_func=logger.log_error):
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()
hwsku_file = os.path.join(hwsku_path, HWSKU_JSON)
if not os.path.exists(hwsku_file):
# Platforms having no hwsku.json do not have RJ45 port
def _extract_ports_index_by_type(port_type, num_of_asics=1):
platform_file = os.path.join(device_info.get_path_to_platform_dir(), device_info.PLATFORM_JSON_FILE)
if not os.path.exists(platform_file):
return None

platform_file = device_info.get_path_to_port_config_file()
platform_dict = load_json_file(platform_file)['interfaces']
hwsku_dict = load_json_file(hwsku_file)['interfaces']
port_name_to_index_map_dict = {}
RJ45_port_index_list = []
port_index_list = []

# Compose a interface name to index mapping from 'platform.json'
for i, (key, value) in enumerate(platform_dict.items()):
Expand All @@ -264,12 +260,37 @@ def extract_RJ45_ports_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.
hwsku_jsons = get_path_list_to_asic_hwsku_dir(num_of_asics)
hwsku_dict = {}
for hwsku_json in hwsku_jsons:
hwsku_dict.update(load_json_file(hwsku_json)['interfaces'])

# Check if "port_type" matches, 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)
if key in port_name_to_index_map_dict and PORT_TYPE_KEY in value and value[PORT_TYPE_KEY] == port_type:
port_index_list.append(int(port_name_to_index_map_dict[key]) - 1)

# Remove duplicates
port_index_list = list(dict.fromkeys(port_index_list))

return port_index_list if port_index_list else None


def get_path_list_to_asic_hwsku_dir(num_of_asics):
platform_path = device_info.get_path_to_platform_dir()
hwsku = device_info.get_hwsku()
if num_of_asics == 1:
return [os.path.join(platform_path, hwsku, HWSKU_JSON)]
else:
return [os.path.join(platform_path, hwsku, str(asic_id), HWSKU_JSON) for asic_id in range(num_of_asics)]


def extract_RJ45_ports_index(num_of_asics=1):
return _extract_ports_index_by_type(RJ45_PORT_TYPE, num_of_asics)


return RJ45_port_index_list if bool(RJ45_port_index_list) else None
def extract_cpo_ports_index(num_of_asics=1):
return _extract_ports_index_by_type(CPO_PORT_TYPE, num_of_asics)


def wait_until(predict, timeout, interval=1, *args, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class TestChangeEvent:
@mock.patch('sonic_platform.device_data.DeviceDataManager.is_module_host_management_mode', mock.MagicMock(return_value=False))
@mock.patch('sonic_platform.device_data.DeviceDataManager.get_sfp_count', mock.MagicMock(return_value=1))
@mock.patch('sonic_platform.chassis.extract_RJ45_ports_index', mock.MagicMock(return_value=[]))
@mock.patch('sonic_platform.chassis.extract_cpo_ports_index', mock.MagicMock(return_value=[]))
@mock.patch('sonic_platform.sfp.SFP.get_module_status')
def test_get_change_event_legacy(self, mock_status, mock_time, mock_create_poll, mock_get_fd):
c = chassis.Chassis()
Expand Down Expand Up @@ -92,6 +93,7 @@ def test_get_change_event_legacy(self, mock_status, mock_time, mock_create_poll,
@mock.patch('sonic_platform.device_data.DeviceDataManager.is_module_host_management_mode', mock.MagicMock(return_value=True))
@mock.patch('sonic_platform.device_data.DeviceDataManager.get_sfp_count', mock.MagicMock(return_value=1))
@mock.patch('sonic_platform.chassis.extract_RJ45_ports_index', mock.MagicMock(return_value=[]))
@mock.patch('sonic_platform.chassis.extract_cpo_ports_index', mock.MagicMock(return_value=[]))
@mock.patch('sonic_platform.module_host_mgmt_initializer.ModuleHostMgmtInitializer.initialize', mock.MagicMock())
def test_get_change_event_for_module_host_management_mode(self, mock_time, mock_create_poll, mock_get_fd, mock_ready):
"""Test steps:
Expand Down
Loading