Source code for hots.allocation

"""Provide resource allocation related functions to handle this problem."""

import math

import numpy as np

from . import init as it
from .placement import spread_containers
from .tools import change_max_dataset


[docs]def check_constraints( my_instance, working_df_indiv, config ): """Check if allocation constraints are satisfied or not. :param my_instance: The Instance object of the current run :type my_instance: Instance :param working_df_indiv: Dataframe with current window individual data :type working_df_indiv: pd.DataFrame :param config: Current HOTS run parameters :type config: Dict :return: True if all constraints are satisfied, False otherwise :rtype: bool """ satisfied = False print(config) print(my_instance.df_host) # while not satisfied: current_max_by_node = get_max_by_node(my_instance.df_indiv) if get_total_max(current_max_by_node) > get_abs_goal_load(my_instance, config): print('Max resources used > wanted max ! (new)') satisfied = False change_max_bound(my_instance, config, working_df_indiv[it.tick_field].max()) else: satisfied = True if max(my_instance.df_host[it.metrics[0]]) > get_abs_goal_load(my_instance, config): print('Max resources used > wanted max !') satisfied = False change_max_bound(my_instance, config, working_df_indiv[it.tick_field].max()) else: print('Max used resources ok.') satisfied = True if my_instance.df_host[it.host_field].nunique() > config['objective']['open_nodes']: print('Too many open nodes !') satisfied = False # TODO Test if we can do it # TODO Do not test with previous data, as it's not supposed to change # move_containers(my_instance, config) elif my_instance.df_host[it.host_field].nunique() < config['objective']['open_nodes']: print('Less open nodes than the objective !') satisfied = True else: print('Right number of open nodes.') satisfied = True return satisfied
# TODO define max by container # TODO change alloc only for containers in node > max
[docs]def change_max_bound(my_instance, config, min_time): """Change max possible resource usage by containers. :param my_instance: The Instance object of the current run :type my_instance: Instance :param config: Current HOTS run parameters :type config: Dict :param min_time: T0 of current time window :type min_time: int """ # max_ok = False max_goal = get_abs_goal_load(my_instance, config) current_max_by_node = get_max_by_node(my_instance.df_indiv) total_max = get_total_max(current_max_by_node) print(current_max_by_node, total_max) print('total resources (max) : ', total_max) total_to_remove = resources_to_remove(max_goal, current_max_by_node) print('total resources to remove : ', total_to_remove) # If we want to remove same amount from all containers # decrease_cont = round_decimals_up( # total_to_remove / my_instance.df_indiv[ # it.indiv_field].nunique()) # print('resources to remove by container (average) : ', decrease_cont) # Remove resources at prorata of their initial max total_will_remove = 0.0 print('Before change dataset : ') print(my_instance.df_indiv) for container in my_instance.df_indiv[it.indiv_field].unique(): print('Doing container %s' % container) node = my_instance.df_indiv.loc[my_instance.df_indiv[ it.indiv_field] == container][it.host_field].to_numpy()[0] percent_total = current_max_by_node[node][container] / total_max to_remove_c = total_to_remove * percent_total total_will_remove += to_remove_c current_max_by_node[node][container] = current_max_by_node[ node][container] - to_remove_c change_max_dataset(my_instance.df_indiv, container, it.indiv_field, current_max_by_node[node][container], it.metrics[0], it.tick_field, min_time) print('After change dataset :') print(my_instance.df_indiv) # input() print('Will be remove : ', total_will_remove) # Retrieve small amount on all, until needed is reached # while not max_ok: # print(current_max_by_node) # input() # decrease = 0.1 # for container in my_instance.df_indiv[it.indiv_field]: # node = my_instance.df_indiv.loc[my_instance.df_indiv[ # it.indiv_field] == container][it.host_field].to_numpy()[0] # current_max_by_node[node][container] = current_max_by_node[ # node][container] - (current_max_by_node[ # node][container] * decrease) # if is_max_goal_ok(current_max_by_node, max_goal): # max_ok = True # break print('New max list : ', current_max_by_node) print('max goal satisfied ? ', is_max_goal_ok(current_max_by_node, max_goal))
[docs]def change_df_max(my_instance, c_id, max_c): """Change the DataFrame (resources values) after changing max. :param my_instance: The Instance object of the current run :type my_instance: Instance :param c_id: ID of container to change values :type c_id: str :param max_c: New bound value for c_id data :type max_c: float """ for time in my_instance.df_indiv[it.tick_field].unique(): if my_instance.df_indiv.loc[ (my_instance.df_indiv[it.tick_field] == time) & ( my_instance.df_indiv[it.indiv_field] == c_id), it.metrics[0] ].to_numpy()[0] > max_c: my_instance.df_indiv.loc[ (my_instance.df_indiv[it.tick_field] == time) & ( my_instance.df_indiv[it.indiv_field] == c_id), it.metrics[0] ] = max_c
[docs]def get_max_by_node(df_indiv): """Get the max possible usage of every container, by node. :param df_indiv: Dataframe storing individual data :type df_indiv: pd.DataFrame :return: Max container value for each node :rtype: Dict """ max_by_node = {} for node, node_data in df_indiv.groupby(it.host_field): node_max = {} for container, cont_data in node_data.groupby(node_data[it.indiv_field]): node_max[container] = max(cont_data[it.metrics[0]]) max_by_node[node] = node_max return max_by_node
[docs]def get_total_max(max_by_node): """Get the total amount of max resources. :param max_by_node: Dict object of max container value in each node :type max_by_node: Dict :return: Sum of every container max value from each node :rtype: float """ total_max = 0.0 for node in max_by_node.keys(): total_max += sum(max_by_node[node].values()) return total_max
[docs]def is_max_goal_ok(current_max_by_node, max_goal): """Check is the max usage objective is satisfied. :param current_max_by_node: Max container value for each node :type current_max_by_node: Dict :param max_goal: Max value objective (not to exceed) :type max_goal: float :return: True if objective is satisfied, False otherwise :rtype: bool """ for node in current_max_by_node.keys(): if sum(current_max_by_node[node].values()) > max_goal: print('Not ok') return False return True
# TODO what if different goal on different nodes ? Dict of nodes goal ?
[docs]def get_abs_goal_load(my_instance, config): """Get the load goal in absolute value. :param my_instance: The Instance object of the current run :type my_instance: Instance :param config: Current HOTS run parameters :type config: Dict :return: Load value to achieve from parameters :rtype: float """ return config['objective']['target_load_CPU'] * ( my_instance.df_host_meta[it.metrics[0]].to_numpy()[0] )
# TODO what if several nodes in goal ?
[docs]def resources_to_remove(max_goal, max_by_node): """Compute the amount of resources to remove to reach the load goal. :param max_goal: Load value to achieve from parameters :type max_goal: float :param max_by_node: Max container value for each node :type max_by_node: Dict :return: Value to retrieve for reaching load goal :rtype: float """ return (get_total_max(max_by_node) - max_goal)
[docs]def round_decimals_up(number, decimals=2): """Return a value rounded up to a specific number of decimal places. :param number: Value to round up :type number: float :param decimals: Wanted numbers after comma, defaults to 2 :type decimals: int, optional :raises TypeError: Wrong type for decimals :raises ValueError: Non-positive value for decimals :return: Rounded up value :rtype: float """ if not isinstance(decimals, int): raise TypeError('decimal places must be an integer') elif decimals < 0: raise ValueError('decimal places has to be 0 or more') elif decimals == 0: return math.ceil(number) factor = 10 ** decimals return math.ceil(number * factor) / factor
[docs]def move_containers(my_instance, config): """Move containers in order to satisfy number open nodes target. :param my_instance: The Instance object of the current run :type my_instance: Instance :param config: Current HOTS run parameters :type config: Dict """ conso_nodes = np.zeros(( config['objective']['open_nodes'], my_instance.window_duration)) spread_containers( my_instance.df_indiv[it.indiv_field].unique(), my_instance, conso_nodes, my_instance.window_duration, config['objective']['open_nodes'])