Ejemplo n.º 1
0
    def _has_active_tasks(self, liveaction_db, mistral_wf_state,
                          mistral_tasks):
        # Identify if there are any active tasks in Mistral.
        active_mistral_tasks = len(
            [t for t in mistral_tasks if t['state'] in ACTIVE_STATES]) > 0

        active_st2_tasks = False
        execution = ActionExecution.get(liveaction__id=str(liveaction_db.id))

        for child_exec_id in execution.children:
            child_exec = ActionExecution.get(id=child_exec_id)

            # Catch exception where a child is requested twice due to st2mistral retrying
            # from a st2 API connection failure. The first child will be stuck in requested
            # while the mistral workflow is already completed.
            if (mistral_wf_state in DONE_STATES and child_exec.status
                    == action_constants.LIVEACTION_STATUS_REQUESTED):
                continue

            if (child_exec.status
                    not in action_constants.LIVEACTION_COMPLETED_STATES
                    and child_exec.status !=
                    action_constants.LIVEACTION_STATUS_PAUSED):
                active_st2_tasks = True
                break

        if active_mistral_tasks:
            LOG.info('There are active mistral tasks for %s.',
                     str(liveaction_db.id))

        if active_st2_tasks:
            LOG.info('There are active st2 tasks for %s.',
                     str(liveaction_db.id))

        return active_mistral_tasks or active_st2_tasks
Ejemplo n.º 2
0
Archivo: base.py Proyecto: Pulsant/st2
    def _get_runner(self, runnertype_db, action_db, liveaction_db):
        runner = get_runner(runnertype_db.runner_module)

        resolved_entry_point = self._get_entry_point_abs_path(action_db.pack,
                                                              action_db.entry_point)

        runner.runner_type_db = runnertype_db
        runner.container_service = RunnerContainerService()
        runner.action = action_db
        runner.action_name = action_db.name
        runner.liveaction = liveaction_db
        runner.liveaction_id = str(liveaction_db.id)
        runner.execution = ActionExecution.get(liveaction__id=runner.liveaction_id)
        runner.execution_id = str(runner.execution.id)
        runner.entry_point = resolved_entry_point
        runner.context = getattr(liveaction_db, 'context', dict())
        runner.callback = getattr(liveaction_db, 'callback', dict())
        runner.libs_dir_path = self._get_action_libs_abs_path(action_db.pack,
                                                              action_db.entry_point)

        # For re-run, get the ActionExecutionDB in which the re-run is based on.
        rerun_ref_id = runner.context.get('re-run', {}).get('ref')
        runner.rerun_ex_ref = ActionExecution.get(id=rerun_ref_id) if rerun_ref_id else None

        return runner
Ejemplo n.º 3
0
    def _get_runner(self, runnertype_db, action_db, liveaction_db):
        runner = get_runner(runnertype_db.runner_module)

        resolved_entry_point = self._get_entry_point_abs_path(action_db.pack,
                                                              action_db.entry_point)

        runner.runner_type_db = runnertype_db
        runner.container_service = RunnerContainerService()
        runner.action = action_db
        runner.action_name = action_db.name
        runner.liveaction = liveaction_db
        runner.liveaction_id = str(liveaction_db.id)
        runner.execution = ActionExecution.get(liveaction__id=runner.liveaction_id)
        runner.execution_id = str(runner.execution.id)
        runner.entry_point = resolved_entry_point
        runner.context = getattr(liveaction_db, 'context', dict())
        runner.callback = getattr(liveaction_db, 'callback', dict())
        runner.libs_dir_path = self._get_action_libs_abs_path(action_db.pack,
                                                              action_db.entry_point)

        # For re-run, get the ActionExecutionDB in which the re-run is based on.
        rerun_ref_id = runner.context.get('re-run', {}).get('ref')
        runner.rerun_ex_ref = ActionExecution.get(id=rerun_ref_id) if rerun_ref_id else None

        return runner
Ejemplo n.º 4
0
Archivo: query.py Proyecto: nzlosh/st2
    def _has_active_tasks(self, liveaction_db, mistral_wf_state, mistral_tasks):
        # Identify if there are any active tasks in Mistral.
        active_mistral_tasks = len([t for t in mistral_tasks if t['state'] in ACTIVE_STATES]) > 0

        active_st2_tasks = False
        execution = ActionExecution.get(liveaction__id=str(liveaction_db.id))

        for child_exec_id in execution.children:
            child_exec = ActionExecution.get(id=child_exec_id)

            # Catch exception where a child is requested twice due to st2mistral retrying
            # from a st2 API connection failure. The first child will be stuck in requested
            # while the mistral workflow is already completed.
            if (mistral_wf_state in DONE_STATES and
                    child_exec.status == action_constants.LIVEACTION_STATUS_REQUESTED):
                continue

            if (child_exec.status not in action_constants.LIVEACTION_COMPLETED_STATES and
                    child_exec.status != action_constants.LIVEACTION_STATUS_PAUSED):
                active_st2_tasks = True
                break

        if active_mistral_tasks:
            LOG.info('There are active mistral tasks for %s.', str(liveaction_db.id))

        if active_st2_tasks:
            LOG.info('There are active st2 tasks for %s.', str(liveaction_db.id))

        return active_mistral_tasks or active_st2_tasks
Ejemplo n.º 5
0
    def _has_active_tasks(self, liveaction_db, mistral_tasks):
        # Identify if there are any active tasks in Mistral.
        active_mistral_tasks = len(
            [t for t in mistral_tasks if t['state'] in ACTIVE_STATES]) > 0

        active_st2_tasks = False
        execution = ActionExecution.get(liveaction__id=str(liveaction_db.id))

        for child_exec_id in execution.children:
            child_exec = ActionExecution.get(id=child_exec_id)

            if (child_exec.status
                    not in action_constants.LIVEACTION_COMPLETED_STATES
                    and child_exec.status !=
                    action_constants.LIVEACTION_STATUS_PAUSED):
                active_st2_tasks = True
                break

        if active_mistral_tasks:
            LOG.info('There are active mistral tasks for %s.',
                     str(liveaction_db.id))

        if active_st2_tasks:
            LOG.info('There are active st2 tasks for %s.',
                     str(liveaction_db.id))

        return active_mistral_tasks or active_st2_tasks
Ejemplo n.º 6
0
    def run(self, action_parameters):

        liveaction_db = action_utils.get_liveaction_by_id(self.liveaction_id)
        exc = ActionExecution.get(liveaction__id=str(liveaction_db.id))

        # Assemble and dispatch trigger
        trigger_ref = ResourceReference.to_string_reference(
            pack=INQUIRY_TRIGGER['pack'],
            name=INQUIRY_TRIGGER['name']
        )
        trigger_payload = {
            "id": str(exc.id),
            "route": self.route
        }
        self.trigger_dispatcher.dispatch(trigger_ref, trigger_payload)

        # We only want to request a pause if this has a parent
        if liveaction_db.context.get("parent"):

            # Get the root liveaction and request that it pauses
            root_liveaction = action_service.get_root_liveaction(liveaction_db)
            action_service.request_pause(
                root_liveaction,
                self.context.get('user', None)
            )

        result = {
            "schema": self.schema,
            "roles": self.roles_param,
            "users": self.users_param,
            "route": self.route,
            "ttl": self.ttl
        }
        return (LIVEACTION_STATUS_PENDING, result, None)
