0ed4f05d63
When using the command `mistral run-action`, by default it will run the action synchronously unless the action can only be used asynchronously (action.is_sync returns False). When it runs synchronously the result of the action is not saved. When it is ran asynchronously the result is saved, as you need to retrieve it from Mistral afterwards. There is a argument on the command `--save-result` that can be used, it causes the action to be ran asynchronously and the result is saved. There is no way to have the action run synchronously and have the result be saved. This patch adds a new API parameter `run_sync` which will be exposed by the CLI as `--run-sync`. This new argument is intended to be used with `--save- result` but can be be used independently to ensure an action isn't ran synchronously by mistake. With the new argument the behaviour of the command is now as follows: * `mistral run-action` This behaves as it did before, it runs synchronously if it can, or it schedules for later and saves the action. * `mistral run-action --save-result` Again, this is the same as before, it schedules the action to run later and the action is saved. * `mistral run-action --run-sync` This is similar to having no argument passed, however, if you try to run an action that can't be used synchronously it will be rejected and an error is returned. * `mistral run-action --run-sync --save-result` The combination of the two arguments runs the actions synchronously and saves the result. If the action can't be ran synchronously then an error is returned. (This commit message uses the CLI to demonstrate the API usage, the new argument is added in in a mistralclient patch. It can of course be used directly via the API also.) Change-Id: I4417750fd5ff47016357655370410e9e7348cc25
164 lines
5.3 KiB
Python
164 lines
5.3 KiB
Python
# Copyright 2013 - Mirantis, Inc.
|
|
# Copyright 2015 - StackStorm, Inc.
|
|
# Copyright 2016 - Brocade Communications Systems, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
from oslo_log import log as logging
|
|
from osprofiler import profiler
|
|
|
|
from mistral import coordination
|
|
from mistral.db.v2 import api as db_api
|
|
from mistral.db.v2.sqlalchemy import models as db_models
|
|
from mistral.engine import action_handler
|
|
from mistral.engine import base
|
|
from mistral.engine import workflow_handler as wf_handler
|
|
from mistral import exceptions
|
|
from mistral import utils as u
|
|
from mistral.workflow import states
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
# Submodules of mistral.engine will throw NoSuchOptError if configuration
|
|
# options required at top level of this __init__.py are not imported before
|
|
# the submodules are referenced.
|
|
|
|
|
|
class DefaultEngine(base.Engine, coordination.Service):
|
|
def __init__(self, engine_client):
|
|
self._engine_client = engine_client
|
|
|
|
coordination.Service.__init__(self, 'engine_group')
|
|
|
|
@u.log_exec(LOG)
|
|
@profiler.trace('engine-start-workflow')
|
|
def start_workflow(self, wf_identifier, wf_input, description='',
|
|
**params):
|
|
with db_api.transaction():
|
|
wf_ex = wf_handler.start_workflow(
|
|
wf_identifier,
|
|
wf_input,
|
|
description,
|
|
params
|
|
)
|
|
|
|
return wf_ex.get_clone()
|
|
|
|
@u.log_exec(LOG)
|
|
def start_action(self, action_name, action_input,
|
|
description=None, **params):
|
|
with db_api.transaction():
|
|
action = action_handler.build_action_by_name(action_name)
|
|
|
|
action.validate_input(action_input)
|
|
|
|
sync = params.get('run_sync')
|
|
save = params.get('save_result')
|
|
target = params.get('target')
|
|
|
|
is_action_sync = action.is_sync(action_input)
|
|
|
|
if sync and not is_action_sync:
|
|
raise exceptions.InputException(
|
|
"Action does not support synchronous execution.")
|
|
|
|
if not sync and (save or not is_action_sync):
|
|
action.schedule(action_input, target)
|
|
|
|
return action.action_ex.get_clone()
|
|
|
|
output = action.run(action_input, target, save=False)
|
|
|
|
state = states.SUCCESS if output.is_success() else states.ERROR
|
|
|
|
if not save:
|
|
# Action execution is not created but we need to return similar
|
|
# object to a client anyway.
|
|
return db_models.ActionExecution(
|
|
name=action_name,
|
|
description=description,
|
|
input=action_input,
|
|
output=output.to_dict(),
|
|
state=state
|
|
)
|
|
|
|
action_ex_id = u.generate_unicode_uuid()
|
|
|
|
values = {
|
|
'id': action_ex_id,
|
|
'name': action_name,
|
|
'description': description,
|
|
'input': action_input,
|
|
'output': output.to_dict(),
|
|
'state': state,
|
|
}
|
|
|
|
return db_api.create_action_execution(values)
|
|
|
|
@u.log_exec(LOG)
|
|
@profiler.trace('engine-on-action-complete')
|
|
def on_action_complete(self, action_ex_id, result, wf_action=False):
|
|
with db_api.transaction():
|
|
if wf_action:
|
|
action_ex = db_api.get_workflow_execution(action_ex_id)
|
|
else:
|
|
action_ex = db_api.get_action_execution(action_ex_id)
|
|
|
|
action_handler.on_action_complete(action_ex, result)
|
|
|
|
return action_ex.get_clone()
|
|
|
|
@u.log_exec(LOG)
|
|
def pause_workflow(self, wf_ex_id):
|
|
with db_api.transaction():
|
|
wf_ex = db_api.get_workflow_execution(wf_ex_id)
|
|
|
|
wf_handler.pause_workflow(wf_ex)
|
|
|
|
return wf_ex.get_clone()
|
|
|
|
@u.log_exec(LOG)
|
|
def rerun_workflow(self, task_ex_id, reset=True, env=None):
|
|
with db_api.transaction():
|
|
task_ex = db_api.get_task_execution(task_ex_id)
|
|
|
|
wf_ex = task_ex.workflow_execution
|
|
|
|
wf_handler.rerun_workflow(wf_ex, task_ex, reset=reset, env=env)
|
|
|
|
return wf_ex.get_clone()
|
|
|
|
@u.log_exec(LOG)
|
|
def resume_workflow(self, wf_ex_id, env=None):
|
|
with db_api.transaction():
|
|
wf_ex = db_api.get_workflow_execution(wf_ex_id)
|
|
|
|
wf_handler.resume_workflow(wf_ex, env=env)
|
|
|
|
return wf_ex.get_clone()
|
|
|
|
@u.log_exec(LOG)
|
|
def stop_workflow(self, wf_ex_id, state, message=None):
|
|
with db_api.transaction():
|
|
wf_ex = db_api.get_workflow_execution(wf_ex_id)
|
|
|
|
wf_handler.stop_workflow(wf_ex, state, message)
|
|
|
|
return wf_ex.get_clone()
|
|
|
|
@u.log_exec(LOG)
|
|
def rollback_workflow(self, wf_ex_id):
|
|
# TODO(rakhmerov): Implement.
|
|
raise NotImplementedError
|