Merge "Refactored Strategy selector to select from DB"

This commit is contained in:
Jenkins
2016-05-11 15:32:06 +00:00
committed by Gerrit Code Review
10 changed files with 111 additions and 60 deletions

View File

@@ -123,7 +123,7 @@ function create_watcher_conf {
iniset $WATCHER_CONF oslo_messaging_rabbit rabbit_password $RABBIT_PASSWORD iniset $WATCHER_CONF oslo_messaging_rabbit rabbit_password $RABBIT_PASSWORD
iniset $WATCHER_CONF oslo_messaging_rabbit rabbit_host $RABBIT_HOST iniset $WATCHER_CONF oslo_messaging_rabbit rabbit_host $RABBIT_HOST
iniset $WATCHER_CONF watcher_goals goals "DUMMY:dummy,BASIC_CONSOLIDATION:basic" iniset $WATCHER_CONF watcher_goals goals "DUMMY:dummy,SERVER_CONSOLIDATION:basic"
configure_auth_token_middleware $WATCHER_CONF watcher $WATCHER_AUTH_CACHE_DIR configure_auth_token_middleware $WATCHER_CONF watcher $WATCHER_AUTH_CACHE_DIR
configure_auth_token_middleware $WATCHER_CONF watcher $WATCHER_AUTH_CACHE_DIR "watcher_clients_auth" configure_auth_token_middleware $WATCHER_CONF watcher $WATCHER_AUTH_CACHE_DIR "watcher_clients_auth"

View File

@@ -287,7 +287,11 @@ class MetricCollectorNotDefined(WatcherException):
class ClusterStateNotDefined(WatcherException): class ClusterStateNotDefined(WatcherException):
msg_fmt = _("the cluster state is not defined") msg_fmt = _("The cluster state is not defined")
class NoAvailableStrategyForGoal(WatcherException):
msg_fmt = _("No strategy could be found to achieve the '%(goal)s' goal.")
# Model # Model

View File

@@ -30,26 +30,20 @@ class DefaultStrategyContext(base.BaseStrategyContext):
def __init__(self): def __init__(self):
super(DefaultStrategyContext, self).__init__() super(DefaultStrategyContext, self).__init__()
LOG.debug("Initializing Strategy Context") LOG.debug("Initializing Strategy Context")
self._strategy_selector = default.DefaultStrategySelector()
self._collector_manager = manager.CollectorManager() self._collector_manager = manager.CollectorManager()
@property @property
def collector(self): def collector(self):
return self._collector_manager return self._collector_manager
@property
def strategy_selector(self):
return self._strategy_selector
def execute_strategy(self, audit_uuid, request_context): def execute_strategy(self, audit_uuid, request_context):
audit = objects.Audit.get_by_uuid(request_context, audit_uuid) audit = objects.Audit.get_by_uuid(request_context, audit_uuid)
# Retrieve the Audit Template # Retrieve the Audit Template
audit_template = objects.\ audit_template = objects.AuditTemplate.get_by_id(
AuditTemplate.get_by_id(request_context, audit.audit_template_id) request_context, audit.audit_template_id)
osc = clients.OpenStackClients() osc = clients.OpenStackClients()
# todo(jed) retrieve in audit_template parameters (threshold,...) # todo(jed) retrieve in audit_template parameters (threshold,...)
# todo(jed) create ActionPlan # todo(jed) create ActionPlan
collector_manager = self.collector.get_cluster_model_collector(osc=osc) collector_manager = self.collector.get_cluster_model_collector(osc=osc)
@@ -57,8 +51,13 @@ class DefaultStrategyContext(base.BaseStrategyContext):
# todo(jed) remove call to get_latest_cluster_data_model # todo(jed) remove call to get_latest_cluster_data_model
cluster_data_model = collector_manager.get_latest_cluster_data_model() cluster_data_model = collector_manager.get_latest_cluster_data_model()
selected_strategy = self.strategy_selector.define_from_goal( strategy_selector = default.DefaultStrategySelector(
audit_template.goal, osc=osc) goal_name=objects.Goal.get_by_name(
request_context, audit_template.goal).name,
strategy_name=None,
osc=osc)
selected_strategy = strategy_selector.select()
# todo(jed) add parameters and remove cluster_data_model # todo(jed) add parameters and remove cluster_data_model
return selected_strategy.execute(cluster_data_model) return selected_strategy.execute(cluster_data_model)

View File

@@ -22,6 +22,7 @@ import six
@six.add_metaclass(abc.ABCMeta) @six.add_metaclass(abc.ABCMeta)
class BaseSelector(object): class BaseSelector(object):
@abc.abstractmethod @abc.abstractmethod
def define_from_goal(self, goal_name): def select(self):
raise NotImplementedError() raise NotImplementedError()

View File

@@ -44,19 +44,48 @@ CONF.register_opts(WATCHER_GOALS_OPTS, goals_opt_group)
class DefaultStrategySelector(base.BaseSelector): class DefaultStrategySelector(base.BaseSelector):
def __init__(self): def __init__(self, goal_name, strategy_name=None, osc=None):
"""Default strategy selector
:param goal_name: Name of the goal
:param strategy_name: Name of the strategy
:param osc: an OpenStackClients instance
"""
super(DefaultStrategySelector, self).__init__() super(DefaultStrategySelector, self).__init__()
self.goal_name = goal_name
self.strategy_name = strategy_name
self.osc = osc
self.strategy_loader = default.DefaultStrategyLoader() self.strategy_loader = default.DefaultStrategyLoader()
def define_from_goal(self, goal_name, osc=None): def select(self):
""":param osc: an OpenStackClients instance""" """Selects a strategy
:raises: :py:class:`~.LoadingError` if it failed to load a strategy
:returns: A :py:class:`~.BaseStrategy` instance
"""
strategy_to_load = None strategy_to_load = None
try: try:
strategy_to_load = CONF.watcher_goals.goals[goal_name] if self.strategy_name:
return self.strategy_loader.load(strategy_to_load, osc=osc) strategy_to_load = self.strategy_name
except KeyError as exc: else:
available_strategies = self.strategy_loader.list_available()
available_strategies_for_goal = list(
key for key, strat in available_strategies.items()
if strat.get_goal_name() == self.goal_name)
if not available_strategies_for_goal:
raise exception.NoAvailableStrategyForGoal(
goal=self.goal_name)
# TODO(v-francoise): We should do some more work here to select
# a strategy out of a given goal instead of just choosing the
# 1st one
strategy_to_load = available_strategies_for_goal[0]
return self.strategy_loader.load(strategy_to_load, osc=self.osc)
except exception.NoAvailableStrategyForGoal:
raise
except Exception as exc:
LOG.exception(exc) LOG.exception(exc)
raise exception.WatcherException( raise exception.LoadingError(
_("Incorrect mapping: could not find " _("Could not load any strategy for goal %(goal)s"),
"associated strategy for '%s'") % goal_name goal=self.goal_name)
)

View File

@@ -28,11 +28,12 @@ from watcher.tests.objects import utils as obj_utils
class TestDefaultAuditHandler(base.DbTestCase): class TestDefaultAuditHandler(base.DbTestCase):
def setUp(self): def setUp(self):
super(TestDefaultAuditHandler, self).setUp() super(TestDefaultAuditHandler, self).setUp()
self.audit_template = obj_utils.create_test_audit_template( obj_utils.create_test_goal(self.context, id=1, name="DUMMY")
audit_template = obj_utils.create_test_audit_template(
self.context) self.context)
self.audit = obj_utils.create_test_audit( self.audit = obj_utils.create_test_audit(
self.context, self.context,
audit_template_id=self.audit_template.id) audit_template_id=audit_template.id)
@mock.patch.object(manager.CollectorManager, "get_cluster_model_collector") @mock.patch.object(manager.CollectorManager, "get_cluster_model_collector")
def test_trigger_audit_without_errors(self, mock_collector): def test_trigger_audit_without_errors(self, mock_collector):

View File

@@ -31,19 +31,19 @@ from watcher.tests.objects import utils as obj_utils
class TestStrategyContext(base.DbTestCase): class TestStrategyContext(base.DbTestCase):
def setUp(self): def setUp(self):
super(TestStrategyContext, self).setUp() super(TestStrategyContext, self).setUp()
self.audit_template = obj_utils. \ obj_utils.create_test_goal(self.context, id=1, name="DUMMY")
create_test_audit_template(self.context) audit_template = obj_utils.create_test_audit_template(
self.audit = obj_utils. \ self.context)
create_test_audit(self.context, self.audit = obj_utils.create_test_audit(
audit_template_id=self.audit_template.id) self.context, audit_template_id=audit_template.id)
strategy_context = DefaultStrategyContext() strategy_context = DefaultStrategyContext()
@mock.patch.object(DefaultStrategySelector, 'define_from_goal') @mock.patch.object(DefaultStrategySelector, 'select')
@mock.patch.object(CollectorManager, "get_cluster_model_collector", @mock.patch.object(CollectorManager, "get_cluster_model_collector",
mock.Mock()) mock.Mock())
def test_execute_strategy(self, mock_call): def test_execute_strategy(self, mock_call):
mock_call.return_value = DummyStrategy() mock_call.return_value = DummyStrategy()
solution = self.strategy_context.execute_strategy(self.audit.uuid, solution = self.strategy_context.execute_strategy(
self.context) self.audit.uuid, self.context)
self.assertIsInstance(solution, DefaultSolution) self.assertIsInstance(solution, DefaultSolution)

View File

@@ -13,37 +13,54 @@
# implied. # implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from mock import patch
import mock
from oslo_config import cfg from oslo_config import cfg
from watcher.common.exception import WatcherException from watcher.common import exception
from watcher.decision_engine.strategy.loading.default import \ from watcher.decision_engine.strategy.loading import default as default_loader
DefaultStrategyLoader from watcher.decision_engine.strategy.selection import (
from watcher.decision_engine.strategy.selection.default import \ default as default_selector)
DefaultStrategySelector from watcher.decision_engine.strategy import strategies
from watcher.tests.base import TestCase from watcher.tests import base
CONF = cfg.CONF CONF = cfg.CONF
class TestStrategySelector(TestCase): class TestStrategySelector(base.TestCase):
strategy_selector = DefaultStrategySelector()
@patch.object(DefaultStrategyLoader, 'load') def setUp(self):
def test_define_from_goal(self, mock_call): super(TestStrategySelector, self).setUp()
cfg.CONF.set_override('goals',
{"DUMMY": "fake"}, group='watcher_goals', @mock.patch.object(default_loader.DefaultStrategyLoader, 'load')
enforce_type=True) def test_select_with_strategy_name(self, m_load):
expected_goal = 'DUMMY' expected_goal = 'DUMMY'
expected_strategy = CONF.watcher_goals.goals[expected_goal] expected_strategy = "dummy"
self.strategy_selector.define_from_goal(expected_goal, osc=None) strategy_selector = default_selector.DefaultStrategySelector(
mock_call.assert_called_once_with(expected_strategy, osc=None) expected_goal, expected_strategy, osc=None)
strategy_selector.select()
m_load.assert_called_once_with(expected_strategy, osc=None)
@patch.object(DefaultStrategyLoader, 'load') @mock.patch.object(default_loader.DefaultStrategyLoader, 'load')
def test_define_from_goal_with_incorrect_mapping(self, mock_call): @mock.patch.object(default_loader.DefaultStrategyLoader, 'list_available')
cfg.CONF.set_override('goals', {}, group='watcher_goals', def test_select_with_goal_name_only(self, m_list_available, m_load):
enforce_type=True) m_list_available.return_value = {"dummy": strategies.DummyStrategy}
self.assertRaises(WatcherException, expected_goal = 'DUMMY'
self.strategy_selector.define_from_goal, expected_strategy = "dummy"
"DUMMY") strategy_selector = default_selector.DefaultStrategySelector(
self.assertEqual(0, mock_call.call_count) expected_goal, osc=None)
strategy_selector.select()
m_load.assert_called_once_with(expected_strategy, osc=None)
def test_select_non_existing_strategy(self):
strategy_selector = default_selector.DefaultStrategySelector(
"DUMMY", "NOT_FOUND")
self.assertRaises(exception.LoadingError, strategy_selector.select)
@mock.patch.object(default_loader.DefaultStrategyLoader, 'list_available')
def test_select_no_available_strategy_for_goal(self, m_list_available):
m_list_available.return_value = {}
strategy_selector = default_selector.DefaultStrategySelector(
"DUMMY")
self.assertRaises(exception.NoAvailableStrategyForGoal,
strategy_selector.select)

View File

@@ -137,7 +137,7 @@ class TestAuditTemplate(base.BaseInfraOptimTest):
new_name = 'my at new name %s' % uuid.uuid4() new_name = 'my at new name %s' % uuid.uuid4()
new_description = 'my new at description' new_description = 'my new at description'
new_host_aggregate = 10 new_host_aggregate = 10
new_goal = 'BASIC_CONSOLIDATION' new_goal = 'SERVER_CONSOLIDATION'
new_extra = {'key1': 'new-value1', 'key2': 'new-value2'} new_extra = {'key1': 'new-value1', 'key2': 'new-value2'}
patch = [{'path': '/name', patch = [{'path': '/name',

View File

@@ -30,7 +30,7 @@ CONF = config.CONF
class TestExecuteBasicStrategy(base.BaseInfraOptimScenarioTest): class TestExecuteBasicStrategy(base.BaseInfraOptimScenarioTest):
"""Tests for action plans""" """Tests for action plans"""
BASIC_GOAL = "BASIC_CONSOLIDATION" BASIC_GOAL = "SERVER_CONSOLIDATION"
@classmethod @classmethod
def skip_checks(cls): def skip_checks(cls):