Ejemplo n.º 7
0
    def _resume_action(self, liveaction_db):
        action_execution_db = ActionExecution.get(liveaction__id=str(liveaction_db.id))
        extra = {'action_execution_db': action_execution_db, 'liveaction_db': liveaction_db}
        LOG.audit('Resuming action execution.', extra=extra)

        # the extra field will not be shown in non-audit logs so temporarily log at info.
        LOG.info('Dispatched {~}action_execution: %s / {~}live_action: %s with "%s" status.',
                 action_execution_db.id, liveaction_db.id, liveaction_db.status)

        try:
            result = self.container.dispatch(liveaction_db)
            LOG.debug('Runner dispatch produced result: %s', result)
        except:
            _, ex, tb = sys.exc_info()
            extra['error'] = str(ex)
            LOG.info('Failed to resume action execution %s.' % (liveaction_db.id), extra=extra)
            raise

        # Cascade the resume upstream if action execution is child of an orquesta workflow.
        # The action service request_resume function is not used here because we do not want
        # other peer subworkflows to be resumed.
        if 'orquesta' in action_execution_db.context and 'parent' in action_execution_db.context:
            wf_svc.handle_action_execution_resume(action_execution_db)

        return result
Ejemplo n.º 8
0
    def _format_action_exec_result(self, action_node, liveaction_db, created_at, updated_at,
                                   error=None):
        """
        Format ActionExecution result so it can be used in the final action result output.

        :rtype: ``dict``
        """
        assert(isinstance(created_at, datetime.datetime))
        assert(isinstance(updated_at, datetime.datetime))

        result = {}

        execution_db = None
        if liveaction_db:
            execution_db = ActionExecution.get(liveaction__id=str(liveaction_db.id))

        result['id'] = action_node.name
        result['name'] = action_node.name
        result['execution_id'] = str(execution_db.id) if execution_db else None
        result['workflow'] = None

        result['created_at'] = isotime.format(dt=created_at)
        result['updated_at'] = isotime.format(dt=updated_at)

        if error or not liveaction_db:
            result['state'] = LIVEACTION_STATUS_FAILED
        else:
            result['state'] = liveaction_db.status

        if error:
            result['result'] = error
        else:
            result['result'] = liveaction_db.result

        return result
Ejemplo n.º 9
0
def request_cancellation(liveaction, requester):
    """
    Request cancellation of an action execution.

    :return: (liveaction, execution)
    :rtype: tuple
    """
    if liveaction.status == action_constants.LIVEACTION_STATUS_CANCELING:
        return liveaction

    if liveaction.status not in action_constants.LIVEACTION_CANCELABLE_STATES:
        raise Exception(
            'Unable to cancel liveaction "%s" because it is already in a '
            'completed state.' % liveaction.id
        )

    result = {
        'message': 'Action canceled by user.',
        'user': requester
    }

    # Run cancelation sequence for liveaction that is in running state or
    # if the liveaction is operating under a workflow.
    if ('parent' in liveaction.context or
            liveaction.status in action_constants.LIVEACTION_STATUS_RUNNING):
        status = action_constants.LIVEACTION_STATUS_CANCELING
    else:
        status = action_constants.LIVEACTION_STATUS_CANCELED

    liveaction = update_status(liveaction, status, result=result)

    execution = ActionExecution.get(liveaction__id=str(liveaction.id))

    return (liveaction, execution)
Ejemplo n.º 10
0
def request_cancellation(liveaction, requester):
    """
    Request cancellation of an action execution.

    :return: (liveaction, execution)
    :rtype: tuple
    """
    if liveaction.status == action_constants.LIVEACTION_STATUS_CANCELING:
        return liveaction

    if liveaction.status not in action_constants.LIVEACTION_CANCELABLE_STATES:
        raise Exception(
            'Unable to cancel liveaction "%s" because it is already in a '
            'completed state.' % liveaction.id)

    result = {'message': 'Action canceled by user.', 'user': requester}

    # Run cancelation sequence for liveaction that is in running state or
    # if the liveaction is operating under a workflow.
    if ('parent' in liveaction.context or liveaction.status
            in action_constants.LIVEACTION_STATUS_RUNNING):
        status = action_constants.LIVEACTION_STATUS_CANCELING
    else:
        status = action_constants.LIVEACTION_STATUS_CANCELED

    liveaction = update_status(liveaction, status, result=result)

    execution = ActionExecution.get(liveaction__id=str(liveaction.id))

    return (liveaction, execution)
Ejemplo n.º 11
0
def request_cancellation(liveaction, requester):
    """
    Request cancellation of an action execution.

    :return: (liveaction, execution)
    :rtype: tuple
    """
    if liveaction.status == action_constants.LIVEACTION_STATUS_CANCELING:
        return liveaction

    if liveaction.status not in action_constants.LIVEACTION_CANCELABLE_STATES:
        raise Exception(
            'Unable to cancel liveaction "%s" because it is already in a '
            'completed state.' % liveaction.id)

    result = {'message': 'Action canceled by user.', 'user': requester}

    # There is real work only when liveaction is still running.
    status = (action_constants.LIVEACTION_STATUS_CANCELING if liveaction.status
              == action_constants.LIVEACTION_STATUS_RUNNING else
              action_constants.LIVEACTION_STATUS_CANCELED)

    liveaction = update_status(liveaction, status, result=result)

    execution = ActionExecution.get(liveaction__id=str(liveaction.id))

    return (liveaction, execution)
    def cancel(self):
        mistral_ctx = self.context.get('mistral', dict())

        if not mistral_ctx.get('execution_id'):
            raise Exception(
                'Unable to cancel because mistral execution_id is missing.')

        # Cancels the main workflow execution. Any non-workflow tasks that are still
        # running will be allowed to complete gracefully.
        self._client.executions.update(mistral_ctx.get('execution_id'),
                                       'CANCELLED')

        # If workflow is executed under another parent workflow, cancel the corresponding
        # action execution for the task in the parent workflow.
        if 'parent' in getattr(self, 'context',
                               {}) and mistral_ctx.get('action_execution_id'):
            mistral_action_ex_id = mistral_ctx.get('action_execution_id')
            self._client.action_executions.update(mistral_action_ex_id,
                                                  'CANCELLED')

        # Identify the list of action executions that are workflows and still running.
        for child_exec_id in self.execution.children:
            child_exec = ActionExecution.get(id=child_exec_id)
            if (child_exec.runner['name']
                    in action_constants.WORKFLOW_RUNNER_TYPES
                    and child_exec.status
                    in action_constants.LIVEACTION_CANCELABLE_STATES):
                action_service.request_cancellation(
                    LiveAction.get(id=child_exec.liveaction['id']),
                    self.context.get('user', None))

        return (action_constants.LIVEACTION_STATUS_CANCELING,
                self.liveaction.result, self.liveaction.context)
Ejemplo n.º 13
0
def update_execution(liveaction_db, publish=True, set_result_size=False):
    """
    :param set_result_size: True to calculate size of the serialized result field value and set it
                            on the "result_size" database field.
    """
    execution = ActionExecution.get(liveaction__id=str(liveaction_db.id))
    decomposed = _decompose_liveaction(liveaction_db)

    kw = {}
    for k, v in six.iteritems(decomposed):
        kw["set__" + k] = v

    if liveaction_db.status != execution.status:
        # Note: If the status changes we store this transition in the "log" attribute of action
        # execution
        kw["push__log"] = _create_execution_log_entry(liveaction_db.status)

    if set_result_size:
        # Sadly with the current ORM abstraction there is no better way to achieve updating
        # result_size and we need to serialize the value again - luckily that operation is fast.
        # To put things into perspective - on 4 MB result dictionary it only takes 7 ms which is
        # negligible compared to other DB operations duration (and for smaller results it takes
        # in sub ms range).
        with Timer(key="action.executions.calculate_result_size"):
            result_size = len(
                ActionExecutionDB.result._serialize_field_value(liveaction_db.result)
            )
            kw["set__result_size"] = result_size

    execution = ActionExecution.update(execution, publish=publish, **kw)
    return execution
