Пример #1
0
class ActionExecutionsController(ResourceController):
    """
        Implements the RESTful web endpoint that handles
        the lifecycle of ActionExecutions in the system.
    """
    model = ActionExecutionAPI
    access = ActionExecution
    views = ExecutionViewsController()

    query_options = {'sort': ['-start_timestamp', 'action']}
    supported_filters = {
        'timestamp_gt': 'start_timestamp.gt',
        'timestamp_lt': 'start_timestamp.lt'
    }
    filter_transform_functions = {
        'timestamp_gt': lambda value: isotime.parse(value=value),
        'timestamp_lt': lambda value: isotime.parse(value=value)
    }

    def __init__(self):
        super(ActionExecutionsController, self).__init__()
        # Add common execution view supported filters
        self.supported_filters.update(SUPPORTED_FILTERS)

    @jsexpose(str)
    def get_one(self, id, *args, **kwargs):
        if args or kwargs:
            if args[0] == 'children':
                return self._get_children(id, **kwargs)
            else:
                msg = 'Unsupported id : %s, args: %s, kwargs: %s' % (id, args,
                                                                     kwargs)
                abort(http_client.BAD_REQUEST, msg)
        else:
            return super(ActionExecutionsController, self)._get_one(id)

    @jsexpose()
    def get_all(self, **kw):
        """
            List all actionexecutions.

            Handles requests:
                GET /actionexecutions/
        """
        LOG.info('GET all /actionexecutions/ with filters=%s', kw)
        return self._get_action_executions(**kw)

    @jsexpose(body=LiveActionAPI, status_code=http_client.CREATED)
    def post(self, execution):
        try:
            # Initialize execution context if it does not exist.
            if not hasattr(execution, 'context'):
                execution.context = dict()

            # Retrieve user context from the request header.
            user = pecan.request.headers.get('X-User-Name')
            if not user:
                user = cfg.CONF.system_user.user
            execution.context['user'] = user

            # Retrieve other st2 context from request header.
            if ('st2-context' in pecan.request.headers
                    and pecan.request.headers['st2-context']):
                context = jsonify.try_loads(
                    pecan.request.headers['st2-context'])
                if not isinstance(context, dict):
                    raise ValueError(
                        'Unable to convert st2-context from the headers into JSON.'
                    )
                execution.context.update(context)

            # Schedule the action execution.
            liveactiondb = LiveActionAPI.to_model(execution)
            _, actionexecutiondb = action_service.schedule(liveactiondb)
            return ActionExecutionAPI.from_model(actionexecutiondb)
        except ValueError as e:
            LOG.exception('Unable to execute action.')
            abort(http_client.BAD_REQUEST, str(e))
        except jsonschema.ValidationError as e:
            LOG.exception(
                'Unable to execute action. Parameter validation failed.')
            abort(http_client.BAD_REQUEST, str(e))
        except Exception as e:
            LOG.exception(
                'Unable to execute action. Unexpected error encountered.')
            abort(http_client.INTERNAL_SERVER_ERROR, str(e))

    @jsexpose()
    def options(self, *args, **kw):
        return

    def _get_action_executions(self, **kw):
        kw['limit'] = int(kw.get('limit', 100))

        LOG.debug('Retrieving all action liveactions with filters=%s', kw)
        return super(ActionExecutionsController, self)._get_all(**kw)

    def _get_children(self, id_, depth=-1):
        # make sure depth is int. Url encoding will make it a string and needs to
        # be converted back in that case.
        depth = int(depth)
        LOG.debug('retrieving children for id: %s with depth: %s', id_, depth)
        descendants = execution_service.get_descendants(actionexecution_id=id_,
                                                        descendant_depth=depth)
        return [
            self.model.from_model(descendant) for descendant in descendants
        ]
