示例#1
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')
示例#2
0
文件: action.py 项目: jonico/st2
def update_status(liveaction, new_status, publish=True):
    if liveaction.status == new_status:
        return liveaction

    old_status = liveaction.status

    liveaction = action_utils.update_liveaction_status(
        status=new_status, liveaction_id=liveaction.id, publish=False)

    action_execution = executions.update_execution(liveaction)

    msg = ('The status of action execution is changed from %s to %s. '
           '<LiveAction.id=%s, ActionExecution.id=%s>' % (old_status,
           new_status, liveaction.id, action_execution.id))

    extra = {
        'action_execution_db': action_execution,
        'liveaction_db': liveaction
    }

    LOG.audit(msg, extra=extra)
    LOG.info(msg)

    if publish:
        LiveAction.publish_status(liveaction)

    return liveaction
示例#3
0
 def test_execution_creation_action_triggered_by_rule(self):
     # Wait for the action execution to complete and then confirm outcome.
     trigger_type = self.MODELS['triggertypes']['triggertype2.yaml']
     trigger = self.MODELS['triggers']['trigger2.yaml']
     trigger_instance = self.MODELS['triggerinstances']['trigger_instance_1.yaml']
     test_liveaction = self.FIXTURES['liveactions']['liveaction3.yaml']
     rule = self.MODELS['rules']['rule3.yaml']
     # Setup LiveAction to point to right rule and trigger_instance.
     # XXX: We need support for dynamic fixtures.
     test_liveaction['context']['rule']['id'] = str(rule.id)
     test_liveaction['context']['trigger_instance']['id'] = str(trigger_instance.id)
     test_liveaction_api = LiveActionAPI(**test_liveaction)
     test_liveaction = LiveAction.add_or_update(LiveActionAPI.to_model(test_liveaction_api))
     liveaction = LiveAction.get(context__trigger_instance__id=str(trigger_instance.id))
     self.assertIsNotNone(liveaction)
     self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_REQUESTED)
     executions_util.create_execution_object(liveaction)
     execution = self._get_action_execution(liveaction__id=str(liveaction.id),
                                            raise_exception=True)
     self.assertDictEqual(execution.trigger, vars(TriggerAPI.from_model(trigger)))
     self.assertDictEqual(execution.trigger_type, vars(TriggerTypeAPI.from_model(trigger_type)))
     self.assertDictEqual(execution.trigger_instance,
                          vars(TriggerInstanceAPI.from_model(trigger_instance)))
     self.assertDictEqual(execution.rule, vars(RuleAPI.from_model(rule)))
     action = action_utils.get_action_by_ref(liveaction.action)
     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.assertEquals(execution.liveaction['id'], str(liveaction.id))
示例#4
0
def _purge_models(execution_db):
    liveaction_id = execution_db.liveaction.get("id", None)

    if not liveaction_id:
        LOG.error("Invalid LiveAction id. Skipping delete: %s", execution_db)

    liveaction_db = None
    try:
        liveaction_db = LiveAction.get_by_id(liveaction_id)
    except:
        LOG.exception("LiveAction with id: %s not found. Skipping delete.", liveaction_id)
    else:
        global DELETED_COUNT
        DELETED_COUNT += 1

    try:
        ActionExecution.delete(execution_db)
    except:
        LOG.exception("Exception deleting Execution model: %s", execution_db)
    else:
        if liveaction_db:
            try:
                LiveAction.delete(liveaction_db)
            except:
                LOG.exception("Zombie LiveAction left in db: %s.", liveaction_db)
示例#5
0
    def test_keep_state_object_on_error_at_update_result(self):
        tracker = self._get_tracker()
        querier = tracker.get_querier('test_querymodule')
        querier._delete_state_object = mock.Mock(return_value=None)
        querier.delete_state_object_on_error = False

        # Ensure state objects are present.
        state1 = ActionExecutionState.get_by_id(ResultsTrackerTests.states['state1.yaml'].id)
        state2 = ActionExecutionState.get_by_id(ResultsTrackerTests.states['state2.yaml'].id)
        self.assertIsNotNone(state1)
        self.assertIsNotNone(state2)

        with mock.patch.object(
                querier.__class__, '_update_action_results',
                mock.MagicMock(side_effect=Exception('Mock update exception.'))):
            tracker._bootstrap()
            eventlet.sleep(1)

            exec_id = str(ResultsTrackerTests.states['state1.yaml'].execution_id)
            exec_db = LiveAction.get_by_id(exec_id)
            self.assertDictEqual(exec_db.result, {})

            exec_id = str(ResultsTrackerTests.states['state2.yaml'].execution_id)
            exec_db = LiveAction.get_by_id(exec_id)
            self.assertDictEqual(exec_db.result, {})

            tracker.shutdown()

        # Ensure deletes are not called.
        querier._delete_state_object.assert_not_called()
示例#6
0
    def test_notify_triggers_end_timestamp_none(self):
        liveaction_db = LiveActionDB(action='core.local')
        liveaction_db.id = bson.ObjectId()
        liveaction_db.description = ''
        liveaction_db.status = 'succeeded'
        liveaction_db.parameters = {}
        on_success = NotificationSubSchema(message='Action succeeded.')
        on_failure = NotificationSubSchema(message='Action failed.')
        liveaction_db.notify = NotificationSchema(on_success=on_success,
                                                  on_failure=on_failure)
        liveaction_db.start_timestamp = date_utils.get_datetime_utc_now()

        # This tests for end_timestamp being set to None, which can happen when a policy cancels
        # a request.
        # The assertions within "MockDispatcher.dispatch" will validate that the underlying code
        # handles this properly, so all we need to do is keep the call to "notifier.process" below
        liveaction_db.end_timestamp = None
        LiveAction.add_or_update(liveaction_db)

        execution = MOCK_EXECUTION
        execution.liveaction = vars(LiveActionAPI.from_model(liveaction_db))
        execution.status = liveaction_db.status

        dispatcher = NotifierTestCase.MockDispatcher(self)
        notifier = Notifier(connection=None, queues=[], trigger_dispatcher=dispatcher)
        notifier.process(execution)
示例#7
0
def _purge_action_models(execution_db):
    liveaction_id = execution_db.liveaction['id']

    if not liveaction_id:
        print('Invalid LiveAction id. Skipping delete: %s', execution_db)

    liveaction_db = None
    try:
        liveaction_db = LiveAction.get_by_id(liveaction_id)
    except:
        print('LiveAction with id: %s not found. Skipping delete.', liveaction_id)
    else:
        global DELETED_COUNT
        DELETED_COUNT += 1

    try:
        ActionExecution.delete(execution_db)
    except Exception as e:
        print('Exception deleting Execution model: %s, exception: %s',
              execution_db, str(e))
    else:
        try:
            LiveAction.delete(liveaction_db)
        except Exception as e:
            print('Zombie LiveAction left in db: %s. Exception: %s',
                  liveaction_db, str(e))
示例#8
0
文件: action.py 项目: Koulio/st2
def update_status(liveaction, new_status, result=None, publish=True):
    if liveaction.status == new_status:
        return liveaction

    old_status = liveaction.status

    liveaction = action_utils.update_liveaction_status(
        status=new_status, result=result, liveaction_id=liveaction.id, publish=False
    )

    action_execution = executions.update_execution(liveaction)

    msg = "The status of action execution is changed from %s to %s. " "<LiveAction.id=%s, ActionExecution.id=%s>" % (
        old_status,
        new_status,
        liveaction.id,
        action_execution.id,
    )

    extra = {"action_execution_db": action_execution, "liveaction_db": liveaction}

    LOG.audit(msg, extra=extra)
    LOG.info(msg)

    if publish:
        LiveAction.publish_status(liveaction)

    return liveaction
