Code refactoring - Watcher Decision Engine package

This patchset is there to change the code structure.
The objective is to flatten the project file tree by merging
'api/' and 'framework/' into a single package. This also contains
some tidy ups in package naming (like using only singular nouns).

This should only affect file/folder names and their subsequent
import paths wherever they were used.

Change-Id: Ie903ba20ca5cf03b0b42efa60131d1b919b0c2c9
This commit is contained in:
Vincent Françoise
2015-11-26 11:54:32 +01:00
parent 6e81a31bd8
commit eb3870a4b3
100 changed files with 124 additions and 158 deletions

View File

@@ -0,0 +1,23 @@
Basic Consolidation is based on Sercon :
Sercon: Server Consolidation Algorithm using Live Migration of Virtual Machines for Green Computing
Virtualization technologies changed the way data centers of enterprises utilize their server resources.
Instead of using dedicated servers for each type of application, virtualization allows viewing resources as a pool of
unified resources, thereby reducing complexity and easing manageability.
Server consolidation technique,which deals with reducing the number of servers used by consolidating applications, is one of the main
applications of virtualization in data centers.
The latter technique helps to use computing resources more effectively and has many benefits,such as reducing costs of power, cooling and, hence, contributes to the
Green IT initiative.
In a dynamic data center environment, where applications encapsulated as virtual machines
are mapped to and released from the nodes frequently, reducing the number of server nodes used can be
achieved by migrating applications without stopping their services, the technology known as live migration.
However, live migration is a costly operation; hence, how to perform periodic server consolidation operation
in a migration-aware way is a challenging task.
Sercon not only minimizes the overall number of used servers, but also minimizes the number of migrations.
Aziz Murtazaev and Sangyoon Oh
Department of Computer and Information Engineering, Ajou University, Suwon, Korea

View File

@@ -0,0 +1,83 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# 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.
import abc
from oslo_log import log
import six
from watcher.decision_engine.solution.default import DefaultSolution
from watcher.decision_engine.strategy.level import StrategyLevel
LOG = log.getLogger(__name__)
@six.add_metaclass(abc.ABCMeta)
class BaseStrategy(object):
"""A Strategy is an algorithm implementation which is able to find a
Solution for a given Goal.
"""
def __init__(self, name=None, description=None):
self._name = name
self.description = description
# default strategy level
self._strategy_level = StrategyLevel.conservative
self._cluster_state_collector = None
# the solution given by the strategy
self._solution = DefaultSolution()
@abc.abstractmethod
def execute(self, model):
"""Execute a strategy
:param model: The name of the strategy to execute (loaded dynamically)
:type model: str
:return: A computed solution (via a placement algorithm)
:rtype: :class:`watcher.decision_engine.api.strategy.solution.Solution`
"""
@property
def solution(self):
return self._solution
@solution.setter
def solution(self, s):
self._solution = s
@property
def name(self):
return self._name
@name.setter
def name(self, n):
self._name = n
@property
def strategy_level(self):
return self._strategy_level
@strategy_level.setter
def strategy_level(self, s):
self._strategy_level = s
@property
def state_collector(self):
return self._cluster_state_collector
@state_collector.setter
def state_collector(self, s):
self._cluster_state_collector = s

View File

