From 347d7262a15b75235b220841c5904ff966f318c2 Mon Sep 17 00:00:00 2001 From: Wirut Getbamrung Date: Sun, 15 Aug 2021 14:00:08 +0700 Subject: [PATCH] [202012][device/celestica]: Fix failed test cases of Haliburton platform API (#8297) To fix failed test cases of Haliburton platform APIs that found on platform_tests script - How I did it - Add device/celestica/x86_64-cel_e1031-r0/platform.json - Update functions to support python3.7 - Add more functions follow latest sonic_platform_base - Fix the bug Signed-off-by: Wirut Getbamrung [wgetbumr@celestica.com] --- .../x86_64-cel_e1031-r0/platform.json | 262 ++++++ .../platform_components.json | 11 + .../x86_64-cel_e1031-r0/plugins/sfputil.py | 61 +- .../sonic_platform/chassis.py | 112 ++- .../sonic_platform/common.py | 39 +- .../sonic_platform/component.py | 101 ++- .../sonic_platform/eeprom.py | 115 ++- .../sonic_platform/event.py | 13 +- .../x86_64-cel_e1031-r0/sonic_platform/fan.py | 91 +- .../sonic_platform/fan_drawer.py | 108 +++ .../sonic_platform/helper.py | 133 --- .../x86_64-cel_e1031-r0/sonic_platform/psu.py | 206 ++++- .../x86_64-cel_e1031-r0/sonic_platform/sfp.py | 838 ++++++++++++++---- .../sonic_platform/thermal.py | 74 +- .../sonic_platform/watchdog.py | 209 ++--- .../debian/platform-modules-haliburton.init | 9 +- 16 files changed, 1774 insertions(+), 608 deletions(-) create mode 100644 device/celestica/x86_64-cel_e1031-r0/platform.json create mode 100644 device/celestica/x86_64-cel_e1031-r0/platform_components.json create mode 100644 device/celestica/x86_64-cel_e1031-r0/sonic_platform/fan_drawer.py delete mode 100644 device/celestica/x86_64-cel_e1031-r0/sonic_platform/helper.py diff --git a/device/celestica/x86_64-cel_e1031-r0/platform.json b/device/celestica/x86_64-cel_e1031-r0/platform.json new file mode 100644 index 0000000000..0873a1398a --- /dev/null +++ b/device/celestica/x86_64-cel_e1031-r0/platform.json @@ -0,0 +1,262 @@ +{ + "chassis": { + "name": "Celestica-E1031-T48S4", + "components": [ + { + "name": "SMC_CPLD" + }, + { + "name": "MMC_CPLD" + }, + { + "name": "BIOS" + } + ], + "fans": [ + { + "name": "FAN-1" + }, + { + "name": "FAN-2" + }, + { + "name": "FAN-3" + } + ], + "fan_drawers": [ + { + "name": "Drawer1", + "fans": [ + { + "name": "FAN-1" + } + ] + }, + { + "name": "Drawer2", + "fans": [ + { + "name": "FAN-2" + } + ] + }, + { + "name": "Drawer3", + "fans": [ + { + "name": "FAN-3" + } + ] + } + ], + "psus": [ + { + "name": "PSU-R", + "fans": [ + { + "name": "PSU-1 FAN-1" + } + ] + }, + { + "name": "PSU-L", + "fans": [ + { + "name": "PSU-2 FAN-1" + } + ] + } + ], + "thermals": [ + { + "name": "Inlet ambient sensor (Rear to Front)" + }, + { + "name": "Helix shutdown sensor (Rear to Front)" + }, + { + "name": "Inlet ambient sensor (Front to Rear, right)" + }, + { + "name": "Helix shutdown sensor (Front to Rear)" + }, + { + "name": "Inlet ambient sensor (Front to Rear, left)" + }, + { + "name": "CPU errata sensor (Front to Rear)" + }, + { + "name": "CPU errata sensor (Rear to Front)" + } + ], + "sfps": [ + { + "name": "Ethernet54" + }, + { + "name": "Ethernet0" + }, + { + "name": "Ethernet1" + }, + { + "name": "Ethernet2" + }, + { + "name": "Ethernet3" + }, + { + "name": "Ethernet4" + }, + { + "name": "Ethernet5" + }, + { + "name": "Ethernet6" + }, + { + "name": "Ethernet7" + }, + { + "name": "Ethernet8" + }, + { + "name": "Ethernet9" + }, + { + "name": "Ethernet10" + }, + { + "name": "Ethernet11" + }, + { + "name": "Ethernet12" + }, + { + "name": "Ethernet13" + }, + { + "name": "Ethernet14" + }, + { + "name": "Ethernet15" + }, + { + "name": "Ethernet16" + }, + { + "name": "Ethernet17" + }, + { + "name": "Ethernet18" + }, + { + "name": "Ethernet19" + }, + { + "name": "Ethernet20" + }, + { + "name": "Ethernet21" + }, + { + "name": "Ethernet22" + }, + { + "name": "Ethernet23" + }, + { + "name": "Ethernet24" + }, + { + "name": "Ethernet25" + }, + { + "name": "Ethernet26" + }, + { + "name": "Ethernet27" + }, + { + "name": "Ethernet28" + }, + { + "name": "Ethernet29" + }, + { + "name": "Ethernet30" + }, + { + "name": "Ethernet31" + }, + { + "name": "Ethernet32" + }, + { + "name": "Ethernet33" + }, + { + "name": "Ethernet34" + }, + { + "name": "Ethernet35" + }, + { + "name": "Ethernet36" + }, + { + "name": "Ethernet37" + }, + { + "name": "Ethernet38" + }, + { + "name": "Ethernet39" + }, + { + "name": "Ethernet40" + }, + { + "name": "Ethernet41" + }, + { + "name": "Ethernet42" + }, + { + "name": "Ethernet43" + }, + { + "name": "Ethernet44" + }, + { + "name": "Ethernet45" + }, + { + "name": "Ethernet46" + }, + { + "name": "Ethernet47" + }, + { + "name": "Ethernet48" + }, + { + "name": "Ethernet49" + }, + { + "name": "Ethernet50" + }, + { + "name": "Ethernet51" + }, + { + "name": "Ethernet52" + }, + { + "name": "Ethernet53" + } + ] + }, + "interfaces": {} +} \ No newline at end of file diff --git a/device/celestica/x86_64-cel_e1031-r0/platform_components.json b/device/celestica/x86_64-cel_e1031-r0/platform_components.json new file mode 100644 index 0000000000..97e691f900 --- /dev/null +++ b/device/celestica/x86_64-cel_e1031-r0/platform_components.json @@ -0,0 +1,11 @@ +{ + "chassis": { + "Celestica-E1031-T48S4": { + "component": { + "BIOS": {}, + "SMC_CPLD": {}, + "MMC_CPLD": {} + } + } + } +} \ No newline at end of file diff --git a/device/celestica/x86_64-cel_e1031-r0/plugins/sfputil.py b/device/celestica/x86_64-cel_e1031-r0/plugins/sfputil.py index d65f2cf3f5..033d466821 100644 --- a/device/celestica/x86_64-cel_e1031-r0/plugins/sfputil.py +++ b/device/celestica/x86_64-cel_e1031-r0/plugins/sfputil.py @@ -11,63 +11,17 @@ class SfpUtil(SfpUtilBase): """Platform-specific SfpUtil class""" PORT_START = 1 - PORT_END = 52 + PORT_END = 55 + SFP_PORT_START = 49 + SFP_PORT_END = 52 port_to_i2c_mapping = { - 1: None, - 2: None, - 3: None, - 4: None, - 5: None, - 6: None, - 7: None, - 8: None, - 9: None, - 10: None, - 11: None, - 12: None, - 13: None, - 14: None, - 15: None, - 16: None, - 17: None, - 18: None, - 19: None, - 20: None, - 21: None, - 22: None, - 23: None, - 24: None, - 25: None, - 26: None, - 27: None, - 28: None, - 29: None, - 30: None, - 31: None, - 32: None, - 33: None, - 34: None, - 35: None, - 36: None, - 37: None, - 38: None, - 39: None, - 40: None, - 41: None, - 42: None, - 43: None, - 44: None, - 45: None, - 46: None, - 47: None, - 48: None, 49: 15, 50: 14, 51: 17, 52: 16 } _port_to_eeprom_mapping = {} - _sfp_port = list(range(49, PORT_END + 1)) + _sfp_port = list(range(SFP_PORT_START, SFP_PORT_END + 1)) @property def port_start(self): @@ -89,7 +43,7 @@ class SfpUtil(SfpUtilBase): # Override port_to_eeprom_mapping for class initialization eeprom_path = '/sys/bus/i2c/devices/i2c-{0}/{0}-0050/eeprom' for x in range(self.PORT_START, self.PORT_END + 1): - port_eeprom_path = eeprom_path.format(self.port_to_i2c_mapping[x]) + port_eeprom_path = eeprom_path.format(self.port_to_i2c_mapping[x]) if x in self._sfp_port else None self.port_to_eeprom_mapping[x] = port_eeprom_path SfpUtilBase.__init__(self) @@ -103,7 +57,7 @@ class SfpUtil(SfpUtilBase): try: with open(sfp_modabs_path, 'r') as port_status: status = int(port_status.read(), 16) - status = (status >> (port_num - 49)) & 1 + status = (status >> (port_num - self.SFP_PORT_START)) & 1 except IOError: return False @@ -138,7 +92,8 @@ class SfpUtil(SfpUtilBase): for port_num in self._sfp_port: change = (changes >> (port_num - 49)) & 1 if change == 1: - port_dict[str(port_num)] = str(int(self.get_presence(port_num))) + port_dict[str(port_num)] = str( + int(self.get_presence(port_num))) found_flag = 1 if not found_flag: diff --git a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/chassis.py b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/chassis.py index de7a246b43..cb442518f1 100644 --- a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/chassis.py +++ b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/chassis.py @@ -6,25 +6,26 @@ # ############################################################################# + try: - import sys from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper from sonic_platform_base.chassis_base import ChassisBase + from sonic_py_common import device_info from .common import Common from .event import SfpEvent except ImportError as e: raise ImportError(str(e) + "- required module not found") NUM_FAN_TRAY = 3 -NUM_FAN = 1 NUM_PSU = 2 NUM_THERMAL = 7 -NUM_SFP = 52 +NUM_SFP = 55 NUM_COMPONENT = 3 RESET_REGISTER = "0x112" HOST_REBOOT_CAUSE_PATH = "/host/reboot-cause/previous-reboot-cause.txt" PMON_REBOOT_CAUSE_PATH = "/usr/share/sonic/platform/api_files/reboot-cause/previous-reboot-cause.txt" HOST_CHK_CMD = "docker > /dev/null 2>&1" +STATUS_LED_PATH = "/sys/devices/platform/e1031.smc/master_led" class Chassis(ChassisBase): @@ -32,66 +33,63 @@ class Chassis(ChassisBase): def __init__(self): ChassisBase.__init__(self) + self._api_common = Common() - self.sfp_module_initialized = False + self._is_host = self._api_common.is_host() + self.__initialize_eeprom() - self.is_host = self._api_common.is_host() + self.__initialize_fan() + self.__initialize_psu() + self.__initialize_thermals() + self.__initialize_components() - if not self.is_host: - self.__initialize_fan() - self.__initialize_psu() - self.__initialize_thermals() - else: - self.__initialize_components() - - self._reboot_cause_path = HOST_REBOOT_CAUSE_PATH if self.__is_host( - ) else PMON_REBOOT_CAUSE_PATH + self.sfp_module_initialized = False + self._reboot_cause_path = HOST_REBOOT_CAUSE_PATH if self._is_host else PMON_REBOOT_CAUSE_PATH def __initialize_sfp(self): sfputil_helper = SfpUtilHelper() port_config_file_path = device_info.get_path_to_port_config_file() sfputil_helper.read_porttab_mappings(port_config_file_path, 0) - from sonic_platform.sfp import Sfp + from .sfp import Sfp for index in range(0, NUM_SFP): - name_idx = 0 if index+1 == NUM_SFP else index+1 - sfp = Sfp(index, sfputil_helper.logical[name_idx]) + sfp = Sfp(index, sfputil_helper.logical[index]) self._sfp_list.append(sfp) self.sfp_module_initialized = True def __initialize_psu(self): - from sonic_platform.psu import Psu + from .psu import Psu for index in range(0, NUM_PSU): psu = Psu(index) self._psu_list.append(psu) def __initialize_fan(self): - from sonic_platform.fan import Fan - for fant_index in range(0, NUM_FAN_TRAY): - for fan_index in range(0, NUM_FAN): - fan = Fan(fant_index, fan_index) - self._fan_list.append(fan) + from .fan_drawer import FanDrawer + for i in range(NUM_FAN_TRAY): + fandrawer = FanDrawer(i) + self._fan_drawer_list.append(fandrawer) + self._fan_list += fandrawer.get_all_fans() def __initialize_thermals(self): - from sonic_platform.thermal import Thermal + from .thermal import Thermal airflow = self.__get_air_flow() for index in range(0, NUM_THERMAL): thermal = Thermal(index, airflow) self._thermal_list.append(thermal) def __initialize_eeprom(self): - from sonic_platform.eeprom import Tlv + from .eeprom import Tlv self._eeprom = Tlv() def __initialize_components(self): - from sonic_platform.component import Component + from .component import Component for index in range(0, NUM_COMPONENT): component = Component(index) self._component_list.append(component) def __get_air_flow(self): air_flow_path = '/usr/share/sonic/device/{}/fan_airflow'.format( - self._api_common.platform) if self.is_host else '/usr/share/sonic/platform/fan_airflow' + self._api_common.get_platform()) if self._is_host else '/usr/share/sonic/platform/fan_airflow' air_flow = self._api_common.read_txt_file(air_flow_path) return air_flow or 'B2F' @@ -154,7 +152,6 @@ class Chassis(ChassisBase): else: reboot_cause = self.REBOOT_CAUSE_NON_HARDWARE description = 'Unknown reason' - return (reboot_cause, description) def get_watchdog(self): @@ -247,8 +244,8 @@ class Chassis(ChassisBase): # The index will start from 1 sfp = self._sfp_list[index-1] except IndexError: - sys.stderr.write("SFP index {} out of range (1-{})\n".format( - index, len(self._sfp_list))) + print("SFP index {} out of range (1-{})\n".format( + index, len(self._sfp_list))) return sfp ############################################################## @@ -269,13 +266,13 @@ class Chassis(ChassisBase): Returns: string: The name of the device """ - return self._api_common.hwsku + return self._api_common.get_hwsku() def get_presence(self): """ - Retrieves the presence of the PSU + Retrieves the presence of the Chassis Returns: - bool: True if PSU is present, False if not + bool: True if Chassis is present, False if not """ return True @@ -303,3 +300,52 @@ class Chassis(ChassisBase): """ return True + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. If the agent cannot determine the parent-relative position + for some reason, or if the associated value of entPhysicalContainedIn is '0', then the value '-1' is returned + Returns: + integer: The 1-based relative physical position in parent device or -1 if cannot determine the position + """ + return -1 + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False + + def set_status_led(self, color): + """ + Sets the state of the PSU status LED + Args: + color: A string representing the color with which to set the PSU status LED + Note: Only support green and off + Returns: + bool: True if status LED state is set successfully, False if not + """ + + status_str = { + self.STATUS_LED_COLOR_GREEN: 'green', + self.STATUS_LED_COLOR_AMBER: 'amber', + self.STATUS_LED_COLOR_OFF: 'off' + }.get(color, 'off') + + return self._api_common.write_txt_file(STATUS_LED_PATH, status_str) + + def get_status_led(self): + """ + Gets the state of the PSU status LED + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings above + """ + status = self._api_common.read_txt_file(STATUS_LED_PATH) + status_str = { + 'on': self.STATUS_LED_COLOR_GREEN, + 'amber': self.STATUS_LED_COLOR_AMBER, + 'off': self.STATUS_LED_COLOR_OFF + }.get(status, None) + + return status_str diff --git a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/common.py b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/common.py index 84fcafd1bc..08e8fc4147 100644 --- a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/common.py +++ b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/common.py @@ -29,7 +29,18 @@ class Common: def __init__(self, conf=None): self._main_conf = conf - (self.platform, self.hwsku) = device_info.get_platform_and_hwsku() + self.platform = None + self.hwsku = None + + def get_platform(self): + (self.platform, self.hwsku) = device_info.get_platform_and_hwsku( + ) if not self.platform else (self.platform, self.hwsku) + return self.platform + + def get_hwsku(self): + (self.platform, self.hwsku) = device_info.get_platform_and_hwsku( + ) if not self.hwsku else (self.platform, self.hwsku) + return self.hwsku def run_command(self, command): status = False @@ -38,7 +49,7 @@ class Common: p = subprocess.Popen( command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) raw_data, err = p.communicate() - if err == '': + if p.returncode == 0: status, output = True, raw_data.strip() except Exception: pass @@ -152,10 +163,28 @@ class Common: status, output = self.run_command(cmd) return output if status else None + def set_reg(self, path, reg_addr, value): + cmd = "echo {0} {1} > {2}".format(reg_addr, value, path) + status, output = self.run_command(cmd) + return output if status else None + def read_txt_file(self, path): - with open(path, 'r') as f: - output = f.readline() - return output.strip('\n') + try: + with open(path, 'r') as f: + output = f.readline() + return output.strip('\n') + except Exception: + pass + return '' + + def read_one_line_file(self, file_path): + try: + with open(file_path, 'r') as fd: + data = fd.readline() + return data.strip() + except IOError: + pass + return '' def write_txt_file(self, file_path, value): try: diff --git a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/component.py b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/component.py index 8d2829f90c..56fae35914 100644 --- a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/component.py +++ b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/component.py @@ -6,12 +6,12 @@ # ############################################################################# -import os.path -import shutil -import shlex -import subprocess try: + import os.path + import shutil + import shlex + import subprocess from sonic_platform_base.component_base import ComponentBase except ImportError as e: raise ImportError(str(e) + "- required module not found") @@ -119,6 +119,29 @@ class Component(ComponentBase): return fw_version + def get_available_firmware_version(self, image_path): + """ + Retrieves the available firmware version of the component + Note: the firmware version will be read from image + Args: + image_path: A string, path to firmware image + Returns: + A string containing the available firmware version of the component + """ + return "N/A" + + def get_firmware_update_notification(self, image_path): + """ + Retrieves a notification on what should be done in order to complete + the component firmware update + Args: + image_path: A string, path to firmware image + Returns: + A string containing the component firmware update notification if required. + By default 'None' value will be used, which indicates that no actions are required + """ + return "None" + def install_firmware(self, image_path): """ Install firmware to module @@ -139,5 +162,73 @@ class Component(ComponentBase): install_command = "ispvm %s" % new_image_path # elif self.name == "BIOS": # install_command = "afulnx_64 %s /p /b /n /x /r" % image_path - return self.__run_command(install_command) + + def update_firmware(self, image_path): + """ + Updates firmware of the component + This API performs firmware update: it assumes firmware installation and loading in a single call. + In case platform component requires some extra steps (apart from calling Low Level Utility) + to load the installed firmware (e.g, reboot, power cycle, etc.) - this will be done automatically by API + Args: + image_path: A string, path to firmware image + Raises: + RuntimeError: update failed + """ + return False + + ############################################################## + ###################### Device methods ######################## + ############################################################## + + def get_presence(self): + """ + Retrieves the presence of the FAN + Returns: + bool: True if FAN is present, False if not + """ + return True + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + return 'N/A' + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + return 'N/A' + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + return True + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + If the agent cannot determine the parent-relative position + for some reason, or if the associated value of + entPhysicalContainedIn is'0', then the value '-1' is returned + Returns: + integer: The 1-based relative physical position in parent device + or -1 if cannot determine the position + """ + return -1 + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False diff --git a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/eeprom.py b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/eeprom.py index c31d29f2ca..dc43a4d7dd 100644 --- a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/eeprom.py +++ b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/eeprom.py @@ -17,13 +17,14 @@ try: else: from cStringIO import StringIO - from sonic_platform_base.sonic_eeprom import eeprom_dts from sonic_platform_base.sonic_eeprom import eeprom_tlvinfo + from sonic_platform_base.sonic_eeprom.eeprom_base import EepromDecoder except ImportError as e: raise ImportError(str(e) + "- required module not found") CACHE_ROOT = '/var/cache/sonic/decode-syseeprom' CACHE_FILE = 'syseeprom_cache' +NULL = 'N/A' class Tlv(eeprom_tlvinfo.TlvInfoDecoder): @@ -32,8 +33,8 @@ class Tlv(eeprom_tlvinfo.TlvInfoDecoder): def __init__(self): self._eeprom_path = "/sys/class/i2c-adapter/i2c-2/2-0050/eeprom" + self._eeprom = None super(Tlv, self).__init__(self._eeprom_path, 0, '', True) - self._eeprom = self._load_eeprom() def __parse_output(self, decode_output): decode_output.replace('\0', '') @@ -50,7 +51,7 @@ class Tlv(eeprom_tlvinfo.TlvInfoDecoder): value = match.group(3).rstrip('\0') _eeprom_info_dict[idx] = value - except Exception: + except BaseException: pass return _eeprom_info_dict @@ -59,7 +60,7 @@ class Tlv(eeprom_tlvinfo.TlvInfoDecoder): sys.stdout = StringIO() try: self.read_eeprom_db() - except Exception: + except BaseException: decode_output = sys.stdout.getvalue() sys.stdout = original_stdout return self.__parse_output(decode_output) @@ -71,7 +72,7 @@ class Tlv(eeprom_tlvinfo.TlvInfoDecoder): if not os.path.exists(CACHE_ROOT): try: os.makedirs(CACHE_ROOT) - except Exception: + except BaseException: pass # @@ -80,7 +81,7 @@ class Tlv(eeprom_tlvinfo.TlvInfoDecoder): # try: self.set_cache_name(os.path.join(CACHE_ROOT, CACHE_FILE)) - except Exception: + except BaseException: pass e = self.read_eeprom() @@ -89,7 +90,7 @@ class Tlv(eeprom_tlvinfo.TlvInfoDecoder): try: self.update_cache(e) - except Exception: + except BaseException: pass self.decode_eeprom(e) @@ -102,14 +103,98 @@ class Tlv(eeprom_tlvinfo.TlvInfoDecoder): return self.__parse_output(decode_output) + def _valid_tlv(self, eeprom_data): + tlvinfo_type_codes_list = [ + self._TLV_CODE_PRODUCT_NAME, + self._TLV_CODE_PART_NUMBER, + self._TLV_CODE_SERIAL_NUMBER, + self._TLV_CODE_MAC_BASE, + self._TLV_CODE_MANUF_DATE, + self._TLV_CODE_DEVICE_VERSION, + self._TLV_CODE_LABEL_REVISION, + self._TLV_CODE_PLATFORM_NAME, + self._TLV_CODE_ONIE_VERSION, + self._TLV_CODE_MAC_SIZE, + self._TLV_CODE_MANUF_NAME, + self._TLV_CODE_MANUF_COUNTRY, + self._TLV_CODE_VENDOR_NAME, + self._TLV_CODE_DIAG_VERSION, + self._TLV_CODE_SERVICE_TAG, + self._TLV_CODE_VENDOR_EXT, + self._TLV_CODE_CRC_32 + ] + + for code in tlvinfo_type_codes_list: + code_str = "0x{:X}".format(code) + eeprom_data[code_str] = eeprom_data.get(code_str, NULL) + return eeprom_data + def get_eeprom(self): - return self._eeprom - - def get_serial(self): - return self._eeprom.get('0x23', "Undefined.") - - def get_mac(self): - return self._eeprom.get('0x24', "Undefined.") + self._eeprom = self._load_eeprom() if not self._eeprom else self._eeprom + return self._valid_tlv(self._eeprom) def get_pn(self): - return self._eeprom.get('0x21', "Undefined.") + return self.get_eeprom()['0x22'] + + def get_serial(self): + return self.get_eeprom()['0x23'] + + def get_mac(self): + return self.get_eeprom()['0x24'] + + +class DeviceEEPROM(eeprom_tlvinfo.TlvInfoDecoder): + + def __init__(self, eeprom_path, device_format, start_offset): + # Decode device eeprom as per specified format + self.format = device_format + self.start_offset = start_offset + + EepromDecoder.__init__(self, eeprom_path, self.format, + self.start_offset, '', True) + self._load_device_eeprom() + + def _load_device_eeprom(self): + """ + Reads the Fan/PSU EEPROM and interprets as per the specified format + """ + self.serial_number = 'NA' + self.model_str = 'NA' + + # device eeproms use proprietary format + try: + # Read Fan/PSU EEPROM as per the specified format. + self.eeprom_data = EepromDecoder.read_eeprom(self) + except Exception as e: + return + + if self.eeprom_data[0] == 255: + return + + (valid, data) = self._get_eeprom_field("Model") + if valid: + self.model_str = data.decode() + + try: + (valid, data) = self._get_eeprom_field("Serial Number") + if valid: + self.serial_number = data.decode() + except Exception as e: + return + + def _get_eeprom_field(self, field_name, decode=False): + """ + For a field name specified in the EEPROM format, returns the + presence of the field and the value for the same. + """ + field_start = 0 + for field in self.format: + field_end = field_start + field[2] + if field[0] == field_name: + if decode: + return (True, self.eeprom_data[field_start:field_end].decode('ascii')) + else: + return (True, self.eeprom_data[field_start:field_end]) + field_start = field_end + + return (False, None) diff --git a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/event.py b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/event.py index c8a487f383..332751386c 100644 --- a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/event.py +++ b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/event.py @@ -1,7 +1,7 @@ try: import time import select - from .helper import APIHelper + from .common import Common from sonic_py_common.logger import Logger except ImportError as e: raise ImportError(repr(e) + " - required module not found") @@ -16,12 +16,12 @@ class SfpEvent: GPIO_SUS7 = '/sys/devices/platform/hlx-ich.0/sci_int_gpio_sus7' def __init__(self, sfp_list): - self._api_helper = APIHelper() + self._api_common = Common() self._sfp_list = sfp_list self._logger = Logger() - + # clear interrupt - self._api_helper.read_one_line_file(self.INT_PATH) + self._api_common.read_one_line_file(self.INT_PATH) def get_sfp_event(self, timeout): epoll = select.epoll() @@ -37,7 +37,7 @@ class SfpEvent: events = epoll.poll(timeout=timeout_sec if timeout != 0 else -1) if events: # Read the QSFP ABS interrupt & status registers - port_changes = self._api_helper.read_one_line_file( + port_changes = self._api_common.read_one_line_file( self.INT_PATH) changes = int(port_changes, 16) for sfp in self._sfp_list: @@ -48,7 +48,8 @@ class SfpEvent: if change == 1: time.sleep(self.DELAY) port_status = sfp.get_presence() - port_dict[str(sfp.port_num)] = '1' if port_status else '0' + port_dict[str(sfp.port_num) + ] = '1' if port_status else '0' return port_dict except Exception as e: diff --git a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/fan.py b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/fan.py index c066cb9c33..90029ffce4 100644 --- a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/fan.py +++ b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/fan.py @@ -6,6 +6,7 @@ # ############################################################################# +from __future__ import division import math import os.path @@ -20,8 +21,10 @@ FAN_PATH = "/sys/devices/platform/e1031.smc/" EMC2305_MAX_PWM = 255 EMC2305_FAN_PWM = "pwm{}" EMC2305_FAN_TARGET = "fan{}_target" +EMC2305_FAN_PWM_MODE = "pwm{}_enable" EMC2305_FAN_INPUT = "pwm{}" FAN_NAME_LIST = ["FAN-1", "FAN-2", "FAN-3"] +FAN_SPEED_TOLERANCE = 10 PSU_FAN_MAX_RPM = 11000 PSU_HWMON_PATH = "/sys/bus/i2c/devices/i2c-{0}/{0}-00{1}/hwmon" PSU_I2C_MAPPING = { @@ -34,12 +37,16 @@ PSU_I2C_MAPPING = { "addr": "5a" }, } +NULL_VAL = 'N/A' class Fan(FanBase): """Platform-specific Fan class""" def __init__(self, fan_tray_index, fan_index=0, is_psu_fan=False, psu_index=0): + FanBase.__init__(self) + + self._api_common = Common() self.fan_index = fan_index self.fan_tray_index = fan_tray_index self.is_psu_fan = is_psu_fan @@ -64,13 +71,10 @@ class Fan(FanBase): self.fan_e1031_led = "fan{}_led" self.fan_e1031_led_col_map = { self.STATUS_LED_COLOR_GREEN: "green", - self.STATUS_LED_COLOR_RED: "amber", + self.STATUS_LED_COLOR_AMBER: "amber", self.STATUS_LED_COLOR_OFF: "off" } - self._api_common = Common() - FanBase.__init__(self) - def __search_file_by_name(self, directory, file_name): for dirpath, dirnames, files in os.walk(directory): for name in files: @@ -141,19 +145,26 @@ class Fan(FanBase): 0 : when PWM mode is use pwm : when pwm mode is not use """ - target = 0 + target = NULL_VAL if not self.is_psu_fan: chip = self.emc2305_chip_mapping[self.fan_index] device = chip['device'] fan_index = chip['index_map'] - sysfs_path = "%s%s/%s" % ( - EMC2305_PATH, device, EMC2305_FAN_TARGET) - sysfs_path = sysfs_path.format(fan_index[self.fan_tray_index]) - raw = self._api_common.read_txt_file(sysfs_path).strip('\r\n') + + enable_path = "%s%s/%s" % ( + EMC2305_PATH, device, EMC2305_FAN_PWM_MODE) + enable = self._api_common.read_txt_file( + enable_path.format(fan_index[self.fan_tray_index])) + + target_mode = EMC2305_FAN_TARGET if enable != "0" else EMC2305_FAN_PWM + target_path = "%s%s/%s" % (EMC2305_PATH, device, + target_mode.format(fan_index[self.fan_tray_index])) + + raw = self._api_common.read_txt_file(target_path) pwm = int(raw, 10) if raw else 0 target = math.ceil(float(pwm) * 100 / EMC2305_MAX_PWM) - return target + return int(target) def get_speed_tolerance(self): """ @@ -162,7 +173,7 @@ class Fan(FanBase): An integer, the percentage of variance from target speed which is considered tolerable """ - return 10 + return FAN_SPEED_TOLERANCE def set_speed(self, speed): """ @@ -211,6 +222,28 @@ class Fan(FanBase): return set_status_led + def get_status_led(self): + """ + Gets the state of the fan status LED + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings above + """ + led = self.STATUS_LED_COLOR_GREEN + if not self.is_psu_fan: + fan_led_file = (FAN_PATH + + self.fan_e1031_led.format(self.fan_tray_index+1)) + + led = self._api_common.read_txt_file(fan_led_file) + return { + 'green': self.STATUS_LED_COLOR_GREEN, + 'off': self.STATUS_LED_COLOR_OFF, + 'amber': self.STATUS_LED_COLOR_AMBER + }.get(led, self.STATUS_LED_COLOR_OFF) + + ############################################################## + ###################### Device methods ######################## + ############################################################## + def get_name(self): """ Retrieves the name of the device @@ -240,11 +273,7 @@ class Fan(FanBase): Returns: string: Model/part number of device """ - if self.is_psu_fan: - return NULL_VAL - - model = NULL_VAL - return model + return NULL_VAL def get_serial(self): """ @@ -252,11 +281,7 @@ class Fan(FanBase): Returns: string: Serial number of device """ - if self.is_psu_fan: - return NULL_VAL - - serial = NULL_VAL - return serial + return NULL_VAL def get_status(self): """ @@ -269,7 +294,7 @@ class Fan(FanBase): fan_fault_sysfs_name = "fan1_fault" fan_fault_sysfs_path = self.__search_file_by_name( self.psu_hwmon_path, fan_fault_sysfs_name) - status = self._api_common.read_txt_file(fan_fault_sysfs_path) + status = self._api_common.read_one_line_file(fan_fault_sysfs_path) elif self.get_presence(): chip = self.emc2305_chip_mapping[self.fan_index] @@ -278,7 +303,27 @@ class Fan(FanBase): sysfs_path = "%s%s/%s" % ( EMC2305_PATH, device, 'fan{}_fault') sysfs_path = sysfs_path.format(fan_index[self.fan_tray_index]) - status = self._api_common.read_txt_file(sysfs_path) + status = self._api_common.read_one_line_file(sysfs_path) return False if int(status) != 0 else True + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + If the agent cannot determine the parent-relative position + for some reason, or if the associated value of + entPhysicalContainedIn is'0', then the value '-1' is returned + Returns: + integer: The 1-based relative physical position in parent device + or -1 if cannot determine the position + """ + return (self.fan_tray_index*2 + self.fan_index + 1) \ + if not self.is_psu_fan else (self.fan_index+1) + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True if not self.is_psu_fan else False diff --git a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/fan_drawer.py b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/fan_drawer.py new file mode 100644 index 0000000000..fe12b0ab10 --- /dev/null +++ b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/fan_drawer.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica +# +# Module contains an implementation of SONiC Platform Base API and +# provides the the Fan-Drawers' information available in the platform +# +############################################################################# + +try: + from sonic_platform_base.fan_drawer_base import FanDrawerBase +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +NUM_FAN = 1 + + +class FanDrawer(FanDrawerBase): + def __init__(self, fantray_index): + FanDrawerBase.__init__(self) + self._index = fantray_index + 1 + self._init_fan(fantray_index) + + def _init_fan(self, fantray_index): + from sonic_platform.fan import Fan + for index in range(NUM_FAN): + fan = Fan(fantray_index, index) + self._fan_list.append(fan) + + def set_status_led(self, color): + """ + Sets the state of the fan drawer status LED + Args: + color: A string representing the color with which to set the + fan drawer status LED + Returns: + bool: True if status LED state is set successfully, False if not + """ + return self._fan_list[0].set_status_led(color) + + def get_status_led(self, color=None): + """ + Gets the state of the fan drawer LED + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings above + """ + return self._fan_list[0].get_status_led() + + ############################################################## + ###################### Device methods ######################## + ############################################################## + + def get_name(self): + """ + Retrieves the name of the device + Returns: + string: The name of the device + """ + return "Drawer{}".format(self._index) + + def get_presence(self): + """ + Retrieves the presence of the device + Returns: + bool: True if device is present, False if not + """ + return self._fan_list[0].get_presence() + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + return self._fan_list[0].get_model() + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + return self._fan_list[0].get_serial() + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + return self._fan_list[0].get_status() + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device + Returns: + integer: The 1-based relative physical position in parent device + """ + return self._index + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True diff --git a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/helper.py b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/helper.py deleted file mode 100644 index 75449ab551..0000000000 --- a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/helper.py +++ /dev/null @@ -1,133 +0,0 @@ -import os -import struct -import subprocess -from mmap import * - -from sonic_py_common import device_info - -HOST_CHK_CMD = "docker > /dev/null 2>&1" -EMPTY_STRING = "" - - -class APIHelper(): - - def __init__(self): - (self.platform, self.hwsku) = device_info.get_platform_and_hwsku() - - def is_host(self): - return os.system(HOST_CHK_CMD) == 0 - - def pci_get_value(self, resource, offset): - status = True - result = "" - try: - fd = os.open(resource, os.O_RDWR) - mm = mmap(fd, 0) - mm.seek(int(offset)) - read_data_stream = mm.read(4) - result = struct.unpack('I', read_data_stream) - except Exception: - status = False - return status, result - - def run_command(self, cmd): - status = True - result = "" - try: - p = subprocess.Popen( - cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - raw_data, err = p.communicate() - if err == '': - result = raw_data.strip() - except Exception: - status = False - return status, result - - def run_interactive_command(self, cmd): - try: - os.system(cmd) - except Exception: - return False - return True - - def read_txt_file(self, file_path): - try: - with open(file_path, 'r') as fd: - data = fd.read() - return data.strip() - except IOError: - pass - return None - - def read_one_line_file(self, file_path): - try: - with open(file_path, 'r') as fd: - data = fd.readline() - return data.strip() - except IOError: - pass - return None - - def write_txt_file(self, file_path, value): - try: - with open(file_path, 'w') as fd: - fd.write(str(value)) - except Exception: - return False - return True - - def get_cpld_reg_value(self, getreg_path, register): - cmd = "echo {1} > {0}; cat {0}".format(getreg_path, register) - status, result = self.run_command(cmd) - return result if status else None - - def ipmi_raw(self, netfn, cmd): - status = True - result = "" - try: - cmd = "ipmitool raw {} {}".format(str(netfn), str(cmd)) - p = subprocess.Popen( - cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - raw_data, err = p.communicate() - if err == '': - result = raw_data.strip() - else: - status = False - except Exception: - status = False - return status, result - - def ipmi_fru_id(self, id, key=None): - status = True - result = "" - try: - cmd = "ipmitool fru print {}".format(str( - id)) if not key else "ipmitool fru print {0} | grep '{1}' ".format(str(id), str(key)) - - p = subprocess.Popen( - cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - raw_data, err = p.communicate() - if err == '': - result = raw_data.strip() - else: - status = False - except Exception: - status = False - return status, result - - def ipmi_set_ss_thres(self, id, threshold_key, value): - status = True - result = "" - try: - cmd = "ipmitool sensor thresh '{}' {} {}".format( - str(id), str(threshold_key), str(value)) - p = subprocess.Popen( - cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - raw_data, err = p.communicate() - if err == '': - result = raw_data.strip() - else: - status = False - except Exception: - status = False - return status, result diff --git a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/psu.py b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/psu.py index ccc63c8ac9..1360108022 100644 --- a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/psu.py +++ b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/psu.py @@ -11,10 +11,13 @@ import os.path try: from sonic_platform_base.psu_base import PsuBase from sonic_platform.fan import Fan + from .common import Common + from .eeprom import DeviceEEPROM except ImportError as e: raise ImportError(str(e) + "- required module not found") FAN_E1031_SPEED_PATH = "/sys/class/hwmon/hwmon{}/fan1_input" +PSU_E1031_STAT_PATH = "/sys/devices/platform/e1031.smc/" HWMON_PATH = "/sys/bus/i2c/devices/i2c-{0}/{0}-00{1}/hwmon" FAN_MAX_RPM = 11000 PSU_NAME_LIST = ["PSU-R", "PSU-L"] @@ -22,13 +25,22 @@ PSU_NUM_FAN = [1, 1] PSU_I2C_MAPPING = { 0: { "num": 13, - "addr": "5b" + "addr": "5b", + "eeprom_addr": "53" }, 1: { "num": 12, - "addr": "5a" + "addr": "5a", + "eeprom_addr": "52" }, } +PSU_EEPROM_PATH = "/sys/bus/i2c/devices/{}-00{}/eeprom" +PSU_EEPROM_FORMAT = [ + ('Serial Number', 's', 16), ('burn', 'x', 16), + ('Model', 's', 16), + ('Part Number', 's', 16), +] +PSU_EEPROM_START_OFFSET = 48 class Psu(PsuBase): @@ -36,32 +48,26 @@ class Psu(PsuBase): def __init__(self, psu_index): PsuBase.__init__(self) + + self._api_common = Common() self.index = psu_index - self.psu_path = "/sys/devices/platform/e1031.smc/" self.psu_presence = "psu{}_prs" self.psu_oper_status = "psu{}_status" self.i2c_num = PSU_I2C_MAPPING[self.index]["num"] self.i2c_addr = PSU_I2C_MAPPING[self.index]["addr"] self.hwmon_path = HWMON_PATH.format(self.i2c_num, self.i2c_addr) + self.eeprom_addr = PSU_EEPROM_PATH.format( + self.i2c_num, PSU_I2C_MAPPING[self.index]["eeprom_addr"]) + for fan_index in range(0, PSU_NUM_FAN[self.index]): fan = Fan(fan_index, 0, is_psu_fan=True, psu_index=self.index) self._fan_list.append(fan) - PsuBase.__init__(self) - def __read_txt_file(self, file_path): - try: - with open(file_path, 'r') as fd: - data = fd.read() - return data.strip() - except IOError: - pass - return "" - - def __search_file_by_contain(self, directory, search_str, file_start): + def _search_file_by_contain(self, directory, search_str, file_start): for dirpath, dirnames, files in os.walk(directory): for name in files: file_path = os.path.join(dirpath, name) - if name.startswith(file_start) and search_str in self.__read_txt_file(file_path): + if name.startswith(file_start) and search_str in self._api_common.read_txt_file(file_path): return file_path return None @@ -76,7 +82,7 @@ class Psu(PsuBase): voltage_name = "in{}_input" voltage_label = "vout1" - vout_label_path = self.__search_file_by_contain( + vout_label_path = self._search_file_by_contain( self.hwmon_path, voltage_label, "in") if vout_label_path: dir_name = os.path.dirname(vout_label_path) @@ -84,7 +90,7 @@ class Psu(PsuBase): in_num = ''.join(list(filter(str.isdigit, basename))) vout_path = os.path.join( dir_name, voltage_name.format(in_num)) - vout_val = self.__read_txt_file(vout_path) + vout_val = self._api_common.read_txt_file(vout_path) psu_voltage = float(vout_val) / 1000 return psu_voltage @@ -99,7 +105,7 @@ class Psu(PsuBase): current_name = "curr{}_input" current_label = "iout1" - curr_label_path = self.__search_file_by_contain( + curr_label_path = self._search_file_by_contain( self.hwmon_path, current_label, "cur") if curr_label_path: dir_name = os.path.dirname(curr_label_path) @@ -107,7 +113,7 @@ class Psu(PsuBase): cur_num = ''.join(list(filter(str.isdigit, basename))) cur_path = os.path.join( dir_name, current_name.format(cur_num)) - cur_val = self.__read_txt_file(cur_path) + cur_val = self._api_common.read_txt_file(cur_path) psu_current = float(cur_val) / 1000 return psu_current @@ -122,7 +128,7 @@ class Psu(PsuBase): current_name = "power{}_input" current_label = "pout1" - pw_label_path = self.__search_file_by_contain( + pw_label_path = self._search_file_by_contain( self.hwmon_path, current_label, "power") if pw_label_path: dir_name = os.path.dirname(pw_label_path) @@ -130,7 +136,7 @@ class Psu(PsuBase): pw_num = ''.join(list(filter(str.isdigit, basename))) pw_path = os.path.join( dir_name, current_name.format(pw_num)) - pw_val = self.__read_txt_file(pw_path) + pw_val = self._api_common.read_txt_file(pw_path) psu_power = float(pw_val) / 1000000 return psu_power @@ -165,6 +171,10 @@ class Psu(PsuBase): # Hardware not supported return self.STATUS_LED_COLOR_OFF + ############################################################## + ###################### Device methods ######################## + ############################################################## + def get_name(self): """ Retrieves the name of the device @@ -180,8 +190,8 @@ class Psu(PsuBase): bool: True if PSU is present, False if not """ psu_location = ["R", "L"] - presences_status = self.__read_txt_file( - self.psu_path + self.psu_presence.format(psu_location[self.index])) or 0 + presences_status = self._api_common.read_txt_file( + PSU_E1031_STAT_PATH + self.psu_presence.format(psu_location[self.index])) or 0 return int(presences_status) == 1 @@ -192,7 +202,153 @@ class Psu(PsuBase): A boolean value, True if device is operating properly, False if not """ psu_location = ["R", "L"] - power_status = self.__read_txt_file( - self.psu_path + self.psu_oper_status.format(psu_location[self.index])) or 0 + power_status = self._api_common.read_txt_file( + PSU_E1031_STAT_PATH + self.psu_oper_status.format(psu_location[self.index])) or 0 return int(power_status) == 1 + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + self._eeprom = DeviceEEPROM( + self.eeprom_addr, PSU_EEPROM_FORMAT, PSU_EEPROM_START_OFFSET) + return self._eeprom.model_str + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + self._eeprom = DeviceEEPROM( + self.eeprom_addr, PSU_EEPROM_FORMAT, PSU_EEPROM_START_OFFSET) + return self._eeprom.serial_number + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. If the agent cannot determine the parent-relative position + for some reason, or if the associated value of entPhysicalContainedIn is '0', then the value '-1' is returned + Returns: + integer: The 1-based relative physical position in parent device or -1 if cannot determine the position + """ + return -1 + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True + + def get_temperature(self): + """ + Retrieves current temperature reading from PSU + Returns: + A float number of current temperature in Celsius up to nearest thousandth + of one degree Celsius, e.g. 30.125 + there are three temp sensors , we choose one of them + """ + psu_temperature = None + temperature_name = "temp{}_input" + temperature_label = "vout1" + + vout_label_path = self._search_file_by_contain( + self.hwmon_path, temperature_label, "in") + if vout_label_path: + dir_name = os.path.dirname(vout_label_path) + basename = os.path.basename(vout_label_path) + in_num = ''.join(list(filter(str.isdigit, basename))) + temp_path = os.path.join( + dir_name, temperature_name.format(in_num)) + vout_val = self._api_common.read_txt_file(temp_path) + psu_temperature = float(vout_val) / 1000 + + return psu_temperature + + def get_temperature_high_threshold(self): + """ + Retrieves the high threshold temperature of PSU + Returns: + A float number, the high threshold temperature of PSU in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + there are three temp sensors , we choose one of them + """ + psu_temperature = None + temperature_name = "temp{}_max" + temperature_label = "vout1" + + vout_label_path = self._search_file_by_contain( + self.hwmon_path, temperature_label, "in") + if vout_label_path: + dir_name = os.path.dirname(vout_label_path) + basename = os.path.basename(vout_label_path) + in_num = ''.join(list(filter(str.isdigit, basename))) + temp_path = os.path.join( + dir_name, temperature_name.format(in_num)) + vout_val = self._api_common.read_txt_file(temp_path) + psu_temperature = float(vout_val) / 1000 + + return psu_temperature + + def get_voltage_high_threshold(self): + """ + Retrieves the high threshold PSU voltage output + Returns: + A float number, the high threshold output voltage in volts, + e.g. 12.1 + """ + psu_voltage = 12.6 + voltage_name = "in{}_crit" + voltage_label = "vout1" + + vout_label_path = self._search_file_by_contain( + self.hwmon_path, voltage_label, "in") + if vout_label_path: + dir_name = os.path.dirname(vout_label_path) + basename = os.path.basename(vout_label_path) + in_num = ''.join(list(filter(str.isdigit, basename))) + vout_path = os.path.join( + dir_name, voltage_name.format(in_num)) + if os.path.exists(vout_path): + vout_val = self._api_common.read_txt_file(vout_path) + psu_voltage = float(vout_val) / 1000 + + return psu_voltage + + def get_voltage_low_threshold(self): + """ + Retrieves the low threshold PSU voltage output + Returns: + A float number, the low threshold output voltage in volts, + e.g. 12.1 + """ + psu_voltage = 11.4 + voltage_name = "in{}_lcrit" + voltage_label = "vout1" + + vout_label_path = self._search_file_by_contain( + self.hwmon_path, voltage_label, "in") + if vout_label_path: + dir_name = os.path.dirname(vout_label_path) + basename = os.path.basename(vout_label_path) + in_num = ''.join(list(filter(str.isdigit, basename))) + vout_path = os.path.join( + dir_name, voltage_name.format(in_num)) + if os.path.exists(vout_path): + vout_val = self._api_common.read_txt_file(vout_path) + psu_voltage = float(vout_val) / 1000 + + return psu_voltage + + def get_maximum_supplied_power(self): + """ + Retrieves the maximum supplied power by PSU + Returns: + A float number, the maximum power output in Watts. + e.g. 1200.1 + """ + return 200.0 diff --git a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/sfp.py b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/sfp.py index 91e5fec65f..157b06de1c 100644 --- a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/sfp.py +++ b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/sfp.py @@ -6,23 +6,44 @@ # ############################################################################# -import os -import time -from ctypes import create_string_buffer - try: + import time + from ctypes import create_string_buffer from sonic_platform_base.sfp_base import SfpBase - from sonic_platform_base.sonic_sfp.sff8472 import sff8472Dom from sonic_platform_base.sonic_sfp.sff8472 import sff8472InterfaceId + from sonic_platform_base.sonic_sfp.sff8472 import sff8472Dom from sonic_platform_base.sonic_sfp.sff8472 import sffbase + from sonic_platform_base.sonic_sfp.sff8436 import sff8436InterfaceId + from sonic_platform_base.sonic_sfp.sff8436 import sff8436Dom + from sonic_platform_base.sonic_sfp.inf8628 import inf8628InterfaceId + from .common import Common except ImportError as e: raise ImportError(str(e) + "- required module not found") -INFO_OFFSET = 0 -DOM_OFFSET = 256 +INFO_OFFSET = 128 +DOM_OFFSET = 0 +# definitions of the offset and width for values in XCVR info eeprom XCVR_INTFACE_BULK_OFFSET = 0 +XCVR_INTFACE_BULK_WIDTH_QSFP = 20 XCVR_INTFACE_BULK_WIDTH_SFP = 21 +XCVR_TYPE_OFFSET = 0 +XCVR_TYPE_WIDTH = 1 +XCVR_EXT_TYPE_OFFSET = 1 +XCVR_EXT_TYPE_WIDTH = 1 +XCVR_CONNECTOR_OFFSET = 2 +XCVR_CONNECTOR_WIDTH = 1 +XCVR_COMPLIANCE_CODE_OFFSET = 3 +XCVR_COMPLIANCE_CODE_WIDTH = 8 +XCVR_ENCODING_OFFSET = 11 +XCVR_ENCODING_WIDTH = 1 +XCVR_NBR_OFFSET = 12 +XCVR_NBR_WIDTH = 1 +XCVR_EXT_RATE_SEL_OFFSET = 13 +XCVR_EXT_RATE_SEL_WIDTH = 1 +XCVR_CABLE_LENGTH_OFFSET = 14 +XCVR_CABLE_LENGTH_WIDTH_QSFP = 5 +XCVR_CABLE_LENGTH_WIDTH_SFP = 6 XCVR_VENDOR_NAME_OFFSET = 20 XCVR_VENDOR_NAME_WIDTH = 16 XCVR_VENDOR_OUI_OFFSET = 37 @@ -30,30 +51,86 @@ XCVR_VENDOR_OUI_WIDTH = 3 XCVR_VENDOR_PN_OFFSET = 40 XCVR_VENDOR_PN_WIDTH = 16 XCVR_HW_REV_OFFSET = 56 +XCVR_HW_REV_WIDTH_OSFP = 2 +XCVR_HW_REV_WIDTH_QSFP = 2 XCVR_HW_REV_WIDTH_SFP = 4 XCVR_VENDOR_SN_OFFSET = 68 XCVR_VENDOR_SN_WIDTH = 16 XCVR_VENDOR_DATE_OFFSET = 84 XCVR_VENDOR_DATE_WIDTH = 8 XCVR_DOM_CAPABILITY_OFFSET = 92 -XCVR_DOM_CAPABILITY_WIDTH = 1 +XCVR_DOM_CAPABILITY_WIDTH = 2 + +XCVR_INTERFACE_DATA_START = 0 +XCVR_INTERFACE_DATA_SIZE = 92 + +QSFP_DOM_BULK_DATA_START = 22 +QSFP_DOM_BULK_DATA_SIZE = 36 +SFP_DOM_BULK_DATA_START = 96 +SFP_DOM_BULK_DATA_SIZE = 10 + +# definitions of the offset for values in OSFP info eeprom +OSFP_TYPE_OFFSET = 0 +OSFP_VENDOR_NAME_OFFSET = 129 +OSFP_VENDOR_PN_OFFSET = 148 +OSFP_HW_REV_OFFSET = 164 +OSFP_VENDOR_SN_OFFSET = 166 + +# Offset for values in QSFP eeprom +QSFP_DOM_REV_OFFSET = 1 +QSFP_DOM_REV_WIDTH = 1 +QSFP_TEMPE_OFFSET = 22 +QSFP_TEMPE_WIDTH = 2 +QSFP_VOLT_OFFSET = 26 +QSFP_VOLT_WIDTH = 2 +QSFP_VERSION_COMPLIANCE_OFFSET = 1 +QSFP_VERSION_COMPLIANCE_WIDTH = 2 +QSFP_CHANNL_MON_OFFSET = 34 +QSFP_CHANNL_MON_WIDTH = 16 +QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH = 24 +QSFP_CHANNL_DISABLE_STATUS_OFFSET = 86 +QSFP_CHANNL_DISABLE_STATUS_WIDTH = 1 +QSFP_CHANNL_RX_LOS_STATUS_OFFSET = 3 +QSFP_CHANNL_RX_LOS_STATUS_WIDTH = 1 +QSFP_CHANNL_TX_FAULT_STATUS_OFFSET = 4 +QSFP_CHANNL_TX_FAULT_STATUS_WIDTH = 1 +QSFP_CONTROL_OFFSET = 86 +QSFP_CONTROL_WIDTH = 8 +QSFP_MODULE_MONITOR_OFFSET = 0 +QSFP_MODULE_MONITOR_WIDTH = 9 +QSFP_POWEROVERRIDE_OFFSET = 93 +QSFP_POWEROVERRIDE_WIDTH = 1 +QSFP_POWEROVERRIDE_BIT = 0 +QSFP_POWERSET_BIT = 1 +QSFP_OPTION_VALUE_OFFSET = 192 +QSFP_OPTION_VALUE_WIDTH = 4 +QSFP_MODULE_UPPER_PAGE3_START = 384 +QSFP_MODULE_THRESHOLD_OFFSET = 128 +QSFP_MODULE_THRESHOLD_WIDTH = 24 +QSFP_CHANNL_THRESHOLD_OFFSET = 176 +QSFP_CHANNL_THRESHOLD_WIDTH = 24 + +SFP_MODULE_ADDRA2_OFFSET = 256 +SFP_MODULE_THRESHOLD_OFFSET = 0 +SFP_MODULE_THRESHOLD_WIDTH = 56 +SFP_CHANNL_THRESHOLD_OFFSET = 112 +SFP_CHANNL_THRESHOLD_WIDTH = 2 -# Offset for values in SFP eeprom SFP_TEMPE_OFFSET = 96 SFP_TEMPE_WIDTH = 2 SFP_VOLT_OFFSET = 98 SFP_VOLT_WIDTH = 2 SFP_CHANNL_MON_OFFSET = 100 SFP_CHANNL_MON_WIDTH = 6 -SFP_MODULE_THRESHOLD_OFFSET = 0 -SFP_MODULE_THRESHOLD_WIDTH = 40 -SFP_CHANNL_THRESHOLD_OFFSET = 112 -SFP_CHANNL_THRESHOLD_WIDTH = 2 -SFP_STATUS_CONTROL_OFFSET = 110 -SFP_STATUS_CONTROL_WIDTH = 1 +SFP_CHANNL_STATUS_OFFSET = 110 +SFP_CHANNL_STATUS_WIDTH = 1 SFP_TX_DISABLE_HARD_BIT = 7 SFP_TX_DISABLE_SOFT_BIT = 6 +qsfp_cable_length_tup = ('Length(km)', 'Length OM3(2m)', + 'Length OM2(m)', 'Length OM1(m)', + 'Length Cable Assembly(m)') + sfp_cable_length_tup = ('LengthSMFkm-UnitsOfKm', 'LengthSMF(UnitsOf100m)', 'Length50um(UnitsOf10m)', 'Length62.5um(UnitsOfm)', 'LengthCable(UnitsOfm)', 'LengthOM3(UnitsOf10m)') @@ -64,44 +141,63 @@ sfp_compliance_code_tup = ('10GEthernetComplianceCode', 'InfinibandComplianceCod 'FibreChannelTechnology', 'SFP+CableTechnology', 'FibreChannelTransmissionMedia', 'FibreChannelSpeed') +qsfp_compliance_code_tup = ('10/40G Ethernet Compliance Code', 'SONET Compliance codes', + 'SAS/SATA compliance codes', 'Gigabit Ethernet Compliant codes', + 'Fibre Channel link length/Transmitter Technology', + 'Fibre Channel transmission media', 'Fibre Channel Speed') + +SFP_TYPE = "SFP" +QSFP_TYPE = "QSFP" +OSFP_TYPE = "OSFP" +ETP_TYPE = "ETP" + +SFP_PORT_START = 49 +SFP_PORT_END = 52 + +PORT_START = 1 +PORT_END = 55 + class Sfp(SfpBase): """Platform-specific Sfp class""" - # Port number - PORT_START = 1 - PORT_END = 52 + # Port I2C number port_to_i2c_mapping = { 49: 15, 50: 14, 51: 17, 52: 16 } - _sfp_port = list(range(49, PORT_END + 1)) - PRS_PATH = "/sys/devices/platform/e1031.smc/SFP/sfp_modabs" - PLATFORM_ROOT_PATH = '/usr/share/sonic/device' - PMON_HWSKU_PATH = '/usr/share/sonic/hwsku' - HOST_CHK_CMD = "docker > /dev/null 2>&1" + _sfp_port = list(range(SFP_PORT_START, SFP_PORT_END + 1)) - PLATFORM = "x86_64-cel_e1031-r0" - HWSKU = "Celestica-E1031-T48S4" + PRS_PATH = "/sys/devices/platform/e1031.smc/SFP/sfp_modabs" def __init__(self, sfp_index, sfp_name): + SfpBase.__init__(self) + + # Init common function + self._api_common = Common() + # Init index self.index = sfp_index self.port_num = self.index + 1 + # Init sfp data + self.sfp_type = self.__get_sfp_type() + self.name = sfp_name + # Init eeprom path eeprom_path = '/sys/bus/i2c/devices/i2c-{0}/{0}-0050/eeprom' self.port_to_eeprom_mapping = {} - for x in range(self.PORT_START, self.PORT_END + 1): + for x in range(PORT_START, PORT_END + 1): if x not in self._sfp_port: self.port_to_i2c_mapping[x] = None self.port_to_eeprom_mapping[x] = eeprom_path.format( self.port_to_i2c_mapping[x]) self.info_dict_keys = ['type', 'hardware_rev', 'serial', 'manufacturer', 'model', 'connector', 'encoding', 'ext_identifier', - 'ext_rateselect_compliance', 'cable_type', 'cable_length', 'nominal_bit_rate', 'specification_compliance', 'vendor_date', 'vendor_oui'] + 'ext_rateselect_compliance', 'cable_type', 'cable_length', 'nominal_bit_rate', 'specification_compliance', + 'vendor_date', 'vendor_oui', "application_advertisement", "type_abbrv_name"] self.dom_dict_keys = ['rx_los', 'tx_fault', 'reset_status', 'power_lpmode', 'tx_disable', 'tx_disable_channel', 'temperature', 'voltage', 'rx1power', 'rx2power', 'rx3power', 'rx4power', 'tx1bias', 'tx2bias', 'tx3bias', 'tx4bias', 'tx1power', 'tx2power', 'tx3power', 'tx4power'] @@ -109,10 +205,12 @@ class Sfp(SfpBase): self.threshold_dict_keys = ['temphighalarm', 'temphighwarning', 'templowalarm', 'templowwarning', 'vcchighalarm', 'vcchighwarning', 'vcclowalarm', 'vcclowwarning', 'rxpowerhighalarm', 'rxpowerhighwarning', 'rxpowerlowalarm', 'rxpowerlowwarning', 'txpowerhighalarm', 'txpowerhighwarning', 'txpowerlowalarm', 'txpowerlowwarning', 'txbiashighalarm', 'txbiashighwarning', 'txbiaslowalarm', 'txbiaslowwarning'] - self.name = sfp_name - SfpBase.__init__(self) + self._dom_capability_detect() - def _convert_string_to_num(self, value_str): + def __get_sfp_type(self): + return SFP_TYPE if self.port_num in self._sfp_port else ETP_TYPE + + def __convert_string_to_num(self, value_str): if "-inf" in value_str: return 'N/A' elif "Unknown" in value_str: @@ -132,18 +230,6 @@ class Sfp(SfpBase): else: return 'N/A' - def __read_txt_file(self, file_path): - try: - with open(file_path, 'r') as fd: - data = fd.read() - return data.strip() - except IOError: - pass - return "" - - def __is_host(self): - return os.system(self.HOST_CHK_CMD) == 0 - def __read_eeprom_specific_bytes(self, offset, num_bytes): sysfsfile_eeprom = None eeprom_raw = [] @@ -156,9 +242,13 @@ class Sfp(SfpBase): sysfs_sfp_i2c_client_eeprom_path, mode="rb", buffering=0) sysfsfile_eeprom.seek(offset) raw = sysfsfile_eeprom.read(num_bytes) - for n in range(0, num_bytes): - eeprom_raw[n] = hex(ord(raw[n]))[2:].zfill(2) - except Exception: + if isinstance(raw, str): + for n in range(0, num_bytes): + eeprom_raw[n] = hex(ord(raw[n]))[2:].zfill(2) + else: + for n in range(0, num_bytes): + eeprom_raw[n] = hex(raw[n])[2:].zfill(2) + except BaseException: pass finally: if sysfsfile_eeprom: @@ -166,6 +256,106 @@ class Sfp(SfpBase): return eeprom_raw + def _dom_capability_detect(self): + if not self.get_presence(): + self.dom_supported = False + self.dom_temp_supported = False + self.dom_volt_supported = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + self.calibration = 0 + return + + if self.sfp_type == "QSFP": + self.calibration = 1 + sfpi_obj = sff8436InterfaceId() + if sfpi_obj is None: + self.dom_supported = False + offset = 128 + + # QSFP capability byte parse, through this byte can know whether it support tx_power or not. + # TODO: in the future when decided to migrate to support SFF-8636 instead of SFF-8436, + # need to add more code for determining the capability and version compliance + # in SFF-8636 dom capability definitions evolving with the versions. + qsfp_dom_capability_raw = self.__read_eeprom_specific_bytes( + (offset + XCVR_DOM_CAPABILITY_OFFSET), XCVR_DOM_CAPABILITY_WIDTH) + if qsfp_dom_capability_raw is not None: + qsfp_version_compliance_raw = self.__read_eeprom_specific_bytes( + QSFP_VERSION_COMPLIANCE_OFFSET, QSFP_VERSION_COMPLIANCE_WIDTH) + qsfp_version_compliance = int( + qsfp_version_compliance_raw[0], 16) + dom_capability = sfpi_obj.parse_dom_capability( + qsfp_dom_capability_raw, 0) + if qsfp_version_compliance >= 0x08: + self.dom_temp_supported = dom_capability['data']['Temp_support']['value'] == 'On' + self.dom_volt_supported = dom_capability['data']['Voltage_support']['value'] == 'On' + self.dom_rx_power_supported = dom_capability['data']['Rx_power_support']['value'] == 'On' + self.dom_tx_power_supported = dom_capability['data']['Tx_power_support']['value'] == 'On' + else: + self.dom_temp_supported = True + self.dom_volt_supported = True + self.dom_rx_power_supported = dom_capability['data']['Rx_power_support']['value'] == 'On' + self.dom_tx_power_supported = True + + self.dom_supported = True + self.calibration = 1 + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return None + qsfp_option_value_raw = self.__read_eeprom_specific_bytes( + QSFP_OPTION_VALUE_OFFSET, QSFP_OPTION_VALUE_WIDTH) + if qsfp_option_value_raw is not None: + optional_capability = sfpd_obj.parse_option_params( + qsfp_option_value_raw, 0) + self.dom_tx_disable_supported = optional_capability[ + 'data']['TxDisable']['value'] == 'On' + dom_status_indicator = sfpd_obj.parse_dom_status_indicator( + qsfp_version_compliance_raw, 1) + self.qsfp_page3_available = dom_status_indicator['data']['FlatMem']['value'] == 'Off' + else: + self.dom_supported = False + self.dom_temp_supported = False + self.dom_volt_supported = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + self.calibration = 0 + self.qsfp_page3_available = False + + elif self.sfp_type == "SFP": + sfpi_obj = sff8472InterfaceId() + if sfpi_obj is None: + return None + sfp_dom_capability_raw = self.__read_eeprom_specific_bytes( + XCVR_DOM_CAPABILITY_OFFSET, XCVR_DOM_CAPABILITY_WIDTH) + if sfp_dom_capability_raw is not None: + sfp_dom_capability = int(sfp_dom_capability_raw[0], 16) + self.dom_supported = (sfp_dom_capability & 0x40 != 0) + if self.dom_supported: + self.dom_temp_supported = True + self.dom_volt_supported = True + self.dom_rx_power_supported = True + self.dom_tx_power_supported = True + if sfp_dom_capability & 0x20 != 0: + self.calibration = 1 + elif sfp_dom_capability & 0x10 != 0: + self.calibration = 2 + else: + self.calibration = 0 + else: + self.dom_temp_supported = False + self.dom_volt_supported = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + self.calibration = 0 + self.dom_tx_disable_supported = ( + int(sfp_dom_capability_raw[1], 16) & 0x40 != 0) + else: + self.dom_supported = False + self.dom_temp_supported = False + self.dom_volt_supported = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + def get_transceiver_info(self): """ Retrieves transceiver info of this SFP @@ -189,85 +379,183 @@ class Sfp(SfpBase): vendor_date |1*255VCHAR |vendor date vendor_oui |1*255VCHAR |vendor OUI ======================================================================== - """ - # check present status - sfpi_obj = sff8472InterfaceId() - if not self.get_presence() or not sfpi_obj: - return {} - - offset = INFO_OFFSET - - sfp_interface_bulk_raw = self.__read_eeprom_specific_bytes( - (offset + XCVR_INTFACE_BULK_OFFSET), XCVR_INTFACE_BULK_WIDTH_SFP) - sfp_interface_bulk_data = sfpi_obj.parse_sfp_info_bulk( - sfp_interface_bulk_raw, 0) - - sfp_vendor_name_raw = self.__read_eeprom_specific_bytes( - (offset + XCVR_VENDOR_NAME_OFFSET), XCVR_VENDOR_NAME_WIDTH) - sfp_vendor_name_data = sfpi_obj.parse_vendor_name( - sfp_vendor_name_raw, 0) - - sfp_vendor_pn_raw = self.__read_eeprom_specific_bytes( - (offset + XCVR_VENDOR_PN_OFFSET), XCVR_VENDOR_PN_WIDTH) - sfp_vendor_pn_data = sfpi_obj.parse_vendor_pn( - sfp_vendor_pn_raw, 0) - - sfp_vendor_rev_raw = self.__read_eeprom_specific_bytes( - (offset + XCVR_HW_REV_OFFSET), XCVR_HW_REV_WIDTH_SFP) - sfp_vendor_rev_data = sfpi_obj.parse_vendor_rev( - sfp_vendor_rev_raw, 0) - - sfp_vendor_sn_raw = self.__read_eeprom_specific_bytes( - (offset + XCVR_VENDOR_SN_OFFSET), XCVR_VENDOR_SN_WIDTH) - sfp_vendor_sn_data = sfpi_obj.parse_vendor_sn( - sfp_vendor_sn_raw, 0) - - sfp_vendor_oui_raw = self.__read_eeprom_specific_bytes( - (offset + XCVR_VENDOR_OUI_OFFSET), XCVR_VENDOR_OUI_WIDTH) - if sfp_vendor_oui_raw is not None: - sfp_vendor_oui_data = sfpi_obj.parse_vendor_oui( - sfp_vendor_oui_raw, 0) - - sfp_vendor_date_raw = self.__read_eeprom_specific_bytes( - (offset + XCVR_VENDOR_DATE_OFFSET), XCVR_VENDOR_DATE_WIDTH) - sfp_vendor_date_data = sfpi_obj.parse_vendor_date( - sfp_vendor_date_raw, 0) - + """ + compliance_code_dict = {} transceiver_info_dict = dict.fromkeys(self.info_dict_keys, 'N/A') - compliance_code_dict = dict() + if not self.get_presence(): + return transceiver_info_dict - if sfp_interface_bulk_data: + # ToDo: OSFP tranceiver info parsing not fully supported. + # in inf8628.py lack of some memory map definition + # will be implemented when the inf8628 memory map ready + if self.sfp_type == OSFP_TYPE: + offset = 0 + vendor_rev_width = XCVR_HW_REV_WIDTH_OSFP + + sfpi_obj = inf8628InterfaceId() + if sfpi_obj is None: + return None + + sfp_type_raw = self.__read_eeprom_specific_bytes( + (offset + OSFP_TYPE_OFFSET), XCVR_TYPE_WIDTH) + if sfp_type_raw is not None: + sfp_type_data = sfpi_obj.parse_sfp_type(sfp_type_raw, 0) + else: + return None + + sfp_vendor_name_raw = self.__read_eeprom_specific_bytes( + (offset + OSFP_VENDOR_NAME_OFFSET), XCVR_VENDOR_NAME_WIDTH) + if sfp_vendor_name_raw is not None: + sfp_vendor_name_data = sfpi_obj.parse_vendor_name( + sfp_vendor_name_raw, 0) + else: + return None + + sfp_vendor_pn_raw = self.__read_eeprom_specific_bytes( + (offset + OSFP_VENDOR_PN_OFFSET), XCVR_VENDOR_PN_WIDTH) + if sfp_vendor_pn_raw is not None: + sfp_vendor_pn_data = sfpi_obj.parse_vendor_pn( + sfp_vendor_pn_raw, 0) + else: + return None + + sfp_vendor_rev_raw = self.__read_eeprom_specific_bytes( + (offset + OSFP_HW_REV_OFFSET), vendor_rev_width) + if sfp_vendor_rev_raw is not None: + sfp_vendor_rev_data = sfpi_obj.parse_vendor_rev( + sfp_vendor_rev_raw, 0) + else: + return None + + sfp_vendor_sn_raw = self.__read_eeprom_specific_bytes( + (offset + OSFP_VENDOR_SN_OFFSET), XCVR_VENDOR_SN_WIDTH) + if sfp_vendor_sn_raw is not None: + sfp_vendor_sn_data = sfpi_obj.parse_vendor_sn( + sfp_vendor_sn_raw, 0) + else: + return None + + transceiver_info_dict['type'] = sfp_type_data['data']['type']['value'] + transceiver_info_dict['manufacturer'] = sfp_vendor_name_data['data']['Vendor Name']['value'] + transceiver_info_dict['model'] = sfp_vendor_pn_data['data']['Vendor PN']['value'] + transceiver_info_dict['hardware_rev'] = sfp_vendor_rev_data['data']['Vendor Rev']['value'] + transceiver_info_dict['serial'] = sfp_vendor_sn_data['data']['Vendor SN']['value'] + transceiver_info_dict['vendor_oui'] = 'N/A' + transceiver_info_dict['vendor_date'] = 'N/A' + transceiver_info_dict['connector'] = 'N/A' + transceiver_info_dict['encoding'] = 'N/A' + transceiver_info_dict['ext_identifier'] = 'N/A' + transceiver_info_dict['ext_rateselect_compliance'] = 'N/A' + transceiver_info_dict['cable_type'] = 'N/A' + transceiver_info_dict['cable_length'] = 'N/A' + transceiver_info_dict['specification_compliance'] = '{}' + transceiver_info_dict['nominal_bit_rate'] = 'N/A' + + else: + if self.sfp_type == QSFP_TYPE: + offset = 128 + vendor_rev_width = XCVR_HW_REV_WIDTH_QSFP + # cable_length_width = XCVR_CABLE_LENGTH_WIDTH_QSFP + interface_info_bulk_width = XCVR_INTFACE_BULK_WIDTH_QSFP + + sfpi_obj = sff8436InterfaceId() + if sfpi_obj is None: + print("Error: sfp_object open failed") + return None + + else: + offset = 0 + vendor_rev_width = XCVR_HW_REV_WIDTH_SFP + # cable_length_width = XCVR_CABLE_LENGTH_WIDTH_SFP + interface_info_bulk_width = XCVR_INTFACE_BULK_WIDTH_SFP + + sfpi_obj = sff8472InterfaceId() + if sfpi_obj is None: + print("Error: sfp_object open failed") + return None + sfp_interface_bulk_raw = self.__read_eeprom_specific_bytes( + offset + XCVR_INTERFACE_DATA_START, XCVR_INTERFACE_DATA_SIZE) + if sfp_interface_bulk_raw is None: + return None + + start = XCVR_INTFACE_BULK_OFFSET - XCVR_INTERFACE_DATA_START + end = start + interface_info_bulk_width + sfp_interface_bulk_data = sfpi_obj.parse_sfp_info_bulk( + sfp_interface_bulk_raw[start: end], 0) + + start = XCVR_VENDOR_NAME_OFFSET - XCVR_INTERFACE_DATA_START + end = start + XCVR_VENDOR_NAME_WIDTH + sfp_vendor_name_data = sfpi_obj.parse_vendor_name( + sfp_interface_bulk_raw[start: end], 0) + + start = XCVR_VENDOR_PN_OFFSET - XCVR_INTERFACE_DATA_START + end = start + XCVR_VENDOR_PN_WIDTH + sfp_vendor_pn_data = sfpi_obj.parse_vendor_pn( + sfp_interface_bulk_raw[start: end], 0) + + start = XCVR_HW_REV_OFFSET - XCVR_INTERFACE_DATA_START + end = start + vendor_rev_width + sfp_vendor_rev_data = sfpi_obj.parse_vendor_rev( + sfp_interface_bulk_raw[start: end], 0) + + start = XCVR_VENDOR_SN_OFFSET - XCVR_INTERFACE_DATA_START + end = start + XCVR_VENDOR_SN_WIDTH + sfp_vendor_sn_data = sfpi_obj.parse_vendor_sn( + sfp_interface_bulk_raw[start: end], 0) + + start = XCVR_VENDOR_OUI_OFFSET - XCVR_INTERFACE_DATA_START + end = start + XCVR_VENDOR_OUI_WIDTH + sfp_vendor_oui_data = sfpi_obj.parse_vendor_oui( + sfp_interface_bulk_raw[start: end], 0) + + start = XCVR_VENDOR_DATE_OFFSET - XCVR_INTERFACE_DATA_START + end = start + XCVR_VENDOR_DATE_WIDTH + sfp_vendor_date_data = sfpi_obj.parse_vendor_date( + sfp_interface_bulk_raw[start: end], 0) transceiver_info_dict['type'] = sfp_interface_bulk_data['data']['type']['value'] + transceiver_info_dict['manufacturer'] = sfp_vendor_name_data['data']['Vendor Name']['value'] + transceiver_info_dict['model'] = sfp_vendor_pn_data['data']['Vendor PN']['value'] + transceiver_info_dict['hardware_rev'] = sfp_vendor_rev_data['data']['Vendor Rev']['value'] + transceiver_info_dict['serial'] = sfp_vendor_sn_data['data']['Vendor SN']['value'] + transceiver_info_dict['vendor_oui'] = sfp_vendor_oui_data['data']['Vendor OUI']['value'] + transceiver_info_dict['vendor_date'] = sfp_vendor_date_data[ + 'data']['VendorDataCode(YYYY-MM-DD Lot)']['value'] transceiver_info_dict['connector'] = sfp_interface_bulk_data['data']['Connector']['value'] transceiver_info_dict['encoding'] = sfp_interface_bulk_data['data']['EncodingCodes']['value'] transceiver_info_dict['ext_identifier'] = sfp_interface_bulk_data['data']['Extended Identifier']['value'] transceiver_info_dict['ext_rateselect_compliance'] = sfp_interface_bulk_data['data']['RateIdentifier']['value'] transceiver_info_dict['type_abbrv_name'] = sfp_interface_bulk_data['data']['type_abbrv_name']['value'] - transceiver_info_dict['manufacturer'] = sfp_vendor_name_data[ - 'data']['Vendor Name']['value'] if sfp_vendor_name_data else 'N/A' - transceiver_info_dict['model'] = sfp_vendor_pn_data['data']['Vendor PN']['value'] if sfp_vendor_pn_data else 'N/A' - transceiver_info_dict['hardware_rev'] = sfp_vendor_rev_data['data']['Vendor Rev']['value'] if sfp_vendor_rev_data else 'N/A' - transceiver_info_dict['serial'] = sfp_vendor_sn_data['data']['Vendor SN']['value'] if sfp_vendor_sn_data else 'N/A' - transceiver_info_dict['vendor_oui'] = sfp_vendor_oui_data['data']['Vendor OUI']['value'] if sfp_vendor_oui_data else 'N/A' - transceiver_info_dict['vendor_date'] = sfp_vendor_date_data[ - 'data']['VendorDataCode(YYYY-MM-DD Lot)']['value'] if sfp_vendor_date_data else 'N/A' - transceiver_info_dict['cable_type'] = "Unknown" - transceiver_info_dict['cable_length'] = "Unknown" + if self.sfp_type == QSFP_TYPE: + for key in qsfp_cable_length_tup: + if key in sfp_interface_bulk_data['data']: + transceiver_info_dict['cable_type'] = key + transceiver_info_dict['cable_length'] = str( + sfp_interface_bulk_data['data'][key]['value']) - for key in sfp_cable_length_tup: - if key in sfp_interface_bulk_data['data']: - transceiver_info_dict['cable_type'] = key - transceiver_info_dict['cable_length'] = str( - sfp_interface_bulk_data['data'][key]['value']) + for key in qsfp_compliance_code_tup: + if key in sfp_interface_bulk_data['data']['Specification compliance']['value']: + compliance_code_dict[key] = sfp_interface_bulk_data['data']['Specification compliance']['value'][key]['value'] + transceiver_info_dict['specification_compliance'] = str( + compliance_code_dict) - for key in sfp_compliance_code_tup: - if key in sfp_interface_bulk_data['data']['Specification compliance']['value']: - compliance_code_dict[key] = sfp_interface_bulk_data['data']['Specification compliance']['value'][key]['value'] - transceiver_info_dict['specification_compliance'] = str( - compliance_code_dict) - transceiver_info_dict['nominal_bit_rate'] = str( - sfp_interface_bulk_data['data']['NominalSignallingRate(UnitsOf100Mbd)']['value']) + transceiver_info_dict['nominal_bit_rate'] = str( + sfp_interface_bulk_data['data']['Nominal Bit Rate(100Mbs)']['value']) + else: + for key in sfp_cable_length_tup: + if key in sfp_interface_bulk_data['data']: + transceiver_info_dict['cable_type'] = key + transceiver_info_dict['cable_length'] = str( + sfp_interface_bulk_data['data'][key]['value']) + + for key in sfp_compliance_code_tup: + if key in sfp_interface_bulk_data['data']['Specification compliance']['value']: + compliance_code_dict[key] = sfp_interface_bulk_data['data']['Specification compliance']['value'][key]['value'] + transceiver_info_dict['specification_compliance'] = str( + compliance_code_dict) + + transceiver_info_dict['nominal_bit_rate'] = str( + sfp_interface_bulk_data['data']['NominalSignallingRate(UnitsOf100Mbd)']['value']) return transceiver_info_dict @@ -296,44 +584,117 @@ class Sfp(SfpBase): | |for example, tx2power stands for tx power of channel 2. ======================================================================== """ - # check present status - sfpd_obj = sff8472Dom() - if not self.get_presence() or not sfpd_obj: - return {} - - eeprom_ifraw = self.__read_eeprom_specific_bytes(0, DOM_OFFSET) - sfpi_obj = sff8472InterfaceId(eeprom_ifraw) - cal_type = sfpi_obj.get_calibration_type() - sfpd_obj._calibration_type = cal_type - - offset = DOM_OFFSET transceiver_dom_info_dict = dict.fromkeys(self.dom_dict_keys, 'N/A') - dom_temperature_raw = self.__read_eeprom_specific_bytes( - (offset + SFP_TEMPE_OFFSET), SFP_TEMPE_WIDTH) - if dom_temperature_raw is not None: + if self.sfp_type == OSFP_TYPE: + pass + + elif self.sfp_type == QSFP_TYPE: + if not self.dom_supported: + return transceiver_dom_info_dict + + offset = 0 + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return transceiver_dom_info_dict + + dom_data_raw = self.__read_eeprom_specific_bytes( + (offset + QSFP_DOM_BULK_DATA_START), QSFP_DOM_BULK_DATA_SIZE) + if dom_data_raw is None: + return transceiver_dom_info_dict + + if self.dom_temp_supported: + start = QSFP_TEMPE_OFFSET - QSFP_DOM_BULK_DATA_START + end = start + QSFP_TEMPE_WIDTH + dom_temperature_data = sfpd_obj.parse_temperature( + dom_data_raw[start: end], 0) + temp = self.__convert_string_to_num( + dom_temperature_data['data']['Temperature']['value']) + if temp is not None: + transceiver_dom_info_dict['temperature'] = temp + + if self.dom_volt_supported: + start = QSFP_VOLT_OFFSET - QSFP_DOM_BULK_DATA_START + end = start + QSFP_VOLT_WIDTH + dom_voltage_data = sfpd_obj.parse_voltage( + dom_data_raw[start: end], 0) + volt = self.__convert_string_to_num( + dom_voltage_data['data']['Vcc']['value']) + if volt is not None: + transceiver_dom_info_dict['voltage'] = volt + + start = QSFP_CHANNL_MON_OFFSET - QSFP_DOM_BULK_DATA_START + end = start + QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power( + dom_data_raw[start: end], 0) + + if self.dom_tx_power_supported: + transceiver_dom_info_dict['tx1power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TX1Power']['value']) + transceiver_dom_info_dict['tx2power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TX2Power']['value']) + transceiver_dom_info_dict['tx3power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TX3Power']['value']) + transceiver_dom_info_dict['tx4power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TX4Power']['value']) + + if self.dom_rx_power_supported: + transceiver_dom_info_dict['rx1power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['RX1Power']['value']) + transceiver_dom_info_dict['rx2power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['RX2Power']['value']) + transceiver_dom_info_dict['rx3power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['RX3Power']['value']) + transceiver_dom_info_dict['rx4power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['RX4Power']['value']) + + transceiver_dom_info_dict['tx1bias'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TX1Bias']['value']) + transceiver_dom_info_dict['tx2bias'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TX2Bias']['value']) + transceiver_dom_info_dict['tx3bias'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TX3Bias']['value']) + transceiver_dom_info_dict['tx4bias'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TX4Bias']['value']) + + else: + if not self.dom_supported: + return transceiver_dom_info_dict + + offset = 256 + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return transceiver_dom_info_dict + sfpd_obj._calibration_type = self.calibration + + dom_data_raw = self.__read_eeprom_specific_bytes( + (offset + SFP_DOM_BULK_DATA_START), SFP_DOM_BULK_DATA_SIZE) + + start = SFP_TEMPE_OFFSET - SFP_DOM_BULK_DATA_START + end = start + SFP_TEMPE_WIDTH dom_temperature_data = sfpd_obj.parse_temperature( - dom_temperature_raw, 0) - transceiver_dom_info_dict['temperature'] = dom_temperature_data['data']['Temperature']['value'] + dom_data_raw[start: end], 0) - dom_voltage_raw = self.__read_eeprom_specific_bytes( - (offset + SFP_VOLT_OFFSET), SFP_VOLT_WIDTH) - if dom_voltage_raw is not None: - dom_voltage_data = sfpd_obj.parse_voltage(dom_voltage_raw, 0) - transceiver_dom_info_dict['voltage'] = dom_voltage_data['data']['Vcc']['value'] + start = SFP_VOLT_OFFSET - SFP_DOM_BULK_DATA_START + end = start + SFP_VOLT_WIDTH + dom_voltage_data = sfpd_obj.parse_voltage( + dom_data_raw[start: end], 0) - dom_channel_monitor_raw = self.__read_eeprom_specific_bytes( - (offset + SFP_CHANNL_MON_OFFSET), SFP_CHANNL_MON_WIDTH) - if dom_channel_monitor_raw is not None: - dom_voltage_data = sfpd_obj.parse_channel_monitor_params( - dom_channel_monitor_raw, 0) - transceiver_dom_info_dict['tx1power'] = dom_voltage_data['data']['TXPower']['value'] - transceiver_dom_info_dict['rx1power'] = dom_voltage_data['data']['RXPower']['value'] - transceiver_dom_info_dict['tx1bias'] = dom_voltage_data['data']['TXBias']['value'] + start = SFP_CHANNL_MON_OFFSET - SFP_DOM_BULK_DATA_START + end = start + SFP_CHANNL_MON_WIDTH + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params( + dom_data_raw[start: end], 0) - for key in transceiver_dom_info_dict: - transceiver_dom_info_dict[key] = self._convert_string_to_num( - transceiver_dom_info_dict[key]) + transceiver_dom_info_dict['temperature'] = self.__convert_string_to_num( + dom_temperature_data['data']['Temperature']['value']) + transceiver_dom_info_dict['voltage'] = self.__convert_string_to_num( + dom_voltage_data['data']['Vcc']['value']) + transceiver_dom_info_dict['rx1power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['RXPower']['value']) + transceiver_dom_info_dict['tx1bias'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TXBias']['value']) + transceiver_dom_info_dict['tx1power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TXPower']['value']) transceiver_dom_info_dict['rx_los'] = self.get_rx_los() transceiver_dom_info_dict['tx_fault'] = self.get_tx_fault() @@ -372,26 +733,79 @@ class Sfp(SfpBase): txbiaslowwarning |FLOAT |Low Warning Threshold value of tx Bias Current in mA. ======================================================================== """ - # check present status - sfpd_obj = sff8472Dom() - - if not self.get_presence() and not sfpd_obj: - return {} - - eeprom_ifraw = self.__read_eeprom_specific_bytes(0, DOM_OFFSET) - sfpi_obj = sff8472InterfaceId(eeprom_ifraw) - cal_type = sfpi_obj.get_calibration_type() - sfpd_obj._calibration_type = cal_type - - offset = DOM_OFFSET transceiver_dom_threshold_info_dict = dict.fromkeys( self.threshold_dict_keys, 'N/A') - dom_module_threshold_raw = self.__read_eeprom_specific_bytes( - (offset + SFP_MODULE_THRESHOLD_OFFSET), SFP_MODULE_THRESHOLD_WIDTH) - if dom_module_threshold_raw is not None: - dom_module_threshold_data = sfpd_obj.parse_alarm_warning_threshold( + + if self.sfp_type == OSFP_TYPE: + pass + + elif self.sfp_type == QSFP_TYPE: + if not self.dom_supported or not self.qsfp_page3_available: + return transceiver_dom_threshold_info_dict + + # Dom Threshold data starts from offset 384 + # Revert offset back to 0 once data is retrieved + offset = QSFP_MODULE_UPPER_PAGE3_START + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return transceiver_dom_threshold_info_dict + + dom_module_threshold_raw = self.__read_eeprom_specific_bytes( + (offset + QSFP_MODULE_THRESHOLD_OFFSET), QSFP_MODULE_THRESHOLD_WIDTH) + if dom_module_threshold_raw is None: + return transceiver_dom_threshold_info_dict + + dom_module_threshold_data = sfpd_obj.parse_module_threshold_values( dom_module_threshold_raw, 0) + dom_channel_threshold_raw = self.__read_eeprom_specific_bytes((offset + QSFP_CHANNL_THRESHOLD_OFFSET), + QSFP_CHANNL_THRESHOLD_WIDTH) + if dom_channel_threshold_raw is None: + return transceiver_dom_threshold_info_dict + dom_channel_threshold_data = sfpd_obj.parse_channel_threshold_values( + dom_channel_threshold_raw, 0) + + # Threshold Data + transceiver_dom_threshold_info_dict['temphighalarm'] = dom_module_threshold_data['data']['TempHighAlarm']['value'] + transceiver_dom_threshold_info_dict['temphighwarning'] = dom_module_threshold_data['data']['TempHighWarning']['value'] + transceiver_dom_threshold_info_dict['templowalarm'] = dom_module_threshold_data['data']['TempLowAlarm']['value'] + transceiver_dom_threshold_info_dict['templowwarning'] = dom_module_threshold_data['data']['TempLowWarning']['value'] + transceiver_dom_threshold_info_dict['vcchighalarm'] = dom_module_threshold_data['data']['VccHighAlarm']['value'] + transceiver_dom_threshold_info_dict['vcchighwarning'] = dom_module_threshold_data['data']['VccHighWarning']['value'] + transceiver_dom_threshold_info_dict['vcclowalarm'] = dom_module_threshold_data['data']['VccLowAlarm']['value'] + transceiver_dom_threshold_info_dict['vcclowwarning'] = dom_module_threshold_data['data']['VccLowWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighalarm'] = dom_channel_threshold_data['data']['RxPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighwarning'] = dom_channel_threshold_data['data']['RxPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowalarm'] = dom_channel_threshold_data['data']['RxPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowwarning'] = dom_channel_threshold_data['data']['RxPowerLowWarning']['value'] + transceiver_dom_threshold_info_dict['txbiashighalarm'] = dom_channel_threshold_data['data']['TxBiasHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiashighwarning'] = dom_channel_threshold_data['data']['TxBiasHighWarning']['value'] + transceiver_dom_threshold_info_dict['txbiaslowalarm'] = dom_channel_threshold_data['data']['TxBiasLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiaslowwarning'] = dom_channel_threshold_data['data']['TxBiasLowWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerhighalarm'] = dom_channel_threshold_data['data']['TxPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerhighwarning'] = dom_channel_threshold_data['data']['TxPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerlowalarm'] = dom_channel_threshold_data['data']['TxPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerlowwarning'] = dom_channel_threshold_data['data']['TxPowerLowWarning']['value'] + + else: + offset = SFP_MODULE_ADDRA2_OFFSET + + if not self.dom_supported: + return transceiver_dom_threshold_info_dict + + sfpd_obj = sff8472Dom(None, self.calibration) + if sfpd_obj is None: + return transceiver_dom_threshold_info_dict + + dom_module_threshold_raw = self.__read_eeprom_specific_bytes((offset + SFP_MODULE_THRESHOLD_OFFSET), + SFP_MODULE_THRESHOLD_WIDTH) + if dom_module_threshold_raw is not None: + dom_module_threshold_data = sfpd_obj.parse_alarm_warning_threshold( + dom_module_threshold_raw, 0) + else: + return transceiver_dom_threshold_info_dict + + # Threshold Data transceiver_dom_threshold_info_dict['temphighalarm'] = dom_module_threshold_data['data']['TempHighAlarm']['value'] transceiver_dom_threshold_info_dict['templowalarm'] = dom_module_threshold_data['data']['TempLowAlarm']['value'] transceiver_dom_threshold_info_dict['temphighwarning'] = dom_module_threshold_data['data']['TempHighWarning']['value'] @@ -415,7 +829,7 @@ class Sfp(SfpBase): transceiver_dom_threshold_info_dict['rxpowerlowwarning'] = dom_module_threshold_data['data']['RXPowerLowWarning']['value'] for key in transceiver_dom_threshold_info_dict: - transceiver_dom_threshold_info_dict[key] = self._convert_string_to_num( + transceiver_dom_threshold_info_dict[key] = self.__convert_string_to_num( transceiver_dom_threshold_info_dict[key]) return transceiver_dom_threshold_info_dict @@ -437,11 +851,27 @@ class Sfp(SfpBase): Note : RX LOS status is latched until a call to get_rx_los or a reset. """ rx_los = False - status_control_raw = self.__read_eeprom_specific_bytes( - SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH) - if status_control_raw: - data = int(status_control_raw[0], 16) - rx_los = (sffbase().test_bit(data, 1) != 0) + if self.sfp_type == OSFP_TYPE: + return False + + elif self.sfp_type == QSFP_TYPE: + offset = 0 + dom_channel_monitor_raw = self.__read_eeprom_specific_bytes( + (offset + QSFP_CHANNL_RX_LOS_STATUS_OFFSET), QSFP_CHANNL_RX_LOS_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + rx_los_data = int(dom_channel_monitor_raw[0], 16) + rx1_los = (rx_los_data & 0x01 != 0) + rx2_los = (rx_los_data & 0x02 != 0) + rx3_los = (rx_los_data & 0x04 != 0) + rx4_los = (rx_los_data & 0x08 != 0) + rx_los = (rx1_los and rx2_los and rx3_los and rx4_los) + else: + offset = 256 + dom_channel_monitor_raw = self.__read_eeprom_specific_bytes( + (offset + SFP_CHANNL_STATUS_OFFSET), SFP_CHANNL_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + rx_los_data = int(dom_channel_monitor_raw[0], 16) + rx_los = (rx_los_data & 0x02 != 0) return rx_los @@ -452,14 +882,32 @@ class Sfp(SfpBase): A Boolean, True if SFP has TX fault, False if not Note : TX fault status is lached until a call to get_tx_fault or a reset. """ - tx_fault = False - status_control_raw = self.__read_eeprom_specific_bytes( - SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH) - if status_control_raw: - data = int(status_control_raw[0], 16) - tx_fault = (sffbase().test_bit(data, 2) != 0) + tx4_fault = False - return tx_fault + if self.sfp_type == OSFP_TYPE or not self.dom_supported: + return False + + elif self.sfp_type == QSFP_TYPE: + offset = 0 + dom_channel_monitor_raw = self.__read_eeprom_specific_bytes( + (offset + QSFP_CHANNL_TX_FAULT_STATUS_OFFSET), QSFP_CHANNL_TX_FAULT_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + tx_fault_data = int(dom_channel_monitor_raw[0], 16) + tx1_fault = (tx_fault_data & 0x01 != 0) + tx2_fault = (tx_fault_data & 0x02 != 0) + tx3_fault = (tx_fault_data & 0x04 != 0) + tx4_fault = (tx_fault_data & 0x08 != 0) + tx4_fault = ( + tx1_fault and tx2_fault and tx3_fault and tx4_fault) + else: + offset = 256 + dom_channel_monitor_raw = self.__read_eeprom_specific_bytes( + (offset + SFP_CHANNL_STATUS_OFFSET), SFP_CHANNL_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + tx_fault_data = int(dom_channel_monitor_raw[0], 16) + tx4_fault = (tx_fault_data & 0x04 != 0) + + return tx4_fault def get_tx_disable(self): """ @@ -469,7 +917,7 @@ class Sfp(SfpBase): """ tx_disable = False status_control_raw = self.__read_eeprom_specific_bytes( - SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH) + SFP_CHANNL_STATUS_OFFSET, SFP_CHANNL_STATUS_WIDTH) if status_control_raw: data = int(status_control_raw[0], 16) tx_disable_hard = (sffbase().test_bit( @@ -516,8 +964,8 @@ class Sfp(SfpBase): Returns: An integer number of current temperature in Celsius """ - transceiver_dom_info_dict = self.get_transceiver_bulk_status() - return transceiver_dom_info_dict.get("temperature", "N/A") + transceiver_bulk_status = self.get_transceiver_bulk_status() + return transceiver_bulk_status.get("temperature", "N/A") def get_voltage(self): """ @@ -525,8 +973,8 @@ class Sfp(SfpBase): Returns: An integer number of supply voltage in mV """ - transceiver_dom_info_dict = self.get_transceiver_bulk_status() - return transceiver_dom_info_dict.get("voltage", "N/A") + transceiver_bulk_status = self.get_transceiver_bulk_status() + return transceiver_bulk_status.get("voltage", "N/A") def get_tx_bias(self): """ @@ -584,7 +1032,7 @@ class Sfp(SfpBase): """ sysfs_sfp_i2c_client_eeprom_path = self.port_to_eeprom_mapping[self.port_num] status_control_raw = self.__read_eeprom_specific_bytes( - SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH) + SFP_CHANNL_STATUS_OFFSET, SFP_CHANNL_STATUS_WIDTH) if status_control_raw is not None: # Set bit 6 for Soft TX Disable Select # 01000000 = 64 and 10111111 = 191 @@ -598,10 +1046,9 @@ class Sfp(SfpBase): buffer = create_string_buffer(1) buffer[0] = chr(tx_disable_ctl) # Write to eeprom - sysfsfile_eeprom.seek(SFP_STATUS_CONTROL_OFFSET) + sysfsfile_eeprom.seek(SFP_CHANNL_STATUS_OFFSET) sysfsfile_eeprom.write(buffer[0]) except Exception: - #print("Error: unable to open file: %s" % str(e)) return False finally: if sysfsfile_eeprom: @@ -656,6 +1103,10 @@ class Sfp(SfpBase): # SFP doesn't support this feature return False + ############################################################## + ###################### Device methods ######################## + ############################################################## + def get_name(self): """ Retrieves the name of the device @@ -700,3 +1151,26 @@ class Sfp(SfpBase): """ transceiver_dom_info_dict = self.get_transceiver_info() return transceiver_dom_info_dict.get("serial", "N/A") + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + return self.get_presence() and not self.get_reset_status() + + def get_position_in_parent(self): + """ + Returns: + Temp return 0 + """ + return 0 + + def is_replaceable(self): + """ + Retrieves if replaceable + Returns: + A boolean value, True if replaceable + """ + return True diff --git a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/thermal.py b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/thermal.py index 2235b4c657..d224281a59 100644 --- a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/thermal.py +++ b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/thermal.py @@ -93,7 +93,6 @@ I2C_ADAPTER_PATH = "/sys/class/i2c-adapter" class Thermal(ThermalBase): """Platform-specific Thermal class""" - THERMAL_NAME_LIST = [] MAINBOARD_SS_PATH = "/sys/class/i2c-adapter/i2c-11/11-001a/hwmon/hwmon2" CPUBOARD_SS_PATH = "/sys/class/i2c-adapter/i2c-3/3-001a/hwmon/hwmon1" SS_CONFIG_PATH = "/usr/share/sonic/device/x86_64-cel_e1031-r0/sensors.conf" @@ -110,9 +109,12 @@ class Thermal(ThermalBase): self.name = self.get_name() self.postion = self._thermal_info["postion"] + self.minimum_thermal = self.get_temperature() + self.maximum_thermal = self.get_temperature() def _get_hwmon_path(self): - hwmon_path = os.path.join(I2C_ADAPTER_PATH, self._thermal_info["i2c_path"]) + hwmon_path = os.path.join( + I2C_ADAPTER_PATH, self._thermal_info["i2c_path"]) hwmon_dir = os.listdir(hwmon_path)[0] return os.path.join(hwmon_path, hwmon_dir) @@ -126,6 +128,17 @@ class Thermal(ThermalBase): temp_file_path = os.path.join(self._hwmon_path, file_name) return self._api_common.write_txt_file(temp_file_path, str(temperature)) + def _get_threshold(self, file_name): + temp_file_path = os.path.join(self._hwmon_path, file_name) + data = self._api_common.read_txt_file(temp_file_path) + if data: + try: + threshold = float(data) + return round(threshold/1000, 3) + except Exception: + pass + return None + def get_temperature(self): """ Retrieves current temperature reading from thermal @@ -144,7 +157,8 @@ class Thermal(ThermalBase): up to nearest thousandth of one degree Celsius, e.g. 30.125 """ max_crit_key = '{}_max'.format(self._airflow) - return self._thermal_info.get(max_crit_key, None) + high_threshold_file = "temp{}_max".format(self._ss_index) + return self._get_threshold(high_threshold_file) or self._thermal_info.get(max_crit_key, None) def get_low_threshold(self): """ @@ -153,7 +167,7 @@ class Thermal(ThermalBase): A float number, the low threshold temperature of thermal in Celsius up to nearest thousandth of one degree Celsius, e.g. 30.125 """ - return 0.0 + return 0.001 def set_high_threshold(self, temperature): """ @@ -166,8 +180,8 @@ class Thermal(ThermalBase): """ temp_file = "temp{}_max".format(self._ss_index) is_set = self._set_threshold(temp_file, int(temperature*1000)) - file_set = False - if is_set: + file_set = True + if is_set and self._api_common.is_host(): try: with open(self.SS_CONFIG_PATH, 'r+') as f: content = f.readlines() @@ -215,7 +229,35 @@ class Thermal(ThermalBase): A float number, the low critical threshold temperature of thermal in Celsius up to nearest thousandth of one degree Celsius, e.g. 30.125 """ - return 0.0 + return 0.001 + + def get_minimum_recorded(self): + """ + Retrieves the minimum recorded temperature of thermal + Returns: + A float number, the minimum recorded temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + tmp = self.get_temperature() + if tmp < self.minimum_thermal: + self.minimum_thermal = tmp + return self.minimum_thermal + + def get_maximum_recorded(self): + """ + Retrieves the maximum recorded temperature of thermal + Returns: + A float number, the maximum recorded temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + tmp = self.get_temperature() + if tmp > self.maximum_thermal: + self.maximum_thermal = tmp + return self.maximum_thermal + + ############################################################## + ###################### Device methods ######################## + ############################################################## def get_name(self): """ @@ -267,3 +309,21 @@ class Thermal(ThermalBase): raw_txt = self._api_common.read_txt_file(fault_file_path) return int(raw_txt) == 0 + + def is_replaceable(self): + """ + Retrieves whether thermal module is replaceable + Returns: + A boolean value, True if replaceable, False if not + """ + return False + + def get_position_in_parent(self): + """ + Retrieves the thermal position information + Returns: + A int value, 0 represent ASIC thermal, 1 represent CPU thermal info + """ + if self.postion == "cpu": + return 1 + return 0 diff --git a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/watchdog.py b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/watchdog.py index 62f325655c..93898bd646 100644 --- a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/watchdog.py +++ b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/watchdog.py @@ -4,114 +4,107 @@ # Watchdog contains an implementation of SONiC Platform Base API # ############################################################################# -import fcntl -import os -import array try: + import os + import time from sonic_platform_base.watchdog_base import WatchdogBase + from .common import Common except ImportError as e: raise ImportError(str(e) + "- required module not found") -""" ioctl constants """ -IO_WRITE = 0x40000000 -IO_READ = 0x80000000 -IO_READ_WRITE = 0xC0000000 -IO_SIZE_INT = 0x00040000 -IO_SIZE_40 = 0x00280000 -IO_TYPE_WATCHDOG = ord('W') << 8 - -WDR_INT = IO_READ | IO_SIZE_INT | IO_TYPE_WATCHDOG -WDR_40 = IO_READ | IO_SIZE_40 | IO_TYPE_WATCHDOG -WDWR_INT = IO_READ_WRITE | IO_SIZE_INT | IO_TYPE_WATCHDOG - -""" Watchdog ioctl commands """ -WDIOC_GETSUPPORT = 0 | WDR_40 -WDIOC_GETSTATUS = 1 | WDR_INT -WDIOC_GETBOOTSTATUS = 2 | WDR_INT -WDIOC_GETTEMP = 3 | WDR_INT -WDIOC_SETOPTIONS = 4 | WDR_INT -WDIOC_KEEPALIVE = 5 | WDR_INT -WDIOC_SETTIMEOUT = 6 | WDWR_INT -WDIOC_GETTIMEOUT = 7 | WDR_INT -WDIOC_SETPRETIMEOUT = 8 | WDWR_INT -WDIOC_GETPRETIMEOUT = 9 | WDR_INT -WDIOC_GETTIMELEFT = 10 | WDR_INT - -""" Watchdog status constants """ -WDIOS_DISABLECARD = 0x0001 -WDIOS_ENABLECARD = 0x0002 +PLATFORM_CPLD_PATH = '/sys/devices/platform/e1031.smc/' +SETREG_FILE = 'setreg' +GETREG_FILE = 'getreg' WDT_COMMON_ERROR = -1 -WD_MAIN_IDENTITY = "iTCO_wdt" -WDT_SYSFS_PATH = "/sys/class/watchdog/" +MMC_VERSION_REG = "0x100" + +# watchdog infomation for cpld v06 +V06_MMC_VERSION = 0x05 +V06_WDT_WIDTH = '0x110' +V06_WDT_WIDTH_SELECTOR = { + 30: '0x1', + 60: '0x2', + 180: '0x3' +} + +V06_CPLD_WDT_INFO = { + 'wdt_en_reg': '0x111', + 'wdt_en_cmd': '0x0', + 'wdt_dis_cmd': '0x1' +} + +# watchdog infomation +WDT_TIMER_L_BIT_REG = '0x117' +WDT_TIMER_M_BIT_REG = '0x118' +WDT_TIMER_H_BIT_REG = '0x119' +WDT_KEEP_ALVIVE_REG = '0x11a' + +CPLD_WDT_INFO = { + 'wdt_en_reg': '0x116', + 'wdt_en_cmd': '0x1', + 'wdt_dis_cmd': '0x0' +} class Watchdog(WatchdogBase): def __init__(self): - WatchdogBase.__init__(self) + # Init api_common + self._api_common = Common() + + # Init cpld reg path + self.setreg_path = os.path.join(PLATFORM_CPLD_PATH, SETREG_FILE) + self.getreg_path = os.path.join(PLATFORM_CPLD_PATH, GETREG_FILE) + + self.mmc_v = self._get_mmc_version() + self.cpld_info = V06_CPLD_WDT_INFO if self.mmc_v <= V06_MMC_VERSION else CPLD_WDT_INFO - self.watchdog, self.wdt_main_dev_name = self._get_wdt() - self.status_path = "/sys/class/watchdog/%s/status" % self.wdt_main_dev_name - self.state_path = "/sys/class/watchdog/%s/state" % self.wdt_main_dev_name - self.timeout_path = "/sys/class/watchdog/%s/timeout" % self.wdt_main_dev_name # Set default value self._disable() self.armed = False - self.timeout = self._gettimeout(self.timeout_path) + self.timeout = 0 - def _is_wd_main(self, dev): - """ - Checks watchdog identity - """ - identity = self._read_file( - "{}/{}/identity".format(WDT_SYSFS_PATH, dev)) - return identity == WD_MAIN_IDENTITY + def _get_mmc_version(self): + hex_str_v = self._api_common.get_reg(self.getreg_path, MMC_VERSION_REG) + return int(hex_str_v, 16) - def _get_wdt(self): - """ - Retrieves watchdog device - """ - wdt_main_dev_list = [dev for dev in os.listdir( - "/dev/") if dev.startswith("watchdog") and self._is_wd_main(dev)] - if not wdt_main_dev_list: - return None - wdt_main_dev_name = wdt_main_dev_list[0] - watchdog_device_path = "/dev/{}".format(wdt_main_dev_name) - watchdog = os.open(watchdog_device_path, os.O_RDWR) - return watchdog, wdt_main_dev_name + def _get_level_hex(self, sub_hex): + sub_hex_str = sub_hex.replace("x", "0") + return hex(int(sub_hex_str, 16)) - def _read_file(self, file_path): - """ - Read text file - """ - try: - with open(file_path, "r") as fd: - txt = fd.read() - except IOError: - return WDT_COMMON_ERROR - return txt.strip() + def _seconds_to_lmh_hex(self, seconds): + ms = seconds*1000 # calculate timeout in ms format + hex_str = hex(ms) + l = self._get_level_hex(hex_str[-2:]) + m = self._get_level_hex(hex_str[-4:-2]) + h = self._get_level_hex(hex_str[-6:-4]) + return (l, m, h) def _enable(self): """ Turn on the watchdog timer """ - req = array.array('h', [WDIOS_ENABLECARD]) - fcntl.ioctl(self.watchdog, WDIOC_SETOPTIONS, req, False) + return self._api_common.set_reg(self.setreg_path, self.cpld_info['wdt_en_reg'], self.cpld_info['wdt_en_cmd']) def _disable(self): """ Turn off the watchdog timer """ - req = array.array('h', [WDIOS_DISABLECARD]) - fcntl.ioctl(self.watchdog, WDIOC_SETOPTIONS, req, False) + return self._api_common.set_reg(self.setreg_path, self.cpld_info['wdt_en_reg'], self.cpld_info['wdt_dis_cmd']) def _keepalive(self): """ Keep alive watchdog timer """ - fcntl.ioctl(self.watchdog, WDIOC_KEEPALIVE) + if self.mmc_v <= V06_MMC_VERSION: + self._disable() + self._enable() + + else: + self._api_common.set_reg( + self.setreg_path, WDT_KEEP_ALVIVE_REG, self.cpld_info['wdt_en_cmd']) def _settimeout(self, seconds): """ @@ -119,29 +112,23 @@ class Watchdog(WatchdogBase): @param seconds - timeout in seconds @return is the actual set timeout """ - req = array.array('I', [seconds]) - fcntl.ioctl(self.watchdog, WDIOC_SETTIMEOUT, req, True) - return int(req[0]) - def _gettimeout(self, timeout_path): - """ - Get watchdog timeout - @return watchdog timeout - """ - req = array.array('I', [0]) - fcntl.ioctl(self.watchdog, WDIOC_GETTIMEOUT, req, True) + if self.mmc_v <= V06_MMC_VERSION: + timeout_hex = V06_WDT_WIDTH_SELECTOR.get(seconds, '0x2') + seconds = 60 if timeout_hex == '0x2' else seconds + self._api_common.set_reg( + self.setreg_path, V06_WDT_WIDTH, timeout_hex) - return int(req[0]) + else: + (l, m, h) = self._seconds_to_lmh_hex(seconds) + self._api_common.set_reg( + self.setreg_path, WDT_TIMER_H_BIT_REG, h) # set high bit + self._api_common.set_reg( + self.setreg_path, WDT_TIMER_M_BIT_REG, m) # set med bit + self._api_common.set_reg( + self.setreg_path, WDT_TIMER_L_BIT_REG, l) # set low bit - def _gettimeleft(self): - """ - Get time left before watchdog timer expires - @return time left in seconds - """ - req = array.array('I', [0]) - fcntl.ioctl(self.watchdog, WDIOC_GETTIMELEFT, req, True) - - return int(req[0]) + return seconds ################################################################# @@ -157,22 +144,25 @@ class Watchdog(WatchdogBase): An integer specifying the *actual* number of seconds the watchdog was armed with. On failure returns -1. """ - ret = WDT_COMMON_ERROR - if seconds < 0: + + if seconds < 0 or seconds > 180: return ret try: if self.timeout != seconds: self.timeout = self._settimeout(seconds) + if self.armed: self._keepalive() else: self._enable() self.armed = True + ret = self.timeout + self.arm_timestamp = time.time() except IOError as e: - pass + print("Error: unable to enable wdt due to : {}".format(e)) return ret @@ -183,14 +173,12 @@ class Watchdog(WatchdogBase): A boolean, True if watchdog is disarmed successfully, False if not """ disarmed = False - if self.is_armed(): - try: - self._disable() - self.armed = False - disarmed = True - except IOError: - pass - + try: + self._disable() + self.armed = False + disarmed = True + except IOError as e: + print("Error: unable to disable wdt due to : {}".format(e)) return disarmed def is_armed(self): @@ -199,7 +187,6 @@ class Watchdog(WatchdogBase): Returns: A boolean, True if watchdog is armed, False if not """ - return self.armed def get_remaining_time(self): @@ -214,16 +201,6 @@ class Watchdog(WatchdogBase): timeleft = WDT_COMMON_ERROR if self.armed: - try: - timeleft = self._gettimeleft() - except IOError: - pass + timeleft = int(self.timeout - (time.time() - self.arm_timestamp)) return timeleft - - def __del__(self): - """ - Close watchdog - """ - - os.close(self.watchdog) diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-haliburton.init b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-haliburton.init index 5c73be6ee5..07a6542109 100644 --- a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-haliburton.init +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-haliburton.init @@ -53,12 +53,11 @@ start) echo max6699 0x1a > /sys/bus/i2c/devices/i2c-3/new_device echo max6699 0x1a > /sys/bus/i2c/devices/i2c-11/new_device - ## TODO: fix dps200 driver cause kernel panic issue - # https://github.com/Azure/sonic-buildimage/issues/6602 - # Attach PSUs - # echo dps200 0x5a > /sys/bus/i2c/devices/i2c-12/new_device - # echo dps200 0x5b > /sys/bus/i2c/devices/i2c-13/new_device + echo dps200 0x5a > /sys/bus/i2c/devices/i2c-12/new_device + echo 24c02 0x52 > /sys/bus/i2c/devices/i2c-12/new_device + echo dps200 0x5b > /sys/bus/i2c/devices/i2c-13/new_device + echo 24c02 0x53 > /sys/bus/i2c/devices/i2c-13/new_device # Attach fans echo emc2305 0x4d > /sys/bus/i2c/devices/i2c-23/new_device