Merge "Refactor watcher API for Action Plan Start"
This commit is contained in:
@@ -326,7 +326,8 @@ class ActionPlansController(rest.RestController):
|
|||||||
from the top-level resource ActionPlan."""
|
from the top-level resource ActionPlan."""
|
||||||
|
|
||||||
_custom_actions = {
|
_custom_actions = {
|
||||||
'detail': ['GET'],
|
'start': ['POST'],
|
||||||
|
'detail': ['GET']
|
||||||
}
|
}
|
||||||
|
|
||||||
def _get_action_plans_collection(self, marker, limit,
|
def _get_action_plans_collection(self, marker, limit,
|
||||||
@@ -535,7 +536,7 @@ class ActionPlansController(rest.RestController):
|
|||||||
if action_plan_to_update[field] != patch_val:
|
if action_plan_to_update[field] != patch_val:
|
||||||
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):
|
patch_val == objects.action_plan.State.PENDING):
|
||||||
launch_action_plan = True
|
launch_action_plan = True
|
||||||
|
|
||||||
@@ -560,3 +561,33 @@ class ActionPlansController(rest.RestController):
|
|||||||
pecan.request.context,
|
pecan.request.context,
|
||||||
action_plan_uuid)
|
action_plan_uuid)
|
||||||
return ActionPlan.convert_with_links(action_plan_to_update)
|
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)
|
||||||
|
|||||||
@@ -336,6 +336,10 @@ class DeleteError(Invalid):
|
|||||||
msg_fmt = _("Couldn't delete when state is '%(state)s'.")
|
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
|
# decision engine
|
||||||
|
|
||||||
class WorkflowExecutionException(WatcherException):
|
class WorkflowExecutionException(WatcherException):
|
||||||
|
|||||||
@@ -71,6 +71,17 @@ rules = [
|
|||||||
'method': 'PATCH'
|
'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'
|
||||||
|
}
|
||||||
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -137,6 +137,12 @@ class FunctionalTest(base.DbTestCase):
|
|||||||
headers=headers, extra_environ=extra_environ,
|
headers=headers, extra_environ=extra_environ,
|
||||||
status=status, method="put")
|
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,
|
def post_json(self, path, params, expect_errors=False, headers=None,
|
||||||
extra_environ=None, status=None):
|
extra_environ=None, status=None):
|
||||||
"""Sends simulated HTTP POST request to Pecan test app.
|
"""Sends simulated HTTP POST request to Pecan test app.
|
||||||
|
|||||||
@@ -363,6 +363,53 @@ class TestDelete(api_base.FunctionalTest):
|
|||||||
self.assertTrue(response.json['error_message'])
|
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):
|
class TestPatch(api_base.FunctionalTest):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -568,7 +615,6 @@ class TestPatchStateTransitionOk(api_base.FunctionalTest):
|
|||||||
'/action_plans/%s' % action_plan.uuid,
|
'/action_plans/%s' % action_plan.uuid,
|
||||||
[{'path': '/state', 'value': self.new_state, 'op': 'replace'}])
|
[{'path': '/state', 'value': self.new_state, 'op': 'replace'}])
|
||||||
updated_ap = self.get_json('/action_plans/%s' % action_plan.uuid)
|
updated_ap = self.get_json('/action_plans/%s' % action_plan.uuid)
|
||||||
|
|
||||||
self.assertNotEqual(self.new_state, initial_ap['state'])
|
self.assertNotEqual(self.new_state, initial_ap['state'])
|
||||||
self.assertEqual(self.new_state, updated_ap['state'])
|
self.assertEqual(self.new_state, updated_ap['state'])
|
||||||
self.assertEqual('application/json', response.content_type)
|
self.assertEqual('application/json', response.content_type)
|
||||||
@@ -642,4 +688,5 @@ class TestActionPlanPolicyEnforcementWithAdminContext(TestListActionPlan,
|
|||||||
"action_plan:detail": "rule:default",
|
"action_plan:detail": "rule:default",
|
||||||
"action_plan:get": "rule:default",
|
"action_plan:get": "rule:default",
|
||||||
"action_plan:get_all": "rule:default",
|
"action_plan:get_all": "rule:default",
|
||||||
"action_plan:update": "rule:default"})
|
"action_plan:update": "rule:default",
|
||||||
|
"action_plan:start": "rule:default"})
|
||||||
|
|||||||
Reference in New Issue
Block a user