Passed
Push — master ( cf341e...dcdf4c )
by
unknown
03:07
created

ActionRunner.get_pack_ref()   A

Complexity

Conditions 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
c 0
b 0
f 0
dl 0
loc 10
rs 9.4285
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
import abc
17
18
import six
19
import yaml
20
from oslo_config import cfg
21
22
from st2common import log as logging
23
from st2common.constants import action as action_constants
24
from st2common.constants import pack as pack_constants
25
from st2common.exceptions.actionrunner import ActionRunnerCreateError
26
from st2common.util import action_db as action_utils
27
from st2common.util.loader import register_runner, register_callback_module
28
from st2common.util.api import get_full_public_api_url
29
from st2common.util.deprecation import deprecated
30
31
__all__ = [
32
    'ActionRunner',
33
    'AsyncActionRunner',
34
    'ShellRunnerMixin',
35
    'get_runner',
36
    'get_metadata'
37
]
38
39
40
LOG = logging.getLogger(__name__)
41
42
# constants to lookup in runner_parameters
43
RUNNER_COMMAND = 'cmd'
44
45
46
def get_runner(module_name, config=None):
47
    """
48
    Load the module and return an instance of the runner.
49
    """
50
51
    LOG.debug('Runner loading python module: %s', module_name)
52
53
    try:
54
        # TODO: Explore modifying this to support register_plugin
55
        module = register_runner(module_name)
56
    except Exception as e:
57
        msg = ('Failed to import runner module %s' % module_name)
58
        LOG.exception(msg)
59
60
        raise ActionRunnerCreateError('%s\n\n%s' % (msg, str(e)))
61
62
    LOG.debug('Instance of runner module: %s', module)
63
64
    if config:
65
        runner_kwargs = {'config': config}
66
    else:
67
        runner_kwargs = {}
68
69
    runner = module.get_runner(**runner_kwargs)
70
    LOG.debug('Instance of runner: %s', runner)
71
    return runner
72
73
74
def get_metadata(package_name):
75
    """
76
    Return runner related metadata for the provided runner package name.
77
78
    :rtype: ``list`` of ``dict``
79
    """
80
    import pkg_resources
81
82
    file_path = pkg_resources.resource_filename(package_name, 'runner.yaml')
83
84
    with open(file_path, 'r') as fp:
85
        content = fp.read()
86
87
    metadata = yaml.safe_load(content)
88
    return metadata
89
90
91
@six.add_metaclass(abc.ABCMeta)
92
class ActionRunner(object):
93
    """
94
        The interface that must be implemented by each StackStorm
95
        Action Runner implementation.
96
    """
97
98
    def __init__(self, runner_id):
99
        """
100
        :param id: Runner id.
101
        :type id: ``str``
102
        """
103
        self.runner_id = runner_id
104
105
        self.runner_type_db = None
106
        self.runner_parameters = None
107
        self.action = None
108
        self.action_name = None
109
        self.liveaction = None
110
        self.liveaction_id = None
111
        self.execution = None
112
        self.execution_id = None
113
        self.entry_point = None
114
        self.libs_dir_path = None
115
        self.context = None
116
        self.callback = None
117
        self.auth_token = None
118
        self.rerun_ex_ref = None
119
120
    def pre_run(self):
121
        runner_enabled = getattr(self.runner_type_db, 'enabled', True)
122
        runner_name = getattr(self.runner_type_db, 'name', 'unknown')
123
        if not runner_enabled:
124
            msg = ('Runner "%s" has been disabled by the administrator' %
125
                   (runner_name))
126
            raise ValueError(msg)
127
128
    # Run will need to take an action argument
129
    # Run may need result data argument
130
    @abc.abstractmethod
131
    def run(self, action_parameters):
132
        raise NotImplementedError()
133
134
    def pause(self):
135
        runner_name = getattr(self.runner_type_db, 'name', 'unknown')
136
        raise NotImplementedError('Pause is not supported for runner %s.' % runner_name)
137
138
    def resume(self):
139
        runner_name = getattr(self.runner_type_db, 'name', 'unknown')
