Ejemplo n.º 1
0
 def build_machine(self):
     self.machine = BMCMachine()
Ejemplo n.º 2
0
class Fscd(object):

    DEFAULT_BOOST = 100
    DEFAULT_BOOST_TYPE = 'default'
    DEFAULT_TRANSITIONAL = 70
    DEFAULT_RAMP_RATE = 10

    def __init__(self, config=RAMFS_CONFIG, zone_config=CONFIG_DIR,
                 log_level='warning'):
        Logger.start("fscd", log_level)
        Logger.info("Starting fscd")
        self.zone_config = zone_config
        self.fsc_config = self.get_fsc_config(config)  # json dump from config
        self.boost = self.DEFAULT_BOOST
        self.boost_type = self.DEFAULT_BOOST_TYPE
        self.transitional = self.DEFAULT_TRANSITIONAL
        self.ramp_rate = self.DEFAULT_RAMP_RATE
        self.sensor_fail = None
        self.ssd_progressive_algorithm = None
        self.sensor_valid_check = None
        self.fail_sensor_type = None
        self.fan_dead_boost = None
        self.fan_fail = None

    # TODO: Add checks for invalid config file path
    def get_fsc_config(self, fsc_config):
        if os.path.isfile(fsc_config):
            Logger.info("Started, reading configuration from %s" %
                        (fsc_config))
            with open(fsc_config, 'r') as f:
                return json.load(f)

    def get_config_params(self):
        self.transitional = self.fsc_config['pwm_transition_value']
        self.boost = self.fsc_config['pwm_boost_value']
        if 'boost' in self.fsc_config and 'fan_fail' in self.fsc_config['boost']:
                self.fan_fail = self.fsc_config['boost']['fan_fail']
        if 'boost' in self.fsc_config and 'progressive' in self.fsc_config['boost']:
                if self.fsc_config['boost']['progressive']:
                    self.boost_type = 'progressive'
        if 'fan_dead_boost' in self.fsc_config:
            self.fan_dead_boost = self.fsc_config['fan_dead_boost']
            self.all_fan_fail_counter = 0
        if 'boost' in self.fsc_config and 'sensor_fail' in self.fsc_config['boost']:
                self.sensor_fail = self.fsc_config['boost']['sensor_fail']
                if self.sensor_fail:
                    if 'fail_sensor_type' in self.fsc_config:
                        self.fail_sensor_type = self.fsc_config['fail_sensor_type']
                    if 'ssd_progressive_algorithm' in self.fsc_config:
                        self.ssd_progressive_algorithm = self.fsc_config['ssd_progressive_algorithm']
        if 'sensor_valid_check' in self.fsc_config:
            self.sensor_valid_check = self.fsc_config['sensor_valid_check']
        self.watchdog = self.fsc_config['watchdog']
        if 'fanpower' in self.fsc_config:
            self.fanpower = self.fsc_config['fanpower']
        else:
            self.fanpower = False
        if 'chassis_intrusion' in self.fsc_config:
            self.chassis_intrusion = self.fsc_config['chassis_intrusion']
        else:
            self.chassis_intrusion = False
        if 'ramp_rate' in self.fsc_config:
            self.ramp_rate = self.fsc_config['ramp_rate']
        self.wdfile = None
        if self.watchdog:
            Logger.info("watchdog pinging enabled")
            self.wdfile = open('/dev/watchdog', 'wb+', buffering=0)
            if not self.wdfile:
                Logger.error("couldn't open watchdog device")
            else:
                self.wdfile.write(b'V')
                self.wdfile.flush()
        self.interval = self.fsc_config['sample_interval_ms'] / 1000.0

    def build_profiles(self):
        self.sensors = {}
        self.profiles = {}

        for name, pdata in list(self.fsc_config['profiles'].items()):
            self.sensors[name] = Sensor(name, pdata)
            self.profiles[name] = profile_constructor(pdata)

    def build_fans(self):
        self.fans = {}
        for name, pdata in list(self.fsc_config['fans'].items()):
            self.fans[name] = Fan(name, pdata)

    def build_zones(self):
        self.zones = []
        counter = 0
        for name, data in list(self.fsc_config['zones'].items()):
            filename = data['expr_file']
            with open(os.path.join(self.zone_config, filename), 'r') as exf:
                source = exf.read()
                Logger.info("Compiling FSC expression for zone:")
                Logger.info(source)
                (expr, inf) = fsc_expr.make_eval_tree(source,
                                                      self.profiles)
                for name in inf['ext_vars']:
                    board, sname = name.split(':')
                    self.machine.frus.add(board)

                zone = Zone(data['pwm_output'], expr, inf, self.transitional,
                            counter, self.boost, self.sensor_fail, self.sensor_valid_check, self.fail_sensor_type,
                            self.ssd_progressive_algorithm)
                counter += 1
                self.zones.append(zone)

    def build_machine(self):
        self.machine = BMCMachine()

    def fsc_fan_action(self, fan, action):
        '''
        Method invokes board actions for a fan.
        '''
        if 'dead' in action:
            board_fan_actions(fan, action='dead')
            board_fan_actions(fan, action='led_red')
        if 'recover' in action:
            board_fan_actions(fan, action='recover')
            board_fan_actions(fan, action='led_blue')

    def fsc_host_action(self, action, cause):
        if 'host_shutdown' in action:
            board_host_actions(action='host_shutdown', cause=cause)
            #board_fan_actions(fan, action='led_blue')

    def fsc_set_all_fan_led(self, color):
        for fan, _value in list(self.fans.items()):
            board_fan_actions(self.fans[fan], action=color)

    def fsc_safe_guards(self, sensors_tuples):
        '''
        Method defines safe guards for fsc.
        Examples: Triggers board action when sensor temp read reaches limits
        configured in json
        '''
        for fru in self.machine.frus:
            for sensor, tuple in list(sensors_tuples[fru].items()):
                if tuple.name in self.fsc_config['profiles']:
                    if 'read_limit' in self.fsc_config['profiles'][tuple.name]:
                        # If temperature read exceeds accpetable temperature reading
                        if 'valid' in self.fsc_config['profiles'][tuple.name]['read_limit']:
                            valid_table = self.fsc_config['profiles'][tuple.name]['read_limit']['valid']
                            valid_read_limit = valid_table['limit']
                            valid_read_action = valid_table['action']
                            if tuple.value > valid_read_limit:
                                reason = sensor + '(v=' + str(tuple.value) + \
                                    ') limit(t=' + str(valid_read_limit) + \
                                    ') reached'
                                self.fsc_host_action(action=valid_read_action,
                                                     cause=reason)
                        # If temperature read fails
                        if 'invalid' in self.fsc_config['profiles'][tuple.name]['read_limit']:
                            invalid_table = self.fsc_config['profiles'][tuple.name]['read_limit']['invalid']
                            invalid_read_th = invalid_table['threshold']
                            invalid_read_action = invalid_table['action']
                            if tuple.read_fail_counter >= invalid_read_th:
                                reason = sensor + '(value=' + str(tuple.value) + \
                                        ') failed to read ' + \
                                        str(tuple.read_fail_counter) + ' times'
                                self.fsc_host_action(action=invalid_read_action,
                                                     cause=reason)

    def update_dead_fans(self, dead_fans):
        '''
        Check for dead and recovered fans
        '''
        last_dead_fans = dead_fans.copy()
        speeds = self.machine.read_fans(self.fans)
        print("\x1b[2J\x1b[H")
        sys.stdout.flush()

        for fan, rpms in list(speeds.items()):
            Logger.info("%s speed: %d RPM" % (fan.label, rpms))
            if rpms < self.fsc_config['min_rpm']:
                dead_fans.add(fan)
                self.fsc_fan_action(fan, action='dead')
            else:
                dead_fans.discard(fan)

        recovered_fans = last_dead_fans - dead_fans
        newly_dead_fans = dead_fans - last_dead_fans
        if len(newly_dead_fans) > 0:
            if self.fanpower:
                Logger.warn("%d fans failed" % (len(dead_fans),))
            else:
                Logger.crit("%d fans failed" % (len(dead_fans),))
            for dead_fan in dead_fans:
                if self.fanpower:
                    Logger.warn("%s dead, %d RPM" % (dead_fan.label,
                                speeds[dead_fan]))
                else:
                    Logger.crit("%s dead, %d RPM" % (dead_fan.label,
                                speeds[dead_fan]))
                Logger.usbdbg("%s fail" % (dead_fan.label))
        for fan in recovered_fans:
            if self.fanpower:
                Logger.warn("%s has recovered" % (fan.label,))
            else:
                Logger.crit("%s has recovered" % (fan.label,))
            Logger.usbdbg("%s recovered" % (fan.label))
            self.fsc_fan_action(fan, action='recover')
        return dead_fans

    def update_zones(self, dead_fans, time_difference):
        """
            TODO: Need to change logic here.

            # Platforms with chassis_intrusion mode enabled
            if chassis_intrusion:
                set the chassis_intrusion_boost_flag to 0
                and then do necessary checks to set flag to 1
                if chassis_intrusion_boost_flag:
                    run boost mode
                else:
                    run normal mode
            else
                # Platforms WITHOUT chassis_intrusion mode
                run normal mode

        """
        sensors_tuples = self.machine.read_sensors(self.sensors)
        self.fsc_safe_guards(sensors_tuples)
        for zone in self.zones:
            Logger.info("PWM: %s" % (json.dumps(zone.pwm_output)))

            chassis_intrusion_boost_flag = 0
            if self.chassis_intrusion:
                self_tray_pull_out = board_callout(
                    callout='chassis_intrusion')
                if self_tray_pull_out == 1:
                    chassis_intrusion_boost_flag = 1

            if chassis_intrusion_boost_flag == 0:
                pwmval = zone.run(sensors=sensors_tuples, dt=time_difference)
            else:
                pwmval = self.boost

            if self.fan_fail:
                if self.boost_type == 'progressive' and self.fan_dead_boost:
                    # Cases where we want to progressively bump PWMs
                    dead = len(dead_fans)
                    if dead > 0:
                        Logger.info("Progressive mode: Failed fans: %s" %
                              (', '.join([str(i.label) for i in dead_fans],)))
                        for fan_count, rate in self.fan_dead_boost["data"]:
                            if dead <= fan_count:
                                pwmval = clamp(pwmval + (dead * rate), 0, 100)
                                break
                        else:
                            pwmval = self.boost
                else:
                    if dead_fans:
                        # If not progressive ,when there is 1 fan failed, boost all fans
                        Logger.info("Failed fans: %s" % (
                            ', '.join([str(i.label) for i in dead_fans],)))
                        pwmval = self.boost
                    if self.fan_dead_boost:
                        # If all the fans failed take action after a few cycles
                        if len(dead_fans) == len(self.fans):
                            self.all_fan_fail_counter = self.all_fan_fail_counter + 1
                            Logger.warn("Currently all fans failed for {} cycles".format(self.all_fan_fail_counter))
                            if self.fan_dead_boost["threshold"] and self.fan_dead_boost["action"]:
                                if self.all_fan_fail_counter >= self.fan_dead_boost["threshold"]:
                                    self.fsc_host_action(
                                        action=self.fan_dead_boost["action"],
                                        cause="All fans are bad for more than "
                                              + str(self.fan_dead_boost["threshold"])
                                              + " cycles"
                                        )
                        else:
                            # If atleast 1 fan is working reset the counter
                            self.all_fan_fail_counter = 0

            if abs(zone.last_pwm - pwmval) > self.ramp_rate:
                if pwmval < zone.last_pwm:
                    pwmval = zone.last_pwm - self.ramp_rate
                else:
                    pwmval = zone.last_pwm + self.ramp_rate
            zone.last_pwm = pwmval

            if hasattr(zone.pwm_output, '__iter__'):
                for output in zone.pwm_output:
                    self.machine.set_pwm(self.fans.get(
                        str(output)), pwmval)
            else:
                self.machine.set_pwm(self.fans[zone.pwm_output], pwmval)

    def builder(self):
        '''
        Method to extract from json and build all internal data staructures
        '''
        # Build a bmc machine object - read/write sensors
        self.build_machine()
        # Extract everything from json
        self.get_config_params()
        self.build_fans()
        self.build_profiles()
        Logger.info("Available profiles: " + ", ".join(list(self.profiles.keys())))
        self.build_zones()
        Logger.info("Read %d zones" % (len(self.zones)))
        Logger.info("Including sensors from: " + ", ".join(self.machine.frus))

    def get_fan_power_status(self):
        '''
        Method invokes board action to determine fan power status.
        If not applicable returns True.
        '''
        if board_callout(callout='read_power'):
            return True
        return False

    def run(self):
        """
        Main FSCD method that builds from the fscd config and runs
        """

        # Get everything from json and build profiles, fans, zones
        self.builder()

        self.machine.set_all_pwm(self.fans, self.transitional)
        self.fsc_set_all_fan_led(color='led_blue')

        last = time.time()
        dead_fans = set()

        if self.fanpower:
            time.sleep(30)

        while True:
            if self.wdfile:
                self.wdfile.write(b'V')
                self.wdfile.flush()

            time.sleep(self.interval)

            if self.fanpower:
                if not self.get_fan_power_status():
                    continue
            if self.fan_fail:
                # Get dead fans for determining speed
                dead_fans = self.update_dead_fans(dead_fans)

            now = time.time()
            time_difference = now - last
            last = now

            # Check sensors and update zones
            self.update_zones(dead_fans, time_difference)