Пример #2
0
class ActionExecutionsController(ActionExecutionsControllerMixin, ResourceController):
    """
        Implements the RESTful web endpoint that handles
        the lifecycle of ActionExecutions in the system.
    """

    # Nested controllers
    views = ExecutionViewsController()

    children = ActionExecutionChildrenController()
    attribute = ActionExecutionAttributeController()
    re_run = ActionExecutionReRunController()

    # ResourceController attributes
    query_options = {
        'sort': ['-start_timestamp', 'action.ref']
    }
    supported_filters = SUPPORTED_EXECUTIONS_FILTERS
    filter_transform_functions = {
        'timestamp_gt': lambda value: isotime.parse(value=value),
        'timestamp_lt': lambda value: isotime.parse(value=value)
    }

    @request_user_has_permission(permission_type=PermissionType.EXECUTION_LIST)
    @jsexpose()
    def get_all(self, exclude_attributes=None, **kw):
        """
        List all executions.

        Handles requests:
            GET /executions[?exclude_attributes=result,trigger_instance]

        :param exclude_attributes: Comma delimited string of attributes to exclude from the object.
        :type exclude_attributes: ``str``
        """
        if exclude_attributes:
            exclude_fields = exclude_attributes.split(',')
        else:
            exclude_fields = None

        exclude_fields = self._validate_exclude_fields(exclude_fields=exclude_fields)

        # Use a custom sort order when filtering on a timestamp so we return a correct result as
        # expected by the user
        if 'timestamp_lt' in kw or 'sort_desc' in kw:
            query_options = {'sort': ['-start_timestamp', 'action.ref']}
            kw['query_options'] = query_options
        elif 'timestamp_gt' in kw or 'sort_asc' in kw:
            query_options = {'sort': ['+start_timestamp', 'action.ref']}
            kw['query_options'] = query_options

        return self._get_action_executions(exclude_fields=exclude_fields, **kw)

    @request_user_has_resource_db_permission(permission_type=PermissionType.EXECUTION_VIEW)
    @jsexpose(arg_types=[str])
    def get_one(self, id, exclude_attributes=None, **kwargs):
        """
        Retrieve a single execution.

        Handles requests:
            GET /executions/<id>[?exclude_attributes=result,trigger_instance]

        :param exclude_attributes: Comma delimited string of attributes to exclude from the object.
        :type exclude_attributes: ``str``
        """
        if exclude_attributes:
            exclude_fields = exclude_attributes.split(',')
        else:
            exclude_fields = None

        exclude_fields = self._validate_exclude_fields(exclude_fields=exclude_fields)

        return self._get_one(id=id, exclude_fields=exclude_fields)

    @jsexpose(body_cls=LiveActionCreateAPI, status_code=http_client.CREATED)
    def post(self, liveaction_api):
        return self._handle_schedule_execution(liveaction_api=liveaction_api)

    @request_user_has_resource_db_permission(permission_type=PermissionType.EXECUTION_STOP)
    @jsexpose(arg_types=[str])
    def delete(self, exec_id):
        """
        Stops a single execution.

        Handles requests:
            DELETE /executions/<id>

        """
        execution_api = self._get_one(id=exec_id)

        if not execution_api:
            abort(http_client.NOT_FOUND, 'Execution with id %s not found.' % exec_id)

        liveaction_id = execution_api.liveaction['id']
        if not liveaction_id:
            abort(http_client.INTERNAL_SERVER_ERROR,
                  'Execution object missing link to liveaction %s.' % liveaction_id)

        try:
            liveaction_db = LiveAction.get_by_id(liveaction_id)
        except:
            abort(http_client.INTERNAL_SERVER_ERROR,
                  'Execution object missing link to liveaction %s.' % liveaction_id)

        if liveaction_db.status == LIVEACTION_STATUS_CANCELED:
            abort(http_client.OK, 'Action is already in "canceled" state.')

        if liveaction_db.status not in LIVEACTION_CANCELABLE_STATES:
            abort(http_client.OK, 'Action cannot be canceled. State = %s.' % liveaction_db.status)

        try:
            (liveaction_db, execution_db) = action_service.request_cancellation(
                liveaction_db, get_requester())
        except:
            LOG.exception('Failed requesting cancellation for liveaction %s.', liveaction_db.id)
            abort(http_client.INTERNAL_SERVER_ERROR, 'Failed canceling execution.')

        from_model_kwargs = self._get_from_model_kwargs_for_request(request=pecan.request)

        return ActionExecutionAPI.from_model(execution_db, from_model_kwargs)

    @jsexpose()
    def options(self, *args, **kw):
        return

    def _get_action_executions(self, exclude_fields=None, **kw):
        """
        :param exclude_fields: A list of object fields to exclude.
        :type exclude_fields: ``list``
        """

        kw['limit'] = int(kw.get('limit', self.default_limit))

        LOG.debug('Retrieving all action executions with filters=%s', kw)
        return super(ActionExecutionsController, self)._get_all(exclude_fields=exclude_fields,
                                                                **kw)
