From 6a9d1e584d6d22bbb6600fb6083c1aa9fd0e0248 Mon Sep 17 00:00:00 2001 From: Alexander Allen Date: Mon, 24 May 2021 12:37:59 -0400 Subject: [PATCH] [Mellanox] Implement Hardware Revision Platform API Call for Mellanox Chassis and PSU (#7552) #### Why I did it This pull request allows calls to be made through the platform 2.0 API that retrieve the PSU and Chassis hardware revision on Mellanox platforms. Access to these values will aid customers in determining their hardware revisions for debugging and technical support. These values are intended to be eventually exposed through the CLI. #### How I did it For the PSU hardware revision I used the existing VPD function calls implemented in https://github.com/Azure/sonic-buildimage/pull/7382 For the Chassis hardware revision I parsed the SMBIOS / DMI type 2 information to retrieve the information. --- .../sonic_platform/chassis.py | 57 +++++++++++++++++++ .../mlnx-platform-api/sonic_platform/psu.py | 17 ++++++ 2 files changed, 74 insertions(+) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py index b18d780fab..341a4407fe 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py @@ -26,6 +26,23 @@ MAX_SELECT_DELAY = 3600 MLNX_NUM_PSU = 2 +DMI_FILE = '/sys/firmware/dmi/entries/2-0/raw' +DMI_HEADER_LEN = 15 +DMI_PRODUCT_NAME = "Product Name" +DMI_MANUFACTURER = "Manufacturer" +DMI_VERSION = "Version" +DMI_SERIAL = "Serial Number" +DMI_ASSET_TAG = "Asset Tag" +DMI_LOC = "Location In Chassis" +DMI_TABLE_MAP = { + DMI_PRODUCT_NAME: 0, + DMI_MANUFACTURER: 1, + DMI_VERSION: 2, + DMI_SERIAL: 3, + DMI_ASSET_TAG: 4, + DMI_LOC: 5 + } + EEPROM_CACHE_ROOT = '/var/cache/sonic/decode-syseeprom' EEPROM_CACHE_FILE = 'syseeprom_cache' @@ -67,6 +84,9 @@ class Chassis(ChassisBase): # Initialize Platform name self.platform_name = device_info.get_platform() + # Initialize DMI data + self.dmi_data = None + # move the initialization of each components to their dedicated initializer # which will be called from platform # @@ -240,6 +260,18 @@ class Chassis(ChassisBase): string: Model/part number of device """ return self.model + + def get_revision(self): + """ + Retrieves the hardware revision of the device + + Returns: + string: Revision value of device + """ + if self.dmi_data is None: + self.dmi_data = self._parse_dmi(DMI_FILE) + + return self.dmi_data.get(DMI_VERSION, "N/A") ############################################## # SFP methods @@ -394,6 +426,31 @@ class Chassis(ChassisBase): return '0' + def _parse_dmi(self, filename): + """ + Read DMI data chassis data and returns a dictionary of values + + Returns: + A dictionary containing the dmi table of the switch chassis info + """ + result = {} + try: + fileobj = open(filename, "rb") + data = fileobj.read() + fileobj.close() + + body = data[DMI_HEADER_LEN:] + records = body.split(b'\x00') + + for k, v in DMI_TABLE_MAP.items(): + result[k] = records[v].decode("utf-8") + + except Exception as e: + logger.log_error("Fail to decode DMI {} due to {}".format(filename, repr(e))) + + return result + + def _verify_reboot_cause(self, filename): ''' Open and read the reboot cause file in diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/psu.py b/platform/mellanox/mlnx-platform-api/sonic_platform/psu.py index 12b7a35b7a..e357a39141 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/psu.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/psu.py @@ -31,6 +31,7 @@ PSU_VPD = "vpd" SN_VPD_FIELD = "SN_VPD_FIELD" PN_VPD_FIELD = "PN_VPD_FIELD" +REV_VPD_FIELD = "REV_VPD_FIELD" # in most platforms the file psuX_curr, psuX_volt and psuX_power contain current, voltage and power data respectively. # but there are exceptions which will be handled by the following dictionary @@ -105,6 +106,12 @@ class Psu(PsuBase): self.serial = "" logger.log_error("Fail to read PSU{} serial number: No key {} in VPD {}".format(self.index, SN_VPD_FIELD, self.psu_vpd)) + if REV_VPD_FIELD in self.vpd_data: + self.rev = self.vpd_data[REV_VPD_FIELD] + else: + self.rev = "" + logger.log_error("Fail to read PSU{} serial number: No key {} in VPD {}".format(self.index, REV_VPD_FIELD, self.psu_vpd)) + else: logger.log_info("Not reading PSU{} VPD data: Platform is fixed".format(self.index)) @@ -210,6 +217,16 @@ class Psu(PsuBase): return self.serial + def get_revision(self): + """ + Retrieves the hardware revision of the device + + Returns: + string: Revision value of device + """ + return self.rev + + def get_powergood_status(self): """ Retrieves the operational status of power supply unit (PSU) defined