示例#9
0
    def test_cancel_subworkflow_action(self):
        liveaction1 = LiveActionDB(action=WF2_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)
        liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
        self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_RUNNING)

        liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)

        # Mock the children of the parent execution to make this
        # test case has subworkflow execution.
        with mock.patch.object(
                ActionExecutionDB, 'children',
                new_callable=mock.PropertyMock) as action_ex_children_mock:
            action_ex_children_mock.return_value = [execution2.id]

            mistral_context = liveaction1.context.get('mistral', None)
            self.assertIsNotNone(mistral_context)
            self.assertEqual(mistral_context['execution_id'], WF2_EXEC.get('id'))
            self.assertEqual(mistral_context['workflow_name'], WF2_EXEC.get('workflow_name'))

            requester = cfg.CONF.system_user.user
            liveaction1, execution1 = action_service.request_cancellation(liveaction1, requester)

            self.assertTrue(executions.ExecutionManager.update.called)
            self.assertEqual(executions.ExecutionManager.update.call_count, 2)

            calls = [
                mock.call(WF2_EXEC.get('id'), 'CANCELLED'),
                mock.call(WF1_EXEC.get('id'), 'CANCELLED')
            ]

            executions.ExecutionManager.update.assert_has_calls(calls, any_order=False)
示例#10
0
    def test_execution_cancellation(self):
        tracker = self._get_tracker()
        querier = tracker.get_querier('test_querymodule')
        querier._delete_state_object = mock.Mock(return_value=None)
        runners_utils.invoke_post_run = mock.Mock(return_value=None)

        # Ensure state objects are present.
        state1 = ActionExecutionState.get_by_id(ResultsTrackerTests.states['state1.yaml'].id)
        state2 = ActionExecutionState.get_by_id(ResultsTrackerTests.states['state2.yaml'].id)
        self.assertIsNotNone(state1)
        self.assertIsNotNone(state2)

        with mock.patch.object(
                querier.__class__, 'query',
                mock.MagicMock(return_value=(action_constants.LIVEACTION_STATUS_CANCELED, {}))):
            tracker._bootstrap()
            eventlet.sleep(2)

            exec_id = str(ResultsTrackerTests.states['state1.yaml'].execution_id)
            exec_db = LiveAction.get_by_id(exec_id)
            self.assertDictEqual(exec_db.result, {})

            exec_id = str(ResultsTrackerTests.states['state2.yaml'].execution_id)
            exec_db = LiveAction.get_by_id(exec_id)
            self.assertDictEqual(exec_db.result, {})

            tracker.shutdown()

        # Ensure deletes are called.
        self.assertEqual(2, querier._delete_state_object.call_count)

        # Ensure invoke_post_run is called.
        self.assertEqual(2, runners_utils.invoke_post_run.call_count)
    def test_resume(self):
        # Launch the workflow execution.
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

        # Pause the workflow execution.
        requester = cfg.CONF.system_user.user
        liveaction, execution = action_service.request_pause(liveaction, requester)
        executions.ExecutionManager.update.assert_called_with(WF1_EXEC.get('id'), 'PAUSED')
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_PAUSING)

        # Manually update the liveaction from pausing to paused. The paused state
        # is usually updated by the mistral querier.
        action_service.update_status(liveaction, action_constants.LIVEACTION_STATUS_PAUSED)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_PAUSED)

        # Resume the workflow execution.
        liveaction, execution = action_service.request_resume(liveaction, requester)
        executions.ExecutionManager.update.assert_called_with(WF1_EXEC.get('id'), 'RUNNING')
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
示例#12
0
    def test_chain_cancel_cascade_to_subworkflow(self):
        # A temp file is created during test setup. Ensure the temp file exists.
        # The test action chain will stall until this file is deleted. This gives
        # the unit test a moment to run any test related logic.
        path = self.temp_file_path
        self.assertTrue(os.path.exists(path))

        action = TEST_PACK + '.' + 'test_cancel_with_subworkflow'
        params = {'tempfile': path, 'message': 'foobar'}
        liveaction = LiveActionDB(action=action, parameters=params)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))

        # Wait until the liveaction is running.
        liveaction = self._wait_on_status(liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        # Wait for subworkflow to register.
        execution = self._wait_for_children(execution)
        self.assertEqual(len(execution.children), 1)

        # Wait until the subworkflow is running.
        task1_exec = ActionExecution.get_by_id(execution.children[0])
        task1_live = LiveAction.get_by_id(task1_exec.liveaction['id'])
        task1_live = self._wait_on_status(task1_live, action_constants.LIVEACTION_STATUS_RUNNING)

        # Request action chain to cancel.
        liveaction, execution = action_service.request_cancellation(liveaction, USERNAME)

        # Wait until the liveaction is canceling.
        liveaction = self._wait_on_status(liveaction, action_constants.LIVEACTION_STATUS_CANCELING)
        self.assertEqual(len(execution.children), 1)

        # Wait until the subworkflow is canceling.
        task1_exec = ActionExecution.get_by_id(execution.children[0])
        task1_live = LiveAction.get_by_id(task1_exec.liveaction['id'])
        task1_live = self._wait_on_status(task1_live, action_constants.LIVEACTION_STATUS_CANCELING)

        # Delete the temporary file that the action chain is waiting on.
        os.remove(path)
        self.assertFalse(os.path.exists(path))

        # Wait until the liveaction is canceled.
        liveaction = self._wait_on_status(liveaction, action_constants.LIVEACTION_STATUS_CANCELED)
        self.assertEqual(len(execution.children), 1)

        # Wait until the subworkflow is canceled.
        task1_exec = ActionExecution.get_by_id(execution.children[0])
        task1_live = LiveAction.get_by_id(task1_exec.liveaction['id'])
        task1_live = self._wait_on_status(task1_live, action_constants.LIVEACTION_STATUS_CANCELED)

        # Wait for non-blocking threads to complete. Ensure runner is not running.
        MockLiveActionPublisherNonBlocking.wait_all()

        # Check liveaction result.
        self.assertIn('tasks', liveaction.result)
        self.assertEqual(len(liveaction.result['tasks']), 1)

        subworkflow = liveaction.result['tasks'][0]
        self.assertEqual(len(subworkflow['result']['tasks']), 1)
        self.assertEqual(subworkflow['state'], action_constants.LIVEACTION_STATUS_CANCELED)
示例#13
0
文件: handler.py 项目: mahak/st2
    def _update_to_scheduled(liveaction_db, execution_queue_item_db):
        liveaction_id = str(liveaction_db.id)
        queue_item_id = str(execution_queue_item_db.id)

        extra = {
            'liveaction_id': liveaction_id,
            'liveaction_status': liveaction_db.status,
            'queue_item_id': queue_item_id
        }

        # Update liveaction status to "scheduled".
        LOG.info('Liveaction (%s) Status Update to Scheduled 1: %s (%s)',
                liveaction_id, liveaction_db.status, queue_item_id, extra=extra)

        if liveaction_db.status in [action_constants.LIVEACTION_STATUS_REQUESTED,
                                    action_constants.LIVEACTION_STATUS_DELAYED]:
            liveaction_db = action_service.update_status(
                liveaction_db, action_constants.LIVEACTION_STATUS_SCHEDULED, publish=False)

        # Publish the "scheduled" status here manually. Otherwise, there could be a
        # race condition with the update of the action_execution_db if the execution
        # of the liveaction completes first.
        LiveAction.publish_status(liveaction_db)

        extra['liveaction_status'] = liveaction_db.status

        # Delete execution queue entry only after status is published.
        ActionExecutionSchedulingQueue.delete(execution_queue_item_db)
        LOG.info('Liveaction (%s) Status Update to Scheduled 2: %s (%s)',
                liveaction_id, liveaction_db.status, queue_item_id)
示例#14
0
    def test_liveaction_gets_deleted(self):
        now = date_utils.get_datetime_utc_now()
        start_ts = now - timedelta(days=15)
        end_ts = now - timedelta(days=14)

        liveaction_model = copy.deepcopy(self.models['liveactions']['liveaction4.yaml'])
        liveaction_model['start_timestamp'] = start_ts
        liveaction_model['end_timestamp'] = end_ts
        liveaction_model['status'] = action_constants.LIVEACTION_STATUS_SUCCEEDED
        liveaction = LiveAction.add_or_update(liveaction_model)

        # Write one execution before cut-off threshold
        exec_model = copy.deepcopy(self.models['executions']['execution1.yaml'])
        exec_model['start_timestamp'] = start_ts
        exec_model['end_timestamp'] = end_ts
        exec_model['status'] = action_constants.LIVEACTION_STATUS_SUCCEEDED
        exec_model['id'] = bson.ObjectId()
        exec_model['liveaction']['id'] = str(liveaction.id)
        ActionExecution.add_or_update(exec_model)

        liveactions = LiveAction.get_all()
        executions = ActionExecution.get_all()
        self.assertEqual(len(liveactions), 1)
        self.assertEqual(len(executions), 1)

        purge_executions(logger=LOG, timestamp=now - timedelta(days=10))

        liveactions = LiveAction.get_all()
        executions = ActionExecution.get_all()
        self.assertEqual(len(executions), 0)
        self.assertEqual(len(liveactions), 0)
示例#15
0
    def test_notify_triggers_jinja_patterns(self, dispatch):
        liveaction_db = LiveActionDB(action='core.local')
        liveaction_db.id = bson.ObjectId()
        liveaction_db.description = ''
        liveaction_db.status = 'succeeded'
        liveaction_db.parameters = {'cmd': 'mamma mia', 'runner_foo': 'foo'}
        on_success = NotificationSubSchema(message='Command {{action_parameters.cmd}} succeeded.',
                                           data={'stdout': '{{action_results.stdout}}'})
        liveaction_db.notify = NotificationSchema(on_success=on_success)
        liveaction_db.start_timestamp = date_utils.get_datetime_utc_now()
        liveaction_db.end_timestamp = \
            (liveaction_db.start_timestamp + datetime.timedelta(seconds=50))

        LiveAction.add_or_update(liveaction_db)

        execution = MOCK_EXECUTION
        execution.liveaction = vars(LiveActionAPI.from_model(liveaction_db))
        execution.status = liveaction_db.status

        notifier = Notifier(connection=None, queues=[])
        notifier.process(execution)
        exp = {'status': 'succeeded',
               'start_timestamp': isotime.format(liveaction_db.start_timestamp),
               'route': 'notify.default', 'runner_ref': 'local-shell-cmd',
               'channel': 'notify.default', 'message': u'Command mamma mia succeeded.',
               'data': {'result': '{}', 'stdout': 'stuff happens'},
               'action_ref': u'core.local',
               'execution_id': str(MOCK_EXECUTION.id),
               'end_timestamp': isotime.format(liveaction_db.end_timestamp)}
        dispatch.assert_called_once_with('core.st2.generic.notifytrigger', payload=exp,
                                         trace_context={})
        notifier.process(execution)
示例#16
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")
示例#17
0
文件: action_db.py 项目: ipv1337/st2
def update_liveaction_status(
    status=None,
    result=None,
    context=None,
    end_timestamp=None,
    liveaction_id=None,
    runner_info=None,
    liveaction_db=None,
    publish=True,
):
    """
        Update the status of the specified LiveAction to the value provided in
        new_status.

        The LiveAction may be specified using either liveaction_id, or as an
        liveaction_db instance.
    """

    if (liveaction_id is None) and (liveaction_db is None):
        raise ValueError("Must specify an liveaction_id or an liveaction_db when " "calling update_LiveAction_status")

    if liveaction_db is None:
        liveaction_db = get_liveaction_by_id(liveaction_id)

    if status not in LIVEACTION_STATUSES:
        raise ValueError(
            'Attempting to set status for LiveAction "%s" ' 'to unknown status string. Unknown status is "%s"',
            liveaction_db,
            status,
        )

    extra = {"liveaction_db": liveaction_db}
    LOG.debug('Updating ActionExection: "%s" with status="%s"', liveaction_db.id, status, extra=extra)

    old_status = liveaction_db.status
    liveaction_db.status = status

    if result:
        liveaction_db.result = result

    if context:
        liveaction_db.context.update(context)

    if end_timestamp:
        liveaction_db.end_timestamp = end_timestamp

    if runner_info:
        liveaction_db.runner_info = runner_info

    liveaction_db = LiveAction.add_or_update(liveaction_db)

    LOG.debug("Updated status for LiveAction object.", extra=extra)

    if publish and status != old_status:
        LiveAction.publish_status(liveaction_db)
        LOG.debug("Published status for LiveAction object.", extra=extra)

    return liveaction_db
示例#18
0
    def test_basic_cancel(self):
        liveaction = LiveActionDB(action='executions.local', parameters={'cmd': 'uname -a'})
        liveaction, _ = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        # Cancel execution.
        action_service.request_cancellation(liveaction, cfg.CONF.system_user.user)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_CANCELED)
        self.assertDictEqual(liveaction.result, MOCK_RESULT)