Ejemplo n.º 14
0
    def _get_execution_for_liveaction(self, liveaction):
        execution = ActionExecution.get(liveaction__id=str(liveaction.id))

        if not execution:
            return None

        return execution
Ejemplo n.º 15
0
    def run(self, action_parameters):

        liveaction_db = action_utils.get_liveaction_by_id(self.liveaction_id)
        exc = ActionExecution.get(liveaction__id=str(liveaction_db.id))

        # Assemble and dispatch trigger
        trigger_ref = ResourceReference.to_string_reference(
            pack=INQUIRY_TRIGGER['pack'], name=INQUIRY_TRIGGER['name'])
        trigger_payload = {"id": str(exc.id), "route": self.route}
        self.trigger_dispatcher.dispatch(trigger_ref, trigger_payload)

        # We only want to request a pause if this has a parent
        if liveaction_db.context.get("parent"):

            # Get the root liveaction and request that it pauses
            root_liveaction = action_service.get_root_liveaction(liveaction_db)
            action_service.request_pause(root_liveaction,
                                         self.context.get('user', None))

        result = {
            "schema": self.schema,
            "roles": self.roles_param,
            "users": self.users_param,
            "route": self.route,
            "ttl": self.ttl
        }
        return (LIVEACTION_STATUS_PENDING, result, None)
Ejemplo n.º 16
0
 def test_chained_executions(self):
     liveaction = LiveActionDB(action='core.chain')
     liveaction, _ = action_service.request(liveaction)
     liveaction = LiveAction.get_by_id(str(liveaction.id))
     self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
     execution = self._get_action_execution(liveaction__id=str(liveaction.id),
                                            raise_exception=True)
     action = action_utils.get_action_by_ref('core.chain')
     self.assertDictEqual(execution.action, vars(ActionAPI.from_model(action)))
     runner = RunnerType.get_by_name(action.runner_type['name'])
     self.assertDictEqual(execution.runner, vars(RunnerTypeAPI.from_model(runner)))
     liveaction = LiveAction.get_by_id(str(liveaction.id))
     self.assertEqual(execution.start_timestamp, liveaction.start_timestamp)
     self.assertEqual(execution.end_timestamp, liveaction.end_timestamp)
     self.assertEqual(execution.result, liveaction.result)
     self.assertEqual(execution.status, liveaction.status)
     self.assertEqual(execution.context, liveaction.context)
     self.assertEqual(execution.liveaction['callback'], liveaction.callback)
     self.assertEqual(execution.liveaction['action'], liveaction.action)
     self.assertGreater(len(execution.children), 0)
     for child in execution.children:
         record = ActionExecution.get(id=child, raise_exception=True)
         self.assertEqual(record.parent, str(execution.id))
         self.assertEqual(record.action['name'], 'local')
         self.assertEqual(record.runner['name'], 'run-local')
Ejemplo n.º 17
0
def update_execution(liveaction_db, publish=True):
    execution = ActionExecution.get(liveaction__id=str(liveaction_db.id))
    decomposed = _decompose_liveaction(liveaction_db)
    for k, v in six.iteritems(decomposed):
        setattr(execution, k, v)
    execution = ActionExecution.add_or_update(execution, publish=publish)
    return execution
Ejemplo n.º 18
0
 def test_chained_executions(self):
     with mock.patch("st2common.runners.register_runner", mock.MagicMock(return_value=action_chain_runner)):
         liveaction = LiveActionDB(action="executions.chain")
         liveaction, _ = action_service.request(liveaction)
         liveaction = LiveAction.get_by_id(str(liveaction.id))
         self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
         execution = self._get_action_execution(liveaction__id=str(liveaction.id), raise_exception=True)
         action = action_utils.get_action_by_ref("executions.chain")
         self.assertDictEqual(execution.action, vars(ActionAPI.from_model(action)))
         runner = RunnerType.get_by_name(action.runner_type["name"])
         self.assertDictEqual(execution.runner, vars(RunnerTypeAPI.from_model(runner)))
         liveaction = LiveAction.get_by_id(str(liveaction.id))
         self.assertEqual(execution.start_timestamp, liveaction.start_timestamp)
         self.assertEqual(execution.end_timestamp, liveaction.end_timestamp)
         self.assertEqual(execution.result, liveaction.result)
         self.assertEqual(execution.status, liveaction.status)
         self.assertEqual(execution.context, liveaction.context)
         self.assertEqual(execution.liveaction["callback"], liveaction.callback)
         self.assertEqual(execution.liveaction["action"], liveaction.action)
         self.assertGreater(len(execution.children), 0)
         for child in execution.children:
             record = ActionExecution.get(id=child, raise_exception=True)
             self.assertEqual(record.parent, str(execution.id))
             self.assertEqual(record.action["name"], "local")
             self.assertEqual(record.runner["name"], "run-local")
    def _resume_action(self, liveaction_db):
        action_execution_db = ActionExecution.get(
            liveaction__id=str(liveaction_db.id))
        extra = {
            'action_execution_db': action_execution_db,
            'liveaction_db': liveaction_db
        }
        LOG.audit('Resuming action execution.', extra=extra)

        # the extra field will not be shown in non-audit logs so temporarily log at info.
        LOG.info(
            'Dispatched {~}action_execution: %s / {~}live_action: %s with "%s" status.',
            action_execution_db.id, liveaction_db.id, liveaction_db.status)

        try:
            result = self.container.dispatch(liveaction_db)
            LOG.debug('Runner dispatch produced result: %s', result)
        except:
            _, ex, tb = sys.exc_info()
            extra['error'] = str(ex)
            LOG.info('Failed to resume action execution %s.' %
                     (liveaction_db.id),
                     extra=extra)
            raise

        return result
Ejemplo n.º 20
0
def request_cancellation(liveaction, requester):
    """
    Request cancellation of an action execution.

    :return: (liveaction, execution)
    :rtype: tuple
    """
    if liveaction.status == action_constants.LIVEACTION_STATUS_CANCELING:
        return liveaction

    if liveaction.status not in action_constants.LIVEACTION_CANCELABLE_STATES:
        raise Exception('Unable to cancel execution because it is already in a completed state.')

    result = {
        'message': 'Action canceled by user.',
        'user': requester
    }

    # There is real work only when liveaction is still running.
    status = (action_constants.LIVEACTION_STATUS_CANCELING
              if liveaction.status == action_constants.LIVEACTION_STATUS_RUNNING
              else action_constants.LIVEACTION_STATUS_CANCELED)

    update_status(liveaction, status, result=result)

    execution = ActionExecution.get(liveaction__id=str(liveaction.id))

    return (liveaction, execution)
Ejemplo n.º 21
0
    def _get_execution_for_liveaction(self, liveaction):
        execution = ActionExecution.get(liveaction__id=str(liveaction.id))

        if not execution:
            return None

        return execution
Ejemplo n.º 22
0
    def _pause_action(self, liveaction_db):
        action_execution_db = ActionExecution.get(
            liveaction__id=str(liveaction_db.id))
        extra = {
            "action_execution_db": action_execution_db,
            "liveaction_db": liveaction_db,
        }
        LOG.audit("Pausing action execution.", extra=extra)

        # the extra field will not be shown in non-audit logs so temporarily log at info.
        LOG.info(
            'Dispatched {~}action_execution: %s / {~}live_action: %s with "%s" status.',
            action_execution_db.id,
            liveaction_db.id,
            liveaction_db.status,
        )

        try:
            result = self.container.dispatch(liveaction_db)
            LOG.debug("Runner dispatch produced result: %s", result)
        except:
            _, ex, tb = sys.exc_info()
            extra["error"] = str(ex)
            LOG.info("Failed to pause action execution %s." %
                     (liveaction_db.id),
                     extra=extra)
            raise

        return result
