Enable strategy parameters
Strategy provides parameters to customize algorithm behavior. End user could query then specify parameters for their requirements. Change-Id: Id097db5f6e79c94b57674c8e5d55b06098abf18c Implements-bp: optimization-threshold
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
from oslo_log import log
|
||||
|
||||
from watcher.common import clients
|
||||
from watcher.common import utils
|
||||
from watcher.decision_engine.strategy.context import base
|
||||
from watcher.decision_engine.strategy.selection import default
|
||||
|
||||
@@ -58,4 +59,14 @@ class DefaultStrategyContext(base.BaseStrategyContext):
|
||||
|
||||
selected_strategy = strategy_selector.select()
|
||||
|
||||
schema = selected_strategy.get_schema()
|
||||
if not audit.parameters and schema:
|
||||
# Default value feedback if no predefined strategy
|
||||
utils.DefaultValidatingDraft4Validator(schema).validate(
|
||||
audit.parameters)
|
||||
|
||||
selected_strategy.input_parameters.update({
|
||||
name: value for name, value in audit.parameters.items()
|
||||
})
|
||||
|
||||
return selected_strategy.execute()
|
||||
|
||||
@@ -41,6 +41,7 @@ import six
|
||||
|
||||
from watcher.common import clients
|
||||
from watcher.common.loader import loadable
|
||||
from watcher.common import utils
|
||||
from watcher.decision_engine.loading import default as loading
|
||||
from watcher.decision_engine.solution import default
|
||||
from watcher.decision_engine.strategy.common import level
|
||||
@@ -76,6 +77,7 @@ class BaseStrategy(loadable.Loadable):
|
||||
self._collector_manager = None
|
||||
self._model = None
|
||||
self._goal = None
|
||||
self._input_parameters = utils.Struct()
|
||||
|
||||
@classmethod
|
||||
@abc.abstractmethod
|
||||
@@ -176,6 +178,23 @@ class BaseStrategy(loadable.Loadable):
|
||||
|
||||
return self._model
|
||||
|
||||
@classmethod
|
||||
def get_schema(cls):
|
||||
"""Defines a Schema that the input parameters shall comply to
|
||||
|
||||
:return: A jsonschema format (mandatory default setting)
|
||||
:rtype: dict
|
||||
"""
|
||||
return {}
|
||||
|
||||
@property
|
||||
def input_parameters(self):
|
||||
return self._input_parameters
|
||||
|
||||
@input_parameters.setter
|
||||
def input_parameters(self, p):
|
||||
self._input_parameters = p
|
||||
|
||||
@property
|
||||
def osc(self):
|
||||
if not self._osc:
|
||||
|
||||
@@ -54,7 +54,10 @@ class DummyStrategy(base.DummyBaseStrategy):
|
||||
raise exception.ClusterStateNotDefined()
|
||||
|
||||
def do_execute(self):
|
||||
LOG.debug("Executing Dummy strategy")
|
||||
para1 = self.input_parameters.para1
|
||||
para2 = self.input_parameters.para2
|
||||
LOG.debug("Executing Dummy strategy with para1=%(p1)f, para2=%(p2)s",
|
||||
{'p1': para1, 'p2': para2})
|
||||
parameters = {'message': 'hello World'}
|
||||
self.solution.add_action(action_type=self.NOP,
|
||||
input_parameters=parameters)
|
||||
@@ -80,3 +83,23 @@ class DummyStrategy(base.DummyBaseStrategy):
|
||||
@classmethod
|
||||
def get_translatable_display_name(cls):
|
||||
return "Dummy strategy"
|
||||
|
||||
@classmethod
|
||||
def get_schema(cls):
|
||||
# Mandatory default setting for each element
|
||||
return {
|
||||
"properties": {
|
||||
"para1": {
|
||||
"description": "number parameter example",
|
||||
"type": "number",
|
||||
"default": 3.2,
|
||||
"minimum": 1.0,
|
||||
"maximum": 10.2,
|
||||
},
|
||||
"para2": {
|
||||
"description": "string parameter example",
|
||||
"type": "string",
|
||||
"default": "hello"
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -73,8 +73,6 @@ class OutletTempControl(base.ThermalOptimizationBaseStrategy):
|
||||
|
||||
# The meter to report outlet temperature in ceilometer
|
||||
METER_NAME = "hardware.ipmi.node.outlet_temperature"
|
||||
# Unit: degree C
|
||||
THRESHOLD = 35.0
|
||||
|
||||
MIGRATION = "migrate"
|
||||
|
||||
@@ -87,10 +85,6 @@ class OutletTempControl(base.ThermalOptimizationBaseStrategy):
|
||||
:type osc: :py:class:`~.OpenStackClients` instance, optional
|
||||
"""
|
||||
super(OutletTempControl, self).__init__(config, osc)
|
||||
# the migration plan will be triggered when the outlet temperature
|
||||
# reaches threshold
|
||||
# TODO(zhenzanz): Threshold should be configurable for each audit
|
||||
self.threshold = self.THRESHOLD
|
||||
self._meter = self.METER_NAME
|
||||
self._ceilometer = None
|
||||
|
||||
@@ -106,6 +100,19 @@ class OutletTempControl(base.ThermalOptimizationBaseStrategy):
|
||||
def get_translatable_display_name(cls):
|
||||
return "Outlet temperature based strategy"
|
||||
|
||||
@classmethod
|
||||
def get_schema(cls):
|
||||
# Mandatory default setting for each element
|
||||
return {
|
||||
"properties": {
|
||||
"threshold": {
|
||||
"description": "temperature threshold for migration",
|
||||
"type": "number",
|
||||
"default": 35.0
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@property
|
||||
def ceilometer(self):
|
||||
if self._ceilometer is None:
|
||||
@@ -224,6 +231,11 @@ class OutletTempControl(base.ThermalOptimizationBaseStrategy):
|
||||
raise wexc.ClusterStateNotDefined()
|
||||
|
||||
def do_execute(self):
|
||||
# the migration plan will be triggered when the outlet temperature
|
||||
# reaches threshold
|
||||
self.threshold = self.input_parameters.threshold
|
||||
LOG.debug("Initializing Outlet temperature strategy with threshold=%d",
|
||||
self.threshold)
|
||||
hosts_need_release, hosts_target = self.group_hosts_by_outlet_temp()
|
||||
|
||||
if len(hosts_need_release) == 0:
|
||||
|
||||
@@ -28,7 +28,8 @@ LOG = log.getLogger(__name__)
|
||||
GoalMapping = collections.namedtuple(
|
||||
'GoalMapping', ['name', 'display_name', 'efficacy_specification'])
|
||||
StrategyMapping = collections.namedtuple(
|
||||
'StrategyMapping', ['name', 'goal_name', 'display_name'])
|
||||
'StrategyMapping',
|
||||
['name', 'goal_name', 'display_name', 'parameters_spec'])
|
||||
|
||||
IndicatorSpec = collections.namedtuple(
|
||||
'IndicatorSpec', ['name', 'description', 'unit', 'schema'])
|
||||
@@ -90,7 +91,8 @@ class Syncer(object):
|
||||
self._available_strategies_map = {
|
||||
StrategyMapping(
|
||||
name=s.name, goal_name=goals_map[s.goal_id],
|
||||
display_name=s.display_name): s
|
||||
display_name=s.display_name,
|
||||
parameters_spec=str(s.parameters_spec)): s
|
||||
for s in self.available_strategies
|
||||
}
|
||||
return self._available_strategies_map
|
||||
@@ -148,6 +150,7 @@ class Syncer(object):
|
||||
strategy_name = strategy_map.name
|
||||
strategy_display_name = strategy_map.display_name
|
||||
goal_name = strategy_map.goal_name
|
||||
parameters_spec = strategy_map.parameters_spec
|
||||
strategy_mapping = dict()
|
||||
|
||||
# Strategies that are matching by name with the given
|
||||
@@ -162,6 +165,7 @@ class Syncer(object):
|
||||
strategy.name = strategy_name
|
||||
strategy.display_name = strategy_display_name
|
||||
strategy.goal_id = objects.Goal.get_by_name(self.ctx, goal_name).id
|
||||
strategy.parameters_spec = parameters_spec
|
||||
strategy.create()
|
||||
LOG.info(_LI("Strategy %s created"), strategy_name)
|
||||
|
||||
@@ -284,7 +288,8 @@ class Syncer(object):
|
||||
strategies_map[strategy_cls.get_name()] = StrategyMapping(
|
||||
name=strategy_cls.get_name(),
|
||||
goal_name=strategy_cls.get_goal_name(),
|
||||
display_name=strategy_cls.get_translatable_display_name())
|
||||
display_name=strategy_cls.get_translatable_display_name(),
|
||||
parameters_spec=str(strategy_cls.get_schema()))
|
||||
|
||||
return discovered_map
|
||||
|
||||
|
||||
Reference in New Issue
Block a user