示例#19
0
文件: scheduler.py 项目: rlugojr/st2
    def process(self, request):
        """Schedules the LiveAction and publishes the request
        to the appropriate action runner(s).

        LiveAction in statuses other than "requested" are ignored.

        :param request: Action execution request.
        :type request: ``st2common.models.db.liveaction.LiveActionDB``
        """

        if request.status != action_constants.LIVEACTION_STATUS_REQUESTED:
            LOG.info(
                '%s is ignoring %s (id=%s) with "%s" status.',
                self.__class__.__name__,
                type(request),
                request.id,
                request.status,
            )
            return

        try:
            liveaction_db = action_utils.get_liveaction_by_id(request.id)
        except StackStormDBObjectNotFoundError:
            LOG.exception("Failed to find liveaction %s in the database.", request.id)
            raise

        # Apply policies defined for the action.
        liveaction_db = self._apply_pre_run_policies(liveaction_db=liveaction_db)

        # Exit if the status of the request is no longer runnable.
        # The status could have be changed by one of the policies.
        if liveaction_db.status not in [
            action_constants.LIVEACTION_STATUS_REQUESTED,
            action_constants.LIVEACTION_STATUS_SCHEDULED,
        ]:
            LOG.info(
                '%s is ignoring %s (id=%s) with "%s" status after policies are applied.',
                self.__class__.__name__,
                type(request),
                request.id,
                liveaction_db.status,
            )
            return

        # Update liveaction status to "scheduled".
        if liveaction_db.status == action_constants.LIVEACTION_STATUS_REQUESTED:
            liveaction_db = action_service.update_status(
                liveaction_db, action_constants.LIVEACTION_STATUS_SCHEDULED, publish=False
            )

        # Publish the "scheduled" status here manually. Otherwise, there could be a
        # race condition with the update of the action_execution_db if the execution
        # of the liveaction completes first.
        LiveAction.publish_status(liveaction_db)
示例#20
0
 def test_dispatch_runner_failure(self):
     runner_container = get_runner_container()
     params = {"actionstr": "bar"}
     liveaction_db = self._get_failingaction_exec_db_model(params)
     liveaction_db = LiveAction.add_or_update(liveaction_db)
     executions.create_execution_object(liveaction_db)
     runner_container.dispatch(liveaction_db)
     # pickup updated liveaction_db
     liveaction_db = LiveAction.get_by_id(liveaction_db.id)
     self.assertTrue("message" in liveaction_db.result)
     self.assertTrue("traceback" in liveaction_db.result)
    def test_invalid_trace_id_provided(self):
        liveactions = LiveAction.get_all()
        self.assertEqual(len(liveactions), 1)  # fixtures loads it.
        self.traceable_liveaction['context']['trace_context'] = {'id_': 'balleilaka'}

        self.assertRaises(TraceNotFoundException, action_services.request,
                          self.traceable_liveaction)

        # Make sure no liveactions are left behind
        liveactions = LiveAction.get_all()
        self.assertEqual(len(liveactions), 0)
示例#22
0
    def test_cancel_on_task_action_concurrency_by_attr(self):
        # Delete other policies in the test pack to avoid conflicts.
        required_policy = 'mistral_tests.cancel_on_concurrency_by_attr'
        self._drop_all_other_policies(required_policy)

        # Get threshold from the policy.
        policy = Policy.get_by_ref(required_policy)
        threshold = policy.parameters.get('threshold', 0)
        self.assertGreater(threshold, 0)

        params = {'friend': 'grande animalerie'}

        # Launch instances of the workflow up to threshold.
        for i in range(0, threshold):
            liveaction = LiveActionDB(action=WF1_NAME, parameters=params)
            liveaction, execution1 = action_service.request(liveaction)
            liveaction = LiveAction.get_by_id(str(liveaction.id))

            liveaction = self._wait_on_status(
                liveaction,
                action_constants.LIVEACTION_STATUS_RUNNING
            )

        # Check number of running instances
        running = LiveAction.count(
            action=WF1_NAME, status=action_constants.LIVEACTION_STATUS_RUNNING,
            parameters__friend=params['friend'])

        self.assertEqual(running, threshold)

        # Mock the mistral runner cancel method to assert cancel is called.
        mistral_runner_cls = runners.get_runner('mistral-v2').__class__
        mock_cancel_return_value = (action_constants.LIVEACTION_STATUS_CANCELING, None, None)
        mock_cancel = mock.MagicMock(return_value=mock_cancel_return_value)

        with mock.patch.object(mistral_runner_cls, 'cancel', mock_cancel):
            # Launch another instance of the workflow with mistral callback defined
            # to indicate that this is executed under a workflow.
            callback = {
                'source': MISTRAL_RUNNER_NAME,
                'url': 'http://127.0.0.1:8989/v2/action_executions/12345'
            }

            liveaction2 = LiveActionDB(action=WF1_NAME, parameters=params, callback=callback)
            liveaction2, execution2 = action_service.request(liveaction2)
            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))

            # Assert cancel has been called.
            liveaction2 = self._wait_on_status(
                liveaction2,
                action_constants.LIVEACTION_STATUS_CANCELING
            )

            mistral_runner_cls.cancel.assert_called_once_with()