Ejemplo n.º 23
0
    def _resume_action(self, liveaction_db):
        action_execution_db = ActionExecution.get(liveaction__id=str(liveaction_db.id))
        extra = {'action_execution_db': action_execution_db, 'liveaction_db': liveaction_db}
        LOG.audit('Resuming action execution.', extra=extra)

        # the extra field will not be shown in non-audit logs so temporarily log at info.
        LOG.info('Dispatched {~}action_execution: %s / {~}live_action: %s with "%s" status.',
                 action_execution_db.id, liveaction_db.id, liveaction_db.status)

        try:
            result = self.container.dispatch(liveaction_db)
            LOG.debug('Runner dispatch produced result: %s', result)
        except:
            _, ex, tb = sys.exc_info()
            extra['error'] = str(ex)
            LOG.info('Failed to resume action execution %s.' % (liveaction_db.id), extra=extra)
            raise

        # Cascade the resume upstream if action execution is child of an orquesta workflow.
        # The action service request_resume function is not used here because we do not want
        # other peer subworkflows to be resumed.
        if 'orquesta' in action_execution_db.context and 'parent' in action_execution_db.context:
            wf_svc.handle_action_execution_resume(action_execution_db)

        return result
Ejemplo n.º 24
0
    def resume(self):
        mistral_ctx = self.context.get('mistral', dict())

        if not mistral_ctx.get('execution_id'):
            raise Exception('Unable to resume because mistral execution_id is missing.')

        # If workflow is executed under another parent workflow, resume the corresponding
        # action execution for the task in the parent workflow.
        if 'parent' in getattr(self, 'context', {}) and mistral_ctx.get('action_execution_id'):
            mistral_action_ex_id = mistral_ctx.get('action_execution_id')
            self._client.action_executions.update(mistral_action_ex_id, 'RUNNING')

        # Pause the main workflow execution. Any non-workflow tasks that are still
        # running will be allowed to complete gracefully.
        self._client.executions.update(mistral_ctx.get('execution_id'), 'RUNNING')

        # Identify the list of action executions that are workflows and cascade resume.
        for child_exec_id in self.execution.children:
            child_exec = ActionExecution.get(id=child_exec_id, raise_exception=True)
            if (child_exec.runner['name'] in action_constants.WORKFLOW_RUNNER_TYPES and
                    child_exec.status == action_constants.LIVEACTION_STATUS_PAUSED):
                action_service.request_resume(
                    LiveAction.get(id=child_exec.liveaction['id']),
                    self.context.get('user', None)
                )

        return (
            action_constants.LIVEACTION_STATUS_RUNNING,
            self.execution.result,
            self.execution.context
        )
Ejemplo n.º 25
0
    def _format_action_exec_result(self, action_node, liveaction_db, created_at, updated_at,
                                   error=None):
        """
        Format ActionExecution result so it can be used in the final action result output.

        :rtype: ``dict``
        """
        assert isinstance(created_at, datetime.datetime)
        assert isinstance(updated_at, datetime.datetime)

        result = {}

        execution_db = None
        if liveaction_db:
            execution_db = ActionExecution.get(liveaction__id=str(liveaction_db.id))

        result['id'] = action_node.name
        result['name'] = action_node.name
        result['execution_id'] = str(execution_db.id) if execution_db else None
        result['workflow'] = None

        result['created_at'] = isotime.format(dt=created_at)
        result['updated_at'] = isotime.format(dt=updated_at)

        if error or not liveaction_db:
            result['state'] = LIVEACTION_STATUS_FAILED
        else:
            result['state'] = liveaction_db.status

        if error:
            result['result'] = error
        else:
            result['result'] = liveaction_db.result

        return result
Ejemplo n.º 26
0
 def _get_execution_id(self, liveaction):
     try:
         execution = ActionExecution.get(liveaction__id=str(liveaction.id))
         return str(execution.id)
     except:
         LOG.exception('Execution object corresponding to LiveAction %s not found.',
                       str(liveaction.id))
         return None
Ejemplo n.º 27
0
 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)
Ejemplo n.º 28
0
def request_resume(liveaction, requester):
    """
    Request resume for a paused action execution.

    :return: (liveaction, execution)
    :rtype: tuple
    """
    # Validate that the runner type of the action supports pause.
    action_db = action_utils.get_action_by_ref(liveaction.action)

    if not action_db:
        raise ValueError(
            'Unable to resume liveaction "%s" because the action "%s" '
            "is not found." % (liveaction.id, liveaction.action))

    if action_db.runner_type[
            "name"] not in action_constants.WORKFLOW_RUNNER_TYPES:
        raise runner_exc.InvalidActionRunnerOperationError(
            'Unable to resume liveaction "%s" because it is not supported by the '
            '"%s" runner.' % (liveaction.id, action_db.runner_type["name"]))

    running_states = [
        action_constants.LIVEACTION_STATUS_RUNNING,
        action_constants.LIVEACTION_STATUS_RESUMING,
    ]

    if liveaction.status in running_states:
        execution = ActionExecution.get(liveaction__id=str(liveaction.id))
        return (liveaction, execution)

    if liveaction.status != action_constants.LIVEACTION_STATUS_PAUSED:
        raise runner_exc.UnexpectedActionExecutionStatusError(
            'Unable to resume liveaction "%s" because it is in "%s" state and '
            'not in "paused" state.' % (liveaction.id, liveaction.status))

    liveaction.context["resumed_by"] = get_requester(requester)
    liveaction = update_status(
        liveaction,
        action_constants.LIVEACTION_STATUS_RESUMING,
        context=liveaction.context,
    )

    execution = ActionExecution.get(liveaction__id=str(liveaction.id))

    return (liveaction, execution)
Ejemplo n.º 29
0
 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,
                                                  set_result_size=True)
     actionexecution_db = ActionExecution.get(
         liveaction__id=str(liveaction_db.id))
     return (liveaction_db, actionexecution_db)
Ejemplo n.º 30
0
def request_resume(liveaction, requester):
    """
    Request resume for a paused action execution.

    :return: (liveaction, execution)
    :rtype: tuple
    """
    # Validate that the runner type of the action supports pause.
    action_db = action_utils.get_action_by_ref(liveaction.action)

    if not action_db:
        raise ValueError(
            'Unable to resume liveaction "%s" because the action "%s" '
            'is not found.' % (liveaction.id, liveaction.action)
        )

    if action_db.runner_type['name'] not in action_constants.WORKFLOW_RUNNER_TYPES:
        raise runner_exc.InvalidActionRunnerOperationError(
            'Unable to resume liveaction "%s" because it is not supported by the '
            '"%s" runner.' % (liveaction.id, action_db.runner_type['name'])
        )

    running_states = [
        action_constants.LIVEACTION_STATUS_RUNNING,
        action_constants.LIVEACTION_STATUS_RESUMING
    ]

    if liveaction.status in running_states:
        execution = ActionExecution.get(liveaction__id=str(liveaction.id))
        return (liveaction, execution)

    if liveaction.status != action_constants.LIVEACTION_STATUS_PAUSED:
        raise runner_exc.UnexpectedActionExecutionStatusError(
            'Unable to resume liveaction "%s" because it is in "%s" state and '
            'not in "paused" state.' % (liveaction.id, liveaction.status)
        )

    liveaction = update_status(liveaction, action_constants.LIVEACTION_STATUS_RESUMING)

    execution = ActionExecution.get(liveaction__id=str(liveaction.id))

    return (liveaction, execution)
