def test_get_system_eeprom_info_from_db(self):
        return_values = {
            ('EEPROM_INFO|State', 'Initialized'):
            '1',
            ('EEPROM_INFO|{}'.format(hex(Eeprom._TLV_CODE_PRODUCT_NAME)), 'Value'):
            'MSN3420',
            ('EEPROM_INFO|{}'.format(hex(Eeprom._TLV_CODE_PART_NUMBER)), 'Value'):
            'MSN3420-CB2FO',
            ('EEPROM_INFO|{}'.format(hex(Eeprom._TLV_CODE_MAC_BASE)), 'Value'):
            '1C:34:DA:1C:9F:00',
            ('EEPROM_INFO|{}'.format(hex(Eeprom._TLV_CODE_SERIAL_NUMBER)), 'Value'):
            'MT2019X13878',
            ('EEPROM_INFO|{}'.format(hex(Eeprom._TLV_CODE_VENDOR_EXT)), 'Num_vendor_ext'):
            '2',
            ('EEPROM_INFO|{}'.format(hex(Eeprom._TLV_CODE_VENDOR_EXT)), 'Value_0'):
            'ext1',
            ('EEPROM_INFO|{}'.format(hex(Eeprom._TLV_CODE_VENDOR_EXT)), 'Value_1'):
            'ext2',
            ('EEPROM_INFO|{}'.format(hex(Eeprom._TLV_CODE_CRC_32)), 'Value'):
            'CRC_VALUE',
        }

        def side_effect(key, field):
            return return_values.get((key, field))

        eeprom = Eeprom()
        eeprom._redis_hget = MagicMock(side_effect=side_effect)

        info = eeprom.get_system_eeprom_info()
        assert eeprom.get_product_name() == 'MSN3420'
        assert eeprom.get_part_number() == 'MSN3420-CB2FO'
        assert eeprom.get_base_mac() == '1C:34:DA:1C:9F:00'
        assert eeprom.get_serial_number() == 'MT2019X13878'
        assert info[hex(Eeprom._TLV_CODE_VENDOR_EXT)] == ['ext1', 'ext2']
        assert info[hex(Eeprom._TLV_CODE_CRC_32)] == 'CRC_VALUE'
 def test_get_system_eeprom_info_from_hardware(self):
     eeprom = Eeprom()
     eeprom.p = os.path.join(test_path, 'mock_eeprom_data')
     eeprom._redis_hget = MagicMock()
     info = eeprom.get_system_eeprom_info()
     assert eeprom.get_product_name() == 'MSN3800'
     assert eeprom.get_part_number() == 'MSN3800-CS2FO'
     assert eeprom.get_base_mac() == 'B8:59:9F:A9:34:00'
     assert eeprom.get_serial_number() == 'MT1937X00537'
     assert info[hex(Eeprom._TLV_CODE_CRC_32)] == '0x9EFF0119'
Exemple #3
0
class Psu(PsuBase):
    """DellEMC Platform-specific PSU class"""

    CPLD_DIR = "/sys/devices/platform/dell-s6000-cpld.0/"
    I2C_DIR = "/sys/class/i2c-adapter/"

    def __init__(self, psu_index):
        PsuBase.__init__(self)
        # PSU is 1-based in DellEMC platforms
        self.index = psu_index + 1
        self.psu_presence_reg = "psu{}_prs".format(psu_index)
        self.psu_status_reg = "powersupply_status"
        self.is_driver_initialized = False

        if self.index == 1:
            ltc_dir = self.I2C_DIR + "i2c-11/11-0042/hwmon/"
        else:
            ltc_dir = self.I2C_DIR + "i2c-11/11-0040/hwmon/"

        try:
            hwmon_node = os.listdir(ltc_dir)[0]
        except OSError:
            hwmon_node = "hwmon*"
        else:
            self.is_driver_initialized = True

        self.HWMON_DIR = ltc_dir + hwmon_node + '/'

        self.psu_voltage_reg = self.HWMON_DIR + "in1_input"
        self.psu_current_reg = self.HWMON_DIR + "curr1_input"
        self.psu_power_reg = self.HWMON_DIR + "power1_input"

        self.eeprom = Eeprom(is_psu=True, psu_index=self.index)

        self._fan_list.append(
            Fan(psu_index=self.index, psu_fan=True, dependency=self))
        for i in range(1, MAX_S6000_THERMALS_PER_PSU + 1):
            self._thermal_list.append(
                Thermal(psu_index=self.index,
                        thermal_index=i,
                        psu_thermal=True,
                        dependency=self))

    def _get_cpld_register(self, reg_name):
        # On successful read, returns the value read from given
        # reg_name and on failure returns 'ERR'
        rv = 'ERR'
        cpld_reg_file = self.CPLD_DIR + reg_name

        if (not os.path.isfile(cpld_reg_file)):
            return rv

        try:
            with open(cpld_reg_file, 'r') as fd:
                rv = fd.read()
        except:
            rv = 'ERR'

        rv = rv.rstrip('\r\n')
        rv = rv.lstrip(" ")
        return rv

    def _get_i2c_register(self, reg_file):
        # On successful read, returns the value read from given
        # reg_name and on failure returns 'ERR'
        rv = 'ERR'

        if not self.is_driver_initialized:
            reg_file_path = glob.glob(reg_file)
            if len(reg_file_path):
                reg_file = reg_file_path[0]
                self._get_sysfs_path()
            else:
                return rv

        if (not os.path.isfile(reg_file)):
            return rv

        try:
            with open(reg_file, 'r') as fd:
                rv = fd.read()
        except:
            rv = 'ERR'

        rv = rv.rstrip('\r\n')
        rv = rv.lstrip(" ")
        return rv

    def _get_sysfs_path(self):
        voltage_reg = glob.glob(self.psu_voltage_reg)
        current_reg = glob.glob(self.psu_current_reg)
        power_reg = glob.glob(self.psu_power_reg)

        if len(voltage_reg) and len(current_reg) and len(power_reg):
            self.psu_voltage_reg = voltage_reg[0]
            self.psu_current_reg = current_reg[0]
            self.psu_power_reg = power_reg[0]
            self.is_driver_initialized = True

    def get_name(self):
        """
        Retrieves the name of the device

        Returns:
            string: The name of the device
        """
        return "PSU{}".format(self.index)

    def get_presence(self):
        """
        Retrieves the presence of the Power Supply Unit (PSU)

        Returns:
            bool: True if PSU is present, False if not
        """
        status = False
        psu_presence = self._get_cpld_register(self.psu_presence_reg)
        if (psu_presence != 'ERR'):
            psu_presence = int(psu_presence)
            if psu_presence:
                status = True

        return status

    def get_model(self):
        """
        Retrieves the part number of the PSU

        Returns:
            string: Part number of PSU
        """
        return self.eeprom.get_part_number()

    def get_serial(self):
        """
        Retrieves the serial number of the PSU

        Returns:
            string: Serial number of PSU
        """
        # Sample Serial number format "US-01234D-54321-25A-0123-A00"
        return self.eeprom.get_serial_number()

    def get_status(self):
        """
        Retrieves the operational status of the PSU

        Returns:
            bool: True if PSU is operating properly, False if not
        """
        status = False
        psu_status = self._get_cpld_register(self.psu_status_reg)
        if (psu_status != 'ERR'):
            psu_status = (int(psu_status, 16) >> int(
                (2 - self.index) * 4)) & 0xF
            if (~psu_status & 0b1000) and (~psu_status & 0b0100):
                status = True

        return 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 or -1 if cannot determine the position
        """
        return self.index

    def is_replaceable(self):
        """
        Indicate whether PSU is replaceable.
        Returns:
            bool: True if it is replaceable.
        """
        return True

    def get_voltage(self):
        """
        Retrieves current PSU voltage output

        Returns:
            A float number, the output voltage in volts,
            e.g. 12.1
        """
        psu_voltage = self._get_i2c_register(self.psu_voltage_reg)
        if (psu_voltage != 'ERR') and self.get_status():
            # Converting the value returned by driver which is in
            # millivolts to volts
            psu_voltage = float(psu_voltage) / 1000
        else:
            psu_voltage = 0.0

        return psu_voltage

    def get_current(self):
        """
        Retrieves present electric current supplied by PSU

        Returns:
            A float number, electric current in amperes,
            e.g. 15.4
        """
        psu_current = self._get_i2c_register(self.psu_current_reg)
        if (psu_current != 'ERR') and self.get_status():
            # Converting the value returned by driver which is in
            # milliamperes to amperes
            psu_current = float(psu_current) / 1000
        else:
            psu_current = 0.0

        return psu_current

    def get_power(self):
        """
        Retrieves current energy supplied by PSU

        Returns:
            A float number, the power in watts,
            e.g. 302.6
        """
        psu_power = self._get_i2c_register(self.psu_power_reg)
        if (psu_power != 'ERR') and self.get_status():
            # Converting the value returned by driver which is in
            # microwatts to watts
            psu_power = float(psu_power) / 1000000
        else:
            psu_power = 0.0

        return psu_power

    def get_powergood_status(self):
        """
        Retrieves the powergood status of PSU
        Returns:
            A boolean, True if PSU has stablized its output voltages and
            passed all its internal self-tests, False if not.
        """
        status = False
        psu_status = self._get_cpld_register(self.psu_status_reg)
        if (psu_status != 'ERR'):
            psu_status = (int(psu_status, 16) >> ((2 - self.index) * 4)) & 0xF
            if (psu_status == 0x2):
                status = True

        return status

    def get_status_led(self):
        """
        Gets the state of the PSU status LED

        Returns:
            A string, one of the predefined STATUS_LED_COLOR_* strings.
        """
        if self.get_powergood_status():
            return self.STATUS_LED_COLOR_GREEN
        else:
            return self.STATUS_LED_COLOR_OFF

    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
        Returns:
            bool: True if status LED state is set successfully, False if
                  not
        """
        # In S6000, the firmware running in the PSU controls the LED
        # and the PSU LED state cannot be changed from CPU.
        return False

    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
        """
        if self.get_presence():
            return self.get_thermal(0).get_temperature()
        else:
            return 0.0

    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
        """
        if self.get_presence():
            return self.get_thermal(0).get_high_threshold()
        else:
            return 0.0

    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
        """
        return 12.6

    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
        """
        return 11.4
