Passed
Pull Request — master (#3507)
by W
07:37 queued 01:55
created

ActionExecutionsController.put()   F

Complexity

Conditions 14

Size

Total Lines 86

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
dl 0
loc 86
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like ActionExecutionsController.put() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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 copy
17
import re
18
import httplib
19
import sys
20
import traceback
21
22
import jsonschema
23
from oslo_config import cfg
24
from six.moves import http_client
25
26
from st2api.controllers.base import BaseRestControllerMixin
27
from st2api.controllers.resource import ResourceController
28
from st2api.controllers.v1.executionviews import ExecutionViewsController
29
from st2api.controllers.v1.executionviews import SUPPORTED_FILTERS
30
from st2common import log as logging
31
from st2common.constants import action as action_constants
32
from st2common.exceptions import actionrunner as runner_exc
33
from st2common.exceptions import apivalidation as validation_exc
34
from st2common.exceptions import param as param_exc
35
from st2common.exceptions import trace as trace_exc
36
from st2common.models.api.action import LiveActionAPI
37
from st2common.models.api.action import LiveActionCreateAPI
38
from st2common.models.api.base import cast_argument_value
39
from st2common.models.api.execution import ActionExecutionAPI
40
from st2common.models.db.auth import UserDB
41
from st2common.persistence.liveaction import LiveAction
42
from st2common.persistence.execution import ActionExecution
43
from st2common.router import abort
44
from st2common.router import Response
45
from st2common.services import action as action_service
46
from st2common.services import executions as execution_service
47
from st2common.services import trace as trace_service
48
from st2common.services import rbac as rbac_service
49
from st2common.util import isotime
50
from st2common.util import action_db as action_utils
51
from st2common.util import param as param_utils
52
from st2common.util.jsonify import try_loads
53
from st2common.rbac.types import PermissionType
54
from st2common.rbac import utils as rbac_utils
55
from st2common.rbac.utils import assert_user_has_resource_db_permission
56
from st2common.rbac.utils import assert_user_is_admin_if_user_query_param_is_provided
57
58
__all__ = [
59
    'ActionExecutionsController'
60
]
61
62
LOG = logging.getLogger(__name__)
63
64
# Note: We initialize filters here and not in the constructor
65
SUPPORTED_EXECUTIONS_FILTERS = copy.deepcopy(SUPPORTED_FILTERS)
66
SUPPORTED_EXECUTIONS_FILTERS.update({
67
    'timestamp_gt': 'start_timestamp.gt',
68
    'timestamp_lt': 'start_timestamp.lt'
69
})
70
71
MONITOR_THREAD_EMPTY_Q_SLEEP_TIME = 5
72
MONITOR_THREAD_NO_WORKERS_SLEEP_TIME = 1
73
74
75
class ActionExecutionsControllerMixin(BaseRestControllerMixin):
76
    """
77
    Mixin class with shared methods.
78
    """
79
80
    model = ActionExecutionAPI
81
    access = ActionExecution
82
83
    # A list of attributes which can be specified using ?exclude_attributes filter
84
    valid_exclude_attributes = [
85
        'result',
86
        'trigger_instance'
87
    ]
88
89
    def _handle_schedule_execution(self, liveaction_api, requester_user, context_string=None,
90
                                   show_secrets=False):
91
        """
92
        :param liveaction: LiveActionAPI object.
93
        :type liveaction: :class:`LiveActionAPI`
94
        """
95
96
        if not requester_user:
97
            requester_user = UserDB(cfg.CONF.system_user.user)
98
99
        # Assert the permissions
100
        action_ref = liveaction_api.action
101
        action_db = action_utils.get_action_by_ref(action_ref)
102
        user = liveaction_api.user or requester_user.name
103
104
        assert_user_has_resource_db_permission(user_db=requester_user, resource_db=action_db,
105
                                               permission_type=PermissionType.ACTION_EXECUTE)
106
107
        # Validate that the authenticated user is admin if user query param is provided
108
        assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user,
109
                                                             user=user)
110
111
        try:
112
            return self._schedule_execution(liveaction=liveaction_api,
113
                                            requester_user=requester_user,
114
                                            user=user,
115
                                            context_string=context_string,
116
                                            show_secrets=show_secrets)
117
        except ValueError as e:
118
            LOG.exception('Unable to execute action.')
119
            abort(http_client.BAD_REQUEST, str(e))