Пример #3
0
class ActionExecutionsController(ActionExecutionsControllerMixin,
                                 ResourceController):
    """
        Implements the RESTful web endpoint that handles
        the lifecycle of ActionExecutions in the system.
    """

    # Nested controllers
    views = ExecutionViewsController()

    children = ActionExecutionChildrenController()
    attribute = ActionExecutionAttributeController()
    re_run = ActionExecutionReRunController()

    # ResourceController attributes
    query_options = {'sort': ['-start_timestamp', 'action.ref']}
    supported_filters = SUPPORTED_EXECUTIONS_FILTERS
    filter_transform_functions = {
        'timestamp_gt': lambda value: isotime.parse(value=value),
        'timestamp_lt': lambda value: isotime.parse(value=value)
    }

    @jsexpose()
    def get_all(self, exclude_attributes=None, **kw):
        """
        List all actionexecutions.

        Handles requests:
            GET /actionexecutions[?exclude_attributes=result,trigger_instance]

        :param exclude_attributes: Comma delimited string of attributes to exclude from the object.
        :type exclude_attributes: ``str``
        """
        if exclude_attributes:
            exclude_fields = exclude_attributes.split(',')
        else:
            exclude_fields = None

        exclude_fields = self._validate_exclude_fields(
            exclude_fields=exclude_fields)

        return self._get_action_executions(exclude_fields=exclude_fields, **kw)

    @jsexpose(arg_types=[str])
    def get_one(self, id, exclude_attributes=None, **kwargs):
        """
        Retrieve a single execution.

        Handles requests:
            GET /actionexecutions/<id>[?exclude_attributes=result,trigger_instance]

        :param exclude_attributes: Comma delimited string of attributes to exclude from the object.
        :type exclude_attributes: ``str``
        """
        if exclude_attributes:
            exclude_fields = exclude_attributes.split(',')
        else:
            exclude_fields = None

        exclude_fields = self._validate_exclude_fields(
            exclude_fields=exclude_fields)

        return self._get_one(id=id, exclude_fields=exclude_fields)

    @jsexpose(body_cls=LiveActionAPI, status_code=http_client.CREATED)
    def post(self, liveaction):
        return self._handle_schedule_execution(liveaction=liveaction)

    @jsexpose(arg_types=[str])
    def delete(self, exec_id):
        """
        Stops a single execution.

        Handles requests:
            DELETE /actionexecutions/<id>

        """
        execution_api = self._get_one(id=exec_id)

        if not execution_api:
            abort(http_client.NOT_FOUND,
                  'Execution with id %s not found.' % exec_id)
            return

        liveaction_id = execution_api.liveaction['id']
        if not liveaction_id:
            abort(
                http_client.INTERNAL_SERVER_ERROR,
                'Execution object missing link to liveaction %s.' %
                liveaction_id)

        try:
            liveaction_db = LiveAction.get_by_id(liveaction_id)
        except:
            abort(
                http_client.INTERNAL_SERVER_ERROR,
                'Execution object missing link to liveaction %s.' %
                liveaction_id)
            return

        if liveaction_db.status == LIVEACTION_STATUS_CANCELED:
            abort(http_client.OK, 'Action is already in "canceled" state.')

        if liveaction_db.status not in CANCELABLE_STATES:
            abort(
                http_client.OK, 'Action cannot be canceled. State = %s.' %
                liveaction_db.status)
            return

        liveaction_db.status = 'canceled'
        liveaction_db.end_timestamp = date_utils.get_datetime_utc_now()
        liveaction_db.result = {'message': 'Action canceled by user.'}
        try:
            LiveAction.add_or_update(liveaction_db)
        except:
            LOG.exception(
                'Failed updating status to canceled for liveaction %s.',
                liveaction_db.id)
            abort(http_client.INTERNAL_SERVER_ERROR,
                  'Failed canceling execution.')
            return

        execution_db = execution_service.update_execution(liveaction_db)
        from_model_kwargs = self._get_from_model_kwargs_for_request(
            request=pecan.request)
        return ActionExecutionAPI.from_model(execution_db, from_model_kwargs)

    @jsexpose()
    def options(self, *args, **kw):
        return

    def _get_action_executions(self, exclude_fields=None, **kw):
        """
        :param exclude_fields: A list of object fields to exclude.
        :type exclude_fields: ``list``
        """
        kw['limit'] = int(kw.get('limit', 100))

        LOG.debug('Retrieving all action executions with filters=%s', kw)
        return super(ActionExecutionsController,
                     self)._get_all(exclude_fields=exclude_fields, **kw)
