Merge "Make default Planner generic to handle new action"

This commit is contained in:
Jenkins
2016-07-12 07:35:21 +00:00
committed by Gerrit Code Review
6 changed files with 81 additions and 12 deletions

View File

@@ -205,6 +205,9 @@ By doing so, your action will be saved within the Watcher Database, ready to be
processed by the planner for creating an action plan which can then be executed processed by the planner for creating an action plan which can then be executed
by the Watcher Applier via its workflow engine. by the Watcher Applier via its workflow engine.
At the last, remember to add the action into the weights in ``watcher.conf``,
otherwise you will get an error when the action be referenced in a strategy.
Scheduling of an action plugin Scheduling of an action plugin
============================== ==============================

View File

@@ -17,6 +17,7 @@
# limitations under the License. # limitations under the License.
# #
from oslo_config import cfg
from oslo_log import log from oslo_log import log
from watcher._i18n import _LW from watcher._i18n import _LW
@@ -30,18 +31,29 @@ LOG = log.getLogger(__name__)
class DefaultPlanner(base.BasePlanner): class DefaultPlanner(base.BasePlanner):
"""Default planner implementation """Default planner implementation
This implementation comes with basic rules with a fixed set of action types This implementation comes with basic rules with a set of action types that
that are weighted. An action having a lower weight will be scheduled before are weighted. An action having a lower weight will be scheduled before the
the other ones. other ones. The set of action types can be specified by 'weights' in the
``watcher.conf``. You need to associate a different weight to all available
actions into the configuration file, otherwise you will get an error when
the new action will be referenced in the solution produced by a strategy.
""" """
priorities = { weights_dict = {
'nop': 0, 'nop': 0,
'sleep': 1, 'sleep': 1,
'change_nova_service_state': 2, 'change_nova_service_state': 2,
'migrate': 3, 'migrate': 3,
} }
@classmethod
def get_config_opts(cls):
return [cfg.DictOpt(
'weights',
help="These weights are used to schedule the actions",
default=cls.weights_dict),
]
def create_action(self, def create_action(self,
action_plan_id, action_plan_id,
action_type, action_type,
@@ -59,6 +71,7 @@ class DefaultPlanner(base.BasePlanner):
def schedule(self, context, audit_id, solution): def schedule(self, context, audit_id, solution):
LOG.debug('Create an action plan for the audit uuid: %s ', audit_id) LOG.debug('Create an action plan for the audit uuid: %s ', audit_id)
priorities = self.config.weights
action_plan = self._create_action_plan(context, audit_id, solution) action_plan = self._create_action_plan(context, audit_id, solution)
actions = list(solution.actions) actions = list(solution.actions)
@@ -68,7 +81,7 @@ class DefaultPlanner(base.BasePlanner):
action_plan_id=action_plan.id, action_plan_id=action_plan.id,
action_type=action.get('action_type'), action_type=action.get('action_type'),
input_parameters=action.get('input_parameters')) input_parameters=action.get('input_parameters'))
to_schedule.append((self.priorities[action.get('action_type')], to_schedule.append((priorities[action.get('action_type')],
json_action)) json_action))
self._create_efficacy_indicators( self._create_efficacy_indicators(

View File

@@ -50,6 +50,7 @@ class TestCase(BaseTestCase):
def setUp(self): def setUp(self):
super(TestCase, self).setUp() super(TestCase, self).setUp()
self.useFixture(conf_fixture.ConfReloadFixture())
self.app = testing.load_test_app(os.path.join( self.app = testing.load_test_app(os.path.join(
os.path.dirname(__file__), os.path.dirname(__file__),
'config.py' 'config.py'

View File

@@ -39,3 +39,22 @@ class ConfFixture(fixtures.Fixture):
self.conf.set_default('verbose', True) self.conf.set_default('verbose', True)
config.parse_args([], default_config_files=[]) config.parse_args([], default_config_files=[])
self.addCleanup(self.conf.reset) self.addCleanup(self.conf.reset)
class ConfReloadFixture(ConfFixture):
"""Fixture to manage reloads of conf settings."""
def __init__(self, conf=cfg.CONF):
self.conf = conf
self._original_parse_cli_opts = self.conf._parse_cli_opts
def _fake_parser(self, *args, **kw):
return cfg.ConfigOpts._parse_cli_opts(self.conf, [])
def _restore_parser(self):
self.conf._parse_cli_opts = self._original_parse_cli_opts
def setUp(self):
super(ConfReloadFixture, self).setUp()
self.conf._parse_cli_opts = self._fake_parser
self.addCleanup(self._restore_parser)

View File

@@ -76,9 +76,9 @@ class TestActionScheduling(base.DbTestCase):
with mock.patch.object( with mock.patch.object(
pbase.DefaultPlanner, "create_action", pbase.DefaultPlanner, "create_action",
wraps=default_planner.create_action) as m_create_action: wraps=default_planner.create_action) as m_create_action:
action_plan = default_planner.schedule( default_planner.config.weights = {'migrate': 3}
self.context, audit.id, solution action_plan = default_planner.schedule(self.context,
) audit.id, solution)
self.assertIsNotNone(action_plan.uuid) self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_action.call_count) self.assertEqual(1, m_create_action.call_count)
@@ -107,9 +107,9 @@ class TestActionScheduling(base.DbTestCase):
with mock.patch.object( with mock.patch.object(
pbase.DefaultPlanner, "create_action", pbase.DefaultPlanner, "create_action",
wraps=default_planner.create_action) as m_create_action: wraps=default_planner.create_action) as m_create_action:
action_plan = default_planner.schedule( default_planner.config.weights = {'migrate': 3, 'nop': 0}
self.context, audit.id, solution action_plan = default_planner.schedule(self.context,
) audit.id, solution)
self.assertIsNotNone(action_plan.uuid) self.assertIsNotNone(action_plan.uuid)
self.assertEqual(2, m_create_action.call_count) self.assertEqual(2, m_create_action.call_count)
# check order # check order
@@ -118,12 +118,45 @@ class TestActionScheduling(base.DbTestCase):
self.assertEqual("nop", actions[0].action_type) self.assertEqual("nop", actions[0].action_type)
self.assertEqual("migrate", actions[1].action_type) self.assertEqual("migrate", actions[1].action_type)
def test_schedule_actions_with_unknown_action(self):
default_planner = pbase.DefaultPlanner(mock.Mock())
audit = db_utils.create_test_audit(uuid=utils.generate_uuid())
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=mock.Mock())
parameters = {
"src_uuid_hypervisor": "server1",
"dst_uuid_hypervisor": "server2",
}
solution.add_action(action_type="migrate",
resource_id="b199db0c-1408-4d52-b5a5-5ca14de0ff36",
input_parameters=parameters)
solution.add_action(action_type="new_action_type",
resource_id="",
input_parameters={})
with mock.patch.object(
pbase.DefaultPlanner, "create_action",
wraps=default_planner.create_action) as m_create_action:
default_planner.config.weights = {'migrate': 0}
self.assertRaises(KeyError, default_planner.schedule,
self.context, audit.id, solution)
self.assertEqual(2, m_create_action.call_count)
class TestDefaultPlanner(base.DbTestCase): class TestDefaultPlanner(base.DbTestCase):
def setUp(self): def setUp(self):
super(TestDefaultPlanner, self).setUp() super(TestDefaultPlanner, self).setUp()
self.default_planner = pbase.DefaultPlanner(mock.Mock()) self.default_planner = pbase.DefaultPlanner(mock.Mock())
self.default_planner.config.weights = {
'nop': 0,
'sleep': 1,
'change_nova_service_state': 2,
'migrate': 3
}
obj_utils.create_test_audit_template(self.context) obj_utils.create_test_audit_template(self.context)
p = mock.patch.object(db_api.BaseConnection, 'create_action_plan') p = mock.patch.object(db_api.BaseConnection, 'create_action_plan')

View File

@@ -29,7 +29,7 @@ class TestListOpts(base.TestCase):
'api', 'watcher_decision_engine', 'watcher_applier', 'api', 'watcher_decision_engine', 'watcher_applier',
'watcher_planner', 'nova_client', 'glance_client', 'watcher_planner', 'nova_client', 'glance_client',
'cinder_client', 'ceilometer_client', 'neutron_client', 'cinder_client', 'ceilometer_client', 'neutron_client',
'watcher_clients_auth'] 'watcher_clients_auth', 'watcher_planners.default']
def test_run_list_opts(self): def test_run_list_opts(self):
expected_sections = self.base_sections expected_sections = self.base_sections