9da521b841
* Fixes engine race condition between start_workflow and on_task_result methods * Engine commands now have local and remote parts (in fact, "in tx" and "non tx") Closes-Bug: #1395679 Change-Id: Icd4aa1a546893b815c01bea23880cde139df2d1b
206 lines
6.0 KiB
Python
206 lines
6.0 KiB
Python
# Copyright 2014 - Mirantis, Inc.
|
|
# Copyright 2014 - StackStorm, 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.
|
|
|
|
import inspect
|
|
|
|
from stevedore import extension
|
|
|
|
from mistral.actions import action_factory
|
|
from mistral.actions import generator_factory
|
|
from mistral.db.v2 import api as db_api
|
|
from mistral import exceptions as exc
|
|
from mistral import expressions as expr
|
|
from mistral.openstack.common import log as logging
|
|
from mistral.services import actions
|
|
from mistral import utils
|
|
from mistral.utils import inspect_utils as i_utils
|
|
|
|
|
|
# TODO(rakhmerov): Make methods more consistent and granular.
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
ACTIONS_PATH = '../resources/actions'
|
|
_ACTION_CTX_PARAM = 'action_context'
|
|
|
|
|
|
# TODO(rakhmerov): It's confusing because we have std.xxx actions and actions
|
|
# TODO(rakhmerov): under '../resources/actions' that we also call standard.
|
|
def register_standard_actions():
|
|
action_paths = utils.get_file_list(ACTIONS_PATH)
|
|
|
|
for action_path in action_paths:
|
|
action_definition = open(action_path).read()
|
|
actions.update_actions(action_definition, scope='public')
|
|
|
|
|
|
def get_registered_actions(**kwargs):
|
|
return db_api.get_actions(**kwargs)
|
|
|
|
|
|
def register_action_class(name, action_class_str, attributes,
|
|
description=None, input_str=None):
|
|
values = {
|
|
'name': name,
|
|
'action_class': action_class_str,
|
|
'attributes': attributes,
|
|
'description': description,
|
|
'input': input_str,
|
|
'is_system': True,
|
|
'scope': 'public'
|
|
}
|
|
|
|
try:
|
|
LOG.debug("Registering action in DB: %s" % name)
|
|
|
|
db_api.create_action(values)
|
|
except exc.DBDuplicateEntry:
|
|
LOG.debug("Action %s already exists in DB." % name)
|
|
|
|
|
|
def _clear_system_action_db():
|
|
db_api.delete_actions(is_system=True)
|
|
|
|
|
|
def sync_db():
|
|
_clear_system_action_db()
|
|
|
|
register_action_classes()
|
|
register_standard_actions()
|
|
|
|
|
|
def _register_dynamic_action_classes():
|
|
for generator in generator_factory.all_generators():
|
|
actions = generator.create_actions()
|
|
|
|
module = generator.base_action_class.__module__
|
|
class_name = generator.base_action_class.__name__
|
|
|
|
action_class_str = "%s.%s" % (module, class_name)
|
|
|
|
for action in actions:
|
|
attrs = i_utils.get_public_fields(action['class'])
|
|
|
|
register_action_class(
|
|
action['name'],
|
|
action_class_str,
|
|
attrs,
|
|
action['description'],
|
|
action['arg_list']
|
|
)
|
|
|
|
|
|
def register_action_classes():
|
|
mgr = extension.ExtensionManager(
|
|
namespace='mistral.actions',
|
|
invoke_on_load=False
|
|
)
|
|
|
|
with db_api.transaction():
|
|
for name in mgr.names():
|
|
action_class_str = mgr[name].entry_point_target.replace(':', '.')
|
|
action_class = mgr[name].plugin
|
|
description = i_utils.get_docstring(action_class)
|
|
input_str = i_utils.get_arg_list_as_str(action_class.__init__)
|
|
|
|
attrs = i_utils.get_public_fields(mgr[name].plugin)
|
|
|
|
register_action_class(
|
|
name,
|
|
action_class_str,
|
|
attrs,
|
|
description=description,
|
|
input_str=input_str
|
|
)
|
|
|
|
_register_dynamic_action_classes()
|
|
|
|
|
|
def get_action_db(action_name):
|
|
return db_api.load_action(action_name)
|
|
|
|
|
|
def get_action_class(action_full_name):
|
|
"""Finds action class by full action name (i.e. 'namespace.action_name').
|
|
|
|
:param action_full_name: Full action name (that includes namespace).
|
|
:return: Action class or None if not found.
|
|
"""
|
|
action_db = get_action_db(action_full_name)
|
|
|
|
if action_db:
|
|
return action_factory.construct_action_class(
|
|
action_db.action_class,
|
|
action_db.attributes
|
|
)
|
|
|
|
|
|
def get_action_context(task_db):
|
|
return {
|
|
_ACTION_CTX_PARAM: {
|
|
'workflow_name': task_db.execution.wf_name,
|
|
'execution_id': task_db.execution_id,
|
|
'task_id': task_db.id,
|
|
'task_name': task_db.name,
|
|
'task_tags': task_db.tags
|
|
}
|
|
}
|
|
|
|
|
|
def has_action_context(action, attributes):
|
|
action_cls = action_factory.construct_action_class(action, attributes)
|
|
arg_spec = inspect.getargspec(action_cls.__init__)
|
|
|
|
return _ACTION_CTX_PARAM in arg_spec.args
|
|
|
|
|
|
def resolve_adhoc_action_name(workbook, action_name):
|
|
action_spec = workbook.get_action(action_name)
|
|
|
|
if not action_spec:
|
|
msg = 'Ad-hoc action class is not registered ' \
|
|
'[workbook=%s, action=%s, action_spec=%s]' % \
|
|
(workbook, action_name, action_spec)
|
|
raise exc.ActionException(msg)
|
|
|
|
base_cls = get_action_class(action_spec.clazz)
|
|
|
|
if not base_cls:
|
|
msg = 'Ad-hoc action base class is not registered ' \
|
|
'[workbook=%s, action=%s, base_class=%s]' % \
|
|
(workbook, action_name, base_cls)
|
|
raise exc.ActionException(msg)
|
|
|
|
return action_spec.clazz
|
|
|
|
|
|
def convert_adhoc_action_params(workbook, action_name, params):
|
|
base_params = workbook.get_action(action_name).base_parameters
|
|
|
|
if not base_params:
|
|
return {}
|
|
|
|
return expr.evaluate_recursively(base_params, params)
|
|
|
|
|
|
def convert_adhoc_action_result(workbook, action_name, result):
|
|
transformer = workbook.get_action(action_name).output
|
|
|
|
if not transformer:
|
|
return result
|
|
|
|
# Use base action result as a context for evaluating expressions.
|
|
return expr.evaluate_recursively(transformer, result)
|