diff --git a/device/celestica/x86_64-cel_seastone-r0/platform.json b/device/celestica/x86_64-cel_seastone-r0/platform.json index aebb6ee0ac..6c874fcfcc 100644 --- a/device/celestica/x86_64-cel_seastone-r0/platform.json +++ b/device/celestica/x86_64-cel_seastone-r0/platform.json @@ -1,4 +1,246 @@ { + "chassis": { + "name": "Celestica-DX010-C32", + "components": [ + { + "name": "CPLD1" + }, + { + "name": "CPLD2" + }, + { + "name": "CPLD3" + }, + { + "name": "CPLD4" + }, + { + "name": "BIOS" + } + ], + "fans": [ + { + "name": "FAN-1F" + }, + { + "name": "FAN-1R" + }, + { + "name": "FAN-2F" + }, + { + "name": "FAN-2R" + }, + { + "name": "FAN-3F" + }, + { + "name": "FAN-3R" + }, + { + "name": "FAN-4F" + }, + { + "name": "FAN-4R" + }, + { + "name": "FAN-5F" + }, + { + "name": "FAN-5R" + } + ], + "fan_drawers": [ + { + "name": "Drawer1", + "fans": [ + { + "name": "FAN-1F" + }, + { + "name": "FAN-1R" + } + ] + }, + { + "name": "Drawer2", + "fans": [ + { + "name": "FAN-2F" + }, + { + "name": "FAN-2R" + } + ] + }, + { + "name": "Drawer3", + "fans": [ + { + "name": "FAN-3F" + }, + { + "name": "FAN-3R" + } + ] + }, + { + "name": "Drawer4", + "fans": [ + { + "name": "FAN-4F" + }, + { + "name": "FAN-4R" + } + ] + }, + { + "name": "Drawer5", + "fans": [ + { + "name": "FAN-5F" + }, + { + "name": "FAN-5R" + } + ] + } + ], + "psus": [ + { + "name": "PSU-1", + "fans": [ + { + "name": "PSU-1 FAN-1" + } + ] + }, + { + "name": "PSU-2", + "fans": [ + { + "name": "PSU-2 FAN-1" + } + ] + } + ], + "thermals": [ + { + "name": "Front-panel temp sensor 1" + }, + { + "name": "Front-panel temp sensor 2" + }, + { + "name": "ASIC temp sensor" + }, + { + "name": "Rear-panel temp sensor 1" + }, + { + "name": "Rear-panel temp sensor 2" + } + ], + "sfps": [ + { + "name": "Ethernet124" + }, + { + "name": "Ethernet0" + }, + { + "name": "Ethernet4" + }, + { + "name": "Ethernet8" + }, + { + "name": "Ethernet12" + }, + { + "name": "Ethernet16" + }, + { + "name": "Ethernet20" + }, + { + "name": "Ethernet24" + }, + { + "name": "Ethernet28" + }, + { + "name": "Ethernet32" + }, + { + "name": "Ethernet36" + }, + { + "name": "Ethernet40" + }, + { + "name": "Ethernet44" + }, + { + "name": "Ethernet48" + }, + { + "name": "Ethernet52" + }, + { + "name": "Ethernet56" + }, + { + "name": "Ethernet60" + }, + { + "name": "Ethernet64" + }, + { + "name": "Ethernet68" + }, + { + "name": "Ethernet72" + }, + { + "name": "Ethernet76" + }, + { + "name": "Ethernet80" + }, + { + "name": "Ethernet84" + }, + { + "name": "Ethernet88" + }, + { + "name": "Ethernet92" + }, + { + "name": "Ethernet96" + }, + { + "name": "Ethernet100" + }, + { + "name": "Ethernet104" + }, + { + "name": "Ethernet108" + }, + { + "name": "Ethernet112" + }, + { + "name": "Ethernet116" + }, + { + "name": "Ethernet120" + } + ] + }, "interfaces": { "Ethernet0": { "index": "1,1,1,1", diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py index 1b5c5773e2..8a9ed17139 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py @@ -17,7 +17,6 @@ except ImportError as e: raise ImportError(str(e) + "- required module not found") NUM_FAN_TRAY = 5 -NUM_FAN = 2 NUM_PSU = 2 NUM_THERMAL = 5 NUM_SFP = 32 @@ -28,6 +27,7 @@ REBOOT_CAUSE_FILE = "reboot-cause.txt" PREV_REBOOT_CAUSE_FILE = "previous-reboot-cause.txt" GETREG_PATH = "/sys/devices/platform/dx010_cpld/getreg" HOST_CHK_CMD = "docker > /dev/null 2>&1" +STATUS_LED_PATH = "/sys/devices/platform/leds_dx010/leds/dx010:green:stat/brightness" class Chassis(ChassisBase): @@ -40,12 +40,10 @@ class Chassis(ChassisBase): self.__initialize_eeprom() self.is_host = self._api_helper.is_host() - if not self.is_host: - self.__initialize_fan() - self.__initialize_psu() - self.__initialize_thermals() - else: - self.__initialize_components() + self.__initialize_fan() + self.__initialize_psu() + self.__initialize_thermals() + self.__initialize_components() def __initialize_sfp(self): sfputil_helper = SfpUtilHelper() @@ -54,8 +52,7 @@ class Chassis(ChassisBase): from sonic_platform.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 @@ -66,11 +63,11 @@ class Chassis(ChassisBase): 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 sonic_platform.fan_drawer import FanDrawer + for i in range(NUM_FAN_TRAY): + fandrawer = FanDrawer(i) + self._fan_drawer_list.append(fandrawer) + self._fan_list.extend(fandrawer._fan_list) def __initialize_thermals(self): from sonic_platform.thermal import Thermal @@ -90,7 +87,9 @@ class Chassis(ChassisBase): self._component_list.append(component) def __get_air_flow(self): - air_flow_path = '/usr/share/sonic/device/{}/fan_airflow'.format(self._api_helper.platform) if self.is_host else '/usr/share/sonic/platform/fan_airflow' + air_flow_path = '/usr/share/sonic/device/{}/fan_airflow'.format( + self._api_helper.platform) \ + if self.is_host else '/usr/share/sonic/platform/fan_airflow' air_flow = self._api_helper.read_one_line_file(air_flow_path) return air_flow or 'B2F' @@ -155,7 +154,6 @@ class Chassis(ChassisBase): return prev_reboot_cause - def get_change_event(self, timeout=0): """ Returns a nested dictionary containing all devices which have @@ -231,7 +229,7 @@ class Chassis(ChassisBase): try: # The index will start from 1 - sfp = self._sfp_list[index-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))) @@ -254,6 +252,10 @@ class Chassis(ChassisBase): return self._watchdog + def get_thermal_manager(self): + from .thermal_manager import ThermalManager + return ThermalManager + ############################################################## ###################### Device methods ######################## ############################################################## @@ -298,6 +300,53 @@ class Chassis(ChassisBase): """ return True - def get_thermal_manager(self): - from .thermal_manager import ThermalManager - return ThermalManager + 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 + """ + + set_status_str = { + self.STATUS_LED_COLOR_GREEN: '1', + self.STATUS_LED_COLOR_OFF: '0' + }.get(color, None) + + if not set_status_str: + return False + + return self._api_helper.write_txt_file(STATUS_LED_PATH, set_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_helper.read_txt_file(STATUS_LED_PATH) + status_str = { + '255': self.STATUS_LED_COLOR_GREEN, + '0': self.STATUS_LED_COLOR_OFF + }.get(status, None) + + return status_str diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/component.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/component.py index 2833bf4d1c..2db8418e18 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/component.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/component.py @@ -128,3 +128,61 @@ class Component(ComponentBase): # install_command = "afulnx_64 %s /p /b /n /x /r" % image_path return self.__run_command(install_command) + + + ############################################################## + ###################### 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_seastone-r0/sonic_platform/eeprom.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/eeprom.py index b2b4a81225..b0d37d57be 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/eeprom.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/eeprom.py @@ -8,24 +8,22 @@ ############################################################################# try: - import glob import os import sys import re - from array import array if sys.version_info.major == 3: from io import StringIO else: from cStringIO import StringIO - from sonic_platform_base.sonic_eeprom import eeprom_dts from sonic_platform_base.sonic_eeprom import eeprom_tlvinfo 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): @@ -52,7 +50,7 @@ class Tlv(eeprom_tlvinfo.TlvInfoDecoder): value = match.group(3).rstrip('\0') _eeprom_info_dict[idx] = value - except: + except BaseException: pass return _eeprom_info_dict @@ -61,7 +59,7 @@ class Tlv(eeprom_tlvinfo.TlvInfoDecoder): sys.stdout = StringIO() try: self.read_eeprom_db() - except: + except BaseException: decode_output = sys.stdout.getvalue() sys.stdout = original_stdout return self.__parse_output(decode_output) @@ -73,7 +71,7 @@ class Tlv(eeprom_tlvinfo.TlvInfoDecoder): if not os.path.exists(CACHE_ROOT): try: os.makedirs(CACHE_ROOT) - except: + except BaseException: pass # @@ -82,7 +80,7 @@ class Tlv(eeprom_tlvinfo.TlvInfoDecoder): # try: self.set_cache_name(os.path.join(CACHE_ROOT, CACHE_FILE)) - except: + except BaseException: pass e = self.read_eeprom() @@ -91,7 +89,7 @@ class Tlv(eeprom_tlvinfo.TlvInfoDecoder): try: self.update_cache(e) - except: + except BaseException: pass self.decode_eeprom(e) @@ -104,11 +102,40 @@ 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 + return self._valid_tlv(self._eeprom) + + def get_pn(self): + return self._eeprom.get('0x22', NULL) def get_serial(self): - return self._eeprom.get('0x23', "Undefined.") + return self._eeprom.get('0x23', NULL) def get_mac(self): - return self._eeprom.get('0x24', "Undefined.") + return self._eeprom.get('0x24', NULL) diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/fan.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/fan.py index bf2b27019b..586efbfbb1 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/fan.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/fan.py @@ -6,7 +6,7 @@ # ############################################################################# -import json +from __future__ import division import math import os.path @@ -25,6 +25,7 @@ EMC2305_FAN_TARGET = "fan{}_target" EMC2305_FAN_INPUT = "pwm{}" FAN_NAME_LIST = ["FAN-1F", "FAN-1R", "FAN-2F", "FAN-2R", "FAN-3F", "FAN-3R", "FAN-4F", "FAN-4R", "FAN-5F", "FAN-5R"] +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 = { @@ -82,7 +83,7 @@ class Fan(FanBase): try: with open(file_path, 'w') as fd: fd.write(str(value)) - except: + except Exception: return False return True @@ -97,7 +98,8 @@ class Fan(FanBase): def __get_gpio_base(self): for r in os.listdir(GPIO_DIR): label_path = os.path.join(GPIO_DIR, r, "label") - if "gpiochip" in r and GPIO_LABEL in self._api_helper.read_txt_file(label_path): + if "gpiochip" in r and GPIO_LABEL in \ + self._api_helper.read_txt_file(label_path): return int(r[8:], 10) return 216 # Reserve @@ -148,7 +150,6 @@ class Fan(FanBase): self.psu_hwmon_path, fan_speed_sysfs_name) fan_speed_rpm = self._api_helper.read_txt_file( fan_speed_sysfs_path) or 0 - fan_speed_raw = float(fan_speed_rpm)/PSU_FAN_MAX_RPM * 100 speed = math.ceil(float(fan_speed_rpm) * 100 / PSU_FAN_MAX_RPM) elif self.get_presence(): chip = self.emc2305_chip_mapping[self.fan_index] @@ -176,7 +177,19 @@ class Fan(FanBase): 0 : when PWM mode is use pwm : when pwm mode is not use """ - return 'N/A' + 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_PWM) + sysfs_path = sysfs_path.format(fan_index[self.fan_tray_index]) + pwm = self._api_helper.read_txt_file(sysfs_path) + target = round(int(pwm) / 255 * 100.0) + + return target def get_speed_tolerance(self): """ @@ -185,7 +198,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): """ @@ -225,41 +238,67 @@ class Fan(FanBase): bool: True if status LED state is set successfully, False if not """ set_status_led = False + + s1_gpio = self.dx010_fan_gpio[self.fan_tray_index+1]['color']['red'] + s2_gpio = self.dx010_fan_gpio[self.fan_tray_index+1]['color']['green'] + if not self.is_psu_fan: - s1, s2 = False, False try: if color == self.STATUS_LED_COLOR_GREEN: - s1 = self.__set_gpio_value( - self.dx010_fan_gpio[self.fan_tray_index+1]['color']['red'], 1) - s2 = self.__set_gpio_value( - self.dx010_fan_gpio[self.fan_tray_index+1]['color']['green'], 0) + s1 = self.__set_gpio_value(s1_gpio, 1) + s2 = self.__set_gpio_value(s2_gpio, 0) elif color == self.STATUS_LED_COLOR_RED: - s1 = self.__set_gpio_value( - self.dx010_fan_gpio[self.fan_tray_index+1]['color']['red'], 0) - s2 = self.__set_gpio_value( - self.dx010_fan_gpio[self.fan_tray_index+1]['color']['green'], 1) + s1 = self.__set_gpio_value(s1_gpio, 0) + s2 = self.__set_gpio_value(s2_gpio, 1) elif color == self.STATUS_LED_COLOR_OFF: - s1 = self.__set_gpio_value( - self.dx010_fan_gpio[self.fan_tray_index+1]['color']['red'], 1) - s2 = self.__set_gpio_value( - self.dx010_fan_gpio[self.fan_tray_index+1]['color']['green'], 1) + s1 = self.__set_gpio_value(s1_gpio, 1) + s2 = self.__set_gpio_value(s2_gpio, 1) + + elif color == self.STATUS_LED_COLOR_AMBER: + s1 = self.__set_gpio_value(s1_gpio, 0) + s2 = self.__set_gpio_value(s2_gpio, 0) + else: + s1, s2 = True, True + set_status_led = s1 and s2 - return set_status_led + except IOError: return False 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 + """ + s1 = self.__get_gpio_value( + self.dx010_fan_gpio[self.fan_tray_index+1]['color']['red']) + s2 = self.__get_gpio_value( + self.dx010_fan_gpio[self.fan_tray_index+1]['color']['green']) + + return { + '10': self.STATUS_LED_COLOR_GREEN, + '01': self.STATUS_LED_COLOR_RED, + '00': self.STATUS_LED_COLOR_AMBER + }.get(s1+s2, self.STATUS_LED_COLOR_OFF) + + ############################################################## + ###################### Device methods ######################## + ############################################################## + def get_name(self): """ Retrieves the name of the device Returns: string: The name of the device """ - fan_name = FAN_NAME_LIST[self.fan_tray_index*2 + self.fan_index] if not self.is_psu_fan else "PSU-{} FAN-{}".format( - self.psu_index+1, self.fan_index+1) + fan_name = FAN_NAME_LIST[self.fan_tray_index*2 + self.fan_index] \ + if not self.is_psu_fan \ + else "PSU-{} FAN-{}".format(self.psu_index+1, self.fan_index+1) return fan_name @@ -321,3 +360,24 @@ class Fan(FanBase): status = self._api_helper.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_seastone-r0/sonic_platform/fan_drawer.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/fan_drawer.py new file mode 100644 index 0000000000..e35ecf6519 --- /dev/null +++ b/device/celestica/x86_64-cel_seastone-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 = 2 + + +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_seastone-r0/sonic_platform/psu.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/psu.py index f9a342953a..8bc95fa4f0 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/psu.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/psu.py @@ -7,7 +7,6 @@ ############################################################################# import os -import sonic_platform try: from sonic_platform_base.psu_base import PsuBase @@ -16,6 +15,9 @@ try: except ImportError as e: raise ImportError(str(e) + "- required module not found") +TLV_ATTR_TYPE_MODEL = 2 +TLV_ATTR_TYPE_SERIAL = 5 +PSU_EEPROM_PATH = "/sys/bus/i2c/devices/{}-00{}/eeprom" GREEN_LED_PATH = "/sys/devices/platform/leds_dx010/leds/dx010:green:p-{}/brightness" HWMON_PATH = "/sys/bus/i2c/devices/i2c-{0}/{0}-00{1}/hwmon" GPIO_DIR = "/sys/class/gpio" @@ -25,11 +27,13 @@ PSU_NUM_FAN = [1, 1] PSU_I2C_MAPPING = { 0: { "num": 10, - "addr": "5a" + "addr": "5a", + "eeprom_addr": "52" }, 1: { "num": 11, - "addr": "5b" + "addr": "5b", + "eeprom_addr": "53" }, } @@ -41,7 +45,7 @@ class Psu(PsuBase): PsuBase.__init__(self) self.index = psu_index self._api_helper = APIHelper() - self.green_led_path = GREEN_LED_PATH.format(self.index+1) + self.green_led_path = GREEN_LED_PATH.format(self.index + 1) self.dx010_psu_gpio = [ {'base': self.__get_gpio_base()}, {'prs': 27, 'status': 22}, @@ -50,6 +54,7 @@ class Psu(PsuBase): 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) @@ -71,11 +76,37 @@ class Psu(PsuBase): def __get_gpio_value(self, pinnum): gpio_base = self.dx010_psu_gpio[0]['base'] - gpio_dir = GPIO_DIR + '/gpio' + str(gpio_base+pinnum) + gpio_dir = GPIO_DIR + '/gpio' + str(gpio_base + pinnum) gpio_file = gpio_dir + "/value" retval = self._api_helper.read_txt_file(gpio_file) return retval.rstrip('\r\n') + def read_fru(self, path, attr_type): + content = [] + attr_idx = 0 + attr_length = 0 + + if(os.path.exists(path)): + with open(path, 'r', encoding='unicode_escape') as f: + content = f.read() + target_offset = ord(content[4]) + target_offset *= 8 # spec defined: offset are in multiples of 8 bytes + + attr_idx = target_offset + 3 + for i in range(1, attr_type): + if attr_idx > len(content): + raise SyntaxError + attr_length = (ord(content[attr_idx])) & (0x3f) + attr_idx += (attr_length + 1) + + attr_length = (ord(content[attr_idx])) & (0x3f) + attr_idx += 1 + else: + print("[PSU] Can't find path to eeprom : %s" % path) + return SyntaxError + + return content[attr_idx:attr_idx + attr_length] + def get_voltage(self): """ Retrieves current PSU voltage output @@ -209,7 +240,7 @@ class Psu(PsuBase): Returns: bool: True if PSU is present, False if not """ - raw = self.__get_gpio_value(self.dx010_psu_gpio[self.index+1]['prs']) + raw = self.__get_gpio_value(self.dx010_psu_gpio[self.index + 1]['prs']) return int(raw, 10) == 0 def get_status(self): @@ -219,5 +250,142 @@ class Psu(PsuBase): A boolean value, True if device is operating properly, False if not """ raw = self.__get_gpio_value( - self.dx010_psu_gpio[self.index+1]['status']) + self.dx010_psu_gpio[self.index + 1]['status']) return int(raw, 10) == 1 + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + model = self.read_fru(self.eeprom_addr, TLV_ATTR_TYPE_MODEL) + if not model: + return "N/A" + return model + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + serial = self.read_fru(self.eeprom_addr, TLV_ATTR_TYPE_SERIAL) + if not serial: + return "N/A" + return serial + + 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_helper.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_helper.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 = 0.0 + 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)) + vout_val = self._api_helper.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 = 0.0 + 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)) + vout_val = self._api_helper.read_txt_file(vout_path) + psu_voltage = float(vout_val) / 1000 + + return psu_voltage diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/sfp.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/sfp.py index 13ae4b8a47..c1423ed8dc 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/sfp.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/sfp.py @@ -1,14 +1,15 @@ +#!/usr/bin/env python + ############################################################################# # Celestica # # Sfp contains an implementation of SONiC Platform Base API and -# provides the sfp device status which are available in the platform +# provides the sfp status which are available in the platform # ############################################################################# import time -import subprocess -from ctypes import create_string_buffer +import struct try: from sonic_platform_base.sfp_base import SfpBase @@ -17,13 +18,13 @@ try: 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 sonic_platform_base.sonic_sfp.qsfp_dd import qsfp_dd_InterfaceId + from sonic_platform_base.sonic_sfp.qsfp_dd import qsfp_dd_Dom + from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper from .helper import APIHelper except ImportError as e: raise ImportError(str(e) + "- required module not found") -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 @@ -55,6 +56,8 @@ XCVR_HW_REV_OFFSET = 56 XCVR_HW_REV_WIDTH_OSFP = 2 XCVR_HW_REV_WIDTH_QSFP = 2 XCVR_HW_REV_WIDTH_SFP = 4 +XCVR_EXT_SPECIFICATION_COMPLIANCE_OFFSET = 64 +XCVR_EXT_SPECIFICATION_COMPLIANCE_WIDTH = 1 XCVR_VENDOR_SN_OFFSET = 68 XCVR_VENDOR_SN_WIDTH = 16 XCVR_VENDOR_DATE_OFFSET = 84 @@ -69,6 +72,8 @@ QSFP_DOM_BULK_DATA_START = 22 QSFP_DOM_BULK_DATA_SIZE = 36 SFP_DOM_BULK_DATA_START = 96 SFP_DOM_BULK_DATA_SIZE = 10 +QSFP_DD_DOM_BULK_DATA_START = 14 +QSFP_DD_DOM_BULK_DATA_SIZE = 4 # definitions of the offset for values in OSFP info eeprom OSFP_TYPE_OFFSET = 0 @@ -77,6 +82,33 @@ OSFP_VENDOR_PN_OFFSET = 148 OSFP_HW_REV_OFFSET = 164 OSFP_VENDOR_SN_OFFSET = 166 +# definitions of the offset for values in QSFP_DD info eeprom +QSFP_DD_TYPE_OFFSET = 0 +QSFP_DD_VENDOR_NAME_OFFSET = 1 +QSFP_DD_VENDOR_PN_OFFSET = 20 +QSFP_DD_VENDOR_SN_OFFSET = 38 +QSFP_DD_VENDOR_OUI_OFFSET = 17 + +# definitions of the offset and width for values in XCVR_QSFP_DD info eeprom +XCVR_EXT_TYPE_OFFSET_QSFP_DD = 72 +XCVR_EXT_TYPE_WIDTH_QSFP_DD = 2 +XCVR_CONNECTOR_OFFSET_QSFP_DD = 75 +XCVR_CONNECTOR_WIDTH_QSFP_DD = 1 +XCVR_CABLE_LENGTH_OFFSET_QSFP_DD = 74 +XCVR_CABLE_LENGTH_WIDTH_QSFP_DD = 1 +XCVR_HW_REV_OFFSET_QSFP_DD = 36 +XCVR_HW_REV_WIDTH_QSFP_DD = 2 +XCVR_VENDOR_DATE_OFFSET_QSFP_DD = 54 +XCVR_VENDOR_DATE_WIDTH_QSFP_DD = 8 +XCVR_DOM_CAPABILITY_OFFSET_QSFP_DD = 2 +XCVR_DOM_CAPABILITY_WIDTH_QSFP_DD = 1 +XCVR_MEDIA_TYPE_OFFSET_QSFP_DD = 85 +XCVR_MEDIA_TYPE_WIDTH_QSFP_DD = 1 +XCVR_FIRST_APPLICATION_LIST_OFFSET_QSFP_DD = 86 +XCVR_FIRST_APPLICATION_LIST_WIDTH_QSFP_DD = 32 +XCVR_SECOND_APPLICATION_LIST_OFFSET_QSFP_DD = 351 +XCVR_SECOND_APPLICATION_LIST_WIDTH_QSFP_DD = 28 + # Offset for values in QSFP eeprom QSFP_DOM_REV_OFFSET = 1 QSFP_DOM_REV_WIDTH = 1 @@ -126,29 +158,102 @@ SFP_CHANNL_MON_WIDTH = 6 SFP_CHANNL_STATUS_OFFSET = 110 SFP_CHANNL_STATUS_WIDTH = 1 +QSFP_DD_TEMPE_OFFSET = 14 +QSFP_DD_TEMPE_WIDTH = 2 +QSFP_DD_VOLT_OFFSET = 16 +QSFP_DD_VOLT_WIDTH = 2 +QSFP_DD_TX_BIAS_OFFSET = 42 +QSFP_DD_TX_BIAS_WIDTH = 16 +QSFP_DD_RX_POWER_OFFSET = 58 +QSFP_DD_RX_POWER_WIDTH = 16 +QSFP_DD_TX_POWER_OFFSET = 26 +QSFP_DD_TX_POWER_WIDTH = 16 +QSFP_DD_CHANNL_MON_OFFSET = 154 +QSFP_DD_CHANNL_MON_WIDTH = 48 +QSFP_DD_CHANNL_DISABLE_STATUS_OFFSET = 86 +QSFP_DD_CHANNL_DISABLE_STATUS_WIDTH = 1 +QSFP_DD_CHANNL_RX_LOS_STATUS_OFFSET = 19 +QSFP_DD_CHANNL_RX_LOS_STATUS_WIDTH = 1 +QSFP_DD_CHANNL_TX_FAULT_STATUS_OFFSET = 7 +QSFP_DD_CHANNL_TX_FAULT_STATUS_WIDTH = 1 +QSFP_DD_MODULE_THRESHOLD_OFFSET = 0 +QSFP_DD_MODULE_THRESHOLD_WIDTH = 72 +QSFP_DD_CHANNL_STATUS_OFFSET = 26 +QSFP_DD_CHANNL_STATUS_WIDTH = 1 + + +sfp_cable_length_tup = ( + 'LengthSMFkm-UnitsOfKm', 'LengthSMF(UnitsOf100m)', + 'Length50um(UnitsOf10m)', 'Length62.5um(UnitsOfm)', + 'LengthCable(UnitsOfm)', 'LengthOM3(UnitsOf10m)' +) + +sfp_compliance_code_tup = ( + '10GEthernetComplianceCode', 'InfinibandComplianceCode', + 'ESCONComplianceCodes', 'SONETComplianceCodes', + 'EthernetComplianceCodes', 'FibreChannelLinkLength', + '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' +) + +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', 'application_advertisement', 'type_abbrv_name' +] 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)') +dom_info_dict_keys = [ + 'rx_los', 'tx_fault', 'reset_status', 'lp_mode', + 'tx_disable', 'tx_disable_channel', 'temperature', 'voltage', + 'rx1power', 'rx2power', 'rx3power', 'rx4power', + 'rx5power', 'rx6power', 'rx7power', 'rx8power', + 'tx1bias', 'tx2bias', 'tx3bias', 'tx4bias', + 'tx5bias', 'tx6bias', 'tx7bias', 'tx8bias', + 'tx1power', 'tx2power', 'tx3power', 'tx4power', + 'tx5power', 'tx6power', 'tx7power', 'tx8power'] -sfp_compliance_code_tup = ('10GEthernetComplianceCode', 'InfinibandComplianceCode', - 'ESCONComplianceCodes', 'SONETComplianceCodes', - 'EthernetComplianceCodes', 'FibreChannelLinkLength', - 'FibreChannelTechnology', 'SFP+CableTechnology', - 'FibreChannelTransmissionMedia', 'FibreChannelSpeed') +threshold_dict_keys = [ + 'temphighalarm', 'temphighwarning', + 'templowalarm', 'templowwarning', + 'vcchighalarm', 'vcchighwarning', + 'vcclowalarm', 'vcclowwarning', + 'rxpowerhighalarm', 'rxpowerhighwarning', + 'rxpowerlowalarm', 'rxpowerlowwarning', + 'txpowerhighalarm', 'txpowerhighwarning', + 'txpowerlowalarm', 'txpowerlowwarning', + 'txbiashighalarm', 'txbiashighwarning', + 'txbiaslowalarm', 'txbiaslowwarning'] -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_CODE_LIST = [ + '03' # SFP/SFP+/SFP28 +] +QSFP_TYPE_CODE_LIST = [ + '0d', # QSFP+ or later + '11' # QSFP28 or later +] +QSFP_DD_TYPE_CODE_LIST = [ + '18' # QSFP-DD Double Density 8X Pluggable Transceiver +] SFP_TYPE = "SFP" QSFP_TYPE = "QSFP" OSFP_TYPE = "OSFP" +QSFP_DD_TYPE = "QSFP_DD" + +NULL_VAL = 'N/A' PORT_START = 1 PORT_END = 56 @@ -156,60 +261,45 @@ QSFP_PORT_START = 1 QSFP_PORT_END = 32 SFP_I2C_START = 26 +I2C_EEPROM_PATH = '/sys/bus/i2c/devices/i2c-{0}/{0}-0050/eeprom' class Sfp(SfpBase): """Platform-specific Sfp class""" - # Path to QSFP sysfs RESET_PATH = "/sys/devices/platform/dx010_cpld/qsfp_reset" LP_PATH = "/sys/devices/platform/dx010_cpld/qsfp_lpmode" PRS_PATH = "/sys/devices/platform/dx010_cpld/qsfp_modprs" - def __init__(self, sfp_index, sfp_name): + def __init__(self, sfp_index=0, sfp_name=None): SfpBase.__init__(self) - # Init index - self.index = sfp_index - self.port_num = self.index + 1 - self.dom_supported = False - self.sfp_type, self.port_name = self.__get_sfp_info() + + self._index = sfp_index + self._port_num = self._index + 1 self._api_helper = APIHelper() - self.name = sfp_name - - # Init eeprom path - eeprom_path = '/sys/bus/i2c/devices/i2c-{0}/{0}-0050/eeprom' - self.port_to_eeprom_mapping = {} - self.port_to_i2c_mapping = {} - - for x in range(PORT_START, PORT_END + 1): - self.port_to_i2c_mapping[x] = (SFP_I2C_START + x) - 1 - port_eeprom_path = eeprom_path.format(self.port_to_i2c_mapping[x]) - self.port_to_eeprom_mapping[x] = port_eeprom_path - - 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'] - - 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'] - - 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 + self._read_porttab_mappings() self._dom_capability_detect() + self._eeprom_path = self._get_eeprom_path() - def __get_sfp_info(self): - port_name = "Unknown" - sfp_type = "Unknown" + def _read_porttab_mappings(self): + self._sfputil_helper = SfpUtilHelper() + self._sfputil_helper.read_porttab_mappings( + self._get_path_to_port_config_file()) - if self.port_num >= QSFP_PORT_START and self.port_num <= QSFP_TYPE: - sfp_type = QSFP_TYPE - port_name = "QSFP" + str(self.port_num - QSFP_PORT_START + 1) - elif self.port_num >= SFP_PORT_START and self.port_num <= SFP_PORT_END: - sfp_type = SFP_TYPE - port_name = "SFP" + str(self.port_num - SFP_PORT_START + 1) - return sfp_type, port_name + def _get_path_to_port_config_file(self): + host_platform_root_path = '/usr/share/sonic/device' + docker_hwsku_path = '/usr/share/sonic/hwsku' - def __convert_string_to_num(self, value_str): + host_platform_path = "/".join([host_platform_root_path, + self._api_helper.platform]) + hwsku_path = "/".join([host_platform_path, self._api_helper.hwsku]) \ + if self._api_helper.is_host() else docker_hwsku_path + + return "/".join([hwsku_path, "port_config.ini"]) + + def _convert_string_to_num(self, value_str): if "-inf" in value_str: return 'N/A' elif "Unknown" in value_str: @@ -229,28 +319,63 @@ class Sfp(SfpBase): else: return 'N/A' - def __read_eeprom_specific_bytes(self, offset, num_bytes): - sysfsfile_eeprom = None + def _read_eeprom_specific_bytes(self, offset, num_bytes): + sysfs_sfp_i2c_client_eeprom_path = self._get_eeprom_path() eeprom_raw = [] + try: + eeprom = open( + sysfs_sfp_i2c_client_eeprom_path, + mode="rb", buffering=0) + except IOError: + return None + for i in range(0, num_bytes): eeprom_raw.append("0x00") - sysfs_sfp_i2c_client_eeprom_path = self.port_to_eeprom_mapping[self.port_num] try: - sysfsfile_eeprom = open( - 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: - pass - finally: - if sysfsfile_eeprom: - sysfsfile_eeprom.close() + eeprom.seek(offset) + raw = eeprom.read(num_bytes) + except IOError: + eeprom.close() + return None + try: + 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: + eeprom.close() + return None + + eeprom.close() return eeprom_raw + def _detect_sfp_type(self): + sfp_type = QSFP_TYPE + eeprom_raw = [] + eeprom_raw = self._read_eeprom_specific_bytes( + XCVR_TYPE_OFFSET, XCVR_TYPE_WIDTH) + if eeprom_raw: + if eeprom_raw[0] in SFP_TYPE_CODE_LIST: + self.sfp_type = SFP_TYPE + elif eeprom_raw[0] in QSFP_TYPE_CODE_LIST: + self.sfp_type = QSFP_TYPE + elif eeprom_raw[0] in QSFP_DD_TYPE_CODE_LIST: + self.sfp_type = QSFP_DD_TYPE + else: + self.sfp_type = sfp_type + else: + self.sfp_type = sfp_type + + def _get_eeprom_path(self): + port_to_i2c_mapping = SFP_I2C_START + self._index + port_eeprom_path = I2C_EEPROM_PATH.format(port_to_i2c_mapping) + return port_eeprom_path + def _dom_capability_detect(self): if not self.get_presence(): self.dom_supported = False @@ -261,22 +386,28 @@ class Sfp(SfpBase): self.calibration = 0 return - if self.sfp_type == "QSFP": + self._detect_sfp_type() + + if self.sfp_type == QSFP_TYPE: 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. + # 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) + + 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_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( @@ -297,7 +428,7 @@ class Sfp(SfpBase): sfpd_obj = sff8436Dom() if sfpd_obj is None: return None - qsfp_option_value_raw = self.__read_eeprom_specific_bytes( + 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( @@ -316,11 +447,52 @@ class Sfp(SfpBase): self.calibration = 0 self.qsfp_page3_available = False - elif self.sfp_type == "SFP": + elif self.sfp_type == QSFP_DD_TYPE: + sfpi_obj = qsfp_dd_InterfaceId() + if sfpi_obj is None: + self.dom_supported = False + + offset = 0 + # two types of QSFP-DD cable types supported: Copper and Optical. + qsfp_dom_capability_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_DOM_CAPABILITY_OFFSET_QSFP_DD), XCVR_DOM_CAPABILITY_WIDTH_QSFP_DD) + if qsfp_dom_capability_raw is not None: + self.dom_temp_supported = True + self.dom_volt_supported = True + dom_capability = sfpi_obj.parse_dom_capability( + qsfp_dom_capability_raw, 0) + if dom_capability['data']['Flat_MEM']['value'] == 'Off': + self.dom_supported = True + self.second_application_list = True + self.dom_rx_power_supported = True + self.dom_tx_power_supported = True + self.dom_tx_bias_power_supported = True + self.dom_thresholds_supported = True + # currently set to False becasue Page 11h is not supported by FW + self.dom_rx_tx_power_bias_supported = False + else: + self.dom_supported = False + self.second_application_list = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + self.dom_tx_bias_power_supported = False + self.dom_thresholds_supported = False + self.dom_rx_tx_power_bias_supported = False + 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.dom_tx_bias_power_supported = False + self.dom_thresholds_supported = False + self.dom_rx_tx_power_bias_supported = False + + elif self.sfp_type == SFP_TYPE: sfpi_obj = sff8472InterfaceId() if sfpi_obj is None: return None - sfp_dom_capability_raw = self.__read_eeprom_specific_bytes( + 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) @@ -356,7 +528,7 @@ class Sfp(SfpBase): Retrieves transceiver info of this SFP Returns: A dict which contains following keys/values : - ======================================================================== + ================================================================================ keys |Value Format |Information ---------------------------|---------------|---------------------------- type |1*255VCHAR |type of SFP @@ -373,12 +545,15 @@ class Sfp(SfpBase): specification_compliance |1*255VCHAR |specification compliance vendor_date |1*255VCHAR |vendor date vendor_oui |1*255VCHAR |vendor OUI - ======================================================================== - """ + application_advertisement |1*255VCHAR |supported applications advertisement + ================================================================================ + """ + + transceiver_info_dict = {} compliance_code_dict = {} - transceiver_info_dict = dict.fromkeys(self.info_dict_keys, 'N/A') - if not self.get_presence(): - return transceiver_info_dict + transceiver_info_dict = dict.fromkeys( + info_dict_keys, NULL_VAL) + transceiver_info_dict["specification_compliance"] = '{}' # ToDo: OSFP tranceiver info parsing not fully supported. # in inf8628.py lack of some memory map definition @@ -389,89 +564,229 @@ class Sfp(SfpBase): sfpi_obj = inf8628InterfaceId() if sfpi_obj is None: - return None + return transceiver_info_dict - sfp_type_raw = self.__read_eeprom_specific_bytes( + 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 + return transceiver_info_dict - sfp_vendor_name_raw = self.__read_eeprom_specific_bytes( + 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 + return transceiver_info_dict - sfp_vendor_pn_raw = self.__read_eeprom_specific_bytes( + 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 + return transceiver_info_dict - sfp_vendor_rev_raw = self.__read_eeprom_specific_bytes( + 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 + return transceiver_info_dict - sfp_vendor_sn_raw = self.__read_eeprom_specific_bytes( + 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 + return transceiver_info_dict 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' + + elif self.sfp_type == QSFP_TYPE: + offset = 128 + vendor_rev_width = XCVR_HW_REV_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 transceiver_info_dict + + elif self.sfp_type == QSFP_DD_TYPE: + offset = 128 + + sfpi_obj = qsfp_dd_InterfaceId() + if sfpi_obj is None: + print("Error: sfp_object open failed") + return transceiver_info_dict + + sfp_type_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_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 transceiver_info_dict + + sfp_vendor_name_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_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 transceiver_info_dict + + sfp_vendor_pn_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_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 transceiver_info_dict + + sfp_vendor_rev_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_HW_REV_OFFSET_QSFP_DD), XCVR_HW_REV_WIDTH_QSFP_DD) + if sfp_vendor_rev_raw is not None: + sfp_vendor_rev_data = sfpi_obj.parse_vendor_rev( + sfp_vendor_rev_raw, 0) + else: + return transceiver_info_dict + + sfp_vendor_sn_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_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 transceiver_info_dict + + sfp_vendor_oui_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_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) + else: + return transceiver_info_dict + + sfp_vendor_date_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_VENDOR_DATE_OFFSET_QSFP_DD), XCVR_VENDOR_DATE_WIDTH_QSFP_DD) + if sfp_vendor_date_raw is not None: + sfp_vendor_date_data = sfpi_obj.parse_vendor_date( + sfp_vendor_date_raw, 0) + else: + return transceiver_info_dict + + sfp_connector_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_CONNECTOR_OFFSET_QSFP_DD), XCVR_CONNECTOR_WIDTH_QSFP_DD) + if sfp_connector_raw is not None: + sfp_connector_data = sfpi_obj.parse_connector( + sfp_connector_raw, 0) + else: + return transceiver_info_dict + + sfp_ext_identifier_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_EXT_TYPE_OFFSET_QSFP_DD), XCVR_EXT_TYPE_WIDTH_QSFP_DD) + if sfp_ext_identifier_raw is not None: + sfp_ext_identifier_data = sfpi_obj.parse_ext_iden( + sfp_ext_identifier_raw, 0) + else: + return transceiver_info_dict + + sfp_cable_len_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_CABLE_LENGTH_OFFSET_QSFP_DD), XCVR_CABLE_LENGTH_WIDTH_QSFP_DD) + if sfp_cable_len_raw is not None: + sfp_cable_len_data = sfpi_obj.parse_cable_len( + sfp_cable_len_raw, 0) + else: + return transceiver_info_dict + + sfp_media_type_raw = self._read_eeprom_specific_bytes( + XCVR_MEDIA_TYPE_OFFSET_QSFP_DD, XCVR_MEDIA_TYPE_WIDTH_QSFP_DD) + if sfp_media_type_raw is not None: + sfp_media_type_dict = sfpi_obj.parse_media_type( + sfp_media_type_raw, 0) + if sfp_media_type_dict is None: + return transceiver_info_dict + + host_media_list = "" + sfp_application_type_first_list = self._read_eeprom_specific_bytes( + (XCVR_FIRST_APPLICATION_LIST_OFFSET_QSFP_DD), XCVR_FIRST_APPLICATION_LIST_WIDTH_QSFP_DD) + if self.second_application_list: + possible_application_count = 15 + sfp_application_type_second_list = self._read_eeprom_specific_bytes( + (XCVR_SECOND_APPLICATION_LIST_OFFSET_QSFP_DD), XCVR_SECOND_APPLICATION_LIST_WIDTH_QSFP_DD) + if sfp_application_type_first_list is not None and sfp_application_type_second_list is not None: + sfp_application_type_list = sfp_application_type_first_list + \ + sfp_application_type_second_list + else: + return transceiver_info_dict + else: + possible_application_count = 8 + if sfp_application_type_first_list is not None: + sfp_application_type_list = sfp_application_type_first_list + else: + return transceiver_info_dict + + for i in range(0, possible_application_count): + if sfp_application_type_list[i * 4] == 'ff': + break + host_electrical, media_interface = sfpi_obj.parse_application( + sfp_media_type_dict, sfp_application_type_list[i * 4], sfp_application_type_list[i * 4 + 1]) + host_media_list = host_media_list + host_electrical + \ + ' - ' + media_interface + '\n\t\t\t\t ' + else: + return transceiver_info_dict + + transceiver_info_dict['type'] = str( + sfp_type_data['data']['type']['value']) + transceiver_info_dict['manufacturer'] = str( + sfp_vendor_name_data['data']['Vendor Name']['value']) + transceiver_info_dict['model'] = str( + sfp_vendor_pn_data['data']['Vendor PN']['value']) + transceiver_info_dict['hardware_rev'] = str( + sfp_vendor_rev_data['data']['Vendor Rev']['value']) + transceiver_info_dict['serial'] = str( + sfp_vendor_sn_data['data']['Vendor SN']['value']) + transceiver_info_dict['vendor_oui'] = str( + sfp_vendor_oui_data['data']['Vendor OUI']['value']) + transceiver_info_dict['vendor_date'] = str( + sfp_vendor_date_data['data']['VendorDataCode(YYYY-MM-DD Lot)']['value']) + transceiver_info_dict['connector'] = str( + sfp_connector_data['data']['Connector']['value']) + transceiver_info_dict['encoding'] = "Not supported for CMIS cables" + transceiver_info_dict['ext_identifier'] = str( + sfp_ext_identifier_data['data']['Extended Identifier']['value']) + transceiver_info_dict['ext_rateselect_compliance'] = "Not supported for CMIS cables" + transceiver_info_dict['specification_compliance'] = "Not supported for CMIS cables" + transceiver_info_dict['cable_type'] = "Length Cable Assembly(m)" + transceiver_info_dict['cable_length'] = str( + sfp_cable_len_data['data']['Length Cable Assembly(m)']['value']) + transceiver_info_dict['nominal_bit_rate'] = "Not supported for CMIS cables" + transceiver_info_dict['application_advertisement'] = host_media_list 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 + offset = 0 + vendor_rev_width = XCVR_HW_REV_WIDTH_SFP + interface_info_bulk_width = XCVR_INTFACE_BULK_WIDTH_SFP - sfpi_obj = sff8436InterfaceId() - if sfpi_obj is None: - print("Error: sfp_object open failed") - return None + sfpi_obj = sff8472InterfaceId() + if sfpi_obj is None: + print("Error: sfp_object open failed") + return transceiver_info_dict - 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( + if self.sfp_type != QSFP_DD_TYPE: + 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 + return transceiver_info_dict start = XCVR_INTFACE_BULK_OFFSET - XCVR_INTERFACE_DATA_START end = start + interface_info_bulk_width @@ -507,6 +822,7 @@ class Sfp(SfpBase): 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'] @@ -530,9 +846,16 @@ class Sfp(SfpBase): 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'] + sfp_ext_specification_compliance_raw = self._read_eeprom_specific_bytes( + offset + XCVR_EXT_SPECIFICATION_COMPLIANCE_OFFSET, XCVR_EXT_SPECIFICATION_COMPLIANCE_WIDTH) + if sfp_ext_specification_compliance_raw is not None: + sfp_ext_specification_compliance_data = sfpi_obj.parse_ext_specification_compliance( + sfp_ext_specification_compliance_raw[0: 1], 0) + if sfp_ext_specification_compliance_data['data']['Extended Specification compliance']['value'] != "Unspecified": + compliance_code_dict['Extended Specification compliance'] = sfp_ext_specification_compliance_data[ + 'data']['Extended Specification compliance']['value'] transceiver_info_dict['specification_compliance'] = str( compliance_code_dict) - transceiver_info_dict['nominal_bit_rate'] = str( sfp_interface_bulk_data['data']['Nominal Bit Rate(100Mbs)']['value']) else: @@ -578,7 +901,8 @@ class Sfp(SfpBase): | |for example, tx2power stands for tx power of channel 2. ======================================================================== """ - transceiver_dom_info_dict = dict.fromkeys(self.dom_dict_keys, 'N/A') + transceiver_dom_info_dict = dict.fromkeys( + dom_info_dict_keys, NULL_VAL) if self.sfp_type == OSFP_TYPE: pass @@ -592,7 +916,7 @@ class Sfp(SfpBase): if sfpd_obj is None: return transceiver_dom_info_dict - dom_data_raw = self.__read_eeprom_specific_bytes( + 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 @@ -602,8 +926,7 @@ class Sfp(SfpBase): 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']) + temp = dom_temperature_data['data']['Temperature']['value'] if temp is not None: transceiver_dom_info_dict['temperature'] = temp @@ -612,8 +935,7 @@ class Sfp(SfpBase): 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']) + volt = dom_voltage_data['data']['Vcc']['value'] if volt is not None: transceiver_dom_info_dict['voltage'] = volt @@ -623,33 +945,116 @@ class Sfp(SfpBase): 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']) + transceiver_dom_info_dict['tx1power'] = dom_channel_monitor_data['data']['TX1Power']['value'] + transceiver_dom_info_dict['tx2power'] = dom_channel_monitor_data['data']['TX2Power']['value'] + transceiver_dom_info_dict['tx3power'] = dom_channel_monitor_data['data']['TX3Power']['value'] + transceiver_dom_info_dict['tx4power'] = 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['rx1power'] = dom_channel_monitor_data['data']['RX1Power']['value'] + transceiver_dom_info_dict['rx2power'] = dom_channel_monitor_data['data']['RX2Power']['value'] + transceiver_dom_info_dict['rx3power'] = dom_channel_monitor_data['data']['RX3Power']['value'] + transceiver_dom_info_dict['rx4power'] = 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']) + transceiver_dom_info_dict['tx1bias'] = dom_channel_monitor_data['data']['TX1Bias']['value'] + transceiver_dom_info_dict['tx2bias'] = dom_channel_monitor_data['data']['TX2Bias']['value'] + transceiver_dom_info_dict['tx3bias'] = dom_channel_monitor_data['data']['TX3Bias']['value'] + transceiver_dom_info_dict['tx4bias'] = dom_channel_monitor_data['data']['TX4Bias']['value'] + + elif self.sfp_type == QSFP_DD_TYPE: + + offset = 0 + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return transceiver_dom_info_dict + + dom_data_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_DOM_BULK_DATA_START), QSFP_DD_DOM_BULK_DATA_SIZE) + if dom_data_raw is None: + return transceiver_dom_info_dict + + if self.dom_temp_supported: + start = QSFP_DD_TEMPE_OFFSET - QSFP_DD_DOM_BULK_DATA_START + end = start + QSFP_DD_TEMPE_WIDTH + dom_temperature_data = sfpd_obj.parse_temperature( + dom_data_raw[start: end], 0) + temp = dom_temperature_data['data']['Temperature']['value'] + if temp is not None: + transceiver_dom_info_dict['temperature'] = temp + + if self.dom_volt_supported: + start = QSFP_DD_VOLT_OFFSET - QSFP_DD_DOM_BULK_DATA_START + end = start + QSFP_DD_VOLT_WIDTH + dom_voltage_data = sfpd_obj.parse_voltage( + dom_data_raw[start: end], 0) + volt = dom_voltage_data['data']['Vcc']['value'] + if volt is not None: + transceiver_dom_info_dict['voltage'] = volt + + if self.dom_rx_tx_power_bias_supported: + # page 11h + dom_data_raw = self._read_eeprom_specific_bytes( + (QSFP_DD_CHANNL_MON_OFFSET), QSFP_DD_CHANNL_MON_WIDTH) + if dom_data_raw is None: + return transceiver_dom_info_dict + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params( + dom_data_raw, 0) + + if self.dom_tx_power_supported: + transceiver_dom_info_dict['tx1power'] = str( + dom_channel_monitor_data['data']['TX1Power']['value']) + transceiver_dom_info_dict['tx2power'] = str( + dom_channel_monitor_data['data']['TX2Power']['value']) + transceiver_dom_info_dict['tx3power'] = str( + dom_channel_monitor_data['data']['TX3Power']['value']) + transceiver_dom_info_dict['tx4power'] = str( + dom_channel_monitor_data['data']['TX4Power']['value']) + transceiver_dom_info_dict['tx5power'] = str( + dom_channel_monitor_data['data']['TX5Power']['value']) + transceiver_dom_info_dict['tx6power'] = str( + dom_channel_monitor_data['data']['TX6Power']['value']) + transceiver_dom_info_dict['tx7power'] = str( + dom_channel_monitor_data['data']['TX7Power']['value']) + transceiver_dom_info_dict['tx8power'] = str( + dom_channel_monitor_data['data']['TX8Power']['value']) + + if self.dom_rx_power_supported: + transceiver_dom_info_dict['rx1power'] = str( + dom_channel_monitor_data['data']['RX1Power']['value']) + transceiver_dom_info_dict['rx2power'] = str( + dom_channel_monitor_data['data']['RX2Power']['value']) + transceiver_dom_info_dict['rx3power'] = str( + dom_channel_monitor_data['data']['RX3Power']['value']) + transceiver_dom_info_dict['rx4power'] = str( + dom_channel_monitor_data['data']['RX4Power']['value']) + transceiver_dom_info_dict['rx5power'] = str( + dom_channel_monitor_data['data']['RX5Power']['value']) + transceiver_dom_info_dict['rx6power'] = str( + dom_channel_monitor_data['data']['RX6Power']['value']) + transceiver_dom_info_dict['rx7power'] = str( + dom_channel_monitor_data['data']['RX7Power']['value']) + transceiver_dom_info_dict['rx8power'] = str( + dom_channel_monitor_data['data']['RX8Power']['value']) + + if self.dom_tx_bias_power_supported: + transceiver_dom_info_dict['tx1bias'] = str( + dom_channel_monitor_data['data']['TX1Bias']['value']) + transceiver_dom_info_dict['tx2bias'] = str( + dom_channel_monitor_data['data']['TX2Bias']['value']) + transceiver_dom_info_dict['tx3bias'] = str( + dom_channel_monitor_data['data']['TX3Bias']['value']) + transceiver_dom_info_dict['tx4bias'] = str( + dom_channel_monitor_data['data']['TX4Bias']['value']) + transceiver_dom_info_dict['tx5bias'] = str( + dom_channel_monitor_data['data']['TX5Bias']['value']) + transceiver_dom_info_dict['tx6bias'] = str( + dom_channel_monitor_data['data']['TX6Bias']['value']) + transceiver_dom_info_dict['tx7bias'] = str( + dom_channel_monitor_data['data']['TX7Bias']['value']) + transceiver_dom_info_dict['tx8bias'] = str( + dom_channel_monitor_data['data']['TX8Bias']['value']) + + return transceiver_dom_info_dict else: if not self.dom_supported: @@ -661,7 +1066,7 @@ class Sfp(SfpBase): return transceiver_dom_info_dict sfpd_obj._calibration_type = self.calibration - dom_data_raw = self.__read_eeprom_specific_bytes( + 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 @@ -679,27 +1084,29 @@ class Sfp(SfpBase): dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params( dom_data_raw[start: end], 0) - 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['temperature'] = dom_temperature_data['data']['Temperature']['value'] + transceiver_dom_info_dict['voltage'] = dom_voltage_data['data']['Vcc']['value'] + transceiver_dom_info_dict['rx1power'] = dom_channel_monitor_data['data']['RXPower']['value'] + transceiver_dom_info_dict['tx1bias'] = dom_channel_monitor_data['data']['TXBias']['value'] + transceiver_dom_info_dict['tx1power'] = 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() - transceiver_dom_info_dict['reset_status'] = self.get_reset_status() transceiver_dom_info_dict['lp_mode'] = self.get_lpmode() + transceiver_dom_info_dict['reset_status'] = self.get_reset_status() + transceiver_dom_info_dict['tx_disable'] = self.get_tx_disable() + transceiver_dom_info_dict['tx_disabled_channel'] = self.get_tx_disable_channel( + ) + + for key in transceiver_dom_info_dict: + val = transceiver_dom_info_dict[key] + transceiver_dom_info_dict[key] = self._convert_string_to_num( + val) if type(val) is str else val return transceiver_dom_info_dict def get_transceiver_threshold_info(self): """ Retrieves transceiver threshold info of this SFP + Returns: A dict which contains following keys/values : ======================================================================== @@ -728,7 +1135,7 @@ class Sfp(SfpBase): ======================================================================== """ transceiver_dom_threshold_info_dict = dict.fromkeys( - self.threshold_dict_keys, 'N/A') + threshold_dict_keys, NULL_VAL) if self.sfp_type == OSFP_TYPE: pass @@ -744,7 +1151,7 @@ class Sfp(SfpBase): if sfpd_obj is None: return transceiver_dom_threshold_info_dict - dom_module_threshold_raw = self.__read_eeprom_specific_bytes( + 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 @@ -752,8 +1159,8 @@ class Sfp(SfpBase): 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) + 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( @@ -781,6 +1188,49 @@ class Sfp(SfpBase): 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'] + elif self.sfp_type == QSFP_DD_TYPE: + if not self.dom_supported: + return transceiver_dom_threshold_info_dict + + if not self.dom_thresholds_supported: + return transceiver_dom_threshold_info_dict + + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return transceiver_dom_threshold_info_dict + + # page 02 + offset = 384 + dom_module_threshold_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_MODULE_THRESHOLD_OFFSET), QSFP_DD_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) + + # 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_module_threshold_data['data']['RxPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighwarning'] = dom_module_threshold_data['data']['RxPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowalarm'] = dom_module_threshold_data['data']['RxPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowwarning'] = dom_module_threshold_data['data']['RxPowerLowWarning']['value'] + transceiver_dom_threshold_info_dict['txbiashighalarm'] = dom_module_threshold_data['data']['TxBiasHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiashighwarning'] = dom_module_threshold_data['data']['TxBiasHighWarning']['value'] + transceiver_dom_threshold_info_dict['txbiaslowalarm'] = dom_module_threshold_data['data']['TxBiasLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiaslowwarning'] = dom_module_threshold_data['data']['TxBiasLowWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerhighalarm'] = dom_module_threshold_data['data']['TxPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerhighwarning'] = dom_module_threshold_data['data']['TxPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerlowalarm'] = dom_module_threshold_data['data']['TxPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerlowwarning'] = dom_module_threshold_data['data']['TxPowerLowWarning']['value'] + else: offset = SFP_MODULE_ADDRA2_OFFSET @@ -791,8 +1241,8 @@ class Sfp(SfpBase): 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) + 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) @@ -823,7 +1273,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 @@ -834,13 +1284,14 @@ class Sfp(SfpBase): Returns: A Boolean, True if reset enabled, False if disabled """ - reset_status_raw = self._api_helper.read_txt_file(self.RESET_PATH).rstrip() + reset_status_raw = self._api_helper.read_txt_file( + self.RESET_PATH).rstrip() if not reset_status_raw: return False reg_value = int(reset_status_raw, 16) bin_format = bin(reg_value)[2:].zfill(32) - return bin_format[::-1][self.index] == '0' + return bin_format[::-1][self._index] == '0' def get_rx_los(self): """ @@ -849,30 +1300,53 @@ class Sfp(SfpBase): A Boolean, True if SFP has RX LOS, False if not. Note : RX LOS status is latched until a call to get_rx_los or a reset. """ - rx_los = False - if self.sfp_type == OSFP_TYPE: - return False + rx_los_list = [] + if self.sfp_type == OSFP_TYPE: + return None 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) + 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) + rx_los_list.append(rx_los_data & 0x01 != 0) + rx_los_list.append(rx_los_data & 0x02 != 0) + rx_los_list.append(rx_los_data & 0x04 != 0) + rx_los_list.append(rx_los_data & 0x08 != 0) + else: + return [False] * 4 + + elif self.sfp_type == QSFP_DD_TYPE: + # page 11h + if self.dom_rx_tx_power_bias_supported: + offset = 128 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_CHANNL_RX_LOS_STATUS_OFFSET), + QSFP_DD_CHANNL_RX_LOS_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + rx_los_data = int(dom_channel_monitor_raw[0], 8) + rx_los_list.append(rx_los_data & 0x01 != 0) + rx_los_list.append(rx_los_data & 0x02 != 0) + rx_los_list.append(rx_los_data & 0x04 != 0) + rx_los_list.append(rx_los_data & 0x08 != 0) + rx_los_list.append(rx_los_data & 0x10 != 0) + rx_los_list.append(rx_los_data & 0x20 != 0) + rx_los_list.append(rx_los_data & 0x40 != 0) + rx_los_list.append(rx_los_data & 0x80 != 0) + else: + return [False] * 8 else: offset = 256 - dom_channel_monitor_raw = self.__read_eeprom_specific_bytes( + 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 + rx_los_list.append(rx_los_data & 0x02 != 0) + else: + return [False] + return rx_los_list def get_tx_fault(self): """ @@ -881,66 +1355,107 @@ 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. """ - tx4_fault = False - - if self.sfp_type == OSFP_TYPE or not self.dom_supported: - return False + tx_fault_list = [] + if self.sfp_type == OSFP_TYPE: + return None 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) + 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) + tx_fault_list.append(tx_fault_data & 0x01 != 0) + tx_fault_list.append(tx_fault_data & 0x02 != 0) + tx_fault_list.append(tx_fault_data & 0x04 != 0) + tx_fault_list.append(tx_fault_data & 0x08 != 0) + else: + return [False] * 4 + elif self.sfp_type == QSFP_DD_TYPE: + # page 11h + if self.dom_rx_tx_power_bias_supported: + offset = 128 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_CHANNL_TX_FAULT_STATUS_OFFSET), + QSFP_DD_CHANNL_TX_FAULT_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + tx_fault_data = int(dom_channel_monitor_raw[0], 8) + tx_fault_list.append(tx_fault_data & 0x01 != 0) + tx_fault_list.append(tx_fault_data & 0x02 != 0) + tx_fault_list.append(tx_fault_data & 0x04 != 0) + tx_fault_list.append(tx_fault_data & 0x08 != 0) + tx_fault_list.append(tx_fault_data & 0x10 != 0) + tx_fault_list.append(tx_fault_data & 0x20 != 0) + tx_fault_list.append(tx_fault_data & 0x40 != 0) + tx_fault_list.append(tx_fault_data & 0x80 != 0) + else: + return [False] * 8 else: offset = 256 - dom_channel_monitor_raw = self.__read_eeprom_specific_bytes( + 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 + tx_fault_list.append(tx_fault_data & 0x04 != 0) + else: + return None + return tx_fault_list def get_tx_disable(self): """ Retrieves the tx_disable status of this SFP Returns: - A Boolean, True if tx_disable is enabled, False if disabled + A list of boolean values, representing the TX disable status + of each available channel, value is True if SFP channel + is TX disabled, False if not. + E.g., for a tranceiver with four channels: [False, False, True, False] """ - - tx_disable = False - - if self.sfp_type == OSFP_TYPE and not self.dom_supported: - return False - + tx_disable_list = [] + if self.sfp_type == OSFP_TYPE: + return None elif self.sfp_type == QSFP_TYPE: offset = 0 - dom_channel_monitor_raw = self.__read_eeprom_specific_bytes( - (offset + QSFP_CHANNL_DISABLE_STATUS_OFFSET), QSFP_CHANNL_DISABLE_STATUS_WIDTH) + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CHANNL_DISABLE_STATUS_OFFSET), + QSFP_CHANNL_DISABLE_STATUS_WIDTH) if dom_channel_monitor_raw is not None: tx_disable_data = int(dom_channel_monitor_raw[0], 16) - tx1_disable = (tx_disable_data & 0x01 != 0) - tx2_disable = (tx_disable_data & 0x02 != 0) - tx3_disable = (tx_disable_data & 0x04 != 0) - tx4_disable = (tx_disable_data & 0x08 != 0) - tx_disable = ( - tx1_disable and tx2_disable and tx3_disable and tx4_disable) + tx_disable_list.append(tx_disable_data & 0x01 != 0) + tx_disable_list.append(tx_disable_data & 0x02 != 0) + tx_disable_list.append(tx_disable_data & 0x04 != 0) + tx_disable_list.append(tx_disable_data & 0x08 != 0) + else: + return [False] * 4 + + elif self.sfp_type == QSFP_DD_TYPE: + if self.dom_rx_tx_power_bias_supported: + offset = 128 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_CHANNL_DISABLE_STATUS_OFFSET), + QSFP_DD_CHANNL_DISABLE_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + tx_disable_data = int(dom_channel_monitor_raw[0], 16) + tx_disable_list.append(tx_disable_data & 0x01 != 0) + tx_disable_list.append(tx_disable_data & 0x02 != 0) + tx_disable_list.append(tx_disable_data & 0x04 != 0) + tx_disable_list.append(tx_disable_data & 0x08 != 0) + tx_disable_list.append(tx_disable_data & 0x10 != 0) + tx_disable_list.append(tx_disable_data & 0x20 != 0) + tx_disable_list.append(tx_disable_data & 0x40 != 0) + tx_disable_list.append(tx_disable_data & 0x80 != 0) + else: + return [False] * 8 else: offset = 256 - dom_channel_monitor_raw = self.__read_eeprom_specific_bytes( + 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_disable_data = int(dom_channel_monitor_raw[0], 16) - tx_disable = (tx_disable_data & 0xC0 != 0) - - return tx_disable + tx_disable_list.append(tx_disable_data & 0xC0 != 0) + else: + return [False] + return tx_disable_list def get_tx_disable_channel(self): """ @@ -948,32 +1463,12 @@ class Sfp(SfpBase): Returns: A hex of 4 bits (bit 0 to bit 3 as channel 0 to channel 3) to represent TX channels which have been disabled in this SFP. - As an example, a returned value of 0x5 indicates that channel 0 + As an example, a returned value of 0x5 indicates that channel 0 and channel 2 have been disabled. """ - tx_disable_list = [False, False, False, False] - - if self.sfp_type == OSFP_TYPE: - pass - - elif self.sfp_type == QSFP_TYPE: - offset = 0 - dom_channel_monitor_raw = self.__read_eeprom_specific_bytes( - (offset + QSFP_CHANNL_DISABLE_STATUS_OFFSET), QSFP_CHANNL_DISABLE_STATUS_WIDTH) - if dom_channel_monitor_raw is not None: - tx_disable_data = int(dom_channel_monitor_raw[0], 16) - tx_disable_list[0] = (tx_disable_data & 0x01 != 0) - tx_disable_list[1] = (tx_disable_data & 0x02 != 0) - tx_disable_list[2] = (tx_disable_data & 0x04 != 0) - tx_disable_list[3] = (tx_disable_data & 0x08 != 0) - 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_disable_data = int(dom_channel_monitor_raw[0], 16) - tx_disable_list[0] = (tx_disable_data & 0xC0 != 0) - + tx_disable_list = self.get_tx_disable() + if tx_disable_list is None: + return 0 tx_disabled = 0 for i in range(len(tx_disable_list)): if tx_disable_list[i]: @@ -997,7 +1492,7 @@ class Sfp(SfpBase): reg_value = int(content, 16) # Determind if port_num start from 1 or 0 - bit_index = self.index + bit_index = self._index # Mask off the bit corresponding to our port mask = (1 << bit_index) @@ -1020,16 +1515,16 @@ class Sfp(SfpBase): if sfpd_obj is None: return False - dom_control_raw = self.__read_eeprom_specific_bytes( - QSFP_CONTROL_OFFSET, QSFP_CONTROL_WIDTH) if self.get_presence() else None + dom_control_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CONTROL_OFFSET), QSFP_CONTROL_WIDTH) if dom_control_raw is not None: dom_control_data = sfpd_obj.parse_control_bytes( dom_control_raw, 0) - power_override = ( - 'On' == dom_control_data['data']['PowerOverride']['value']) - return power_override + return ('On' == dom_control_data['data']['PowerOverride']) + else: + return False else: - return False + return NotImplementedError def get_temperature(self): """ @@ -1037,8 +1532,61 @@ class Sfp(SfpBase): Returns: An integer number of current temperature in Celsius """ - transceiver_bulk_status = self.get_transceiver_bulk_status() - return transceiver_bulk_status.get("temperature", "N/A") + default = 0.0 + if not self.dom_supported: + return default + + if self.sfp_type == QSFP_TYPE: + offset = 0 + + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return default + + if self.dom_temp_supported: + dom_temperature_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_TEMPE_OFFSET), QSFP_TEMPE_WIDTH) + if dom_temperature_raw is not None: + dom_temperature_data = sfpd_obj.parse_temperature( + dom_temperature_raw, 0) + temp = self._convert_string_to_num( + dom_temperature_data['data']['Temperature']['value']) + return temp + + elif self.sfp_type == QSFP_DD_TYPE: + offset = 0 + + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return default + + if self.dom_temp_supported: + dom_temperature_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_TEMPE_OFFSET), QSFP_DD_TEMPE_WIDTH) + if dom_temperature_raw is not None: + dom_temperature_data = sfpd_obj.parse_temperature( + dom_temperature_raw, 0) + temp = self._convert_string_to_num( + dom_temperature_data['data']['Temperature']['value']) + return temp + + else: + offset = 256 + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return default + sfpd_obj._calibration_type = 1 + + dom_temperature_raw = self._read_eeprom_specific_bytes( + (offset + SFP_TEMPE_OFFSET), SFP_TEMPE_WIDTH) + if dom_temperature_raw is not None: + dom_temperature_data = sfpd_obj.parse_temperature( + dom_temperature_raw, 0) + temp = self._convert_string_to_num( + dom_temperature_data['data']['Temperature']['value']) + return temp + + return default def get_voltage(self): """ @@ -1046,8 +1594,62 @@ class Sfp(SfpBase): Returns: An integer number of supply voltage in mV """ - transceiver_bulk_status = self.get_transceiver_bulk_status() - return transceiver_bulk_status.get("voltage", "N/A") + default = 0.0 + + if not self.dom_supported: + return default + + if self.sfp_type == QSFP_TYPE: + offset = 0 + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return default + + if self.dom_volt_supported: + dom_voltage_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_VOLT_OFFSET), QSFP_VOLT_WIDTH) + if dom_voltage_raw is not None: + dom_voltage_data = sfpd_obj.parse_voltage( + dom_voltage_raw, 0) + voltage = self._convert_string_to_num( + dom_voltage_data['data']['Vcc']['value']) + return voltage + + if self.sfp_type == QSFP_DD_TYPE: + offset = 128 + + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return default + + if self.dom_volt_supported: + dom_voltage_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_VOLT_OFFSET), QSFP_DD_VOLT_WIDTH) + if dom_voltage_raw is not None: + dom_voltage_data = sfpd_obj.parse_voltage( + dom_voltage_raw, 0) + voltage = self._convert_string_to_num( + dom_voltage_data['data']['Vcc']['value']) + return voltage + + else: + offset = 256 + + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return default + + sfpd_obj._calibration_type = self.calibration + + 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) + voltage = self._convert_string_to_num( + dom_voltage_data['data']['Vcc']['value']) + return voltage + + return default def get_tx_bias(self): """ @@ -1057,12 +1659,87 @@ class Sfp(SfpBase): for channel 0 to channel 4. Ex. ['110.09', '111.12', '108.21', '112.09'] """ - transceiver_bulk_status = self.get_transceiver_bulk_status() - tx1_bs = transceiver_bulk_status.get("tx1bias", "N/A") - tx2_bs = transceiver_bulk_status.get("tx2bias", "N/A") - tx3_bs = transceiver_bulk_status.get("tx3bias", "N/A") - tx4_bs = transceiver_bulk_status.get("tx4bias", "N/A") - tx_bias_list = [tx1_bs, tx2_bs, tx3_bs, tx4_bs] + tx_bias_list = [] + if self.sfp_type == QSFP_TYPE: + offset = 0 + default = [0.0] * 4 + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return default + + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power( + dom_channel_monitor_raw, 0) + tx_bias_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX1Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX2Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX3Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX4Bias']['value'])) + else: + return default + + elif self.sfp_type == QSFP_DD_TYPE: + default = [0.0] * 8 + # page 11h + if self.dom_rx_tx_power_bias_supported: + offset = 128 + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return default + + if dom_tx_bias_power_supported: + dom_tx_bias_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_TX_BIAS_OFFSET), QSFP_DD_TX_BIAS_WIDTH) + if dom_tx_bias_raw is not None: + dom_tx_bias_data = sfpd_obj.parse_dom_tx_bias( + dom_tx_bias_raw, 0) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX1Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX2Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX3Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX4Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX5Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX6Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX7Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX8Bias']['value'])) + else: + return default + else: + return default + + else: + offset = 256 + default = [0.0] + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return default + sfpd_obj._calibration_type = self.calibration + + if self.dom_supported: + 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_channel_monitor_data = sfpd_obj.parse_channel_monitor_params( + dom_channel_monitor_raw, 0) + tx_bias_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TXBias']['value'])) + else: + return default + else: + return default + return tx_bias_list def get_rx_power(self): @@ -1074,12 +1751,93 @@ class Sfp(SfpBase): Ex. ['1.77', '1.71', '1.68', '1.70'] """ rx_power_list = [] - transceiver_bulk_status = self.get_transceiver_bulk_status() - rx1_p = transceiver_bulk_status.get("rx1power", "N/A") - rx2_p = transceiver_bulk_status.get("rx2power", "N/A") - rx3_p = transceiver_bulk_status.get("rx3power", "N/A") - rx4_p = transceiver_bulk_status.get("rx4power", "N/A") - rx_power_list = [rx1_p, rx2_p, rx3_p, rx4_p] + if self.sfp_type == OSFP_TYPE: + # OSFP not supported on our platform yet. + return None + + elif self.sfp_type == QSFP_TYPE: + offset = 0 + default = [0.0] * 4 + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return default + + if self.dom_rx_power_supported: + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power( + dom_channel_monitor_raw, 0) + rx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['RX1Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['RX2Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['RX3Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['RX4Power']['value'])) + else: + return default + else: + return default + + elif self.sfp_type == QSFP_DD_TYPE: + default = [0.0] * 8 + # page 11 + if self.dom_rx_tx_power_bias_supported: + offset = 128 + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return default + + if self.dom_rx_power_supported: + dom_rx_power_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_RX_POWER_OFFSET), QSFP_DD_RX_POWER_WIDTH) + if dom_rx_power_raw is not None: + dom_rx_power_data = sfpd_obj.parse_dom_rx_power( + dom_rx_power_raw, 0) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX1Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX2Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX3Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX4Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX5Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX6Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX7Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX8Power']['value'])) + else: + return default + else: + return default + + else: + offset = 256 + default = [0.0] + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return default + + if self.dom_supported: + sfpd_obj._calibration_type = self.calibration + + 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_channel_monitor_data = sfpd_obj.parse_channel_monitor_params( + dom_channel_monitor_raw, 0) + rx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['RXPower']['value'])) + else: + return default + else: + return default return rx_power_list def get_tx_power(self): @@ -1091,12 +1849,97 @@ class Sfp(SfpBase): Ex. ['1.86', '1.86', '1.86', '1.86'] """ tx_power_list = [] - transceiver_bulk_status = self.get_transceiver_bulk_status() - tx1_p = transceiver_bulk_status.get("tx1power", "N/A") - tx2_p = transceiver_bulk_status.get("tx2power", "N/A") - tx3_p = transceiver_bulk_status.get("tx3power", "N/A") - tx4_p = transceiver_bulk_status.get("tx4power", "N/A") - tx_power_list = [tx1_p, tx2_p, tx3_p, tx4_p] + if self.sfp_type == OSFP_TYPE: + # OSFP not supported on our platform yet. + return tx_power_list + + elif self.sfp_type == QSFP_TYPE: + offset = 0 + default = [0.0] * 4 + + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return tx_power_list + + if self.dom_tx_power_supported: + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CHANNL_MON_OFFSET), + QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power( + dom_channel_monitor_raw, 0) + tx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX1Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX2Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX3Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX4Power']['value'])) + else: + return default + else: + return default + + elif self.sfp_type == QSFP_DD_TYPE: + default = [0.0] * 8 + + # page 11 + if self.dom_rx_tx_power_bias_supported: + offset = 128 + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return default + + if self.dom_tx_power_supported: + dom_tx_power_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_TX_POWER_OFFSET), + QSFP_DD_TX_POWER_WIDTH) + if dom_tx_power_raw is not None: + dom_tx_power_data = sfpd_obj.parse_dom_tx_power( + dom_tx_power_raw, 0) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX1Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX2Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX3Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX4Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX5Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX6Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX7Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX8Power']['value'])) + else: + return default + else: + return default + + else: + offset = 256 + default = [0.0] + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return default + + if self.dom_supported: + sfpd_obj._calibration_type = self.calibration + + 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_channel_monitor_data = sfpd_obj.parse_channel_monitor_params( + dom_channel_monitor_raw, 0) + tx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TXPower']['value'])) + else: + return default + else: + return default return tx_power_list def reset(self): @@ -1120,7 +1963,7 @@ class Sfp(SfpBase): reg_value = int(content, 16) # Determind if port_num start from 1 or 0 - bit_index = self.index + bit_index = self._index # Mask off the bit corresponding to our port mask = (1 << bit_index) @@ -1162,16 +2005,12 @@ class Sfp(SfpBase): if self.sfp_type == QSFP_TYPE: sysfsfile_eeprom = None try: - tx_disable_ctl = 0xf if tx_disable else 0x0 - buffer = create_string_buffer(1) - buffer[0] = chr(tx_disable_ctl) + tx_disable_value = 0xf if tx_disable else 0x0 # Write to eeprom - sysfsfile_eeprom = open( - self.port_to_eeprom_mapping[self.port_num], "r+b") + sysfsfile_eeprom = open(self._eeprom_path, "r+b") sysfsfile_eeprom.seek(QSFP_CONTROL_OFFSET) - sysfsfile_eeprom.write(buffer[0]) - except IOError as e: - #print("Error: unable to open file: %s" % str(e)) + sysfsfile_eeprom.write(struct.pack('B', tx_disable_value)) + except IOError: return False finally: if sysfsfile_eeprom is not None: @@ -1194,20 +2033,15 @@ class Sfp(SfpBase): if self.sfp_type == QSFP_TYPE: sysfsfile_eeprom = None try: - channel_state = self.get_tx_disable_channel() - tx_enable_mask = [0xe, 0xd, 0xb, 0x7] - tx_disable_mask = [0x1, 0x3, 0x7, 0xf] - tx_disable_ctl = channel_state | tx_disable_mask[ - channel] if disable else channel_state & tx_enable_mask[channel] - buffer = create_string_buffer(1) - buffer[0] = chr(tx_disable_ctl) + current_state = self.get_tx_disable_channel() + tx_disable_value = (current_state | channel) if \ + disable else (current_state & (~channel)) + # Write to eeprom - sysfsfile_eeprom = open( - self.port_to_eeprom_mapping[self.port_num], "r+b") + sysfsfile_eeprom = open(self._eeprom_path, "r+b") sysfsfile_eeprom.seek(QSFP_CONTROL_OFFSET) - sysfsfile_eeprom.write(buffer[0]) - except IOError as e: - #print("Error: unable to open file: %s" % str(e)) + sysfsfile_eeprom.write(struct.pack('B', tx_disable_value)) + except IOError: return False finally: if sysfsfile_eeprom is not None: @@ -1237,7 +2071,7 @@ class Sfp(SfpBase): reg_value = int(content, 16) # Determind if port_num start from 1 or 0 - bit_index = self.index + bit_index = self._index # Mask off the bit corresponding to our port mask = (1 << bit_index) @@ -1270,31 +2104,24 @@ class Sfp(SfpBase): A boolean, True if power-override and power_set are set successfully, False if not """ - if self.sfp_type == QSFP_TYPE: + sysfsfile_eeprom = None + if self.sfp_type == QSFP_TYPE and self.get_presence(): try: - power_override_bit = 0 - if power_override: - power_override_bit |= 1 << 0 + power_override_bit = 0x1 if power_override else 0 + power_set_bit = 0x2 if power_set else 0 + value = power_override_bit | power_set_bit - power_set_bit = 0 - if power_set: - power_set_bit |= 1 << 1 - - buffer = create_string_buffer(1) - buffer[0] = chr(power_override_bit | power_set_bit) # Write to eeprom - sysfsfile_eeprom = open( - self.port_to_eeprom_mapping[self.port_num], "r+b") + sysfsfile_eeprom = open(self._eeprom_path, "r+b") sysfsfile_eeprom.seek(QSFP_POWEROVERRIDE_OFFSET) - sysfsfile_eeprom.write(buffer[0]) + sysfsfile_eeprom.write(struct.pack('B', value)) except IOError as e: - #print("Error: unable to open file: %s" % str(e)) - return False + print("Error: unable to open file: %s" % str(e)) finally: if sysfsfile_eeprom is not None: sysfsfile_eeprom.close() time.sleep(0.01) - return True + return True return False ############################################################## @@ -1307,7 +2134,7 @@ class Sfp(SfpBase): Returns: string: The name of the device """ - return self.name + return self._name def get_presence(self): """ @@ -1315,7 +2142,8 @@ class Sfp(SfpBase): Returns: bool: True if PSU is present, False if not """ - presence_status_raw = self._api_helper.read_txt_file(self.PRS_PATH).rstrip() + presence_status_raw = self._api_helper.read_txt_file( + self.PRS_PATH).rstrip() if not presence_status_raw: return False @@ -1323,7 +2151,7 @@ class Sfp(SfpBase): reg_value = int(content, 16) # Determind if port_num start from 1 or 0 - bit_index = self.index + bit_index = self._index # Mask off the bit corresponding to our port mask = (1 << bit_index) @@ -1359,3 +2187,18 @@ class Sfp(SfpBase): 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_seastone-r0/sonic_platform/thermal.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/thermal.py index 3848f93405..ce2a45f0c5 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/thermal.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/thermal.py @@ -7,7 +7,6 @@ ############################################################################# import os -import re import os.path try: @@ -19,14 +18,18 @@ except ImportError as e: THERMAL_INFO = { 0: { "F2B_max": 50, + "F2B_max_crit": 75, "B2F_max": 55, + "B2F_max_crit": 75, "postion": "asic", "name": "Front-panel temp sensor 1", "i2c_path": "i2c-5/5-0048/hwmon/hwmon1", # u4 system-inlet }, 1: { "F2B_max": 50, + "F2B_max_crit": 75, "B2F_max": 55, + "B2F_max_crit": 75, "postion": "asic", "name": "Front-panel temp sensor 2", "i2c_path": "i2c-6/6-0049/hwmon/hwmon2", # u2 system-inlet @@ -51,7 +54,9 @@ THERMAL_INFO = { }, 4: { "F2B_max": 70, + "F2B_max_crit": 75, "B2F_max": 55, + "B2F_max_crit": 75, "postion": "cpu", "name": "Rear-panel temp sensor 2", "i2c_path": "i2c-15/15-004e/hwmon/hwmon5" # u9201 system-outlet @@ -72,8 +77,7 @@ class Thermal(ThermalBase): self._api_helper = APIHelper() self._airflow = airflow self._thermal_info = THERMAL_INFO[self.index] - self._hwmon_path = "{}/{}".format(I2C_ADAPTER_PATH, - self._thermal_info["i2c_path"]) + self._hwmon_path = "{}/{}".format(I2C_ADAPTER_PATH, self._thermal_info["i2c_path"]) self.name = self.get_name() self.postion = self._thermal_info["postion"] self.ss_index = 1 @@ -110,8 +114,9 @@ class Thermal(ThermalBase): A float number, the high threshold temperature of thermal in Celsius 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) + temp_file = "temp{}_max".format(self.ss_index) + temp = float(self.__get_temp(temp_file)) + return temp def get_low_threshold(self): """ @@ -120,7 +125,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): """ @@ -133,31 +138,33 @@ 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: - try: - with open(self.SS_CONFIG_PATH, 'r+') as f: - content = f.readlines() - f.seek(0) - ss_found = False - for idx, val in enumerate(content): - if self.name in val: - ss_found = True - elif ss_found and temp_file in val: - content[idx] = " set {} {}\n".format( - temp_file, temperature) - f.writelines(content) - file_set = True - break - except IOError: - file_set = False + file_set = True + if self._api_helper.is_host(): + file_set = False + if is_set: + try: + with open(self.SS_CONFIG_PATH, 'r+') as f: + content = f.readlines() + f.seek(0) + ss_found = False + for idx, val in enumerate(content): + if self.name in val: + ss_found = True + elif ss_found and temp_file in val: + content[idx] = " set {} {}\n".format( + temp_file, temperature) + f.writelines(content) + file_set = True + break + except IOError as err: + file_set = False return is_set & file_set def set_low_threshold(self, temperature): """ Sets the low threshold temperature of thermal - Args : + Args : temperature: A float number up to nearest thousandth of one degree Celsius, e.g. 30.125 Returns: @@ -173,7 +180,10 @@ class Thermal(ThermalBase): up to nearest thousandth of one degree Celsius, e.g. 30.125 """ max_crit_key = '{}_max_crit'.format(self._airflow) - return self._thermal_info.get(max_crit_key, None) + max_crit_threshold = self._thermal_info.get(max_crit_key, None) + if max_crit_threshold is not None: + max_crit_threshold = float(max_crit_threshold) + return max_crit_threshold def get_low_critical_threshold(self): """ @@ -182,7 +192,7 @@ 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_name(self): """ @@ -234,3 +244,21 @@ class Thermal(ThermalBase): raw_txt = self.__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_seastone-r0/sonic_platform/watchdog.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/watchdog.py index 5ab79309f0..122b5d90dd 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/watchdog.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/watchdog.py @@ -135,6 +135,9 @@ class Watchdog(WatchdogBase): ret = WDT_COMMON_ERROR if seconds < 0: return ret + if seconds > 16779: + return ret + try: if self.timeout != seconds: diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.init b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.init index e27d4df467..8ee3d44655 100644 --- a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.init +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.init @@ -89,10 +89,19 @@ start) echo emc2305 0x2e > /sys/bus/i2c/devices/i2c-13/new_device echo emc2305 0x4d > /sys/bus/i2c/devices/i2c-13/new_device + echo 24c02 0x50 > /sys/bus/i2c/devices/i2c-18/new_device + echo 24c02 0x50 > /sys/bus/i2c/devices/i2c-19/new_device + echo 24c02 0x50 > /sys/bus/i2c/devices/i2c-20/new_device + echo 24c02 0x50 > /sys/bus/i2c/devices/i2c-21/new_device + echo 24c02 0x50 > /sys/bus/i2c/devices/i2c-22/new_device + # Attach PSUs echo dps460 0x5a > /sys/bus/i2c/devices/i2c-10/new_device echo dps460 0x5b > /sys/bus/i2c/devices/i2c-11/new_device + echo 24c02 0x52 > /sys/bus/i2c/devices/i2c-10/new_device + echo 24c02 0x53 > /sys/bus/i2c/devices/i2c-11/new_device + # Attach PCA9506 GPIO expander for 40 pins echo pca9505 0x20 > /sys/bus/i2c/devices/i2c-17/new_device @@ -117,16 +126,16 @@ start) export_gpio 27 "in" # PSU L ABS export_gpio 28 "in" # PSU R ABS - export_gpio 29 "out" # Fan 1 LED: Red - export_gpio 30 "out" # Fan 1 LED: Yellow - export_gpio 31 "out" # Fan 2 LED: Red - export_gpio 32 "out" # Fan 2 LED: Yellow - export_gpio 33 "out" # Fan 3 LED: Red - export_gpio 34 "out" # Fan 3 LED: Yellow - export_gpio 35 "out" # Fan 4 LED: Red - export_gpio 36 "out" # Fan 4 LED: Yellow - export_gpio 37 "out" # Fan 5 LED: Red - export_gpio 38 "out" # Fan 5 LED: Yellow + export_gpio 29 "out" # Fan 2 LED: Red + export_gpio 30 "out" # Fan 2 LED: Green + export_gpio 31 "out" # Fan 1 LED: Red + export_gpio 32 "out" # Fan 1 LED: Green + export_gpio 33 "out" # Fan 5 LED: Red + export_gpio 34 "out" # Fan 5 LED: Green + export_gpio 35 "out" # Fan 3 LED: Red + export_gpio 36 "out" # Fan 3 LED: Green + export_gpio 37 "out" # Fan 4 LED: Red + export_gpio 38 "out" # Fan 4 LED: Green # Turn off/down lpmod by defult (0 - Normal, 1 - Low Pow) echo 0x00000000 > /sys/devices/platform/dx010_cpld/qsfp_lpmode diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.install b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.install index a36e2cd137..98c17e55a9 100644 --- a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.install +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.install @@ -5,4 +5,5 @@ dx010/scripts/fancontrol.sh etc/init.d dx010/scripts/fancontrol.service lib/systemd/system services/fancontrol/fancontrol usr/local/bin dx010/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/x86_64-cel_seastone-r0 +dx010/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/x86_64-cel_seastone-r0 services/platform_api/platform_api_mgnt.sh usr/local/bin diff --git a/platform/broadcom/sonic-platform-modules-cel/services/platform_api/platform_api_mgnt.sh b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/platform_api_mgnt.sh index 493119bd37..208c8696c4 100755 --- a/platform/broadcom/sonic-platform-modules-cel/services/platform_api/platform_api_mgnt.sh +++ b/platform/broadcom/sonic-platform-modules-cel/services/platform_api/platform_api_mgnt.sh @@ -4,13 +4,20 @@ PREV_REBOOT_CAUSE="/host/reboot-cause/" DEVICE="/usr/share/sonic/device" PLATFORM=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform) FILES=$DEVICE/$PLATFORM/api_files +PY2_PACK=$DEVICE/$PLATFORM/sonic_platform-1.0-py2-none-any.whl +PY3_PACK=$DEVICE/$PLATFORM/sonic_platform-1.0-py3-none-any.whl install() { - # Install sonic-platform package - if [ -e $DEVICE/$PLATFORM/sonic_platform-1.0-py2-none-any.whl ]; then - pip install $DEVICE/$PLATFORM/sonic_platform-1.0-py2-none-any.whl - pip3 install $device/$platform/sonic_platform-1.0-py3-none-any.whl + # Install python2.7 sonic-platform package + if [ -e $PY2_PACK ]; then + pip install $PY2_PACK fi + + # Install python3 sonic-platform package + if [ -e $PY3_PACK ]; then + pip3 install $PY3_PACK + fi + } init() {