Ejemplo n.º 31
0
    def _get_runner(self, runnertype_db, action_db, liveaction_db):
        resolved_entry_point = self._get_entry_point_abs_path(
            action_db.pack, action_db.entry_point)
        context = getattr(liveaction_db, 'context', dict())
        user = context.get('user', cfg.CONF.system_user.user)

        # Note: Right now configs are only supported by the Python runner actions
        if runnertype_db.runner_module == 'python_runner':
            LOG.debug('Loading config for pack')

            config_loader = ContentPackConfigLoader(pack_name=action_db.pack,
                                                    user=user)
            config = config_loader.get_config()
        else:
            config = None

        runner = get_runner(package_name=runnertype_db.runner_package,
                            module_name=runnertype_db.runner_module,
                            config=config)

        # TODO: Pass those arguments to the constructor instead of late
        # assignment, late assignment is awful
        runner.runner_type_db = runnertype_db
        runner.action = action_db
        runner.action_name = action_db.name
        runner.liveaction = liveaction_db
        runner.liveaction_id = str(liveaction_db.id)
        runner.execution = ActionExecution.get(
            liveaction__id=runner.liveaction_id)
        runner.execution_id = str(runner.execution.id)
        runner.entry_point = resolved_entry_point
        runner.context = context
        runner.callback = getattr(liveaction_db, 'callback', dict())
        runner.libs_dir_path = self._get_action_libs_abs_path(
            action_db.pack, action_db.entry_point)

        # For re-run, get the ActionExecutionDB in which the re-run is based on.
        rerun_ref_id = runner.context.get('re-run', {}).get('ref')
        runner.rerun_ex_ref = ActionExecution.get(
            id=rerun_ref_id) if rerun_ref_id else None

        return runner
Ejemplo n.º 32
0
def get_trace_db_by_live_action(liveaction):
    """
    Given a liveaction does the best attempt to return a TraceDB.
    1. From trace_context in liveaction.context
    2. From parent in liveaction.context
    3. From action_execution associated with provided liveaction
    4. Creates a new TraceDB (which calling method is on the hook to persist).

    :param liveaction: liveaction from which to figure out a TraceDB.
    :type liveaction: ``LiveActionDB``

    :returns: (boolean, TraceDB) if the TraceDB was created(but not saved to DB) or
               retrieved from the DB and the TraceDB itself.
    :rtype: ``tuple``
    """
    trace_db = None
    created = False
    # 1. Try to get trace_db from liveaction context.
    #    via trigger_instance + rule or via user specified trace_context
    trace_context = liveaction.context.get(TRACE_CONTEXT, None)
    if trace_context:
        trace_context = _get_valid_trace_context(trace_context)
        trace_db = get_trace(trace_context=trace_context,
                             ignore_trace_tag=True)
        # found a trace_context but no trace_db. This implies a user supplied
        # trace_tag so create a new trace_db
        if not trace_db:
            trace_db = TraceDB(trace_tag=trace_context.trace_tag)
            created = True
        return (created, trace_db)
    # 2. If not found then check if parent context contains an execution_id.
    #    This cover case for child execution of a workflow.
    parent_context = executions.get_parent_context(liveaction_db=liveaction)
    if not trace_context and parent_context:
        parent_execution_id = parent_context.get("execution_id", None)
        if parent_execution_id:
            # go straight to a trace_db. If there is a parent execution then that must
            # be associated with a Trace.
            trace_db = get_trace_db_by_action_execution(
                action_execution_id=parent_execution_id)
            if not trace_db:
                raise StackStormDBObjectNotFoundError(
                    "No trace found for execution %s" % parent_execution_id)
            return (created, trace_db)
    # 3. Check if the action_execution associated with liveaction leads to a trace_db
    execution = ActionExecution.get(liveaction__id=str(liveaction.id))
    if execution:
        trace_db = get_trace_db_by_action_execution(action_execution=execution)
    # 4. No trace_db found, therefore create one. This typically happens
    #    when execution is run by hand.
    if not trace_db:
        trace_db = TraceDB(trace_tag="execution-%s" % str(liveaction.id))
        created = True
    return (created, trace_db)
Ejemplo n.º 33
0
def request_pause(liveaction, requester):
    """
    Request pause for a running action execution.

    :return: (liveaction, execution)
    :rtype: tuple
    """
    # Validate that the runner type of the action supports pause.
    action_db = action_utils.get_action_by_ref(liveaction.action)

    if not action_db:
        raise ValueError(
            'Unable to pause liveaction "%s" because the action "%s" '
            "is not found." % (liveaction.id, liveaction.action)
        )

    if action_db.runner_type["name"] not in action_constants.WORKFLOW_RUNNER_TYPES:
        raise runner_exc.InvalidActionRunnerOperationError(
            'Unable to pause liveaction "%s" because it is not supported by the '
            '"%s" runner.' % (liveaction.id, action_db.runner_type["name"])
        )

    if (
        liveaction.status == action_constants.LIVEACTION_STATUS_PAUSING
        or liveaction.status == action_constants.LIVEACTION_STATUS_PAUSED
    ):
        execution = ActionExecution.get(liveaction__id=str(liveaction.id))
        return (liveaction, execution)

    if liveaction.status != action_constants.LIVEACTION_STATUS_RUNNING:
        raise runner_exc.UnexpectedActionExecutionStatusError(
            'Unable to pause liveaction "%s" because it is not in a running state.'
            % liveaction.id
        )

    liveaction = update_status(liveaction, action_constants.LIVEACTION_STATUS_PAUSING)

    execution = ActionExecution.get(liveaction__id=str(liveaction.id))

    return (liveaction, execution)
Ejemplo n.º 34
0
def get_trace_db_by_live_action(liveaction):
    """
    Given a liveaction does the best attempt to return a TraceDB.
    1. From trace_context in liveaction.context
    2. From parent in liveaction.context
    3. From action_execution associated with provided liveaction
    4. Creates a new TraceDB (which calling method is on the hook to persist).

    :param liveaction: liveaction from which to figure out a TraceDB.
    :type liveaction: ``LiveActionDB``

    :returns: (boolean, TraceDB) if the TraceDB was created(but not saved to DB) or
               retrieved from the DB and the TraceDB itself.
    :rtype: ``tuple``
    """
    trace_db = None
    created = False
    # 1. Try to get trace_db from liveaction context.
    #    via trigger_instance + rule or via user specified trace_context
    trace_context = liveaction.context.get(TRACE_CONTEXT, None)
    if trace_context:
        trace_context = _get_valid_trace_context(trace_context)
        trace_db = get_trace(trace_context=trace_context, ignore_trace_tag=True)
        # found a trace_context but no trace_db. This implies a user supplied
        # trace_tag so create a new trace_db
        if not trace_db:
            trace_db = TraceDB(trace_tag=trace_context.trace_tag)
            created = True
        return (created, trace_db)
    # 2. If not found then check if parent context contains an execution_id.
    #    This cover case for child execution of a workflow.
    parent_context = executions.get_parent_context(liveaction_db=liveaction)
    if not trace_context and parent_context:
        parent_execution_id = parent_context.get('execution_id', None)
        if parent_execution_id:
            # go straight to a trace_db. If there is a parent execution then that must
            # be associated with a Trace.
            trace_db = get_trace_db_by_action_execution(action_execution_id=parent_execution_id)
            if not trace_db:
                raise StackStormDBObjectNotFoundError('No trace found for execution %s' %
                                                      parent_execution_id)
            return (created, trace_db)
    # 3. Check if the action_execution associated with liveaction leads to a trace_db
    execution = ActionExecution.get(liveaction__id=str(liveaction.id))
    if execution:
        trace_db = get_trace_db_by_action_execution(action_execution=execution)
    # 4. No trace_db found, therefore create one. This typically happens
    #    when execution is run by hand.
    if not trace_db:
        trace_db = TraceDB(trace_tag='execution-%s' % str(liveaction.id))
        created = True
    return (created, trace_db)