示例#23
0
 def _update_action_results(self, execution_id, status, results):
     liveaction_db = LiveAction.get_by_id(execution_id)
     if not liveaction_db:
         raise Exception('No DB model for liveaction_id: %s' % execution_id)
     liveaction_db.result = results
     liveaction_db.status = status
     # update liveaction, update actionexecution and then publish update.
     updated_liveaction = LiveAction.add_or_update(liveaction_db, publish=False)
     executions.update_execution(updated_liveaction)
     LiveAction.publish_update(updated_liveaction)
     return updated_liveaction
示例#24
0
 def test_dispatch_override_default_action_params(self):
     runner_container = get_runner_container()
     params = {"actionstr": "foo", "actionint": 20}
     liveaction_db = self._get_action_exec_db_model(RunnerContainerTest.action_db, params)
     liveaction_db = LiveAction.add_or_update(liveaction_db)
     executions.create_execution_object(liveaction_db)
     # Assert that execution ran successfully.
     runner_container.dispatch(liveaction_db)
     liveaction_db = LiveAction.get_by_id(liveaction_db.id)
     result = liveaction_db.result
     self.assertTrue(result.get("action_params").get("actionint") == 20)
     self.assertTrue(result.get("action_params").get("actionstr") == "foo")
示例#25
0
    def test_failed_cancel(self):
        liveaction = LiveActionDB(action='executions.local', parameters={'cmd': 'uname -a'})
        liveaction, _ = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        # Cancel execution.
        action_service.request_cancellation(liveaction, cfg.CONF.system_user.user)

        # Cancellation failed and execution state remains "canceling".
        ActionRunner.cancel.assert_called_once_with()
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_CANCELING)
示例#26
0
    def test_no_double_entries(self):
        self.reset()

        liveaction_db = self._create_liveaction_db()

        LiveAction.publish_status(liveaction_db)
        LiveAction.publish_status(liveaction_db)

        schedule_q_db = self.scheduling_queue._get_next_execution()
        self.assertIsNotNone(schedule_q_db)

        schedule_q_db = self.scheduling_queue._get_next_execution()
        self.assertIsNone(schedule_q_db)
示例#27
0
 def test_bootstrap(self):
     tracker = results_tracker.get_tracker()
     tracker._bootstrap()
     eventlet.sleep(0.2)
     exec_id = str(ResultsTrackerTests.states['state1.yaml'].execution_id)
     exec_db = LiveAction.get_by_id(exec_id)
     self.assertTrue(exec_db.result['called_with'][exec_id] is not None,
                     exec_db.result)
     exec_id = str(ResultsTrackerTests.states['state2.yaml'].execution_id)
     exec_db = LiveAction.get_by_id(exec_id)
     self.assertTrue(exec_db.result['called_with'][exec_id] is not None,
                     exec_db.result)
     tracker.shutdown()
示例#28
0
    def test_created_temporary_auth_token_is_correctly_scoped_to_user_who_ran_the_action(self):
        params = {
            'actionstr': 'bar',
            'mock_status': action_constants.LIVEACTION_STATUS_SUCCEEDED
        }

        global global_runner
        global_runner = None

        def mock_get_runner(*args, **kwargs):
            global global_runner
            runner = original_get_runner(*args, **kwargs)
            global_runner = runner
            return runner

        # user joe_1

        runner_container = get_runner_container()
        original_get_runner = runner_container._get_runner

        liveaction_db = self._get_failingaction_exec_db_model(params)
        liveaction_db = LiveAction.add_or_update(liveaction_db)
        liveaction_db.context = {'user': '******'}
        executions.create_execution_object(liveaction_db)

        runner_container._get_runner = mock_get_runner

        self.assertEqual(getattr(global_runner, 'auth_token', None), None)
        runner_container.dispatch(liveaction_db)
        self.assertEqual(global_runner.auth_token.user, 'user_joe_1')
        self.assertEqual(global_runner.auth_token.metadata['service'], 'actions_container')

        runner_container._get_runner = original_get_runner

        # user mark_1
        global_runner = None
        runner_container = get_runner_container()
        original_get_runner = runner_container._get_runner

        liveaction_db = self._get_failingaction_exec_db_model(params)
        liveaction_db = LiveAction.add_or_update(liveaction_db)
        liveaction_db.context = {'user': '******'}
        executions.create_execution_object(liveaction_db)
        original_get_runner = runner_container._get_runner

        runner_container._get_runner = mock_get_runner

        self.assertEqual(getattr(global_runner, 'auth_token', None), None)
        runner_container.dispatch(liveaction_db)
        self.assertEqual(global_runner.auth_token.user, 'user_mark_2')
        self.assertEqual(global_runner.auth_token.metadata['service'], 'actions_container')
示例#29
0
    def test_no_processing_of_non_requested_actions(self):
        self.reset()

        for status in action_constants.LIVEACTION_STATUSES:
            liveaction_db = self._create_liveaction_db(status=status)

            LiveAction.publish_status(liveaction_db)

            schedule_q_db = self.scheduling_queue._get_next_execution()

            if status is action_constants.LIVEACTION_STATUS_REQUESTED:
                self.assertIsNotNone(schedule_q_db)
            else:
                self.assertIsNone(schedule_q_db)
示例#30
0
    def test_noop_cancel(self):
        liveaction = LiveActionDB(action='executions.local', parameters={'cmd': 'uname -a'})
        liveaction, _ = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_REQUESTED)

        # Cancel execution.
        action_service.request_cancellation(liveaction, cfg.CONF.system_user.user)

        # Cancel is only called when liveaction is still in running state.
        # Otherwise, the cancellation is only a state change.
        self.assertFalse(ActionRunner.cancel.called)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_CANCELED)
示例#31
0
    def test_callback_paused_state(self):
        local_runner_cls = self.get_runner_class('local_runner')
        local_run_result = (action_constants.LIVEACTION_STATUS_PAUSED,
                            NON_EMPTY_RESULT, None)
        local_runner_cls.run = mock.Mock(return_value=local_run_result)
        expected_mistral_status = self.status_map[local_run_result[0]]

        liveaction = LiveActionDB(
            action='core.local',
            parameters={'cmd': 'uname -a'},
            callback={
                'source': MISTRAL_RUNNER_NAME,
                'url': 'http://127.0.0.1:8989/v2/action_executions/12345'
            })

        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))

        self.assertEqual(liveaction.status, local_run_result[0])

        action_executions.ActionExecutionManager.update.assert_called_with(
            '12345', state=expected_mistral_status, output=NON_EMPTY_RESULT)