@@ -0,0 +1,489 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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.
#
from oslo_log import log
from watcher.common.exception import ClusterEmpty
from watcher.common.exception import ClusteStateNotDefined
from watcher.decision_engine.strategy.base import BaseStrategy
from watcher.decision_engine.strategy.level import StrategyLevel
from watcher.decision_engine.meta_action.hypervisor_state import \
ChangeHypervisorState
from watcher.decision_engine.meta_action.migrate import Migrate
from watcher.decision_engine.meta_action.migrate import MigrationType
from watcher.decision_engine.meta_action.power_state import ChangePowerState
from watcher.decision_engine.meta_action.power_state import PowerState
from watcher.decision_engine.model.hypervisor_state import HypervisorState
from watcher.decision_engine.model.resource import ResourceType
from watcher.decision_engine.model.vm_state import VMState
from watcher.metrics_engine.cluster_history.ceilometer import \
CeilometerClusterHistory
LOG = log.getLogger(__name__)
class BasicConsolidation(BaseStrategy):
DEFAULT_NAME = "basic"
DEFAULT_DESCRIPTION = "Basic offline consolidation"
def __init__(self, name=DEFAULT_NAME, description=DEFAULT_DESCRIPTION):
"""Basic offline Consolidation using live migration
The basic consolidation algorithm has several limitations.
It has been developed only for tests.
eg: The BasicConsolidation assumes that the virtual mahine and
the compute node are on the same private network.
Good Strategy :
The workloads of the VMs are changing over the time
and often tend to migrate from one physical machine to another.
Hence, the traditional and offline heuristics such as bin packing
are not applicable for the placement VM in cloud computing.
So, the decision Engine optimizer provide placement strategy considering
not only the performance effects but also the workload characteristics of
VMs and others metrics like the power consumption and
the tenants constraints (SLAs).
The watcher optimizer use an online VM placement technique
based on machine learning and meta-heuristics that must handle :
- multi-objectives
- Contradictory objectives
- Adapt to changes dynamically
- Fast convergence
:param name: the name of the strategy
:param description: a description of the strategy
"""
super(BasicConsolidation, self).__init__(name, description)
# set default value for the number of released nodes
self.number_of_released_nodes = 0
# set default value for the number of migrations
self.number_of_migrations = 0
# set default value for number of allowed migration attempts
self.migration_attempts = 0
# set default value for the efficiency
self.efficiency = 100
self._ceilometer = None
# TODO(jed) improve threshold overbooking ?,...
self.threshold_mem = 1
self.threshold_disk = 1
self.threshold_cores = 1
# TODO(jed) target efficiency
self.target_efficiency = 60
# TODO(jed) weight
self.weight_cpu = 1
self.weight_mem = 1
self.weight_disk = 1
# TODO(jed) bound migration attempts (80 %)
self.bound_migration = 0.80
@property
def ceilometer(self):
if self._ceilometer is None:
self._ceilometer = CeilometerClusterHistory()
return self._ceilometer
@ceilometer.setter
def ceilometer(self, c):
self._ceilometer = c
def compute_attempts(self, size_cluster):
"""Upper bound of the number of migration
:param size_cluster:
"""
self.migration_attempts = size_cluster * self.bound_migration
def check_migration(self, model,
src_hypervisor,
dest_hypervisor,
vm_to_mig):
'''check if the migration is possible
:param model: current state of the cluster
:param src_hypervisor: the current of the virtual machine
:param dest_hypervisor:the destination of the virtual machine
:param vm_to_mig: the virtual machine
:return: True if the there is enough place otherwise false
'''
if src_hypervisor == dest_hypervisor:
return False
LOG.debug('Migrate VM {0} from {1} to {2} '.format(vm_to_mig,
src_hypervisor,
dest_hypervisor,
))
total_cores = 0
total_disk = 0
total_mem = 0
cap_cores = model.get_resource_from_id(ResourceType.cpu_cores)
cap_disk = model.get_resource_from_id(ResourceType.disk)
cap_mem = model.get_resource_from_id(ResourceType.memory)
for vm_id in model.get_mapping().get_node_vms(dest_hypervisor):
vm = model.get_vm_from_id(vm_id)
total_cores += cap_cores.get_capacity(vm)
total_disk += cap_disk.get_capacity(vm)
total_mem += cap_mem.get_capacity(vm)
# capacity requested by hypervisor
total_cores += cap_cores.get_capacity(vm_to_mig)
total_disk += cap_disk.get_capacity(vm_to_mig)
total_mem += cap_mem.get_capacity(vm_to_mig)
return self.check_threshold(model,
dest_hypervisor,
total_cores,
total_disk,
total_mem)
def check_threshold(self, model,
dest_hypervisor,
total_cores,
total_disk,
total_mem):
"""Check threshold
check the threshold value defined by the ratio of
aggregated CPU capacity of VMS on one node to CPU capacity
of this node must not exceed the threshold value.
:param dest_hypervisor:
:param total_cores
:param total_disk
:param total_mem
:return: True if the threshold is not exceed
"""
cap_cores = model.get_resource_from_id(ResourceType.cpu_cores)
cap_disk = model.get_resource_from_id(ResourceType.disk)
cap_mem = model.get_resource_from_id(ResourceType.memory)
# available
cores_available = cap_cores.get_capacity(dest_hypervisor)
disk_available = cap_disk.get_capacity(dest_hypervisor)
mem_available = cap_mem.get_capacity(dest_hypervisor)
if cores_available >= total_cores * self.threshold_cores \
and disk_available >= total_disk * self.threshold_disk \
and mem_available >= total_mem * self.threshold_mem:
return True
else:
return False
def get_allowed_migration_attempts(self):
"""Allowed migration
Maximum allowed number of migrations this allows us to fix
the upper bound of the number of migrations
:return:
"""
return self.migration_attempts
def get_threshold_cores(self):
return self.threshold_cores
def set_threshold_cores(self, threshold):
self.threshold_cores = threshold
def get_number_of_released_nodes(self):
return self.number_of_released_nodes
def get_number_of_migrations(self):
return self.number_of_migrations
def calculate_weight(self, model, element, total_cores_used,
total_disk_used, total_memory_used):
"""Calculate weight of every
:param model:
:param element:
:param total_cores_used:
:param total_disk_used:
:param total_memory_used:
:return:
"""
cpu_capacity = model.get_resource_from_id(
ResourceType.cpu_cores).get_capacity(element)
disk_capacity = model.get_resource_from_id(
ResourceType.disk).get_capacity(element)
memory_capacity = model.get_resource_from_id(
ResourceType.memory).get_capacity(element)
score_cores = (1 - (float(cpu_capacity) - float(total_cores_used)) /
float(cpu_capacity))
# It's possible that disk_capacity is 0, e.g. m1.nano.disk = 0
if disk_capacity == 0:
score_disk = 0
else:
score_disk = (1 - (float(disk_capacity) - float(total_disk_used)) /
float(disk_capacity))
score_memory = (
1 - (float(memory_capacity) - float(total_memory_used)) /
float(memory_capacity))
# todo(jed) take in account weight
return (score_cores + score_disk + score_memory) / 3
def calculate_score_node(self, hypervisor, model):
"""calculate the score that reprensent the utilization level
:param hypervisor:
:param model:
:return:
"""
cpu_avg_vm = self.ceilometer. \
statistic_aggregation(resource_id=hypervisor.uuid,
meter_name='compute.node.cpu.percent',
period="7200",
aggregate='avg'
)
if cpu_avg_vm is None:
LOG.error(
"No values returned for {0} compute.node.cpu.percent".format(
hypervisor.uuid))
cpu_avg_vm = 100
cpu_capacity = model.get_resource_from_id(
ResourceType.cpu_cores).get_capacity(hypervisor)
total_cores_used = cpu_capacity * (cpu_avg_vm / 100)
return self.calculate_weight(model, hypervisor, total_cores_used,
0,
0)
def calculate_migration_efficiency(self):
"""Calculate migration efficiency
:return: The efficiency tells us that every VM migration resulted
in releasing on node
"""
if self.number_of_migrations > 0:
return (float(self.number_of_released_nodes) / float(
self.number_of_migrations)) * 100
else:
return 0
def calculate_score_vm(self, vm, model):
"""Calculate Score of virtual machine
:param vm_id: the id of virtual machine
:param model: the model
:return: score
"""
if model is None:
raise ClusteStateNotDefined()
vm = model.get_vm_from_id(vm.uuid)
vm_cpu_utilization = self.ceilometer. \
statistic_aggregation(resource_id=vm.uuid,
meter_name='cpu_util',
period="7200",
aggregate='avg'
)
if vm_cpu_utilization is None:
LOG.error(
"No values returned for {0} cpu_util".format(vm.uuid))
vm_cpu_utilization = 100
cpu_capacity = model.get_resource_from_id(
ResourceType.cpu_cores).get_capacity(vm)
total_cores_used = cpu_capacity * (vm_cpu_utilization / 100)
return self.calculate_weight(model, vm, total_cores_used,
0,
0)
return self.calculate_weight(model, vm, total_cores_used,
0,
0)
def print_utilization(self, model):
if model is None:
raise ClusteStateNotDefined()
for node_id in model.get_all_hypervisors():
LOG.debug("{0} utilization {1} % ".
format(node_id,
self.calculate_score_node(
model.get_hypervisor_from_id(
node_id),
model)))
def execute(self, orign_model):
LOG.debug("initialize Sercon Consolidation")
if orign_model is None:
raise ClusteStateNotDefined()
# todo(jed) clone model
current_model = orign_model
self.efficiency = 100
unsuccessful_migration = 0
first = True
size_cluster = len(current_model.get_all_hypervisors())
if size_cluster == 0:
raise ClusterEmpty()
self.compute_attempts(size_cluster)
for hypervisor_id in current_model.get_all_hypervisors():
hypervisor = current_model.get_hypervisor_from_id(hypervisor_id)
count = current_model.get_mapping(). \
get_node_vms_from_id(hypervisor_id)
if len(count) == 0:
change_power = ChangePowerState(hypervisor)
change_power.powerstate = PowerState.g1_S1
change_power.level = StrategyLevel.conservative
self.solution.add_change_request(change_power)
if hypervisor.state == HypervisorState.ONLINE:
h = ChangeHypervisorState(hypervisor)
h.level = StrategyLevel.aggressive
h.state = HypervisorState.OFFLINE
self.solution.add_change_request(h)
while self.get_allowed_migration_attempts() >= unsuccessful_migration:
if first is not True:
self.efficiency = self.calculate_migration_efficiency()
if self.efficiency < float(self.target_efficiency):
break
first = False
score = []
''' calculate score of nodes based on load by VMs '''
for hypervisor_id in current_model.get_all_hypervisors():
hypervisor = current_model.get_hypervisor_from_id(
hypervisor_id)
count = current_model.get_mapping(). \
get_node_vms_from_id(hypervisor_id)
if len(count) > 0:
result = self.calculate_score_node(hypervisor,
current_model)
else:
''' the hypervisor has not VMs '''
result = 0
if len(count) > 0:
score.append((hypervisor_id, result))
''' sort compute nodes by Score decreasing '''''
s = sorted(score, reverse=True, key=lambda x: (x[1]))
LOG.debug("Hypervisor(s) BFD {0}".format(s))
''' get Node to be released '''
if len(score) == 0:
LOG.warning(
"The workloads of the compute nodes"
" of the cluster is zero.")
break
node_to_release = s[len(score) - 1][0]
''' get List of VMs from Node '''
vms_to_mig = current_model.get_mapping().get_node_vms_from_id(
node_to_release)
vm_score = []
for vm_id in vms_to_mig:
vm = current_model.get_vm_from_id(vm_id)
if vm.state == VMState.ACTIVE.value:
vm_score.append(
(vm_id, self.calculate_score_vm(vm, current_model)))
''' sort VM's by Score '''
v = sorted(vm_score, reverse=True, key=lambda x: (x[1]))
LOG.debug("VM(s) BFD {0}".format(v))
m = 0
tmp_vm_migration_schedule = []
for vm in v:
for j in range(0, len(s)):
mig_vm = current_model.get_vm_from_id(vm[0])
mig_src_hypervisor = current_model.get_hypervisor_from_id(
node_to_release)
mig_dst_hypervisor = current_model.get_hypervisor_from_id(
s[j][0])
result = self.check_migration(current_model,
mig_src_hypervisor,
mig_dst_hypervisor, mig_vm)
if result is True:
''' create migration VM '''
if current_model.get_mapping(). \
migrate_vm(mig_vm, mig_src_hypervisor,
mig_dst_hypervisor):
live_migrate = Migrate(mig_vm,
mig_src_hypervisor,
mig_dst_hypervisor)
# live migration
live_migrate.set_migration_type(
MigrationType.pre_copy)
live_migrate.level = StrategyLevel.conservative
tmp_vm_migration_schedule.append(live_migrate)
if len(current_model.get_mapping().get_node_vms(
mig_src_hypervisor)) == 0:
# TODO(jed) how to manage strategy level
# from conservative to aggressive
change_power = ChangePowerState(mig_src_hypervisor)
change_power.powerstate = PowerState.g1_S1
change_power.level = StrategyLevel.conservative
tmp_vm_migration_schedule.append(change_power)
h = ChangeHypervisorState(mig_src_hypervisor)
h.level = StrategyLevel.aggressive
h.state = HypervisorState.OFFLINE
tmp_vm_migration_schedule.append(h)
self.number_of_released_nodes += 1
m += 1
break
if m > 0:
self.number_of_migrations = self.number_of_migrations + m
unsuccessful_migration = 0
for a in tmp_vm_migration_schedule:
self.solution.add_change_request(a)
else:
unsuccessful_migration += 1
# self.print_utilization(current_model)
infos = {
"number_of_migrations": self.number_of_migrations,
"number_of_nodes_released": self.number_of_released_nodes,
"efficiency": self.efficiency
}
LOG.debug(infos)
self.solution.model = current_model
self.solution.efficiency = self.efficiency
return self.solution