120
        except jsonschema.ValidationError as e:
121
            LOG.exception('Unable to execute action. Parameter validation failed.')
122
            abort(http_client.BAD_REQUEST, re.sub("u'([^']*)'", r"'\1'", e.message))
123
        except trace_exc.TraceNotFoundException as e:
124
            abort(http_client.BAD_REQUEST, str(e))
125
        except validation_exc.ValueValidationException as e:
126
            raise e
127
        except Exception as e:
128
            LOG.exception('Unable to execute action. Unexpected error encountered.')
129
            abort(http_client.INTERNAL_SERVER_ERROR, str(e))
130
131
    def _schedule_execution(self, liveaction, requester_user, user=None, context_string=None,
132
                            show_secrets=False):
133
        # Initialize execution context if it does not exist.
134
        if not hasattr(liveaction, 'context'):
135
            liveaction.context = dict()
136
137
        liveaction.context['user'] = user
138
        LOG.debug('User is: %s' % liveaction.context['user'])
139
140
        # Retrieve other st2 context from request header.
141
        if context_string:
142
            context = try_loads(context_string)
143
            if not isinstance(context, dict):
144
                raise ValueError('Unable to convert st2-context from the headers into JSON.')
145
            liveaction.context.update(context)
146
147
        # Include RBAC context (if RBAC is available and enabled)
148
        if cfg.CONF.rbac.enable:
149
            user_db = UserDB(name=user)
150
            role_dbs = rbac_service.get_roles_for_user(user_db=user_db, include_remote=True)
151
            roles = [role_db.name for role_db in role_dbs]
152
            liveaction.context['rbac'] = {
153
                'user': user,
154
                'roles': roles
155
            }
156
157
        # Schedule the action execution.
158
        liveaction_db = LiveActionAPI.to_model(liveaction)
159
        liveaction_db, actionexecution_db = action_service.create_request(liveaction_db)
160
161
        action_db = action_utils.get_action_by_ref(liveaction_db.action)
162
        runnertype_db = action_utils.get_runnertype_by_name(action_db.runner_type['name'])
163
164
        try:
165
            liveaction_db.parameters = param_utils.render_live_params(
166
                runnertype_db.runner_parameters, action_db.parameters, liveaction_db.parameters,
167
                liveaction_db.context)
168
        except param_exc.ParamException:
169
            # By this point the execution is already in the DB therefore need to mark it failed.
170
            _, e, tb = sys.exc_info()
171
            action_service.update_status(
172
                liveaction=liveaction_db,
173
                new_status=action_constants.LIVEACTION_STATUS_FAILED,
174
                result={'error': str(e), 'traceback': ''.join(traceback.format_tb(tb, 20))})
175
            # Might be a good idea to return the actual ActionExecution rather than bubble up
176
            # the execption.
177
            raise validation_exc.ValueValidationException(str(e))
178
179
        liveaction_db = LiveAction.add_or_update(liveaction_db, publish=False)
180
181
        _, actionexecution_db = action_service.publish_request(liveaction_db, actionexecution_db)
182
        mask_secrets = self._get_mask_secrets(requester_user, show_secrets=show_secrets)
183
        execution_api = ActionExecutionAPI.from_model(actionexecution_db, mask_secrets=mask_secrets)
184
185
        return Response(json=execution_api, status=http_client.CREATED)
186
187
    def _get_result_object(self, id):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
188
        """
189
        Retrieve result object for the provided action execution.
190
191
        :param id: Action execution ID.
192
        :type id: ``str``
193
194
        :rtype: ``dict``
195
        """
196
        fields = ['result']
197
        action_exec_db = self.access.impl.model.objects.filter(id=id).only(*fields).get()
198
        return action_exec_db.result
199
200
    def _get_children(self, id_, requester_user, depth=-1, result_fmt=None,
201
                      show_secrets=False):
202
        # make sure depth is int. Url encoding will make it a string and needs to
203
        # be converted back in that case.
204
        depth = int(depth)
205
        LOG.debug('retrieving children for id: %s with depth: %s', id_, depth)
206
        descendants = execution_service.get_descendants(actionexecution_id=id_,
207
                                                        descendant_depth=depth,
208
                                                        result_fmt=result_fmt)
209
210
        mask_secrets = self._get_mask_secrets(requester_user, show_secrets=show_secrets)