示例#32
0
    def test_launch_workflow_with_notifications(self):
        notify_data = {'on_complete': {'routes': ['slack'],
                       'message': '"@channel: Action succeeded."', 'data': {}}}

        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, notify=notify_data)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            'st2_execution_id': str(execution.id),
            'st2_liveaction_id': str(liveaction.id),
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'api_url': 'http://0.0.0.0:9101/v1',
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': {
                            'execution_id': str(execution.id)
                        },
                        'notify': NotificationsHelper.from_model(liveaction.notify),
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env)
示例#33
0
    def test_resume_option_reset_tasks(self):
        patched_mistral_runner = runners.get_runner('mistral-v2').__class__

        mock_resume_result = (action_constants.LIVEACTION_STATUS_RUNNING, {
            'tasks': []
        }, {
            'execution_id': str(uuid.uuid4())
        })

        with mock.patch.object(
                patched_mistral_runner, 'resume_workflow',
                mock.MagicMock(return_value=mock_resume_result)):

            liveaction1 = LiveActionDB(action=WF1_NAME,
                                       parameters=ACTION_PARAMS)
            liveaction1, execution1 = action_service.request(liveaction1)
            self.assertFalse(patched_mistral_runner.resume_workflow.called)

            # Rerun the execution.
            context = {
                're-run': {
                    'ref': execution1.id,
                    'tasks': ['x', 'y'],
                    'reset': ['y']
                }
            }

            liveaction2 = LiveActionDB(action=WF1_NAME,
                                       parameters=ACTION_PARAMS,
                                       context=context)
            liveaction2, execution2 = action_service.request(liveaction2)
            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
            self.assertEqual(liveaction2.status,
                             action_constants.LIVEACTION_STATUS_RUNNING)

            task_specs = {'x': {'reset': False}, 'y': {'reset': True}}

            patched_mistral_runner.resume_workflow.assert_called_with(
                ex_ref=execution1, task_specs=task_specs)
示例#34
0
    def test_chain_cancel(self):
        # A temp file is created during test setup. Ensure the temp file exists.
        # The test action chain will stall until this file is deleted. This gives
        # the unit test a moment to run any test related logic.
        path = self.temp_file_path
        self.assertTrue(os.path.exists(path))

        action = TEST_PACK + "." + "test_cancel"
        params = {"tempfile": path, "message": "foobar"}
        liveaction = LiveActionDB(action=action, parameters=params)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))

        # Wait until the liveaction is running.
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        # Request action chain to cancel.
        liveaction, execution = action_service.request_cancellation(
            liveaction, USERNAME)

        # Wait until the liveaction is canceling.
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_CANCELING)

        # Delete the temporary file that the action chain is waiting on.
        os.remove(path)
        self.assertFalse(os.path.exists(path))

        # Wait until the liveaction is canceled.
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_CANCELED)

        # Wait for non-blocking threads to complete. Ensure runner is not running.
        MockLiveActionPublisherNonBlocking.wait_all()

        # Check liveaction result.
        self.assertIn("tasks", liveaction.result)
        self.assertEqual(len(liveaction.result["tasks"]), 1)
示例#35
0
文件: notifier.py 项目: ljhmily/st2
    def process(self, execution_db):
        execution_id = str(execution_db.id)
        extra = {'execution': execution_db}
        LOG.debug('Processing execution %s', execution_id, extra=extra)

        if execution_db.status not in LIVEACTION_COMPLETED_STATES:
            LOG.debug(
                'Skipping processing of execution %s since it\'s not in a completed state'
                % (execution_id),
                extra=extra)
            return

        liveaction_id = execution_db.liveaction['id']
        liveaction_db = LiveAction.get_by_id(liveaction_id)
        self._apply_post_run_policies(liveaction_db=liveaction_db)

        if liveaction_db.notify is not None:
            self._post_notify_triggers(liveaction_db=liveaction_db,
                                       execution_db=execution_db)

        self._post_generic_trigger(liveaction_db=liveaction_db,
                                   execution_db=execution_db)
示例#36
0
    def test_callback_retry(self):
        cfg.CONF.set_default('max_attempts', 3, group='mistral')
        cfg.CONF.set_default('retry_wait', 0, group='mistral')

        liveaction = LiveActionDB(
            action='core.local',
            parameters={'cmd': 'uname -a'},
            callback={
                'source': 'mistral',
                'url': 'http://localhost:8989/v2/action_executions/12345'
            })

        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status,
                         action_constants.LIVEACTION_STATUS_SUCCEEDED)

        calls = [
            call('12345', state='SUCCESS', output=NON_EMPTY_RESULT)
            for i in range(0, 2)
        ]
        action_executions.ActionExecutionManager.update.assert_has_calls(calls)
示例#37
0
文件: notifier.py 项目: zwunix/st2
    def process(self, execution_db):
        execution_id = str(execution_db.id)
        extra = {'execution': execution_db}
        LOG.debug('Processing action execution "%s".', execution_id, extra=extra)

        # Get the corresponding liveaction record.
        liveaction_db = LiveAction.get_by_id(execution_db.liveaction['id'])

        if execution_db.status in LIVEACTION_COMPLETED_STATES:
            # If the action execution is executed under an orquesta workflow, policies for the
            # action execution will be applied by the workflow engine. A policy may affect the
            # final state of the action execution thereby impacting the state of the workflow.
            if not workflow_service.is_action_execution_under_workflow_context(execution_db):
                with CounterWithTimer(key='notifier.apply_post_run_policies'):
                    policy_service.apply_post_run_policies(liveaction_db)

            if liveaction_db.notify:
                with CounterWithTimer(key='notifier.notify_trigger.post'):
                    self._post_notify_triggers(liveaction_db=liveaction_db,
                                               execution_db=execution_db)

        self._post_generic_trigger(liveaction_db=liveaction_db, execution_db=execution_db)
示例#38
0
    def test_resume_option(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)
        self.assertFalse(MistralRunner.resume.called)

        # Rerun the execution.
        context = {'re-run': {'ref': execution1.id, 'tasks': ['x']}}

        liveaction2 = LiveActionDB(action=WF1_NAME,
                                   parameters=ACTION_PARAMS,
                                   context=context)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
        self.assertEqual(liveaction2.status,
                         action_constants.LIVEACTION_STATUS_RUNNING)

        task_specs = {'x': {'reset': False}}

        MistralRunner.resume.assert_called_with(ex_ref=execution1,
                                                task_specs=task_specs)
示例#39
0
    def test_callback_retry_exhausted(self):
        local_runner_cls = self.get_runner_class('local_runner')
        local_run_result = (action_constants.LIVEACTION_STATUS_SUCCEEDED, NON_EMPTY_RESULT, None)
        local_runner_cls.run = mock.Mock(return_value=local_run_result)

        liveaction = LiveActionDB(
            action='core.local', parameters={'cmd': 'uname -a'},
            callback={
                'source': MISTRAL_RUNNER_NAME,
                'url': 'http://127.0.0.1:8989/v2/action_executions/12345'
            }
        )

        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_SUCCEEDED)

        # This test initially setup mock for action_executions.ActionExecutionManager.update
        # to fail the first 4 times and return success on the 5th times. The max attempts
        # is set to 3. We expect only 3 calls to pass thru the update method.
        calls = [call('12345', state='SUCCESS', output=NON_EMPTY_RESULT) for i in range(0, 2)]
        action_executions.ActionExecutionManager.update.assert_has_calls(calls)
示例#40
0
    def test_launch_workflow_with_st2_https(self):
        cfg.CONF.set_override('api_url', 'https://0.0.0.0:9101', group='auth')

        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            'st2_execution_id': str(execution.id),
            'st2_liveaction_id': str(liveaction.id),
            'st2_action_api_url': 'https://0.0.0.0:9101/v1',
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'api_url': 'https://0.0.0.0:9101/v1',
                        'endpoint': 'https://0.0.0.0:9101/v1/actionexecutions',
                        'parent': {
                            'pack': 'mistral_tests',
                            'execution_id': str(execution.id)
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env)
示例#41
0
    def test_callback_resuming_state(self):
        local_runner_cls = self.get_runner_class('local_runner')
        local_run_result = (action_constants.LIVEACTION_STATUS_RESUMING,
                            NON_EMPTY_RESULT, None)
        local_runner_cls.run = mock.Mock(return_value=local_run_result)
        local_resume_result = (action_constants.LIVEACTION_STATUS_RUNNING,
                               NON_EMPTY_RESULT, None)
        local_runner_cls.resume = mock.Mock(return_value=local_resume_result)

        liveaction = LiveActionDB(
            action='core.local',
            parameters={'cmd': 'uname -a'},
            callback={
                'source': MISTRAL_RUNNER_NAME,
                'url': 'http://127.0.0.1:8989/v2/action_executions/12345'
            })

        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))

        self.assertEqual(liveaction.status, local_resume_result[0])
        self.assertFalse(
            action_executions.ActionExecutionManager.update.called)
    def test_update_LiveAction_status_invalid(self):
        liveaction_db = LiveActionDB()
        liveaction_db.status = 'initializing'
        liveaction_db.start_timestamp = get_datetime_utc_now()
        liveaction_db.action = ResourceReference(
            name=ActionDBUtilsTestCase.action_db.name,
            pack=ActionDBUtilsTestCase.action_db.pack).ref
        params = {
            'actionstr': 'foo',
            'some_key_that_aint_exist_in_action_or_runner': 'bar',
            'runnerint': 555
        }
        liveaction_db.parameters = params
        liveaction_db = LiveAction.add_or_update(liveaction_db)

        # Update by id.
        self.assertRaises(ValueError,
                          action_db_utils.update_liveaction_status,
                          status='mea culpa',
                          liveaction_id=liveaction_db.id)

        # Verify that state is not published.
        self.assertFalse(LiveActionPublisher.publish_state.called)