View File

@@ -0,0 +1,28 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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.
#
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class BaseStrategyContext(object):
@abc.abstractmethod
def execute_strategy(self, model):
raise NotImplementedError(
"Should have implemented this") # pragma:no cover

View File

@@ -0,0 +1,48 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# 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.
from oslo_log import log
from watcher.decision_engine.planner.default import DefaultPlanner
from watcher.decision_engine.strategy.context.base import BaseStrategyContext
from watcher.decision_engine.strategy.selector.default import StrategySelector
LOG = log.getLogger(__name__)
class StrategyContext(BaseStrategyContext):
def __init__(self, broker=None):
LOG.debug("Initializing decision_engine Engine API ")
self.strategies = {}
self.selected_strategies = []
self.broker = broker
self.planner = DefaultPlanner()
self.strategy_selector = StrategySelector()
self.goal = None
def add_strategy(self, strategy):
self.strategies[strategy.name] = strategy
self.selected_strategy = strategy.name
def remove_strategy(self, strategy):
pass
def set_goal(self, goal):
self.goal = goal
def execute_strategy(self, model):
# todo(jed) create thread + refactoring
selected_strategy = self.strategy_selector.define_from_goal(self.goal)
return selected_strategy.execute(model)

View File

@@ -0,0 +1,38 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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.
#
from oslo_log import log
from watcher.decision_engine.meta_action.nop import Nop
from watcher.decision_engine.strategy.base import BaseStrategy
LOG = log.getLogger(__name__)
class DummyStrategy(BaseStrategy):
DEFAULT_NAME = "dummy"
DEFAULT_DESCRIPTION = "Dummy Strategy"
def __init__(self, name=DEFAULT_NAME, description=DEFAULT_DESCRIPTION):
super(DummyStrategy, self).__init__(name, description)
def execute(self, model):
n = Nop()
self.solution.add_change_request(n)
return self.solution