Exemple #4
0
class Chassis(ChassisBase):
    """
    DELLEMC Platform-specific Chassis class
    """
    CPLD_DIR = "/sys/devices/platform/dell-s6000-cpld.0"

    sfp_control = ""
    PORT_START = 0
    PORT_END = 0
    reset_reason_dict = {}
    reset_reason_dict[0xe] = ChassisBase.REBOOT_CAUSE_NON_HARDWARE
    reset_reason_dict[0x6] = ChassisBase.REBOOT_CAUSE_NON_HARDWARE
    reset_reason_dict[0x7] = ChassisBase.REBOOT_CAUSE_THERMAL_OVERLOAD_OTHER

    def __init__(self):
        ChassisBase.__init__(self)
        # Initialize SFP list
        self.PORT_START = 0
        self.PORT_END = 31
        EEPROM_OFFSET = 20
        PORTS_IN_BLOCK = (self.PORT_END + 1)

        # sfp.py will read eeprom contents and retrive the eeprom data.
        # It will also provide support sfp controls like reset and setting
        # low power mode.
        # We pass the eeprom path and sfp control path from chassis.py
        # So that sfp.py implementation can be generic to all platforms
        eeprom_base = "/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom"
        self.sfp_control = "/sys/devices/platform/dell-s6000-cpld.0/"

        for index in range(0, PORTS_IN_BLOCK):
            eeprom_path = eeprom_base.format(index + EEPROM_OFFSET)
            sfp_node = Sfp(index, 'QSFP', eeprom_path, self.sfp_control, index)
            self._sfp_list.append(sfp_node)

        # Get Transceiver status
        self.modprs_register = self._get_transceiver_status()

        with open("/sys/class/dmi/id/product_name", "r") as fd:
            board_type = fd.read()

        if 'S6000-ON' in board_type:
            self._eeprom = Eeprom()
        else:
            self._eeprom = EepromS6000()

        for i in range(MAX_S6000_FAN):
            fan = Fan(i)
            self._fan_list.append(fan)

        for i in range(MAX_S6000_PSU):
            psu = Psu(i)
            self._psu_list.append(psu)

        for i in range(MAX_S6000_THERMAL):
            thermal = Thermal(i)
            self._thermal_list.append(thermal)

        for i in range(MAX_S6000_COMPONENT):
            component = Component(i)
            self._component_list.append(component)

    def _get_cpld_register(self, reg_name):
        rv = 'ERR'
        mb_reg_file = self.CPLD_DIR + '/' + reg_name

        if (not os.path.isfile(mb_reg_file)):
            return rv

        try:
            with open(mb_reg_file, 'r') as fd:
                rv = fd.read()
        except Exception as error:
            rv = 'ERR'

        rv = rv.rstrip('\r\n')
        rv = rv.lstrip(" ")
        return rv

    def _nvram_write(self, offset, val):
        resource = "/dev/nvram"
        fd = os.open(resource, os.O_RDWR)
        if (fd < 0):
            print('File open failed ', resource)
            return
        if (os.lseek(fd, offset, os.SEEK_SET) != offset):
            print('lseek failed on ', resource)
            return
        ret = os.write(fd, struct.pack('B', val))
        if ret != 1:
            print('Write failed ', str(ret))
            return
        os.close(fd)

    def _get_thermal_reset(self):
        reset_file = "/host/reboot-cause/reboot-cause.txt"
        if (not os.path.isfile(reset_file)):
            return False
        try:
            with open(reset_file, 'r') as fd:
                rv = fd.read()
        except Exception as error:
            return False

        if "Thermal Overload" in rv:
            return True

        return False

    def get_name(self):
        """
        Retrieves the name of the chassis
        Returns:
            string: The name of the chassis
        """
        return self._eeprom.get_model()

    def get_presence(self):
        """
        Retrieves the presence of the chassis
        Returns:
            bool: True if chassis is present, False if not
        """
        return True

    def get_model(self):
        """
        Retrieves the model number (or part number) of the chassis
        Returns:
            string: Model/part number of chassis
        """
        return self._eeprom.get_part_number()

    def get_serial(self):
        """
        Retrieves the serial number of the chassis (Service tag)
        Returns:
            string: Serial number of chassis
        """
        return self._eeprom.get_serial()

    def get_status(self):
        """
        Retrieves the operational status of the chassis
        Returns:
            bool: A boolean value, True if chassis is operating properly
            False if not
        """
        return True

    def get_base_mac(self):
        """
        Retrieves the base MAC address for the chassis

        Returns:
            A string containing the MAC address in the format
            'XX:XX:XX:XX:XX:XX'
        """
        return self._eeprom.get_base_mac()

    def get_serial_number(self):
        """
        Retrieves the hardware serial number for the chassis

        Returns:
            A string containing the hardware serial number for this
            chassis.
        """
        return self._eeprom.get_serial_number()

    def get_system_eeprom_info(self):
        """
        Retrieves the full content of system EEPROM information for the
        chassis

        Returns:
            A dictionary where keys are the type code defined in
            OCP ONIE TlvInfo EEPROM format and values are their
            corresponding values.
        """
        return self._eeprom.system_eeprom_info()

    def get_reboot_cause(self):
        """
        Retrieves the cause of the previous reboot
        """
        # In S6000, We track the reboot reason by writing the reason in
        # NVRAM. Only Warmboot and Coldboot reason are supported here.
        # Since it does not support any hardware reason, we return
        # non_hardware as default
        if self._get_thermal_reset() == True:
            self._nvram_write(0x49, 0x7)

        lrr = self._get_cpld_register('last_reboot_reason')
        if (lrr != 'ERR'):
            reset_reason = int(lrr, base=16)
            if (reset_reason in self.reset_reason_dict):
                return (self.reset_reason_dict[reset_reason], None)

        return (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, None)

    def _get_transceiver_status(self):
        presence_ctrl = self.sfp_control + 'qsfp_modprs'
        try:
            reg_file = open(presence_ctrl)

        except IOError as e:
            return False

        content = reg_file.readline().rstrip()
        reg_file.close()

        return int(content, 16)

    def get_change_event(self, timeout=0):
        """
        Returns a nested dictionary containing all devices which have
        experienced a change at chassis level

        Args:
            timeout: Timeout in milliseconds (optional). If timeout == 0,
                this method will block until a change is detected.

        Returns:
            (bool, dict):
                - True if call successful, False if not;
                - A nested dictionary where key is a device type,
                  value is a dictionary with key:value pairs in the
                  format of {'device_id':'device_event'},
                  where device_id is the device ID for this device and
                        device_event,
                             status='1' represents device inserted,
                             status='0' represents device removed.
                  Ex. {'fan':{'0':'0', '2':'1'}, 'sfp':{'11':'0'}}
                      indicates that fan 0 has been removed, fan 2
                      has been inserted and sfp 11 has been removed.
        """
        start_time = time.time()
        port_dict = {}
        ret_dict = {"sfp": port_dict}
        port = self.PORT_START
        forever = False

        if timeout == 0:
            forever = True
        elif timeout > 0:
            timeout = timeout / float(1000)  # Convert to secs
        else:
            return False, {}
        end_time = start_time + timeout

        if (start_time > end_time):
            return False, ret_dict  # Time wrap or possibly incorrect timeout

        while (timeout >= 0):
            # Check for OIR events and return updated port_dict
            reg_value = self._get_transceiver_status()
            if (reg_value != self.modprs_register):
                changed_ports = (self.modprs_register ^ reg_value)
                while (port >= self.PORT_START and port <= self.PORT_END):
                    # Mask off the bit corresponding to our port
                    mask = (1 << port)
                    if (changed_ports & mask):
                        # ModPrsL is active low
                        if reg_value & mask == 0:
                            port_dict[port] = '1'
                        else:
                            port_dict[port] = '0'
                    port += 1

                # Update reg value
                self.modprs_register = reg_value
                return True, ret_dict

            if forever:
                time.sleep(1)
            else:
                timeout = end_time - time.time()
                if timeout >= 1:
                    time.sleep(1)  # We poll at 1 second granularity
                else:
                    if timeout > 0:
                        time.sleep(timeout)
                    return True, ret_dict
        return False, ret_dict
