[Mellanox] Fixes issue: CLI sfputil does not work based on sonic platform API (#7018)

#### Why I did it

Recently, CLI sfputil replace the old sonic platform utils with sonic platform API. However, sonic platform API does not support SFP low power mode and reset related operation. The PR is to fix it.

The change to replace platform utils with sonic platform API was reverted on 202012, once this PR is merged, we can cherry-pick these two PRs to 202012 together.

#### How I did it

In low power mode and reset related operation, use "docker exec" if the command is running on host side.
This commit is contained in:
Junchao-Mellanox 2021-03-12 10:54:33 +08:00 committed by GitHub
parent 06de63a835
commit 7caa70d2d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 159 additions and 56 deletions

View File

@ -7,16 +7,16 @@
#############################################################################
try:
import subprocess
from sonic_platform_base.platform_base import PlatformBase
from sonic_platform.chassis import Chassis
from . import utils
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
class Platform(PlatformBase):
def __init__(self):
PlatformBase.__init__(self)
if self._is_host():
if utils.is_host():
self._chassis = Chassis()
self._chassis.initialize_components()
self._chassis.initizalize_system_led()
@ -27,26 +27,3 @@ class Platform(PlatformBase):
self._chassis.initialize_fan()
self._chassis.initialize_eeprom()
self._chassis.initialize_thermals()
def _is_host(self):
"""
Test whether current process is running on the host or an docker
return True for host and False for docker
"""
is_host = False
try:
proc = subprocess.Popen("docker --version 2>/dev/null",
stdout=subprocess.PIPE,
shell=True,
stderr=subprocess.STDOUT,
universal_newlines=True)
stdout = proc.communicate()[0]
proc.wait()
result = stdout.rstrip('\n')
if result != '':
is_host = True
except OSError as e:
pass
return is_host

View File

@ -10,7 +10,6 @@
try:
import subprocess
import time
from sonic_platform_base.sfp_base import SfpBase
from sonic_platform_base.sonic_eeprom import eeprom_dts
from sonic_platform_base.sonic_sfp.sff8472 import sff8472InterfaceId
@ -21,6 +20,7 @@ try:
from sonic_platform_base.sonic_sfp.qsfp_dd import qsfp_dd_InterfaceId
from sonic_platform_base.sonic_sfp.qsfp_dd import qsfp_dd_Dom
from sonic_py_common.logger import Logger
from . import utils
except ImportError as e:
raise ImportError (str(e) + "- required module not found")
@ -314,6 +314,19 @@ def deinitialize_sdk_handle(sdk_handle):
logger.log_warning("Sdk handle is none")
return False
class SdkHandleContext(object):
def __init__(self):
self.sdk_handle = None
def __enter__(self):
self.sdk_handle = initialize_sdk_handle()
return self.sdk_handle
def __exit__(self, exc_type, exc_val, exc_tb):
deinitialize_sdk_handle(self.sdk_handle)
class SFP(SfpBase):
"""Platform-specific SFP class"""
@ -1474,13 +1487,14 @@ class SFP(SfpBase):
return tx_disabled
def mgmt_phy_mod_pwr_attr_get(self, power_attr_type):
@classmethod
def mgmt_phy_mod_pwr_attr_get(cls, power_attr_type, sdk_handle, sdk_index):
sx_mgmt_phy_mod_pwr_attr_p = new_sx_mgmt_phy_mod_pwr_attr_t_p()
sx_mgmt_phy_mod_pwr_attr = sx_mgmt_phy_mod_pwr_attr_t()
sx_mgmt_phy_mod_pwr_attr.power_attr_type = power_attr_type
sx_mgmt_phy_mod_pwr_attr_t_p_assign(sx_mgmt_phy_mod_pwr_attr_p, sx_mgmt_phy_mod_pwr_attr)
try:
rc = sx_mgmt_phy_mod_pwr_attr_get(self.sdk_handle, self.sdk_index, sx_mgmt_phy_mod_pwr_attr_p)
rc = sx_mgmt_phy_mod_pwr_attr_get(sdk_handle, sdk_index, sx_mgmt_phy_mod_pwr_attr_p)
assert SX_STATUS_SUCCESS == rc, "sx_mgmt_phy_mod_pwr_attr_get failed"
sx_mgmt_phy_mod_pwr_attr = sx_mgmt_phy_mod_pwr_attr_t_p_value(sx_mgmt_phy_mod_pwr_attr_p)
pwr_mode_attr = sx_mgmt_phy_mod_pwr_attr.pwr_mode_attr
@ -1489,6 +1503,7 @@ class SFP(SfpBase):
delete_sx_mgmt_phy_mod_pwr_attr_t_p(sx_mgmt_phy_mod_pwr_attr_p)
def get_lpmode(self):
"""
Retrieves the lpmode (low power mode) status of this SFP
@ -1496,8 +1511,35 @@ class SFP(SfpBase):
Returns:
A Boolean, True if lpmode is enabled, False if disabled
"""
admin_pwr_mode, oper_pwr_mode = self.mgmt_phy_mod_pwr_attr_get(SX_MGMT_PHY_MOD_PWR_ATTR_PWR_MODE_E)
if utils.is_host():
# To avoid performance issue,
# call class level method to avoid initialize the whole sonic platform API
get_lpmode_code = 'from sonic_platform import sfp;\n' \
'with sfp.SdkHandleContext() as sdk_handle:' \
'print(sfp.SFP._get_lpmode(sdk_handle, {}))'.format(self.sdk_index)
lpm_cmd = "docker exec pmon python -c \"{}\"".format(get_lpmode_code)
try:
output = subprocess.check_output(lpm_cmd, shell=True, universal_newlines=True)
return 'True' in output
except subprocess.CalledProcessError as e:
print("Error! Unable to get LPM for {}, rc = {}, err msg: {}".format(self.index, e.returncode, e.output))
return False
else:
return self._get_lpmode(self.sdk_handle, self.sdk_index)
@classmethod
def _get_lpmode(cls, sdk_handle, sdk_index):
"""Class level method to get low power mode.
Args:
sdk_handle: SDK handle
sdk_index (integer): SDK port index
Returns:
[boolean]: True if low power mode is on else off
"""
_, oper_pwr_mode = cls.mgmt_phy_mod_pwr_attr_get(SX_MGMT_PHY_MOD_PWR_ATTR_PWR_MODE_E, sdk_handle, sdk_index)
return oper_pwr_mode == SX_MGMT_PHY_MOD_PWR_MODE_LOW_E
@ -1872,7 +1914,28 @@ class SFP(SfpBase):
refer plugins/sfpreset.py
"""
rc = sx_mgmt_phy_mod_reset(self.sdk_handle, self.sdk_index)
if utils.is_host():
# To avoid performance issue,
# call class level method to avoid initialize the whole sonic platform API
reset_code = 'from sonic_platform import sfp;\n' \
'with sfp.SdkHandleContext() as sdk_handle:' \
'print(sfp.SFP._reset(sdk_handle, {}))' \
.format(self.sdk_index)
reset_cmd = "docker exec pmon python -c \"{}\"".format(reset_code)
try:
output = subprocess.check_output(reset_cmd, shell=True, universal_newlines=True)
return 'True' in output
except subprocess.CalledProcessError as e:
print("Error! Unable to set LPM for {}, rc = {}, err msg: {}".format(self.index, e.returncode, e.output))
return False
else:
return self._reset(self.sdk_handle, self.sdk_index)
@classmethod
def _reset(cls, sdk_handle, sdk_index):
rc = sx_mgmt_phy_mod_reset(sdk_handle, sdk_index)
if rc != SX_STATUS_SUCCESS:
logger.log_warning("sx_mgmt_phy_mod_reset failed, rc = %d" % rc)
@ -1914,19 +1977,22 @@ class SFP(SfpBase):
return NotImplementedError
def is_nve(self, port):
@classmethod
def is_nve(cls, port):
return (port & NVE_MASK) != 0
def is_cpu(self, port):
@classmethod
def is_cpu(cls, port):
return (port & CPU_MASK) != 0
def is_port_admin_status_up(self, log_port):
@classmethod
def is_port_admin_status_up(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()
rc = sx_api_port_state_get(self.sdk_handle, log_port, oper_state_p, admin_state_p, module_state_p)
rc = sx_api_port_state_get(sdk_handle, log_port, oper_state_p, admin_state_p, module_state_p)
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)
@ -1941,31 +2007,33 @@ class SFP(SfpBase):
return False
def set_port_admin_status_by_log_port(self, log_port, admin_status):
rc = sx_api_port_state_set(self.sdk_handle, log_port, admin_status)
@classmethod
def set_port_admin_status_by_log_port(cls, sdk_handle, log_port, admin_status):
rc = sx_api_port_state_set(sdk_handle, log_port, admin_status)
if SX_STATUS_SUCCESS != rc:
logger.log_error("sx_api_port_state_set failed, rc = %d" % rc)
return SX_STATUS_SUCCESS == rc
def get_logical_ports(self):
@classmethod
def get_logical_ports(cls, sdk_handle, sdk_index):
# Get all the ports related to the sfp, if port admin status is up, put it to list
port_attributes_list = new_sx_port_attributes_t_arr(SX_PORT_ATTR_ARR_SIZE)
port_cnt_p = new_uint32_t_p()
uint32_t_p_assign(port_cnt_p, SX_PORT_ATTR_ARR_SIZE)
rc = sx_api_port_device_get(self.sdk_handle, DEVICE_ID , SWITCH_ID, port_attributes_list, port_cnt_p)
rc = sx_api_port_device_get(sdk_handle, DEVICE_ID , SWITCH_ID, port_attributes_list, port_cnt_p)
assert rc == SX_STATUS_SUCCESS, "sx_api_port_device_get failed, rc = %d" % rc
port_cnt = uint32_t_p_value(port_cnt_p)
log_port_list = []
for i in range(0, port_cnt):
port_attributes = sx_port_attributes_t_arr_getitem(port_attributes_list, i)
if not self.is_nve(int(port_attributes.log_port)) \
and not self.is_cpu(int(port_attributes.log_port)) \
and port_attributes.port_mapping.module_port == self.sdk_index \
and self.is_port_admin_status_up(port_attributes.log_port):
if not cls.is_nve(int(port_attributes.log_port)) \
and not cls.is_cpu(int(port_attributes.log_port)) \
and port_attributes.port_mapping.module_port == sdk_index \
and cls.is_port_admin_status_up(sdk_handle, port_attributes.log_port):
log_port_list.append(port_attributes.log_port)
delete_sx_port_attributes_t_arr(port_attributes_list)
@ -1973,7 +2041,8 @@ class SFP(SfpBase):
return log_port_list
def mgmt_phy_mod_pwr_attr_set(self, power_attr_type, admin_pwr_mode):
@classmethod
def mgmt_phy_mod_pwr_attr_set(cls, sdk_handle, sdk_index, power_attr_type, admin_pwr_mode):
result = False
sx_mgmt_phy_mod_pwr_attr = sx_mgmt_phy_mod_pwr_attr_t()
sx_mgmt_phy_mod_pwr_mode_attr = sx_mgmt_phy_mod_pwr_mode_attr_t()
@ -1983,7 +2052,7 @@ class SFP(SfpBase):
sx_mgmt_phy_mod_pwr_attr_p = new_sx_mgmt_phy_mod_pwr_attr_t_p()
sx_mgmt_phy_mod_pwr_attr_t_p_assign(sx_mgmt_phy_mod_pwr_attr_p, sx_mgmt_phy_mod_pwr_attr)
try:
rc = sx_mgmt_phy_mod_pwr_attr_set(self.sdk_handle, SX_ACCESS_CMD_SET, self.sdk_index, sx_mgmt_phy_mod_pwr_attr_p)
rc = sx_mgmt_phy_mod_pwr_attr_set(sdk_handle, SX_ACCESS_CMD_SET, sdk_index, sx_mgmt_phy_mod_pwr_attr_p)
if SX_STATUS_SUCCESS != rc:
logger.log_error("sx_mgmt_phy_mod_pwr_attr_set failed, rc = %d" % rc)
result = False
@ -1995,23 +2064,24 @@ class SFP(SfpBase):
return result
def _set_lpmode_raw(self, ports, attr_type, power_mode):
@classmethod
def _set_lpmode_raw(cls, sdk_handle, sdk_index, ports, attr_type, power_mode):
result = False
# Check if the module already works in the same mode
admin_pwr_mode, oper_pwr_mode = self.mgmt_phy_mod_pwr_attr_get(attr_type)
admin_pwr_mode, oper_pwr_mode = cls.mgmt_phy_mod_pwr_attr_get(attr_type, sdk_handle, sdk_index)
if (power_mode == SX_MGMT_PHY_MOD_PWR_MODE_LOW_E and oper_pwr_mode == SX_MGMT_PHY_MOD_PWR_MODE_LOW_E) \
or (power_mode == SX_MGMT_PHY_MOD_PWR_MODE_AUTO_E and admin_pwr_mode == SX_MGMT_PHY_MOD_PWR_MODE_AUTO_E):
return True
try:
# Bring the port down
for port in ports:
self.set_port_admin_status_by_log_port(port, SX_PORT_ADMIN_STATUS_DOWN)
cls.set_port_admin_status_by_log_port(sdk_handle, port, SX_PORT_ADMIN_STATUS_DOWN)
# Set the desired power mode
result = self.mgmt_phy_mod_pwr_attr_set(attr_type, power_mode)
result = cls.mgmt_phy_mod_pwr_attr_set(sdk_handle, sdk_index, attr_type, power_mode)
finally:
# Bring the port up
for port in ports:
self.set_port_admin_status_by_log_port(port, SX_PORT_ADMIN_STATUS_UP)
cls.set_port_admin_status_by_log_port(sdk_handle, port, SX_PORT_ADMIN_STATUS_UP)
return result
@ -2027,14 +2097,36 @@ class SFP(SfpBase):
Returns:
A boolean, True if lpmode is set successfully, False if not
"""
log_port_list = self.get_logical_ports()
if lpmode:
self._set_lpmode_raw(log_port_list, SX_MGMT_PHY_MOD_PWR_ATTR_PWR_MODE_E, SX_MGMT_PHY_MOD_PWR_MODE_LOW_E)
logger.log_info("Enabled low power mode for module [%d]" % (self.sdk_index))
if utils.is_host():
# To avoid performance issue,
# call class level method to avoid initialize the whole sonic platform API
set_lpmode_code = 'from sonic_platform import sfp;\n' \
'with sfp.SdkHandleContext() as sdk_handle:' \
'print(sfp.SFP._set_lpmode({}, sdk_handle, {}))' \
.format('True' if lpmode else 'False', self.sdk_index)
lpm_cmd = "docker exec pmon python -c \"{}\"".format(set_lpmode_code)
# Set LPM
try:
output = subprocess.check_output(lpm_cmd, shell=True, universal_newlines=True)
return 'True' in output
except subprocess.CalledProcessError as e:
print("Error! Unable to set LPM for {}, rc = {}, err msg: {}".format(self.index, e.returncode, e.output))
return False
else:
self._set_lpmode_raw(log_port_list, SX_MGMT_PHY_MOD_PWR_ATTR_PWR_MODE_E, SX_MGMT_PHY_MOD_PWR_MODE_AUTO_E)
logger.log_info( "Disabled low power mode for module [%d]" % (self.sdk_index))
return self._set_lpmode(lpmode, self.sdk_handle, self.sdk_index)
@classmethod
def _set_lpmode(cls, lpmode, sdk_handle, sdk_index):
log_port_list = cls.get_logical_ports(sdk_handle, sdk_index)
sdk_lpmode = SX_MGMT_PHY_MOD_PWR_MODE_LOW_E if lpmode else SX_MGMT_PHY_MOD_PWR_MODE_AUTO_E
cls._set_lpmode_raw(sdk_handle,
sdk_index,
log_port_list,
SX_MGMT_PHY_MOD_PWR_ATTR_PWR_MODE_E,
sdk_lpmode)
logger.log_info("{} low power mode for module {}".format("Enabled" if lpmode else "Disabled", sdk_index))
return True

View File

@ -1,3 +1,9 @@
import subprocess
# flags to indicate whether this process is running in docker or host
_is_host = None
def read_str_from_file(file_path, default='', raise_exception=False):
"""
Read string content from file
@ -55,3 +61,31 @@ def write_file(file_path, content, raise_exception=False):
else:
raise e
return True
def is_host():
"""
Test whether current process is running on the host or an docker
return True for host and False for docker
"""
global _is_host
if _is_host is not None:
return _is_host
_is_host = False
try:
proc = subprocess.Popen("docker --version 2>/dev/null",
stdout=subprocess.PIPE,
shell=True,
stderr=subprocess.STDOUT,
universal_newlines=True)
stdout = proc.communicate()[0]
proc.wait()
result = stdout.rstrip('\n')
if result != '':
_is_host = True
except OSError as e:
pass
return _is_host