086a3d43fa
* It turned out that Mistral used a lot of memory because it used cached DB lookups for task executions (only task executions in a terminal state get cached) and the maximum size of the cache was too big, 20000 entries. One task execution in certain cases may take a lot of memory (e.g. several megabytes) so 20000 objects make memory footprint huge. Additionally, when a workflow completes we need to invalidate coresponding task executions in the cache. This didn't happen before this patch. * This patch fixes the aforementioned issues by using partial invalidation of the cache and setting smaller cache size. * Fixed "Starting workflow .." log statement to not print the entire structure of the workflow definition into the workflow log, only its name and input parameters * Minor style fixes Change-Id: I0ee300f631a4bdfa2f618c2a10048267f27b3345 Closes-bug: #1664864
156 lines
5.2 KiB
Python
156 lines
5.2 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 osprofiler import profiler
|
|
|
|
from mistral.db import utils as db_utils
|
|
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 action_queue
|
|
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
|
|
|
|
|
|
# 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):
|
|
@action_queue.process
|
|
@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()
|
|
|
|
@action_queue.process
|
|
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)
|
|
|
|
@db_utils.retry_on_deadlock
|
|
@action_queue.process
|
|
@profiler.trace('engine-on-action-complete', hide_args=True)
|
|
def on_action_complete(self, action_ex_id, result, wf_action=False,
|
|
async_=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()
|
|
|
|
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()
|
|
|
|
@action_queue.process
|
|
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()
|
|
|
|
@action_queue.process
|
|
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()
|
|
|
|
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()
|
|
|
|
def rollback_workflow(self, wf_ex_id):
|
|
# TODO(rakhmerov): Implement.
|
|
raise NotImplementedError
|