Exemple #5
0
class Fan(FanBase):
    """DellEMC Platform-specific Fan class"""

    CPLD_DIR = "/sys/devices/platform/dell-s6000-cpld.0/"
    I2C_DIR = "/sys/class/i2c-adapter/"

    def __init__(self, fan_index, psu_fan=False, dependency=None):
        self.is_psu_fan = psu_fan
        self.is_driver_initialized = True

        if not self.is_psu_fan:
            # Fan is 1-based in DellEMC platforms
            self.index = fan_index + 1
            self.fan_presence_reg = "fan_prs"
            self.fan_led_reg = "fan{}_led".format(fan_index)
            self.get_fan_speed_reg = self.I2C_DIR + "i2c-11/11-0029/" +\
                    "fan{}_input".format(self.index)
            self.set_fan_speed_reg = self.I2C_DIR + "i2c-11/11-0029/" +\
                    "fan{}_target".format(self.index)
            self.eeprom = Eeprom(is_fan=True, fan_index=self.index)
            self.max_fan_speed = MAX_S6000_FAN_SPEED
            self.supported_led_color = ['off', 'green', 'amber']
        else:
            self.index = fan_index
            self.dependency = dependency
            self.set_fan_speed_reg = self.I2C_DIR +\
                    "i2c-1/1-005{}/fan1_target".format(10 - self.index)

            hwmon_dir = self.I2C_DIR +\
                    "i2c-1/1-005{}/hwmon/".format(10 - self.index)
            try:
                hwmon_node = os.listdir(hwmon_dir)[0]
            except OSError:
                hwmon_node = "hwmon*"
                self.is_driver_initialized = False

            self.get_fan_speed_reg = hwmon_dir + hwmon_node + '/fan1_input'
            self.max_fan_speed = MAX_S6000_PSU_FAN_SPEED

    def _get_cpld_register(self, reg_name):
        # On successful read, returns the value read from given
        # reg_name and on failure returns 'ERR'
        rv = 'ERR'
        cpld_reg_file = self.CPLD_DIR + reg_name

        if (not os.path.isfile(cpld_reg_file)):
            return rv

        try:
            with open(cpld_reg_file, 'r') as fd:
                rv = fd.read()
        except:
            rv = 'ERR'

        rv = rv.rstrip('\r\n')
        rv = rv.lstrip(" ")
        return rv

    def _set_cpld_register(self, reg_name, value):
        # On successful write, returns the value will be written on
        # reg_name and on failure returns 'ERR'
        rv = 'ERR'
        cpld_reg_file = self.CPLD_DIR + reg_name

        if (not os.path.isfile(cpld_reg_file)):
            print("open error")
            return rv

        try:
            with open(cpld_reg_file, 'w') as fd:
                rv = fd.write(str(value))
        except:
            rv = 'ERR'

        return rv

    def _get_i2c_register(self, reg_file):
        # On successful read, returns the value read from given
        # reg_name and on failure returns 'ERR'
        rv = 'ERR'

        if not self.is_driver_initialized:
            reg_file_path = glob.glob(reg_file)
            if len(reg_file_path):
                reg_file = reg_file_path[0]
                self._get_sysfs_path()
            else:
                return rv

        if (not os.path.isfile(reg_file)):
            return rv

        try:
            with open(reg_file, 'r') as fd:
                rv = fd.read()
        except:
            rv = 'ERR'

        rv = rv.rstrip('\r\n')
        rv = rv.lstrip(" ")
        return rv

    def _set_i2c_register(self, reg_file, value):
        # On successful write, the value read will be written on
        # reg_name and on failure returns 'ERR'
        rv = 'ERR'

        if (not os.path.isfile(reg_file)):
            return rv

        try:
            with open(reg_file, 'w') as fd:
                rv = fd.write(str(value))
        except:
            rv = 'ERR'

        return rv

    def _get_sysfs_path(self):
        fan_speed_reg = glob.glob(self.get_fan_speed_reg)

        if len(fan_speed_reg):
            self.get_fan_speed_reg = fan_speed_reg[0]
            self.is_driver_initialized = True

    def get_name(self):
        """
        Retrieves the name of the Fan

        Returns:
            string: The name of the Fan
        """
        if not self.is_psu_fan:
            return "FanTray{}-Fan1".format(self.index)
        else:
            return "PSU{} Fan".format(self.index)

    def get_presence(self):
        """
        Retrieves the presence of the Fan Unit

        Returns:
            bool: True if Fan is present, False if not
        """
        status = False
        if self.is_psu_fan:
            return self.dependency.get_presence()

        fan_presence = self._get_cpld_register(self.fan_presence_reg)
        if (fan_presence != 'ERR'):
            fan_presence = int(fan_presence, 16) & self.index
            if fan_presence:
                status = True

        return status

    def get_model(self):
        """
        Retrieves the part number of the Fan

        Returns:
            string: Part number of Fan
        """
        if not self.is_psu_fan:
            return self.eeprom.get_part_number()
        else:
            return 'NA'

    def get_serial(self):
        """
        Retrieves the serial number of the Fan

        Returns:
            string: Serial number of Fan
        """
        # Sample Serial number format "US-01234D-54321-25A-0123-A00"
        if not self.is_psu_fan:
            return self.eeprom.get_serial_number()
        else:
            return 'NA'

    def get_status(self):
        """
        Retrieves the operational status of the Fan

        Returns:
            bool: True if Fan is operating properly, False if not
        """
        status = False
        fan_speed = self._get_i2c_register(self.get_fan_speed_reg)
        if (fan_speed != 'ERR'):
            if (int(fan_speed) > 1000):
                status = True

        return status

    def get_direction(self):
        """
        Retrieves the fan airflow direction

        Returns:
            A string, either FAN_DIRECTION_INTAKE or
            FAN_DIRECTION_EXHAUST depending on fan direction

        Notes:
            In DellEMC platforms,
            - Forward/Exhaust : Air flows from Port side to Fan side.
            - Reverse/Intake  : Air flows from Fan side to Port side.
        """
        if self.is_psu_fan:
            direction = {
                1: self.FAN_DIRECTION_EXHAUST,
                2: self.FAN_DIRECTION_INTAKE,
                3: self.FAN_DIRECTION_EXHAUST,
                4: self.FAN_DIRECTION_INTAKE
            }
            fan_direction = self.dependency.eeprom.airflow_fan_type()
        else:
            direction = {
                1: self.FAN_DIRECTION_EXHAUST,
                2: self.FAN_DIRECTION_INTAKE
            }
            fan_direction = self.eeprom.airflow_fan_type()

        return direction.get(fan_direction, self.FAN_DIRECTION_NOT_APPLICABLE)

    def get_speed(self):
        """
        Retrieves the speed of fan

        Returns:
            int: percentage of the max fan speed
        """
        fan_speed = self._get_i2c_register(self.get_fan_speed_reg)
        if (fan_speed != 'ERR') and self.get_presence():
            speed_in_rpm = int(fan_speed, 10)
            speed = (100 * speed_in_rpm) // self.max_fan_speed
        else:
            speed = 0

        return speed

    def get_speed_tolerance(self):
        """
        Retrieves the speed tolerance of the fan

        Returns:
            An integer, the percentage of variance from target speed
            which is considered tolerable
        """
        if self.get_presence():
            # The tolerance value is fixed as 20% for all the DellEmc platform
            tolerance = 20
        else:
            tolerance = 0

        return tolerance

    def set_speed(self, speed):
        """
        Set fan speed to expected value
        Args:
            speed: An integer, the percentage of full fan speed to set
            fan to, in the range 0 (off) to 100 (full speed)
        Returns:
            bool: True if set success, False if fail.
        """
        fan_set = (speed * self.max_fan_speed) / 100
        rv = self._set_i2c_register(self.set_fan_speed_reg, fan_set)
        if (rv != 'ERR'):
            return True
        else:
            return False

    def set_status_led(self, color):
        """
        Set led to expected color
        Args:
            color: A string representing the color with which to set the
                   fan module status LED
        Returns:
            bool: True if set success, False if fail.
        """
        if self.is_psu_fan or (color not in self.supported_led_color):
            return False
        if (color == self.STATUS_LED_COLOR_AMBER):
            color = 'yellow'

        rv = self._set_cpld_register(self.fan_led_reg, color)
        if (rv != 'ERR'):
            return True
        else:
            return False

    def get_status_led(self):
        """
        Gets the state of the fan status LED

        Returns:
            A string, one of the predefined STATUS_LED_COLOR_* strings.
        """
        if self.is_psu_fan:
            # No LED available for PSU Fan
            return None

        fan_led = self._get_cpld_register(self.fan_led_reg)
        if (fan_led != 'ERR'):
            if (fan_led == 'yellow'):
                return self.STATUS_LED_COLOR_AMBER
            else:
                return fan_led
        else:
            return self.STATUS_LED_COLOR_OFF

    def get_target_speed(self):
        """
        Retrieves the target (expected) speed of the fan

        Returns:
            An integer, the percentage of full fan speed, in the range 0
            (off) to 100 (full speed)
        """
        # Fan speeds are controlled by fancontrol.sh
        return self.get_speed()