示例#43
0
    def test_state_db_not_created_for_async_actions(self):
        runner_container = get_runner_container()

        params = {
            'actionstr': 'foo',
            'actionint': 20,
            'async_test': True
        }

        liveaction_db = self._get_liveaction_model(
            RunnerContainerTest.async_action_db,
            params
        )

        liveaction_db = LiveAction.add_or_update(liveaction_db)
        executions.create_execution_object(liveaction_db)

        # Assert that execution ran without exceptions.
        runner_container.dispatch(liveaction_db)
        states = ActionExecutionState.get_all()
        found = [state for state in states if state.execution_id == liveaction_db.id]

        self.assertTrue(len(found) == 0, 'There should not be a state db object.')
示例#44
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
        )
示例#45
0
    def test_update_liveaction_with_incorrect_output_schema(self):
        liveaction_db = LiveActionDB()
        liveaction_db.status = "initializing"
        liveaction_db.start_timestamp = get_datetime_utc_now()
        liveaction_db.action = ResourceReference(
            name=ActionDBUtilsTestCase.action_db.name,
            pack=ActionDBUtilsTestCase.action_db.pack,
        ).ref
        params = {
            "actionstr": "foo",
            "some_key_that_aint_exist_in_action_or_runner": "bar",
            "runnerint": 555,
        }
        liveaction_db.parameters = params
        runner = mock.MagicMock()
        runner.output_schema = {"notaparam": {"type": "boolean"}}
        liveaction_db.runner = runner
        liveaction_db = LiveAction.add_or_update(liveaction_db)
        origliveaction_db = copy.copy(liveaction_db)

        now = get_datetime_utc_now()
        status = "succeeded"
        result = "Work is done."
        context = {"third_party_id": uuid.uuid4().hex}
        newliveaction_db = action_db_utils.update_liveaction_status(
            status=status,
            result=result,
            context=context,
            end_timestamp=now,
            liveaction_id=liveaction_db.id,
        )

        self.assertEqual(origliveaction_db.id, newliveaction_db.id)
        self.assertEqual(newliveaction_db.status, status)
        self.assertEqual(newliveaction_db.result, result)
        self.assertDictEqual(newliveaction_db.context, context)
        self.assertEqual(newliveaction_db.end_timestamp, now)
示例#46
0
    def test_launch_workflow(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status,
                         action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            'st2_execution_id': str(execution.id),
            'st2_liveaction_id': str(liveaction.id),
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': {
                            'execution_id': str(execution.id)
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env)
示例#47
0
    def test_chained_executions(self):
        liveaction = LiveActionDB(action="executions.chain")
        liveaction, _ = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, 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)
        # NOTE: Timestamp of liveaction and execution may be a bit different, depending on how long
        # it takes to persist each object in the database
        self.assertEqual(
            execution.end_timestamp.replace(microsecond=0),
            liveaction.end_timestamp.replace(microsecond=0),
        )
        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"], "local-shell-cmd")
示例#48
0
def test_save_large_execution(benchmark, fixture_file: str, approach: str) -> None:
    with open(os.path.join(FIXTURES_DIR, fixture_file), "r") as fp:
        content = fp.read()

    data = json.loads(content)

    db_setup()

    model_cls = get_model_class_for_approach(approach=approach)

    def run_benchmark():
        live_action_db = model_cls()
        live_action_db.status = "succeeded"
        live_action_db.action = "core.local"
        live_action_db.result = data

        inserted_live_action_db = LiveAction.add_or_update(live_action_db)
        return inserted_live_action_db

    inserted_live_action_db = benchmark(run_benchmark)
    retrieved_live_action_db = LiveAction.get_by_id(inserted_live_action_db.id)
    # Assert that result is correctly converted back to dict on retrieval
    assert inserted_live_action_db.result == data
    assert inserted_live_action_db == retrieved_live_action_db
示例#49
0
def recover_delayed_executions():
    coordinator = coordination.get_coordinator()
    dt_now = date_utils.get_datetime_utc_now()
    dt_delta = datetime.timedelta(
        seconds=cfg.CONF.scheduler.delayed_execution_recovery)
    dt_timeout = dt_now - dt_delta

    with coordinator.get_lock('st2-rescheduling-delayed-executions'):
        liveactions = LiveAction.query(
            status=action_constants.LIVEACTION_STATUS_DELAYED,
            start_timestamp__lte=dt_timeout,
            order_by=['start_timestamp'])

        if not liveactions:
            return

        LOG.info(
            'There are %d liveactions that have been delayed for longer than %d seconds.',
            len(liveactions), cfg.CONF.scheduler.delayed_execution_recovery)

        # Update status to requested and publish status for each liveactions.
        rescheduled = 0
        for instance in liveactions:
            try:
                action_service.update_status(
                    instance,
                    action_constants.LIVEACTION_STATUS_REQUESTED,
                    publish=True)
                rescheduled += 1
            except:
                LOG.exception(
                    'Unable to reschedule liveaction. <LiveAction.id=%s>',
                    instance.id)

        LOG.info('Rescheduled %d out of %d delayed liveactions.',
                 len(liveactions), rescheduled)
示例#50
0
    def test_callback_retry(self):
        local_runner_cls = self.get_runner_class('local_runner')
        local_run_result = (action_constants.LIVEACTION_STATUS_SUCCEEDED,
                            NON_EMPTY_RESULT, None)
        local_runner_cls.run = mock.Mock(return_value=local_run_result)

        liveaction = LiveActionDB(
            action='core.local',
            parameters={'cmd': 'uname -a'},
            callback={
                'source': MISTRAL_RUNNER_NAME,
                'url': 'http://127.0.0.1:8989/v2/action_executions/12345'
            })

        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status,
                         action_constants.LIVEACTION_STATUS_SUCCEEDED)

        calls = [
            call('12345', state='SUCCESS', output=NON_EMPTY_RESULT)
            for i in range(0, 2)
        ]
        action_executions.ActionExecutionManager.update.assert_has_calls(calls)
示例#51
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')

        # 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'] == self.runner_type_db.name
                    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)
示例#52
0
    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
示例#53
0
    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'])
示例#54
0
def create_request(liveaction):
    """
    Create an action execution.

    :return: (liveaction, execution)
    :rtype: tuple
    """
    # Use the user context from the parent action execution. Subtasks in a workflow
    # action can be invoked by a system user and so we want to use the user context
    # from the original workflow action.
    parent_context = executions.get_parent_context(liveaction)
    if parent_context:
        parent_user = parent_context.get('user', None)
        if parent_user:
            liveaction.context['user'] = parent_user

    # Validate action.
    action_db = action_utils.get_action_by_ref(liveaction.action)
    if not action_db:
        raise ValueError('Action "%s" cannot be found.' % liveaction.action)
    if not action_db.enabled:
        raise ValueError('Unable to execute. Action "%s" is disabled.' %
                         liveaction.action)

    runnertype_db = action_utils.get_runnertype_by_name(
        action_db.runner_type['name'])

    if not hasattr(liveaction, 'parameters'):
        liveaction.parameters = dict()

    # Validate action parameters.
    schema = util_schema.get_schema_for_action_parameters(action_db)
    validator = util_schema.get_validator()
    util_schema.validate(liveaction.parameters,
                         schema,
                         validator,
                         use_default=True,
                         allow_default_none=True)

    # validate that no immutable params are being overriden. Although possible to
    # ignore the override it is safer to inform the user to avoid surprises.
    immutables = _get_immutable_params(action_db.parameters)
    immutables.extend(_get_immutable_params(runnertype_db.runner_parameters))
    overridden_immutables = [
        p for p in six.iterkeys(liveaction.parameters) if p in immutables
    ]
    if len(overridden_immutables) > 0:
        raise ValueError(
            'Override of immutable parameter(s) %s is unsupported.' %
            str(overridden_immutables))

    # Set notification settings for action.
    # XXX: There are cases when we don't want notifications to be sent for a particular
    # execution. So we should look at liveaction.parameters['notify']
    # and not set liveaction.notify.
    if not _is_notify_empty(action_db.notify):
        liveaction.notify = action_db.notify

    # Write to database and send to message queue.
    liveaction.status = action_constants.LIVEACTION_STATUS_REQUESTED
    liveaction.start_timestamp = date_utils.get_datetime_utc_now()

    # Set the "action_is_workflow" attribute
    liveaction.action_is_workflow = action_db.is_workflow()

    # Publish creation after both liveaction and actionexecution are created.
    liveaction = LiveAction.add_or_update(liveaction, publish=False)

    # Get trace_db if it exists. This could throw. If it throws, we have to cleanup
    # liveaction object so we don't see things in requested mode.
    trace_db = None
    try:
        _, trace_db = trace_service.get_trace_db_by_live_action(liveaction)
    except db_exc.StackStormDBObjectNotFoundError as e:
        _cleanup_liveaction(liveaction)
        raise trace_exc.TraceNotFoundException(str(e))

    execution = executions.create_execution_object(liveaction, publish=False)

    if trace_db:
        trace_service.add_or_update_given_trace_db(
            trace_db=trace_db,
            action_executions=[
                trace_service.get_trace_component_for_action_execution(
                    execution, liveaction)
            ])

    return liveaction, execution