Ejemplo n.º 35
0
    def _get_runner(self, runner_type_db, action_db, liveaction_db):
        resolved_entry_point = self._get_entry_point_abs_path(
            action_db.pack, action_db.entry_point)
        context = getattr(liveaction_db, "context", dict())
        user = context.get("user", cfg.CONF.system_user.user)
        config = None

        # Note: Right now configs are only supported by the Python runner actions
        if (runner_type_db.name == "python-script"
                or runner_type_db.runner_module == "python_runner"):
            LOG.debug("Loading config from pack for python runner.")
            config_loader = ContentPackConfigLoader(pack_name=action_db.pack,
                                                    user=user)
            config = config_loader.get_config()

        runner = get_runner(name=runner_type_db.name, config=config)

        # TODO: Pass those arguments to the constructor instead of late
        # assignment, late assignment is awful
        runner.runner_type = runner_type_db
        runner.action = action_db
        runner.action_name = action_db.name
        runner.liveaction = liveaction_db
        runner.liveaction_id = str(liveaction_db.id)
        runner.execution = ActionExecution.get(
            liveaction__id=runner.liveaction_id)
        runner.execution_id = str(runner.execution.id)
        runner.entry_point = resolved_entry_point
        runner.context = context
        runner.callback = getattr(liveaction_db, "callback", dict())
        runner.libs_dir_path = self._get_action_libs_abs_path(
            action_db.pack, action_db.entry_point)

        # For re-run, get the ActionExecutionDB in which the re-run is based on.
        rerun_ref_id = runner.context.get("re-run", {}).get("ref")
        runner.rerun_ex_ref = (ActionExecution.get(
            id=rerun_ref_id) if rerun_ref_id else None)

        return runner
Ejemplo n.º 36
0
def update_execution(liveaction_db, publish=True):
    execution = ActionExecution.get(liveaction__id=str(liveaction_db.id))
    decomposed = _decompose_liveaction(liveaction_db)

    kw = {}
    for k, v in six.iteritems(decomposed):
        kw['set__' + k] = v

    if liveaction_db.status != execution.status:
        # Note: If the status changes we store this transition in the "log" attribute of action
        # execution
        kw['push__log'] = _create_execution_log_entry(liveaction_db.status)
    execution = ActionExecution.update(execution, publish=publish, **kw)
    return execution
Ejemplo n.º 37
0
def update_execution(liveaction_db, publish=True):
    execution = ActionExecution.get(liveaction__id=str(liveaction_db.id))
    decomposed = _decompose_liveaction(liveaction_db)

    kw = {}
    for k, v in six.iteritems(decomposed):
        kw['set__' + k] = v

    if liveaction_db.status != execution.status:
        # Note: If the status changes we store this transition in the "log" attribute of action
        # execution
        kw['push__log'] = _create_execution_log_entry(liveaction_db.status)
    execution = ActionExecution.update(execution, publish=publish, **kw)
    return execution
Ejemplo n.º 38
0
    def _fix_missing_action_execution_id(self):
        """
        Auto-populate the action_execution_id in ActionExecutionSchedulingQueue if empty.
        """
        for entry in ActionExecutionSchedulingQueue.query(action_execution_id__in=['', None]):
            execution_db = ActionExecution.get(liveaction__id=entry.liveaction_id)

            if not execution_db:
                continue

            msg = '[%s] Populating action_execution_id for item "%s".'
            LOG.info(msg, str(execution_db.id), str(entry.id))
            entry.action_execution_id = str(execution_db.id)
            ActionExecutionSchedulingQueue.add_or_update(entry, publish=False)
Ejemplo n.º 39
0
Archivo: base.py Proyecto: nzlosh/st2
    def _get_runner(self, runner_type_db, action_db, liveaction_db):
        resolved_entry_point = self._get_entry_point_abs_path(action_db.pack, action_db.entry_point)
        context = getattr(liveaction_db, 'context', dict())
        user = context.get('user', cfg.CONF.system_user.user)
        config = None

        # Note: Right now configs are only supported by the Python runner actions
        if (runner_type_db.name == 'python-script' or
                runner_type_db.runner_module == 'python_runner'):
            LOG.debug('Loading config from pack for python runner.')
            config_loader = ContentPackConfigLoader(pack_name=action_db.pack, user=user)
            config = config_loader.get_config()

        runner = get_runner(
            name=runner_type_db.name,
            config=config)

        # TODO: Pass those arguments to the constructor instead of late
        # assignment, late assignment is awful
        runner.runner_type = runner_type_db
        runner.action = action_db
        runner.action_name = action_db.name
        runner.liveaction = liveaction_db
        runner.liveaction_id = str(liveaction_db.id)
        runner.execution = ActionExecution.get(liveaction__id=runner.liveaction_id)
        runner.execution_id = str(runner.execution.id)
        runner.entry_point = resolved_entry_point
        runner.context = context
        runner.callback = getattr(liveaction_db, 'callback', dict())
        runner.libs_dir_path = self._get_action_libs_abs_path(action_db.pack,
                                                              action_db.entry_point)

        # For re-run, get the ActionExecutionDB in which the re-run is based on.
        rerun_ref_id = runner.context.get('re-run', {}).get('ref')
        runner.rerun_ex_ref = ActionExecution.get(id=rerun_ref_id) if rerun_ref_id else None

        return runner
Ejemplo n.º 40
0
    def pause(self):
        # Identify the list of action executions that are workflows and cascade pause.
        for child_exec_id in self.execution.children:
            child_exec = ActionExecution.get(id=child_exec_id,
                                             raise_exception=True)
            if (child_exec.runner['name']
                    in action_constants.WORKFLOW_RUNNER_TYPES
                    and child_exec.status
                    == action_constants.LIVEACTION_STATUS_RUNNING):
                action_service.request_pause(
                    LiveAction.get(id=child_exec.liveaction['id']),
                    self.context.get('user', None))

        return (action_constants.LIVEACTION_STATUS_PAUSING,
                self.liveaction.result, self.liveaction.context)
Ejemplo n.º 41
0
    def _format_action_exec_result(self,
                                   action_node,
                                   liveaction_db,
                                   created_at,
                                   updated_at,
                                   error=None):
        """
        Format ActionExecution result so it can be used in the final action result output.

        :rtype: ``dict``
        """
        if not isinstance(created_at, datetime.datetime):
            raise TypeError(
                f"The created_at is not a datetime object was({type(created_at)})."
            )
        if not isinstance(updated_at, datetime.datetime):
            raise TypeError(
                f"The updated_at is not a datetime object was({type(updated_at)})."
            )

        result = {}

        execution_db = None
        if liveaction_db:
            execution_db = ActionExecution.get(
                liveaction__id=str(liveaction_db.id))

        result["id"] = action_node.name
        result["name"] = action_node.name
        result["execution_id"] = str(execution_db.id) if execution_db else None
        result["liveaction_id"] = str(
            liveaction_db.id) if liveaction_db else None
        result["workflow"] = None

        result["created_at"] = isotime.format(dt=created_at)
        result["updated_at"] = isotime.format(dt=updated_at)

        if error or not liveaction_db:
            result["state"] = action_constants.LIVEACTION_STATUS_FAILED
        else:
            result["state"] = liveaction_db.status

        if error:
            result["result"] = error
        else:
            result["result"] = liveaction_db.result

        return result