211
        return [self.model.from_model(descendant, mask_secrets=mask_secrets) for
212
                descendant in descendants]
213
214
215
class BaseActionExecutionNestedController(ActionExecutionsControllerMixin, ResourceController):
216
    # Note: We need to override "get_one" and "get_all" to return 404 since nested controller
217
    # don't implement thos methods
218
219
    # ResourceController attributes
220
    query_options = {}
221
    supported_filters = {}
222
223
    def get_all(self):
224
        abort(httplib.NOT_FOUND)
225
226
    def get_one(self, id):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
227
        abort(httplib.NOT_FOUND)
228
229
230
class ActionExecutionChildrenController(BaseActionExecutionNestedController):
231
    def get_one(self, id, requester_user, depth=-1, result_fmt=None, show_secrets=False):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
Bug introduced by
Arguments number differs from overridden 'get_one' method
Loading history...
232
        """
233
        Retrieve children for the provided action execution.
234
235
        :rtype: ``list``
236
        """
237
238
        instance = self._get_by_id(resource_id=id)
239
240
        permission_type = PermissionType.EXECUTION_VIEW
241
        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
242
                                                          resource_db=instance,
243
                                                          permission_type=permission_type)
244
245
        return self._get_children(id_=id, depth=depth, result_fmt=result_fmt,
246
                                  requester_user=requester_user, show_secrets=show_secrets)
247
248
249
class ActionExecutionAttributeController(BaseActionExecutionNestedController):
250
    valid_exclude_attributes = ['action__pack', 'action__uid'] + \
251
        ActionExecutionsControllerMixin.valid_exclude_attributes
252
253
    def get(self, id, attribute, requester_user):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
254
        """
255
        Retrieve a particular attribute for the provided action execution.
256
257
        Handles requests:
258
259
            GET /executions/<id>/attribute/<attribute name>
260
261
        :rtype: ``dict``
262
        """
263
        fields = [attribute, 'action__pack', 'action__uid']
264
        fields = self._validate_exclude_fields(fields)
265
        action_exec_db = self.access.impl.model.objects.filter(id=id).only(*fields).get()
266
267
        permission_type = PermissionType.EXECUTION_VIEW
268
        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
269
                                                          resource_db=action_exec_db,
270
                                                          permission_type=permission_type)
271
272
        result = getattr(action_exec_db, attribute, None)
273
        return result
274
275
276
class ActionExecutionReRunController(ActionExecutionsControllerMixin, ResourceController):
277
    supported_filters = {}
278
    exclude_fields = [
279
        'result',
280
        'trigger_instance'
281
    ]
282
283
    class ExecutionSpecificationAPI(object):
284
        def __init__(self, parameters=None, tasks=None, reset=None, user=None):
285
            self.parameters = parameters or {}
286
            self.tasks = tasks or []
287
            self.reset = reset or []
288
            self.user = user
289
290
        def validate(self):
291
            if (self.tasks or self.reset) and self.parameters:
292
                raise ValueError('Parameters override is not supported when '
293
                                 're-running task(s) for a workflow.')
294
295
            if self.parameters:
296
                assert isinstance(self.parameters, dict)
297
298
            if self.tasks:
299
                assert isinstance(self.tasks, list)
300
301
            if self.reset:
302
                assert isinstance(self.reset, list)
303
304
            if list(set(self.reset) - set(self.tasks)):
305
                raise ValueError('List of tasks to reset does not match the tasks to rerun.')
306
307
            return self
308
309
    def post(self, spec_api, id, requester_user, no_merge=False, show_secrets=False):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
310
        """
311
        Re-run the provided action execution optionally specifying override parameters.
312
313
        Handles requests:
314
315
            POST /executions/<id>/re_run
316
        """
317
318
        if (spec_api.tasks or spec_api.reset) and spec_api.parameters:
319
            raise ValueError('Parameters override is not supported when '
320
                             're-running task(s) for a workflow.')
321
322
        if spec_api.parameters:
323
            assert isinstance(spec_api.parameters, dict)
324
325
        if spec_api.tasks:
326
            assert isinstance(spec_api.tasks, list)
327
328
        if spec_api.reset:
329
            assert isinstance(spec_api.reset, list)
330
331
        if list(set(spec_api.reset) - set(spec_api.tasks)):
332
            raise ValueError('List of tasks to reset does not match the tasks to rerun.')
