def main(): try: check_sanity() except Exception as e: MyUtils.log_error("{0} {1}".format(str(e), str(traceback.format_exc())), debug=True)
def main(): try: rebalancer = ReBalancer() rebalancer.rebalance() except Exception as e: MyUtils.log_error("{0} {1}".format(str(e), str(traceback.format_exc())), debug=True)
def main(): try: energy_manager = EnergyManager() energy_manager.process() except Exception as e: MyUtils.log_error("{0} {1}".format(str(e), str(traceback.format_exc())), debug=True)
def check_unstable_configuration(): try: MyUtils.log_info("Checking for invalid configuration", debug) service = MyUtils.get_service(db_handler, "guardian") guardian_configuration = service["config"] event_timeout = MyUtils.get_config_value( guardian_configuration, src.Guardian.Guardian.CONFIG_DEFAULT_VALUES, "EVENT_TIMEOUT") window_timelapse = MyUtils.get_config_value( guardian_configuration, src.Guardian.Guardian.CONFIG_DEFAULT_VALUES, "WINDOW_TIMELAPSE") rules = db_handler.get_rules() for rule in rules: if "generates" in rule and rule[ "generates"] == "requests" and rule["active"]: event_count = int(rule["events_to_remove"]) event_window_time_to_trigger = window_timelapse * (event_count) # Leave a slight buffer time to account for window times skewness if event_window_time_to_trigger > event_timeout: MyUtils.log_error( "Rule: '{0}' could never be activated -> guardian event timeout: '{1}', number of events" .format(rule["name"], str(event_timeout)) + " required to trigger the rule: '{0}' and guardian polling time: '{1}'" .format(str(event_count), str(window_timelapse)), debug) except Exception as e: MyUtils.log_error( "Error doing configuration check up: {0} {1}".format( str(e), str(traceback.format_exc())), debug)
def app_can_be_rebalanced(application, rebalancing_level, couchdb_handler): try: data = { # "energy": # {"structure": # {"energy": # {"usage": application["resources"]["energy"]["usage"], # "max": application["resources"]["energy"]["max"]}}}, "cpu": { "structure": { "cpu": { "usage": application["resources"]["cpu"]["usage"], "min": application["resources"]["cpu"]["min"], "current": application["resources"]["cpu"]["current"] } } } } except KeyError: return False if rebalancing_level != "container" and rebalancing_level != "application": MyUtils.log_error( "Invalid app rebalancing policy '{0}'".format(rebalancing_level), debug=True) return False # rule_low_usage = couchdb_handler.get_rule("energy_exceeded_upper") # if jsonLogic(rule_low_usage["rule"], data): # # Application is overusing energy # if rebalancing_level == "container": # # Let the Guardian or the dynamic energy balancing deal with it # return False # elif rebalancing_level == "application": # # It may be a receiver for dynamic energy rebalancing # return True # # rule_high_usage = couchdb_handler.get_rule("energy_dropped_lower") # if jsonLogic(rule_high_usage["rule"], data): # # Application is underusing energy # if rebalancing_level == "container": # # Let the Guardian or the dynamic energy balancing deal with it # return False # # It may be a donor for dynamic energy rebalancing # elif rebalancing_level == "application": # return True # The application is in the middle area of energy utilization if rebalancing_level == "container": # Perform an internal balancing through container cpu limit rebalancing return True elif rebalancing_level == "application": # It may need internal container rebalancing return False
def rebalance(self, ): logging.basicConfig(filename=SERVICE_NAME + '.log', level=logging.INFO) while True: # Get service info service = MyUtils.get_service(self.couchdb_handler, SERVICE_NAME) # Heartbeat MyUtils.beat(self.couchdb_handler, SERVICE_NAME) # CONFIG self.config = service["config"] self.debug = MyUtils.get_config_value(self.config, CONFIG_DEFAULT_VALUES, "DEBUG") window_difference = MyUtils.get_config_value( self.config, CONFIG_DEFAULT_VALUES, "WINDOW_TIMELAPSE") #self.userReBalancer.rebalance_users(self.config) #self.applicationReBalancer.rebalance_applications(self.config) self.containerRebalancer.rebalance_containers(self.config) MyUtils.log_info( "Epoch processed at {0}".format(MyUtils.get_time_now_string()), self.debug) time.sleep(window_difference)
def __dynamic_app_rebalancing(self, applications): donors, receivers = list(), list() for app in applications: if self.__app_energy_can_be_rebalanced(app): diff = app["resources"]["energy"]["max"] - app["resources"][ "energy"]["usage"] if diff > self.__ENERGY_DIFF_PERCENTAGE * app["resources"][ "energy"]["max"]: donors.append(app) else: receivers.append(app) if not receivers: MyUtils.log_info("No app to give energy shares", self.__debug) return else: # Order the apps from lower to upper energy limit apps_to_receive = sorted( receivers, key=lambda c: c["resources"]["energy"]["max"]) shuffling_tuples = list() for app in donors: diff = app["resources"]["energy"]["max"] - app["resources"][ "energy"]["usage"] stolen_amount = self.__ENERGY_STOLEN_PERCENTAGE * diff shuffling_tuples.append((app, stolen_amount)) shuffling_tuples = sorted(shuffling_tuples, key=lambda c: c[1]) # Give the resources to the bottlenecked applications for receiver in apps_to_receive: if shuffling_tuples: donor, amount_to_scale = shuffling_tuples.pop(0) else: MyUtils.log_info( "No more donors, app {0} left out".format( receiver["name"]), self.__debug) continue donor["resources"]["energy"]["max"] -= amount_to_scale receiver["resources"]["energy"]["max"] += amount_to_scale MyUtils.update_structure(donor, self.__couchdb_handler, self.__debug) MyUtils.update_structure(receiver, self.__couchdb_handler, self.__debug) MyUtils.log_info( "Energy swap between {0}(donor) and {1}(receiver)".format( donor["name"], receiver["name"]), self.__debug)
def check_sanity(): logging.basicConfig(filename=SERVICE_NAME + '.log', level=logging.INFO) global debug while True: # Get service info service = MyUtils.get_service(db_handler, SERVICE_NAME) # CONFIG config = service["config"] debug = MyUtils.get_config_value(config, CONFIG_DEFAULT_VALUES, "DEBUG") delay = MyUtils.get_config_value(config, CONFIG_DEFAULT_VALUES, "DELAY") compact_databases() check_unstable_configuration() # check_core_mapping() MyUtils.log_info("Sanity checked", debug) time_waited = 0 heartbeat_delay = 10 # seconds while time_waited < delay: # Heartbeat MyUtils.beat(db_handler, SERVICE_NAME) time.sleep(heartbeat_delay) time_waited += heartbeat_delay
def beat(self): # Serverless Containers from src.StateDatabase import couchdb as couchDB from src.MyUtils import MyUtils as MyUtils self.logger.info("Starting heartbeat of " + self.SERVICE_NAME) db_handler = couchDB.CouchDBServer() while True: try: MyUtils.beat(db_handler, self.SERVICE_NAME) time.sleep(10) except ValueError: # Service not found: # - maybe it doesn't exist at all, register it # - it may have been deleted while the daemon was running, re-register it register_service(db_handler, self.SERVICE_NAME) except requests.ConnectionError: # Error connecting to the Couchdb database, ignore as it may be temporary pass
def disable_scaler(scaler_service): scaler_service["config"]["ACTIVE"] = False get_db().update_service(scaler_service) # Wait a little bit, half the polling time of the Scaler polling_freq = MyUtils.get_config_value( scaler_service["config"], src.Scaler.Scaler.CONFIG_DEFAULT_VALUES, "POLLING_FREQUENCY") time.sleep(int(polling_freq))
def __inter_user_rebalancing(self, users): donors, receivers = list(), list() for user in users: diff = user["energy"]["max"] - user["energy"]["used"] if diff > self.__ENERGY_DIFF_PERCENTAGE * user["energy"]["max"]: donors.append(user) else: receivers.append(user) if not receivers: MyUtils.log_info("No user to give energy shares", self.__debug) return else: # Order the apps from lower to upper energy limit users_to_receive = sorted(receivers, key=lambda c: c["energy"]["max"]) shuffling_tuples = list() for user in donors: diff = user["energy"]["max"] - user["energy"]["used"] stolen_amount = self.__ENERGY_STOLEN_PERCENTAGE * diff shuffling_tuples.append((user, stolen_amount)) shuffling_tuples = sorted(shuffling_tuples, key=lambda c: c[1]) # Give the resources to the other users for receiver in users_to_receive: if shuffling_tuples: donor, amount_to_scale = shuffling_tuples.pop(0) else: MyUtils.log_info( "No more donors, user {0} left out".format( receiver["name"]), self.__debug) continue donor["energy"]["max"] -= amount_to_scale receiver["energy"]["max"] += amount_to_scale MyUtils.update_user(donor, self.__couchdb_handler, self.__debug) MyUtils.update_user(receiver, self.__couchdb_handler, self.__debug) MyUtils.log_info( "Energy swap between {0}(donor) and {1}(receiver)".format( donor["name"], receiver["name"]), self.__debug)
def __static_app_rebalancing(self, applications, max_energy): # Add up all the shares total_shares = sum( [app["resources"]["energy"]["shares"] for app in applications]) # For each application calculate the energy according to its shares for app in applications: percentage = app["resources"]["energy"]["shares"] / total_shares limit_energy = int(max_energy * percentage) current_energy = app["resources"]["energy"]["max"] # If the database record and the new limit are not the same, update if current_energy != limit_energy: app["resources"]["energy"]["max"] = limit_energy self.__couchdb_handler.update_structure(app) MyUtils.log_info( "Updated energy limit of app {0}".format(app["name"]), self.__debug)
def update_user_used_energy(self, applications, users): for user in users: total_user = {"cpu": 0, "energy": 0} total_user_current_cpu = 0 user_apps = get_user_apps(applications, user) for app in user_apps: for resource in ["energy", "cpu"]: if "usage" in app["resources"][resource] and app[ "resources"][resource]["usage"]: total_user[resource] += app["resources"][resource][ "usage"] else: MyUtils.log_error( "Application {0} of user {1} has no used {2} field or value" .format(app["name"], user["name"], resource), self.__debug) if "current" in app["resources"]["cpu"] and app["resources"][ "cpu"]["usage"]: total_user_current_cpu += app["resources"][resource][ "current"] else: MyUtils.log_error( "Application {0} of user {1} has no current cpu field or value" .format(app["name"], user["name"]), self.__debug) user["energy"]["used"] = total_user["energy"] user["cpu"]["usage"] = total_user["cpu"] user["cpu"]["current"] = total_user_current_cpu self.__couchdb_handler.update_user(user) MyUtils.log_info( "Updated energy consumed by user {0}".format(user["name"]), self.__debug)
def desubscribe_container(structure_name): structure = retrieve_structure(structure_name) cont_name = structure["name"] # Look for any application that hosts this container, and remove it from the list apps = get_db().get_structures(subtype="application") for app in apps: if cont_name in app["containers"]: desubscribe_container_from_app(structure_name, app["name"]) # Disable the Scaler as we will modify the core mapping of a host scaler_service = get_db().get_service(src.Scaler.Scaler.SERVICE_NAME) previous_state = MyUtils.get_config_value( scaler_service["config"], src.Scaler.Scaler.CONFIG_DEFAULT_VALUES, "ACTIVE") if previous_state: disable_scaler(scaler_service) # Free resources # CPU # Get the core map of the container's host and free the allocated shares for this container cont_host = structure["host"] host = get_db().get_structure(cont_host) core_map = host["resources"]["cpu"]["core_usage_mapping"] freed_shares = 0 for core in core_map: if cont_name in core_map[core]: core_shares = core_map[core][cont_name] freed_shares += core_shares core_map[core][cont_name] = 0 core_map[core]["free"] += core_shares host["resources"]["cpu"]["core_usage_mapping"] = core_map host["resources"]["cpu"]["free"] += freed_shares # MEM host["resources"]["mem"]["free"] += structure["resources"]["mem"][ "current"] get_db().update_structure(host) # Delete the document for this structure get_db().delete_structure(structure) # Restore the previous state of the Scaler service restore_scaler_state(scaler_service, previous_state) return jsonify(201)
def compact_databases(): try: compacted_dbs = list() for db in DATABASES: success = db_handler.compact_database(db) if success: compacted_dbs.append(db) else: MyUtils.log_warning( "Database {0} could not be compacted".format(db), debug) MyUtils.log_info( "Databases {0} have been compacted".format(str(compacted_dbs)), debug) except Exception as e: MyUtils.log_error( "Error doing database compaction: {0} {1}".format( str(e), str(traceback.format_exc())), debug)
def rebalance_users(self, config): self.__config = config self.__debug = MyUtils.get_config_value(self.__config, CONFIG_DEFAULT_VALUES, "DEBUG") rebalance_users = MyUtils.get_config_value(self.__config, CONFIG_DEFAULT_VALUES, "REBALANCE_USERS") self.__ENERGY_DIFF_PERCENTAGE = MyUtils.get_config_value( self.__config, CONFIG_DEFAULT_VALUES, "ENERGY_DIFF_PERCENTAGE") self.__ENERGY_STOLEN_PERCENTAGE = MyUtils.get_config_value( self.__config, CONFIG_DEFAULT_VALUES, "ENERGY_STOLEN_PERCENTAGE") MyUtils.log_info( "ENERGY_DIFF_PERCENTAGE -> {0}".format( self.__ENERGY_DIFF_PERCENTAGE), self.__debug) MyUtils.log_info( "ENERGY_STOLEN_PERCENTAGE -> {0}".format( self.__ENERGY_STOLEN_PERCENTAGE), self.__debug) MyUtils.log_info("_______________", self.__debug) MyUtils.log_info("Performing USER ENERGY Balancing", self.__debug) try: #applications = MyUtils.get_structures(self.__couchdb_handler, self.__debug, subtype="application") users = self.__couchdb_handler.get_users() except requests.exceptions.HTTPError as e: MyUtils.log_error("Couldn't get users and/or applications", self.__debug) MyUtils.log_error(str(e), self.__debug) return #self.update_user_used_energy(applications, users) if rebalance_users: self.__inter_user_rebalancing(users) MyUtils.log_info("_______________\n", self.__debug)
def register_service(db_handler, service_name): service = dict(name=service_name, heartbeat="", heartbeat_human="", type="service") MyUtils.register_service(db_handler, service)
def subscribe_container(structure_name): req_cont = request.json["container"] req_limits = request.json["limits"] node_scaler_session = requests.Session() # Check that all the needed data is present on the requestes container container = {} for key in [ "name", "host_rescaler_ip", "host_rescaler_port", "host", "guard", "subtype" ]: if key not in req_cont: return abort(400, {"message": "Missing key '{0}'".format(key)}) else: container[key] = req_cont[key] # Check that all the needed data for resources is present on the requested container container["resources"] = {} if "resources" not in req_cont: return abort(400, {"message": "Missing resource information"}) elif "cpu" not in req_cont["resources"] or "mem" not in req_cont[ "resources"]: return abort(400, {"message": "Missing cpu or mem resource information"}) else: container["resources"] = {"cpu": {}, "mem": {}} for key in ["max", "min", "current", "guard"]: if key not in req_cont["resources"]["cpu"] or key not in req_cont[ "resources"]["mem"]: return abort( 400, { "message": "Missing key '{0}' for cpu or mem resource".format(key) }) else: container["resources"]["cpu"][key] = req_cont["resources"][ "cpu"][key] container["resources"]["mem"][key] = req_cont["resources"][ "mem"][key] # Check that the endpoint requested container name matches with the one in the request if container["name"] != structure_name: return abort(400, {"message": "Name mismatch".format(key)}) # Check if the container already exists try: cont = get_db().get_structure(structure_name) if cont: return abort(400, { "message": "Container with this name already exists".format(key) }) except ValueError: pass # Check that its supposed host exists and that it reports this container try: get_db().get_structure(container["host"]) except ValueError: return abort(400, {"message": "Container host does not exist".format(key)}) host_containers = get_host_containers(container["host_rescaler_ip"], container["host_rescaler_port"], node_scaler_session, True) if container["name"] not in host_containers: return abort( 400, { "message": "Container host does not report any container named '{0}'". format(container["name"]) }) container["type"] = "structure" # Check that all the needed data for resources is present on the requested container LIMITS limits = {} if "resources" not in req_limits: return abort( 400, {"message": "Missing resource information for the limits"}) elif "cpu" not in req_limits["resources"] or "mem" not in req_limits[ "resources"]: return abort(400, { "message": "Missing cpu or mem resource information for the limits" }) else: limits["resources"] = {"cpu": {}, "mem": {}} for key in ["boundary"]: if key not in req_limits["resources"][ "cpu"] or key not in req_limits["resources"]["mem"]: return abort( 400, { "message": "Missing key '{0}' for cpu or mem resource".format(key) }) else: limits["resources"]["cpu"][key] = req_limits["resources"][ "cpu"][key] limits["resources"]["mem"][key] = req_limits["resources"][ "mem"][key] limits["type"] = 'limit' limits["name"] = container["name"] #### ALL looks good up to this point, proceed # Disable the Scaler as we will modify the core mapping of a host scaler_service = get_db().get_service(src.Scaler.Scaler.SERVICE_NAME) previous_state = MyUtils.get_config_value( scaler_service["config"], src.Scaler.Scaler.CONFIG_DEFAULT_VALUES, "ACTIVE") if previous_state: disable_scaler(scaler_service) # Get the host info cont_host = container["host"] cont_name = container["name"] host = get_db().get_structure(cont_host) # CPU # Look for resource shares on the container's host needed_shares = container["resources"]["cpu"]["current"] if host["resources"]["cpu"]["free"] < needed_shares: return abort( 400, {"message": "Host does not have enough shares".format(key)}) core_map = host["resources"]["cpu"]["core_usage_mapping"] host_max_cores = int(host["resources"]["cpu"]["max"] / 100) host_cpu_list = [str(i) for i in range(host_max_cores)] pending_shares = needed_shares used_cores = list() # Try to satisfy the request by looking and adding a single core for core in host_cpu_list: if core_map[core]["free"] >= pending_shares: core_map[core]["free"] -= pending_shares core_map[core][cont_name] += pending_shares pending_shares = 0 used_cores.append(core) break # Finally, if unsuccessful, add as many cores as necessary, starting with the ones with the largest free shares to avoid too much spread if pending_shares > 0: l = list() for core in host_cpu_list: l.append((core, core_map[core]["free"])) l.sort(key=lambda tup: tup[1], reverse=True) less_used_cores = [i[0] for i in l] for core in less_used_cores: # If this core has free shares if core_map[core]["free"] > 0 and pending_shares > 0: if cont_name not in core_map[core]: core_map[core][cont_name] = 0 # If it has more free shares than needed, assign them and finish if core_map[core]["free"] >= pending_shares: core_map[core]["free"] -= pending_shares core_map[core][cont_name] += pending_shares pending_shares = 0 used_cores.append(core) break else: # Otherwise, assign as many as possible and continue core_map[core][cont_name] += core_map[core]["free"] pending_shares -= core_map[core]["free"] core_map[core]["free"] = 0 used_cores.append(core) if pending_shares > 0: return abort( 400, { "message": "Container host does not have enough free CPU shares as requested" }) host["resources"]["cpu"]["core_usage_mapping"] = core_map host["resources"]["cpu"]["free"] -= needed_shares # MEM needed_memory = container["resources"]["mem"]["current"] host_memory = host["resources"]["mem"]["free"] if needed_memory > host_memory: return abort(400, { "message": "Container host does not have enough free memory requested" }) host["resources"]["mem"]["free"] -= needed_memory resource_dict = { "cpu": { "cpu_num": ",".join(used_cores), "cpu_allowance_limit": needed_shares }, "mem": { "mem_limit": needed_memory } } Scaler.set_container_resources(node_scaler_session, container, resource_dict, True) get_db().add_structure(container) get_db().add_limit(limits) get_db().update_structure(host) # Restore the previous state of the Scaler service restore_scaler_state(scaler_service, previous_state) return jsonify(201)
def process(self, ): logging.basicConfig(filename=SERVICE_NAME + '.log', level=logging.INFO) while True: # Get service info service = MyUtils.get_service(self.couchdb_handler, SERVICE_NAME) config = service["config"] self.debug = MyUtils.get_config_value(config, CONFIG_DEFAULT_VALUES, "DEBUG") polling_frequency = MyUtils.get_config_value( config, CONFIG_DEFAULT_VALUES, "POLLING_FREQUENCY") # Heartbeat MyUtils.beat(self.couchdb_handler, SERVICE_NAME) # Remote database operation users = None structures = None try: structures = MyUtils.get_structures(self.couchdb_handler, self.debug, subtype="application") users = self.couchdb_handler.get_users() except requests.exceptions.HTTPError as e: MyUtils.log_error("Couldn't get users", self.debug) MyUtils.log_error(str(e), self.debug) for user in users: MyUtils.log_info("Processing user {0}".format(user["name"]), self.debug) max_energy = user["energy"]["max"] # Add up all the shares total_shares = 0 for structure in structures: if structure["name"] in user["clusters"]: total_shares += structure["resources"]["energy"][ "shares"] total_user_energy = 0 for structure in structures: if structure["name"] in user["clusters"]: percentage = structure["resources"]["energy"][ "shares"] / total_shares limit_energy = max_energy * percentage current_energy = structure["resources"]["energy"][ "max"] if current_energy != limit_energy: structure["resources"]["energy"][ "max"] = limit_energy self.couchdb_handler.update_structure(structure) MyUtils.log_info( "Processed cluster {0} with an energy share of {1}% and {2} Watts" .format(structure["name"], str("%.2f" % (100 * percentage)), limit_energy), self.debug) if "used" in structure["resources"]["energy"]: total_user_energy += structure["resources"][ "energy"]["used"] MyUtils.log_info( "Updated energy consumed by user {0}".format(user["name"]), self.debug) user["energy"]["used"] = total_user_energy self.couchdb_handler.update_user(user) MyUtils.log_info( "Epoch processed at {0}".format(MyUtils.get_time_now_string()), self.debug) time.sleep(polling_frequency)
def test_match_rules_and_events(self): def get_valid_state(): st = { "guard": True, "guard_policy": "serverless", "host": "c14-13", "host_rescaler_ip": "c14-13", "host_rescaler_port": "8000", "name": "node0", "resources": { "cpu": { "current": 140, "guard": True, "max": 200, "min": 50 }, "energy": { "guard": False, "max": 20, "min": 0, "usage": 2.34 }, "mem": { "current": 3072, "guard": True, "max": 10240, "min": 512 } }, "subtype": "container", "type": "structure" } lim = {"cpu": {"upper": 120, "lower": 80, "boundary": 20}, "mem": {"upper": 2048, "lower": 1024, "boundary": 1024}, "energy": {"upper": 20, "lower": 10, "boundary": 5}, } us = {"structure.cpu.usage": 100, "structure.mem.usage": 1536, "structure.energy.usage": 15} return st, lim, us event_rules = [cpu_exceeded_upper, cpu_dropped_lower, mem_exceeded_upper, mem_dropped_lower, energy_exceeded_upper, energy_dropped_lower] rescaling_rules = [CpuRescaleUp, CpuRescaleDown, MemRescaleUp, MemRescaleDown, EnergyRescaleUp, EnergyRescaleDown] for rule in event_rules + rescaling_rules: rule["active"] = True self.guardian.guardable_resources = ["cpu", "mem"] for tuple in [("cpu", CpuRescaleDown, -50), ("mem", MemRescaleDown, -50), ("cpu", CpuRescaleUp, 100), ("mem", MemRescaleUp, 2034)]: resource, rescale_rule, amount = tuple structure, limits, usages = get_valid_state() usages["structure." + resource + ".usage"] = limits[resource]["lower"] + amount events = list() num_needed_events = rescale_rule["rule"]["and"][0][">="][1] for i in range(num_needed_events): events += self.guardian.match_usages_and_limits(structure["name"], event_rules, usages, limits, structure["resources"]) events = self.guardian.reduce_structure_events(events) generated_requests, events_to_remove = self.guardian.match_rules_and_events(structure, rescaling_rules, events, limits, usages) expected_events_to_remove = dict() event_to_remove_name = MyUtils.generate_event_name(events[resource]["events"], resource) expected_events_to_remove[event_to_remove_name] = num_needed_events TestCase.assertEqual(self, first=expected_events_to_remove, second=events_to_remove)
def rebalance_applications(self, config): self.__config = config self.__debug = MyUtils.get_config_value(self.__config, CONFIG_DEFAULT_VALUES, "DEBUG") self.__ENERGY_DIFF_PERCENTAGE = MyUtils.get_config_value( self.__config, CONFIG_DEFAULT_VALUES, "ENERGY_DIFF_PERCENTAGE") self.__ENERGY_STOLEN_PERCENTAGE = MyUtils.get_config_value( self.__config, CONFIG_DEFAULT_VALUES, "ENERGY_STOLEN_PERCENTAGE") MyUtils.log_info("_______________", self.__debug) MyUtils.log_info("Performing APP ENERGY Balancing", self.__debug) MyUtils.log_info( "ENERGY_DIFF_PERCENTAGE -> {0}".format( self.__ENERGY_DIFF_PERCENTAGE), self.__debug) MyUtils.log_info( "ENERGY_STOLEN_PERCENTAGE -> {0}".format( self.__ENERGY_STOLEN_PERCENTAGE), self.__debug) try: applications = MyUtils.get_structures(self.__couchdb_handler, self.__debug, subtype="application") users = self.__couchdb_handler.get_users() except requests.exceptions.HTTPError as e: MyUtils.log_error("Couldn't get users and/or applications", self.__debug) MyUtils.log_error(str(e), self.__debug) return for user in users: MyUtils.log_info("Processing user {0}".format(user["name"]), self.__debug) user_apps = get_user_apps(applications, user) if "energy_policy" not in user: balancing_policy = "static" MyUtils.log_info( "Energy balancing policy unset for user {0}, defaulting to 'static'" .format(user["name"]), self.__debug) else: balancing_policy = user["energy_policy"] MyUtils.log_info( "User {0} has {1} policy".format(user["name"], balancing_policy), self.__debug) if balancing_policy == "static": max_energy = user["energy"]["max"] self.__static_app_rebalancing(user_apps, max_energy) elif balancing_policy == "dynamic": self.__dynamic_app_rebalancing(user_apps) else: MyUtils.log_error( "Unknown energy balancing policy '{0}'".format( balancing_policy), self.__debug) MyUtils.log_info("_______________\n", self.__debug)