def post(self, action): """ Create a new action. Handles requests: POST /actions/ """ # Perform validation validate_not_part_of_system_pack(action) action_validator.validate_action(action) # Write pack data files to disk (if any are provided) data_files = getattr(action, 'data_files', []) written_data_files = [] if data_files: written_data_files = self._handle_data_files(pack_name=action.pack, data_files=data_files) action_model = ActionAPI.to_model(action) LOG.debug('/actions/ POST verified ActionAPI object=%s', action) action_db = Action.add_or_update(action_model) LOG.debug('/actions/ POST saved ActionDB object=%s', action_db) # Dispatch an internal trigger for each written data file. This way user # automate comitting this files to git using StackStorm rule if written_data_files: self._dispatch_trigger_for_written_data_files( action_db=action_db, written_data_files=written_data_files) extra = {'acion_db': action_db} LOG.audit('Action created. Action.id=%s' % (action_db.id), extra=extra) action_api = ActionAPI.from_model(action_db) return action_api
def setUpClass(cls): super(TestActionExecutionService, cls).setUpClass() cls.runner = RunnerTypeAPI(**RUNNER) cls.runnerdb = RunnerType.add_or_update( RunnerTypeAPI.to_model(cls.runner)) cls.action = ActionAPI(**ACTION) cls.actiondb = Action.add_or_update(ActionAPI.to_model(cls.action))
def setUpClass(cls): super(ExecutionCancellationTestCase, cls).setUpClass() for _, fixture in six.iteritems(FIXTURES['actions']): instance = ActionAPI(**fixture) Action.add_or_update(ActionAPI.to_model(instance)) runners_registrar.register_runners()
def _register_action(self, pack, action): content = self._meta_loader.load(action) pack_field = content.get('pack', None) if not pack_field: content['pack'] = pack pack_field = pack if pack_field != pack: raise Exception('Model is in pack "%s" but field "pack" is different: %s' % (pack, pack_field)) action_api = ActionAPI(**content) action_api.validate() action_validator.validate_action(action_api) model = ActionAPI.to_model(action_api) action_ref = ResourceReference.to_string_reference(pack=pack, name=str(content['name'])) existing = action_utils.get_action_by_ref(action_ref) if not existing: LOG.debug('Action %s not found. Creating new one with: %s', action_ref, content) else: LOG.debug('Action %s found. Will be updated from: %s to: %s', action_ref, existing, model) model.id = existing.id try: model = Action.add_or_update(model) extra = {'action_db': model} LOG.audit('Action updated. Action %s from %s.', model, action, extra=extra) except Exception: LOG.exception('Failed to write action to db %s.', model.name) raise
def put(self, action_ref_or_id, action): action_db = self._get_by_ref_or_id(ref_or_id=action_ref_or_id) action_id = action_db.id try: validate_not_part_of_system_pack(action_db) except ValueValidationException as e: abort(http_client.BAD_REQUEST, str(e)) if not getattr(action, 'pack', None): action.pack = action_db.pack try: action_validator.validate_action(action) except ValueValidationException as e: abort(http_client.BAD_REQUEST, str(e)) return try: action_db = ActionAPI.to_model(action) action_db.id = action_id action_db = Action.add_or_update(action_db) except (ValidationError, ValueError) as e: LOG.exception('Unable to update action data=%s', action) abort(http_client.BAD_REQUEST, str(e)) return action_api = ActionAPI.from_model(action_db) LOG.debug('PUT /actions/ client_result=%s', action_api) return action_api
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 post(self, action): """ Create a new action. Handles requests: POST /actions/ """ if not hasattr(action, 'pack'): setattr(action, 'pack', DEFAULT_PACK_NAME) try: action_validator.validate_action(action) except ValueValidationException as e: abort(http_client.BAD_REQUEST, str(e)) return # ActionsController._validate_action_parameters(action, runnertype_db) action_model = ActionAPI.to_model(action) LOG.debug('/actions/ POST verified ActionAPI object=%s', action) action_db = Action.add_or_update(action_model) LOG.debug('/actions/ POST saved ActionDB object=%s', action_db) extra = {'action_db': action_db} LOG.audit('Action created. Action.id=%s' % (action_db.id), extra=extra) action_api = ActionAPI.from_model(action_db) return action_api
def post(self, action): """ Create a new action. Handles requests: POST /actions/ """ # Perform validation validate_not_part_of_system_pack(action) action_validator.validate_action(action) # Write pack data files to disk (if any are provided) data_files = getattr(action, 'data_files', []) written_data_files = [] if data_files: written_data_files = self._handle_data_files(pack_name=action.pack, data_files=data_files) action_model = ActionAPI.to_model(action) LOG.debug('/actions/ POST verified ActionAPI object=%s', action) action_db = Action.add_or_update(action_model) LOG.debug('/actions/ POST saved ActionDB object=%s', action_db) # Dispatch an internal trigger for each written data file. This way user # automate comitting this files to git using StackStorm rule if written_data_files: self._dispatch_trigger_for_written_data_files(action_db=action_db, written_data_files=written_data_files) extra = {'acion_db': action_db} LOG.audit('Action created. Action.id=%s' % (action_db.id), extra=extra) action_api = ActionAPI.from_model(action_db) return action_api
def get_all( self, exclude_attributes=None, include_attributes=None, sort=None, offset=0, limit=None, requester_user=None, **raw_filters, ): """ List all actions. Handles requests: GET /actions/views/overview """ resp = super(OverviewController, self)._get_all( exclude_fields=exclude_attributes, include_fields=include_attributes, sort=sort, offset=offset, limit=limit, raw_filters=raw_filters, requester_user=requester_user, ) runner_type_names = set([]) action_ids = [] result = [] for item in resp.json: action_api = ActionAPI(**item) result.append(action_api) runner_type_names.add(action_api.runner_type) action_ids.append(str(action_api.id)) # Add combined runner and action parameters to the compound result object # NOTE: This approach results in 2 additional queries while previous one resulted in # N * 2 additional queries # 1. Retrieve all the respective runner objects - we only need parameters runner_type_dbs = RunnerType.query( name__in=runner_type_names, only_fields=["name", "runner_parameters"]) runner_type_dbs = dict([(runner_db.name, runner_db) for runner_db in runner_type_dbs]) # 2. Retrieve all the respective action objects - we only need parameters action_dbs = dict([(action_db.id, action_db) for action_db in result]) for action_api in result: action_db = action_dbs.get(action_api.id, None) runner_db = runner_type_dbs.get(action_api.runner_type, None) all_params = action_param_utils.get_params_view( action_db=action_db, runner_db=runner_db, merged_only=True) action_api.parameters = all_params resp.json = result return resp
def setUpClass(cls): super(TestActionExecutionHistoryWorker, cls).setUpClass() runners_registrar.register_runner_types() action_local = ActionAPI(**copy.deepcopy(fixture.ARTIFACTS['actions']['local'])) Action.add_or_update(ActionAPI.to_model(action_local)) action_chain = ActionAPI(**copy.deepcopy(fixture.ARTIFACTS['actions']['chain'])) action_chain.entry_point = fixture.PATH + '/chain.yaml' Action.add_or_update(ActionAPI.to_model(action_chain))
def setUpClass(cls): super(ActionParamsUtilsTest, cls).setUpClass() cls.runnertype = RunnerTypeAPI( **FIXTURES['runners']['testrunner1.yaml']) cls.runnertype_db = RunnerType.add_or_update( RunnerTypeAPI.to_model(cls.runnertype)) cls.action = ActionAPI(**FIXTURES['actions']['action1.yaml']) cls.action_db = Action.add_or_update(ActionAPI.to_model(cls.action))
def setUpClass(cls): super(TestStreamController, cls).setUpClass() instance = RunnerTypeAPI(**RUNNER_TYPE_1) RunnerType.add_or_update(RunnerTypeAPI.to_model(instance)) instance = ActionAPI(**ACTION_1) Action.add_or_update(ActionAPI.to_model(instance))
def setUpClass(cls): super(TestActionExecutionHistoryWorker, cls).setUpClass() runners_registrar.register_runners() action_local = ActionAPI(**copy.deepcopy(fixture.ARTIFACTS["actions"]["local"])) Action.add_or_update(ActionAPI.to_model(action_local)) action_chain = ActionAPI(**copy.deepcopy(fixture.ARTIFACTS["actions"]["chain"])) action_chain.entry_point = fixture.PATH + "/chain.yaml" Action.add_or_update(ActionAPI.to_model(action_chain))
def setUpClass(cls): super(TestMistralRunner, cls).setUpClass() runners_registrar.register_runner_types() metadata = fixture.ARTIFACTS['metadata'] action_local = ActionAPI(**copy.deepcopy(metadata['actions']['local'])) Action.add_or_update(ActionAPI.to_model(action_local)) action_wkflow = ActionAPI(**copy.deepcopy(metadata['actions']['workflow-v2'])) Action.add_or_update(ActionAPI.to_model(action_wkflow))
def setUpClass(cls): super(TestMistralRunner, cls).setUpClass() runners_registrar.register_runner_types() metadata = fixture.ARTIFACTS['metadata'] action_local = ActionAPI(**copy.deepcopy(metadata['actions']['local'])) Action.add_or_update(ActionAPI.to_model(action_local)) action_wkflow = ActionAPI( **copy.deepcopy(metadata['actions']['workflow-v2'])) Action.add_or_update(ActionAPI.to_model(action_wkflow))
def _register_action(self, pack, action): content = self._meta_loader.load(action) pack_field = content.get('pack', None) if not pack_field: content['pack'] = pack pack_field = pack if pack_field != pack: raise Exception( 'Model is in pack "%s" but field "pack" is different: %s' % (pack, pack_field)) action_api = ActionAPI(**content) try: action_api.validate() except jsonschema.ValidationError as e: # We throw a more user-friendly exception on invalid parameter name msg = str(e) is_invalid_parameter_name = 'Additional properties are not allowed' in msg is_invalid_parameter_name &= 'in schema[\'properties\'][\'parameters\']' in msg if is_invalid_parameter_name: parameter_name = re.search('\'(.+?)\' was unexpected', msg).groups()[0] new_msg = ( 'Parameter name "%s" is invalid. Valid characters for parameter name ' 'are [a-zA-Z0-0_].' % (parameter_name)) new_msg += '\n\n' + msg raise jsonschema.ValidationError(new_msg) raise e action_validator.validate_action(action_api) model = ActionAPI.to_model(action_api) action_ref = ResourceReference.to_string_reference( pack=pack, name=str(content['name'])) existing = action_utils.get_action_by_ref(action_ref) if not existing: LOG.debug('Action %s not found. Creating new one with: %s', action_ref, content) else: LOG.debug('Action %s found. Will be updated from: %s to: %s', action_ref, existing, model) model.id = existing.id try: model = Action.add_or_update(model) extra = {'action_db': model} LOG.audit('Action updated. Action %s from %s.', model, action, extra=extra) except Exception: LOG.exception('Failed to write action to db %s.', model.name) raise
def post(self, action): """ Create a new action. Handles requests: POST /actions/ """ LOG.info('POST /actions/ with action data=%s', action) if not hasattr(action, 'enabled'): LOG.debug( 'POST /actions/ incoming action data has enabled field unset. ' 'Defaulting enabled to True.') setattr(action, 'enabled', True) else: action.enabled = bool(action.enabled) if not hasattr(action, 'pack'): setattr(action, 'pack', DEFAULT_PACK_NAME) try: action_validator.validate_action(action) except ValueValidationException as e: abort(http_client.BAD_REQUEST, str(e)) return # ActionsController._validate_action_parameters(action, runnertype_db) action_model = ActionAPI.to_model(action) LOG.debug('/actions/ POST verified ActionAPI object=%s', action) try: action_db = Action.add_or_update(action_model) except StackStormDBObjectConflictError as e: # If an existing DB object conflicts with new object then raise error. LOG.warn( '/actions/ POST unable to save ActionDB object "%s" due to uniqueness ' 'conflict. %s', action_model, str(e)) abort(http_client.CONFLICT, str(e), body={'conflict-id': e.conflict_id}) return except Exception as e: LOG.exception( '/actions/ POST unable to save ActionDB object "%s". %s', action_model, e) abort(http_client.INTERNAL_SERVER_ERROR, str(e)) return LOG.debug('/actions/ POST saved ActionDB object=%s', action_db) LOG.audit('Action created. Action=%s', action_db) action_api = ActionAPI.from_model(action_db) LOG.debug('POST /actions/ client_result=%s', action_api) return action_api
def setUpClass(cls): super(SchedulerTest, cls).setUpClass() for _, fixture in six.iteritems(FIXTURES['runners']): instance = RunnerTypeAPI(**fixture) RunnerType.add_or_update(RunnerTypeAPI.to_model(instance)) for _, fixture in six.iteritems(FIXTURES['actions']): instance = ActionAPI(**fixture) Action.add_or_update(ActionAPI.to_model(instance))
def setUpClass(cls): super(DSLTransformTestCase, cls).setUpClass() runners_registrar.register_runner_types() for _, fixture in six.iteritems(FIXTURES['runners']): instance = RunnerTypeAPI(**fixture) RunnerType.add_or_update(RunnerTypeAPI.to_model(instance)) for _, fixture in six.iteritems(FIXTURES['actions']): instance = ActionAPI(**fixture) Action.add_or_update(ActionAPI.to_model(instance))
def post(self, action, requester_user): """ Create a new action. Handles requests: POST /actions/ """ permission_type = PermissionType.ACTION_CREATE rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_api_permission( user_db=requester_user, resource_api=action, permission_type=permission_type) try: # Perform validation validate_not_part_of_system_pack(action) action_validator.validate_action(action) except ( ValidationError, ValueError, ValueValidationException, InvalidActionParameterException, ) as e: LOG.exception("Unable to create action data=%s", action) abort(http_client.BAD_REQUEST, six.text_type(e)) return # Write pack data files to disk (if any are provided) data_files = getattr(action, "data_files", []) written_data_files = [] if data_files: written_data_files = self._handle_data_files(pack_ref=action.pack, data_files=data_files) action_model = ActionAPI.to_model(action) LOG.debug("/actions/ POST verified ActionAPI object=%s", action) action_db = Action.add_or_update(action_model) LOG.debug("/actions/ POST saved ActionDB object=%s", action_db) # Dispatch an internal trigger for each written data file. This way user # automate comitting this files to git using StackStorm rule if written_data_files: self._dispatch_trigger_for_written_data_files( action_db=action_db, written_data_files=written_data_files) extra = {"acion_db": action_db} LOG.audit("Action created. Action.id=%s" % (action_db.id), extra=extra) action_api = ActionAPI.from_model(action_db) return Response(json=action_api, status=http_client.CREATED)
def setUpClass(cls): super(MistralValidationTest, cls).setUpClass() for _, fixture in six.iteritems(FIXTURES['runners']): instance = RunnerTypeAPI(**fixture) RunnerType.add_or_update(RunnerTypeAPI.to_model(instance)) for _, fixture in six.iteritems(FIXTURES['actions']): instance = ActionAPI(**fixture) Action.add_or_update(ActionAPI.to_model(instance)) cls.validator = wf_validation_utils.get_validator()
def _register_action(self, pack, action): content = self._meta_loader.load(action) pack_field = content.get('pack', None) if not pack_field: content['pack'] = pack pack_field = pack if pack_field != pack: raise Exception('Model is in pack "%s" but field "pack" is different: %s' % (pack, pack_field)) action_api = ActionAPI(**content) try: action_api.validate() except jsonschema.ValidationError as e: # We throw a more user-friendly exception on invalid parameter name msg = str(e) is_invalid_parameter_name = 'does not match any of the regexes: ' in msg if is_invalid_parameter_name: match = re.search('\'(.+?)\' does not match any of the regexes', msg) if match: parameter_name = match.groups()[0] else: parameter_name = 'unknown' new_msg = ('Parameter name "%s" is invalid. Valid characters for parameter name ' 'are [a-zA-Z0-0_].' % (parameter_name)) new_msg += '\n\n' + msg raise jsonschema.ValidationError(new_msg) raise e action_validator.validate_action(action_api) model = ActionAPI.to_model(action_api) action_ref = ResourceReference.to_string_reference(pack=pack, name=str(content['name'])) existing = action_utils.get_action_by_ref(action_ref) if not existing: LOG.debug('Action %s not found. Creating new one with: %s', action_ref, content) else: LOG.debug('Action %s found. Will be updated from: %s to: %s', action_ref, existing, model) model.id = existing.id try: model = Action.add_or_update(model) extra = {'action_db': model} LOG.audit('Action updated. Action %s from %s.', model, action, extra=extra) except Exception: LOG.exception('Failed to write action to db %s.', model.name) raise
def setUpClass(cls): super(DSLTransformTestCase, cls).setUpClass() runners_registrar.register_runner_types() action_local = ActionAPI(**copy.deepcopy(FIXTURES['actions']['local.yaml'])) Action.add_or_update(ActionAPI.to_model(action_local)) for action_name in ['action1', 'action2', 'action3']: metadata = copy.deepcopy(FIXTURES['actions']['local.yaml']) metadata['name'] = action_name metadata['pack'] = 'demo' action = ActionAPI(**metadata) Action.add_or_update(ActionAPI.to_model(action))
def post(self, action): """ Create a new action. Handles requests: POST /actions/ """ LOG.info('POST /actions/ with action data=%s', action) if not hasattr(action, 'enabled'): LOG.debug('POST /actions/ incoming action data has enabled field unset. ' 'Defaulting enabled to True.') setattr(action, 'enabled', True) else: action.enabled = bool(action.enabled) if not hasattr(action, 'pack'): setattr(action, 'pack', 'default') try: action_validator.validate_action(action) except ValueValidationException as e: abort(http_client.BAD_REQUEST, str(e)) return # ActionsController._validate_action_parameters(action, runnertype_db) action_model = ActionAPI.to_model(action) LOG.debug('/actions/ POST verified ActionAPI object=%s', action) try: action_db = Action.add_or_update(action_model) except NotUniqueError as e: # If an existing DB object conflicts with new object then raise error. LOG.warn('/actions/ POST unable to save ActionDB object "%s" due to uniqueness ' 'conflict. %s', action_model, str(e)) abort(http_client.CONFLICT, str(e)) return except Exception as e: LOG.exception('/actions/ POST unable to save ActionDB object "%s". %s', action_model, e) abort(http_client.INTERNAL_SERVER_ERROR, str(e)) return LOG.debug('/actions/ POST saved ActionDB object=%s', action_db) LOG.audit('Action created. Action=%s', action_db) action_api = ActionAPI.from_model(action_db) LOG.debug('POST /actions/ client_result=%s', action_api) return action_api
def setUpClass(cls): super(TestActionExecutionService, cls).setUpClass() cls.runner = RunnerTypeAPI(**RUNNER) cls.runnerdb = RunnerType.add_or_update( RunnerTypeAPI.to_model(cls.runner)) runner_api = RunnerTypeAPI(**RUNNER_ACTION_CHAIN) RunnerType.add_or_update(RunnerTypeAPI.to_model(runner_api)) cls.actions = { ACTION['name']: ActionAPI(**ACTION), ACTION_WORKFLOW['name']: ActionAPI(**ACTION_WORKFLOW), ACTION_OVR_PARAM['name']: ActionAPI(**ACTION_OVR_PARAM), ACTION_OVR_PARAM_MUTABLE['name']: ActionAPI(**ACTION_OVR_PARAM_MUTABLE), ACTION_OVR_PARAM_IMMUTABLE['name']: ActionAPI(**ACTION_OVR_PARAM_IMMUTABLE), ACTION_OVR_PARAM_BAD_ATTR['name']: ActionAPI(**ACTION_OVR_PARAM_BAD_ATTR), ACTION_OVR_PARAM_BAD_ATTR_NOOP['name']: ActionAPI(**ACTION_OVR_PARAM_BAD_ATTR_NOOP) } cls.actiondbs = { name: Action.add_or_update(ActionAPI.to_model(action)) for name, action in six.iteritems(cls.actions) } cls.container = RunnerContainer()
def put(self, action, ref_or_id, requester_user): action_db = self._get_by_ref_or_id(ref_or_id=ref_or_id) # Assert permissions permission_type = PermissionType.ACTION_MODIFY rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=action_db, permission_type=permission_type, ) action_id = action_db.id if not getattr(action, "pack", None): action.pack = action_db.pack # Perform validation validate_not_part_of_system_pack(action) action_validator.validate_action(action) # Write pack data files to disk (if any are provided) data_files = getattr(action, "data_files", []) written_data_files = [] if data_files: written_data_files = self._handle_data_files(pack_ref=action.pack, data_files=data_files) try: action_db = ActionAPI.to_model(action) LOG.debug("/actions/ PUT incoming action: %s", action_db) action_db.id = action_id action_db = Action.add_or_update(action_db) LOG.debug("/actions/ PUT after add_or_update: %s", action_db) except (ValidationError, ValueError) as e: LOG.exception("Unable to update action data=%s", action) abort(http_client.BAD_REQUEST, six.text_type(e)) return # Dispatch an internal trigger for each written data file. This way user # automate committing this files to git using StackStorm rule if written_data_files: self._dispatch_trigger_for_written_data_files( action_db=action_db, written_data_files=written_data_files) action_api = ActionAPI.from_model(action_db) LOG.debug("PUT /actions/ client_result=%s", action_api) return action_api
def _load_actions(): actions = {} action_dirs = ContentPackLoader().get_content(content_utils.get_packs_base_paths(), 'actions') for pack in action_dirs: for action_path in ActionsRegistrar().get_resources_from_pack(action_dirs[pack]): content = MetaLoader().load(action_path) ref = pack + "." + content['name'] action_api = ActionAPI(pack=pack, **content) action_api.validate() # action_validator.validate_action(action_api) actions[ref] = ActionAPI.to_model(action_api) return actions
def post(self, action, requester_user): """ Create a new action. Handles requests: POST /actions/ """ permission_type = PermissionType.ACTION_CREATE rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_api_permission(user_db=requester_user, resource_api=action, permission_type=permission_type) try: # Perform validation validate_not_part_of_system_pack(action) action_validator.validate_action(action) except (ValidationError, ValueError, ValueValidationException, InvalidActionParameterException) as e: LOG.exception('Unable to create action data=%s', action) abort(http_client.BAD_REQUEST, six.text_type(e)) return # Write pack data files to disk (if any are provided) data_files = getattr(action, 'data_files', []) written_data_files = [] if data_files: written_data_files = self._handle_data_files(pack_ref=action.pack, data_files=data_files) action_model = ActionAPI.to_model(action) LOG.debug('/actions/ POST verified ActionAPI object=%s', action) action_db = Action.add_or_update(action_model) LOG.debug('/actions/ POST saved ActionDB object=%s', action_db) # Dispatch an internal trigger for each written data file. This way user # automate comitting this files to git using StackStorm rule if written_data_files: self._dispatch_trigger_for_written_data_files(action_db=action_db, written_data_files=written_data_files) extra = {'acion_db': action_db} LOG.audit('Action created. Action.id=%s' % (action_db.id), extra=extra) action_api = ActionAPI.from_model(action_db) return Response(json=action_api, status=http_client.CREATED)
def get_all(self, exclude_attributes=None, include_attributes=None, sort=None, offset=0, limit=None, requester_user=None, **raw_filters): """ List all actions. Handles requests: GET /actions/views/overview """ resp = super(OverviewController, self)._get_all(exclude_fields=exclude_attributes, include_fields=include_attributes, sort=sort, offset=offset, limit=limit, raw_filters=raw_filters, requester_user=requester_user) runner_type_names = set([]) action_ids = [] result = [] for item in resp.json: action_api = ActionAPI(**item) result.append(action_api) runner_type_names.add(action_api.runner_type) action_ids.append(str(action_api.id)) # Add combined runner and action parameters to the compound result object # NOTE: This approach results in 2 additional queries while previous one resulted in # N * 2 additional queries # 1. Retrieve all the respective runner objects - we only need parameters runner_type_dbs = RunnerType.query(name__in=runner_type_names, only_fields=['name', 'runner_parameters']) runner_type_dbs = dict([(runner_db.name, runner_db) for runner_db in runner_type_dbs]) # 2. Retrieve all the respective action objects - we only need parameters action_dbs = dict([(action_db.id, action_db) for action_db in result]) for action_api in result: action_db = action_dbs.get(action_api.id, None) runner_db = runner_type_dbs.get(action_api.runner_type, None) all_params = action_param_utils.get_params_view(action_db=action_db, runner_db=runner_db, merged_only=True) action_api.parameters = all_params resp.json = result return resp
def test_execution_creation_manual_action_run(self): liveaction = self.MODELS['liveactions']['liveaction1.yaml'] pre_creation_timestamp = date_utils.get_datetime_utc_now() executions_util.create_execution_object(liveaction) post_creation_timestamp = date_utils.get_datetime_utc_now() execution = self._get_action_execution(liveaction__id=str( liveaction.id), raise_exception=True) self.assertDictEqual(execution.trigger, {}) self.assertDictEqual(execution.trigger_type, {}) self.assertDictEqual(execution.trigger_instance, {}) self.assertDictEqual(execution.rule, {}) action = action_utils.get_action_by_ref('core.local') self.assertDictEqual(execution.action, vars(ActionAPI.from_model(action))) runner = RunnerType.get_by_name(action.runner_type['name']) self.assertDictEqual(execution.runner, vars(RunnerTypeAPI.from_model(runner))) liveaction = LiveAction.get_by_id(str(liveaction.id)) self.assertEquals(execution.liveaction['id'], str(liveaction.id)) self.assertEquals(len(execution.log), 1) self.assertEquals(execution.log[0]['status'], liveaction.status) self.assertGreater(execution.log[0]['timestamp'], pre_creation_timestamp) self.assertLess(execution.log[0]['timestamp'], post_creation_timestamp)
def setUpClass(cls): super(ActionParamsUtilsTest, cls).setUpClass() cls.runnertype_dbs = {} cls.action_dbs = {} for _, fixture in six.iteritems(FIXTURES['runners']): instance = RunnerTypeAPI(**fixture) runnertype_db = RunnerType.add_or_update( RunnerTypeAPI.to_model(instance)) cls.runnertype_dbs[runnertype_db.name] = runnertype_db for _, fixture in six.iteritems(FIXTURES['actions']): instance = ActionAPI(**fixture) action_db = Action.add_or_update(ActionAPI.to_model(instance)) cls.action_dbs[action_db.name] = action_db
def setUpClass(cls): super(TestActionExecutionService, cls).setUpClass() cls.runner = RunnerTypeAPI(**RUNNER) cls.runnerdb = RunnerType.add_or_update(RunnerTypeAPI.to_model(cls.runner)) cls.action = ActionAPI(**ACTION) cls.actiondb = Action.add_or_update(ActionAPI.to_model(cls.action)) cls.container = RunnerContainer()
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))
def test_chained_executions(self): action_ref = ResourceReference(name='chain', pack='core') execution = ActionExecutionDB(action=action_ref.ref) execution = action_service.schedule(execution) execution = ActionExecution.get_by_id(str(execution.id)) self.assertEqual(execution.status, ACTIONEXEC_STATUS_SUCCEEDED) history = ActionExecutionHistory.get(execution__id=str(execution.id), raise_exception=True) action, _ = action_utils.get_action_by_dict({ 'name': action_ref.name, 'pack': action_ref.pack }) self.assertDictEqual(history.action, vars(ActionAPI.from_model(action))) runner = RunnerType.get_by_name(action.runner_type['name']) self.assertDictEqual(history.runner, vars(RunnerTypeAPI.from_model(runner))) execution = ActionExecution.get_by_id(str(execution.id)) self.assertDictEqual(history.execution, vars(ActionExecutionAPI.from_model(execution))) self.assertGreater(len(history.children), 0) for child in history.children: record = ActionExecutionHistory.get(id=child, raise_exception=True) self.assertEqual(record.parent, str(history.id)) self.assertEqual(record.action['name'], 'local') self.assertEqual(record.runner['name'], 'run-local')
def test_basic_execution(self): action_ref = ResourceReference(name='local', pack='core') execution = ActionExecutionDB(action=action_ref.ref, parameters={'cmd': 'uname -a'}) execution = action_service.schedule(execution) execution = ActionExecution.get_by_id(str(execution.id)) self.assertEqual(execution.status, ACTIONEXEC_STATUS_SUCCEEDED) history = ActionExecutionHistory.get(execution__id=str(execution.id), raise_exception=True) self.assertDictEqual(history.trigger, {}) self.assertDictEqual(history.trigger_type, {}) self.assertDictEqual(history.trigger_instance, {}) self.assertDictEqual(history.rule, {}) action, _ = action_utils.get_action_by_dict({ 'name': action_ref.name, 'pack': action_ref.pack }) self.assertDictEqual(history.action, vars(ActionAPI.from_model(action))) runner = RunnerType.get_by_name(action.runner_type['name']) self.assertDictEqual(history.runner, vars(RunnerTypeAPI.from_model(runner))) execution = ActionExecution.get_by_id(str(execution.id)) self.assertDictEqual(history.execution, vars(ActionExecutionAPI.from_model(execution)))
def test_execution_creation_action_triggered_by_rule(self): # Wait for the action execution to complete and then confirm outcome. trigger_type = self.MODELS['triggertypes']['triggertype2.yaml'] trigger = self.MODELS['triggers']['trigger2.yaml'] trigger_instance = self.MODELS['triggerinstances']['trigger_instance_1.yaml'] test_liveaction = self.FIXTURES['liveactions']['liveaction3.yaml'] rule = self.MODELS['rules']['rule3.yaml'] # Setup LiveAction to point to right rule and trigger_instance. # XXX: We need support for dynamic fixtures. test_liveaction['context']['rule']['id'] = str(rule.id) test_liveaction['context']['trigger_instance']['id'] = str(trigger_instance.id) test_liveaction_api = LiveActionAPI(**test_liveaction) test_liveaction = LiveAction.add_or_update(LiveActionAPI.to_model(test_liveaction_api)) liveaction = LiveAction.get(context__trigger_instance__id=str(trigger_instance.id)) self.assertIsNotNone(liveaction) self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_REQUESTED) executions_util.create_execution_object(liveaction) execution = self._get_action_execution(liveaction__id=str(liveaction.id), raise_exception=True) self.assertDictEqual(execution.trigger, vars(TriggerAPI.from_model(trigger))) self.assertDictEqual(execution.trigger_type, vars(TriggerTypeAPI.from_model(trigger_type))) self.assertDictEqual(execution.trigger_instance, vars(TriggerInstanceAPI.from_model(trigger_instance))) self.assertDictEqual(execution.rule, vars(RuleAPI.from_model(rule))) action = action_utils.get_action_by_ref(liveaction.action) self.assertDictEqual(execution.action, vars(ActionAPI.from_model(action))) runner = RunnerType.get_by_name(action.runner_type['name']) self.assertDictEqual(execution.runner, vars(RunnerTypeAPI.from_model(runner))) liveaction = LiveAction.get_by_id(str(liveaction.id)) self.assertEquals(execution.liveaction['id'], str(liveaction.id))
def test_basic_execution(self): liveaction = LiveActionDB(action='executions.local', parameters={'cmd': 'uname -a'}) liveaction, _ = action_service.request(liveaction) liveaction = self._wait_on_status(liveaction, action_constants.LIVEACTION_STATUS_FAILED) execution = self._get_action_execution( liveaction__id=str(liveaction.id), raise_exception=True ) self.assertDictEqual(execution.trigger, {}) self.assertDictEqual(execution.trigger_type, {}) self.assertDictEqual(execution.trigger_instance, {}) self.assertDictEqual(execution.rule, {}) action = action_utils.get_action_by_ref('executions.local') self.assertDictEqual(execution.action, vars(ActionAPI.from_model(action))) runner = RunnerType.get_by_name(action.runner_type['name']) self.assertDictEqual(execution.runner, vars(RunnerTypeAPI.from_model(runner))) liveaction = LiveAction.get_by_id(str(liveaction.id)) self.assertEqual(execution.start_timestamp, liveaction.start_timestamp) self.assertEqual(execution.end_timestamp, liveaction.end_timestamp) self.assertEqual(execution.result, liveaction.result) self.assertEqual(execution.status, liveaction.status) self.assertEqual(execution.context, liveaction.context) self.assertEqual(execution.liveaction['callback'], liveaction.callback) self.assertEqual(execution.liveaction['action'], liveaction.action)
def test_chained_executions(self): liveaction = LiveActionDB(action='core.chain') liveaction, _ = action_service.request(liveaction) liveaction = LiveAction.get_by_id(str(liveaction.id)) self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED) execution = self._get_action_execution(liveaction__id=str(liveaction.id), raise_exception=True) action = action_utils.get_action_by_ref('core.chain') self.assertDictEqual(execution.action, vars(ActionAPI.from_model(action))) runner = RunnerType.get_by_name(action.runner_type['name']) self.assertDictEqual(execution.runner, vars(RunnerTypeAPI.from_model(runner))) liveaction = LiveAction.get_by_id(str(liveaction.id)) self.assertEqual(execution.start_timestamp, liveaction.start_timestamp) self.assertEqual(execution.end_timestamp, liveaction.end_timestamp) self.assertEqual(execution.result, liveaction.result) self.assertEqual(execution.status, liveaction.status) self.assertEqual(execution.context, liveaction.context) self.assertEqual(execution.liveaction['callback'], liveaction.callback) self.assertEqual(execution.liveaction['action'], liveaction.action) self.assertGreater(len(execution.children), 0) for child in execution.children: record = ActionExecution.get(id=child, raise_exception=True) self.assertEqual(record.parent, str(execution.id)) self.assertEqual(record.action['name'], 'local') self.assertEqual(record.runner['name'], 'run-local')
def test_validate_runner_type_happy_case(self): action_api_dict = fixture.ARTIFACTS['actions']['local'] action_api = ActionAPI(**action_api_dict) try: action_validator.validate_action(action_api) except: self.fail('Exception validating action: %s' % json.dumps(action_api_dict))
def test_basic_execution(self): liveaction = LiveActionDB(action="executions.local", parameters={"cmd": "uname -a"}) liveaction, _ = action_service.request(liveaction) liveaction = self._wait_on_status( liveaction, action_constants.LIVEACTION_STATUS_FAILED) execution = self._get_action_execution(liveaction__id=str( liveaction.id), raise_exception=True) self.assertDictEqual(execution.trigger, {}) self.assertDictEqual(execution.trigger_type, {}) self.assertDictEqual(execution.trigger_instance, {}) self.assertDictEqual(execution.rule, {}) action = action_utils.get_action_by_ref("executions.local") self.assertDictEqual(execution.action, vars(ActionAPI.from_model(action))) runner = RunnerType.get_by_name(action.runner_type["name"]) self.assertDictEqual(execution.runner, vars(RunnerTypeAPI.from_model(runner))) liveaction = LiveAction.get_by_id(str(liveaction.id)) self.assertEqual(execution.start_timestamp, liveaction.start_timestamp) # NOTE: Timestamp of liveaction and execution may be a bit different, depending on how long # it takes to persist each object in the database self.assertEqual( execution.end_timestamp.replace(microsecond=0), liveaction.end_timestamp.replace(microsecond=0), ) self.assertEqual(execution.result, liveaction.result) self.assertEqual(execution.status, liveaction.status) self.assertEqual(execution.context, liveaction.context) self.assertEqual(execution.liveaction["callback"], liveaction.callback) self.assertEqual(execution.liveaction["action"], liveaction.action)
def test_chained_executions(self): with mock.patch("st2common.runners.register_runner", mock.MagicMock(return_value=action_chain_runner)): liveaction = LiveActionDB(action="executions.chain") liveaction, _ = action_service.request(liveaction) liveaction = LiveAction.get_by_id(str(liveaction.id)) self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED) execution = self._get_action_execution(liveaction__id=str(liveaction.id), raise_exception=True) action = action_utils.get_action_by_ref("executions.chain") self.assertDictEqual(execution.action, vars(ActionAPI.from_model(action))) runner = RunnerType.get_by_name(action.runner_type["name"]) self.assertDictEqual(execution.runner, vars(RunnerTypeAPI.from_model(runner))) liveaction = LiveAction.get_by_id(str(liveaction.id)) self.assertEqual(execution.start_timestamp, liveaction.start_timestamp) self.assertEqual(execution.end_timestamp, liveaction.end_timestamp) self.assertEqual(execution.result, liveaction.result) self.assertEqual(execution.status, liveaction.status) self.assertEqual(execution.context, liveaction.context) self.assertEqual(execution.liveaction["callback"], liveaction.callback) self.assertEqual(execution.liveaction["action"], liveaction.action) self.assertGreater(len(execution.children), 0) for child in execution.children: record = ActionExecution.get(id=child, raise_exception=True) self.assertEqual(record.parent, str(execution.id)) self.assertEqual(record.action["name"], "local") self.assertEqual(record.runner["name"], "run-local")
def get_all(self, sort=None, offset=0, limit=None, requester_user=None, **raw_filters): """ List all actions. Handles requests: GET /actions/views/overview """ resp = super(OverviewController, self)._get_all(sort=sort, offset=offset, limit=limit, raw_filters=raw_filters) result = [] for item in resp.json: action_api = ActionAPI(**item) result.append( self._transform_action_api(action_api=action_api, requester_user=requester_user)) resp.json = result return resp
def put(self, action, ref_or_id, requester_user): action_db = self._get_by_ref_or_id(ref_or_id=ref_or_id) # Assert permissions permission_type = PermissionType.ACTION_MODIFY rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=action_db, permission_type=permission_type) action_id = action_db.id if not getattr(action, 'pack', None): action.pack = action_db.pack # Perform validation validate_not_part_of_system_pack(action) action_validator.validate_action(action) # Write pack data files to disk (if any are provided) data_files = getattr(action, 'data_files', []) written_data_files = [] if data_files: written_data_files = self._handle_data_files(pack_ref=action.pack, data_files=data_files) try: action_db = ActionAPI.to_model(action) LOG.debug('/actions/ PUT incoming action: %s', action_db) action_db.id = action_id action_db = Action.add_or_update(action_db) LOG.debug('/actions/ PUT after add_or_update: %s', action_db) except (ValidationError, ValueError) as e: LOG.exception('Unable to update action data=%s', action) abort(http_client.BAD_REQUEST, six.text_type(e)) return # Dispatch an internal trigger for each written data file. This way user # automate committing this files to git using StackStorm rule if written_data_files: self._dispatch_trigger_for_written_data_files(action_db=action_db, written_data_files=written_data_files) action_api = ActionAPI.from_model(action_db) LOG.debug('PUT /actions/ client_result=%s', action_api) return action_api
def record_action_execution(self, body): try: history_id = bson.ObjectId() execution = ActionExecution.get_by_id(str(body.id)) action_ref = ResourceReference.from_string_reference( ref=execution.action) action_db, _ = action_utils.get_action_by_dict({ 'name': action_ref.name, 'pack': action_ref.pack }) runner = RunnerType.get_by_name(action_db.runner_type['name']) attrs = { 'id': history_id, 'action': vars(ActionAPI.from_model(action_db)), 'runner': vars(RunnerTypeAPI.from_model(runner)), 'execution': vars(ActionExecutionAPI.from_model(execution)) } if 'rule' in execution.context: rule = reference.get_model_from_ref( Rule, execution.context.get('rule', {})) attrs['rule'] = vars(RuleAPI.from_model(rule)) if 'trigger_instance' in execution.context: trigger_instance_id = execution.context.get( 'trigger_instance', {}) trigger_instance_id = trigger_instance_id.get('id', None) trigger_instance = TriggerInstance.get_by_id( trigger_instance_id) trigger = reference.get_model_by_resource_ref( db_api=Trigger, ref=trigger_instance.trigger) trigger_type = reference.get_model_by_resource_ref( db_api=TriggerType, ref=trigger.type) trigger_instance = reference.get_model_from_ref( TriggerInstance, execution.context.get('trigger_instance', {})) attrs['trigger_instance'] = vars( TriggerInstanceAPI.from_model(trigger_instance)) attrs['trigger'] = vars(TriggerAPI.from_model(trigger)) attrs['trigger_type'] = vars( TriggerTypeAPI.from_model(trigger_type)) parent = ActionExecutionHistory.get( execution__id=execution.context.get('parent', '')) if parent: attrs['parent'] = str(parent.id) if str(history_id) not in parent.children: parent.children.append(str(history_id)) ActionExecutionHistory.add_or_update(parent) history = ActionExecutionHistoryDB(**attrs) history = ActionExecutionHistory.add_or_update(history) except: LOG.exception('An unexpected error occurred while creating the ' 'action execution history.') raise
def setUpClass(cls): super(MistralValidationControllerTest, cls).setUpClass() for _, fixture in six.iteritems(FIXTURES['runners']): instance = RunnerTypeAPI(**fixture) RunnerType.add_or_update(RunnerTypeAPI.to_model(instance)) for _, fixture in six.iteritems(FIXTURES['actions']): instance = ActionAPI(**fixture) Action.add_or_update(ActionAPI.to_model(instance))
def setUpClass(cls): super(DSLTransformTestCase, cls).setUpClass() for _, fixture in six.iteritems(FIXTURES['runners']): instance = RunnerTypeAPI(**fixture) RunnerType.add_or_update(RunnerTypeAPI.to_model(instance)) for _, fixture in six.iteritems(FIXTURES['actions']): instance = ActionAPI(**fixture) Action.add_or_update(ActionAPI.to_model(instance))
def create_execution_object(liveaction, action_db=None, runnertype_db=None, publish=True): if not action_db: action_db = action_utils.get_action_by_ref(liveaction.action) if not runnertype_db: runnertype_db = RunnerType.get_by_name(action_db.runner_type['name']) attrs = { 'action': vars(ActionAPI.from_model(action_db)), 'parameters': liveaction['parameters'], 'runner': vars(RunnerTypeAPI.from_model(runnertype_db)) } attrs.update(_decompose_liveaction(liveaction)) if 'rule' in liveaction.context: rule = reference.get_model_from_ref(Rule, liveaction.context.get('rule', {})) attrs['rule'] = vars(RuleAPI.from_model(rule)) if 'trigger_instance' in liveaction.context: trigger_instance_id = liveaction.context.get('trigger_instance', {}) trigger_instance_id = trigger_instance_id.get('id', None) trigger_instance = TriggerInstance.get_by_id(trigger_instance_id) trigger = reference.get_model_by_resource_ref(db_api=Trigger, ref=trigger_instance.trigger) trigger_type = reference.get_model_by_resource_ref(db_api=TriggerType, ref=trigger.type) trigger_instance = reference.get_model_from_ref( TriggerInstance, liveaction.context.get('trigger_instance', {})) attrs['trigger_instance'] = vars(TriggerInstanceAPI.from_model(trigger_instance)) attrs['trigger'] = vars(TriggerAPI.from_model(trigger)) attrs['trigger_type'] = vars(TriggerTypeAPI.from_model(trigger_type)) parent = _get_parent_execution(liveaction) if parent: attrs['parent'] = str(parent.id) attrs['log'] = [_create_execution_log_entry(liveaction['status'])] # TODO: This object initialization takes 20-30or so ms execution = ActionExecutionDB(**attrs) # TODO: Do 100% research this is fully safe and unique in distributed setups execution.id = ObjectId() execution.web_url = _get_web_url_for_execution(str(execution.id)) # NOTE: User input data is already validate as part of the API request, # other data is set by us. Skipping validation here makes operation 10%-30% faster execution = ActionExecution.add_or_update(execution, publish=publish, validate=False) if parent and str(execution.id) not in parent.children: values = {} values['push__children'] = str(execution.id) ActionExecution.update(parent, **values) return execution
def post(self, action): """ Create a new action. Handles requests: POST /actions/ """ if not hasattr(action, 'pack'): setattr(action, 'pack', DEFAULT_PACK_NAME) try: action_validator.validate_action(action) except ValueValidationException as e: abort(http_client.BAD_REQUEST, str(e)) return # ActionsController._validate_action_parameters(action, runnertype_db) action_model = ActionAPI.to_model(action) LOG.debug('/actions/ POST verified ActionAPI object=%s', action) try: action_db = Action.add_or_update(action_model) except StackStormDBObjectConflictError as e: # If an existing DB object conflicts with new object then raise error. LOG.warn('/actions/ POST unable to save ActionDB object "%s" due to uniqueness ' 'conflict. %s', action_model, str(e)) abort(http_client.CONFLICT, str(e), body={'conflict-id': e.conflict_id}) return except Exception as e: LOG.exception('/actions/ POST unable to save ActionDB object "%s". %s', action_model, e) abort(http_client.INTERNAL_SERVER_ERROR, str(e)) return LOG.debug('/actions/ POST saved ActionDB object=%s', action_db) extra = {'action_db': action_db} LOG.audit('Action created. Action.id=%s' % (action_db.id), extra=extra) action_api = ActionAPI.from_model(action_db) return action_api
def get_all(self, **kwargs): """ List all actions. Handles requests: GET /actions/views/overview """ LOG.info('GET all /actions/views/overview with filters=%s', kwargs) kwargs = self._get_filters(**kwargs) action_dbs = Action.get_all(**kwargs) action_apis = [ActionAPI.from_model(action_db) for action_db in action_dbs] return map(self._transform_action_api, action_apis)
def setUpClass(cls): super(MistralValidationTest, cls).setUpClass() for _, fixture in six.iteritems(FIXTURES["runners"]): instance = RunnerTypeAPI(**fixture) RunnerType.add_or_update(RunnerTypeAPI.to_model(instance)) for _, fixture in six.iteritems(FIXTURES["actions"]): instance = ActionAPI(**fixture) Action.add_or_update(ActionAPI.to_model(instance)) cls.validator = wf_validation_utils.get_validator()
def get_one(self, action_id): """ List action by id. Handle: GET /actions/views/overview/1 """ LOG.info('GET /actions/views/overview with id=%s', action_id) action_db = LookupUtils._get_action_by_id(action_id) action_api = ActionAPI.from_model(action_db) return self._transform_action_api(action_api)
def put(self, action_ref_or_id, action): action_db = self._get_by_ref_or_id(ref_or_id=action_ref_or_id) # Assert permissions action_id = action_db.id if not getattr(action, 'pack', None): action.pack = action_db.pack # Perform validation validate_not_part_of_system_pack(action) action_validator.validate_action(action) # Write pack data files to disk (if any are provided) data_files = getattr(action, 'data_files', []) written_data_files = [] if data_files: written_data_files = self._handle_data_files(pack_name=action.pack, data_files=data_files) try: action_db = ActionAPI.to_model(action) action_db.id = action_id action_db = Action.add_or_update(action_db) except (ValidationError, ValueError) as e: LOG.exception('Unable to update action data=%s', action) abort(http_client.BAD_REQUEST, str(e)) return # Dispatch an internal trigger for each written data file. This way user # automate comitting this files to git using StackStorm rule if written_data_files: self._dispatch_trigger_for_written_data_files(action_db=action_db, written_data_files=written_data_files) action_api = ActionAPI.from_model(action_db) LOG.debug('PUT /actions/ client_result=%s', action_api) return action_api
def create_execution_object(liveaction, publish=True): action_db = action_utils.get_action_by_ref(liveaction.action) runner = RunnerType.get_by_name(action_db.runner_type['name']) attrs = { 'action': vars(ActionAPI.from_model(action_db)), 'parameters': liveaction['parameters'], 'runner': vars(RunnerTypeAPI.from_model(runner)) } attrs.update(_decompose_liveaction(liveaction)) if 'rule' in liveaction.context: rule = reference.get_model_from_ref(Rule, liveaction.context.get('rule', {})) attrs['rule'] = vars(RuleAPI.from_model(rule)) if 'trigger_instance' in liveaction.context: trigger_instance_id = liveaction.context.get('trigger_instance', {}) trigger_instance_id = trigger_instance_id.get('id', None) trigger_instance = TriggerInstance.get_by_id(trigger_instance_id) trigger = reference.get_model_by_resource_ref(db_api=Trigger, ref=trigger_instance.trigger) trigger_type = reference.get_model_by_resource_ref(db_api=TriggerType, ref=trigger.type) trigger_instance = reference.get_model_from_ref( TriggerInstance, liveaction.context.get('trigger_instance', {})) attrs['trigger_instance'] = vars(TriggerInstanceAPI.from_model(trigger_instance)) attrs['trigger'] = vars(TriggerAPI.from_model(trigger)) attrs['trigger_type'] = vars(TriggerTypeAPI.from_model(trigger_type)) parent = _get_parent_execution(liveaction) if parent: attrs['parent'] = str(parent.id) attrs['log'] = [_create_execution_log_entry(liveaction['status'])] execution = ActionExecutionDB(**attrs) execution = ActionExecution.add_or_update(execution, publish=False) # Update the web_url field in execution. Unfortunately, we need # the execution id for constructing the URL which we only get # after the model is written to disk. execution.web_url = _get_web_url_for_execution(str(execution.id)) execution = ActionExecution.add_or_update(execution, publish=publish) if parent: if str(execution.id) not in parent.children: parent.children.append(str(execution.id)) ActionExecution.add_or_update(parent) return execution