Ejemplo n.º 42
0
def get_parent_execution(execution_db):
    """Get the action execution for the parent workflow

    Useful for finding the parent workflow. Pass in any ActionExecutionDB instance,
    and this function will return the action execution of the parent workflow.

    :param execution_db: The ActionExecutionDB instance for which to find the parent.
    :rtype: ActionExecutionDB
    """

    if not execution_db.parent:
        return None

    parent_execution_db = ActionExecution.get(id=execution_db.parent)

    return parent_execution_db
Ejemplo n.º 43
0
    def _create_execution_queue_item_db_from_liveaction(
            self, liveaction, delay=None):
        """
        Create ActionExecutionSchedulingQueueItemDB from live action.
        """
        execution = ActionExecution.get(liveaction__id=str(liveaction.id))

        execution_queue_item_db = ActionExecutionSchedulingQueueItemDB()
        execution_queue_item_db.action_execution_id = str(execution.id)
        execution_queue_item_db.liveaction_id = str(liveaction.id)
        execution_queue_item_db.original_start_timestamp = liveaction.start_timestamp
        execution_queue_item_db.scheduled_start_timestamp = date.append_milliseconds_to_time(
            liveaction.start_timestamp, delay or 0)
        execution_queue_item_db.delay = delay

        return execution_queue_item_db
Ejemplo n.º 44
0
def get_parent_execution(execution_db):
    """Get the action execution for the parent workflow

    Useful for finding the parent workflow. Pass in any ActionExecutionDB instance,
    and this function will return the action execution of the parent workflow.

    :param execution_db: The ActionExecutionDB instance for which to find the parent.
    :rtype: ActionExecutionDB
    """

    if not execution_db.parent:
        return None

    parent_execution_db = ActionExecution.get(id=execution_db.parent)

    return parent_execution_db
Ejemplo n.º 45
0
    def pause(self):
        # Identify the list of action executions that are workflows and cascade pause.
        for child_exec_id in self.execution.children:
            child_exec = ActionExecution.get(id=child_exec_id, raise_exception=True)
            if (child_exec.runner['name'] in action_constants.WORKFLOW_RUNNER_TYPES and
                    child_exec.status == action_constants.LIVEACTION_STATUS_RUNNING):
                action_service.request_pause(
                    LiveAction.get(id=child_exec.liveaction['id']),
                    self.context.get('user', None)
                )

        return (
            action_constants.LIVEACTION_STATUS_PAUSING,
            self.liveaction.result,
            self.liveaction.context
        )
Ejemplo n.º 46
0
def get_root_execution(ac_ex_db):
    """Recursively ascends until the root action execution is found

    Useful for finding an original parent workflow. Pass in any ActionExecutionDB instance,
    and this function will eventually return the top-most action execution, even if the two
    are one and the same.

    :param ac_ex_db: The ActionExecutionDB instance for which to find the root parent.
    :rtype: ActionExecutionDB
    """

    if not ac_ex_db.parent:
        return ac_ex_db

    parent_ac_ex_db = ActionExecution.get(id=ac_ex_db.parent)

    return get_root_execution(parent_ac_ex_db)
Ejemplo n.º 47
0
    def _create_execution_queue_item_db_from_liveaction(self, liveaction, delay=None):
        """
        Create ActionExecutionSchedulingQueueItemDB from live action.
        """
        execution = ActionExecution.get(liveaction__id=str(liveaction.id))

        execution_queue_item_db = ActionExecutionSchedulingQueueItemDB()
        execution_queue_item_db.action_execution_id = str(execution.id)
        execution_queue_item_db.liveaction_id = str(liveaction.id)
        execution_queue_item_db.original_start_timestamp = liveaction.start_timestamp
        execution_queue_item_db.scheduled_start_timestamp = date.append_milliseconds_to_time(
            liveaction.start_timestamp,
            delay or 0
        )
        execution_queue_item_db.delay = delay

        return execution_queue_item_db
Ejemplo n.º 48
0
Archivo: worker.py Proyecto: hejin/st2
    def _cancel_action(self, liveaction_db):
        action_execution_db = ActionExecution.get(liveaction__id=str(liveaction_db.id))
        extra = {'action_execution_db': action_execution_db, 'liveaction_db': liveaction_db}
        LOG.audit('Canceling action execution.', extra=extra)

        # the extra field will not be shown in non-audit logs so temporarily log at info.
        LOG.info('Dispatched {~}action_execution: %s / {~}live_action: %s with "%s" status.',
                 action_execution_db.id, liveaction_db.id, liveaction_db.status)

        try:
            result = self.container.dispatch(liveaction_db)
            LOG.debug('Runner dispatch produced result: %s', result)
        except:
            _, ex, tb = sys.exc_info()
            extra['error'] = str(ex)
            LOG.info('Failed to cancel action execution %s.' % (liveaction_db.id), extra=extra)
            raise

        return result
Ejemplo n.º 49
0
def get_parent_liveaction(liveaction_db):
    """Get the liveaction for the parent workflow

    Useful for finding the parent workflow. Pass in any LiveActionDB instance,
    and this function will return the liveaction of the parent workflow.

    :param liveaction_db: The LiveActionDB instance for which to find the parent.
    :rtype: LiveActionDB
    """

    parent = liveaction_db.context.get('parent')

    if not parent:
        return None

    parent_execution_db = ActionExecution.get(id=parent['execution_id'])
    parent_liveaction_db = LiveAction.get(id=parent_execution_db.liveaction['id'])

    return parent_liveaction_db
Ejemplo n.º 50
0
def is_children_active(liveaction_id):
    execution_db = ActionExecution.get(liveaction__id=str(liveaction_id))

    if execution_db.runner['name'] not in action_constants.WORKFLOW_RUNNER_TYPES:
        return False

    children_execution_dbs = ActionExecution.query(parent=str(execution_db.id))

    inactive_statuses = (
        action_constants.LIVEACTION_COMPLETED_STATES +
        [action_constants.LIVEACTION_STATUS_PAUSED, action_constants.LIVEACTION_STATUS_PENDING]
    )

    completed = [
        child_exec_db.status in inactive_statuses
        for child_exec_db in children_execution_dbs
    ]

    return (not all(completed))
Ejemplo n.º 51
0
Archivo: action.py Proyecto: Koulio/st2
def request_cancellation(liveaction, requester):
    """
    Request cancellation of an action execution.

    :return: (liveaction, execution)
    :rtype: tuple
    """
    if liveaction.status == action_constants.LIVEACTION_STATUS_CANCELING:
        return liveaction

    if liveaction.status not in action_constants.CANCELABLE_STATES:
        raise Exception("Unable to cancel execution because it is already in a completed state.")

    result = {"message": "Action canceled by user.", "user": requester}

    update_status(liveaction, action_constants.LIVEACTION_STATUS_CANCELING, result=result)

    execution = ActionExecution.get(liveaction__id=str(liveaction.id))

    return (liveaction, execution)
Ejemplo n.º 52
0
Archivo: base.py Proyecto: beryah/st2
    def _get_runner(self, runnertype_db, action_db, liveaction_db):
        runner = get_runner(runnertype_db.runner_module)

        resolved_entry_point = self._get_entry_point_abs_path(action_db.pack,
                                                              action_db.entry_point)

        runner.container_service = RunnerContainerService()
        runner.action = action_db
        runner.action_name = action_db.name
        runner.liveaction = liveaction_db
        runner.liveaction_id = str(liveaction_db.id)
        runner.execution = ActionExecution.get(liveaction__id=runner.liveaction_id)
        runner.execution_id = str(runner.execution.id)
        runner.entry_point = resolved_entry_point
        runner.context = getattr(liveaction_db, 'context', dict())
        runner.callback = getattr(liveaction_db, 'callback', dict())
        runner.libs_dir_path = self._get_action_libs_abs_path(action_db.pack,
                                                              action_db.entry_point)

        return runner