333
334
        no_merge = cast_argument_value(value_type=bool, value=no_merge)
335
        existing_execution = self._get_one_by_id(id=id, exclude_fields=self.exclude_fields,
336
                                                 requester_user=requester_user,
337
                                                 permission_type=PermissionType.EXECUTION_VIEW)
338
339
        if spec_api.tasks and existing_execution.runner['name'] != 'mistral-v2':
340
            raise ValueError('Task option is only supported for Mistral workflows.')
341
342
        # Merge in any parameters provided by the user
343
        new_parameters = {}
344
        if not no_merge:
345
            new_parameters.update(getattr(existing_execution, 'parameters', {}))
346
        new_parameters.update(spec_api.parameters)
347
348
        # Create object for the new execution
349
        action_ref = existing_execution.action['ref']
350
351
        # Include additional option(s) for the execution
352
        context = {
353
            're-run': {
354
                'ref': id,
355
            }
356
        }
357
358
        if spec_api.tasks:
359
            context['re-run']['tasks'] = spec_api.tasks
360
361
        if spec_api.reset:
362
            context['re-run']['reset'] = spec_api.reset
363
364
        # Add trace to the new execution
365
        trace = trace_service.get_trace_db_by_action_execution(
366
            action_execution_id=existing_execution.id)
367
368
        if trace:
369
            context['trace_context'] = {'id_': str(trace.id)}
370
371
        new_liveaction_api = LiveActionCreateAPI(action=action_ref,
372
                                                 context=context,
373
                                                 parameters=new_parameters,
374
                                                 user=spec_api.user)
375
376
        return self._handle_schedule_execution(liveaction_api=new_liveaction_api,
377
                                               requester_user=requester_user,
378
                                               show_secrets=show_secrets)
379
380
381
class ActionExecutionsController(ActionExecutionsControllerMixin, ResourceController):
382
    """
383
        Implements the RESTful web endpoint that handles
384
        the lifecycle of ActionExecutions in the system.
385
    """
386
387
    # Nested controllers
388
    views = ExecutionViewsController()
389
390
    children = ActionExecutionChildrenController()
391
    attribute = ActionExecutionAttributeController()
392
    re_run = ActionExecutionReRunController()
393
394
    # ResourceController attributes
395
    query_options = {
396
        'sort': ['-start_timestamp', 'action.ref']
397
    }
398
    supported_filters = SUPPORTED_EXECUTIONS_FILTERS
399
    filter_transform_functions = {
400
        'timestamp_gt': lambda value: isotime.parse(value=value),
401
        'timestamp_lt': lambda value: isotime.parse(value=value)
402
    }
403
404
    def get_all(self, requester_user, exclude_attributes=None, sort=None, offset=0, limit=None,
405
                show_secrets=False, **raw_filters):
406
        """
407
        List all executions.
408
409
        Handles requests:
410
            GET /executions[?exclude_attributes=result,trigger_instance]
411
412
        :param exclude_attributes: Comma delimited string of attributes to exclude from the object.
413
        :type exclude_attributes: ``str``
414
        """
415
        if exclude_attributes:
416
            exclude_fields = exclude_attributes.split(',')
417
        else:
418
            exclude_fields = None
419
420
        exclude_fields = self._validate_exclude_fields(exclude_fields=exclude_fields)
421
422
        # Use a custom sort order when filtering on a timestamp so we return a correct result as
423
        # expected by the user
424
        query_options = None
425
        if raw_filters.get('timestamp_lt', None) or raw_filters.get('sort_desc', None):
426
            query_options = {'sort': ['-start_timestamp', 'action.ref']}
427
        elif raw_filters.get('timestamp_gt', None) or raw_filters.get('sort_asc', None):
428
            query_options = {'sort': ['+start_timestamp', 'action.ref']}
429
430
        from_model_kwargs = {
431
            'mask_secrets': self._get_mask_secrets(requester_user, show_secrets=show_secrets)
432
        }
433
        return self._get_action_executions(exclude_fields=exclude_fields,
434
                                           from_model_kwargs=from_model_kwargs,
435
                                           sort=sort,
436
                                           offset=offset,
437
                                           limit=limit,
438
                                           query_options=query_options,
439
                                           raw_filters=raw_filters)
440
441
    def get_one(self, id, requester_user, exclude_attributes=None, show_secrets=False):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
