Merge "Refactor watcher API for Action Plan Start"

This commit is contained in:
Zuul 2018-05-09 06:16:38 +00:00 committed by Gerrit Code Review
commit 5107cfa30f
5 changed files with 103 additions and 4 deletions

View File

@ -326,7 +326,8 @@ class ActionPlansController(rest.RestController):
from the top-level resource ActionPlan."""
_custom_actions = {
'detail': ['GET'],
'start': ['POST'],
'detail': ['GET']
}
def _get_action_plans_collection(self, marker, limit,
@ -535,7 +536,7 @@ class ActionPlansController(rest.RestController):
if action_plan_to_update[field] != patch_val:
action_plan_to_update[field] = patch_val
if (field == 'state'and
if (field == 'state' and
patch_val == objects.action_plan.State.PENDING):
launch_action_plan = True
@ -560,3 +561,33 @@ class ActionPlansController(rest.RestController):
pecan.request.context,
action_plan_uuid)
return ActionPlan.convert_with_links(action_plan_to_update)
@wsme_pecan.wsexpose(ActionPlan, types.uuid)
def start(self, action_plan_uuid, **kwargs):
"""Start an action_plan
:param action_plan_uuid: UUID of an action_plan.
"""
action_plan_to_start = api_utils.get_resource(
'ActionPlan', action_plan_uuid, eager=True)
context = pecan.request.context
policy.enforce(context, 'action_plan:start', action_plan_to_start,
action='action_plan:start')
if action_plan_to_start['state'] != \
objects.action_plan.State.RECOMMENDED:
raise Exception.StartError(
state=action_plan_to_start.state)
action_plan_to_start['state'] = objects.action_plan.State.PENDING
action_plan_to_start.save()
applier_client = rpcapi.ApplierAPI()
applier_client.launch_action_plan(pecan.request.context,
action_plan_uuid)
action_plan_to_start = objects.ActionPlan.get_by_uuid(
pecan.request.context, action_plan_uuid)
return ActionPlan.convert_with_links(action_plan_to_start)

View File

@ -336,6 +336,10 @@ class DeleteError(Invalid):
msg_fmt = _("Couldn't delete when state is '%(state)s'.")
class StartError(Invalid):
msg_fmt = _("Couldn't start when state is '%(state)s'.")
# decision engine
class WorkflowExecutionException(WatcherException):

View File

@ -71,6 +71,17 @@ rules = [
'method': 'PATCH'
}
]
),
policy.DocumentedRuleDefault(
name=ACTION_PLAN % 'start',
check_str=base.RULE_ADMIN_API,
description='Start an action plans.',
operations=[
{
'path': '/v1/action_plans/{action_plan_uuid}/action',
'method': 'POST'
}
]
)
]

View File

@ -137,6 +137,12 @@ class FunctionalTest(base.DbTestCase):
headers=headers, extra_environ=extra_environ,
status=status, method="put")
def post(self, *args, **kwargs):
headers = kwargs.pop('headers', {})
headers.setdefault('Accept', 'application/json')
kwargs['headers'] = headers
return self.app.post(*args, **kwargs)
def post_json(self, path, params, expect_errors=False, headers=None,
extra_environ=None, status=None):
"""Sends simulated HTTP POST request to Pecan test app.

View File

@ -363,6 +363,53 @@ class TestDelete(api_base.FunctionalTest):
self.assertTrue(response.json['error_message'])
class TestStart(api_base.FunctionalTest):
def setUp(self):
super(TestStart, self).setUp()
obj_utils.create_test_goal(self.context)
obj_utils.create_test_strategy(self.context)
obj_utils.create_test_audit(self.context)
self.action_plan = obj_utils.create_test_action_plan(
self.context, state=objects.action_plan.State.RECOMMENDED)
p = mock.patch.object(db_api.BaseConnection, 'update_action_plan')
self.mock_action_plan_update = p.start()
self.mock_action_plan_update.side_effect = \
self._simulate_rpc_action_plan_update
self.addCleanup(p.stop)
def _simulate_rpc_action_plan_update(self, action_plan):
action_plan.save()
return action_plan
@mock.patch('watcher.common.policy.enforce')
def test_start_action_plan_not_found(self, mock_policy):
mock_policy.return_value = True
uuid = utils.generate_uuid()
response = self.post('/v1/action_plans/%s/%s' %
(uuid, 'start'), expect_errors=True)
self.assertEqual(404, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
@mock.patch('watcher.common.policy.enforce')
def test_start_action_plan(self, mock_policy):
mock_policy.return_value = True
action = obj_utils.create_test_action(
self.context, id=1)
self.action_plan.state = objects.action_plan.State.SUCCEEDED
response = self.post('/v1/action_plans/%s/%s/'
% (self.action_plan.uuid, 'start'),
expect_errors=True)
self.assertEqual(200, response.status_int)
act_response = self.get_json(
'/actions/%s' % action.uuid,
expect_errors=True)
self.assertEqual(200, act_response.status_int)
self.assertEqual('PENDING', act_response.json['state'])
self.assertEqual('application/json', act_response.content_type)
class TestPatch(api_base.FunctionalTest):
def setUp(self):
@ -568,7 +615,6 @@ class TestPatchStateTransitionOk(api_base.FunctionalTest):
'/action_plans/%s' % action_plan.uuid,
[{'path': '/state', 'value': self.new_state, 'op': 'replace'}])
updated_ap = self.get_json('/action_plans/%s' % action_plan.uuid)
self.assertNotEqual(self.new_state, initial_ap['state'])
self.assertEqual(self.new_state, updated_ap['state'])
self.assertEqual('application/json', response.content_type)
@ -642,4 +688,5 @@ class TestActionPlanPolicyEnforcementWithAdminContext(TestListActionPlan,
"action_plan:detail": "rule:default",
"action_plan:get": "rule:default",
"action_plan:get_all": "rule:default",
"action_plan:update": "rule:default"})
"action_plan:update": "rule:default",
"action_plan:start": "rule:default"})