Exemple #6
0
class Chassis(ChassisBase):
    """
    NOKIA IXR7250 Platform-specific Chassis class
    """

    REBOOT_CAUSE_DICT = {
        'powerloss': ChassisBase.REBOOT_CAUSE_POWER_LOSS,
        'overtemp': ChassisBase.REBOOT_CAUSE_THERMAL_OVERLOAD_OTHER,
        'reboot': ChassisBase.REBOOT_CAUSE_NON_HARDWARE,
        'watchdog': ChassisBase.REBOOT_CAUSE_WATCHDOG,
        'under-voltage': ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER,
        'over-voltage': ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER,
    }

    def __init__(self):
        ChassisBase.__init__(self)

        # logger.set_min_log_priority_info()

        # Chassis specific slot numbering
        self.is_chassis_modular = nokia_common.is_chassis_modular()
        self.cpm_instance = nokia_common._get_cpm_slot()
        self.my_instance = nokia_common._get_my_slot()
        self.is_cpm = nokia_common.is_cpm()

        # Create a GRPC channel
        self.chassis_stub = None
        self.fan_stub = None
        self.thermal_stub = None
        self.psu_stub = None
        self.firmware_stub = None

        # Get maximum power consumed by each module like cards, fan-trays etc
        self._get_modules_consumed_power()

        # Module list
        self.get_module_list()

        # PSU list
        self._get_psu_list()

        # FAN list
        self._get_fantray_list()

        # Thermal list
        self.get_thermal_list()

        # Component List
        self._get_component_list()

        # SFP
        self.sfp_module_initialized = False
        self.sfp_event_initialized = False

        # Watchdog
        if self._watchdog is None:
            self._watchdog = Watchdog("dog")
            logger.log_info('HW Watchdog initialized')

        # system eeprom
        self._eeprom = Eeprom()

    def get_presence(self):
        module = self._get_my_module()
        if module is not None:
            return module.get_presence()
        return False

    def get_status(self):
        module = self._get_my_module()
        if module is not None:
            return module.get_status()
        return False

    def get_reboot_cause(self):
        unknown = (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, None)
        # if reboot cause is non-hardware, return NON_HARDWARE
        return unknown

    def _get_my_module(self):
        module = None
        my_slot = self.get_my_slot()
        supervisor_slot = self.get_supervisor_slot()
        if supervisor_slot == my_slot:
            index = 0
        else:
            index = self.get_module_index(ModuleBase.MODULE_TYPE_LINE+str(my_slot-1))

        module = self.get_module(index)
        return module

    def get_status_led(self):
        color = Chassis.STATUS_LED_COLOR_OFF
        led_type = platform_ndk_pb2.ReqLedType.LED_TYPE_BOARD_STATUS
        channel, stub = nokia_common.channel_setup(nokia_common.NOKIA_GRPC_LED_SERVICE)
        if not channel or not stub:
            return color
        ret, response = nokia_common.try_grpc(stub.GetLed,
                                              platform_ndk_pb2.ReqLedInfoPb(led_type=led_type))
        nokia_common.channel_shutdown(channel)

        if ret is False:
            return color

        color = nokia_common.led_info_to_color(response.led_get.led_info[0])
        return color

    def set_status_led(self, color):
        led_type = platform_ndk_pb2.ReqLedType.LED_TYPE_BOARD_STATUS
        _led_info = nokia_common.led_color_to_info(color)

        channel, stub = nokia_common.channel_setup(nokia_common.NOKIA_GRPC_LED_SERVICE)
        if not channel or not stub:
            return False
        ret, response = nokia_common.try_grpc(stub.SetLed,
                                              platform_ndk_pb2.ReqLedInfoPb(led_type=led_type, led_info=_led_info))
        nokia_common.channel_shutdown(channel)

        if ret is False:
            return False
        return True

    def is_modular_chassis(self):
        return self.is_chassis_modular

    def get_supervisor_slot(self):
        return self.cpm_instance

    def get_my_slot(self):
        return self.my_instance

    def get_module_list(self):
        channel, stub = nokia_common.channel_setup(nokia_common.NOKIA_GRPC_CHASSIS_SERVICE)
        if not channel or not stub:
            return
        ret, response = nokia_common.try_grpc(stub.GetChassisProperties,
                                              platform_ndk_pb2.ReqModuleInfoPb())
        nokia_common.channel_shutdown(channel)

        if ret is False:
            return

        if self.is_modular_chassis() and not self.is_cpm:
            index = 0
            supervisor = Module(index,
                                ModuleBase.MODULE_TYPE_SUPERVISOR+str(index),
                                ModuleBase.MODULE_TYPE_SUPERVISOR,
                                self.get_supervisor_slot(), self.chassis_stub)
            supervisor.set_maximum_consumed_power(self.supervisor_power)

            index = 1
            module = Module(index,
                            ModuleBase.MODULE_TYPE_LINE+str(self.get_my_slot()-1),
                            ModuleBase.MODULE_TYPE_LINE,
                            self.get_my_slot(), self.chassis_stub)
            module.set_maximum_consumed_power(self.line_card_power)

            self._module_list.append(supervisor)
            self._module_list.append(module)
            logger.log_info('Not control card. Adding self into module list')
            return

        if self.is_modular_chassis() and self.is_cpm:
            for property_index in range(len(response.chassis_property.hw_property)):
                hw_property = response.chassis_property.hw_property[property_index]

                if hw_property.module_type == platform_ndk_pb2.HwModuleType.HW_MODULE_TYPE_CONTROL:
                    # Single CPM is supported in chassis
                    num_control_cards = 1
                    for j in range(num_control_cards):
                        module = Module(property_index,
                                        ModuleBase.MODULE_TYPE_SUPERVISOR+str(j),
                                        ModuleBase.MODULE_TYPE_SUPERVISOR,
                                        self.get_supervisor_slot(),
                                        self.chassis_stub)
                        module.set_maximum_consumed_power(self.supervisor_power)
                        self._module_list.append(module)

                elif hw_property.module_type == platform_ndk_pb2.HwModuleType.HW_MODULE_TYPE_LINE:
                    for j in range(hw_property.max_num):
                        module = Module(property_index,
                                        ModuleBase.MODULE_TYPE_LINE+str(j),
                                        ModuleBase.MODULE_TYPE_LINE,
                                        hw_property.slot[j],
                                        self.chassis_stub)
                        module.set_maximum_consumed_power(self.line_card_power)
                        self._module_list.append(module)

                elif hw_property.module_type == platform_ndk_pb2.HwModuleType.HW_MODULE_TYPE_FABRIC:
                    for j in range(hw_property.max_num):
                        module = Module(property_index,
                                        ModuleBase.MODULE_TYPE_FABRIC+str(j),
                                        ModuleBase.MODULE_TYPE_FABRIC,
                                        hw_property.slot[j],
                                        self.chassis_stub)
                        module.set_maximum_consumed_power(self.fabric_card_power)
                        self._module_list.append(module)

    def get_module_index(self, module_name):
        if not self.is_modular_chassis():
            return -1

        # For IMM on chassis, return supervisor-index as 0 and self index as 1
        if not self.is_cpm:
            if module_name.startswith(ModuleBase.MODULE_TYPE_SUPERVISOR):
                return 0
            else:
                return 1

        # For CPM on chassis
        module_index = -1
        channel, stub = nokia_common.channel_setup(nokia_common.NOKIA_GRPC_CHASSIS_SERVICE)
        if not channel or not stub:
            return module_index
        ret, response = nokia_common.try_grpc(stub.GetChassisProperties,
                                              platform_ndk_pb2.ReqModuleInfoPb())
        nokia_common.channel_shutdown(channel)

        if ret is False:
            return module_index

        for property_index in range(len(response.chassis_property.hw_property)):
            hw_property = response.chassis_property.hw_property[property_index]

            if hw_property.module_type == platform_ndk_pb2.HwModuleType.HW_MODULE_TYPE_CONTROL:
                # Single CPM is supported in chassis
                num_control_cards = 1
            elif hw_property.module_type == platform_ndk_pb2.HwModuleType.HW_MODULE_TYPE_LINE:
                num_line_cards = hw_property.max_num
            # elif hw_property.module_type == platform_ndk_pb2.HwModuleType.HW_MODULE_TYPE_FABRIC:
            #    num_fabric_cards = hw_property.max_num

        if module_name.startswith(ModuleBase.MODULE_TYPE_SUPERVISOR):
            module_index = 0
        elif module_name.startswith(ModuleBase.MODULE_TYPE_LINE):
            import re
            parse_nums = re.findall(r'\d+', module_name)
            module_number = int(parse_nums[0])
            module_index = num_control_cards + int(module_number)
        elif module_name.startswith(ModuleBase.MODULE_TYPE_FABRIC):
            import re
            parse_nums = re.findall(r'\d+', module_name)
            module_number = int(parse_nums[0])
            module_index = num_control_cards + num_line_cards + int(module_number)

        return module_index

    # PSU and power related
    def _get_psu_list(self):
        if not self.is_cpm:
            return []

        channel, stub = nokia_common.channel_setup(nokia_common.NOKIA_GRPC_PSU_SERVICE)
        if not channel or not stub:
            return
        ret, response = nokia_common.try_grpc(stub.GetPsuNum,
                                              platform_ndk_pb2.ReqPsuInfoPb())
        nokia_common.channel_shutdown(channel)

        if ret is False:
            return

        self.num_psus = response.num_psus
        for i in range(self.num_psus):
            psu = Psu(i, self.psu_stub)
            self._psu_list.append(psu)
        return self._psu_list

    def _get_modules_consumed_power(self):
        channel, stub = nokia_common.channel_setup(nokia_common.NOKIA_GRPC_CHASSIS_SERVICE)
        if not channel or not stub:
            return
        ret, response = nokia_common.try_grpc(stub.GetModuleMaxPower,
                                              platform_ndk_pb2.ReqModuleInfoPb())
        nokia_common.channel_shutdown(channel)

        if ret is False:
            return

        i = 0
        while i < len(response.power_info.module_power):
            module_info = response.power_info.module_power[i]
            i = i + 1
            type = module_info.module_type
            if type == platform_ndk_pb2.HwModuleType.HW_MODULE_TYPE_CONTROL:
                self.supervisor_power = module_info.module_maxpower
            elif type == platform_ndk_pb2.HwModuleType.HW_MODULE_TYPE_LINE:
                self.line_card_power = module_info.module_maxpower
            elif type == platform_ndk_pb2.HwModuleType.HW_MODULE_TYPE_FABRIC:
                self.fabric_card_power = module_info.module_maxpower
            elif type == platform_ndk_pb2.HwModuleType.HW_MODULE_TYPE_FANTRAY:
                self.fantray_power = module_info.module_maxpower

    # Fan related
    def _get_fantray_list(self):
        if not self.is_cpm:
            return []

        channel, stub = nokia_common.channel_setup(nokia_common.NOKIA_GRPC_FAN_SERVICE)
        if not channel or not stub:
            return
        ret, response = nokia_common.try_grpc(stub.GetFanNum,
                                              platform_ndk_pb2.ReqFanTrayOpsPb())
        nokia_common.channel_shutdown(channel)

        if ret is False:
            return

        self.num_fantrays = response.fan_nums.num_fantrays

        fan_index = 0
        for drawer_index in range(self.num_fantrays):
            fan_drawer = FanDrawer(drawer_index)
            fan_drawer.set_maximum_consumed_power(self.fantray_power)
            fan = Fan(fan_index, drawer_index, False, self.fan_stub)
            fan_drawer._fan_list.append(fan)
            fan_index = fan_index + 1
            self._fan_drawer_list.append(fan_drawer)

        return self._fan_drawer_list

    def allow_fan_platform_override(self):
        from os import path
        if path.exists(nokia_common.NOKIA_ALLOW_FAN_OVERRIDE_FILE):
            return True
        return False

    # Thermal related
    def get_thermal_list(self):
        channel, stub = nokia_common.channel_setup(nokia_common.NOKIA_GRPC_THERMAL_SERVICE)
        if not channel or not stub:
            return
        ret, response = nokia_common.try_grpc(stub.GetThermalDevicesInfo,
                                              platform_ndk_pb2.ReqTempParamsPb())
        nokia_common.channel_shutdown(channel)

        if ret is False:
            return

        all_temp_devices = response.temp_devices
        self.num_thermals = len(all_temp_devices.temp_device)
        i = 0

        # Empty previous list
        if len(self._thermal_list) != self.num_thermals:
            del self._thermal_list[:]

            for i in range(self.num_thermals):
                temp_device_ = all_temp_devices.temp_device[i]
                thermal = Thermal(i, temp_device_.device_idx, temp_device_.sensor_name, self.thermal_stub)
                self._thermal_list.append(thermal)

    def get_all_thermals(self):
        self.get_thermal_list()
        return self._thermal_list

    def get_thermal_manager(self):
        from .thermal_manager import ThermalManager
        return ThermalManager

    def _get_component_list(self):
        channel, stub = nokia_common.channel_setup(nokia_common.NOKIA_GRPC_FIRMWARE_SERVICE)
        if not channel or not stub:
            return
        ret, response = nokia_common.try_grpc(stub.HwFirmwareGetComponents,
                                              platform_ndk_pb2.ReqHwFirmwareInfoPb())
        nokia_common.channel_shutdown(channel)

        if ret is False:
            return

        for i in range(len(response.firmware_info.component)):
            hw_dev = response.firmware_info.component[i]
            component = Component(i, hw_dev.dev_type, hw_dev.dev_name,
                                  hw_dev.dev_desc, self.firmware_stub)
            self._component_list.append(component)

    # SFP operations below
    def initialize_sfp(self):
        from sonic_platform.sfp import Sfp

        if not nokia_common.is_cpm():
            if self.sfp_module_initialized:
                logger.log_error("SFPs are already initialized! stub {}".format(self.sfp_stub))
                return

            self.sfp_stub = None
            for index in range(1, NUM_SFP+1):
                logger.log_info("Creating SFP '%d'" % index)
                # default to QSFP-DD type for the moment. Type gets set dynamically when module is read
                sfp = Sfp(index, 'QSFP-DD', self.sfp_stub)
                self._sfp_list.append(sfp)
                # force 1st read to dynamically set type and detect dom capability
                sfp.get_transceiver_info()
                sfp._dom_capability_detect()
            self.sfp_module_initialized = True
            logger.log_info("SFPs are now initialized... stub {}".format(self.sfp_stub))

    def get_change_event(self, timeout=0):
        # logger.log_error("Get-change-event with thread-{} start ".format(
        #    str(os.getpid())+str(threading.current_thread().ident)))
        if not self.sfp_event_initialized:
            from sonic_platform.sfp_event import sfp_event
            # use the same stub as direct sfp ops does
            self.sfp_event_stub = self.sfp_stub

            logger.log_info("Initializing sfp_event with num {} and stub {} : sfp stub {}".format(
                NUM_SFP, self.sfp_event_stub, self.sfp_stub))
            self.sfp_event_list = sfp_event(NUM_SFP, self.sfp_event_stub)
            self.sfp_event_list.initialize()
            self.sfp_event_initialized = True

        timeout = 0
        port_dict = {}
        self.sfp_event_list.check_sfp_status(port_dict, timeout)

        return True, {'sfp': port_dict}

    def get_num_sfps(self):
        """
        Retrieves the number of sfps available on this chassis
        Returns:
            An integer, the number of sfps available on this chassis
        """
        if not self.sfp_module_initialized:
            self.initialize_sfp()

        return len(self._sfp_list)

    def get_all_sfps(self):
        """
        Retrieves all sfps available on this chassis
        Returns:
            A list of objects derived from SfpBase representing all sfps
            available on this chassis
        """
        if not self.sfp_module_initialized:
            self.initialize_sfp()

        return self._sfp_list

    def get_sfp(self, index):
        """
        Retrieves sfp represented by (1-based) index <index>
        Args:
            index: An integer, the index (1-based) of the sfp to retrieve.
            The index should be the sequence of a physical port in a chassis,
            starting from 1.
            For example, 1 for Ethernet0, 2 for Ethernet4 and so on.
        Returns:
            An object dervied from SfpBase representing the specified sfp
        """
        sfp = None
        if not self.sfp_module_initialized:
            self.initialize_sfp()

        try:
            # The index will start from 1
            sfp = self._sfp_list[index-1]
        except IndexError:
            logger.log_error("SFP index {} out of range (1-{})\n".format(
                             index, len(self._sfp_list)))

        return sfp

    # System eeprom below
    def get_name(self):
        """
        Retrieves the hardware product name for the chassis

        Returns:
            A string containing the hardware product name for this chassis.
        """
        return self._eeprom.get_product_name()

    def get_base_mac(self):
        """
        Retrieves the base MAC address for the chassis

        Returns:
            A string containing the MAC address in the format
            'XX:XX:XX:XX:XX:XX'
        """
        return self._eeprom.get_base_mac()

    def get_serial(self):
        """
        Retrieves the hardware serial number for the chassis

        Returns:
            A string containing the hardware serial number for this chassis.
        """
        return self._eeprom.get_serial_number()

    def get_serial_number(self):
        """
        Retrieves the hardware serial number for the chassis

        Returns:
            A string containing the hardware serial number for this chassis.
        """
        return self._eeprom.get_serial_number()

    def get_model(self):
        """
        Retrieves the model number (or part number) of the chassis
        Returns:
            string: Model/part number of chassis
        """
        return self._eeprom.get_part_number()

    def get_system_eeprom_info(self):
        """
        Retrieves the full content of system EEPROM information for the chassis

        Returns:
            A dictionary where keys are the type code defined in
            OCP ONIE TlvInfo EEPROM format and values are their corresponding
            values.
        """
        return self._eeprom.get_system_eeprom_info()

    def get_eeprom(self):
        """
        Retrieves the Sys Eeprom instance for the chassis.
        Returns :
            The instance of the Sys Eeprom
        """
        return self._eeprom

    # Midplane
    def init_midplane_switch(self):
        return True

    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 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

    # Test suitest
    def test_suite_chassis(self):
        print("Starting Chassis UT")
        from sonic_platform.test import test_chassis
        test_chassis.run_all(self)

    def test_suite_psu(self):
        print("Starting PSUs UT")
        from sonic_platform.test import test_psu
        test_psu.run_all(self._psu_list)

    # UT related helper apis
    def empty_fan_drawer_list(self):
        for fantray in self.get_all_fan_drawers():
            del fantray._fan_list[:]
        del self._fan_drawer_list[:]

    def empty_fan_list(self, fantray_idx):
        fantray = self.get_fan_drawer(fantray_idx)
        del fantray._fan_list[:]