class ActionExecutionsController(ActionExecutionsControllerMixin,
                                 ResourceController):
    """
        Implements the RESTful web endpoint that handles
        the lifecycle of ActionExecutions in the system.
    """

    # Nested controllers
    views = ExecutionViewsController()

    children = ActionExecutionChildrenController()
    attribute = ActionExecutionAttributeController()
    re_run = ActionExecutionReRunController()

    # ResourceController attributes
    query_options = {'sort': ['-start_timestamp', 'action.ref']}
    supported_filters = SUPPORTED_EXECUTIONS_FILTERS
    filter_transform_functions = {
        'timestamp_gt': lambda value: isotime.parse(value=value),
        'timestamp_lt': lambda value: isotime.parse(value=value)
    }

    def get_all(self,
                requester_user,
                exclude_attributes=None,
                sort=None,
                offset=0,
                limit=None,
                show_secrets=False,
                include_attributes=None,
                advanced_filters=None,
                **raw_filters):
        """
        List all executions.

        Handles requests:
            GET /executions[?exclude_attributes=result,trigger_instance]

        :param exclude_attributes: List of attributes to exclude from the object.
        :type exclude_attributes: ``list``
        """
        exclude_fields = self._validate_exclude_fields(
            exclude_fields=exclude_attributes)

        # Use a custom sort order when filtering on a timestamp so we return a correct result as
        # expected by the user
        query_options = None
        if raw_filters.get('timestamp_lt', None) or raw_filters.get(
                'sort_desc', None):
            query_options = {'sort': ['-start_timestamp', 'action.ref']}
        elif raw_filters.get('timestamp_gt', None) or raw_filters.get(
                'sort_asc', None):
            query_options = {'sort': ['+start_timestamp', 'action.ref']}

        from_model_kwargs = {
            'mask_secrets':
            self._get_mask_secrets(requester_user, show_secrets=show_secrets)
        }
        return self._get_action_executions(exclude_fields=exclude_fields,
                                           include_fields=include_attributes,
                                           from_model_kwargs=from_model_kwargs,
                                           sort=sort,
                                           offset=offset,
                                           limit=limit,
                                           query_options=query_options,
                                           raw_filters=raw_filters,
                                           advanced_filters=advanced_filters,
                                           requester_user=requester_user)

    def get_one(self,
                id,
                requester_user,
                exclude_attributes=None,
                show_secrets=False):
        """
        Retrieve a single execution.

        Handles requests:
            GET /executions/<id>[?exclude_attributes=result,trigger_instance]

        :param exclude_attributes: List of attributes to exclude from the object.
        :type exclude_attributes: ``list``
        """
        exclude_fields = self._validate_exclude_fields(
            exclude_fields=exclude_attributes)

        from_model_kwargs = {
            'mask_secrets':
            self._get_mask_secrets(requester_user, show_secrets=show_secrets)
        }

        # Special case for id == "last"
        if id == 'last':
            execution_db = ActionExecution.query().order_by('-id').limit(
                1).only('id').first()
            id = str(execution_db.id)

        return self._get_one_by_id(
            id=id,
            exclude_fields=exclude_fields,
            requester_user=requester_user,
            from_model_kwargs=from_model_kwargs,
            permission_type=PermissionType.EXECUTION_VIEW)

    def post(self,
             liveaction_api,
             requester_user,
             context_string=None,
             show_secrets=False):
        return self._handle_schedule_execution(liveaction_api=liveaction_api,
                                               requester_user=requester_user,
                                               context_string=context_string,
                                               show_secrets=show_secrets)

    def put(self, id, liveaction_api, requester_user, show_secrets=False):
        """
        Updates a single execution.

        Handles requests:
            PUT /executions/<id>

        """
        if not requester_user:
            requester_user = UserDB(cfg.CONF.system_user.user)

        from_model_kwargs = {
            'mask_secrets':
            self._get_mask_secrets(requester_user, show_secrets=show_secrets)
        }

        execution_api = self._get_one_by_id(
            id=id,
            requester_user=requester_user,
            from_model_kwargs=from_model_kwargs,
            permission_type=PermissionType.EXECUTION_STOP)

        if not execution_api:
            abort(http_client.NOT_FOUND,
                  'Execution with id %s not found.' % id)

        liveaction_id = execution_api.liveaction['id']
        if not liveaction_id:
            abort(
                http_client.INTERNAL_SERVER_ERROR,
                'Execution object missing link to liveaction %s.' %
                liveaction_id)

        try:
            liveaction_db = LiveAction.get_by_id(liveaction_id)
        except:
            abort(
                http_client.INTERNAL_SERVER_ERROR,
                'Execution object missing link to liveaction %s.' %
                liveaction_id)

        if liveaction_db.status in action_constants.LIVEACTION_COMPLETED_STATES:
            abort(http_client.BAD_REQUEST,
                  'Execution is already in completed state.')

        def update_status(liveaction_api, liveaction_db):
            status = liveaction_api.status
            result = getattr(liveaction_api, 'result', None)
            liveaction_db = action_service.update_status(
                liveaction_db, status, result)
            actionexecution_db = ActionExecution.get(
                liveaction__id=str(liveaction_db.id))
            return (liveaction_db, actionexecution_db)

        try:
            if (liveaction_db.status
                    == action_constants.LIVEACTION_STATUS_CANCELING
                    and liveaction_api.status
                    == action_constants.LIVEACTION_STATUS_CANCELED):
                if action_service.is_children_active(liveaction_id):
                    liveaction_api.status = action_constants.LIVEACTION_STATUS_CANCELING
                liveaction_db, actionexecution_db = update_status(
                    liveaction_api, liveaction_db)
            elif (liveaction_api.status
                  == action_constants.LIVEACTION_STATUS_CANCELING
                  or liveaction_api.status
                  == action_constants.LIVEACTION_STATUS_CANCELED):
                liveaction_db, actionexecution_db = action_service.request_cancellation(
                    liveaction_db, requester_user.name
                    or cfg.CONF.system_user.user)
            elif (liveaction_db.status
                  == action_constants.LIVEACTION_STATUS_PAUSING
                  and liveaction_api.status
                  == action_constants.LIVEACTION_STATUS_PAUSED):
                if action_service.is_children_active(liveaction_id):
                    liveaction_api.status = action_constants.LIVEACTION_STATUS_PAUSING
                liveaction_db, actionexecution_db = update_status(
                    liveaction_api, liveaction_db)
            elif (liveaction_api.status
                  == action_constants.LIVEACTION_STATUS_PAUSING
                  or liveaction_api.status
                  == action_constants.LIVEACTION_STATUS_PAUSED):
                liveaction_db, actionexecution_db = action_service.request_pause(
                    liveaction_db, requester_user.name
                    or cfg.CONF.system_user.user)
            elif liveaction_api.status == action_constants.LIVEACTION_STATUS_RESUMING:
                liveaction_db, actionexecution_db = action_service.request_resume(
                    liveaction_db, requester_user.name
                    or cfg.CONF.system_user.user)
            else:
                liveaction_db, actionexecution_db = update_status(
                    liveaction_api, liveaction_db)
        except runner_exc.InvalidActionRunnerOperationError as e:
            LOG.exception('Failed updating liveaction %s. %s',
                          liveaction_db.id, str(e))
            abort(http_client.BAD_REQUEST,
                  'Failed updating execution. %s' % str(e))
        except runner_exc.UnexpectedActionExecutionStatusError as e:
            LOG.exception('Failed updating liveaction %s. %s',
                          liveaction_db.id, str(e))
            abort(http_client.BAD_REQUEST,
                  'Failed updating execution. %s' % str(e))
        except Exception as e:
            LOG.exception('Failed updating liveaction %s. %s',
                          liveaction_db.id, str(e))
            abort(http_client.INTERNAL_SERVER_ERROR,
                  'Failed updating execution due to unexpected error.')

        mask_secrets = self._get_mask_secrets(requester_user,
                                              show_secrets=show_secrets)
        execution_api = ActionExecutionAPI.from_model(
            actionexecution_db, mask_secrets=mask_secrets)

        return execution_api

    def delete(self, id, requester_user, show_secrets=False):
        """
        Stops a single execution.

        Handles requests:
            DELETE /executions/<id>

        """
        if not requester_user:
            requester_user = UserDB(cfg.CONF.system_user.user)

        from_model_kwargs = {
            'mask_secrets':
            self._get_mask_secrets(requester_user, show_secrets=show_secrets)
        }
        execution_api = self._get_one_by_id(
            id=id,
            requester_user=requester_user,
            from_model_kwargs=from_model_kwargs,
            permission_type=PermissionType.EXECUTION_STOP)

        if not execution_api:
            abort(http_client.NOT_FOUND,
                  'Execution with id %s not found.' % id)

        liveaction_id = execution_api.liveaction['id']
        if not liveaction_id:
            abort(
                http_client.INTERNAL_SERVER_ERROR,
                'Execution object missing link to liveaction %s.' %
                liveaction_id)

        try:
            liveaction_db = LiveAction.get_by_id(liveaction_id)
        except:
            abort(
                http_client.INTERNAL_SERVER_ERROR,
                'Execution object missing link to liveaction %s.' %
                liveaction_id)

        if liveaction_db.status == action_constants.LIVEACTION_STATUS_CANCELED:
            LOG.info('Action %s already in "canceled" state; \
                returning execution object.' % liveaction_db.id)
            return execution_api

        if liveaction_db.status not in action_constants.LIVEACTION_CANCELABLE_STATES:
            abort(
                http_client.OK, 'Action cannot be canceled. State = %s.' %
                liveaction_db.status)

        try:
            (liveaction_db,
             execution_db) = action_service.request_cancellation(
                 liveaction_db, requester_user.name
                 or cfg.CONF.system_user.user)
        except:
            LOG.exception('Failed requesting cancellation for liveaction %s.',
                          liveaction_db.id)
            abort(http_client.INTERNAL_SERVER_ERROR,
                  'Failed canceling execution.')

        return ActionExecutionAPI.from_model(
            execution_db, mask_secrets=from_model_kwargs['mask_secrets'])

    def _get_action_executions(self,
                               exclude_fields=None,
                               include_fields=None,
                               sort=None,
                               offset=0,
                               limit=None,
                               advanced_filters=None,
                               query_options=None,
                               raw_filters=None,
                               from_model_kwargs=None,
                               requester_user=None):
        """
        :param exclude_fields: A list of object fields to exclude.
        :type exclude_fields: ``list``
        """

        if limit is None:
            limit = self.default_limit

        limit = int(limit)

        LOG.debug('Retrieving all action executions with filters=%s',
                  raw_filters)
        return super(ActionExecutionsController,
                     self)._get_all(exclude_fields=exclude_fields,
                                    include_fields=include_fields,
                                    from_model_kwargs=from_model_kwargs,
                                    sort=sort,
                                    offset=offset,
                                    limit=limit,
                                    query_options=query_options,
                                    raw_filters=raw_filters,
                                    advanced_filters=advanced_filters,
                                    requester_user=requester_user)
