def to_model(cls, action): name = getattr(action, 'name', None) description = getattr(action, 'description', None) enabled = bool(getattr(action, 'enabled', True)) entry_point = str(action.entry_point) pack = str(action.pack) runner_type = {'name': str(action.runner_type)} parameters = getattr(action, 'parameters', dict()) tags = TagsHelper.to_model(getattr(action, 'tags', [])) ref = ResourceReference.to_string_reference(pack=pack, name=name) if getattr(action, 'notify', None): notify = NotificationsHelper.to_model(action.notify) else: # We use embedded document model for ``notify`` in action model. If notify is # set notify to None, Mongoengine interprets ``None`` as unmodified # field therefore doesn't delete the embedded document. Therefore, we need # to use an empty document. notify = NotificationsHelper.to_model({}) model = cls.model(name=name, description=description, enabled=enabled, entry_point=entry_point, pack=pack, runner_type=runner_type, tags=tags, parameters=parameters, notify=notify, ref=ref) return model
def test_model_transformations(self): notify = {} notify['on_complete'] = { 'message': 'Action completed.', 'data': { 'foo': '{{foo}}', 'bar': 1, 'baz': [1, 2, 3] } } notify['on_success'] = { 'message': 'Action succeeded.', 'data': { 'foo': '{{foo}}', 'bar': 1, } } notify_model = NotificationsHelper.to_model(notify) self.assertEqual(notify['on_complete']['message'], notify_model.on_complete.message) self.assertDictEqual(notify['on_complete']['data'], notify_model.on_complete.data) self.assertEqual(notify['on_success']['message'], notify_model.on_success.message) self.assertDictEqual(notify['on_success']['data'], notify_model.on_success.data) notify_api = NotificationsHelper.from_model(notify_model) self.assertEqual(notify['on_complete']['message'], notify_api['on_complete']['message']) self.assertDictEqual(notify['on_complete']['data'], notify_api['on_complete']['data']) self.assertEqual(notify['on_success']['message'], notify_api['on_success']['message']) self.assertDictEqual(notify['on_success']['data'], notify_api['on_success']['data'])
def test_model_transformations(self): notify = {} notify_model = NotificationsHelper.to_model(notify) self.assertEqual(notify_model.on_success, None) self.assertEqual(notify_model.on_failure, None) self.assertEqual(notify_model.on_complete, None) notify_api = NotificationsHelper.from_model(notify_model) self.assertEqual(notify_api, {}) notify['on-complete'] = { 'message': 'Action completed.', 'routes': ['66'], 'data': { 'foo': '{{foo}}', 'bar': 1, 'baz': [1, 2, 3] } } notify['on-success'] = { 'message': 'Action succeeded.', 'routes': ['100'], 'data': { 'foo': '{{foo}}', 'bar': 1, } } notify_model = NotificationsHelper.to_model(notify) self.assertEqual(notify['on-complete']['message'], notify_model.on_complete.message) self.assertDictEqual(notify['on-complete']['data'], notify_model.on_complete.data) self.assertListEqual(notify['on-complete']['routes'], notify_model.on_complete.routes) self.assertEqual(notify['on-success']['message'], notify_model.on_success.message) self.assertDictEqual(notify['on-success']['data'], notify_model.on_success.data) self.assertListEqual(notify['on-success']['routes'], notify_model.on_success.routes) notify_api = NotificationsHelper.from_model(notify_model) self.assertEqual(notify['on-complete']['message'], notify_api['on-complete']['message']) self.assertDictEqual(notify['on-complete']['data'], notify_api['on-complete']['data']) self.assertListEqual(notify['on-complete']['routes'], notify_api['on-complete']['routes']) self.assertEqual(notify['on-success']['message'], notify_api['on-success']['message']) self.assertDictEqual(notify['on-success']['data'], notify_api['on-success']['data']) self.assertListEqual(notify['on-success']['routes'], notify_api['on-success']['routes'])
def test_model_transformations(self): notify = {} notify_model = NotificationsHelper.to_model(notify) self.assertEqual(notify_model.on_success, None) self.assertEqual(notify_model.on_failure, None) self.assertEqual(notify_model.on_complete, None) notify_api = NotificationsHelper.from_model(notify_model) self.assertEqual(notify_api, {}) notify["on-complete"] = { "message": "Action completed.", "routes": ["66"], "data": { "foo": "{{foo}}", "bar": 1, "baz": [1, 2, 3] }, } notify["on-success"] = { "message": "Action succeeded.", "routes": ["100"], "data": { "foo": "{{foo}}", "bar": 1, }, } notify_model = NotificationsHelper.to_model(notify) self.assertEqual(notify["on-complete"]["message"], notify_model.on_complete.message) self.assertDictEqual(notify["on-complete"]["data"], notify_model.on_complete.data) self.assertListEqual(notify["on-complete"]["routes"], notify_model.on_complete.routes) self.assertEqual(notify["on-success"]["message"], notify_model.on_success.message) self.assertDictEqual(notify["on-success"]["data"], notify_model.on_success.data) self.assertListEqual(notify["on-success"]["routes"], notify_model.on_success.routes) notify_api = NotificationsHelper.from_model(notify_model) self.assertEqual(notify["on-complete"]["message"], notify_api["on-complete"]["message"]) self.assertDictEqual(notify["on-complete"]["data"], notify_api["on-complete"]["data"]) self.assertListEqual(notify["on-complete"]["routes"], notify_api["on-complete"]["routes"]) self.assertEqual(notify["on-success"]["message"], notify_api["on-success"]["message"]) self.assertDictEqual(notify["on-success"]["data"], notify_api["on-success"]["data"]) self.assertListEqual(notify["on-success"]["routes"], notify_api["on-success"]["routes"])
def _run_action(self, action_node, parent_execution_id, params, wait_for_completion=True): liveaction = LiveActionDB(action=action_node.ref) liveaction.parameters = action_param_utils.cast_params( action_ref=action_node.ref, params=params) if action_node.notify: liveaction.notify = NotificationsHelper.to_model( action_node.notify) liveaction.context = { 'parent': str(parent_execution_id), 'chain': vars(action_node) } liveaction, _ = action_service.schedule(liveaction) while (wait_for_completion and liveaction.status != LIVEACTION_STATUS_SUCCEEDED and liveaction.status != LIVEACTION_STATUS_FAILED): eventlet.sleep(1) liveaction = action_db_util.get_liveaction_by_id(liveaction.id) return liveaction
def to_model(cls, live_action): action = live_action.action if getattr(live_action, 'start_timestamp', None): start_timestamp = isotime.parse(live_action.start_timestamp) else: start_timestamp = None if getattr(live_action, 'end_timestamp', None): end_timestamp = isotime.parse(live_action.end_timestamp) else: end_timestamp = None status = getattr(live_action, 'status', None) parameters = getattr(live_action, 'parameters', dict()) context = getattr(live_action, 'context', dict()) callback = getattr(live_action, 'callback', dict()) result = getattr(live_action, 'result', None) if getattr(live_action, 'notify', None): notify = NotificationsHelper.to_model(live_action.notify) else: notify = None model = cls.model(action=action, start_timestamp=start_timestamp, end_timestamp=end_timestamp, status=status, parameters=parameters, context=context, callback=callback, result=result, notify=notify) return model
def to_model(cls, live_action): name = getattr(live_action, 'name', None) description = getattr(live_action, 'description', None) action = live_action.action if getattr(live_action, 'start_timestamp', None): start_timestamp = isotime.parse(live_action.start_timestamp) else: start_timestamp = None if getattr(live_action, 'end_timestamp', None): end_timestamp = isotime.parse(live_action.end_timestamp) else: end_timestamp = None status = getattr(live_action, 'status', None) parameters = getattr(live_action, 'parameters', dict()) context = getattr(live_action, 'context', dict()) callback = getattr(live_action, 'callback', dict()) result = getattr(live_action, 'result', None) if getattr(live_action, 'notify', None): notify = NotificationsHelper.to_model(live_action.notify) else: notify = None model = cls.model(name=name, description=description, action=action, start_timestamp=start_timestamp, end_timestamp=end_timestamp, status=status, parameters=parameters, context=context, callback=callback, result=result, notify=notify) return model
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) 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), '__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)
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)
def to_model(cls, action): name = getattr(action, "name", None) description = getattr(action, "description", None) enabled = bool(getattr(action, "enabled", True)) entry_point = str(action.entry_point) pack = str(action.pack) runner_type = {"name": str(action.runner_type)} parameters = getattr(action, "parameters", dict()) tags = TagsHelper.to_model(getattr(action, "tags", [])) ref = ResourceReference.to_string_reference(pack=pack, name=name) if getattr(action, "notify", None): notify = NotificationsHelper.to_model(action.notify) else: notify = None model = cls.model( name=name, description=description, enable=enabled, enabled=enabled, entry_point=entry_point, pack=pack, runner_type=runner_type, tags=tags, parameters=parameters, notify=notify, ref=ref, ) return model
def to_model(cls, action): name = getattr(action, 'name', None) description = getattr(action, 'description', None) enabled = bool(getattr(action, 'enabled', True)) entry_point = str(action.entry_point) pack = str(action.pack) runner_type = {'name': str(action.runner_type)} parameters = getattr(action, 'parameters', dict()) tags = TagsHelper.to_model(getattr(action, 'tags', [])) ref = ResourceReference.to_string_reference(pack=pack, name=name) if getattr(action, 'notify', None): notify = NotificationsHelper.to_model(action.notify) else: notify = None model = cls.model(name=name, description=description, enable=enabled, enabled=enabled, entry_point=entry_point, pack=pack, runner_type=runner_type, tags=tags, parameters=parameters, notify=notify, ref=ref) return model
def _construct_workflow_execution_options(self): # This URL is used by Mistral to talk back to the API api_url = get_mistral_api_url() endpoint = api_url + '/actionexecutions' # This URL is available in the context and can be used by the users inside a workflow, # similar to "ST2_ACTION_API_URL" environment variable available to actions public_api_url = get_full_public_api_url() # Build context with additional information parent_context = {'execution_id': self.execution_id} if getattr(self.liveaction, 'context', None): parent_context.update(self.liveaction.context) # Convert jinja expressions in the params of Action Chain under the parent context # into raw block. If there is any jinja expressions, Mistral will try to evaulate # the expression. If there is a local context reference, the evaluation will fail # because the local context reference is out of scope. chain_ctx = parent_context.get('chain') or {} for attr in ['params', 'parameters']: chain_params_ctx = chain_ctx.get(attr) or {} for k, v in six.iteritems(chain_params_ctx): parent_context['chain'][attr][ k] = jinja.convert_jinja_to_raw_block(v) st2_execution_context = { 'api_url': api_url, 'endpoint': endpoint, 'parent': parent_context, 'notify': {}, 'skip_notify_tasks': self._skip_notify_tasks } # Include notification information if self._notify: notify_dict = NotificationsHelper.from_model( notify_model=self._notify) st2_execution_context['notify'] = notify_dict if self.auth_token: st2_execution_context['auth_token'] = self.auth_token.token options = { 'env': { 'st2_execution_id': self.execution_id, 'st2_liveaction_id': self.liveaction_id, 'st2_action_api_url': public_api_url, '__actions': { 'st2.action': { 'st2_context': st2_execution_context } } } } return options
def test_model_transformations(self): notify = {} notify_model = NotificationsHelper.to_model(notify) self.assertEqual(notify_model.on_success, None) self.assertEqual(notify_model.on_failure, None) self.assertEqual(notify_model.on_complete, None) notify_api = NotificationsHelper.from_model(notify_model) self.assertEqual(notify_api, {}) notify['on-complete'] = { 'message': 'Action completed.', 'routes': [ '66' ], 'data': { 'foo': '{{foo}}', 'bar': 1, 'baz': [1, 2, 3] } } notify['on-success'] = { 'message': 'Action succeeded.', 'routes': [ '100' ], 'data': { 'foo': '{{foo}}', 'bar': 1, } } notify_model = NotificationsHelper.to_model(notify) self.assertEqual(notify['on-complete']['message'], notify_model.on_complete.message) self.assertDictEqual(notify['on-complete']['data'], notify_model.on_complete.data) self.assertListEqual(notify['on-complete']['routes'], notify_model.on_complete.routes) self.assertEqual(notify['on-success']['message'], notify_model.on_success.message) self.assertDictEqual(notify['on-success']['data'], notify_model.on_success.data) self.assertListEqual(notify['on-success']['routes'], notify_model.on_success.routes) notify_api = NotificationsHelper.from_model(notify_model) self.assertEqual(notify['on-complete']['message'], notify_api['on-complete']['message']) self.assertDictEqual(notify['on-complete']['data'], notify_api['on-complete']['data']) self.assertListEqual(notify['on-complete']['routes'], notify_api['on-complete']['routes']) self.assertEqual(notify['on-success']['message'], notify_api['on-success']['message']) self.assertDictEqual(notify['on-success']['data'], notify_api['on-success']['data']) self.assertListEqual(notify['on-success']['routes'], notify_api['on-success']['routes'])
def from_model(cls, model, mask_secrets=False): action = cls._from_model(model) action["runner_type"] = action.get("runner_type", {}).get("name", None) action["tags"] = TagsHelper.from_model(model.tags) if getattr(model, "notify", None): action["notify"] = NotificationsHelper.from_model(model.notify) return cls(**action)
def from_model(cls, model, mask_secrets=False): action = cls._from_model(model) action['runner_type'] = action['runner_type']['name'] action['tags'] = TagsHelper.from_model(model.tags) if getattr(model, 'notify', None): action['notify'] = NotificationsHelper.from_model(model.notify) return cls(**action)
def from_model(cls, model, mask_secrets=False): action = cls._from_model(model) action["runner_type"] = action["runner_type"]["name"] action["tags"] = TagsHelper.from_model(model.tags) if getattr(model, "notify", None): action["notify"] = NotificationsHelper.from_model(model.notify) return cls(**action)
def from_model(cls, model): action = cls._from_model(model) action['runner_type'] = action['runner_type']['name'] action['tags'] = TagsHelper.from_model(model.tags) if getattr(model, 'notify', None): action['notify'] = NotificationsHelper.from_model(model.notify) return cls(**action)
def _get_notify(self, action_node): if action_node.name not in self._skip_notify_tasks: if action_node.notify: task_notify = NotificationsHelper.to_model(action_node.notify) return task_notify elif self._chain_notify: return self._chain_notify return None
def from_model(cls, model, mask_secrets=False): action = cls._from_model(model) action['runner_type'] = action.get('runner_type', {}).get('name', None) action['tags'] = TagsHelper.from_model(model.tags) if getattr(model, 'notify', None): action['notify'] = NotificationsHelper.from_model(model.notify) return cls(**action)
def from_model(cls, model): doc = super(cls, cls)._from_model(model) if model.start_timestamp: doc['start_timestamp'] = isotime.format(model.start_timestamp, offset=False) if model.end_timestamp: doc['end_timestamp'] = isotime.format(model.end_timestamp, offset=False) if getattr(model, 'notify', None): doc['notify'] = NotificationsHelper.from_model(model.notify) return cls(**doc)
def from_model(cls, model, mask_secrets=False): doc = super(cls, cls)._from_model(model, mask_secrets=mask_secrets) if model.start_timestamp: doc["start_timestamp"] = isotime.format(model.start_timestamp, offset=False) if model.end_timestamp: doc["end_timestamp"] = isotime.format(model.end_timestamp, offset=False) if getattr(model, "notify", None): doc["notify"] = NotificationsHelper.from_model(model.notify) return cls(**doc)
def from_model(cls, model, mask_secrets=False): doc = super(cls, cls)._from_model(model, mask_secrets=mask_secrets) if model.start_timestamp: doc['start_timestamp'] = isotime.format(model.start_timestamp, offset=False) if model.end_timestamp: doc['end_timestamp'] = isotime.format(model.end_timestamp, offset=False) if getattr(model, 'notify', None): doc['notify'] = NotificationsHelper.from_model(model.notify) return cls(**doc)
def to_model(cls, action): name = getattr(action, "name", None) description = getattr(action, "description", None) enabled = bool(getattr(action, "enabled", True)) entry_point = str(action.entry_point) pack = str(action.pack) runner_type = {"name": str(action.runner_type)} parameters = getattr(action, "parameters", dict()) output_schema = getattr(action, "output_schema", dict()) tags = TagsHelper.to_model(getattr(action, "tags", [])) ref = ResourceReference.to_string_reference(pack=pack, name=name) if getattr(action, "notify", None): notify = NotificationsHelper.to_model(action.notify) else: # We use embedded document model for ``notify`` in action model. If notify is # set notify to None, Mongoengine interprets ``None`` as unmodified # field therefore doesn't delete the embedded document. Therefore, we need # to use an empty document. notify = NotificationsHelper.to_model({}) metadata_file = getattr(action, "metadata_file", None) model = cls.model( name=name, description=description, enabled=enabled, entry_point=entry_point, pack=pack, runner_type=runner_type, tags=tags, parameters=parameters, output_schema=output_schema, notify=notify, ref=ref, metadata_file=metadata_file, ) return model
def to_model(cls, action): model = super(cls, cls).to_model(action) model.enabled = bool(action.enabled) model.entry_point = str(action.entry_point) model.pack = str(action.pack) model.runner_type = {'name': str(action.runner_type)} model.parameters = getattr(action, 'parameters', dict()) model.tags = TagsHelper.to_model(getattr(action, 'tags', [])) model.ref = ResourceReference.to_string_reference(pack=model.pack, name=model.name) if getattr(action, 'notify', None): model.notify = NotificationsHelper.to_model(action.notify) return model
def test_model_transformations(self): notify = {} notify_model = NotificationsHelper.to_model(notify) self.assertEqual(notify_model.on_success, None) self.assertEqual(notify_model.on_failure, None) self.assertEqual(notify_model.on_complete, None) notify_api = NotificationsHelper.from_model(notify_model) self.assertEqual(notify_api, {}) notify["on-complete"] = {"message": "Action completed.", "data": {"foo": "{{foo}}", "bar": 1, "baz": [1, 2, 3]}} notify["on-success"] = {"message": "Action succeeded.", "data": {"foo": "{{foo}}", "bar": 1}} notify_model = NotificationsHelper.to_model(notify) self.assertEqual(notify["on-complete"]["message"], notify_model.on_complete.message) self.assertDictEqual(notify["on-complete"]["data"], notify_model.on_complete.data) self.assertEqual(notify["on-success"]["message"], notify_model.on_success.message) self.assertDictEqual(notify["on-success"]["data"], notify_model.on_success.data) notify_api = NotificationsHelper.from_model(notify_model) self.assertEqual(notify["on-complete"]["message"], notify_api["on-complete"]["message"]) self.assertDictEqual(notify["on-complete"]["data"], notify_api["on-complete"]["data"]) self.assertEqual(notify["on-success"]["message"], notify_api["on-success"]["message"]) self.assertDictEqual(notify["on-success"]["data"], notify_api["on-success"]["data"])
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)
def _construct_workflow_execution_options(self): # This URL is used by Mistral to talk back to the API api_url = get_mistral_api_url() endpoint = api_url + '/actionexecutions' # This URL is available in the context and can be used by the users inside a workflow, # similar to "ST2_ACTION_API_URL" environment variable available to actions public_api_url = get_full_public_api_url() # Build context with additional information parent_context = { 'execution_id': self.execution_id } if getattr(self.liveaction, 'context', None): parent_context.update(self.liveaction.context) st2_execution_context = { 'api_url': api_url, 'endpoint': endpoint, 'parent': parent_context, 'notify': {}, 'skip_notify_tasks': self._skip_notify_tasks } # Include notification information if self._notify: notify_dict = NotificationsHelper.from_model(notify_model=self._notify) st2_execution_context['notify'] = notify_dict if self.auth_token: st2_execution_context['auth_token'] = self.auth_token.token options = { 'env': { 'st2_execution_id': self.execution_id, 'st2_liveaction_id': self.liveaction_id, 'st2_action_api_url': public_api_url, '__actions': { 'st2.action': { 'st2_context': st2_execution_context } } } } return options
def _construct_workflow_execution_options(self): # This URL is used by Mistral to talk back to the API api_url = get_mistral_api_url() endpoint = api_url + '/actionexecutions' # This URL is available in the context and can be used by the users inside a workflow, # similar to "ST2_ACTION_API_URL" environment variable available to actions public_api_url = get_full_public_api_url() # Build context with additional information parent_context = {'execution_id': self.execution_id} if getattr(self.liveaction, 'context', None): parent_context.update(self.liveaction.context) st2_execution_context = { 'api_url': api_url, 'endpoint': endpoint, 'parent': parent_context, 'notify': {}, 'skip_notify_tasks': self._skip_notify_tasks } # Include notification information if self._notify: notify_dict = NotificationsHelper.from_model( notify_model=self._notify) st2_execution_context['notify'] = notify_dict if self.auth_token: st2_execution_context['auth_token'] = self.auth_token.token options = { 'env': { 'st2_execution_id': self.execution_id, 'st2_liveaction_id': self.liveaction_id, 'st2_action_api_url': public_api_url, '__actions': { 'st2.action': { 'st2_context': st2_execution_context } } } } return options
def _run_action(action_node, parent_execution_id, params, wait_for_completion=True): execution = LiveActionDB(action=action_node.ref) execution.parameters = action_param_utils.cast_params(action_ref=action_node.ref, params=params) if action_node.notify: execution.notify = NotificationsHelper.to_model(action_node.notify) execution.context = { 'parent': str(parent_execution_id), 'chain': vars(action_node) } liveaction, _ = action_service.schedule(execution) while (wait_for_completion and liveaction.status != LIVEACTION_STATUS_SUCCEEDED and liveaction.status != LIVEACTION_STATUS_FAILED): eventlet.sleep(1) liveaction = action_db_util.get_liveaction_by_id(liveaction.id) return liveaction
def to_model(cls, liveaction): model = super(cls, cls).to_model(liveaction) model.action = liveaction.action if getattr(liveaction, 'start_timestamp', None): model.start_timestamp = isotime.parse(liveaction.start_timestamp) if getattr(liveaction, 'end_timestamp', None): model.end_timestamp = isotime.parse(liveaction.end_timestamp) model.status = getattr(liveaction, 'status', None) model.parameters = getattr(liveaction, 'parameters', dict()) model.context = getattr(liveaction, 'context', dict()) model.callback = getattr(liveaction, 'callback', dict()) model.result = getattr(liveaction, 'result', None) if getattr(liveaction, 'notify', None): model.notify = NotificationsHelper.to_model(liveaction.notify) return model
def to_model(cls, live_action): name = getattr(live_action, "name", None) description = getattr(live_action, "description", None) action = live_action.action if getattr(live_action, "start_timestamp", None): start_timestamp = isotime.parse(live_action.start_timestamp) else: start_timestamp = None if getattr(live_action, "end_timestamp", None): end_timestamp = isotime.parse(live_action.end_timestamp) else: end_timestamp = None status = getattr(live_action, "status", None) parameters = getattr(live_action, "parameters", dict()) context = getattr(live_action, "context", dict()) callback = getattr(live_action, "callback", dict()) result = getattr(live_action, "result", None) if getattr(live_action, "notify", None): notify = NotificationsHelper.to_model(live_action.notify) else: notify = None model = cls.model( name=name, description=description, action=action, start_timestamp=start_timestamp, end_timestamp=end_timestamp, status=status, parameters=parameters, context=context, callback=callback, result=result, notify=notify, ) return model
def _construct_workflow_execution_options(self): # This URL is used by Mistral to talk back to the API api_url = get_mistral_api_url() endpoint = api_url + "/actionexecutions" # This URL is available in the context and can be used by the users inside a workflow, # similar to "ST2_ACTION_API_URL" environment variable available to actions public_api_url = get_full_public_api_url() # Build context with additional information parent_context = {"execution_id": self.execution_id} if getattr(self.liveaction, "context", None): parent_context.update(self.liveaction.context) st2_execution_context = { "endpoint": endpoint, "parent": parent_context, "notify": {}, "skip_notify_tasks": self._skip_notify_tasks, } # Include notification information if self._notify: notify_dict = NotificationsHelper.from_model(notify_model=self._notify) st2_execution_context["notify"] = notify_dict if self.auth_token: st2_execution_context["auth_token"] = self.auth_token.token options = { "env": { "st2_execution_id": self.execution_id, "st2_liveaction_id": self.liveaction_id, "st2_action_api_url": public_api_url, "__actions": {"st2.action": {"st2_context": st2_execution_context}}, } } return options
def to_model(cls, live_action): action = live_action.action if getattr(live_action, "start_timestamp", None): start_timestamp = isotime.parse(live_action.start_timestamp) else: start_timestamp = None if getattr(live_action, "end_timestamp", None): end_timestamp = isotime.parse(live_action.end_timestamp) else: end_timestamp = None status = getattr(live_action, "status", None) parameters = getattr(live_action, "parameters", dict()) context = getattr(live_action, "context", dict()) callback = getattr(live_action, "callback", dict()) result = getattr(live_action, "result", None) delay = getattr(live_action, "delay", None) if getattr(live_action, "notify", None): notify = NotificationsHelper.to_model(live_action.notify) else: notify = None model = cls.model( action=action, start_timestamp=start_timestamp, end_timestamp=end_timestamp, status=status, parameters=parameters, context=context, callback=callback, result=result, notify=notify, delay=delay, ) return model
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)
def _construct_workflow_execution_options(self): # This URL is used by Mistral to talk back to the API api_url = get_mistral_api_url() endpoint = api_url + '/actionexecutions' # This URL is available in the context and can be used by the users inside a workflow, # similar to "ST2_ACTION_API_URL" environment variable available to actions public_api_url = get_full_public_api_url() # Build context with additional information parent_context = { 'execution_id': self.execution_id } if getattr(self.liveaction, 'context', None): parent_context.update(self.liveaction.context) # Convert jinja expressions in the params of Action Chain under the parent context # into raw block. If there is any jinja expressions, Mistral will try to evaulate # the expression. If there is a local context reference, the evaluation will fail # because the local context reference is out of scope. chain_ctx = parent_context.get('chain') or {} for attr in ['params', 'parameters']: chain_params_ctx = chain_ctx.get(attr) or {} for k, v in six.iteritems(chain_params_ctx): parent_context['chain'][attr][k] = jinja.convert_jinja_to_raw_block(v) st2_execution_context = { 'api_url': api_url, 'endpoint': endpoint, 'parent': parent_context, 'notify': {}, 'skip_notify_tasks': self._skip_notify_tasks } # Include notification information if self._notify: notify_dict = NotificationsHelper.from_model(notify_model=self._notify) st2_execution_context['notify'] = notify_dict if self.auth_token: st2_execution_context['auth_token'] = self.auth_token.token options = { 'env': { 'st2_execution_id': self.execution_id, 'st2_liveaction_id': self.liveaction_id, 'st2_action_api_url': public_api_url, '__actions': { 'st2.action': { 'st2_context': st2_execution_context } } } } if not self.is_polling_enabled(): options['notify'] = [{'type': 'st2'}] # Only used on reverse type workflows task_name = self.runner_parameters.get('task_name', None) if task_name is not None: options['task_name'] = task_name return options
def try_run(self, action_parameters): # Test connection self._client.workflows.list() # Setup inputs for the workflow execution. inputs = self.runner_parameters.get('context', dict()) inputs.update(action_parameters) endpoint = 'http://%s:%s/v1/actionexecutions' % (cfg.CONF.api.host, cfg.CONF.api.port) # Build context with additional information parent_context = {'execution_id': self.execution_id} if getattr(self.liveaction, 'context', None): parent_context.update(self.liveaction.context) st2_execution_context = { 'endpoint': endpoint, 'parent': parent_context, 'notify': {}, 'skip_notify_tasks': self._skip_notify_tasks } # Include notification information if self._notify: notify_dict = NotificationsHelper.from_model( notify_model=self._notify) st2_execution_context['notify'] = notify_dict if self.auth_token: st2_execution_context['auth_token'] = self.auth_token.token options = { 'env': { 'st2_execution_id': self.execution_id, 'st2_liveaction_id': self.liveaction_id, '__actions': { 'st2.action': { 'st2_context': st2_execution_context } } } } # Get workbook/workflow definition from file. with open(self.entry_point, 'r') as def_file: def_yaml = def_file.read() def_dict = yaml.safe_load(def_yaml) is_workbook = ('workflows' in def_dict) if not is_workbook: # Non-workbook definition containing multiple workflows is not supported. if len([k for k, _ in six.iteritems(def_dict) if k != 'version' ]) != 1: raise Exception( 'Workflow (not workbook) definition is detected. ' 'Multiple workflows is not supported.') action_ref = '%s.%s' % (self.action.pack, self.action.name) self._check_name(action_ref, is_workbook, def_dict) def_dict_xformed = utils.transform_definition(def_dict) def_yaml_xformed = yaml.safe_dump(def_dict_xformed, default_flow_style=False) # Save workbook/workflow definition. if is_workbook: self._save_workbook(action_ref, def_yaml_xformed) default_workflow = self._find_default_workflow(def_dict_xformed) execution = self._client.executions.create(default_workflow, workflow_input=inputs, **options) else: self._save_workflow(action_ref, def_yaml_xformed) execution = self._client.executions.create(action_ref, workflow_input=inputs, **options) status = LIVEACTION_STATUS_RUNNING partial_results = {'tasks': []} # pylint: disable=no-member current_context = { 'execution_id': str(execution.id), 'workflow_name': execution.workflow_name } exec_context = self.context exec_context = self._build_mistral_context(exec_context, current_context) LOG.info('Mistral query context is %s' % exec_context) return (status, partial_results, exec_context)
def run(self, action_parameters): # Test connection self._client.workflows.list() # Setup inputs for the workflow execution. inputs = self.runner_parameters.get('context', dict()) inputs.update(action_parameters) # This URL is used by Mistral to talk back to the API api_url = get_mistral_api_url() endpoint = api_url + '/actionexecutions' # This URL is available in the context and can be used by the users inside a workflow, # similar to "ST2_ACTION_API_URL" environment variable available to actions public_api_url = get_full_public_api_url() # Build context with additional information parent_context = { 'execution_id': self.execution_id } if getattr(self.liveaction, 'context', None): parent_context.update(self.liveaction.context) st2_execution_context = { 'endpoint': endpoint, 'parent': parent_context, 'notify': {}, 'skip_notify_tasks': self._skip_notify_tasks } # Include notification information if self._notify: notify_dict = NotificationsHelper.from_model(notify_model=self._notify) st2_execution_context['notify'] = notify_dict if self.auth_token: st2_execution_context['auth_token'] = self.auth_token.token options = { 'env': { 'st2_execution_id': self.execution_id, 'st2_liveaction_id': self.liveaction_id, 'st2_action_api_url': public_api_url, '__actions': { 'st2.action': { 'st2_context': st2_execution_context } } } } # Get workbook/workflow definition from file. with open(self.entry_point, 'r') as def_file: def_yaml = def_file.read() def_dict = yaml.safe_load(def_yaml) is_workbook = ('workflows' in def_dict) if not is_workbook: # Non-workbook definition containing multiple workflows is not supported. if len([k for k, _ in six.iteritems(def_dict) if k != 'version']) != 1: raise Exception('Workflow (not workbook) definition is detected. ' 'Multiple workflows is not supported.') action_ref = '%s.%s' % (self.action.pack, self.action.name) self._check_name(action_ref, is_workbook, def_dict) def_dict_xformed = utils.transform_definition(def_dict) def_yaml_xformed = yaml.safe_dump(def_dict_xformed, default_flow_style=False) # Save workbook/workflow definition. if is_workbook: self._save_workbook(action_ref, def_yaml_xformed) default_workflow = self._find_default_workflow(def_dict_xformed) execution = self._client.executions.create(default_workflow, workflow_input=inputs, **options) else: self._save_workflow(action_ref, def_yaml_xformed) execution = self._client.executions.create(action_ref, workflow_input=inputs, **options) status = LIVEACTION_STATUS_RUNNING partial_results = {'tasks': []} # pylint: disable=no-member current_context = { 'execution_id': str(execution.id), 'workflow_name': execution.workflow_name } exec_context = self.context exec_context = self._build_mistral_context(exec_context, current_context) LOG.info('Mistral query context is %s' % exec_context) return (status, partial_results, exec_context)
FIXTURES_PACK, 'actionchains', 'malformedchain.yaml') CHAIN_TYPED_PARAMS = FixturesLoader().get_fixture_file_path_abs( FIXTURES_PACK, 'actionchains', 'chain_typed_params.yaml') CHAIN_SYSTEM_PARAMS = FixturesLoader().get_fixture_file_path_abs( FIXTURES_PACK, 'actionchains', 'chain_typed_system_params.yaml') CHAIN_WITH_ACTIONPARAM_VARS = FixturesLoader().get_fixture_file_path_abs( FIXTURES_PACK, 'actionchains', 'chain_with_actionparam_vars.yaml') CHAIN_WITH_SYSTEM_VARS = FixturesLoader().get_fixture_file_path_abs( FIXTURES_PACK, 'actionchains', 'chain_with_system_vars.yaml') CHAIN_WITH_PUBLISH = FixturesLoader().get_fixture_file_path_abs( FIXTURES_PACK, 'actionchains', 'chain_with_publish.yaml') CHAIN_WITH_INVALID_ACTION = FixturesLoader().get_fixture_file_path_abs( FIXTURES_PACK, 'actionchains', 'chain_with_invalid_action.yaml') CHAIN_NOTIFY_API = {'notify': {'on-complete': {'message': 'foo happened.'}}} CHAIN_NOTIFY_DB = NotificationsHelper.to_model(CHAIN_NOTIFY_API) @mock.patch.object(action_db_util, 'get_runnertype_by_name', mock.MagicMock(return_value=RUNNER)) class TestActionChainRunner(DbTestCase): def test_runner_creation(self): runner = acr.get_runner() self.assertTrue(runner) self.assertTrue(runner.runner_id) def test_malformed_chain(self): try: chain_runner = acr.get_runner() chain_runner.entry_point = MALFORMED_CHAIN_PATH chain_runner.action = ACTION_1
def try_run(self, action_parameters): # Test connection self._client.workflows.list() # Setup inputs for the workflow execution. inputs = self.runner_parameters.get("context", dict()) inputs.update(action_parameters) endpoint = "http://%s:%s/v1/actionexecutions" % (cfg.CONF.api.host, cfg.CONF.api.port) # Build context with additional information parent_context = {"execution_id": self.execution_id} if getattr(self.liveaction, "context", None): parent_context.update(self.liveaction.context) st2_execution_context = { "endpoint": endpoint, "parent": parent_context, "notify": {}, "skip_notify_tasks": self._skip_notify_tasks, } # Include notification information if self._notify: notify_dict = NotificationsHelper.from_model(notify_model=self._notify) st2_execution_context["notify"] = notify_dict if self.auth_token: st2_execution_context["auth_token"] = self.auth_token.token options = { "env": { "st2_execution_id": self.execution_id, "st2_liveaction_id": self.liveaction_id, "__actions": {"st2.action": {"st2_context": st2_execution_context}}, } } # Get workbook/workflow definition from file. with open(self.entry_point, "r") as def_file: def_yaml = def_file.read() def_dict = yaml.safe_load(def_yaml) is_workbook = "workflows" in def_dict if not is_workbook: # Non-workbook definition containing multiple workflows is not supported. if len([k for k, _ in six.iteritems(def_dict) if k != "version"]) != 1: raise Exception( "Workflow (not workbook) definition is detected. " "Multiple workflows is not supported." ) action_ref = "%s.%s" % (self.action.pack, self.action.name) self._check_name(action_ref, is_workbook, def_dict) def_dict_xformed = utils.transform_definition(def_dict) def_yaml_xformed = yaml.safe_dump(def_dict_xformed, default_flow_style=False) # Save workbook/workflow definition. if is_workbook: self._save_workbook(action_ref, def_yaml_xformed) default_workflow = self._find_default_workflow(def_dict_xformed) execution = self._client.executions.create(default_workflow, workflow_input=inputs, **options) else: self._save_workflow(action_ref, def_yaml_xformed) execution = self._client.executions.create(action_ref, workflow_input=inputs, **options) status = LIVEACTION_STATUS_RUNNING partial_results = {"tasks": []} # pylint: disable=no-member current_context = {"execution_id": str(execution.id), "workflow_name": execution.workflow_name} exec_context = self.context exec_context = self._build_mistral_context(exec_context, current_context) LOG.info("Mistral query context is %s" % exec_context) return (status, partial_results, exec_context)
FIXTURES_PACK, 'actionchains', 'malformedchain.yaml') CHAIN_TYPED_PARAMS = FixturesLoader().get_fixture_file_path_abs( FIXTURES_PACK, 'actionchains', 'chain_typed_params.yaml') CHAIN_SYSTEM_PARAMS = FixturesLoader().get_fixture_file_path_abs( FIXTURES_PACK, 'actionchains', 'chain_typed_system_params.yaml') CHAIN_WITH_ACTIONPARAM_VARS = FixturesLoader().get_fixture_file_path_abs( FIXTURES_PACK, 'actionchains', 'chain_with_actionparam_vars.yaml') CHAIN_WITH_SYSTEM_VARS = FixturesLoader().get_fixture_file_path_abs( FIXTURES_PACK, 'actionchains', 'chain_with_system_vars.yaml') CHAIN_WITH_PUBLISH = FixturesLoader().get_fixture_file_path_abs( FIXTURES_PACK, 'actionchains', 'chain_with_publish.yaml') CHAIN_WITH_INVALID_ACTION = FixturesLoader().get_fixture_file_path_abs( FIXTURES_PACK, 'actionchains', 'chain_with_invalid_action.yaml') CHAIN_NOTIFY_API = {'notify': {'on-complete': {'message': 'foo happened.'}}} CHAIN_NOTIFY_DB = NotificationsHelper.to_model(CHAIN_NOTIFY_API) @mock.patch.object(action_db_util, 'get_runnertype_by_name', mock.MagicMock(return_value=RUNNER)) class TestActionChainRunner(DbTestCase): def test_runner_creation(self): runner = acr.get_runner() self.assertTrue(runner) self.assertTrue(runner.runner_id) def test_malformed_chain(self): try: chain_runner = acr.get_runner() chain_runner.entry_point = MALFORMED_CHAIN_PATH
def try_run(self, action_parameters): # Test connection self._client.workflows.list() # Setup inputs for the workflow execution. inputs = self.runner_parameters.get('context', dict()) inputs.update(action_parameters) endpoint = 'http://%s:%s/v1/actionexecutions' % (cfg.CONF.api.host, cfg.CONF.api.port) # Build context with additional information st2_execution_context = { 'endpoint': endpoint, 'parent': self.liveaction_id, 'notify': {}, 'skip_notify_tasks': self._skip_notify_tasks } # Include notification information if self._notify: notify_dict = NotificationsHelper.from_model(notify_model=self._notify) st2_execution_context['notify'] = notify_dict if self.auth_token: st2_execution_context['auth_token'] = self.auth_token.token options = { 'env': { 'st2_execution_id': self.execution_id, 'st2_liveaction_id': self.liveaction_id, '__actions': { 'st2.action': { 'st2_context': st2_execution_context } } } } # Get workbook/workflow definition from file. with open(self.entry_point, 'r') as def_file: def_yaml = def_file.read() def_dict = yaml.safe_load(def_yaml) is_workbook = ('workflows' in def_dict) if not is_workbook: # Non-workbook definition containing multiple workflows is not supported. if len([k for k, _ in six.iteritems(def_dict) if k != 'version']) != 1: raise Exception('Workflow (not workbook) definition is detected. ' 'Multiple workflows is not supported.') action_ref = '%s.%s' % (self.action.pack, self.action.name) self._check_name(action_ref, is_workbook, def_dict) def_dict_xformed = utils.transform_definition(def_dict) def_yaml_xformed = yaml.safe_dump(def_dict_xformed, default_flow_style=False) # Save workbook/workflow definition. if is_workbook: self._save_workbook(action_ref, def_yaml_xformed) default_workflow = self._find_default_workflow(def_dict_xformed) execution = self._client.executions.create(default_workflow, workflow_input=inputs, **options) else: self._save_workflow(action_ref, def_yaml_xformed) execution = self._client.executions.create(action_ref, workflow_input=inputs, **options) status = LIVEACTION_STATUS_RUNNING partial_results = {'tasks': []} # pylint: disable=no-member current_context = { 'execution_id': str(execution.id), 'workflow_name': execution.workflow_name } exec_context = self.context exec_context = self._build_mistral_context(exec_context, current_context) LOG.info('Mistral query context is %s' % exec_context) return (status, partial_results, exec_context)