442
        """
443
        Retrieve a single execution.
444
445
        Handles requests:
446
            GET /executions/<id>[?exclude_attributes=result,trigger_instance]
447
448
        :param exclude_attributes: Comma delimited string of attributes to exclude from the object.
449
        :type exclude_attributes: ``str``
450
        """
451
        if exclude_attributes:
452
            exclude_fields = exclude_attributes.split(',')
453
        else:
454
            exclude_fields = None
455
456
        exclude_fields = self._validate_exclude_fields(exclude_fields=exclude_fields)
457
458
        from_model_kwargs = {
459
            'mask_secrets': self._get_mask_secrets(requester_user, show_secrets=show_secrets)
460
        }
461
        return self._get_one_by_id(id=id, exclude_fields=exclude_fields,
462
                                   requester_user=requester_user,
463
                                   from_model_kwargs=from_model_kwargs,
464
                                   permission_type=PermissionType.EXECUTION_VIEW)
465
466
    def post(self, liveaction_api, requester_user, context_string=None, show_secrets=False):
467
        return self._handle_schedule_execution(liveaction_api=liveaction_api,
468
                                               requester_user=requester_user,
469
                                               context_string=context_string,
470
                                               show_secrets=show_secrets)
471
472
    def put(self, id, liveaction_api, requester_user, show_secrets=False):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
473
        """
474
        Updates a single execution.
475
476
        Handles requests:
477
            PUT /executions/<id>
478
479
        """
480
        if not requester_user:
481
            requester_user = UserDB(cfg.CONF.system_user.user)
482
483
        from_model_kwargs = {
484
            'mask_secrets': self._get_mask_secrets(requester_user, show_secrets=show_secrets)
485
        }
486
487
        execution_api = self._get_one_by_id(id=id, requester_user=requester_user,
488
                                            from_model_kwargs=from_model_kwargs,
489
                                            permission_type=PermissionType.EXECUTION_STOP)
490
491
        if not execution_api:
492
            abort(http_client.NOT_FOUND, 'Execution with id %s not found.' % id)
493
494
        liveaction_id = execution_api.liveaction['id']
495
        if not liveaction_id:
496
            abort(http_client.INTERNAL_SERVER_ERROR,
497
                  'Execution object missing link to liveaction %s.' % liveaction_id)
498
499
        try:
500
            liveaction_db = LiveAction.get_by_id(liveaction_id)
501
        except:
502
            abort(http_client.INTERNAL_SERVER_ERROR,
503
                  'Execution object missing link to liveaction %s.' % liveaction_id)
504
505
        if liveaction_db.status in action_constants.LIVEACTION_COMPLETED_STATES:
506
            abort(http_client.BAD_REQUEST, 'Execution is already in completed state.')
507
508
        if (getattr(liveaction_api, 'result', None) is not None and
509
                liveaction_db.status in [
510
                    action_constants.LIVEACTION_STATUS_PAUSING,
511
                    action_constants.LIVEACTION_STATUS_PAUSED,
512
                    action_constants.LIVEACTION_STATUS_RESUMING]):
513
            LOG.warning(
514
                'The given result attribute of the liveaction update for %s '
515
                'is ignored for status change to pause or resume.',
516
                liveaction_id
517
            )
518
519
        try:
520
            if (liveaction_api.status == action_constants.LIVEACTION_STATUS_PAUSING or
521
                    liveaction_api.status == action_constants.LIVEACTION_STATUS_PAUSED):
522
                liveaction_db, actionexecution_db = action_service.request_pause(
523
                    liveaction_db, requester_user.name or cfg.CONF.system_user.user)
524
            elif liveaction_api.status == action_constants.LIVEACTION_STATUS_RESUMING:
525
                liveaction_db, actionexecution_db = action_service.request_resume(
526
                    liveaction_db, requester_user.name or cfg.CONF.system_user.user)
527
            else:
528
                liveaction_db = action_service.update_status(
529
                    liveaction_db,
530
                    liveaction_api.status,
531
                    result=getattr(liveaction_api, 'result', None)
532
                )
533
534
                actionexecution_db = ActionExecution.get(liveaction__id=str(liveaction_db.id))
535
        except runner_exc.InvalidActionRunnerOperationError as e:
536
            LOG.exception('Failed updating liveaction %s. %s', liveaction_db.id, str(e))