Ejemplo n.º 53
0
def create_execution_object(liveaction, publish=True):
    action_db = action_utils.get_action_by_ref(liveaction.action)
    runner = RunnerType.get_by_name(action_db.runner_type['name'])

    attrs = {
        'action': vars(ActionAPI.from_model(action_db)),
        'runner': vars(RunnerTypeAPI.from_model(runner))
    }
    attrs.update(_decompose_liveaction(liveaction))

    if 'rule' in liveaction.context:
        rule = reference.get_model_from_ref(Rule, liveaction.context.get('rule', {}))
        attrs['rule'] = vars(RuleAPI.from_model(rule))

    if 'trigger_instance' in liveaction.context:
        trigger_instance_id = liveaction.context.get('trigger_instance', {})
        trigger_instance_id = trigger_instance_id.get('id', None)
        trigger_instance = TriggerInstance.get_by_id(trigger_instance_id)
        trigger = reference.get_model_by_resource_ref(db_api=Trigger,
                                                      ref=trigger_instance.trigger)
        trigger_type = reference.get_model_by_resource_ref(db_api=TriggerType,
                                                           ref=trigger.type)
        trigger_instance = reference.get_model_from_ref(
            TriggerInstance, liveaction.context.get('trigger_instance', {}))
        attrs['trigger_instance'] = vars(TriggerInstanceAPI.from_model(trigger_instance))
        attrs['trigger'] = vars(TriggerAPI.from_model(trigger))
        attrs['trigger_type'] = vars(TriggerTypeAPI.from_model(trigger_type))

    parent = ActionExecution.get(liveaction__id=liveaction.context.get('parent', ''))
    if parent:
        attrs['parent'] = str(parent.id)

    execution = ActionExecutionDB(**attrs)
    execution = ActionExecution.add_or_update(execution, publish=publish)

    if parent:
        if str(execution.id) not in parent.children:
            parent.children.append(str(execution.id))
            ActionExecution.add_or_update(parent)

    return execution
Ejemplo n.º 54
0
    def cancel(self):
        mistral_ctx = self.context.get('mistral', dict())

        if not mistral_ctx.get('execution_id'):
            raise Exception('Unable to cancel because mistral execution_id is missing.')

        # Cancels the main workflow execution. Any non-workflow tasks that are still
        # running will be allowed to complete gracefully.
        self._client.executions.update(mistral_ctx.get('execution_id'), 'CANCELLED')

        # If workflow is executed under another parent workflow, cancel the corresponding
        # action execution for the task in the parent workflow.
        if 'parent' in getattr(self, 'context', {}) and mistral_ctx.get('action_execution_id'):
            mistral_action_ex_id = mistral_ctx.get('action_execution_id')
            self._client.action_executions.update(mistral_action_ex_id, 'CANCELLED')

        # Identify the list of action executions that are workflows and still running.
        for child_exec_id in self.execution.children:
            child_exec = ActionExecution.get(id=child_exec_id)
            if (child_exec.runner['name'] in action_constants.WORKFLOW_RUNNER_TYPES and
                    child_exec.status in action_constants.LIVEACTION_CANCELABLE_STATES):
                action_service.request_cancellation(
                    LiveAction.get(id=child_exec.liveaction['id']),
                    self.context.get('user', None)
                )

        status = (
            action_constants.LIVEACTION_STATUS_CANCELING
            if action_service.is_children_active(self.liveaction.id)
            else action_constants.LIVEACTION_STATUS_CANCELED
        )

        return (
            status,
            self.liveaction.result,
            self.liveaction.context
        )
Ejemplo n.º 55
0
 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)
Ejemplo n.º 56
0
 def _get_action_execution(self, **kwargs):
     return ActionExecution.get(**kwargs)
Ejemplo n.º 57
0
    def test_over_threshold_delay_executions(self):
        # Ensure the concurrency policy is accurate.
        policy_db = Policy.get_by_ref('wolfpack.action-1.concurrency')
        self.assertGreater(policy_db.parameters['threshold'], 0)

        # Launch action executions until the expected threshold is reached.
        for i in range(0, policy_db.parameters['threshold']):
            parameters = {'actionstr': 'foo-' + str(i)}
            liveaction = LiveActionDB(action='wolfpack.action-1', parameters=parameters)
            action_service.request(liveaction)

        # Run the scheduler to schedule action executions.
        self._process_scheduling_queue()

        # Check the number of action executions in scheduled state.
        scheduled = [item for item in LiveAction.get_all() if item.status in SCHEDULED_STATES]
        self.assertEqual(len(scheduled), policy_db.parameters['threshold'])

        # Assert the correct number of published states and action executions. This is to avoid
        # duplicate executions caused by accidental publishing of state in the concurrency policies.
        # num_state_changes = len(scheduled) * len(['requested', 'scheduled', 'running'])
        expected_num_exec = len(scheduled)
        expected_num_pubs = expected_num_exec * 3
        self.assertEqual(expected_num_pubs, LiveActionPublisher.publish_state.call_count)
        self.assertEqual(expected_num_exec, runner.MockActionRunner.run.call_count)

        # Execution is expected to be delayed since concurrency threshold is reached.
        liveaction = LiveActionDB(action='wolfpack.action-1', parameters={'actionstr': 'foo-last'})
        liveaction, _ = action_service.request(liveaction)

        expected_num_pubs += 1  # Tally requested state.
        self.assertEqual(expected_num_pubs, LiveActionPublisher.publish_state.call_count)

        # Run the scheduler to schedule action executions.
        self._process_scheduling_queue()

        # Since states are being processed async, wait for the liveaction to go into delayed state.
        liveaction = self._wait_on_status(liveaction, action_constants.LIVEACTION_STATUS_DELAYED)

        expected_num_exec += 0  # This request will not be scheduled for execution.
        expected_num_pubs += 0  # The delayed status change should not be published.
        self.assertEqual(expected_num_pubs, LiveActionPublisher.publish_state.call_count)
        self.assertEqual(expected_num_exec, runner.MockActionRunner.run.call_count)

        # Mark one of the scheduled/running execution as completed.
        action_service.update_status(
            scheduled[0],
            action_constants.LIVEACTION_STATUS_SUCCEEDED,
            publish=True
        )

        expected_num_pubs += 1  # Tally succeeded state.
        self.assertEqual(expected_num_pubs, LiveActionPublisher.publish_state.call_count)

        # Run the scheduler to schedule action executions.
        self._process_scheduling_queue()

        # Once capacity freed up, the delayed execution is published as scheduled.
        expected_num_exec += 1  # This request is expected to be executed.
        expected_num_pubs += 2  # Tally scheduled and running state.

        # Since states are being processed async, wait for the liveaction to be scheduled.
        liveaction = self._wait_on_statuses(liveaction, SCHEDULED_STATES)
        self.assertEqual(expected_num_pubs, LiveActionPublisher.publish_state.call_count)
        self.assertEqual(expected_num_exec, runner.MockActionRunner.run.call_count)

        # Check the status changes.
        execution = ActionExecution.get(liveaction__id=str(liveaction.id))
        expected_status_changes = ['requested', 'delayed', 'requested', 'scheduled', 'running']
        actual_status_changes = [entry['status'] for entry in execution.log]
        self.assertListEqual(actual_status_changes, expected_status_changes)