def step_impl(context): for row in context.table.rows: amb_props = {context.table.headings[i]: v for i, v in enumerate(row)} for num_prop in ["degrees", "rate", "pause_at"]: amb_props[num_prop] = int(amb_props[num_prop]) ISystemEnvironment.set_ambient_props(amb_props)
def step_impl(context, temp): old_ambient = ISystemEnvironment.get_ambient() if not math.isclose(temp, old_ambient): ISystemEnvironment.set_ambient(temp) context.engine.handle_ambient_update(old_ambient, temp) context.tracker.wait_thermal_queue()
def step_impl(context, new_volt): old_volt = ISystemEnvironment.get_voltage() ISystemEnvironment.set_voltage(new_volt) context.engine.handle_voltage_update(old_voltage=old_volt, new_voltage=new_volt) # wait for completion of event loop if new_volt != old_volt: logging.info(context.tracker.wait_load_queue())
def _parents_available(self): """Indicates whether a state action can be performed; checks if parent nodes are up & running and all OIDs indicate 'on' status Returns: bool: True if parents are available """ asset_keys, oid_keys = GraphReference.get_parent_keys( self._graph_ref.get_session(), self._asset_key ) # if wall-powered, check the mains if not asset_keys and not ISystemEnvironment.power_source_available(): return False state_managers = list(map(self.get_state_manager_by_key, asset_keys)) min_volt_value = self.min_voltage_prop() # check if power is present enough_voltage = len( list(filter(lambda sm: sm.output_voltage > min_volt_value, state_managers)) ) parent_assets_up = len(list(filter(lambda sm: sm.status, state_managers))) oid_clause = ( lambda rvalue, rkey: rvalue.split(b"|")[1].decode() == oid_keys[rkey]["switchOff"] ) oids_on = self._check_parents(oid_keys.keys(), oid_clause) return (parent_assets_up and enough_voltage and oids_on) or (not asset_keys)
def on_input_voltage_down(self, event, *args, **kwargs): """React to input voltage drop; UPS can transfer to battery if in-voltage is low or it can transfer back to input power if volt dropped from too high to acceptable; """ asset_event = event.get_next_power_event(self) asset_event.out_volt.old = self.state.output_voltage battery_level = self.state.battery_level should_transfer, reason = self.state.process_voltage(event.in_volt.new) upstream_load_change = self._get_ups_load_update(should_transfer) if upstream_load_change: asset_event.load.old, asset_event.load.new = upstream_load_change high_line_t_reason = self.state.InputLineFailCause.highLineVoltage # voltage is low, transfer to battery if should_transfer and self.state.battery_level: self._launch_battery_drain(reason) asset_event.out_volt.new = ISystemEnvironment.wallpower_volt_standard( ) # voltage was too high but is okay now elif (self.state.transfer_reason == high_line_t_reason and reason != high_line_t_reason): self._launch_battery_charge(power_up_on_charge=(not battery_level)) else: asset_event.calc_load_from_volt() self._update_load(self.state.load + asset_event.load.difference) return asset_event
def _cpu_impact(self): """Keep updating *this sensor based on cpu load changes This function waits for the thermal event switch and exits when the connection between this sensor & cpu load is removed; """ # avoid circular imports with the server from enginecore.state.api import IBMCServerStateManager with self._graph_ref.get_session() as session: asset_info = GraphReference.get_asset_and_components( session, self._server_key) server_sm = IBMCServerStateManager(asset_info) cpu_impact_degrees_1 = 0 cpu_impact_degrees_2 = 0 while True: self._s_thermal_event.wait() rel_details = GraphReference.get_cpu_thermal_rel( session, self._server_key, self.name) # relationship was deleted if not rel_details: return with self._s_file_locks.get_lock(self.name): current_cpu_load = server_sm.cpu_load # calculate cpu impact based on the model cpu_impact_degrees_2 = self._calc_approx_value( json.loads(rel_details["model"]), current_cpu_load) new_calc_value = (int(self.sensor_value) + cpu_impact_degrees_2 - cpu_impact_degrees_1) # meaning update is needed if cpu_impact_degrees_1 != cpu_impact_degrees_2: ambient = ISystemEnvironment.get_ambient() self.sensor_value = (new_calc_value if new_calc_value > ambient else int(ambient)) logger.debug( "Thermal impact of CPU load at (%s%%) updated: (%s°)->(%s°)", current_cpu_load, cpu_impact_degrees_1, cpu_impact_degrees_2, ) cpu_impact_degrees_1 = cpu_impact_degrees_2 time.sleep(5)
def on_input_voltage_up(self, event, *args, **kwargs): """React to input voltage spike; UPS can transfer to battery if in-voltage is too high; It can also transfer back to input power if voltage level is within the acceptable threshold; """ asset_event = event.get_next_power_event(self) asset_event.out_volt.old = self.state.output_voltage asset_event.calc_load_from_volt() # process voltage, see if tranfer to battery is needed should_transfer, reason = self.state.process_voltage(event.in_volt.new) upstream_load_change = self._get_ups_load_update(should_transfer) if upstream_load_change: asset_event.load.old, asset_event.load.new = upstream_load_change # transfer back to input power if ups was running on battery if not should_transfer and self.state.on_battery: battery_level = self.state.battery_level self._launch_battery_charge(power_up_on_charge=(not battery_level)) if battery_level: asset_event.state.new = self.state.power_up() # if already on battery (& should stay so), stop voltage propagation elif self.state.on_battery: asset_event.out_volt.new = asset_event.out_volt.old # voltage is too high, transfer to battery if should_transfer and reason == self.state.InputLineFailCause.highLineVoltage: self._launch_battery_drain(reason) asset_event.out_volt.new = ISystemEnvironment.wallpower_volt_standard( ) if not math.isclose(asset_event.load.new, 0) and not math.isclose( asset_event.load.new, self.state.load): self._update_load(self.state.load + asset_event.load.difference) return asset_event
def _target_sensor(self, target, event): """Keep updating the target sensor based on the relationship between this sensor and the target; This function waits for the thermal event switch and exits when the connection between source & target is removed; Args: target(str): name of the target sensor event(str): name of the event that enables thermal impact """ with self._graph_ref.get_session() as session: while True: self._s_thermal_event.wait() rel_details = GraphReference.get_sensor_thermal_rel( session, self._server_key, relationship={ "source": self.name, "target": { "attribute": "name", "value": '"{}"'.format(target) }, "event": event, }, ) # shut down thread upon relationship removal if not rel_details: del self._th_sensor_t[target][event] return rel = rel_details["rel"] causes_heating = rel["action"] == "increase" source_sensor_status = (operator.eq if rel["event"] == "down" else operator.ne) bound_op = operator.lt if causes_heating else operator.gt arith_op = operator.add if causes_heating else operator.sub # if model is specified -> use the runtime mappings if "model" in rel and rel["model"]: calc_new_sv = arith_op arith_op = lambda sv, _: calc_new_sv( sv, self._calc_approx_value(json.loads(rel["model"]), int(self.sensor_value) * 10), ) source_sensor_status = operator.ne # verify that sensor value doesn't go below room temp if causes_heating or rel[ "pauseAt"] > ISystemEnvironment.get_ambient(): pause_at = rel["pauseAt"] else: pause_at = ISystemEnvironment.get_ambient() # update target sensor value with self._s_file_locks.get_lock(target), open( os.path.join(self._s_dir, target), "r+") as sf_handler: current_value = int(sf_handler.read()) change_by = (int(rel["degrees"]) if "degrees" in rel and rel["degrees"] else 0) new_sensor_value = arith_op(current_value, change_by) # Source sensor status activated thermal impact if source_sensor_status(int(self.sensor_value), 0): needs_update = bound_op(new_sensor_value, pause_at) if not needs_update and bound_op( current_value, pause_at): needs_update = True new_sensor_value = int(pause_at) if needs_update: logger.info( "Current sensor value (%s°) will be updated to %s°", current_value, int(new_sensor_value), ) sf_handler.seek(0) sf_handler.truncate() sf_handler.write(str(new_sensor_value)) time.sleep(int(rel["rate"]))
def _target_storage(self, controller, target, hd_type, event): with self._graph_ref.get_session() as session: while True: self._s_thermal_event.wait() # target if hd_type == HDComponents.CacheVault: target_attr = "serialNumber" target_value = '"{}"'.format(target) elif hd_type == HDComponents.PhysicalDrive: target_attr = "DID" target_value = target else: raise ValueError("Unknown hardware component!") rel_details = GraphReference.get_sensor_thermal_rel( session, self._server_key, relationship={ "source": self._s_name, "target": { "attribute": target_attr, "value": target_value }, "event": event, }, ) if not rel_details: del self._th_storage_t[target][event] return rel = rel_details["rel"] causes_heating = rel["action"] == "increase" source_sensor_status = (operator.eq if rel["event"] == "down" else operator.ne) # if model is specified -> use the runtime mappings if "model" in rel and rel["model"]: rel["degrees"] = self._calc_approx_value( json.loads(rel["model"]), int(self.sensor_value) * 10) source_sensor_status = operator.ne if source_sensor_status(int(self.sensor_value), 0): updated, new_temp = GraphReference.add_to_hd_component_temperature( session, target={ "server_key": self._server_key, "controller": controller, "attribute": target_attr, "value": target_value, "hd_type": hd_type.name, }, temp_change=rel["degrees"] * 1 if causes_heating else -1, limit={ "lower": ISystemEnvironment.get_ambient(), "upper": rel["pauseAt"] if causes_heating else None, }, ) if updated: logger.info("temperature sensor was updated to %s°", new_temp) time.sleep(rel["rate"])
def step_impl(_, temp): ambient = ISystemEnvironment.get_ambient() assert_that(ambient, equal_to(temp))
def step_impl(_, temp, delay): time.sleep(delay) ambient = ISystemEnvironment.get_ambient() assert_that(ambient, equal_to(temp))
def step_impl(context): if not ISystemEnvironment.mains_status(): ISystemEnvironment.power_restore() context.engine.handle_voltage_update(old_voltage=0, new_voltage=120) logging.info(context.tracker.wait_load_queue())