class Chassis(ChassisBase): """ Nokia platform-specific Chassis class Derived from Dell S6000 platform. customized for the 7215 platform. """ def __init__(self): ChassisBase.__init__(self) self.system_led_supported_color = [ 'off', 'amber', 'green', 'amber_blink', 'green_blink' ] # Port numbers for SFP List Initialization self.COPPER_PORT_START = COPPER_PORT_START self.COPPER_PORT_END = COPPER_PORT_END self.SFP_PORT_START = SFP_PORT_START self.SFP_PORT_END = SFP_PORT_END self.PORT_END = PORT_END # for non-sfp ports create dummy objects for copper / non-sfp ports for index in range(self.COPPER_PORT_START, self.COPPER_PORT_END + 1): sfp_node = Sfp(index, 'COPPER', 'N/A', 'N/A') self._sfp_list.append(sfp_node) # Verify optoe2 driver SFP eeprom devices were enumerated and exist # then create the sfp nodes eeprom_path = "/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom" mux_dev = sorted(glob.glob("/sys/class/i2c-adapter/i2c-0/i2c-[0-9]")) y = 0 for index in range(self.SFP_PORT_START, self.SFP_PORT_END + 1): mux_dev_num = mux_dev[y] port_i2c_map = mux_dev_num[-1] y = y + 1 port_eeprom_path = eeprom_path.format(port_i2c_map) if not os.path.exists(port_eeprom_path): sonic_logger.log_info("path %s didnt exist" % port_eeprom_path) sfp_node = Sfp(index, 'SFP', port_eeprom_path, port_i2c_map) self._sfp_list.append(sfp_node) self.sfp_event_initialized = False # Instantiate system eeprom object self._eeprom = Eeprom() # Construct lists fans, power supplies, thermals & components drawer_num = MAX_7215_FAN_DRAWERS fan_num_per_drawer = MAX_7215_FANS_PER_DRAWER drawer_ctor = RealDrawer fan_index = 0 for drawer_index in range(drawer_num): drawer = drawer_ctor(drawer_index) self._fan_drawer_list.append(drawer) for index in range(fan_num_per_drawer): fan = Fan(fan_index, drawer) fan_index += 1 drawer._fan_list.append(fan) self._fan_list.append(fan) for i in range(MAX_7215_PSU): psu = Psu(i) self._psu_list.append(psu) for i in range(MAX_7215_THERMAL): thermal = Thermal(i) self._thermal_list.append(thermal) for i in range(MAX_7215_COMPONENT): component = Component(i) self._component_list.append(component) 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 physical SFP ports in a chassis starting from 1. Returns: An object dervied from SfpBase representing the specified sfp """ sfp = None try: # The index will start from 1 sfp = self._sfp_list[index - 1] except IndexError: sys.stderr.write("SFP index {} out of range (1-{})\n".format( index, len(self._sfp_list))) return sfp def get_name(self): """ Retrieves the name of the chassis Returns: string: The name of the chassis """ return self._eeprom.modelstr() 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.part_number_str() def get_service_tag(self): """ Retrieves the Service Tag of the chassis Returns: string: Service Tag of chassis """ return self._eeprom.service_tag_str() 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.base_mac_addr() 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.serial_number_str() 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 Returns: A tuple (string, string) where the first element is a string containing the cause of the previous reboot. This string must be one of the predefined strings in this class. If the first string is "REBOOT_CAUSE_HARDWARE_OTHER", the second string can be used to pass a description of the reboot cause. """ # The ixs7215 CPLD does not have a hardware reboot cause register so # the hardware portion of reboot cause can't be implemented return (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, None) 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. """ # Initialize SFP event first if not self.sfp_event_initialized: from sonic_platform.sfp_event import sfp_event self.sfp_event = sfp_event() self.sfp_event.initialize() self.MAX_SELECT_EVENT_RETURNED = self.PORT_END self.sfp_event_initialized = True wait_for_ever = (timeout == 0) port_dict = {} if wait_for_ever: # xrcvd will call this monitor loop in the "SYSTEM_READY" state timeout = MAX_SELECT_DELAY while True: status = self.sfp_event.check_sfp_status(port_dict, timeout) if not port_dict == {}: break else: # At boot up and in "INIT" state call from xrcvd will have timeout # value return true without change after timeout and will # transition to "SYSTEM_READY" status = self.sfp_event.check_sfp_status(port_dict, timeout) if status: return True, {'sfp': port_dict} else: return True, {'sfp': {}} def get_thermal_manager(self): from .thermal_manager import ThermalManager return ThermalManager 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.system_led_supported_color: return False if (color == 'off'): value = 0x00 elif (color == 'amber'): value = 0x01 elif (color == 'green'): value = 0x02 elif (color == 'amber_blink'): value = 0x03 elif (color == 'green_blink'): value = 0x04 else: return False # Write sys led if smbus_present == 0: # called from host (e.g. 'show system-health') cmdstatus, value = cmd.getstatusoutput( 'sudo i2cset -y 0 0x41 0x7 %d' % value) if cmdstatus: sonic_logger.log_warning(" System LED set %s failed" % value) return False else: bus = smbus.SMBus(0) DEVICE_ADDRESS = 0x41 DEVICEREG = 0x7 bus.write_byte_data(DEVICE_ADDRESS, DEVICEREG, value) return True 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. """ # Read sys led if smbus_present == 0: # called from host cmdstatus, value = cmd.getstatusoutput('sudo i2cget -y 0 0x41 0x7') value = int(value, 16) else: bus = smbus.SMBus(0) DEVICE_ADDRESS = 0x41 DEVICE_REG = 0x7 value = bus.read_byte_data(DEVICE_ADDRESS, DEVICE_REG) if value == 0x00: color = 'off' elif value == 0x01: color = 'amber' elif value == 0x02: color = 'green' elif value == 0x03: color = 'amber_blink' elif value == 0x04: color = 'green_blink' else: return None return color def get_watchdog(self): """ Retrieves hardware watchdog device on this chassis Returns: An object derived from WatchdogBase representing the hardware watchdog device Note: We overload this method to ensure that watchdog is only initialized when it is referenced. Currently, only one daemon can open the watchdog. To initialize watchdog in the constructor causes multiple daemon try opening watchdog when loading and constructing a chassis object and fail. By doing so we can eliminate that risk. """ try: if self._watchdog is None: from sonic_platform.watchdog import WatchdogImplBase watchdog_device_path = "/dev/watchdog0" self._watchdog = WatchdogImplBase(watchdog_device_path) except Exception as e: sonic_logger.log_warning(" Fail to load watchdog {}".format( repr(e))) return self._watchdog 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
class Fan(FanBase): """Nokia platform-specific Fan class""" def __init__(self, fan_index, fan_drawer, psu_fan=False, dependency=None): self.is_psu_fan = psu_fan ADT7473_DIR = "/sys/bus/i2c/devices/0-002e/hwmon/hwmon1/" if not self.is_psu_fan: # Fan is 1-based in Nokia platforms self.index = fan_index + 1 self.fan_drawer = fan_drawer self.set_fan_speed_reg = ADT7473_DIR+"pwm{}".format(self.index) self.get_fan_speed_reg = ADT7473_DIR+"fan{}_input".format(self.index) self.max_fan_speed = MAX_IXS7215_FAN_SPEED self.supported_led_color = ['off', 'green', 'red'] # Fan eeprom self.eeprom = Eeprom(is_fan=True, fan_index=self.index) else: # this is a PSU Fan self.index = fan_index self.dependency = dependency 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 os.path.isfile(reg_file)): return rv try: with open(reg_file, 'r') as fd: rv = fd.read() except Exception as e: 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 Exception as e: rv = 'ERR' # Ensure that the write operation has succeeded if (int(self._get_i2c_register(reg_file)) != value ): time.sleep(3) if (int(self._get_i2c_register(reg_file)) != value ): rv = 'ERR' return rv def get_name(self): """ Retrieves the name of the Fan Returns: string: The name of the Fan """ if not self.is_psu_fan: return "Fan{}".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 """ if smbus_present == 0: sonic_logger.log_info("PMON fan-smbus ERROR - presence ") return False else: bus = smbus.SMBus(0) DEVICE_ADDRESS = 0x41 DEVICE_REG = 0xb fanstatus = bus.read_byte_data(DEVICE_ADDRESS, DEVICE_REG) if self.index == 1: fanstatus = fanstatus & 1 if fanstatus == 1: return False if self.index == 2: fanstatus = fanstatus & 2 if fanstatus == 2: return False return True def get_model(self): """ Retrieves the model number of the Fan Returns: string: Model number of Fan. Use part number for this. """ return self.eeprom.part_number_str() def get_serial(self): """ Retrieves the serial number of the Fan Returns: string: Serial number of Fan """ return self.eeprom.serial_number_str() def get_part_number(self): """ Retrieves the part number of the Fan Returns: string: Part number of Fan """ return self.eeprom.part_number_str() def get_service_tag(self): """ Retrieves the service tag of the Fan Returns: string: Service Tag of Fan """ return self.eeprom.service_tag_str() 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) > WORKING_IXS7215_FAN_SPEED): status = True return status def get_direction(self): """ Retrieves the fan airflow direction Possible fan directions (relative to port-side of device) Returns: A string, either FAN_DIRECTION_INTAKE or FAN_DIRECTION_EXHAUST depending on fan direction """ return 'intake' 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 def get_speed(self): """ Retrieves the speed of a Front FAN in the tray in revolutions per minute defined by 1-based index :param index: An integer, 1-based index of the FAN to query speed :return: integer, denoting front FAN speed """ speed = 0 fan_speed = self._get_i2c_register(self.get_fan_speed_reg) if (fan_speed != 'ERR'): speed_in_rpm = int(fan_speed) else: speed_in_rpm = 0 speed = 100*speed_in_rpm//MAX_IXS7215_FAN_SPEED if speed > 100: speed = 100 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 25% for this platform tolerance = 25 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. """ if self.is_psu_fan: return False # Set current fan duty cycle # - 0x00 : fan off # - 0x40 : 25% duty cycle # - 0x80 : 50% duty cycle (default) # - 0xff : 100% duty cycle (full speed) if speed in range(0, 6): fandutycycle = 0x00 elif speed in range(6, 41): fandutycycle = 64 elif speed in range(41, 76): fandutycycle = 128 elif speed in range(76, 101): fandutycycle = 255 else: return False rv = self._set_i2c_register(self.set_fan_speed_reg, fandutycycle) 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. off , red and green are the only settings 7215 fans """ if self.is_psu_fan or (color not in self.supported_led_color): return False if (color == self.STATUS_LED_COLOR_AMBER): return False if (color == self.STATUS_LED_COLOR_RED): value = 0x02 elif (color == self.STATUS_LED_COLOR_GREEN): value = 0x01 elif (color == self.STATUS_LED_COLOR_OFF): value = 0x00 else: return False if smbus_present == 0: return False else: bus = smbus.SMBus(0) DEVICE_ADDRESS = 0x41 DEVICEREG = 0x8 original = bus.read_byte_data(DEVICE_ADDRESS, DEVICEREG) if (self.index == 1): new = value << 4 ledstatus = original & 0xcf ledstatus = ledstatus | new elif self.index == 2: new = value << 6 ledstatus = original & 0x3f ledstatus = ledstatus | new else: return False bus.write_byte_data(DEVICE_ADDRESS, DEVICEREG, ledstatus) return True 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: return None if smbus_present == 0: return None else: bus = smbus.SMBus(0) DEVICE_ADDRESS = 0x41 DEVICE_REG = 0x8 ledstatus = bus.read_byte_data(DEVICE_ADDRESS, DEVICE_REG) if self.index == 1: ledstatus = (ledstatus & 0x30) ledstatus = ledstatus >> 4 elif self.index == 2: ledstatus = (ledstatus & 0xC0) ledstatus = ledstatus >> 6 if ledstatus == 0x02: return self.STATUS_LED_COLOR_RED elif ledstatus == 0x1: return self.STATUS_LED_COLOR_GREEN 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) """ speed = 0 fan_duty = self._get_i2c_register(self.set_fan_speed_reg) if (fan_duty != 'ERR'): dutyspeed = int(fan_duty) if dutyspeed == 0: speed = 0 elif dutyspeed == 64: speed = 25 elif dutyspeed == 128: speed = 50 elif dutyspeed == 255: speed = 100 return speed