Пример #5
0
class ActionExecutionsController(ActionExecutionsControllerMixin,
                                 ResourceController):
    """
        Implements the RESTful web endpoint that handles
        the lifecycle of ActionExecutions in the system.
    """

    # Nested controllers
    views = ExecutionViewsController()

    children = ActionExecutionChildrenController()
    attribute = ActionExecutionAttributeController()
    re_run = ActionExecutionReRunController()

    # ResourceController attributes
    query_options = {'sort': ['-start_timestamp', 'action.ref']}
    supported_filters = SUPPORTED_EXECUTIONS_FILTERS
    filter_transform_functions = {
        'timestamp_gt': lambda value: isotime.parse(value=value),
        'timestamp_lt': lambda value: isotime.parse(value=value)
    }

    def get_all(self,
                exclude_attributes=None,
                sort=None,
                offset=0,
                limit=None,
                **raw_filters):
        """
        List all executions.

        Handles requests:
            GET /executions[?exclude_attributes=result,trigger_instance]

        :param exclude_attributes: Comma delimited string of attributes to exclude from the object.
        :type exclude_attributes: ``str``
        """
        if exclude_attributes:
            exclude_fields = exclude_attributes.split(',')
        else:
            exclude_fields = None

        exclude_fields = self._validate_exclude_fields(
            exclude_fields=exclude_fields)

        # Use a custom sort order when filtering on a timestamp so we return a correct result as
        # expected by the user
        query_options = None
        if raw_filters.get('timestamp_lt', None) or raw_filters.get(
                'sort_desc', None):
            query_options = {'sort': ['-start_timestamp', 'action.ref']}
        elif raw_filters.get('timestamp_gt', None) or raw_filters.get(
                'sort_asc', None):
            query_options = {'sort': ['+start_timestamp', 'action.ref']}

        return self._get_action_executions(exclude_fields=exclude_fields,
                                           sort=sort,
                                           offset=offset,
                                           limit=limit,
                                           query_options=query_options,
                                           raw_filters=raw_filters)

    def get_one(self, id, requester_user, exclude_attributes=None):
        """
        Retrieve a single execution.

        Handles requests:
            GET /executions/<id>[?exclude_attributes=result,trigger_instance]

        :param exclude_attributes: Comma delimited string of attributes to exclude from the object.
        :type exclude_attributes: ``str``
        """
        if exclude_attributes:
            exclude_fields = exclude_attributes.split(',')
        else:
            exclude_fields = None

        exclude_fields = self._validate_exclude_fields(
            exclude_fields=exclude_fields)

        return self._get_one_by_id(
            id=id,
            exclude_fields=exclude_fields,
            requester_user=requester_user,
            permission_type=PermissionType.EXECUTION_VIEW)

    def post(self,
             liveaction_api,
             requester_user=None,
             context_string=None,
             show_secrets=False):
        return self._handle_schedule_execution(liveaction_api=liveaction_api,
                                               requester_user=requester_user,
                                               context_string=context_string,
                                               show_secrets=show_secrets)

    def delete(self, id, requester_user, show_secrets=False):
        """
        Stops a single execution.

        Handles requests:
            DELETE /executions/<id>

        """
        if not requester_user:
            requester_user = UserDB(cfg.CONF.system_user.user)

        execution_api = self._get_one_by_id(
            id=id,
            requester_user=requester_user,
            permission_type=PermissionType.EXECUTION_STOP)

        if not execution_api:
            abort(http_client.NOT_FOUND,
                  'Execution with id %s not found.' % id)

        liveaction_id = execution_api.liveaction['id']
        if not liveaction_id:
            abort(
                http_client.INTERNAL_SERVER_ERROR,
                'Execution object missing link to liveaction %s.' %
                liveaction_id)

        try:
            liveaction_db = LiveAction.get_by_id(liveaction_id)
        except:
            abort(
                http_client.INTERNAL_SERVER_ERROR,
                'Execution object missing link to liveaction %s.' %
                liveaction_id)

        if liveaction_db.status == LIVEACTION_STATUS_CANCELED:
            LOG.info('Action %s already in "canceled" state; \
                returning execution object.' % liveaction_db.id)
            return execution_api

        if liveaction_db.status not in LIVEACTION_CANCELABLE_STATES:
            abort(
                http_client.OK, 'Action cannot be canceled. State = %s.' %
                liveaction_db.status)

        try:
            (liveaction_db,
             execution_db) = action_service.request_cancellation(
                 liveaction_db, requester_user.name
                 or cfg.CONF.system_user.user)
        except:
            LOG.exception('Failed requesting cancellation for liveaction %s.',
                          liveaction_db.id)
            abort(http_client.INTERNAL_SERVER_ERROR,
                  'Failed canceling execution.')

        return ActionExecutionAPI.from_model(execution_db,
                                             mask_secrets=(not show_secrets))

    def _get_action_executions(self,
                               exclude_fields=None,
                               sort=None,
                               offset=0,
                               limit=None,
                               query_options=None,
                               raw_filters=None):
        """
        :param exclude_fields: A list of object fields to exclude.
        :type exclude_fields: ``list``
        """

        if limit is None:
            limit = self.default_limit

        limit = int(limit)

        LOG.debug('Retrieving all action executions with filters=%s',
                  raw_filters)
        return super(ActionExecutionsController,
                     self)._get_all(exclude_fields=exclude_fields,
                                    sort=sort,
                                    offset=offset,
                                    limit=limit,
                                    query_options=query_options,
                                    raw_filters=raw_filters)
