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
commit e78610e7d1
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
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
==============================

View File

@ -17,6 +17,7 @@
# limitations under the License.
#
from oslo_config import cfg
from oslo_log import log
from watcher._i18n import _LW
@ -30,18 +31,29 @@ LOG = log.getLogger(__name__)
class DefaultPlanner(base.BasePlanner):
"""Default planner implementation
This implementation comes with basic rules with a fixed set of action types
that are weighted. An action having a lower weight will be scheduled before
the other ones.
This implementation comes with basic rules with a set of action types that
are weighted. An action having a lower weight will be scheduled before the
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,
'sleep': 1,
'change_nova_service_state': 2,
'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,
action_plan_id,
action_type,
@ -59,6 +71,7 @@ class DefaultPlanner(base.BasePlanner):
def schedule(self, context, audit_id, solution):
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)
actions = list(solution.actions)
@ -68,7 +81,7 @@ class DefaultPlanner(base.BasePlanner):
action_plan_id=action_plan.id,
action_type=action.get('action_type'),
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))
self._create_efficacy_indicators(

View File

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

View File

@ -39,3 +39,22 @@ class ConfFixture(fixtures.Fixture):
self.conf.set_default('verbose', True)
config.parse_args([], default_config_files=[])
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(
pbase.DefaultPlanner, "create_action",
wraps=default_planner.create_action) as m_create_action:
action_plan = default_planner.schedule(
self.context, audit.id, solution
)
default_planner.config.weights = {'migrate': 3}
action_plan = default_planner.schedule(self.context,
audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_action.call_count)
@ -107,9 +107,9 @@ class TestActionScheduling(base.DbTestCase):
with mock.patch.object(
pbase.DefaultPlanner, "create_action",
wraps=default_planner.create_action) as m_create_action:
action_plan = default_planner.schedule(
self.context, audit.id, solution
)
default_planner.config.weights = {'migrate': 3, 'nop': 0}
action_plan = default_planner.schedule(self.context,
audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(2, m_create_action.call_count)
# check order
@ -118,12 +118,45 @@ class TestActionScheduling(base.DbTestCase):
self.assertEqual("nop", actions[0].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):
def setUp(self):
super(TestDefaultPlanner, self).setUp()
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)
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',
'watcher_planner', 'nova_client', 'glance_client',
'cinder_client', 'ceilometer_client', 'neutron_client',
'watcher_clients_auth']
'watcher_clients_auth', 'watcher_planners.default']
def test_run_list_opts(self):
expected_sections = self.base_sections