Example #1
0
class MistralRunnerTest(ExecutionDbTestCase):
    @classmethod
    def setUpClass(cls):
        super(MistralRunnerTest, cls).setUpClass()

        cfg.CONF.set_override('api_url', 'http://0.0.0.0:9101', group='auth')

        # Register runners.
        runnersregistrar.register_runners()

        # Register test pack(s).
        actions_registrar = actionsregistrar.ActionsRegistrar(
            use_pack_cache=False, fail_on_failure=True)

        for pack in PACKS:
            actions_registrar.register_from_pack(pack)

    @classmethod
    def get_runner_class(cls, runner_name):
        return runners.get_runner(runner_name, runner_name).__class__

    def test_build_context(self):
        parent = {
            'mistral': {
                'workflow_name': 'foo',
                'workflow_execution_id':
                'b222b934-7473-4cd4-a2ec-e204a8c93848',
                'task_tags': None,
                'task_name': 'some_fancy_wf_task',
                'task_id': '6c7d4334-3e7d-49c6-918d-698e846affaf',
                'action_execution_id': '24da5c88-834c-4a65-8b56-4ddbd654eb68'
            }
        }

        current = {
            'workflow_name': 'foo.subwf',
            'workflow_execution_id': '135e3446-4c89-4afe-821f-6ec6a0849b27'
        }

        context = MistralRunner._build_mistral_context(parent, current)
        self.assertTrue(context is not None)
        self.assertTrue('parent' in list(context['mistral'].keys()))

        parent_dict = {
            'workflow_name': parent['mistral']['workflow_name'],
            'workflow_execution_id': parent['mistral']['workflow_execution_id']
        }

        self.assertDictEqual(context['mistral']['parent'], parent_dict)
        self.assertEqual(context['mistral']['workflow_execution_id'],
                         current['workflow_execution_id'])

        parent = None
        context = MistralRunner._build_mistral_context(parent, current)
        self.assertDictEqual(context['mistral'], current)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow(self):
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, 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': {
                            '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, notify=NOTIFY)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_under_parent_chain_with_jinja_params(self):
        ac_ctx = {
            'chain': {
                'params': {
                    'var1': 'foobar',
                    'var2': '{{foobar}}',
                    'var3': ['{{foo}}', '{{bar}}'],
                    'var4': {
                        'foobar': '{{foobar}}'
                    },
                    'var5': {
                        'foobar': '{% for item in items %}foobar{% end for %}'
                    }
                }
            }
        }

        liveaction = LiveActionDB(action=WF1_NAME,
                                  parameters=ACTION_PARAMS,
                                  context=ac_ctx)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, 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': {
                            'pack': 'mistral_tests',
                            'execution_id': str(execution.id),
                            'chain': {
                                'params': {
                                    'var1':
                                    'foobar',
                                    'var2':
                                    '{% raw %}{{foobar}}{% endraw %}',
                                    'var3': [
                                        '{% raw %}{{foo}}{% endraw %}',
                                        '{% raw %}{{bar}}{% endraw %}'
                                    ],
                                    'var4': {
                                        'foobar':
                                        '{% raw %}{{foobar}}{% endraw %}'
                                    },
                                    'var5': {
                                        'foobar':
                                        ('{% raw %}{% for item in items %}'
                                         'foobar{% end for %}{% endraw %}')
                                    }
                                }
                            }
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env, notify=NOTIFY)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_under_parent_chain_with_jinja_parameters(self):
        ac_ctx = {
            'chain': {
                'parameters': {
                    'var1': 'foobar',
                    'var2': '{{foobar}}',
                    'var3': ['{{foo}}', '{{bar}}'],
                    'var4': {
                        'foobar': '{{foobar}}'
                    },
                }
            }
        }

        liveaction = LiveActionDB(action=WF1_NAME,
                                  parameters=ACTION_PARAMS,
                                  context=ac_ctx)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, 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': {
                            'pack': 'mistral_tests',
                            'execution_id': str(execution.id),
                            'chain': {
                                'parameters': {
                                    'var1':
                                    'foobar',
                                    'var2':
                                    '{% raw %}{{foobar}}{% endraw %}',
                                    'var3': [
                                        '{% raw %}{{foo}}{% endraw %}',
                                        '{% raw %}{{bar}}{% endraw %}'
                                    ],
                                    'var4': {
                                        'foobar':
                                        '{% raw %}{{foobar}}{% endraw %}'
                                    }
                                }
                            }
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env, notify=NOTIFY)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_under_parent_chain_with_nonetype_in_chain_context(
            self):
        ac_ctx = {'chain': None}

        liveaction = LiveActionDB(action=WF1_NAME,
                                  parameters=ACTION_PARAMS,
                                  context=ac_ctx)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, 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': {
                            'pack': 'mistral_tests',
                            'execution_id': str(execution.id),
                            'chain': None
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env, notify=NOTIFY)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_under_parent_chain_with_nonetype_in_params_context(
            self):
        ac_ctx = {'chain': {'params': None}}

        liveaction = LiveActionDB(action=WF1_NAME,
                                  parameters=ACTION_PARAMS,
                                  context=ac_ctx)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, 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': {
                            'pack': 'mistral_tests',
                            'execution_id': str(execution.id),
                            'chain': {
                                'params': None
                            }
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env, notify=NOTIFY)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    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 = self._wait_on_status(
            liveaction, 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, notify=NOTIFY)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_with_notifications(self):
        notify_data = {
            'on_complete': {
                'routes': ['slack'],
                'message': '"@channel: Action succeeded."',
                'data': {}
            }
        }

        liveaction = LiveActionDB(action=WF1_NAME,
                                  parameters=ACTION_PARAMS,
                                  notify=notify_data)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, 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': {
                            'pack': 'mistral_tests',
                            '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, notify=NOTIFY)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(side_effect=requests.exceptions.ConnectionError(
            'Connection refused')))
    def test_launch_workflow_mistral_offline(self):
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Connection refused', liveaction.result['error'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(side_effect=[requests.exceptions.ConnectionError(), []])
    )
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_mistral_retry(self):
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, 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'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(
            side_effect=[APIException(error_message='Duplicate entry.'), WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_duplicate_error(self):
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, 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'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1_OLD))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(workflows.WorkflowManager, 'update',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_when_workflow_definition_changed(self):
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, 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'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(workbooks.WorkbookManager, 'delete',
                       mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_when_workflow_not_exists(self):
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, 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'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF2))
    def test_launch_workflow_with_many_workflows(self):
        liveaction = LiveActionDB(action=WF2_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Multiple workflows is not supported.',
                      liveaction.result['error'])

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(side_effect=Exception()))
    def test_launch_workflow_name_mistmatch(self):
        action_ref = TEST_PACK + '.workflow_v2_name_mismatch'
        liveaction = LiveActionDB(action=action_ref, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Name of the workflow must be the same',
                      liveaction.result['error'])

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF3))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF3]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF3_EXEC)))
    def test_launch_workflow_reverse(self):
        # no differences for liveaction (direct == reverse)
        liveaction = LiveActionDB(action=WF3_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        # no differences for mistral_context (direct == reverse)
        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF3_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF3_EXEC.get('workflow_name'))

        # no differences for workflow_input (direct == reverse)
        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': {
                            'pack': 'mistral_tests',
                            'execution_id': str(execution.id)
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        # task_name must be passed to mistral.executions.create for reverse workflows
        task_name = WF3_REVERSE_TARGET_TASK_NAME

        executions.ExecutionManager.create.assert_called_with(
            WF3_NAME,
            workflow_input=workflow_input,
            env=env,
            notify=NOTIFY,
            task_name=task_name)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(workbooks.WorkbookManager, 'update',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_workbook(self):
        liveaction = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

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

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(return_value=WB2))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB2))
    @mock.patch.object(workbooks.WorkbookManager, 'update',
                       mock.MagicMock(return_value=WB2))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB2_EXEC)))
    def test_launch_workbook_with_many_workflows(self):
        liveaction = LiveActionDB(action=WB2_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

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

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(return_value=WB3))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB3))
    @mock.patch.object(workbooks.WorkbookManager, 'update',
                       mock.MagicMock(return_value=WB3))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB3_EXEC)))
    def test_launch_workbook_with_many_workflows_no_default(self):
        liveaction = LiveActionDB(action=WB3_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Default workflow cannot be determined.',
                      liveaction.result['error'])

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(return_value=WB1_OLD))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(workbooks.WorkbookManager, 'update',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_when_workbook_definition_changed(self):
        liveaction = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

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

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(workflows.WorkflowManager, 'delete',
                       mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_when_workbook_not_exists(self):
        liveaction = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

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

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(side_effect=Exception()))
    def test_launch_workbook_name_mismatch(self):
        action_ref = TEST_PACK + '.workbook_v2_name_mismatch'
        liveaction = LiveActionDB(action=action_ref, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Name of the workbook must be the same',
                      liveaction.result['error'])
Example #2
0
class TestMistralRunner(DbTestCase):

    @classmethod
    def setUpClass(cls):
        super(TestMistralRunner, cls).setUpClass()
        runners_registrar.register_runner_types()

        for _, fixture in six.iteritems(FIXTURES['actions']):
            instance = ActionAPI(**fixture)
            Action.add_or_update(ActionAPI.to_model(instance))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        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 = {
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': str(liveaction.id),
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        access_service, 'create_token',
        mock.MagicMock(return_value=TOKEN_DB))
    def test_launch_workflow_with_auth(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=ACTION_CONTEXT)
        liveaction, _ = action_service.request(execution)
        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 = {
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'auth_token': TOKEN_DB.token,
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': str(liveaction.id),
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_with_notifications(self):
        notify_data = {'on_complete': {'channels': ['slack'],
                       'message': '"@channel: Action succeeded."', 'data': {}}}

        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, notify=notify_data)
        liveaction, _ = action_service.request(execution)
        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 = {
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': str(liveaction.id),
                        'notify': NotificationsHelper.from_model(liveaction.notify),
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(side_effect=requests.exceptions.ConnectionError()))
    def test_launch_workflow_mistral_offline(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Failed to connect to mistral', liveaction.result['message'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(side_effect=[requests.exceptions.ConnectionError(), []]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_mistral_retry(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        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'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(side_effect=[APIException(error_message='Duplicate entry.'), WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_duplicate_error(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        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'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1_OLD))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        workflows.WorkflowManager, 'update',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_when_workflow_definition_changed(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        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'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(
        workbooks.WorkbookManager, 'delete',
        mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_when_workflow_not_exists(self):
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        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'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF2))
    def test_launch_workflow_with_many_workflows(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF2_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF2_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Multiple workflows is not supported.', liveaction.result['message'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(side_effect=Exception()))
    def test_launch_workflow_name_mistmatch(self):
        action_ref = 'generic.workflow_v2_name_mismatch'
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=action_ref, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Name of the workflow must be the same', liveaction.result['message'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        workbooks.WorkbookManager, 'update',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_workbook(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        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'], WB1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WB1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(return_value=WB2))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB2))
    @mock.patch.object(
        workbooks.WorkbookManager, 'update',
        mock.MagicMock(return_value=WB2))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB2_EXEC)))
    def test_launch_workbook_with_many_workflows(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB2_YAML_FILE_PATH)
        execution = LiveActionDB(action=WB2_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        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'], WB2_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WB2_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(return_value=WB3))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB3))
    @mock.patch.object(
        workbooks.WorkbookManager, 'update',
        mock.MagicMock(return_value=WB3))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB3_EXEC)))
    def test_launch_workbook_with_many_workflows_no_default(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB3_YAML_FILE_PATH)
        execution = LiveActionDB(action=WB3_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Default workflow cannot be determined.', liveaction.result['message'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(return_value=WB1_OLD))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        workbooks.WorkbookManager, 'update',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_when_workbook_definition_changed(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        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'], WB1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WB1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(
        workflows.WorkflowManager, 'delete',
        mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_when_workbook_not_exists(self):
        execution = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        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'], WB1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WB1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(side_effect=Exception()))
    def test_launch_workbook_name_mismatch(self):
        action_ref = 'generic.workbook_v2_name_mismatch'
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB1_YAML_FILE_PATH)
        execution = LiveActionDB(action=action_ref, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Name of the workbook must be the same', liveaction.result['message'])

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_text(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED,
                                        '<html></html>')

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_dict(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED, {'a': 1})

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_json_str(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED, '{"a": 1}')
        MistralCallbackHandler.callback('http://localhost:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED, "{'a': 1}")

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_list(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED,
                                        ["a", "b", "c"])

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_list_str(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED,
                                        '["a", "b", "c"]')

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback(self):
        execution = LiveActionDB(
            action='core.local', parameters={'cmd': 'uname -a'},
            callback={
                'source': 'mistral',
                'url': 'http://localhost:8989/v2/action_executions/12345'
            }
        )

        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        requests.request.assert_called_with('PUT', liveaction.callback['url'],
                                            data=json.dumps({'state': 'SUCCESS',
                                                             'output': NON_EMPTY_RESULT}),
                                            headers={'content-type': 'application/json'})

    def test_build_context(self):
        parent = {
            'mistral': {
                'workflow_name': 'foo',
                'workflow_execution_id': 'b222b934-7473-4cd4-a2ec-e204a8c93848',
                'task_tags': None,
                'task_name': 'some_fancy_wf_task',
                'task_id': '6c7d4334-3e7d-49c6-918d-698e846affaf',
                'action_execution_id': '24da5c88-834c-4a65-8b56-4ddbd654eb68'
            }
        }

        current = {
            'workflow_name': 'foo.subwf',
            'workflow_execution_id': '135e3446-4c89-4afe-821f-6ec6a0849b27'
        }

        context = MistralRunner._build_mistral_context(parent, current)
        self.assertTrue(context is not None)
        self.assertTrue('parent' in context['mistral'].keys())

        parent_dict = {
            'workflow_name': parent['mistral']['workflow_name'],
            'workflow_execution_id': parent['mistral']['workflow_execution_id']
        }

        self.assertDictEqual(context['mistral']['parent'], parent_dict)
        self.assertEqual(context['mistral']['workflow_execution_id'],
                         current['workflow_execution_id'])

        parent = None
        context = MistralRunner._build_mistral_context(parent, current)
        self.assertDictEqual(context['mistral'], current)
Example #3
0
class MistralRunnerTest(DbTestCase):

    @classmethod
    def setUpClass(cls):
        super(MistralRunnerTest, cls).setUpClass()
        runners_registrar.register_runner_types()

        for _, fixture in six.iteritems(FIXTURES['actions']):
            instance = ActionAPI(**fixture)
            Action.add_or_update(ActionAPI.to_model(instance))

    def setUp(self):
        super(MistralRunnerTest, self).setUp()
        cfg.CONF.set_override('api_url', 'http://0.0.0.0:9101', group='auth')

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    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),
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
            '__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)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_with_st2_https(self):
        cfg.CONF.set_override('api_url', 'https://0.0.0.0:9101', group='auth')

        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),
            'st2_action_api_url': 'https://0.0.0.0:9101/v1',
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'endpoint': 'https://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)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    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': {
                        '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)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(side_effect=requests.exceptions.ConnectionError('Connection refused')))
    def test_launch_workflow_mistral_offline(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_FAILED)
        self.assertIn('Connection refused', liveaction.result['error'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(side_effect=[requests.exceptions.ConnectionError(), []]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_mistral_retry(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'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(side_effect=[APIException(error_message='Duplicate entry.'), WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_duplicate_error(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'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1_OLD))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        workflows.WorkflowManager, 'update',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_when_workflow_definition_changed(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'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(
        workbooks.WorkbookManager, 'delete',
        mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_when_workflow_not_exists(self):
        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'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF2))
    def test_launch_workflow_with_many_workflows(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF2_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WF2_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_FAILED)
        self.assertIn('Multiple workflows is not supported.', liveaction.result['error'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(side_effect=Exception()))
    def test_launch_workflow_name_mistmatch(self):
        action_ref = 'generic.workflow_v2_name_mismatch'
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=action_ref, 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_FAILED)
        self.assertIn('Name of the workflow must be the same', liveaction.result['error'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        workbooks.WorkbookManager, 'update',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_workbook(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WB1_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'], WB1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WB1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(return_value=WB2))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB2))
    @mock.patch.object(
        workbooks.WorkbookManager, 'update',
        mock.MagicMock(return_value=WB2))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB2_EXEC)))
    def test_launch_workbook_with_many_workflows(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB2_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WB2_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'], WB2_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WB2_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(return_value=WB3))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB3))
    @mock.patch.object(
        workbooks.WorkbookManager, 'update',
        mock.MagicMock(return_value=WB3))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB3_EXEC)))
    def test_launch_workbook_with_many_workflows_no_default(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB3_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WB3_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_FAILED)
        self.assertIn('Default workflow cannot be determined.', liveaction.result['error'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(return_value=WB1_OLD))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        workbooks.WorkbookManager, 'update',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_when_workbook_definition_changed(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WB1_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'], WB1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WB1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(
        workflows.WorkflowManager, 'delete',
        mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_when_workbook_not_exists(self):
        liveaction = LiveActionDB(action=WB1_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'], WB1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WB1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(side_effect=Exception()))
    def test_launch_workbook_name_mismatch(self):
        action_ref = 'generic.workbook_v2_name_mismatch'
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=action_ref, 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_FAILED)
        self.assertIn('Name of the workbook must be the same', liveaction.result['error'])

    def test_callback_handler_status_map(self):
        # Ensure all StackStorm status are mapped otherwise leads to zombie workflow.
        self.assertListEqual(sorted(mistral_status_map.keys()),
                             sorted(action_constants.LIVEACTION_STATUSES))

    @mock.patch.object(
        action_executions.ActionExecutionManager, 'update',
        mock.MagicMock(return_value=None))
    def test_callback_handler_with_result_as_text(self):
        MistralCallbackHandler.callback('http://127.0.0.1:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED,
                                        '<html></html>')

    @mock.patch.object(
        action_executions.ActionExecutionManager, 'update',
        mock.MagicMock(return_value=None))
    def test_callback_handler_with_result_as_dict(self):
        MistralCallbackHandler.callback('http://127.0.0.1:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED, {'a': 1})

    @mock.patch.object(
        action_executions.ActionExecutionManager, 'update',
        mock.MagicMock(return_value=None))
    def test_callback_handler_with_result_as_json_str(self):
        MistralCallbackHandler.callback('http://127.0.0.1:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED, '{"a": 1}')
        MistralCallbackHandler.callback('http://127.0.0.1:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED, "{'a': 1}")

    @mock.patch.object(
        action_executions.ActionExecutionManager, 'update',
        mock.MagicMock(return_value=None))
    def test_callback_handler_with_result_as_list(self):
        MistralCallbackHandler.callback('http://127.0.0.1:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED,
                                        ["a", "b", "c"])

    @mock.patch.object(
        action_executions.ActionExecutionManager, 'update',
        mock.MagicMock(return_value=None))
    def test_callback_handler_with_result_as_list_str(self):
        MistralCallbackHandler.callback('http://127.0.0.1:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED,
                                        '["a", "b", "c"]')

    @mock.patch.object(
        action_executions.ActionExecutionManager, 'update',
        mock.MagicMock(return_value=None))
    def test_callback(self):
        liveaction = LiveActionDB(
            action='core.local', parameters={'cmd': 'uname -a'},
            callback={
                'source': 'mistral',
                'url': 'http://127.0.0.1:8989/v2/action_executions/12345'
            }
        )

        for status in action_constants.LIVEACTION_COMPLETED_STATES:
            expected_mistral_status = mistral_status_map[status]
            LocalShellRunner.run = mock.Mock(return_value=(status, NON_EMPTY_RESULT, None))
            liveaction, execution = action_service.request(liveaction)
            liveaction = LiveAction.get_by_id(str(liveaction.id))
            self.assertEqual(liveaction.status, status)
            action_executions.ActionExecutionManager.update.assert_called_with(
                '12345', state=expected_mistral_status, output=NON_EMPTY_RESULT)

    @mock.patch.object(
        LocalShellRunner, 'run',
        mock.MagicMock(return_value=(action_constants.LIVEACTION_STATUS_RUNNING,
                                     NON_EMPTY_RESULT, None)))
    @mock.patch.object(
        action_executions.ActionExecutionManager, 'update',
        mock.MagicMock(return_value=None))
    def test_callback_incomplete_state(self):
        liveaction = LiveActionDB(
            action='core.local', parameters={'cmd': 'uname -a'},
            callback={
                'source': 'mistral',
                '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_RUNNING)
        self.assertFalse(action_executions.ActionExecutionManager.update.called)

    @mock.patch.object(
        LocalShellRunner, 'run',
        mock.MagicMock(return_value=(action_constants.LIVEACTION_STATUS_SUCCEEDED,
                                     NON_EMPTY_RESULT, None)))
    @mock.patch.object(
        action_executions.ActionExecutionManager, 'update',
        mock.MagicMock(side_effect=[
            requests.exceptions.ConnectionError(),
            None]))
    def test_callback_retry(self):
        liveaction = LiveActionDB(
            action='core.local', parameters={'cmd': 'uname -a'},
            callback={
                'source': 'mistral',
                '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)

    @mock.patch.object(
        LocalShellRunner, 'run',
        mock.MagicMock(return_value=(action_constants.LIVEACTION_STATUS_SUCCEEDED,
                                     NON_EMPTY_RESULT, None)))
    @mock.patch.object(
        action_executions.ActionExecutionManager, 'update',
        mock.MagicMock(side_effect=[
            requests.exceptions.ConnectionError(),
            requests.exceptions.ConnectionError(),
            requests.exceptions.ConnectionError(),
            requests.exceptions.ConnectionError(),
            None]))
    def test_callback_retry_exhausted(self):
        liveaction = LiveActionDB(
            action='core.local', parameters={'cmd': 'uname -a'},
            callback={
                'source': 'mistral',
                '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)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'update',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC_PAUSED)))
    def test_cancel(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'))

        requester = cfg.CONF.system_user.user
        liveaction, execution = action_service.request_cancellation(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_CANCELED)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'update',
        mock.MagicMock(side_effect=[requests.exceptions.ConnectionError(),
                                    executions.Execution(None, WF1_EXEC_PAUSED)]))
    def test_cancel_retry(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'))

        requester = cfg.CONF.system_user.user
        liveaction, execution = action_service.request_cancellation(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_CANCELED)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'update',
        mock.MagicMock(side_effect=requests.exceptions.ConnectionError('Connection refused')))
    def test_cancel_retry_exhausted(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'))

        requester = cfg.CONF.system_user.user
        liveaction, execution = action_service.request_cancellation(liveaction, requester)

        calls = [call(WF1_EXEC.get('id'), 'PAUSED') for i in range(0, 2)]
        executions.ExecutionManager.update.assert_has_calls(calls)

        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_CANCELING)

    def test_build_context(self):
        parent = {
            'mistral': {
                'workflow_name': 'foo',
                'workflow_execution_id': 'b222b934-7473-4cd4-a2ec-e204a8c93848',
                'task_tags': None,
                'task_name': 'some_fancy_wf_task',
                'task_id': '6c7d4334-3e7d-49c6-918d-698e846affaf',
                'action_execution_id': '24da5c88-834c-4a65-8b56-4ddbd654eb68'
            }
        }

        current = {
            'workflow_name': 'foo.subwf',
            'workflow_execution_id': '135e3446-4c89-4afe-821f-6ec6a0849b27'
        }

        context = MistralRunner._build_mistral_context(parent, current)
        self.assertTrue(context is not None)
        self.assertTrue('parent' in context['mistral'].keys())

        parent_dict = {
            'workflow_name': parent['mistral']['workflow_name'],
            'workflow_execution_id': parent['mistral']['workflow_execution_id']
        }

        self.assertDictEqual(context['mistral']['parent'], parent_dict)
        self.assertEqual(context['mistral']['workflow_execution_id'],
                         current['workflow_execution_id'])

        parent = None
        context = MistralRunner._build_mistral_context(parent, current)
        self.assertDictEqual(context['mistral'], current)
Example #4
0
class TestMistralRunner(DbTestCase):
    @classmethod
    def setUpClass(cls):
        super(TestMistralRunner, cls).setUpClass()
        runners_registrar.register_runner_types()

        for _, fixture in six.iteritems(FIXTURES['actions']):
            instance = ActionAPI(**fixture)
            Action.add_or_update(ActionAPI.to_model(instance))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, 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'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(side_effect=requests.exceptions.ConnectionError()))
    def test_launch_workflow_mistral_offline(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_FAILED)
        self.assertIn('Failed to connect to mistral',
                      liveaction.result['message'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(side_effect=[requests.exceptions.ConnectionError(), []])
    )
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_mistral_retry(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, 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'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(
            side_effect=[APIException(error_message='Duplicate entry.'), WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_duplicate_error(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, 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'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1_OLD))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(workflows.WorkflowManager, 'update',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_when_workflow_definition_changed(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, 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'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(workbooks.WorkbookManager, 'delete',
                       mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_when_workflow_not_exists(self):
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, 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'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF2))
    def test_launch_workflow_with_many_workflows(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF2_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF2_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_FAILED)
        self.assertIn('Multiple workflows is not supported.',
                      liveaction.result['message'])

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(side_effect=Exception()))
    def test_launch_workflow_name_mistmatch(self):
        action_ref = 'generic.workflow_v2_name_mismatch'
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=action_ref, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_FAILED)
        self.assertIn('Name of the workflow must be the same',
                      liveaction.result['message'])

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(workbooks.WorkbookManager, 'update',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_workbook(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WB1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_RUNNING)

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

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(return_value=WB2))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB2))
    @mock.patch.object(workbooks.WorkbookManager, 'update',
                       mock.MagicMock(return_value=WB2))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB2_EXEC)))
    def test_launch_workbook_with_many_workflows(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WB2_YAML_FILE_PATH)
        execution = LiveActionDB(action=WB2_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_RUNNING)

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

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(return_value=WB3))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB3))
    @mock.patch.object(workbooks.WorkbookManager, 'update',
                       mock.MagicMock(return_value=WB3))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB3_EXEC)))
    def test_launch_workbook_with_many_workflows_no_default(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WB3_YAML_FILE_PATH)
        execution = LiveActionDB(action=WB3_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_FAILED)
        self.assertIn('Default workflow cannot be determined.',
                      liveaction.result['message'])

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(return_value=WB1_OLD))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(workbooks.WorkbookManager, 'update',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_when_workbook_definition_changed(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WB1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_RUNNING)

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

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(workflows.WorkflowManager, 'delete',
                       mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_when_workbook_not_exists(self):
        execution = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_RUNNING)

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

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(side_effect=Exception()))
    def test_launch_workbook_name_mismatch(self):
        action_ref = 'generic.workbook_v2_name_mismatch'
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WB1_YAML_FILE_PATH)
        execution = LiveActionDB(action=action_ref, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_FAILED)
        self.assertIn('Name of the workbook must be the same',
                      liveaction.result['message'])

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_text(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/tasks/12345',
                                        {}, LIVEACTION_STATUS_SUCCEEDED,
                                        '<html></html>')

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_dict(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/tasks/12345',
                                        {}, LIVEACTION_STATUS_SUCCEEDED,
                                        {'a': 1})

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_json_str(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/tasks/12345',
                                        {}, LIVEACTION_STATUS_SUCCEEDED,
                                        '{"a": 1}')
        MistralCallbackHandler.callback('http://localhost:8989/v2/tasks/12345',
                                        {}, LIVEACTION_STATUS_SUCCEEDED,
                                        "{'a': 1}")

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_list(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/tasks/12345',
                                        {}, LIVEACTION_STATUS_SUCCEEDED,
                                        ["a", "b", "c"])

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_list_str(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/tasks/12345',
                                        {}, LIVEACTION_STATUS_SUCCEEDED,
                                        '["a", "b", "c"]')

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback(self):
        execution = LiveActionDB(action='core.local',
                                 parameters={'cmd': 'uname -a'},
                                 callback={
                                     'source': 'mistral',
                                     'url':
                                     'http://localhost:8989/v2/tasks/12345'
                                 })
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_SUCCEEDED)
        requests.request.assert_called_with(
            'PUT',
            liveaction.callback['url'],
            data=json.dumps({
                'state': 'SUCCESS',
                'result': NON_EMPTY_RESULT
            }),
            headers={'content-type': 'application/json'})

    def test_build_context(self):
        parent = {
            'mistral': {
                'workflow_name': 'foo',
                'execution_id': 'zbbjb-r87t84-bbjd',
                'task_tags': None,
                'task_name': 'some_fancy_wf_task',
                'task_id': '6c7d4334-3e7d-49c6-918d-698e846affaf'
            }
        }
        current = {
            'workflow_name': 'foo.subwf',
            'execution_id': 'cbjhv-csvhjvsh-vvshvc'
        }
        context = MistralRunner._build_mistral_context(parent, current)
        self.assertTrue(context is not None)
        self.assertTrue('parent' in context['mistral'].keys())
        parent_dict = {
            'workflow_name': parent['mistral']['workflow_name'],
            'execution_id': parent['mistral']['execution_id']
        }
        self.assertDictEqual(context['mistral']['parent'], parent_dict)
        self.assertEqual(context['mistral']['execution_id'],
                         current['execution_id'])

        parent = None
        context = MistralRunner._build_mistral_context(parent, current)
        self.assertDictEqual(context['mistral'], current)