140
        raise NotImplementedError('Resume is not supported for runner %s.' % runner_name)
141
142
    def cancel(self):
143
        return (
144
            action_constants.LIVEACTION_STATUS_CANCELED,
145
            self.liveaction.result,
146
            self.liveaction.context
147
        )
148
149
    def post_run(self, status, result):
150
        callback = self.callback or {}
151
152
        if callback and not (set(['url', 'source']) - set(callback.keys())):
0 ignored issues
show
Unused Code Coding Style introduced by
There is an unnecessary parenthesis after not.
Loading history...
153
            callback_url = callback['url']
154
            callback_module_name = callback['source']
155
156
            try:
157
                callback_module = register_callback_module(callback_module_name)
158
            except:
159
                LOG.exception('Failed importing callback module: %s', callback_module_name)
160
161
            callback_handler = callback_module.get_instance()
162
163
            callback_handler.callback(
164
                callback_url,
165
                self.context,
166
                status,
167
                result
168
            )
169
170
    @deprecated
171
    def get_pack_name(self):
172
        return self.get_pack_ref()
173
174
    def get_pack_ref(self):
175
        """
176
        Retrieve pack name for the action which is being currently executed.
177
178
        :rtype: ``str``
179
        """
180
        if self.action:
181
            return self.action.pack
182
183
        return pack_constants.DEFAULT_PACK_NAME
184
185
    def get_user(self):
186
        """
187
        Retrieve a name of the user which triggered this action execution.
188
189
        :rtype: ``str``
190
        """
191
        context = getattr(self, 'context', {}) or {}
192
        user = context.get('user', cfg.CONF.system_user.user)
193
194
        return user
195
196
    def _get_common_action_env_variables(self):
197
        """
198
        Retrieve common ST2_ACTION_ environment variables which will be available to the action.
199
200
        Note: Environment variables are prefixed with ST2_ACTION_* so they don't clash with CLI
201
        environment variables.
202
203
        :rtype: ``dict``
204
        """
205
        result = {}
206
        result['ST2_ACTION_PACK_NAME'] = self.get_pack_ref()
207
        result['ST2_ACTION_EXECUTION_ID'] = str(self.execution_id)
208
        result['ST2_ACTION_API_URL'] = get_full_public_api_url()
209
210
        if self.auth_token:
211
            result['ST2_ACTION_AUTH_TOKEN'] = self.auth_token.token
212
213
        return result
214
215
    def __str__(self):
216
        attrs = ', '.join(['%s=%s' % (k, v) for k, v in six.iteritems(self.__dict__)])
217
        return '%s@%s(%s)' % (self.__class__.__name__, str(id(self)), attrs)
218
219
220
@six.add_metaclass(abc.ABCMeta)
221
class AsyncActionRunner(ActionRunner):
222
    pass
223
224
225
class ShellRunnerMixin(object):
226
    """
227
    Class which contains utility functions to be used by shell runners.
228
    """
229
230
    def _transform_named_args(self, named_args):
231
        """
232
        Transform named arguments to the final form.
233
234
        :param named_args: Named arguments.
235
        :type named_args: ``dict``
236
237
        :rtype: ``dict``
238
        """
239
        if named_args:
240
            return {self._kwarg_op + k: v for (k, v) in six.iteritems(named_args)}
241
        return None
242
243
    def _get_script_args(self, action_parameters):
244
        """
245
        :param action_parameters: Action parameters.
246
        :type action_parameters: ``dict``
247
248
        :return: (positional_args, named_args)
249
        :rtype: (``str``, ``dict``)
250
        """
251
        # TODO: return list for positional args, command classes should escape it
252
        # and convert it to string
253
254
        is_script_run_as_cmd = self.runner_parameters.get(RUNNER_COMMAND, None)
255
256
        pos_args = ''
257
        named_args = {}
258
259
        if is_script_run_as_cmd:
260
            pos_args = self.runner_parameters.get(RUNNER_COMMAND, '')
261
            named_args = action_parameters
262
        else:
263
            pos_args, named_args = action_utils.get_args(action_parameters, self.action)
264
265
        return pos_args, named_args
266