Пример #6
0
class ActionExecutionsController(ActionExecutionsControllerMixin,
                                 ResourceController):
    """
        Implements the RESTful web endpoint that handles
        the lifecycle of ActionExecutions in the system.
    """

    # Nested controllers
    views = ExecutionViewsController()

    children = ActionExecutionChildrenController()
    attribute = ActionExecutionAttributeController()

    query_options = {'sort': ['-start_timestamp', 'action']}
    supported_filters = {
        'timestamp_gt': 'start_timestamp.gt',
        'timestamp_lt': 'start_timestamp.lt'
    }
    filter_transform_functions = {
        'timestamp_gt': lambda value: isotime.parse(value=value),
        'timestamp_lt': lambda value: isotime.parse(value=value)
    }

    def __init__(self):
        super(ActionExecutionsController, self).__init__()
        # Add common execution view supported filters
        self.supported_filters.update(SUPPORTED_FILTERS)

    @jsexpose()
    def get_all(self, exclude_attributes=None, **kw):
        """
        List all actionexecutions.

        Handles requests:
            GET /actionexecutions/[?exclude_attributes=result,trigger_instance]

        :param exclude_attributes: Comma delimited string of attributes to exclude from the object.
        :type exclude_attributes: ``str``
        """
        if exclude_attributes:
            exclude_fields = exclude_attributes.split(',')
        else:
            exclude_fields = None

        exclude_fields = self._validate_exclude_fields(
            exclude_fields=exclude_fields)

        return self._get_action_executions(exclude_fields=exclude_fields, **kw)

    @jsexpose(arg_types=[str])
    def get_one(self, id, exclude_attributes=None, **kwargs):
        """
        Retrieve a single execution.

        Handles requests:
            GET /actionexecutions/<id>[?exclude_attributes=result,trigger_instance]

        :param exclude_attributes: Comma delimited string of attributes to exclude from the object.
        :type exclude_attributes: ``str``
        """
        if exclude_attributes:
            exclude_fields = exclude_attributes.split(',')
        else:
            exclude_fields = None

        exclude_fields = self._validate_exclude_fields(
            exclude_fields=exclude_fields)

        return self._get_one(id=id, exclude_fields=exclude_fields)

    @jsexpose(body_cls=LiveActionAPI, status_code=http_client.CREATED)
    def post(self, execution):
        try:
            # Initialize execution context if it does not exist.
            if not hasattr(execution, 'context'):
                execution.context = dict()

            # Retrieve username of the authed user (note - if auth is disabled, user will not be
            # set so we fall back to the system user name)
            request_token = pecan.request.context.get('token', None)

            if request_token:
                user = request_token.user
            else:
                user = cfg.CONF.system_user.user

            execution.context['user'] = user

            # Retrieve other st2 context from request header.
            if ('st2-context' in pecan.request.headers
                    and pecan.request.headers['st2-context']):
                context = jsonify.try_loads(
                    pecan.request.headers['st2-context'])
                if not isinstance(context, dict):
                    raise ValueError(
                        'Unable to convert st2-context from the headers into JSON.'
                    )
                execution.context.update(context)

            # Schedule the action execution.
            liveactiondb = LiveActionAPI.to_model(execution)
            _, actionexecutiondb = action_service.schedule(liveactiondb)
            return ActionExecutionAPI.from_model(actionexecutiondb)
        except ValueError as e:
            LOG.exception('Unable to execute action.')
            abort(http_client.BAD_REQUEST, str(e))
        except jsonschema.ValidationError as e:
            LOG.exception(
                'Unable to execute action. Parameter validation failed.')
            abort(http_client.BAD_REQUEST, str(e))
        except Exception as e:
            LOG.exception(
                'Unable to execute action. Unexpected error encountered.')
            abort(http_client.INTERNAL_SERVER_ERROR, str(e))

    @jsexpose(arg_types=[str])
    def delete(self, exec_id):
        """
        Stops a single execution.

        Handles requests:
            DELETE /actionexecutions/<id>

        """
        execution_api = self._get_one(id=exec_id)

        if not execution_api:
            abort(http_client.NOT_FOUND,
                  'Execution with id %s not found.' % exec_id)
            return

        liveaction_id = execution_api.liveaction['id']
        if not liveaction_id:
            abort(
                http_client.INTERNAL_SERVER_ERROR,
                'Execution object missing link to liveaction %s.' %
                liveaction_id)

        try:
            liveaction_db = LiveAction.get_by_id(liveaction_id)
        except:
            abort(
                http_client.INTERNAL_SERVER_ERROR,
                'Execution object missing link to liveaction %s.' %
                liveaction_id)
            return

        if liveaction_db.status == LIVEACTION_STATUS_CANCELED:
            abort(http_client.OK, 'Action is already in "canceled" state.')

        if liveaction_db.status not in CANCELABLE_STATES:
            abort(
                http_client.OK, 'Action cannot be canceled. State = %s.' %
                liveaction_db.status)
            return

        liveaction_db.status = 'canceled'
        liveaction_db.end_timestamp = isotime.add_utc_tz(
            datetime.datetime.utcnow())
        liveaction_db.result = {'message': 'Action canceled by user.'}
        try:
            LiveAction.add_or_update(liveaction_db)
        except:
            LOG.exception(
                'Failed updating status to canceled for liveaction %s.',
                liveaction_db.id)
            abort(http_client.INTERNAL_SERVER_ERROR,
                  'Failed canceling execution.')
            return

        execution_db = execution_service.update_execution(liveaction_db)
        return ActionExecutionAPI.from_model(execution_db)

    @jsexpose()
    def options(self, *args, **kw):
        return

    def _get_action_executions(self, exclude_fields=None, **kw):
        """
        :param exclude_fields: A list of object fields to exclude.
        :type exclude_fields: ``list``
        """
        kw['limit'] = int(kw.get('limit', 100))

        LOG.debug('Retrieving all action liveactions with filters=%s', kw)
        return super(ActionExecutionsController,
                     self)._get_all(exclude_fields=exclude_fields, **kw)