Merge "Add action context to all action executions"

This commit is contained in:
Jenkins 2016-07-05 10:52:35 +00:00 committed by Gerrit Code Review
commit 7f1c1d0808
3 changed files with 176 additions and 26 deletions

View File

@ -122,22 +122,9 @@ class Action(object):
""" """
return True return True
def _create_action_execution(self, input_dict, runtime_ctx, desc=''): def _create_action_execution(self, input_dict, runtime_ctx,
# Assign the action execution ID here to minimize database calls. desc='', action_ex_id=None):
# Otherwise, the input property of the action execution DB object needs action_ex_id = action_ex_id or utils.generate_unicode_uuid()
# to be updated with the action execution ID after the action execution
# DB object is created.
action_ex_id = utils.generate_unicode_uuid()
# TODO(rakhmerov): Bad place, we probably need to push action context
# to all actions. It's related to
# https://blueprints.launchpad.net/mistral/+spec/mistral-custom-actions-api
if a_m.has_action_context(
self.action_def.action_class,
self.action_def.attributes or {}) and self.task_ex:
input_dict.update(
a_m.get_action_context(self.task_ex, action_ex_id)
)
values = { values = {
'id': action_ex_id, 'id': action_ex_id,
@ -212,10 +199,19 @@ class PythonAction(Action):
def schedule(self, input_dict, target, index=0, desc=''): def schedule(self, input_dict, target, index=0, desc=''):
assert not self.action_ex assert not self.action_ex
# Assign the action execution ID here to minimize database calls.
# Otherwise, the input property of the action execution DB object needs
# to be updated with the action execution ID after the action execution
# DB object is created.
action_ex_id = utils.generate_unicode_uuid()
self._insert_action_context(action_ex_id, input_dict)
self._create_action_execution( self._create_action_execution(
self._prepare_input(input_dict), self._prepare_input(input_dict),
self._prepare_runtime_context(index), self._prepare_runtime_context(index),
desc=desc desc=desc,
action_ex_id=action_ex_id
) )
scheduler.schedule_call( scheduler.schedule_call(
@ -233,8 +229,21 @@ class PythonAction(Action):
input_dict = self._prepare_input(input_dict) input_dict = self._prepare_input(input_dict)
runtime_ctx = self._prepare_runtime_context(index) runtime_ctx = self._prepare_runtime_context(index)
# Assign the action execution ID here to minimize database calls.
# Otherwise, the input property of the action execution DB object needs
# to be updated with the action execution ID after the action execution
# DB object is created.
action_ex_id = utils.generate_unicode_uuid()
self._insert_action_context(action_ex_id, input_dict, save=save)
if save: if save:
self._create_action_execution(input_dict, runtime_ctx, desc=desc) self._create_action_execution(
input_dict,
runtime_ctx,
desc=desc,
action_ex_id=action_ex_id
)
result = rpc.get_executor_client().run_action( result = rpc.get_executor_client().run_action(
self.action_ex.id if self.action_ex else None, self.action_ex.id if self.action_ex else None,
@ -286,6 +295,24 @@ class PythonAction(Action):
""" """
return {'index': index} return {'index': index}
def _insert_action_context(self, action_ex_id, input_dict, save=True):
"""Template method to prepare action context.
It inserts the action context in the input if required
runtime context.
"""
# we need to push action context to all actions. It's related to
# https://blueprints.launchpad.net/mistral/+spec/mistral-custom-actions-api
has_action_context = a_m.has_action_context(
self.action_def.action_class,
self.action_def.attributes or {}
)
if has_action_context:
input_dict.update(
a_m.get_action_context(self.task_ex, action_ex_id, save=save)
)
class AdHocAction(PythonAction): class AdHocAction(PythonAction):
"""Ad-hoc action.""" """Ad-hoc action."""

View File

@ -147,7 +147,8 @@ def get_action_class(action_full_name):
) )
def get_action_context(task_ex, action_ex_id): def get_action_context(task_ex, action_ex_id, save=True):
if task_ex:
return { return {
_ACTION_CTX_PARAM: { _ACTION_CTX_PARAM: {
'workflow_name': task_ex.workflow_name, 'workflow_name': task_ex.workflow_name,
@ -159,6 +160,30 @@ def get_action_context(task_ex, action_ex_id):
'callback_url': '/v2/action_executions/%s' % action_ex_id 'callback_url': '/v2/action_executions/%s' % action_ex_id
} }
} }
elif save:
return {
_ACTION_CTX_PARAM: {
'workflow_name': None,
'workflow_execution_id': None,
'task_id': None,
'task_name': None,
'task_tags': None,
'action_execution_id': action_ex_id,
'callback_url': '/v2/action_executions/%s' % action_ex_id
}
}
return {
_ACTION_CTX_PARAM: {
'workflow_name': None,
'workflow_execution_id': None,
'task_id': None,
'task_name': None,
'task_tags': None,
'action_execution_id': None,
'callback_url': None
}
}
def get_empty_action_context(): def get_empty_action_context():

View File

@ -91,3 +91,101 @@ class ActionContextTest(base.EngineTestCase):
proxies=None, proxies=None,
verify=None verify=None
) )
@mock.patch.object(
requests, 'request',
mock.MagicMock(return_value=test_base.FakeHTTPResponse('', 200, 'OK')))
def test_single_async_saved_action_context(self):
action_ex = self.engine.start_action(
'std.mistral_http',
{'url': 'https://wiki.openstack.org/wiki/mistral'},
save_result=True
)
action_context = {
'action_execution_id': action_ex.id,
'callback_url': '/v2/action_executions/%s' % action_ex.id,
'task_id': None,
'task_name': None,
'task_tags': None,
'workflow_name': None,
'workflow_execution_id': None
}
self.assertIn('action_context', action_ex.input)
self.assertEqual(action_context, action_ex.input['action_context'])
@mock.patch.object(
requests, 'request',
mock.MagicMock(return_value=test_base.FakeHTTPResponse('', 200, 'OK')))
def test_single_async_action_context(self):
action_ex = self.engine.start_action(
'std.mistral_http',
{'url': 'https://wiki.openstack.org/wiki/mistral'},
save_result=False
)
action_context = {
'action_execution_id': action_ex.id,
'callback_url': '/v2/action_executions/%s' % action_ex.id,
'task_id': None,
'task_name': None,
'task_tags': None,
'workflow_name': None,
'workflow_execution_id': None
}
self.assertIn('action_context', action_ex.input)
self.assertEqual(action_context, action_ex.input['action_context'])
@mock.patch.object(
requests, 'request',
mock.MagicMock(return_value=test_base.FakeHTTPResponse('', 200, 'OK')))
@mock.patch.object(
std_actions.MistralHTTPAction, 'is_sync',
mock.MagicMock(return_value=True))
def test_single_sync_saved_action_context(self):
action_ex = self.engine.start_action(
'std.mistral_http',
{'url': 'https://wiki.openstack.org/wiki/mistral'},
save_result=True
)
action_context = {
'action_execution_id': action_ex.id,
'callback_url': '/v2/action_executions/%s' % action_ex.id,
'task_id': None,
'task_name': None,
'task_tags': None,
'workflow_name': None,
'workflow_execution_id': None
}
self.assertIn('action_context', action_ex.input)
self.assertEqual(action_context, action_ex.input['action_context'])
@mock.patch.object(
requests, 'request',
mock.MagicMock(return_value=test_base.FakeHTTPResponse('', 200, 'OK')))
@mock.patch.object(
std_actions.MistralHTTPAction, 'is_sync',
mock.MagicMock(return_value=True))
def test_single_sync_action_context(self):
action_ex = self.engine.start_action(
'std.mistral_http',
{'url': 'https://wiki.openstack.org/wiki/mistral'},
save_result=False
)
action_context = {
'action_execution_id': None,
'callback_url': None,
'task_id': None,
'task_name': None,
'task_tags': None,
'workflow_name': None,
'workflow_execution_id': None
}
self.assertIn('action_context', action_ex.input)
self.assertEqual(action_context, action_ex.input['action_context'])