示例#55
0
    def _schedule_execution(self,
                            liveaction,
                            requester_user,
                            user=None,
                            context_string=None,
                            show_secrets=False,
                            pack=None):
        # Initialize execution context if it does not exist.
        if not hasattr(liveaction, 'context'):
            liveaction.context = dict()

        liveaction.context['user'] = user
        liveaction.context['pack'] = pack
        LOG.debug('User is: %s' % liveaction.context['user'])

        # Retrieve other st2 context from request header.
        if context_string:
            context = try_loads(context_string)
            if not isinstance(context, dict):
                raise ValueError(
                    'Unable to convert st2-context from the headers into JSON.'
                )
            liveaction.context.update(context)

        # Include RBAC context (if RBAC is available and enabled)
        if cfg.CONF.rbac.enable:
            user_db = UserDB(name=user)
            role_dbs = rbac_service.get_roles_for_user(user_db=user_db,
                                                       include_remote=True)
            roles = [role_db.name for role_db in role_dbs]
            liveaction.context['rbac'] = {'user': user, 'roles': roles}

        # Schedule the action execution.
        liveaction_db = LiveActionAPI.to_model(liveaction)
        action_db = action_utils.get_action_by_ref(liveaction_db.action)
        runnertype_db = action_utils.get_runnertype_by_name(
            action_db.runner_type['name'])

        try:
            liveaction_db.parameters = param_utils.render_live_params(
                runnertype_db.runner_parameters, action_db.parameters,
                liveaction_db.parameters, liveaction_db.context)
        except param_exc.ParamException:

            # We still need to create a request, so liveaction_db is assigned an ID
            liveaction_db, actionexecution_db = action_service.create_request(
                liveaction_db)

            # By this point the execution is already in the DB therefore need to mark it failed.
            _, e, tb = sys.exc_info()
            action_service.update_status(
                liveaction=liveaction_db,
                new_status=action_constants.LIVEACTION_STATUS_FAILED,
                result={
                    'error': str(e),
                    'traceback': ''.join(traceback.format_tb(tb, 20))
                })
            # Might be a good idea to return the actual ActionExecution rather than bubble up
            # the exception.
            raise validation_exc.ValueValidationException(str(e))

        # The request should be created after the above call to render_live_params
        # so any templates in live parameters have a chance to render.
        liveaction_db, actionexecution_db = action_service.create_request(
            liveaction_db)
        liveaction_db = LiveAction.add_or_update(liveaction_db, publish=False)

        _, actionexecution_db = action_service.publish_request(
            liveaction_db, actionexecution_db)
        mask_secrets = self._get_mask_secrets(requester_user,
                                              show_secrets=show_secrets)
        execution_api = ActionExecutionAPI.from_model(
            actionexecution_db, mask_secrets=mask_secrets)

        return Response(json=execution_api, status=http_client.CREATED)
示例#56
0
def _cleanup_liveaction(liveaction):
    try:
        LiveAction.delete(liveaction)
    except:
        LOG.exception('Failed cleaning up LiveAction: %s.', liveaction)
        pass
 def _save_liveaction(liveaction):
     return LiveAction.add_or_update(liveaction)
