[Mellanox] implement sfp.reset for CMIS management (#16862)

- Why I did it
For CMIS host management module, we need a different implementation for sfp.reset. This PR is to implement it

- How I did it
For SW control modules, do reset from hw_reset
For FW control modules, do reset as the original way

- How to verify it
Manual test
sonic-mgmt platform test
This commit is contained in:
Junchao-Mellanox 2023-12-17 14:02:47 +08:00 committed by mssonicbld
parent cacf46ff86
commit 56ba5b10b4
3 changed files with 77 additions and 22 deletions

View File

@ -26,6 +26,7 @@ try:
import ctypes
import subprocess
import os
import threading
from sonic_py_common.logger import Logger
from sonic_py_common.general import check_output_pipe
from . import utils
@ -219,6 +220,9 @@ class SdkHandleContext(object):
deinitialize_sdk_handle(self.sdk_handle)
class NvidiaSFPCommon(SfpOptoeBase):
sfp_index_to_logical_port_dict = {}
sfp_index_to_logical_lock = threading.Lock()
def __init__(self, sfp_index):
super(NvidiaSFPCommon, self).__init__()
self.index = sfp_index + 1
@ -248,6 +252,30 @@ class NvidiaSFPCommon(SfpOptoeBase):
return oper_state, error_type
@classmethod
def get_sfp_index_to_logical_port(cls, force=False):
if not cls.sfp_index_to_logical_port_dict or force:
config_db = utils.DbUtils.get_db_instance('CONFIG_DB')
port_data = config_db.get_table('PORT')
for key, data in port_data.items():
if data['index'] not in cls.sfp_index_to_logical_port_dict:
cls.sfp_index_to_logical_port_dict[int(data['index']) - 1] = key
@classmethod
def get_logical_port_by_sfp_index(cls, sfp_index):
with cls.sfp_index_to_logical_lock:
cls.get_sfp_index_to_logical_port()
logical_port_name = cls.sfp_index_to_logical_port_dict.get(sfp_index)
if not logical_port_name:
cls.get_sfp_index_to_logical_port(force=True)
else:
config_db = utils.DbUtils.get_db_instance('CONFIG_DB')
current_index = int(config_db.get('CONFIG_DB', f'PORT|{logical_port_name}', 'index'))
if current_index != sfp_index:
cls.get_sfp_index_to_logical_port(force=True)
logical_port_name = cls.sfp_index_to_logical_port_dict.get(sfp_index)
return logical_port_name
class SFP(NvidiaSFPCommon):
"""Platform-specific SFP class"""
@ -299,6 +327,17 @@ class SFP(NvidiaSFPCommon):
Returns:
bool: True if device is present, False if not
"""
if DeviceDataManager.is_independent_mode():
if utils.read_int_from_file(f'/sys/module/sx_core/asic0/module{self.sdk_index}/control') != 0:
if not utils.read_int_from_file(f'/sys/module/sx_core/asic0/module{self.sdk_index}/hw_present'):
return False
if not utils.read_int_from_file(f'/sys/module/sx_core/asic0/module{self.sdk_index}/power_good'):
return False
if not utils.read_int_from_file(f'/sys/module/sx_core/asic0/module{self.sdk_index}/power_on'):
return False
if utils.read_int_from_file(f'/sys/module/sx_core/asic0/module{self.sdk_index}/hw_reset') == 1:
return False
eeprom_raw = self._read_eeprom(0, 1, log_on_error=False)
return eeprom_raw is not None
@ -455,8 +494,17 @@ class SFP(NvidiaSFPCommon):
refer plugins/sfpreset.py
"""
try:
if not self.is_sw_control():
file_path = SFP_SDK_MODULE_SYSFS_ROOT_TEMPLATE.format(self.sdk_index) + SFP_SYSFS_RESET
return utils.write_file(file_path, '1')
else:
file_path = SFP_SDK_MODULE_SYSFS_ROOT_TEMPLATE.format(self.sdk_index) + SFP_SYSFS_HWRESET
return utils.write_file(file_path, '0') and utils.write_file(file_path, '1')
except Exception as e:
print(f'Failed to reset module - {e}')
logger.log_error(f'Failed to reset module - {e}')
return False
@classmethod
@ -918,15 +966,15 @@ class SFP(NvidiaSFPCommon):
return False
db = utils.DbUtils.get_db_instance('STATE_DB')
control_type = db.get('STATE_DB', f'TRANSCEIVER_MODULES_MGMT|{self.sdk_index}', 'control_type')
control_file_value = utils.read_int_from_file(f'/sys/module/sx_core/asic0/module{self.sdk_index}/control')
logical_port = NvidiaSFPCommon.get_logical_port_by_sfp_index(self.sdk_index)
if not logical_port:
raise Exception(f'Module {self.sdk_index} is not present or in initialization')
if control_type == 'SW_CONTROL' and control_file_value == 1:
return True
elif control_type == 'FW_CONTROL' and control_file_value == 0:
return False
else:
raise Exception(f'Module {self.sdk_index} is in initialization, please retry later')
initialized = db.exists('STATE_DB', f'TRANSCEIVER_STATUS|{logical_port}')
if not initialized:
raise Exception(f'Module {self.sdk_index} is not present or in initialization')
return utils.read_int_from_file(f'/sys/module/sx_core/asic0/module{self.sdk_index}/control') == 1
class RJ45Port(NvidiaSFPCommon):

View File

@ -381,9 +381,9 @@ class DbUtils:
cls.db_instances.data = {}
if db_name not in cls.db_instances.data:
from swsscommon.swsscommon import SonicV2Connector
db = SonicV2Connector(use_unix_socket_path=True)
db.connect(db_name)
from swsscommon.swsscommon import ConfigDBConnector
db = ConfigDBConnector(use_unix_socket_path=True)
db.db_connect(db_name)
cls.db_instances.data[db_name] = db
return cls.db_instances.data[db_name]
except Exception as e:

View File

@ -266,9 +266,14 @@ class TestSfp:
@mock.patch('sonic_platform.utils.write_file')
def test_reset(self, mock_write):
sfp = SFP(0)
sfp.is_sw_control = mock.MagicMock(return_value=False)
mock_write.return_value = True
assert sfp.reset()
mock_write.assert_called_with('/sys/module/sx_core/asic0/module0/reset', '1')
sfp.is_sw_control.return_value = True
assert sfp.reset()
sfp.is_sw_control.side_effect = Exception('')
assert not sfp.reset()
@mock.patch('sonic_platform.sfp.SFP.read_eeprom')
def test_get_xcvr_api(self, mock_read):
@ -332,31 +337,33 @@ class TestSfp:
assert sfp.get_temperature_warning_threashold() == 75.0
assert sfp.get_temperature_critical_threashold() == 85.0
@mock.patch('sonic_platform.sfp.NvidiaSFPCommon.get_logical_port_by_sfp_index')
@mock.patch('sonic_platform.utils.read_int_from_file')
@mock.patch('sonic_platform.device_data.DeviceDataManager.is_independent_mode')
@mock.patch('sonic_platform.utils.DbUtils.get_db_instance')
def test_is_sw_control(self, mock_get_db, mock_mode, mock_read):
def test_is_sw_control(self, mock_get_db, mock_mode, mock_read, mock_get_logical):
sfp = SFP(0)
mock_mode.return_value = False
assert not sfp.is_sw_control()
mock_mode.return_value = True
mock_db = mock.MagicMock()
mock_get_db.return_value = mock_db
mock_db.get = mock.MagicMock(return_value=None)
mock_get_logical.return_value = None
with pytest.raises(Exception):
sfp.is_sw_control()
mock_read.return_value = 0
mock_db.get.return_value = 'FW_CONTROL'
assert not sfp.is_sw_control()
mock_read.return_value = 1
mock_db.get.return_value = 'SW_CONTROL'
assert sfp.is_sw_control()
mock_read.return_value = 0
mock_get_logical.return_value = 'Ethernet0'
mock_db = mock.MagicMock()
mock_get_db.return_value = mock_db
mock_db.exists = mock.MagicMock(return_value=False)
with pytest.raises(Exception):
sfp.is_sw_control()
mock_db.exists.return_value = True
mock_read.return_value = 0
assert not sfp.is_sw_control()
mock_read.return_value = 1
assert sfp.is_sw_control()
@mock.patch('sonic_platform.device_data.DeviceDataManager.is_independent_mode', mock.MagicMock(return_value=False))
@mock.patch('sonic_platform.sfp.SFP.is_sw_control', mock.MagicMock(return_value=False))
@mock.patch('sonic_platform.utils.is_host', mock.MagicMock(side_effect = [True, True, False, False]))