Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 11 commits into from
Jun 21, 2022
3 changes: 0 additions & 3 deletions device/mellanox/x86_64-nvidia_sn2201-r0/platform.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,6 @@
{
"name": "Ambient CPU Board Temp"
},
{
"name": "Ambient Switch Board Temp"
},
{
"name": "CPU Pack Temp"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"component": {
"ONIE": { },
"SSD": { },
"BIOS": { },
"CPLD1": { },
"CPLD2": { }
}
Expand Down
33 changes: 25 additions & 8 deletions platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,17 @@
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"
Expand Down Expand Up @@ -106,6 +109,10 @@ 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
self.RJ45_port_list = extract_RJ45_ports_index()

logger.log_info("Chassis loaded successfully")

def __del__(self):
Expand Down Expand Up @@ -242,22 +249,31 @@ 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] = RJ45Port(index)
else:
self._sfp_list[index] = SFP(index)
self.sfp_initialized_count += 1

def initialize_sfp(self):
if not self._sfp_list:
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):
Expand Down Expand Up @@ -324,7 +340,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)
Expand All @@ -340,7 +356,8 @@ 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 port_dict:
self.reinit_sfps(port_dict)
result_dict = {'sfp':port_dict}
if error_dict:
result_dict['sfp_error'] = error_dict
Expand Down Expand Up @@ -515,8 +532,8 @@ 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())
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):
"""
Expand Down
71 changes: 66 additions & 5 deletions platform/mellanox/mlnx-platform-api/sonic_platform/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -336,6 +347,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))
Expand Down Expand Up @@ -698,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'
Expand Down Expand Up @@ -744,7 +789,7 @@ def __get_mst_device(self):

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

Expand Down Expand Up @@ -830,9 +875,9 @@ def install_firmware(self, image_path):
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:
Expand All @@ -845,11 +890,11 @@ def update_firmware(self, image_path):
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):
Expand All @@ -862,3 +907,19 @@ def get_component_list(cls):
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
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,7 @@
'thermal': {
"capability": {
"comex_amb": False,
"cpu_amb": True,
"swb_amb": True
"cpu_amb": True
}
}
},
Expand Down Expand Up @@ -281,3 +280,21 @@ def get_cpu_thermal_threshold(cls):
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()
Loading