示例#58
0
    def test_chain_cancel_cascade_to_parent_workflow(self):
        # A temp file is created during test setup. Ensure the temp file exists.
        # The test action chain will stall until this file is deleted. This gives
        # the unit test a moment to run any test related logic.
        path = self.temp_file_path
        self.assertTrue(os.path.exists(path))

        action = TEST_PACK + '.' + 'test_cancel_with_subworkflow'
        params = {'tempfile': path, 'message': 'foobar'}
        liveaction = LiveActionDB(action=action, parameters=params)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))

        # Wait until the liveaction is running.
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        # Wait for subworkflow to register.
        execution = self._wait_for_children(execution)
        self.assertEqual(len(execution.children), 1)

        # Wait until the subworkflow is running.
        task1_exec = ActionExecution.get_by_id(execution.children[0])
        task1_live = LiveAction.get_by_id(task1_exec.liveaction['id'])
        task1_live = self._wait_on_status(
            task1_live, action_constants.LIVEACTION_STATUS_RUNNING)

        # Request subworkflow to cancel.
        task1_live, task1_exec = action_service.request_cancellation(
            task1_live, USERNAME)

        # Wait until the subworkflow is canceling.
        task1_exec = ActionExecution.get_by_id(execution.children[0])
        task1_live = LiveAction.get_by_id(task1_exec.liveaction['id'])
        task1_live = self._wait_on_status(
            task1_live, action_constants.LIVEACTION_STATUS_CANCELING)

        # Delete the temporary file that the action chain is waiting on.
        os.remove(path)
        self.assertFalse(os.path.exists(path))

        # Wait until the subworkflow is canceled.
        task1_exec = ActionExecution.get_by_id(execution.children[0])
        task1_live = LiveAction.get_by_id(task1_exec.liveaction['id'])
        task1_live = self._wait_on_status(
            task1_live, action_constants.LIVEACTION_STATUS_CANCELED)

        # Wait until the parent liveaction is canceled.
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_CANCELED)
        self.assertEqual(len(execution.children), 1)

        # Wait for non-blocking threads to complete. Ensure runner is not running.
        MockLiveActionPublisherNonBlocking.wait_all()

        # Check liveaction result.
        self.assertIn('tasks', liveaction.result)
        self.assertEqual(len(liveaction.result['tasks']), 1)

        subworkflow = liveaction.result['tasks'][0]
        self.assertEqual(len(subworkflow['result']['tasks']), 1)
        self.assertEqual(subworkflow['state'],
                         action_constants.LIVEACTION_STATUS_CANCELED)
    def test_migrate_executions(self):
        ActionExecutionDB._meta["allow_inheritance"] = True
        LiveActionDB._meta["allow_inheritance"] = True

        class ActionExecutionDB_OldFieldType(ActionExecutionDB):
            result = stormbase.EscapedDynamicField(default={})

        class LiveActionDB_OldFieldType(LiveActionDB):
            result = stormbase.EscapedDynamicField(default={})

        execution_dbs = ActionExecution.query(
            __raw__={"result": {
                "$not": {
                    "$type": "binData",
                },
            }})
        self.assertEqual(len(execution_dbs), 0)
        execution_dbs = ActionExecution.query(__raw__={
            "result": {
                "$type": "object",
            },
        })
        self.assertEqual(len(execution_dbs), 0)

        # 1. Insert data in old format
        liveaction_1_db = LiveActionDB_OldFieldType()
        liveaction_1_db.action = "foo.bar"
        liveaction_1_db.status = action_constants.LIVEACTION_STATUS_FAILED
        liveaction_1_db.result = MOCK_RESULT_1
        liveaction_1_db.start_timestamp = datetime.datetime.utcnow().replace(
            tzinfo=datetime.timezone.utc)
        liveaction_1_db = LiveAction.add_or_update(liveaction_1_db,
                                                   publish=False)

        execution_1_db = ActionExecutionDB_OldFieldType()
        execution_1_db.action = {"a": 1}
        execution_1_db.runner = {"a": 1}
        execution_1_db.liveaction = {"id": liveaction_1_db.id}
        execution_1_db.status = action_constants.LIVEACTION_STATUS_FAILED
        execution_1_db.result = MOCK_RESULT_1
        execution_1_db.start_timestamp = datetime.datetime.utcnow().replace(
            tzinfo=datetime.timezone.utc)

        execution_1_db = ActionExecution.add_or_update(execution_1_db,
                                                       publish=False)

        # This execution is not in a final state yet so it should not be migrated
        liveaction_2_db = LiveActionDB_OldFieldType()
        liveaction_2_db.action = "foo.bar2"
        liveaction_2_db.status = action_constants.LIVEACTION_STATUS_RUNNING
        liveaction_2_db.result = MOCK_RESULT_2
        liveaction_2_db.start_timestamp = datetime.datetime.utcnow().replace(
            tzinfo=datetime.timezone.utc)

        liveaction_2_db = LiveAction.add_or_update(liveaction_2_db,
                                                   publish=False)

        execution_2_db = ActionExecutionDB_OldFieldType()
        execution_2_db.action = {"a": 2}
        execution_2_db.runner = {"a": 2}
        execution_2_db.liveaction = {"id": liveaction_2_db.id}
        execution_2_db.status = action_constants.LIVEACTION_STATUS_RUNNING
        execution_2_db.result = MOCK_RESULT_2
        execution_2_db.start_timestamp = datetime.datetime.utcnow().replace(
            tzinfo=datetime.timezone.utc)

        execution_2_db = ActionExecution.add_or_update(execution_2_db,
                                                       publish=False)

        # This object is older than the default threshold so it should not be migrated
        execution_3_db = ActionExecutionDB_OldFieldType()
        execution_3_db.action = {"a": 2}
        execution_3_db.runner = {"a": 2}
        execution_3_db.liveaction = {"id": liveaction_2_db.id}
        execution_3_db.status = action_constants.LIVEACTION_STATUS_SUCCEEDED
        execution_3_db.result = MOCK_RESULT_1
        execution_3_db.start_timestamp = datetime.datetime.utcfromtimestamp(
            0).replace(tzinfo=datetime.timezone.utc)

        execution_3_db = ActionExecution.add_or_update(execution_3_db,
                                                       publish=False)

        # Verify data has been inserted in old format
        execution_dbs = ActionExecution.query(__raw__={
            "result": {
                "$type": "object",
            },
        })
        self.assertEqual(len(execution_dbs), 3)
        execution_dbs = ActionExecution.query(
            __raw__={"result": {
                "$not": {
                    "$type": "binData",
                },
            }})
        self.assertEqual(len(execution_dbs), 3)
        execution_dbs = ActionExecution.query(__raw__={
            "result": {
                "$type": "binData",
            },
        })
        self.assertEqual(len(execution_dbs), 0)

        liveaction_dbs = LiveAction.query(__raw__={
            "result": {
                "$type": "object",
            },
        })
        self.assertEqual(len(liveaction_dbs), 2)
        liveaction_dbs = LiveAction.query(
            __raw__={"result": {
                "$not": {
                    "$type": "binData",
                },
            }})
        self.assertEqual(len(liveaction_dbs), 2)
        liveaction_dbs = LiveAction.query(__raw__={
            "result": {
                "$type": "binData",
            },
        })
        self.assertEqual(len(liveaction_dbs), 0)

        # Update inserted documents and remove special _cls field added by mongoengine. We need to
        # do that here due to how mongoengine works with subclasses.
        ActionExecution.query(__raw__={
            "result": {
                "$type": "object",
            },
        }).update(set___cls="ActionExecutionDB")

        LiveAction.query(__raw__={
            "result": {
                "$type": "object",
            },
        }).update(set___cls="LiveActionDB")

        # 2. Run migration
        start_dt = datetime.datetime.utcnow().replace(
            tzinfo=datetime.timezone.utc) - datetime.timedelta(hours=2)
        end_dt = datetime.datetime.utcnow().replace(
            tzinfo=datetime.timezone.utc)
        migration_module.migrate_executions(start_dt=start_dt, end_dt=end_dt)

        # 3. Verify data has been migrated - only 1 item should have been migrated since it's in a
        # completed state
        execution_dbs = ActionExecution.query(__raw__={
            "result": {
                "$type": "object",
            },
        })
        self.assertEqual(len(execution_dbs), 2)
        execution_dbs = ActionExecution.query(__raw__={
            "result": {
                "$type": "binData",
            },
        })
        self.assertEqual(len(execution_dbs), 1)

        execution_db_1_retrieved = ActionExecution.get_by_id(execution_1_db.id)
        self.assertEqual(execution_db_1_retrieved.result, MOCK_RESULT_1)

        execution_db_2_retrieved = ActionExecution.get_by_id(execution_2_db.id)
        self.assertEqual(execution_db_2_retrieved.result, MOCK_RESULT_2)

        liveaction_db_1_retrieved = LiveAction.get_by_id(liveaction_1_db.id)
        self.assertEqual(liveaction_db_1_retrieved.result, MOCK_RESULT_1)

        liveaction_db_2_retrieved = LiveAction.get_by_id(liveaction_2_db.id)
        self.assertEqual(liveaction_db_2_retrieved.result, MOCK_RESULT_2)
示例#60
0
    def test_post_run_is_always_called_after_run(self):
        # 1. post_run should be called on success, failure, etc.
        runner_container = get_runner_container()
        params = {
            'actionstr': 'bar',
            'mock_status': action_constants.LIVEACTION_STATUS_SUCCEEDED
        }
        liveaction_db = self._get_failingaction_exec_db_model(params)
        liveaction_db = LiveAction.add_or_update(liveaction_db)
        executions.create_execution_object(liveaction_db)

        global global_runner
        global_runner = None
        original_get_runner = runner_container._get_runner

        def mock_get_runner(*args, **kwargs):
            global global_runner
            runner = original_get_runner(*args, **kwargs)
            global_runner = runner
            return runner

        runner_container._get_runner = mock_get_runner

        # Note: We can't assert here that post_run hasn't been called yet because runner instance
        # is only instantiated later inside dispatch method
        runner_container.dispatch(liveaction_db)
        self.assertTrue(global_runner.post_run_called)

        # 2. Verify post_run is called if run() throws
        runner_container = get_runner_container()
        params = {'actionstr': 'bar', 'raise': True}
        liveaction_db = self._get_failingaction_exec_db_model(params)
        liveaction_db = LiveAction.add_or_update(liveaction_db)
        executions.create_execution_object(liveaction_db)

        global_runner = None
        original_get_runner = runner_container._get_runner

        def mock_get_runner(*args, **kwargs):
            global global_runner
            runner = original_get_runner(*args, **kwargs)
            global_runner = runner
            return runner

        runner_container._get_runner = mock_get_runner

        # Note: We can't assert here that post_run hasn't been called yet because runner instance
        # is only instantiated later inside dispatch method
        runner_container.dispatch(liveaction_db)
        self.assertTrue(global_runner.post_run_called)

        # 2. Verify post_run is also called if _delete_auth_token throws
        runner_container = get_runner_container()
        runner_container._delete_auth_token = mock.Mock(
            side_effect=ValueError('throw'))
        params = {
            'actionstr': 'bar',
            'mock_status': action_constants.LIVEACTION_STATUS_SUCCEEDED
        }
        liveaction_db = self._get_failingaction_exec_db_model(params)
        liveaction_db = LiveAction.add_or_update(liveaction_db)
        executions.create_execution_object(liveaction_db)

        global_runner = None
        original_get_runner = runner_container._get_runner

        def mock_get_runner(*args, **kwargs):
            global global_runner
            runner = original_get_runner(*args, **kwargs)
            global_runner = runner
            return runner

        runner_container._get_runner = mock_get_runner

        # Note: We can't assert here that post_run hasn't been called yet because runner instance
        # is only instantiated later inside dispatch method
        runner_container.dispatch(liveaction_db)
        self.assertTrue(global_runner.post_run_called)