537
            abort(
538
                http_client.BAD_REQUEST,
539
                'Failed updating execution because the specific type of request is not supported.'
540
            )
541
        except runner_exc.UnexpectedActionExecutionStatusError as e:
542
            LOG.exception('Failed updating liveaction %s. %s', liveaction_db.id, str(e))
543
            abort(
544
                http_client.BAD_REQUEST,
545
                'Failed updating execution. %s' % str(e)
546
            )
547
        except Exception as e:
548
            LOG.exception('Failed updating liveaction %s. %s', liveaction_db.id, str(e))
549
            abort(
550
                http_client.INTERNAL_SERVER_ERROR,
551
                'Failed updating execution due to unexpected error.'
552
            )
553
554
        mask_secrets = self._get_mask_secrets(requester_user, show_secrets=show_secrets)
555
        execution_api = ActionExecutionAPI.from_model(actionexecution_db, mask_secrets=mask_secrets)
556
557
        return execution_api
558
559
    def delete(self, id, requester_user, show_secrets=False):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
560
        """
561
        Stops a single execution.
562
563
        Handles requests:
564
            DELETE /executions/<id>
565
566
        """
567
        if not requester_user:
568
            requester_user = UserDB(cfg.CONF.system_user.user)
569
570
        from_model_kwargs = {
571
            'mask_secrets': self._get_mask_secrets(requester_user, show_secrets=show_secrets)
572
        }
573
        execution_api = self._get_one_by_id(id=id, requester_user=requester_user,
574
                                            from_model_kwargs=from_model_kwargs,
575
                                            permission_type=PermissionType.EXECUTION_STOP)
576
577
        if not execution_api:
578
            abort(http_client.NOT_FOUND, 'Execution with id %s not found.' % id)
579
580
        liveaction_id = execution_api.liveaction['id']
581
        if not liveaction_id:
582
            abort(http_client.INTERNAL_SERVER_ERROR,
583
                  'Execution object missing link to liveaction %s.' % liveaction_id)
584
585
        try:
586
            liveaction_db = LiveAction.get_by_id(liveaction_id)
587
        except:
588
            abort(http_client.INTERNAL_SERVER_ERROR,
589
                  'Execution object missing link to liveaction %s.' % liveaction_id)
590
591
        if liveaction_db.status == action_constants.LIVEACTION_STATUS_CANCELED:
592
            LOG.info(
593
                'Action %s already in "canceled" state; \
594
                returning execution object.' % liveaction_db.id
595
            )
596
            return execution_api
597
598
        if liveaction_db.status not in action_constants.LIVEACTION_CANCELABLE_STATES:
599
            abort(http_client.OK, 'Action cannot be canceled. State = %s.' % liveaction_db.status)
600
601
        try:
602
            (liveaction_db, execution_db) = action_service.request_cancellation(
603
                liveaction_db, requester_user.name or cfg.CONF.system_user.user)
604
        except:
605
            LOG.exception('Failed requesting cancellation for liveaction %s.', liveaction_db.id)
606
            abort(http_client.INTERNAL_SERVER_ERROR, 'Failed canceling execution.')
607
608
        return ActionExecutionAPI.from_model(execution_db,
609
                                             mask_secrets=from_model_kwargs['mask_secrets'])
610
611
    def _get_action_executions(self, exclude_fields=None, sort=None, offset=0, limit=None,
612
                               query_options=None, raw_filters=None, from_model_kwargs=None):
613
        """
614
        :param exclude_fields: A list of object fields to exclude.
615
        :type exclude_fields: ``list``
616
        """
617
618
        if limit is None:
619
            limit = self.default_limit
620
621
        limit = int(limit)
622
623
        LOG.debug('Retrieving all action executions with filters=%s', raw_filters)
624
        return super(ActionExecutionsController, self)._get_all(exclude_fields=exclude_fields,
625
                                                                from_model_kwargs=from_model_kwargs,
626
                                                                sort=sort,
627
                                                                offset=offset,
628
                                                                limit=limit,
629
                                                                query_options=query_options,
630
                                                                raw_filters=raw_filters)
631
632
633
action_executions_controller = ActionExecutionsController()
634
action_execution_rerun_controller = ActionExecutionReRunController()
635
action_execution_attribute_controller = ActionExecutionAttributeController()
636
action_execution_children_controller = ActionExecutionChildrenController()
637