Пример #1
0
    def execute(self, *args, **kwargs):
        def _do_execute_action(*args, **kwargs):
            try:
                db_action = self.do_execute(*args, **kwargs)
                notifications.action.send_execution_notification(
                    self.engine.context, db_action,
                    fields.NotificationAction.EXECUTION,
                    fields.NotificationPhase.END)
            except Exception as e:
                LOG.exception(e)
                LOG.error(
                    'The workflow engine has failed'
                    'to execute the action: %s', self.name)
                db_action = self.engine.notify(self._db_action,
                                               objects.action.State.FAILED)
                notifications.action.send_execution_notification(
                    self.engine.context,
                    db_action,
                    fields.NotificationAction.EXECUTION,
                    fields.NotificationPhase.ERROR,
                    priority=fields.NotificationPriority.ERROR)
                raise

        # NOTE: spawn a new thread for action execution, so that if action plan
        # is cancelled workflow engine will not wait to finish action execution
        et = eventlet.spawn(_do_execute_action, *args, **kwargs)
        # NOTE: check for the state of action plan periodically,so that if
        # action is finished or action plan is cancelled we can exit from here.
        while True:
            action_object = objects.Action.get_by_uuid(self.engine.context,
                                                       self._db_action.uuid,
                                                       eager=True)
            action_plan_object = objects.ActionPlan.get_by_id(
                self.engine.context, action_object.action_plan_id)
            if (action_object.state in [
                    objects.action.State.SUCCEEDED, objects.action.State.FAILED
            ] or action_plan_object.state in CANCEL_STATE):
                break
            time.sleep(1)
        try:
            # NOTE: kill the action execution thread, if action plan is
            # cancelled for all other cases wait for the result from action
            # execution thread.
            # Not all actions support abort operations, kill only those action
            # which support abort operations
            abort = self.action.check_abort()
            if (action_plan_object.state in CANCEL_STATE and abort):
                et.kill()
            et.wait()

            # NOTE: catch the greenlet exit exception due to thread kill,
            # taskflow will call revert for the action,
            # we will redirect it to abort.
        except eventlet.greenlet.GreenletExit:
            self.engine.notify_cancel_start(action_plan_object.uuid)
            raise exception.ActionPlanCancelled(uuid=action_plan_object.uuid)

        except Exception as e:
            LOG.exception(e)
            raise
Пример #2
0
 def pre_execute(self):
     try:
         # NOTE(adisky): check the state of action plan before starting
         # next action, if action plan is cancelled raise the exceptions
         # so that taskflow does not schedule further actions.
         action_plan = objects.ActionPlan.get_by_id(
             self.engine.context, self._db_action.action_plan_id)
         if action_plan.state in CANCEL_STATE:
             raise exception.ActionPlanCancelled(uuid=action_plan.uuid)
         db_action = self.do_pre_execute()
         notifications.action.send_execution_notification(
             self.engine.context, db_action,
             fields.NotificationAction.EXECUTION,
             fields.NotificationPhase.START)
     except exception.ActionPlanCancelled as e:
         LOG.exception(e)
         self.engine.notify_cancel_start(action_plan.uuid)
         raise
     except Exception as e:
         LOG.exception(e)
         db_action = self.engine.notify(self._db_action,
                                        objects.action.State.FAILED)
         notifications.action.send_execution_notification(
             self.engine.context,
             db_action,
             fields.NotificationAction.EXECUTION,
             fields.NotificationPhase.ERROR,
             priority=fields.NotificationPriority.ERROR)
Пример #3
0
 def test_cancel_action_plan_with_exception(self, m_get_action_plan,
                                            m_execute):
     m_get_action_plan.return_value = self.action_plan
     m_execute.side_effect = exception.ActionPlanCancelled(
         self.action_plan.uuid)
     command = default.DefaultActionPlanHandler(
         self.context, mock.MagicMock(), self.action_plan.uuid)
     command.execute()
     self.assertEqual(ap_objects.State.CANCELLED, self.action_plan.state)