class FanDrawer(FanDrawerBase):
    """DellEMC Platform-specific Fan Drawer class"""

    CPLD_DIR = "/sys/devices/platform/dell-s6000-cpld.0/"

    def __init__(self, fantray_index):
        FanDrawerBase.__init__(self)
        # FanTray is 1-based in DellEMC platforms
        self.index = fantray_index + 1
        self.eeprom = Eeprom(is_fantray=True, fantray_index=self.index)
        self.fantray_presence_reg = "fan_prs"
        self.fantray_led_reg = "fan{}_led".format(self.index - 1)
        self.supported_led_color = ['off', 'green', 'amber']

        for i in range(1, MAX_S6000_FANS_PER_FANTRAY + 1):
            self._fan_list.append(
                Fan(fantray_index=self.index, fan_index=i, dependency=self))

    def _get_cpld_register(self, reg_name):
        # On successful read, returns the value read from given
        # reg_name and on failure returns 'ERR'
        rv = 'ERR'
        cpld_reg_file = self.CPLD_DIR + reg_name

        if (not os.path.isfile(cpld_reg_file)):
            return rv

        try:
            with open(cpld_reg_file, 'r') as fd:
                rv = fd.read()
        except IOError:
            rv = 'ERR'

        rv = rv.rstrip('\r\n')
        rv = rv.lstrip(" ")
        return rv

    def _set_cpld_register(self, reg_name, value):
        # On successful write, returns the value will be written on
        # reg_name and on failure returns 'ERR'
        rv = 'ERR'
        cpld_reg_file = self.CPLD_DIR + reg_name

        if (not os.path.isfile(cpld_reg_file)):
            return rv

        try:
            with open(cpld_reg_file, 'w') as fd:
                rv = fd.write(str(value))
        except IOError:
            rv = 'ERR'

        return rv

    def get_name(self):
        """
        Retrieves the Fandrawer name
        Returns:
            string: The name of the device
        """
        return "FanTray{}".format(self.index)

    def get_presence(self):
        """
        Retrieves the presence of the Fandrawer

        Returns:
            bool: True if Fandrawer is present, False if not
        """
        presence = False

        fantray_presence = self._get_cpld_register(self.fantray_presence_reg)
        if (fantray_presence != 'ERR'):
            fantray_presence = int(fantray_presence, 16) & (1 <<
                                                            (self.index - 1))
            if fantray_presence:
                presence = True

        return presence

    def get_model(self):
        """
        Retrieves the part number of the Fandrawer

        Returns:
            string: Part number of Fandrawer
        """
        return self.eeprom.get_part_number()

    def get_serial(self):
        """
        Retrieves the serial number of the Fandrawer

        Returns:
            string: Serial number of Fandrawer
        """
        # Sample Serial number format "US-01234D-54321-25A-0123-A00"
        return self.eeprom.get_serial_number()

    def get_status(self):
        """
        Retrieves the operational status of the Fandrawer

        Returns:
            bool: True if Fandrawer is operating properly, False if not
        """
        status = True
        for fan in self.get_all_fans():
            status &= fan.get_status()

        return 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 or -1 if cannot determine the position
        """
        return self.index

    def is_replaceable(self):
        """
        Indicate whether this fan drawer is replaceable.
        Returns:
            bool: True if it is replaceable.
        """
        return True

    def set_status_led(self, color):
        """
        Set led to expected color
        Args:
            color: A string representing the color with which to set the
                   fandrawer status LED
        Returns:
            bool: True if set success, False if fail.
        """
        if color not in self.supported_led_color:
            return False
        if color == self.STATUS_LED_COLOR_AMBER:
            color = 'yellow'

        rv = self._set_cpld_register(self.fantray_led_reg, color)
        if (rv != 'ERR'):
            return True
        else:
            return False

    def get_status_led(self):
        """
        Gets the state of the fandrawer status LED

        Returns:
            A string, one of the predefined STATUS_LED_COLOR_* strings.
        """
        fantray_led = self._get_cpld_register(self.fantray_led_reg)
        if (fantray_led != 'ERR'):
            if (fantray_led == 'yellow'):
                return self.STATUS_LED_COLOR_AMBER
            else:
                return fantray_led
        else:
            return self.STATUS_LED_COLOR_OFF

    def get_maximum_consumed_power(self):
        """
        Retrives the maximum power drawn by Fan Drawer

        Returns:
            A float, with value of the maximum consumable power of the
            component.
        """
        return 18.0
class Chassis(ChassisBase):
    """
    DELLEMC Platform-specific Chassis class
    """
    CPLD_DIR = "/sys/devices/platform/dell-s6000-cpld.0"

    sfp_control = ""
    PORT_START = 0
    PORT_END = 0
    reset_reason_dict = {}
    reset_reason_dict[0xe] = ChassisBase.REBOOT_CAUSE_NON_HARDWARE
    reset_reason_dict[0x6] = ChassisBase.REBOOT_CAUSE_NON_HARDWARE
    reset_reason_dict[0x7] = ChassisBase.REBOOT_CAUSE_THERMAL_OVERLOAD_OTHER

    _num_monitor_thermals = 3
    _monitor_thermal_list = []
    _is_fan_control_enabled = False
    _fan_control_initialised = False

    def __init__(self):
        ChassisBase.__init__(self)
        self.status_led_reg = "system_led"
        self.supported_led_color = [
            'green', 'blinking green', 'amber', 'blinking amber'
        ]
        # Initialize SFP list
        self.PORT_START = 0
        self.PORT_END = 31
        EEPROM_OFFSET = 20
        PORTS_IN_BLOCK = (self.PORT_END + 1)

        # sfp.py will read eeprom contents and retrive the eeprom data.
        # It will also provide support sfp controls like reset and setting
        # low power mode.
        # We pass the eeprom path and sfp control path from chassis.py
        # So that sfp.py implementation can be generic to all platforms
        eeprom_base = "/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom"
        self.sfp_control = "/sys/devices/platform/dell-s6000-cpld.0/"

        for index in range(0, PORTS_IN_BLOCK):
            eeprom_path = eeprom_base.format(index + EEPROM_OFFSET)
            sfp_node = Sfp(index, 'QSFP', eeprom_path, self.sfp_control, index)
            self._sfp_list.append(sfp_node)

        # Get Transceiver status
        self.modprs_register = self._get_transceiver_status()

        with open("/sys/class/dmi/id/product_name", "r") as fd:
            board_type = fd.read()

        if 'S6000-ON' in board_type:
            self._eeprom = Eeprom()
        else:
            self._eeprom = EepromS6000()

        for i in range(MAX_S6000_FANTRAY):
            fandrawer = FanDrawer(i)
            self._fan_drawer_list.append(fandrawer)
            self._fan_list.extend(fandrawer._fan_list)

        for i in range(MAX_S6000_PSU):
            psu = Psu(i)
            self._psu_list.append(psu)

        for i in range(MAX_S6000_THERMAL):
            thermal = Thermal(i)
            self._thermal_list.append(thermal)

        for i in range(MAX_S6000_COMPONENT):
            component = Component(i)
            self._component_list.append(component)

    def _get_cpld_register(self, reg_name):
        rv = 'ERR'
        mb_reg_file = self.CPLD_DIR + '/' + reg_name

        if (not os.path.isfile(mb_reg_file)):
            return rv

        try:
            with open(mb_reg_file, 'r') as fd:
                rv = fd.read()
        except IOError:
            rv = 'ERR'

        rv = rv.rstrip('\r\n')
        rv = rv.lstrip(" ")
        return rv

    def _set_cpld_register(self, reg_name, value):
        # On successful write, returns the value will be written on
        # reg_name and on failure returns 'ERR'
        rv = 'ERR'
        cpld_reg_file = self.CPLD_DIR + '/' + reg_name

        if (not os.path.isfile(cpld_reg_file)):
            return rv

        try:
            with open(cpld_reg_file, 'w') as fd:
                rv = fd.write(str(value))
        except IOError:
            rv = 'ERR'

        return rv

    def _nvram_write(self, offset, val):
        resource = "/dev/nvram"
        fd = os.open(resource, os.O_RDWR)
        if (fd < 0):
            print('File open failed ', resource)
            return
        if (os.lseek(fd, offset, os.SEEK_SET) != offset):
            print('lseek failed on ', resource)
            return
        ret = os.write(fd, struct.pack('B', val))
        if ret != 1:
            print('Write failed ', str(ret))
            return
        os.close(fd)

    def _init_fan_control(self):

        if not self._fan_control_initialised:
            for i in range(self._num_monitor_thermals):
                self._monitor_thermal_list.append(Thermal(i))
            self._fan_control_initialised = True

    def get_name(self):
        """
        Retrieves the name of the chassis
        Returns:
            string: The name of the chassis
        """
        return self._eeprom.get_model()

    def get_presence(self):
        """
        Retrieves the presence of the chassis
        Returns:
            bool: True if chassis is present, False if not
        """
        return True

    def get_model(self):
        """
        Retrieves the model number (or part number) of the chassis
        Returns:
            string: Model/part number of chassis
        """
        return self._eeprom.get_part_number()

    def get_serial(self):
        """
        Retrieves the serial number of the chassis (Service tag)
        Returns:
            string: Serial number of chassis
        """
        return self._eeprom.get_serial()

    def get_status(self):
        """
        Retrieves the operational status of the chassis
        Returns:
            bool: A boolean value, True if chassis is operating properly
            False if not
        """
        return True

    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 or -1 if cannot determine the position
        """
        return -1

    def is_replaceable(self):
        """
        Indicate whether Chassis is replaceable.
        Returns:
            bool: True if it is replaceable.
        """
        return False

    def get_base_mac(self):
        """
        Retrieves the base MAC address for the chassis

        Returns:
            A string containing the MAC address in the format
            'XX:XX:XX:XX:XX:XX'
        """
        return self._eeprom.get_base_mac()

    def get_system_eeprom_info(self):
        """
        Retrieves the full content of system EEPROM information for the
        chassis

        Returns:
            A dictionary where keys are the type code defined in
            OCP ONIE TlvInfo EEPROM format and values are their
            corresponding values.
        """
        return self._eeprom.system_eeprom_info()

    def get_reboot_cause(self):
        """
        Retrieves the cause of the previous reboot
        """
        # In S6000, We track the reboot reason by writing the reason in
        # NVRAM. Only Warmboot and Coldboot reason are supported here.
        # Since it does not support any hardware reason, we return
        # non_hardware as default
        lrr = self._get_cpld_register('last_reboot_reason')
        if (lrr != 'ERR'):
            reset_reason = int(lrr, base=16)
            if (reset_reason in self.reset_reason_dict):
                return (self.reset_reason_dict[reset_reason], None)

        return (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, None)

    def _get_transceiver_status(self):
        presence_ctrl = self.sfp_control + 'qsfp_modprs'
        try:
            reg_file = open(presence_ctrl)

        except IOError as e:
            return False

        content = reg_file.readline().rstrip()
        reg_file.close()

        return int(content, 16)

    def get_change_event(self, timeout=0):
        """
        Returns a nested dictionary containing all devices which have
        experienced a change at chassis level

        Args:
            timeout: Timeout in milliseconds (optional). If timeout == 0,
                this method will block until a change is detected.

        Returns:
            (bool, dict):
                - True if call successful, False if not;
                - A nested dictionary where key is a device type,
                  value is a dictionary with key:value pairs in the
                  format of {'device_id':'device_event'},
                  where device_id is the device ID for this device and
                        device_event,
                             status='1' represents device inserted,
                             status='0' represents device removed.
                  Ex. {'fan':{'0':'0', '2':'1'}, 'sfp':{'11':'0'}}
                      indicates that fan 0 has been removed, fan 2
                      has been inserted and sfp 11 has been removed.
        """
        start_time = time.time()
        port_dict = {}
        ret_dict = {"sfp": port_dict}
        port = self.PORT_START
        forever = False

        if timeout == 0:
            forever = True
        elif timeout > 0:
            timeout = timeout / float(1000)  # Convert to secs
        else:
            return False, {}
        end_time = start_time + timeout

        if (start_time > end_time):
            return False, ret_dict  # Time wrap or possibly incorrect timeout

        while (timeout >= 0):
            # Check for OIR events and return updated port_dict
            reg_value = self._get_transceiver_status()
            if (reg_value != self.modprs_register):
                changed_ports = (self.modprs_register ^ reg_value)
                while (port >= self.PORT_START and port <= self.PORT_END):
                    # Mask off the bit corresponding to our port
                    mask = (1 << port)
                    if (changed_ports & mask):
                        # ModPrsL is active low
                        if reg_value & mask == 0:
                            port_dict[port] = '1'
                        else:
                            port_dict[port] = '0'
                    port += 1

                # Update reg value
                self.modprs_register = reg_value
                return True, ret_dict

            if forever:
                time.sleep(1)
            else:
                timeout = end_time - time.time()
                if timeout >= 1:
                    time.sleep(1)  # We poll at 1 second granularity
                else:
                    if timeout > 0:
                        time.sleep(timeout)
                    return True, ret_dict
        return False, ret_dict

    def initizalize_system_led(self):
        return True

    def set_status_led(self, color):
        """
        Sets the state of the system LED

        Args:
            color: A string representing the color with which to set the
                   system LED

        Returns:
            bool: True if system LED state is set successfully, False if not
        """
        if color not in self.supported_led_color:
            return False

        # Change color string format to the one used by driver
        color = color.replace('amber', 'yellow')
        color = color.replace('blinking ', 'blink_')
        rv = self._set_cpld_register(self.status_led_reg, color)
        if (rv != 'ERR'):
            return True
        else:
            return False

    def get_status_led(self):
        """
        Gets the state of the system LED

        Returns:
            A string, one of the valid LED color strings which could be vendor
            specified.
        """
        status_led = self._get_cpld_register(self.status_led_reg)
        if (status_led != 'ERR'):
            status_led = status_led.replace('yellow', 'amber')
            status_led = status_led.replace('blink_', 'blinking ')
            return status_led
        else:
            return None

    def get_thermal_manager(self):
        """
        Retrieves thermal manager class on this chassis

        Returns:
            A class derived from ThermalManagerBase representing the
            specified thermal manager
        """
        from .thermal_manager import ThermalManager
        return ThermalManager

    def set_fan_control_status(self, enable):

        if enable and not self._is_fan_control_enabled:
            self._init_fan_control()
            for thermal in self._monitor_thermal_list:
                thermal.set_high_threshold(LEVEL5_THRESHOLD, force=True)
            self._is_fan_control_enabled = True
        elif not enable and self._is_fan_control_enabled:
            for thermal in self._monitor_thermal_list:
                thermal.set_high_threshold(LEVEL4_THRESHOLD, force=True)
            self._is_fan_control_enabled = False

    def get_monitor_thermals(self):
        return self._monitor_thermal_list

    def thermal_shutdown(self):
        # Update reboot cause
        self._nvram_write(0x49, 0x7)

        subprocess.call('sync')
        time.sleep(1)
        for thermal in self._monitor_thermal_list:
            thermal.set_high_threshold(LEVEL4_THRESHOLD, force=True)

    @staticmethod
    def get_system_thermal_level(curr_thermal_level, system_temperature):
        def get_level_in_hystersis(curr_level, level1, level2):
            if curr_level != level1 and curr_level != level2:
                return level1 if abs(curr_level -
                                     level1) < abs(curr_level -
                                                   level2) else level2
            else:
                return curr_level

        if system_temperature < LEVEL0_THRESHOLD:
            curr_thermal_level = 0
        elif LEVEL0_THRESHOLD <= system_temperature < LEVEL1_THRESHOLD:
            curr_thermal_level = get_level_in_hystersis(
                curr_thermal_level, 0, 1)
        elif LEVEL1_THRESHOLD <= system_temperature <= (LEVEL2_THRESHOLD -
                                                        HYST_RANGE):
            curr_thermal_level = 1
        elif (LEVEL2_THRESHOLD -
              HYST_RANGE) < system_temperature < LEVEL2_THRESHOLD:
            curr_thermal_level = get_level_in_hystersis(
                curr_thermal_level, 1, 2)
        elif LEVEL2_THRESHOLD <= system_temperature <= (LEVEL3_THRESHOLD -
                                                        HYST_RANGE):
            curr_thermal_level = 2
        elif (LEVEL3_THRESHOLD -
              HYST_RANGE) < system_temperature < LEVEL3_THRESHOLD:
            curr_thermal_level = get_level_in_hystersis(
                curr_thermal_level, 2, 3)
        elif LEVEL3_THRESHOLD <= system_temperature < LEVEL4_THRESHOLD:
            curr_thermal_level = 3
        else:
            curr_thermal_level = 4

        return curr_thermal_level

    @staticmethod
    def is_over_temperature(temperature_list):

        over_temperature = False
        for temperature in temperature_list:
            if temperature > LEVEL4_THRESHOLD:
                over_temperature = True
                break

        return over_temperature