deb-mistral/mistral/services/action_manager.py

180 lines
5.2 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 oslo_log import log as logging
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.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',
run_in_tx=False
)
def get_registered_actions(**kwargs):
return db_api.get_action_definitions(**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_definition(values)
except exc.DBDuplicateEntryException:
LOG.debug("Action %s already exists in DB." % name)
def _clear_system_action_db():
db_api.delete_action_definitions(is_system=True)
def sync_db():
with db_api.transaction():
_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
)
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_definition(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_ex, action_ex_id):
return {
_ACTION_CTX_PARAM: {
'workflow_name': task_ex.workflow_name,
'workflow_execution_id': task_ex.workflow_execution_id,
'task_id': task_ex.id,
'task_name': task_ex.name,
'task_tags': task_ex.tags,
'action_execution_id': action_ex_id,
'callback_url': '/v2/action_executions/%s' % action_ex_id
}
}
def get_empty_action_context():
return {
_ACTION_CTX_PARAM: {}
}
def _has_argument(action, attributes, argument_name):
action_cls = action_factory.construct_action_class(action, attributes)
arg_spec = inspect.getargspec(action_cls.__init__)
return argument_name in arg_spec.args
def has_action_context(action, attributes):
return _has_argument(action, attributes, _ACTION_CTX_PARAM)