def post(self, rule): """ Create a new rule. Handles requests: POST /rules/ """ try: rule_db = RuleAPI.to_model(rule) LOG.debug('/rules/ POST verified RuleAPI and formulated RuleDB=%s', rule_db) rule_db = Rule.add_or_update(rule_db) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for rule data=%s.', rule) abort(http_client.BAD_REQUEST, str(e)) return except ValueValidationException as e: LOG.exception('Validation failed for rule data=%s.', rule) abort(http_client.BAD_REQUEST, str(e)) return except TriggerDoesNotExistException as e: msg = 'Trigger %s in rule does not exist in system' % rule.trigger['type'] LOG.exception(msg) abort(http_client.BAD_REQUEST, msg) return except StackStormDBObjectConflictError as e: LOG.warn('Rule creation of %s failed with uniqueness conflict. Exception %s', rule, str(e)) abort(http_client.CONFLICT, str(e), body={'conflict-id': e.conflict_id}) return extra = {'rule_db': rule_db} LOG.audit('Rule created. Rule.id=%s' % (rule_db.id), extra=extra) rule_api = RuleAPI.from_model(rule_db) return rule_api
def _register_rules_from_pack(self, pack, rules): registered_count = 0 for rule in rules: LOG.debug('Loading rule from %s.', rule) try: content = self._meta_loader.load(rule) rule_api = RuleAPI(**content) rule_api.validate() rule_db = RuleAPI.to_model(rule_api) try: rule_db.id = Rule.get_by_name(rule_api.name).id except ValueError: LOG.debug('Rule %s not found. Creating new one.', rule) try: rule_db = Rule.add_or_update(rule_db) extra = {'rule_db': rule_db} LOG.audit('Rule updated. Rule %s from %s.', rule_db, rule, extra=extra) except Exception: LOG.exception('Failed to create rule %s.', rule_api.name) except: LOG.exception('Failed registering rule from %s.', rule) else: registered_count += 1 return registered_count
def put(self, rule_ref_or_id, rule): try: rule_db = self._get_by_ref_or_id(rule_ref_or_id) except Exception as e: LOG.exception(e.message) abort(http_client.NOT_FOUND, e.message) return LOG.debug('PUT /rules/ lookup with id=%s found object: %s', rule_ref_or_id, rule_db) try: if rule.id is not None and rule.id is not '' and rule.id != rule_ref_or_id: LOG.warning('Discarding mismatched id=%s found in payload and using uri_id=%s.', rule.id, rule_ref_or_id) old_rule_db = rule_db rule_db = RuleAPI.to_model(rule) rule_db.id = rule_ref_or_id rule_db = Rule.add_or_update(rule_db) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for rule data=%s', rule) abort(http_client.BAD_REQUEST, str(e)) return extra = {'old_rule_db': old_rule_db, 'new_rule_db': rule_db} LOG.audit('Rule updated. Rule.id=%s.' % (rule_db.id), extra=extra) rule_api = RuleAPI.from_model(rule_db) return rule_api
def post(self, rule, requester_user): """ Create a new rule. Handles requests: POST /rules/ """ permission_type = PermissionType.RULE_CREATE rbac_utils.assert_user_has_resource_api_permission(user_db=requester_user, resource_api=rule, permission_type=permission_type) if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) # Validate that the authenticated user is admin if user query param is provided user = requester_user.name assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user) if not hasattr(rule, 'context'): rule.context = dict() rule.context['user'] = user try: rule_db = RuleAPI.to_model(rule) LOG.debug('/rules/ POST verified RuleAPI and formulated RuleDB=%s', rule_db) # Check referenced trigger and action permissions # Note: This needs to happen after "to_model" call since to_model performs some # validation (trigger exists, etc.) assert_user_has_rule_trigger_and_action_permission(user_db=requester_user, rule_api=rule) rule_db = Rule.add_or_update(rule_db) # After the rule has been added modify the ref_count. This way a failure to add # the rule due to violated constraints will have no impact on ref_count. increment_trigger_ref_count(rule_api=rule) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for rule data=%s.', rule) abort(http_client.BAD_REQUEST, six.text_type(e)) return except (ValueValidationException, jsonschema.ValidationError) as e: LOG.exception('Validation failed for rule data=%s.', rule) abort(http_client.BAD_REQUEST, six.text_type(e)) return except TriggerDoesNotExistException as e: msg = ('Trigger "%s" defined in the rule does not exist in system or it\'s missing ' 'required "parameters" attribute' % (rule.trigger['type'])) LOG.exception(msg) abort(http_client.BAD_REQUEST, msg) return extra = {'rule_db': rule_db} LOG.audit('Rule created. Rule.id=%s' % (rule_db.id), extra=extra) rule_api = RuleAPI.from_model(rule_db) return Response(json=rule_api, status=exc.HTTPCreated.code)
def post(self, rule): """ Create a new rule. Handles requests: POST /rules/ """ try: if not hasattr(rule, 'pack'): setattr(rule, 'pack', DEFAULT_PACK_NAME) rule_db = RuleAPI.to_model(rule) LOG.debug('/rules/ POST verified RuleAPI and formulated RuleDB=%s', rule_db) rule_db = Rule.add_or_update(rule_db) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for rule data=%s.', rule) abort(http_client.BAD_REQUEST, str(e)) return except (ValueValidationException, jsonschema.ValidationError) as e: LOG.exception('Validation failed for rule data=%s.', rule) abort(http_client.BAD_REQUEST, str(e)) return except TriggerDoesNotExistException as e: msg = 'Trigger %s in rule does not exist in system' % rule.trigger['type'] LOG.exception(msg) abort(http_client.BAD_REQUEST, msg) return extra = {'rule_db': rule_db} LOG.audit('Rule created. Rule.id=%s' % (rule_db.id), extra=extra) rule_api = RuleAPI.from_model(rule_db) return rule_api
def put(self, rule_ref_or_id, rule): rule_db = self._get_by_ref_or_id(rule_ref_or_id) LOG.debug('PUT /rules/ lookup with id=%s found object: %s', rule_ref_or_id, rule_db) try: if rule.id is not None and rule.id is not '' and rule.id != rule_ref_or_id: LOG.warning('Discarding mismatched id=%s found in payload and using uri_id=%s.', rule.id, rule_ref_or_id) old_rule_db = rule_db rule_db = RuleAPI.to_model(rule) # Check referenced trigger and action permissions # Note: This needs to happen after "to_model" call since to_model performs some # validation (trigger exists, etc.) assert_request_user_has_rule_trigger_and_action_permission(request=pecan.request, rule_api=rule) rule_db.id = rule_ref_or_id rule_db = Rule.add_or_update(rule_db) # After the rule has been added modify the ref_count. This way a failure to add # the rule due to violated constraints will have no impact on ref_count. increment_trigger_ref_count(rule_api=rule) except (ValueValidationException, jsonschema.ValidationError, ValueError) as e: LOG.exception('Validation failed for rule data=%s', rule) abort(http_client.BAD_REQUEST, str(e)) return # use old_rule_db for cleanup. cleanup_trigger_db_for_rule(old_rule_db) extra = {'old_rule_db': old_rule_db, 'new_rule_db': rule_db} LOG.audit('Rule updated. Rule.id=%s.' % (rule_db.id), extra=extra) rule_api = RuleAPI.from_model(rule_db) return rule_api
def post(self, rule): """ Create a new rule. Handles requests: POST /rules/ """ LOG.info('POST /rules/ with rule data=%s', rule) try: rule_db = RuleAPI.to_model(rule) LOG.debug('/rules/ POST verified RuleAPI and formulated RuleDB=%s', rule_db) rule_db = Rule.add_or_update(rule_db) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for rule data=%s.', rule) abort(http_client.BAD_REQUEST, str(e)) return except ValueValidationException as e: LOG.exception('Validation failed for rule data=%s.', rule) abort(http_client.BAD_REQUEST, str(e)) return except NotUniqueError as e: LOG.warn('Rule creation of %s failed with uniqueness conflict. Exception %s', rule, str(e)) abort(http_client.CONFLICT, str(e)) return LOG.audit('Rule created. Rule=%s', rule_db) rule_api = RuleAPI.from_model(rule_db) LOG.debug('POST /rules/ client_result=%s', rule_api) return rule_api
def put(self, rule, rule_ref_or_id, requester_user): rule_db = self._get_by_ref_or_id(rule_ref_or_id) rbac_utils = get_rbac_backend().get_utils_class() permission_type = PermissionType.RULE_MODIFY rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=rule, permission_type=permission_type) LOG.debug('PUT /rules/ lookup with id=%s found object: %s', rule_ref_or_id, rule_db) if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) # Validate that the authenticated user is admin if user query param is provided user = requester_user.name rbac_utils.assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user) if not hasattr(rule, 'context'): rule.context = dict() rule.context['user'] = user try: if rule.id is not None and rule.id != '' and rule.id != rule_ref_or_id: LOG.warning('Discarding mismatched id=%s found in payload and using uri_id=%s.', rule.id, rule_ref_or_id) old_rule_db = rule_db try: rule_db = RuleAPI.to_model(rule) except TriggerDoesNotExistException as e: abort(http_client.BAD_REQUEST, six.text_type(e)) return # Check referenced trigger and action permissions # Note: This needs to happen after "to_model" call since to_model performs some # validation (trigger exists, etc.) rbac_utils.assert_user_has_rule_trigger_and_action_permission(user_db=requester_user, rule_api=rule) rule_db.id = rule_ref_or_id rule_db = Rule.add_or_update(rule_db) # After the rule has been added modify the ref_count. This way a failure to add # the rule due to violated constraints will have no impact on ref_count. increment_trigger_ref_count(rule_api=rule) except (ValueValidationException, jsonschema.ValidationError, ValueError) as e: LOG.exception('Validation failed for rule data=%s', rule) abort(http_client.BAD_REQUEST, six.text_type(e)) return # use old_rule_db for cleanup. cleanup_trigger_db_for_rule(old_rule_db) extra = {'old_rule_db': old_rule_db, 'new_rule_db': rule_db} LOG.audit('Rule updated. Rule.id=%s.' % (rule_db.id), extra=extra) rule_api = RuleAPI.from_model(rule_db) return rule_api
def _register_rules_from_pack(self, pack, rules): registered_count = 0 for rule in rules: LOG.debug('Loading rule from %s.', rule) try: content = self._meta_loader.load(rule) 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)) rule_api = RuleAPI(**content) rule_api.validate() rule_db = RuleAPI.to_model(rule_api) # Migration from rule without pack to rule with pack. # There might be a rule with same name but in pack `default` # generated in migration script. In this case, we want to # delete so we don't have duplicates. if pack_field != DEFAULT_PACK_NAME: try: rule_ref = ResourceReference.to_string_reference(name=content['name'], pack=DEFAULT_PACK_NAME) LOG.debug('Looking for rule %s in pack %s', content['name'], DEFAULT_PACK_NAME) existing = Rule.get_by_ref(rule_ref) LOG.debug('Existing = %s', existing) if existing: LOG.debug('Found rule in pack default: %s; Deleting.', rule_ref) Rule.delete(existing) except: LOG.exception('Exception deleting rule from %s pack.', DEFAULT_PACK_NAME) try: rule_ref = ResourceReference.to_string_reference(name=content['name'], pack=content['pack']) existing = Rule.get_by_ref(rule_ref) if existing: rule_db.id = existing.id LOG.debug('Found existing rule: %s with id: %s', rule_ref, existing.id) except ValueError: LOG.debug('Rule %s not found. Creating new one.', rule) try: rule_db = Rule.add_or_update(rule_db) extra = {'rule_db': rule_db} LOG.audit('Rule updated. Rule %s from %s.', rule_db, rule, extra=extra) except Exception: LOG.exception('Failed to create rule %s.', rule_api.name) except: LOG.exception('Failed registering rule from %s.', rule) else: registered_count += 1 return registered_count
def test_triggered_execution(self): docs = { 'trigger_type': copy.deepcopy(fixture.ARTIFACTS['trigger_type']), 'trigger': copy.deepcopy(fixture.ARTIFACTS['trigger']), 'rule': copy.deepcopy(fixture.ARTIFACTS['rule']), 'trigger_instance': copy.deepcopy(fixture.ARTIFACTS['trigger_instance'])} # Trigger an action execution. trigger_type = TriggerType.add_or_update( TriggerTypeAPI.to_model(TriggerTypeAPI(**docs['trigger_type']))) trigger = Trigger.add_or_update(TriggerAPI.to_model(TriggerAPI(**docs['trigger']))) rule = RuleAPI.to_model(RuleAPI(**docs['rule'])) rule.trigger = reference.get_str_resource_ref_from_model(trigger) rule = Rule.add_or_update(rule) trigger_instance = TriggerInstance.add_or_update( TriggerInstanceAPI.to_model(TriggerInstanceAPI(**docs['trigger_instance']))) trace_service.add_or_update_given_trace_context( trace_context={'trace_tag': 'test_triggered_execution_trace'}, trigger_instances=[str(trigger_instance.id)]) enforcer = RuleEnforcer(trigger_instance, rule) enforcer.enforce() # Wait for the action execution to complete and then confirm outcome. liveaction = LiveAction.get(context__trigger_instance__id=str(trigger_instance.id)) self.assertIsNotNone(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, 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.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_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 filter(self): LOG.info('Validating rule %s for %s.', self.rule.id, self.trigger['name']) if not self.rule.enabled: return False criteria = RuleAPI.from_model(self.rule).criteria is_rule_applicable = True if criteria and not self.trigger_instance.payload: return False transform = get_transformer(self.trigger_instance.payload) LOG.debug('Trigger payload: %s', self.trigger_instance.payload) for criterion_k in criteria.keys(): criterion_v = criteria[criterion_k] is_rule_applicable = self._check_criterion(criterion_k, criterion_v, transform) if not is_rule_applicable: break if not is_rule_applicable: LOG.debug('Rule %s not applicable for %s.', self.rule.id, self.trigger['name']) return is_rule_applicable
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, rule): """ Create a new rule. Handles requests: POST /rules/ """ try: rule_db = RuleAPI.to_model(rule) LOG.debug('/rules/ POST verified RuleAPI and formulated RuleDB=%s', rule_db) # Check referenced trigger and action permissions # Note: This needs to happen after "to_model" call since to_model performs some # validation (trigger exists, etc.) assert_request_user_has_rule_trigger_and_action_permission(request=pecan.request, rule_api=rule) rule_db = Rule.add_or_update(rule_db) # After the rule has been added modify the ref_count. This way a failure to add # the rule due to violated constraints will have no impact on ref_count. increment_trigger_ref_count(rule_api=rule) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for rule data=%s.', rule) abort(http_client.BAD_REQUEST, str(e)) return except (ValueValidationException, jsonschema.ValidationError) as e: LOG.exception('Validation failed for rule data=%s.', rule) abort(http_client.BAD_REQUEST, str(e)) return except TriggerDoesNotExistException as e: msg = ('Trigger "%s" defined in the rule does not exist in system or it\'s missing ' 'required "parameters" attribute' % (rule.trigger['type'])) LOG.exception(msg) abort(http_client.BAD_REQUEST, msg) return extra = {'rule_db': rule_db} LOG.audit('Rule created. Rule.id=%s' % (rule_db.id), extra=extra) rule_api = RuleAPI.from_model(rule_db) return rule_api
def get_all(self, **kw): """ List all rules. Handles requests: GET /rules/ """ LOG.info('GET all /rules/ with filters=%s', kw) rule_dbs = Rule.get_all(**kw) rule_apis = [RuleAPI.from_model(rule_db) for rule_db in rule_dbs] LOG.debug('GET all /rules/ client_result=%s', rule_apis) return rule_apis
def get_one(self, id): """ List rule by id. Handle: GET /rules/1 """ LOG.info('GET /rules/ with id=%s', id) rule_db = RuleController.__get_by_id(id) rule_api = RuleAPI.from_model(rule_db) LOG.debug('GET /rules/ with id=%s, client_result=%s', id, rule_api) return rule_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
def post(self, rule): """ Create a new rule. Handles requests: POST /rules/ """ try: if not hasattr(rule, 'pack'): setattr(rule, 'pack', DEFAULT_PACK_NAME) rule_db = RuleAPI.to_model(rule) LOG.debug('/rules/ POST verified RuleAPI and formulated RuleDB=%s', rule_db) # Check referenced trigger and action permissions # Note: This needs to happen after "to_model" call since to_model performs some # validation (trigger exists, etc.) assert_request_user_has_rule_trigger_and_action_permission(request=pecan.request, rule_api=rule) rule_db = Rule.add_or_update(rule_db) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for rule data=%s.', rule) abort(http_client.BAD_REQUEST, str(e)) return except (ValueValidationException, jsonschema.ValidationError) as e: LOG.exception('Validation failed for rule data=%s.', rule) abort(http_client.BAD_REQUEST, str(e)) return except TriggerDoesNotExistException as e: msg = 'Trigger %s in rule does not exist in system' % rule.trigger['type'] LOG.exception(msg) abort(http_client.BAD_REQUEST, msg) return extra = {'rule_db': rule_db} LOG.audit('Rule created. Rule.id=%s' % (rule_db.id), extra=extra) rule_api = RuleAPI.from_model(rule_db) return rule_api
def test_create_trigger_db_from_rule_duplicate(self): test_fixtures = { "rules": ["cron_timer_rule_1.yaml", "cron_timer_rule_2.yaml"] } loader = FixturesLoader() fixtures = loader.load_fixtures(fixtures_pack="generic", fixtures_dict=test_fixtures) rules = fixtures["rules"] trigger_db_ret_1 = trigger_service.create_trigger_db_from_rule( RuleAPI(**rules["cron_timer_rule_1.yaml"])) self.assertIsNotNone(trigger_db_ret_1) trigger_db_ret_2 = trigger_service.create_trigger_db_from_rule( RuleAPI(**rules["cron_timer_rule_2.yaml"])) self.assertIsNotNone(trigger_db_ret_2) self.assertEqual(trigger_db_ret_1, trigger_db_ret_2, "Should reuse same trigger.") trigger_db = Trigger.get_by_id(trigger_db_ret_1.id) self.assertDictEqual( trigger_db.parameters, rules["cron_timer_rule_1.yaml"]["trigger"]["parameters"], )
def put(self, rule_id, rule): LOG.info('PUT /rules/ with rule id=%s and data=%s', rule_id, rule) rule_db = RuleController.__get_by_id(rule_id) LOG.debug('PUT /rules/ lookup with id=%s found object: %s', rule_id, rule_db) try: if rule.id is not None and rule.id is not '' and rule.id != rule_id: LOG.warning('Discarding mismatched id=%s found in payload and using uri_id=%s.', rule.id, rule_id) old_rule_db = rule_db rule_db = RuleAPI.to_model(rule) rule_db.id = rule_id rule_db = Rule.add_or_update(rule_db) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for rule data=%s', rule) abort(http_client.BAD_REQUEST, str(e)) return LOG.audit('Rule updated. Rule=%s and original Rule=%s.', rule_db, old_rule_db) rule_api = RuleAPI.from_model(rule_db) LOG.debug('PUT /rules/ client_result=%s', rule_api) return rule_api
def put(self, rule_ref_or_id, rule): rule_db = self._get_by_ref_or_id(rule_ref_or_id) LOG.debug('PUT /rules/ lookup with id=%s found object: %s', rule_ref_or_id, rule_db) try: if rule.id is not None and rule.id is not '' and rule.id != rule_ref_or_id: LOG.warning( 'Discarding mismatched id=%s found in payload and using uri_id=%s.', rule.id, rule_ref_or_id) old_rule_db = rule_db rule_db = RuleAPI.to_model(rule) # Check referenced trigger and action permissions # Note: This needs to happen after "to_model" call since to_model performs some # validation (trigger exists, etc.) assert_request_user_has_rule_trigger_and_action_permission( request=pecan.request, rule_api=rule) rule_db.id = rule_ref_or_id rule_db = Rule.add_or_update(rule_db) # After the rule has been added modify the ref_count. This way a failure to add # the rule due to violated constraints will have no impact on ref_count. increment_trigger_ref_count(rule_api=rule) except (ValueValidationException, jsonschema.ValidationError, ValueError) as e: LOG.exception('Validation failed for rule data=%s', rule) abort(http_client.BAD_REQUEST, str(e)) return # use old_rule_db for cleanup. cleanup_trigger_db_for_rule(old_rule_db) extra = {'old_rule_db': old_rule_db, 'new_rule_db': rule_db} LOG.audit('Rule updated. Rule.id=%s.' % (rule_db.id), extra=extra) rule_api = RuleAPI.from_model(rule_db) return rule_api
def test_triggered_execution(self): docs = { 'trigger_type': copy.deepcopy(fixture.ARTIFACTS['trigger_type']), 'trigger': copy.deepcopy(fixture.ARTIFACTS['trigger']), 'rule': copy.deepcopy(fixture.ARTIFACTS['rule']), 'trigger_instance': copy.deepcopy(fixture.ARTIFACTS['trigger_instance'])} # Trigger an action execution. trigger_type = TriggerType.add_or_update( TriggerTypeAPI.to_model(TriggerTypeAPI(**docs['trigger_type']))) trigger = Trigger.add_or_update(TriggerAPI.to_model(TriggerAPI(**docs['trigger']))) rule = RuleAPI.to_model(RuleAPI(**docs['rule'])) rule.trigger = reference.get_str_resource_ref_from_model(trigger) rule = Rule.add_or_update(rule) trigger_instance = TriggerInstance.add_or_update( TriggerInstanceAPI.to_model(TriggerInstanceAPI(**docs['trigger_instance']))) enforcer = RuleEnforcer(trigger_instance, rule) enforcer.enforce() # Wait for the action execution to complete and then confirm outcome. execution = ActionExecution.get(context__trigger_instance__id=str(trigger_instance.id)) self.assertIsNotNone(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, vars(TriggerAPI.from_model(trigger))) self.assertDictEqual(history.trigger_type, vars(TriggerTypeAPI.from_model(trigger_type))) self.assertDictEqual(history.trigger_instance, vars(TriggerInstanceAPI.from_model(trigger_instance))) self.assertDictEqual(history.rule, vars(RuleAPI.from_model(rule))) action_ref = ResourceReference.from_string_reference(ref=execution.action) 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 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
def post(self, rule): """ Create a new rule. Handles requests: POST /rules/ """ LOG.info('POST /rules/ with rule data=%s', rule) try: rule_db = RuleAPI.to_model(rule) LOG.debug('/rules/ POST verified RuleAPI and formulated RuleDB=%s', rule_db) rule_db = Rule.add_or_update(rule_db) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for rule data=%s.', rule) abort(http_client.BAD_REQUEST, str(e)) return except ValueValidationException as e: LOG.exception('Validation failed for rule data=%s.', rule) abort(http_client.BAD_REQUEST, str(e)) return except TriggerDoesNotExistException as e: msg = 'Trigger %s in rule does not exist in system' % rule.trigger['type'] LOG.exception(msg) abort(http_client.BAD_REQUEST, msg) return except StackStormDBObjectConflictError as e: LOG.warn('Rule creation of %s failed with uniqueness conflict. Exception %s', rule, str(e)) abort(http_client.CONFLICT, str(e), body={'conflict-id': e.conflict_id}) return LOG.audit('Rule created. Rule=%s', rule_db) rule_api = RuleAPI.from_model(rule_db) LOG.debug('POST /rules/ client_result=%s', rule_api) return rule_api
def post(self, rule): """ Create a new rule. Handles requests: POST /rules/ """ try: if not hasattr(rule, "pack"): setattr(rule, "pack", DEFAULT_PACK_NAME) rule_db = RuleAPI.to_model(rule) LOG.debug("/rules/ POST verified RuleAPI and formulated RuleDB=%s", rule_db) rule_db = Rule.add_or_update(rule_db) except (ValidationError, ValueError) as e: LOG.exception("Validation failed for rule data=%s.", rule) abort(http_client.BAD_REQUEST, str(e)) return except (ValueValidationException, jsonschema.ValidationError) as e: LOG.exception("Validation failed for rule data=%s.", rule) abort(http_client.BAD_REQUEST, str(e)) return except TriggerDoesNotExistException as e: msg = "Trigger %s in rule does not exist in system" % rule.trigger["type"] LOG.exception(msg) abort(http_client.BAD_REQUEST, msg) return except StackStormDBObjectConflictError as e: LOG.warn("Rule creation of %s failed with uniqueness conflict. Exception %s", rule, str(e)) abort(http_client.CONFLICT, str(e), body={"conflict-id": e.conflict_id}) return extra = {"rule_db": rule_db} LOG.audit("Rule created. Rule.id=%s" % (rule_db.id), extra=extra) rule_api = RuleAPI.from_model(rule_db) return rule_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 record_action_execution(self, body): try: execution = ActionExecution.get_by_id(str(body.id)) action_db = action_utils.get_action_by_ref(execution.action) runner = RunnerType.get_by_name(action_db.runner_type['name']) attrs = { '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) history = ActionExecutionHistoryDB(**attrs) history = ActionExecutionHistory.add_or_update(history) if parent: if str(history.id) not in parent.children: parent.children.append(str(history.id)) ActionExecutionHistory.add_or_update(parent) except: LOG.exception('An unexpected error occurred while creating the ' 'action execution history.') raise
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)), '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 = ActionExecution.get( liveaction__id=liveaction.context.get('parent', '')) if parent: attrs['parent'] = str(parent.id) execution = ActionExecutionDB(**attrs) 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
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) execution = ActionExecutionDB(**attrs) 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
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.assertEqual(execution.liveaction["id"], str(liveaction.id))
def _register_rules_from_pack(pack, rules): for rule in rules: LOG.debug('Loading rule from %s.', rule) try: with open(rule, 'r') as fd: try: content = json.load(fd) except ValueError: LOG.exception('Unable to load rule from %s.', rule) continue rule_api = RuleAPI(**content) rule_db = RuleAPI.to_model(rule_api) try: rule_db.id = Rule.get_by_name(rule_api.name).id except ValueError: LOG.info('Rule %s not found. Creating new one.', rule) try: rule_db = Rule.add_or_update(rule_db) LOG.audit('Rule updated. Rule %s from %s.', rule_db, rule) except Exception: LOG.exception('Failed to create rule %s.', rule_api.name) except: LOG.exception('Failed registering rule from %s.', rule)
def put(self, rule, rule_ref_or_id, requester_user): rule_db = self._get_by_ref_or_id(rule_ref_or_id) rbac_utils = get_rbac_backend().get_utils_class() permission_type = PermissionType.RULE_MODIFY rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=rule, permission_type=permission_type) LOG.debug("PUT /rules/ lookup with id=%s found object: %s", rule_ref_or_id, rule_db) if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) # Validate that the authenticated user is admin if user query param is provided user = requester_user.name rbac_utils.assert_user_is_admin_if_user_query_param_is_provided( user_db=requester_user, user=user) if not hasattr(rule, "context"): rule.context = dict() rule.context["user"] = user try: if rule.id is not None and rule.id != "" and rule.id != rule_ref_or_id: LOG.warning( "Discarding mismatched id=%s found in payload and using uri_id=%s.", rule.id, rule_ref_or_id, ) old_rule_db = rule_db try: rule_db = RuleAPI.to_model(rule) except TriggerDoesNotExistException as e: abort(http_client.BAD_REQUEST, six.text_type(e)) return # Check referenced trigger and action permissions # Note: This needs to happen after "to_model" call since to_model performs some # validation (trigger exists, etc.) rbac_utils.assert_user_has_rule_trigger_and_action_permission( user_db=requester_user, rule_api=rule) rule_db.id = rule_ref_or_id rule_db = Rule.add_or_update(rule_db) # After the rule has been added modify the ref_count. This way a failure to add # the rule due to violated constraints will have no impact on ref_count. increment_trigger_ref_count(rule_api=rule) except (ValueValidationException, jsonschema.ValidationError, ValueError) as e: LOG.exception("Validation failed for rule data=%s", rule) abort(http_client.BAD_REQUEST, six.text_type(e)) return # use old_rule_db for cleanup. cleanup_trigger_db_for_rule(old_rule_db) extra = {"old_rule_db": old_rule_db, "new_rule_db": rule_db} LOG.audit("Rule updated. Rule.id=%s." % (rule_db.id), extra=extra) rule_api = RuleAPI.from_model(rule_db) return rule_api
def post(self, rule, requester_user): """ Create a new rule. Handles requests: POST /rules/ """ rbac_utils = get_rbac_backend().get_utils_class() permission_type = PermissionType.RULE_CREATE rbac_utils.assert_user_has_resource_api_permission( user_db=requester_user, resource_api=rule, permission_type=permission_type) if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) # Validate that the authenticated user is admin if user query param is provided user = requester_user.name rbac_utils.assert_user_is_admin_if_user_query_param_is_provided( user_db=requester_user, user=user) if not hasattr(rule, "context"): rule.context = dict() rule.context["user"] = user try: rule_db = RuleAPI.to_model(rule) LOG.debug("/rules/ POST verified RuleAPI and formulated RuleDB=%s", rule_db) # Check referenced trigger and action permissions # Note: This needs to happen after "to_model" call since to_model performs some # validation (trigger exists, etc.) rbac_utils.assert_user_has_rule_trigger_and_action_permission( user_db=requester_user, rule_api=rule) rule_db = Rule.add_or_update(rule_db) # After the rule has been added modify the ref_count. This way a failure to add # the rule due to violated constraints will have no impact on ref_count. increment_trigger_ref_count(rule_api=rule) except (ValidationError, ValueError) as e: LOG.exception("Validation failed for rule data=%s.", rule) abort(http_client.BAD_REQUEST, six.text_type(e)) return except (ValueValidationException, jsonschema.ValidationError) as e: LOG.exception("Validation failed for rule data=%s.", rule) abort(http_client.BAD_REQUEST, six.text_type(e)) return except TriggerDoesNotExistException: msg = ( 'Trigger "%s" defined in the rule does not exist in system or it\'s missing ' 'required "parameters" attribute' % (rule.trigger["type"])) LOG.exception(msg) abort(http_client.BAD_REQUEST, msg) return extra = {"rule_db": rule_db} LOG.audit("Rule created. Rule.id=%s" % (rule_db.id), extra=extra) rule_api = RuleAPI.from_model(rule_db) return Response(json=rule_api, status=exc.HTTPCreated.code)
def test_triggered_execution(self): docs = { 'trigger_type': copy.deepcopy(fixture.ARTIFACTS['trigger_type']), 'trigger': copy.deepcopy(fixture.ARTIFACTS['trigger']), 'rule': copy.deepcopy(fixture.ARTIFACTS['rule']), 'trigger_instance': copy.deepcopy(fixture.ARTIFACTS['trigger_instance']) } # Trigger an action execution. trigger_type = TriggerType.add_or_update( TriggerTypeAPI.to_model(TriggerTypeAPI(**docs['trigger_type']))) trigger = Trigger.add_or_update( TriggerAPI.to_model(TriggerAPI(**docs['trigger']))) rule = RuleAPI.to_model(RuleAPI(**docs['rule'])) rule.trigger = reference.get_str_resource_ref_from_model(trigger) rule = Rule.add_or_update(rule) trigger_instance = TriggerInstance.add_or_update( TriggerInstanceAPI.to_model( TriggerInstanceAPI(**docs['trigger_instance']))) trace_service.add_or_update_given_trace_context( trace_context={'trace_tag': 'test_triggered_execution_trace'}, trigger_instances=[str(trigger_instance.id)]) enforcer = RuleEnforcer(trigger_instance, rule) enforcer.enforce() # Wait for the action execution to complete and then confirm outcome. liveaction = LiveAction.get( context__trigger_instance__id=str(trigger_instance.id)) self.assertIsNotNone(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) 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.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 _get_sample_rules(self): if self.rules: # Make sure rules are created only once return self.rules RULE_1 = { 'enabled': True, 'name': 'st2.test.rule1', 'pack': 'yoyohoneysingh', 'trigger': { 'type': 'dummy_pack_1.st2.test.trigger1' }, 'criteria': { 'k1': { # Missing prefix 'trigger'. This rule won't match. 'pattern': 't1_p_v', 'type': 'equals' } }, 'action': { 'ref': 'sixpack.st2.test.action', 'parameters': { 'ip2': '{{rule.k1}}', 'ip1': '{{trigger.t1_p}}' } }, 'id': '23', 'description': '' } rule_api = RuleAPI(**RULE_1) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) self.rules.append(rule_db) RULE_2 = { # Rule should match. 'enabled': True, 'name': 'st2.test.rule2', 'pack': 'yoyohoneysingh', 'trigger': { 'type': 'dummy_pack_1.st2.test.trigger1' }, 'criteria': { 'trigger.k1': { 'pattern': 't1_p_v', 'type': 'equals' } }, 'action': { 'ref': 'sixpack.st2.test.action', 'parameters': { 'ip2': '{{rule.k1}}', 'ip1': '{{trigger.t1_p}}' } }, 'id': '23', 'description': '' } rule_api = RuleAPI(**RULE_2) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) self.rules.append(rule_db) RULE_3 = { 'enabled': False, # Disabled rule shouldn't match. 'name': 'st2.test.rule3', 'pack': 'yoyohoneysingh', 'trigger': { 'type': 'dummy_pack_1.st2.test.trigger1' }, 'criteria': { 'trigger.k1': { 'pattern': 't1_p_v', 'type': 'equals' } }, 'action': { 'ref': 'sixpack.st2.test.action', 'parameters': { 'ip2': '{{rule.k1}}', 'ip1': '{{trigger.t1_p}}' } }, 'id': '23', 'description': '' } rule_api = RuleAPI(**RULE_3) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) self.rules.append(rule_db) return self.rules
def test_triggered_execution(self): docs = { "trigger_type": copy.deepcopy(fixture.ARTIFACTS["trigger_type"]), "trigger": copy.deepcopy(fixture.ARTIFACTS["trigger"]), "rule": copy.deepcopy(fixture.ARTIFACTS["rule"]), "trigger_instance": copy.deepcopy(fixture.ARTIFACTS["trigger_instance"]), } # Trigger an action execution. trigger_type = TriggerType.add_or_update( TriggerTypeAPI.to_model(TriggerTypeAPI(**docs["trigger_type"]))) trigger = Trigger.add_or_update( TriggerAPI.to_model(TriggerAPI(**docs["trigger"]))) rule = RuleAPI.to_model(RuleAPI(**docs["rule"])) rule.trigger = reference.get_str_resource_ref_from_model(trigger) rule = Rule.add_or_update(rule) trigger_instance = TriggerInstance.add_or_update( TriggerInstanceAPI.to_model( TriggerInstanceAPI(**docs["trigger_instance"]))) trace_service.add_or_update_given_trace_context( trace_context={"trace_tag": "test_triggered_execution_trace"}, trigger_instances=[str(trigger_instance.id)], ) enforcer = RuleEnforcer(trigger_instance, rule) enforcer.enforce() # Wait for the action execution to complete and then confirm outcome. liveaction = LiveAction.get( context__trigger_instance__id=str(trigger_instance.id)) self.assertIsNotNone(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, 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.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 _setup_sample_rule(self, rule): rule_api = RuleAPI(**rule) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) return rule_db
def _register_rules_from_pack(self, pack, rules): registered_count = 0 # TODO: Refactor this monstrosity for rule in rules: LOG.debug('Loading rule from %s.', rule) try: content = self._meta_loader.load(rule) 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)) rule_api = RuleAPI(**content) rule_api.validate() rule_db = RuleAPI.to_model(rule_api) # Migration from rule without pack to rule with pack. # There might be a rule with same name but in pack `default` # generated in migration script. In this case, we want to # delete so we don't have duplicates. if pack_field != DEFAULT_PACK_NAME: try: rule_ref = ResourceReference.to_string_reference(name=content['name'], pack=DEFAULT_PACK_NAME) LOG.debug('Looking for rule %s in pack %s', content['name'], DEFAULT_PACK_NAME) existing = Rule.get_by_ref(rule_ref) LOG.debug('Existing = %s', existing) if existing: LOG.debug('Found rule in pack default: %s; Deleting.', rule_ref) Rule.delete(existing) except: LOG.exception('Exception deleting rule from %s pack.', DEFAULT_PACK_NAME) try: rule_ref = ResourceReference.to_string_reference(name=content['name'], pack=content['pack']) existing = Rule.get_by_ref(rule_ref) if existing: rule_db.id = existing.id LOG.debug('Found existing rule: %s with id: %s', rule_ref, existing.id) except StackStormDBObjectNotFoundError: LOG.debug('Rule %s not found. Creating new one.', rule) try: rule_db = Rule.add_or_update(rule_db) increment_trigger_ref_count(rule_api=rule_api) extra = {'rule_db': rule_db} LOG.audit('Rule updated. Rule %s from %s.', rule_db, rule, extra=extra) except Exception: LOG.exception('Failed to create rule %s.', rule_api.name) # If there was an existing rule then the ref count was updated in # to_model so it needs to be adjusted down here. Also, update could # lead to removal of a Trigger so now is a good time for book-keeping. if existing: cleanup_trigger_db_for_rule(existing) except Exception as e: if self._fail_on_failure: msg = ('Failed to register rule "%s" from pack "%s": %s' % (rule, pack, str(e))) raise ValueError(msg) LOG.exception('Failed registering rule from %s.', rule) else: registered_count += 1 return registered_count
def _setup_sample_rules(self): rules = [] # Rules for st2.test.trigger1 RULE_1 = { 'enabled': True, 'name': 'st2.test.rule1', 'trigger': { 'type': 'dummy_pack_1.st2.test.trigger1' }, 'criteria': { 'k1': { # Missing prefix 'trigger'. This rule won't match. 'pattern': 't1_p_v', 'type': 'equals' } }, 'action': { 'ref': 'sixpack.st2.test.action', 'parameters': { 'ip2': '{{rule.k1}}', 'ip1': '{{trigger.t1_p}}' } }, 'id': '23', 'description': '' } rule_api = RuleAPI(**RULE_1) rule_db = RuleAPI.to_model(rule_api) trigger_api = TriggerAPI(**rule_api.trigger) trigger_db = TriggerService.create_trigger_db(trigger_api) trigger_ref = reference.get_str_resource_ref_from_model(trigger_db) rule_db.trigger = trigger_ref rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) RULE_2 = { # Rule should match. 'enabled': True, 'name': 'st2.test.rule2', 'trigger': { 'type': 'dummy_pack_1.st2.test.trigger1' }, 'criteria': { 'trigger.k1': { 'pattern': 't1_p_v', 'type': 'equals' } }, 'action': { 'ref': 'sixpack.st2.test.action', 'parameters': { 'ip2': '{{rule.k1}}', 'ip1': '{{trigger.t1_p}}' } }, 'id': '23', 'description': '' } rule_api = RuleAPI(**RULE_2) rule_db = RuleAPI.to_model(rule_api) rule_db.trigger = trigger_ref rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) RULE_3 = { 'enabled': False, # Disabled rule shouldn't match. 'name': 'st2.test.rule3', 'trigger': { 'type': 'dummy_pack_1.st2.test.trigger1' }, 'criteria': { 'trigger.k1': { 'pattern': 't1_p_v', 'type': 'equals' } }, 'action': { 'ref': 'sixpack.st2.test.action', 'parameters': { 'ip2': '{{rule.k1}}', 'ip1': '{{trigger.t1_p}}' } }, 'id': '23', 'description': '' } rule_api = RuleAPI(**RULE_3) rule_db = RuleAPI.to_model(rule_api) rule_db.trigger = trigger_ref rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) # Rules for st2.test.trigger2 RULE_4 = { 'enabled': True, 'name': 'st2.test.rule4', 'trigger': { 'type': 'dummy_pack_1.st2.test.trigger2' }, 'criteria': { 'trigger.k1': { 'pattern': 't1_p_v', 'type': 'equals' } }, 'action': { 'ref': 'sixpack.st2.test.action', 'parameters': { 'ip2': '{{rule.k1}}', 'ip1': '{{trigger.t1_p}}' } }, 'id': '23', 'description': '' } rule_api = RuleAPI(**RULE_4) rule_db = RuleAPI.to_model(rule_api) trigger_api = TriggerAPI(**rule_api.trigger) trigger_db = TriggerService.create_trigger_db(trigger_api) trigger_ref = reference.get_str_resource_ref_from_model(trigger_db) rule_db.trigger = trigger_ref rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) return rules
def _register_rules_from_pack(self, pack, rules): registered_count = 0 overridden_count = 0 # TODO: Refactor this monstrosity for rule in rules: LOG.debug("Loading rule from %s.", rule) try: content = self._meta_loader.load(rule) 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)) metadata_file = content_utils.get_relative_path_to_pack_file( pack_ref=pack, file_path=rule, use_pack_cache=True) content["metadata_file"] = metadata_file # Pass override information altered = self._override_loader.override( pack, "rules", content) rule_api = RuleAPI(**content) rule_api.validate() rule_db = RuleAPI.to_model(rule_api) # Migration from rule without pack to rule with pack. # There might be a rule with same name but in pack `default` # generated in migration script. In this case, we want to # delete so we don't have duplicates. if pack_field != DEFAULT_PACK_NAME: try: rule_ref = ResourceReference.to_string_reference( name=content["name"], pack=DEFAULT_PACK_NAME) LOG.debug( "Looking for rule %s in pack %s", content["name"], DEFAULT_PACK_NAME, ) existing = Rule.get_by_ref(rule_ref) LOG.debug("Existing = %s", existing) if existing: LOG.debug( "Found rule in pack default: %s; Deleting.", rule_ref) Rule.delete(existing) except: LOG.exception("Exception deleting rule from %s pack.", DEFAULT_PACK_NAME) try: rule_ref = ResourceReference.to_string_reference( name=content["name"], pack=content["pack"]) existing = Rule.get_by_ref(rule_ref) if existing: rule_db.id = existing.id LOG.debug("Found existing rule: %s with id: %s", rule_ref, existing.id) except StackStormDBObjectNotFoundError: LOG.debug("Rule %s not found. Creating new one.", rule) try: rule_db = Rule.add_or_update(rule_db) increment_trigger_ref_count(rule_api=rule_api) extra = {"rule_db": rule_db} LOG.audit("Rule updated. Rule %s from %s.", rule_db, rule, extra=extra) except Exception: LOG.exception("Failed to create rule %s.", rule_api.name) # If there was an existing rule then the ref count was updated in # to_model so it needs to be adjusted down here. Also, update could # lead to removal of a Trigger so now is a good time for book-keeping. if existing: cleanup_trigger_db_for_rule(existing) except Exception as e: if self._fail_on_failure: msg = 'Failed to register rule "%s" from pack "%s": %s' % ( rule, pack, six.text_type(e), ) raise ValueError(msg) LOG.exception("Failed registering rule from %s.", rule) else: registered_count += 1 if altered: overridden_count += 1 return registered_count, overridden_count
def _setup_sample_rules(self): rules = [] # Rules for st2.test.trigger1 RULE_1 = { "enabled": True, "name": "st2.test.rule1", "pack": "sixpack", "trigger": {"type": "dummy_pack_1.st2.test.trigger1"}, "criteria": { "k1": {"pattern": "t1_p_v", "type": "equals"} # Missing prefix 'trigger'. This rule won't match. }, "action": { "ref": "sixpack.st2.test.action", "parameters": {"ip2": "{{rule.k1}}", "ip1": "{{trigger.t1_p}}"}, }, "id": "23", "description": "", } rule_api = RuleAPI(**RULE_1) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) RULE_2 = { # Rule should match. "enabled": True, "name": "st2.test.rule2", "pack": "sixpack", "trigger": {"type": "dummy_pack_1.st2.test.trigger1"}, "criteria": {"trigger.k1": {"pattern": "t1_p_v", "type": "equals"}}, "action": { "ref": "sixpack.st2.test.action", "parameters": {"ip2": "{{rule.k1}}", "ip1": "{{trigger.t1_p}}"}, }, "id": "23", "description": "", } rule_api = RuleAPI(**RULE_2) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) RULE_3 = { "enabled": False, # Disabled rule shouldn't match. "name": "st2.test.rule3", "pack": "sixpack", "trigger": {"type": "dummy_pack_1.st2.test.trigger1"}, "criteria": {"trigger.k1": {"pattern": "t1_p_v", "type": "equals"}}, "action": { "ref": "sixpack.st2.test.action", "parameters": {"ip2": "{{rule.k1}}", "ip1": "{{trigger.t1_p}}"}, }, "id": "23", "description": "", } rule_api = RuleAPI(**RULE_3) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) # Rules for st2.test.trigger2 RULE_4 = { "enabled": True, "name": "st2.test.rule4", "pack": "sixpack", "trigger": {"type": "dummy_pack_1.st2.test.trigger2"}, "criteria": {"trigger.k1": {"pattern": "t1_p_v", "type": "equals"}}, "action": { "ref": "sixpack.st2.test.action", "parameters": {"ip2": "{{rule.k1}}", "ip1": "{{trigger.t1_p}}"}, }, "id": "23", "description": "", } rule_api = RuleAPI(**RULE_4) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) return rules
def _setup_sample_rules(self): rules = [] # Rules for st2.test.trigger1 RULE_1 = { 'enabled': True, 'name': 'st2.test.rule1', 'pack': 'sixpack', 'trigger': { 'type': 'dummy_pack_1.st2.test.trigger1' }, 'criteria': { 'k1': { # Missing prefix 'trigger'. This rule won't match. 'pattern': 't1_p_v', 'type': 'equals' } }, 'action': { 'ref': 'sixpack.st2.test.action', 'parameters': { 'ip2': '{{rule.k1}}', 'ip1': '{{trigger.t1_p}}' } }, 'id': '23', 'description': '' } rule_api = RuleAPI(**RULE_1) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) RULE_2 = { # Rule should match. 'enabled': True, 'name': 'st2.test.rule2', 'pack': 'sixpack', 'trigger': { 'type': 'dummy_pack_1.st2.test.trigger1' }, 'criteria': { 'trigger.k1': { 'pattern': 't1_p_v', 'type': 'equals' } }, 'action': { 'ref': 'sixpack.st2.test.action', 'parameters': { 'ip2': '{{rule.k1}}', 'ip1': '{{trigger.t1_p}}' } }, 'id': '23', 'description': '' } rule_api = RuleAPI(**RULE_2) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) RULE_3 = { 'enabled': False, # Disabled rule shouldn't match. 'name': 'st2.test.rule3', 'pack': 'sixpack', 'trigger': { 'type': 'dummy_pack_1.st2.test.trigger1' }, 'criteria': { 'trigger.k1': { 'pattern': 't1_p_v', 'type': 'equals' } }, 'action': { 'ref': 'sixpack.st2.test.action', 'parameters': { 'ip2': '{{rule.k1}}', 'ip1': '{{trigger.t1_p}}' } }, 'id': '23', 'description': '' } rule_api = RuleAPI(**RULE_3) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) # Rules for st2.test.trigger2 RULE_4 = { 'enabled': True, 'name': 'st2.test.rule4', 'pack': 'sixpack', 'trigger': { 'type': 'dummy_pack_1.st2.test.trigger2' }, 'criteria': { 'trigger.k1': { 'pattern': 't1_p_v', 'type': 'equals' } }, 'action': { 'ref': 'sixpack.st2.test.action', 'parameters': { 'ip2': '{{rule.k1}}', 'ip1': '{{trigger.t1_p}}' } }, 'id': '23', 'description': '' } rule_api = RuleAPI(**RULE_4) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) return rules
def _register_rules_from_pack(self, pack, rules): registered_count = 0 for rule in rules: LOG.debug('Loading rule from %s.', rule) try: content = self._meta_loader.load(rule) 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)) rule_api = RuleAPI(**content) rule_api.validate() rule_db = RuleAPI.to_model(rule_api) # Migration from rule without pack to rule with pack. # There might be a rule with same name but in pack `default` # generated in migration script. In this case, we want to # delete so we don't have duplicates. if pack_field != DEFAULT_PACK_NAME: try: rule_ref = ResourceReference.to_string_reference( name=content['name'], pack=DEFAULT_PACK_NAME) LOG.debug('Looking for rule %s in pack %s', content['name'], DEFAULT_PACK_NAME) existing = Rule.get_by_ref(rule_ref) LOG.debug('Existing = %s', existing) if existing: LOG.debug( 'Found rule in pack default: %s; Deleting.', rule_ref) Rule.delete(existing) except: LOG.exception('Exception deleting rule from %s pack.', DEFAULT_PACK_NAME) try: rule_ref = ResourceReference.to_string_reference( name=content['name'], pack=content['pack']) existing = Rule.get_by_ref(rule_ref) if existing: rule_db.id = existing.id LOG.debug('Found existing rule: %s with id: %s', rule_ref, existing.id) except ValueError: LOG.debug('Rule %s not found. Creating new one.', rule) try: rule_db = Rule.add_or_update(rule_db) extra = {'rule_db': rule_db} LOG.audit('Rule updated. Rule %s from %s.', rule_db, rule, extra=extra) except Exception: LOG.exception('Failed to create rule %s.', rule_api.name) except: LOG.exception('Failed registering rule from %s.', rule) else: registered_count += 1 return registered_count
def _setup_sample_rules(self): rules = [] # Rules for st2.test.trigger1 RULE_1 = { "enabled": True, "name": "st2.test.rule1", "pack": "sixpack", "trigger": {"type": "dummy_pack_1.st2.test.trigger1"}, "criteria": { "k1": { # Missing prefix 'trigger'. This rule won't match. "pattern": "t1_p_v", "type": "equals", } }, "action": { "ref": "sixpack.st2.test.action", "parameters": {"ip2": "{{rule.k1}}", "ip1": "{{trigger.t1_p}}"}, }, "id": "23", "description": "", } rule_api = RuleAPI(**RULE_1) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) RULE_2 = { # Rule should match. "enabled": True, "name": "st2.test.rule2", "pack": "sixpack", "trigger": {"type": "dummy_pack_1.st2.test.trigger1"}, "criteria": {"trigger.k1": {"pattern": "t1_p_v", "type": "equals"}}, "action": { "ref": "sixpack.st2.test.action", "parameters": {"ip2": "{{rule.k1}}", "ip1": "{{trigger.t1_p}}"}, }, "id": "23", "description": "", } rule_api = RuleAPI(**RULE_2) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) RULE_3 = { "enabled": False, # Disabled rule shouldn't match. "name": "st2.test.rule3", "pack": "sixpack", "trigger": {"type": "dummy_pack_1.st2.test.trigger1"}, "criteria": {"trigger.k1": {"pattern": "t1_p_v", "type": "equals"}}, "action": { "ref": "sixpack.st2.test.action", "parameters": {"ip2": "{{rule.k1}}", "ip1": "{{trigger.t1_p}}"}, }, "id": "23", "description": "", } rule_api = RuleAPI(**RULE_3) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) # Rules for st2.test.trigger2 RULE_4 = { "enabled": True, "name": "st2.test.rule4", "pack": "sixpack", "trigger": {"type": "dummy_pack_1.st2.test.trigger2"}, "criteria": {"trigger.k1": {"pattern": "t1_p_v", "type": "equals"}}, "action": { "ref": "sixpack.st2.test.action", "parameters": {"ip2": "{{rule.k1}}", "ip1": "{{trigger.t1_p}}"}, }, "id": "23", "description": "", } rule_api = RuleAPI(**RULE_4) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) return rules
def _get_sample_rules(self): rules = [] RULE_1 = { 'enabled': True, 'name': 'st2.test.rule1', 'trigger': { 'type': 'dummy_pack_1.st2.test.trigger1' }, 'criteria': { 'k1': { # Missing prefix 'trigger'. This rule won't match. 'pattern': 't1_p_v', 'type': 'equals' } }, 'action': { 'ref': 'sixpack.st2.test.action', 'parameters': { 'ip2': '{{rule.k1}}', 'ip1': '{{trigger.t1_p}}' } }, 'id': '23', 'description': '' } rule_api = RuleAPI(**RULE_1) rule_db = RuleAPI.to_model(rule_api) trigger_api = TriggerAPI(**rule_api.trigger) trigger_db = TriggerService.create_trigger_db(trigger_api) trigger_ref = reference.get_str_resource_ref_from_model(trigger_db) rule_db.trigger = trigger_ref rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) RULE_2 = { # Rule should match. 'enabled': True, 'name': 'st2.test.rule2', 'trigger': { 'type': 'dummy_pack_1.st2.test.trigger1' }, 'criteria': { 'trigger.k1': { 'pattern': 't1_p_v', 'type': 'equals' } }, 'action': { 'ref': 'sixpack.st2.test.action', 'parameters': { 'ip2': '{{rule.k1}}', 'ip1': '{{trigger.t1_p}}' } }, 'id': '23', 'description': '' } rule_api = RuleAPI(**RULE_2) rule_db = RuleAPI.to_model(rule_api) rule_db.trigger = trigger_ref rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) RULE_3 = { 'enabled': False, # Disabled rule shouldn't match. 'name': 'st2.test.rule3', 'trigger': { 'type': 'dummy_pack_1.st2.test.trigger1' }, 'criteria': { 'trigger.k1': { 'pattern': 't1_p_v', 'type': 'equals' } }, 'action': { 'ref': 'sixpack.st2.test.action', 'parameters': { 'ip2': '{{rule.k1}}', 'ip1': '{{trigger.t1_p}}' } }, 'id': '23', 'description': '' } rule_api = RuleAPI(**RULE_3) rule_db = RuleAPI.to_model(rule_api) rule_db.trigger = trigger_ref rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) return rules
def _get_sample_rules(self): rules = [] RULE_1 = { 'enabled': True, 'name': 'st2.test.rule1', 'trigger': { 'type': 'dummy_pack_1.st2.test.trigger1' }, 'criteria': { 'k1': { # Missing prefix 'trigger'. This rule won't match. 'pattern': 't1_p_v', 'type': 'equals' } }, 'action': { 'ref': 'sixpack.st2.test.action', 'parameters': { 'ip2': '{{rule.k1}}', 'ip1': '{{trigger.t1_p}}' } }, 'id': '23', 'description': '' } rule_api = RuleAPI(**RULE_1) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) RULE_2 = { # Rule should match. 'enabled': True, 'name': 'st2.test.rule2', 'trigger': { 'type': 'dummy_pack_1.st2.test.trigger1' }, 'criteria': { 'trigger.k1': { 'pattern': 't1_p_v', 'type': 'equals' } }, 'action': { 'ref': 'sixpack.st2.test.action', 'parameters': { 'ip2': '{{rule.k1}}', 'ip1': '{{trigger.t1_p}}' } }, 'id': '23', 'description': '' } rule_api = RuleAPI(**RULE_2) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) RULE_3 = { 'enabled': False, # Disabled rule shouldn't match. 'name': 'st2.test.rule3', 'trigger': { 'type': 'dummy_pack_1.st2.test.trigger1' }, 'criteria': { 'trigger.k1': { 'pattern': 't1_p_v', 'type': 'equals' } }, 'action': { 'ref': 'sixpack.st2.test.action', 'parameters': { 'ip2': '{{rule.k1}}', 'ip1': '{{trigger.t1_p}}' } }, 'id': '23', 'description': '' } rule_api = RuleAPI(**RULE_3) rule_db = RuleAPI.to_model(rule_api) rule_db = Rule.add_or_update(rule_db) rules.append(rule_db) return rules
def test_user_has_resource_api_permission(self): resolver = RulePermissionsResolver() # Admin user, should always return true user_db = self.users['admin'] resource_db = self.resources['rule_1'] resource_api = RuleAPI.from_model(resource_db) self.assertTrue(resolver.user_has_resource_api_permission( user_db=user_db, resource_api=resource_api, permission_type=PermissionType.RULE_CREATE)) # Observer, should return false user_db = self.users['observer'] resource_db = self.resources['rule_1'] resource_api = RuleAPI.from_model(resource_db) self.assertFalse(resolver.user_has_resource_api_permission( user_db=user_db, resource_api=resource_api, permission_type=PermissionType.RULE_CREATE)) # No roles, should return false user_db = self.users['no_roles'] resource_db = self.resources['rule_1'] resource_api = RuleAPI.from_model(resource_db) self.assertFalse(resolver.user_has_resource_api_permission( user_db=user_db, resource_api=resource_api, permission_type=PermissionType.RULE_CREATE)) # Custom role with no permission grants, should return false user_db = self.users['1_custom_role_no_permissions'] resource_db = self.resources['rule_1'] resource_api = RuleAPI.from_model(resource_db) self.assertFalse(resolver.user_has_resource_api_permission( user_db=user_db, resource_api=resource_api, permission_type=PermissionType.RULE_CREATE)) # Custom role with "rule_create" grant on parent pack user_db = self.users['rule_pack_rule_create_grant'] resource_db = self.resources['rule_1'] resource_api = RuleAPI.from_model(resource_db) self.assertTrue(resolver.user_has_resource_api_permission( user_db=user_db, resource_api=resource_api, permission_type=PermissionType.RULE_CREATE)) # Custom role with "rule_all" grant on the parent pack user_db = self.users['rule_pack_rule_all_grant'] resource_db = self.resources['rule_1'] resource_api = RuleAPI.from_model(resource_db) self.assertTrue(resolver.user_has_resource_api_permission( user_db=user_db, resource_api=resource_api, permission_type=PermissionType.RULE_CREATE)) # Custom role with "rule_create" grant directly on the resource user_db = self.users['rule_rule_create_grant'] resource_db = self.resources['rule_1'] resource_api = RuleAPI.from_model(resource_db) self.assertTrue(resolver.user_has_resource_api_permission( user_db=user_db, resource_api=resource_api, permission_type=PermissionType.RULE_CREATE)) # Custom role with "rule_all" grant directly on the resource user_db = self.users['rule_rule_all_grant'] resource_db = self.resources['rule_1'] resource_api = RuleAPI.from_model(resource_db) self.assertTrue(resolver.user_has_resource_api_permission( user_db=user_db, resource_api=resource_api, permission_type=PermissionType.RULE_CREATE))
def _register_rules_from_pack(self, pack, rules): registered_count = 0 # TODO: Refactor this monstrosity for rule in rules: LOG.debug('Loading rule from %s.', rule) try: content = self._meta_loader.load(rule) 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)) rule_api = RuleAPI(**content) rule_api.validate() rule_db = RuleAPI.to_model(rule_api) # Migration from rule without pack to rule with pack. # There might be a rule with same name but in pack `default` # generated in migration script. In this case, we want to # delete so we don't have duplicates. if pack_field != DEFAULT_PACK_NAME: try: rule_ref = ResourceReference.to_string_reference( name=content['name'], pack=DEFAULT_PACK_NAME) LOG.debug('Looking for rule %s in pack %s', content['name'], DEFAULT_PACK_NAME) existing = Rule.get_by_ref(rule_ref) LOG.debug('Existing = %s', existing) if existing: LOG.debug( 'Found rule in pack default: %s; Deleting.', rule_ref) Rule.delete(existing) except: LOG.exception('Exception deleting rule from %s pack.', DEFAULT_PACK_NAME) try: rule_ref = ResourceReference.to_string_reference( name=content['name'], pack=content['pack']) existing = Rule.get_by_ref(rule_ref) if existing: rule_db.id = existing.id LOG.debug('Found existing rule: %s with id: %s', rule_ref, existing.id) except StackStormDBObjectNotFoundError: LOG.debug('Rule %s not found. Creating new one.', rule) try: rule_db = Rule.add_or_update(rule_db) increment_trigger_ref_count(rule_api=rule_api) extra = {'rule_db': rule_db} LOG.audit('Rule updated. Rule %s from %s.', rule_db, rule, extra=extra) except Exception: LOG.exception('Failed to create rule %s.', rule_api.name) # If there was an existing rule then the ref count was updated in # to_model so it needs to be adjusted down here. Also, update could # lead to removal of a Trigger so now is a good time for book-keeping. if existing: cleanup_trigger_db_for_rule(existing) except Exception as e: if self._fail_on_failure: raise e LOG.exception('Failed registering rule from %s.', rule) else: registered_count += 1 return registered_count
def test_user_has_resource_api_permission(self): resolver = RulePermissionsResolver() # Admin user, should always return true user_db = self.users['admin'] resource_db = self.resources['rule_1'] resource_api = RuleAPI.from_model(resource_db) self.assertTrue( resolver.user_has_resource_api_permission( user_db=user_db, resource_api=resource_api, permission_type=PermissionType.RULE_CREATE)) # Observer, should return false user_db = self.users['observer'] resource_db = self.resources['rule_1'] resource_api = RuleAPI.from_model(resource_db) self.assertFalse( resolver.user_has_resource_api_permission( user_db=user_db, resource_api=resource_api, permission_type=PermissionType.RULE_CREATE)) # No roles, should return false user_db = self.users['no_roles'] resource_db = self.resources['rule_1'] resource_api = RuleAPI.from_model(resource_db) self.assertFalse( resolver.user_has_resource_api_permission( user_db=user_db, resource_api=resource_api, permission_type=PermissionType.RULE_CREATE)) # Custom role with no permission grants, should return false user_db = self.users['1_custom_role_no_permissions'] resource_db = self.resources['rule_1'] resource_api = RuleAPI.from_model(resource_db) self.assertFalse( resolver.user_has_resource_api_permission( user_db=user_db, resource_api=resource_api, permission_type=PermissionType.RULE_CREATE)) # Custom role with "rule_create" grant on parent pack user_db = self.users['rule_pack_rule_create_grant'] resource_db = self.resources['rule_1'] resource_api = RuleAPI.from_model(resource_db) self.assertTrue( resolver.user_has_resource_api_permission( user_db=user_db, resource_api=resource_api, permission_type=PermissionType.RULE_CREATE)) # Custom role with "rule_all" grant on the parent pack user_db = self.users['rule_pack_rule_all_grant'] resource_db = self.resources['rule_1'] resource_api = RuleAPI.from_model(resource_db) self.assertTrue( resolver.user_has_resource_api_permission( user_db=user_db, resource_api=resource_api, permission_type=PermissionType.RULE_CREATE)) # Custom role with "rule_create" grant directly on the resource user_db = self.users['rule_rule_create_grant'] resource_db = self.resources['rule_1'] resource_api = RuleAPI.from_model(resource_db) self.assertTrue( resolver.user_has_resource_api_permission( user_db=user_db, resource_api=resource_api, permission_type=PermissionType.RULE_CREATE)) # Custom role with "rule_all" grant directly on the resource user_db = self.users['rule_rule_all_grant'] resource_db = self.resources['rule_1'] resource_api = RuleAPI.from_model(resource_db) self.assertTrue( resolver.user_has_resource_api_permission( user_db=user_db, resource_api=resource_api, permission_type=PermissionType.RULE_CREATE))
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