Ejemplo n.º 3
0
class Fscd(object):

    DEFAULT_BOOST = 100
    DEFAULT_BOOST_TYPE = "default"
    DEFAULT_TRANSITIONAL = 70
    DEFAULT_RAMP_RATE = 10

    def __init__(self,
                 config=RAMFS_CONFIG,
                 zone_config=CONFIG_DIR,
                 log_level="warning"):
        Logger.start("fscd", log_level)
        Logger.info("Starting fscd")
        self.zone_config = zone_config
        self.fsc_config = self.get_fsc_config(config)  # json dump from config
        self.boost = self.DEFAULT_BOOST
        self.boost_type = self.DEFAULT_BOOST_TYPE
        self.transitional = self.DEFAULT_TRANSITIONAL
        self.ramp_rate = self.DEFAULT_RAMP_RATE
        self.sensor_fail = None
        self.ssd_progressive_algorithm = None
        self.sensor_valid_check = None
        self.fail_sensor_type = None
        self.fan_dead_boost = None
        self.fan_fail = None
        self.fan_recovery_pending = False
        self.fan_recovery_time = None

    # TODO: Add checks for invalid config file path
    def get_fsc_config(self, fsc_config):
        if os.path.isfile(fsc_config):
            Logger.info("Started, reading configuration from %s" %
                        (fsc_config))
            with open(fsc_config, "r") as f:
                return json.load(f)

    def get_config_params(self):
        self.transitional = self.fsc_config["pwm_transition_value"]
        self.boost = self.fsc_config["pwm_boost_value"]
        if "boost" in self.fsc_config and "fan_fail" in self.fsc_config[
                "boost"]:
            self.fan_fail = self.fsc_config["boost"]["fan_fail"]
        if "boost" in self.fsc_config and "progressive" in self.fsc_config[
                "boost"]:
            if self.fsc_config["boost"]["progressive"]:
                self.boost_type = "progressive"
        if "fan_dead_boost" in self.fsc_config:
            self.fan_dead_boost = self.fsc_config["fan_dead_boost"]
            self.all_fan_fail_counter = 0
        if "boost" in self.fsc_config and "sensor_fail" in self.fsc_config[
                "boost"]:
            self.sensor_fail = self.fsc_config["boost"]["sensor_fail"]
            if self.sensor_fail:
                if "fail_sensor_type" in self.fsc_config:
                    self.fail_sensor_type = self.fsc_config["fail_sensor_type"]
                if "ssd_progressive_algorithm" in self.fsc_config:
                    self.ssd_progressive_algorithm = self.fsc_config[
                        "ssd_progressive_algorithm"]
        if "sensor_valid_check" in self.fsc_config:
            self.sensor_valid_check = self.fsc_config["sensor_valid_check"]
        self.watchdog = self.fsc_config["watchdog"]
        if "fanpower" in self.fsc_config:
            self.fanpower = self.fsc_config["fanpower"]
        else:
            self.fanpower = False
        if "chassis_intrusion" in self.fsc_config:
            self.chassis_intrusion = self.fsc_config["chassis_intrusion"]
        else:
            self.chassis_intrusion = False
        if "ramp_rate" in self.fsc_config:
            self.ramp_rate = self.fsc_config["ramp_rate"]
        if self.watchdog:
            Logger.info("watchdog pinging enabled")
            kick_watchdog()
        self.interval = self.fsc_config["sample_interval_ms"] / 1000.0
        if "fan_recovery_time" in self.fsc_config:
            self.fan_recovery_time = self.fsc_config["fan_recovery_time"]

    def build_profiles(self):
        self.sensors = {}
        self.profiles = {}

        for name, pdata in list(self.fsc_config["profiles"].items()):
            self.sensors[name] = Sensor(name, pdata)
            self.profiles[name] = profile_constructor(pdata)

    def build_fans(self):
        self.fans = {}
        for name, pdata in list(self.fsc_config["fans"].items()):
            self.fans[name] = Fan(name, pdata)

    def build_zones(self):
        self.zones = []
        counter = 0
        for name, data in list(self.fsc_config["zones"].items()):
            filename = data["expr_file"]
            with open(os.path.join(self.zone_config, filename), "r") as exf:
                source = exf.read()
                Logger.info("Compiling FSC expression for zone:")
                Logger.info(source)
                (expr, inf) = fsc_expr.make_eval_tree(source, self.profiles)
                for name in inf["ext_vars"]:
                    sdata = name.split(":")
                    board = sdata[0]
                    # sname never used. so comment out (avoid lint error)
                    # sname = sdata[1]
                    if board not in self.machine.frus:
                        self.machine.nums[board] = []
                    self.machine.frus.add(board)
                    if len(sdata) == 3:
                        self.machine.nums[board].append(sdata[2])

                zone = Zone(
                    data["pwm_output"],
                    expr,
                    inf,
                    self.transitional,
                    counter,
                    self.boost,
                    self.sensor_fail,
                    self.sensor_valid_check,
                    self.fail_sensor_type,
                    self.ssd_progressive_algorithm,
                )
                counter += 1
                self.zones.append(zone)

    def build_machine(self):
        self.machine = BMCMachine()

    def fsc_fan_action(self, fan, action):
        """
        Method invokes board actions for a fan.
        """
        if "dead" in action:
            board_fan_actions(fan, action="dead")
            board_fan_actions(fan, action="led_red")
        if "recover" in action:
            board_fan_actions(fan, action="recover")
            board_fan_actions(fan, action="led_blue")

    def fsc_host_action(self, action, cause):
        if "host_shutdown" in action:
            board_host_actions(action="host_shutdown", cause=cause)
            # board_fan_actions(fan, action='led_blue')

    def fsc_set_all_fan_led(self, color):
        for fan, _value in list(self.fans.items()):
            board_fan_actions(self.fans[fan], action=color)

    def fsc_safe_guards(self, sensors_tuples):
        """
        Method defines safe guards for fsc.
        Examples: Triggers board action when sensor temp read reaches limits
        configured in json
        """
        for fru in self.machine.frus:
            for sensor, tuple in list(sensors_tuples[fru].items()):
                if tuple.name in self.fsc_config["profiles"]:
                    if "read_limit" in self.fsc_config["profiles"][tuple.name]:
                        # If temperature read exceeds accpetable temperature reading
                        if ("valid" in self.fsc_config["profiles"][tuple.name]
                            ["read_limit"]):
                            valid_table = self.fsc_config["profiles"][
                                tuple.name]["read_limit"]["valid"]
                            valid_read_limit = valid_table["limit"]
                            valid_read_action = valid_table["action"]
                            valid_read_th = valid_table["threshold"]
                            if isinstance(self.sensors[tuple.name].source,
                                          FscSensorSourceUtil):
                                if tuple.value == None:
                                    self.sensors[
                                        tuple.
                                        name].source.read_source_fail_counter += 1
                                else:
                                    self.sensors[
                                        tuple.
                                        name].source.read_source_fail_counter = 0
                                    if tuple.value > valid_read_limit:
                                        reason = (sensor + "(v=" +
                                                  str(tuple.value) +
                                                  ") limit(t=" +
                                                  str(valid_read_limit) +
                                                  ") reached")
                                        self.fsc_host_action(
                                            action=valid_read_action,
                                            cause=reason)
                            else:
                                if tuple.value > valid_read_limit:
                                    if tuple.wrong_read_counter < valid_read_th:
                                        self.sensors[
                                            tuple.
                                            name].source.read_source_wrong_counter += 1
                                        Logger.warn(
                                            "inlet_temp v=%d, and counter=%d" %
                                            (tuple.value,
                                             tuple.wrong_read_counter))
                                        continue
                                    reason = (sensor + "(v=" +
                                              str(tuple.value) + ") limit(t=" +
                                              str(valid_read_limit) +
                                              ") reached")
                                    self.fsc_host_action(
                                        action=valid_read_action, cause=reason)
                                self.sensors[
                                    tuple.
                                    name].source.read_source_wrong_counter = 0

                        # If temperature read fails
                        if ("invalid" in self.fsc_config["profiles"][
                                tuple.name]["read_limit"]):
                            invalid_table = self.fsc_config["profiles"][
                                tuple.name]["read_limit"]["invalid"]
                            invalid_read_th = invalid_table["threshold"]
                            invalid_read_action = invalid_table["action"]
                            if isinstance(self.sensors[tuple.name].source,
                                          FscSensorSourceUtil):
                                read_fail_counter = self.sensors[
                                    tuple.name].source.read_source_fail_counter
                                if read_fail_counter >= invalid_read_th:
                                    reason = (sensor + "(value=" +
                                              str(tuple.value) +
                                              ") failed to read " +
                                              str(read_fail_counter) +
                                              " times")
                                    self.fsc_host_action(
                                        action=invalid_read_action,
                                        cause=reason)
                            else:
                                if tuple.read_fail_counter >= invalid_read_th:
                                    reason = (sensor + "(value=" +
                                              str(tuple.value) +
                                              ") failed to read " +
                                              str(tuple.read_fail_counter) +
                                              " times")
                                    self.fsc_host_action(
                                        action=invalid_read_action,
                                        cause=reason)

    def update_dead_fans(self, dead_fans):
        """
        Check for dead and recovered fans
        """
        last_dead_fans = dead_fans.copy()
        speeds = self.machine.read_fans(self.fans)
        print("\x1b[2J\x1b[H")
        sys.stdout.flush()

        for fan, rpms in list(speeds.items()):
            Logger.info("%s speed: %d RPM" % (fan.label, rpms))
            if rpms < self.fsc_config["min_rpm"]:
                dead_fans.add(fan)
                self.fsc_fan_action(fan, action="dead")
            else:
                dead_fans.discard(fan)

        recovered_fans = last_dead_fans - dead_fans
        newly_dead_fans = dead_fans - last_dead_fans
        if len(newly_dead_fans) > 0:
            if self.fanpower:
                Logger.warn("%d fans failed" % (len(dead_fans), ))
            else:
                Logger.crit("%d fans failed" % (len(dead_fans), ))
            for dead_fan in dead_fans:
                if self.fanpower:
                    Logger.warn("%s dead, %d RPM" %
                                (dead_fan.label, speeds[dead_fan]))
                else:
                    Logger.crit("%s dead, %d RPM" %
                                (dead_fan.label, speeds[dead_fan]))
                Logger.usbdbg("%s fail" % (dead_fan.label))
                fan_fail_record_path = FAN_FAIL_RECORD_DIR + "%s" % (
                    dead_fan.label)
                if not os.path.isfile(fan_fail_record_path):
                    fan_fail_record = open(fan_fail_record_path, "w")
                    fan_fail_record.close()
        for fan in recovered_fans:
            if self.fanpower:
                Logger.warn("%s has recovered" % (fan.label, ))
            else:
                Logger.crit("%s has recovered" % (fan.label, ))
            Logger.usbdbg("%s recovered" % (fan.label))
            self.fsc_fan_action(fan, action="recover")
            fan_fail_record_path = FAN_FAIL_RECORD_DIR + "%s" % (fan.label)
            if os.path.isfile(fan_fail_record_path):
                os.remove(fan_fail_record_path)
        return dead_fans

    def update_zones(self, dead_fans, time_difference):
        """
            TODO: Need to change logic here.

            # Platforms with chassis_intrusion mode enabled
            if chassis_intrusion:
                set the chassis_intrusion_boost_flag to 0
                and then do necessary checks to set flag to 1
                if chassis_intrusion_boost_flag:
                    run boost mode
                else:
                    run normal mode
            else
                # Platforms WITHOUT chassis_intrusion mode
                run normal mode

        """
        sensors_tuples = self.machine.read_sensors(self.sensors)
        self.fsc_safe_guards(sensors_tuples)
        for zone in self.zones:
            Logger.info("PWM: %s" % (json.dumps(zone.pwm_output)))
            mode = 0
            chassis_intrusion_boost_flag = 0
            if self.chassis_intrusion:
                self_tray_pull_out = board_callout(callout="chassis_intrusion")
                if self_tray_pull_out == 1:
                    chassis_intrusion_boost_flag = 1

            if chassis_intrusion_boost_flag == 0:
                pwmval = zone.run(sensors=sensors_tuples, dt=time_difference)
                mode = zone.get_set_fan_mode(mode, action="read")
            else:
                pwmval = self.boost
                mode = fan_mode["boost_mode"]

            if self.fan_fail:
                boost_record_path = RECORD_DIR + "fan_fail_boost"
                if self.boost_type == "progressive" and self.fan_dead_boost:
                    # Cases where we want to progressively bump PWMs
                    dead = len(dead_fans)
                    if dead > 0:
                        Logger.info(
                            "Progressive mode: Failed fans: %s" %
                            (", ".join([str(i.label) for i in dead_fans])))
                        for fan_count, rate in self.fan_dead_boost["data"]:
                            if dead <= fan_count:
                                pwmval = clamp(pwmval + (dead * rate), 0, 100)
                                mode = fan_mode["normal_mode"]
                                if os.path.isfile(boost_record_path):
                                    os.remove(boost_record_path)
                                break
                        else:
                            pwmval = self.boost
                            mode = fan_mode["boost_mode"]
                            if not os.path.isfile(boost_record_path):
                                fan_fail_boost_record = open(
                                    boost_record_path, "w")
                                fan_fail_boost_record.close()
                    else:
                        if os.path.isfile(boost_record_path):
                            os.remove(boost_record_path)
                else:
                    if dead_fans:
                        # If not progressive ,when there is 1 fan failed, boost all fans
                        Logger.info(
                            "Failed fans: %s" %
                            (", ".join([str(i.label) for i in dead_fans])))
                        pwmval = self.boost
                        mode = fan_mode["boost_mode"]
                        if not os.path.isfile(boost_record_path):
                            fan_fail_boost_record = open(
                                boost_record_path, "w")
                            fan_fail_boost_record.close()
                    else:
                        if os.path.isfile(boost_record_path):
                            os.remove(boost_record_path)
                    if self.fan_dead_boost:
                        # If all the fans failed take action after a few cycles
                        if len(dead_fans) == len(self.fans):
                            self.all_fan_fail_counter = self.all_fan_fail_counter + 1
                            Logger.warn(
                                "Currently all fans failed for {} cycles".
                                format(self.all_fan_fail_counter))
                            if (self.fan_dead_boost["threshold"]
                                    and self.fan_dead_boost["action"]):
                                if (self.all_fan_fail_counter >=
                                        self.fan_dead_boost["threshold"]):
                                    self.fsc_host_action(
                                        action=self.fan_dead_boost["action"],
                                        cause="All fans are bad for more than "
                                        +
                                        str(self.fan_dead_boost["threshold"]) +
                                        " cycles",
                                    )
                        else:
                            # If atleast 1 fan is working reset the counter
                            self.all_fan_fail_counter = 0

            if abs(zone.last_pwm - pwmval) > self.ramp_rate:
                if pwmval < zone.last_pwm:
                    pwmval = zone.last_pwm - self.ramp_rate
                else:
                    pwmval = zone.last_pwm + self.ramp_rate
            zone.last_pwm = pwmval

            if hasattr(zone.pwm_output, "__iter__"):
                for output in zone.pwm_output:
                    self.machine.set_pwm(self.fans.get(str(output)), pwmval)
            else:
                self.machine.set_pwm(self.fans[zone.pwm_output], pwmval)

            zone.get_set_fan_mode(mode, action="write")

    def builder(self):
        """
        Method to extract from json and build all internal data staructures
        """
        # Build a bmc machine object - read/write sensors
        self.build_machine()
        # Extract everything from json
        self.get_config_params()
        self.build_fans()
        self.build_profiles()
        Logger.info("Available profiles: " +
                    ", ".join(list(self.profiles.keys())))
        self.build_zones()
        Logger.info("Read %d zones" % (len(self.zones)))
        Logger.info("Including sensors from: " + ", ".join(self.machine.frus))

    def get_fan_power_status(self):
        """
        Method invokes board action to determine fan power status.
        If not applicable returns True.
        """
        if board_callout(callout="read_power"):
            return True
        return False

    def fail_record_dir(self):
        """
        Create directory to store which sensors and fans failed
        """

        if not os.path.isdir(RECORD_DIR):
            os.mkdir(RECORD_DIR)

        if not os.path.isdir(SENSOR_FAIL_RECORD_DIR):
            os.mkdir(SENSOR_FAIL_RECORD_DIR)

        if not os.path.isdir(FAN_FAIL_RECORD_DIR):
            os.mkdir(FAN_FAIL_RECORD_DIR)

    def run(self):
        """
        Main FSCD method that builds from the fscd config and runs
        """

        # Get everything from json and build profiles, fans, zones
        self.builder()

        self.fail_record_dir()

        self.machine.set_all_pwm(self.fans, self.transitional)
        self.fsc_set_all_fan_led(color="led_blue")

        mode = fan_mode["trans_mode"]
        self.zones[0].get_set_fan_mode(mode, action="write")

        last = time.time()
        dead_fans = set()

        if self.fanpower:
            time.sleep(30)

        while True:
            if self.watchdog:
                kick_watchdog()

            time.sleep(self.interval)

            if self.fanpower:
                if not self.get_fan_power_status():
                    self.fan_recovery_pending = True
                    continue
            if self.fan_fail:
                if self.fan_recovery_pending and self.fan_recovery_time != None:
                    # Accelerating, wait for a while
                    time.sleep(self.fan_recovery_time)
                    self.fan_recovery_pending = False
                # Get dead fans for determining speed
                dead_fans = self.update_dead_fans(dead_fans)

            now = time.time()
            time_difference = now - last
            last = now

            # Check sensors and update zones
            self.update_zones(dead_fans, time_difference)