299 lines
10 KiB
Python

# Copyright 2012 Anton Beloglazov
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
""" This is the main module of the MHOD algorithm.
"""
from contracts import contract
from neat.contracts_primitive import *
from neat.contracts_extra import *
import neat.locals.overload.mhod.multisize_estimation as estimation
import neat.locals.overload.mhod.bruteforce as bruteforce
from neat.locals.overload.mhod.l_2_states import ls
import logging
log = logging.getLogger(__name__)
@contract
def mhod_factory(time_step, migration_time, params):
""" Creates the MHOD algorithm.
:param time_step: The length of the simulation time step in seconds.
:type time_step: int,>=0
:param migration_time: The VM migration time in time seconds.
:type migration_time: float,>=0
:param params: A dictionary containing the algorithm's parameters.
:type params: dict(str: *)
:return: A function implementing the MHOD algorithm.
:rtype: function
"""
def mhod_wrapper(utilization, state=None):
if not state:
state = init_state(params['history_size'],
params['window_sizes'],
len(params['state_config']) + 1)
return mhod(params['state_config'],
params['otf'],
params['window_sizes'],
params['bruteforce_step'],
params['learning_steps'],
time_step,
migration_time,
utilization,
state)
return mhod_wrapper
@contract
def init_state(history_size, window_sizes, number_of_states):
""" Initialize the state dictionary of the MHOD algorithm.
:param history_size: The number of last system states to store.
:type history_size: int,>0
:param window_sizes: The required window sizes.
:type window_sizes: list(int)
:param number_of_states: The number of states.
:type number_of_states: int,>0
:return: The initialization state dictionary.
:rtype: dict(str: *)
"""
return {
'previous_state': 0,
'previous_utilization': [],
'time_in_states': 0,
'time_in_state_n': 0,
'request_windows': estimation.init_request_windows(
number_of_states, max(window_sizes)),
'estimate_windows': estimation.init_deque_structure(
window_sizes, number_of_states),
'variances': estimation.init_variances(
window_sizes, number_of_states),
'acceptable_variances': estimation.init_variances(
window_sizes, number_of_states)}
@contract
def mhod(state_config, otf, window_sizes, bruteforce_step, learning_steps,
time_step, migration_time, utilization, state):
""" The MHOD algorithm returning whether the host is overloaded.
:param state_config: The state configuration.
:type state_config: list(float)
:param otf: The OTF parameter.
:type otf: float,>0
:param window_sizes: A list of window sizes.
:type window_sizes: list(int)
:param bruteforce_step: The step of the bruteforce algorithm.
:type bruteforce_step: float
:param time_step: The length of the simulation time step in seconds.
:type time_step: int,>=0
:param migration_time: The VM migration time in time seconds.
:type migration_time: float,>=0
:param utilization: The history of the host's CPU utilization.
:type utilization: list(float)
:param state: The state of the algorithm.
:type state: dict
:return: The updated state and decision of the algorithm.
:rtype: tuple(bool, dict)
"""
utilization_length = len(utilization)
# if utilization_length == state['time_in_states'] and \
# utilization == state['previous_utilization']:
# # No new utilization values
# return False, state
number_of_states = len(state_config) + 1
previous_state = 0
# state['previous_utilization'] = utilization
state['request_windows'] = estimation.init_request_windows(
number_of_states, max(window_sizes))
state['estimate_windows'] = estimation.init_deque_structure(
window_sizes, number_of_states)
state['variances'] = estimation.init_variances(
window_sizes, number_of_states)
state['acceptable_variances'] = estimation.init_variances(
window_sizes, number_of_states)
for i, current_state in enumerate(utilization_to_states(state_config, utilization)):
state['request_windows'] = estimation.update_request_windows(
state['request_windows'],
previous_state,
current_state)
state['estimate_windows'] = estimation.update_estimate_windows(
state['estimate_windows'],
state['request_windows'],
previous_state)
state['variances'] = estimation.update_variances(
state['variances'],
state['estimate_windows'],
previous_state)
state['acceptable_variances'] = estimation.update_acceptable_variances(
state['acceptable_variances'],
state['estimate_windows'],
previous_state)
previous_state = current_state
selected_windows = estimation.select_window(
state['variances'],
state['acceptable_variances'],
window_sizes)
p = estimation.select_best_estimates(
state['estimate_windows'],
selected_windows)
# These two are saved for testing purposes
state['selected_windows'] = selected_windows
state['p'] = p
state_vector = build_state_vector(state_config, utilization)
current_state = get_current_state(state_vector)
state['previous_state'] = current_state
state_n = len(state_config)
# if utilization_length > state['time_in_states'] + 1:
# for s in utilization_to_states(
# state_config,
# utilization[-(utilization_length - state['time_in_states']):]):
# state['time_in_states'] += 1
# if s == state_n:
# state['time_in_state_n'] += 1
# else:
state['time_in_states'] += 1
if current_state == state_n:
state['time_in_state_n'] += 1
if log.isEnabledFor(logging.DEBUG):
log.debug('MHOD utilization:' + str(utilization))
log.debug('MHOD time_in_states:' + str(state['time_in_states']))
log.debug('MHOD time_in_state_n:' + str(state['time_in_state_n']))
log.debug('MHOD p:' + str(p))
log.debug('MHOD current_state:' + str(current_state))
log.debug('MHOD p[current_state]:' + str(p[current_state]))
if utilization_length >= learning_steps:
if current_state == state_n and p[state_n][state_n] > 0:
# if p[current_state][state_n] > 0:
policy = bruteforce.optimize(
bruteforce_step, 1.0, otf, (migration_time / time_step), ls, p,
state_vector, state['time_in_states'], state['time_in_state_n'])
# This is saved for testing purposes
state['policy'] = policy
if log.isEnabledFor(logging.DEBUG):
log.debug('MHOD policy:' + str(policy))
command = issue_command_deterministic(policy)
if log.isEnabledFor(logging.DEBUG):
log.debug('MHOD command:' + str(command))
return command, state
return False, state
@contract
def build_state_vector(state_config, utilization):
""" Build the current state PMF corresponding to the utilization
history and state config.
:param state_config: The state configuration.
:type state_config: list(float)
:param utilization: The history of the host's CPU utilization.
:type utilization: list(float)
:return: The current state vector.
:rtype: list(int)
"""
state = utilization_to_state(state_config, utilization[-1])
return [int(state == x) for x in range(len(state_config) + 1)]
@contract
def utilization_to_state(state_config, utilization):
""" Transform a utilization value into the corresponding state.
:param state_config: The state configuration.
:type state_config: list(float)
:param utilization: A utilization value.
:type utilization: number,>=0
:return: The state corresponding to the utilization value.
:rtype: int
"""
prev = -1
for state, threshold in enumerate(state_config):
if utilization >= prev and utilization < threshold:
return state
prev = state
return prev + 1
@contract
def get_current_state(state_vector):
""" Get the current state corresponding to the state probability vector.
:param state_vector: The state PMF vector.
:type state_vector: list(int)
:return: The current state.
:rtype: int,>=0
"""
return state_vector.index(1)
@contract
def utilization_to_states(state_config, utilization):
""" Get the state history corresponding to the utilization history.
Adds the 0 state to the beginning to simulate the first transition.
(map (partial utilization-to-state state-config) utilization))
:param state_config: The state configuration.
:type state_config: list(float)
:param utilization: The history of the host's CPU utilization.
:type utilization: list(float)
:return: The state history.
:rtype: list(int)
"""
return [utilization_to_state(state_config, x) for x in utilization]
@contract
def issue_command_deterministic(policy):
""" Issue a migration command according to the policy PMF p.
:param policy: A policy PMF.
:type policy: list(number)
:return: A migration command.
:rtype: bool
"""
return len(policy) == 0