View File

@@ -0,0 +1,27 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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.
#
from enum import Enum
class StrategyLevel(Enum):
conservative = "conservative"
balanced = "balanced"
growth = "growth"
aggressive = "aggressive"

View File

@@ -0,0 +1,52 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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.
#
from __future__ import unicode_literals
from oslo_log import log
from stevedore import ExtensionManager
from watcher.decision_engine.strategy.basic_consolidation import \
BasicConsolidation
LOG = log.getLogger(__name__)
class StrategyLoader(object):
default_strategy_cls = BasicConsolidation
def load_strategies(self):
extension_manager = ExtensionManager(
namespace='watcher_strategies',
invoke_on_load=True,
)
return {ext.name: ext.plugin for ext in extension_manager.extensions}
def load(self, model):
strategy = None
try:
available_strategies = self.load_strategies()
strategy_cls = available_strategies.get(
model, self.default_strategy_cls
)
strategy = strategy_cls()
except Exception as exc:
LOG.exception(exc)
return strategy

View File

@@ -0,0 +1,28 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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.
#
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class Selector(object):
@abc.abstractmethod
def define_from_goal(self, goal_name):
raise NotImplementedError(
"Should have implemented this") # pragma:no cover

View File

@@ -0,0 +1,53 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# 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.
from oslo_config import cfg
from oslo_log import log
from watcher.decision_engine.strategy.loader import StrategyLoader
from watcher.decision_engine.strategy.selector.base import Selector
from watcher.objects.audit_template import Goal
LOG = log.getLogger(__name__)
CONF = cfg.CONF
goals = {
'SERVERS_CONSOLIDATION': 'basic',
'MINIMIZE_ENERGY_CONSUMPTION': 'basic',
'BALANCE_LOAD': 'basic',
'MINIMIZE_LICENSING_COST': 'basic',
'PREPARE_PLANNED_OPERATION': 'basic'
}
WATCHER_GOALS_OPTS = [
cfg.DictOpt('goals',
default=goals, help='Goals used for the optimization ')
]
goals_opt_group = cfg.OptGroup(name='watcher_goals',
title='Goals available for the optimization')
CONF.register_group(goals_opt_group)
CONF.register_opts(WATCHER_GOALS_OPTS, goals_opt_group)
class StrategySelector(Selector):
def __init__(self):
self.strategy_loader = StrategyLoader()
def define_from_goal(self, goal_name):
if goal_name is None:
goal_name = Goal.SERVERS_CONSOLIDATION
strategy_to_load = CONF.watcher_goals.goals[goal_name]
return self.strategy_loader.load(strategy_to_load)

View File

@@ -0,0 +1,28 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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.
#
from enum import Enum
class StrategyState(Enum):
INIT = 1,
READY = 2,
RUNNING = 3,
TERMINATED = 4,
ERROR = 5