def create_trigger_instance(trigger, payload, occurrence_time): """ This creates a trigger instance object given trigger and payload. Trigger can be just a string reference (pack.name) or a ``dict`` containing 'type' and 'parameters'. :param trigger: Dictionary with trigger query filters. :type trigger: ``dict`` :param payload: Trigger payload. :type payload: ``dict`` """ # TODO: This is nasty, this should take a unique reference and not a dict if isinstance(trigger, six.string_types): trigger_db = TriggerService.get_trigger_db_by_ref(trigger) else: type = trigger.get('type', None) parameters = trigger.get('parameters', {}) trigger_db = TriggerService.get_trigger_db_given_type_and_params(type=type, parameters=parameters) if trigger_db is None: LOG.info('No trigger in db for %s', trigger) return None trigger_ref = trigger_db.get_reference().ref trigger_instance = TriggerInstanceDB() trigger_instance.trigger = trigger_ref trigger_instance.payload = payload trigger_instance.occurrence_time = occurrence_time return TriggerInstance.add_or_update(trigger_instance)
def test_rule_enforcement_is_created_on_exception_3(self): # 1. Exception in payload_lookup.get_value rule_enforcement_dbs = list(RuleEnforcement.get_all()) self.assertEqual(rule_enforcement_dbs, []) self._setup_sample_trigger('st2.test.trigger4') rule_4_db = self._setup_sample_rule(RULE_4) rules = [rule_4_db] trigger_instance = container_utils.create_trigger_instance( 'dummy_pack_1.st2.test.trigger4', {'k1': 't2_p_v', 'k2': 'v2'}, date_utils.get_datetime_utc_now() ) trigger = get_trigger_db_by_ref(trigger_instance.trigger) rules_matcher = RulesMatcher(trigger_instance, trigger, rules) matching_rules = rules_matcher.get_matching_rules() self.assertEqual(matching_rules, []) self.assertEqual(len(matching_rules), 0) rule_enforcement_dbs = list(RuleEnforcement.get_all()) self.assertEqual(len(rule_enforcement_dbs), 1) expected_failure = ('Failed to match rule "yoyohoneysingh.st2.test.rule4" against trigger ' 'instance "%s": There might be a problem with the criteria in rule ' 'yoyohoneysingh.st2.test.rule4: exception in equals' % (str(trigger_instance.id))) self.assertEqual(rule_enforcement_dbs[0].failure_reason, expected_failure) self.assertEqual(rule_enforcement_dbs[0].trigger_instance_id, str(trigger_instance.id)) self.assertEqual(rule_enforcement_dbs[0].rule['id'], str(rule_4_db.id)) self.assertEqual(rule_enforcement_dbs[0].status, RULE_ENFORCEMENT_STATUS_FAILED)
def create_trigger_instance(trigger, payload, occurrence_time, raise_on_no_trigger=False): """ This creates a trigger instance object given trigger and payload. Trigger can be just a string reference (pack.name) or a ``dict`` containing 'type' and 'parameters'. :param trigger: Dictionary with trigger query filters. :type trigger: ``dict`` :param payload: Trigger payload. :type payload: ``dict`` """ # TODO: This is nasty, this should take a unique reference and not a dict if isinstance(trigger, six.string_types): trigger_db = TriggerService.get_trigger_db_by_ref(trigger) else: type_ = trigger.get('type', None) parameters = trigger.get('parameters', {}) trigger_db = TriggerService.get_trigger_db_given_type_and_params(type=type_, parameters=parameters) if trigger_db is None: LOG.debug('No trigger in db for %s', trigger) if raise_on_no_trigger: raise StackStormDBObjectNotFoundError('Trigger not found for %s', trigger) return None trigger_ref = trigger_db.get_reference().ref trigger_instance = TriggerInstanceDB() trigger_instance.trigger = trigger_ref trigger_instance.payload = payload trigger_instance.occurrence_time = occurrence_time trigger_instance.status = TRIGGER_INSTANCE_PENDING return TriggerInstance.add_or_update(trigger_instance)
def create_trigger_instance(trigger, payload, occurrence_time, raise_on_no_trigger=False): """ This creates a trigger instance object given trigger and payload. Trigger can be just a string reference (pack.name) or a ``dict`` containing 'id' or 'uid' or type' and 'parameters' keys. :param trigger: Trigger reference or dictionary with trigger query filters. :type trigger: ``str`` or ``dict`` :param payload: Trigger payload. :type payload: ``dict`` """ # TODO: This is nasty, this should take a unique reference and not a dict if isinstance(trigger, six.string_types): trigger_db = TriggerService.get_trigger_db_by_ref(trigger) else: # If id / uid is available we try to look up Trigger by id. This way we can avoid bug in # pymongo / mongoengine related to "parameters" dictionary lookups trigger_id = trigger.get('id', None) trigger_uid = trigger.get('uid', None) # TODO: Remove parameters dictionary look up when we can confirm each trigger dictionary # passed to this method always contains id or uid if trigger_id: LOG.debug('Looking up TriggerDB by id: %s', trigger_id) trigger_db = TriggerService.get_trigger_db_by_id(id=trigger_id) elif trigger_uid: LOG.debug('Looking up TriggerDB by uid: %s', trigger_uid) trigger_db = TriggerService.get_trigger_db_by_uid(uid=trigger_uid) else: # Last resort - look it up by parameters trigger_type = trigger.get('type', None) parameters = trigger.get('parameters', {}) LOG.debug('Looking up TriggerDB by type and parameters: type=%s, parameters=%s', trigger_type, parameters) trigger_db = TriggerService.get_trigger_db_given_type_and_params(type=trigger_type, parameters=parameters) if trigger_db is None: LOG.debug('No trigger in db for %s', trigger) if raise_on_no_trigger: raise StackStormDBObjectNotFoundError('Trigger not found for %s', trigger) return None trigger_ref = trigger_db.get_reference().ref trigger_instance = TriggerInstanceDB() trigger_instance.trigger = trigger_ref trigger_instance.payload = payload trigger_instance.occurrence_time = occurrence_time trigger_instance.status = TRIGGER_INSTANCE_PENDING return TriggerInstance.add_or_update(trigger_instance)
def test_get_matching_rules(self): self._setup_sample_trigger('st2.test.trigger1') trigger_instance = container_utils.create_trigger_instance( 'dummy_pack_1.st2.test.trigger1', {'k1': 't1_p_v', 'k2': 'v2'}, datetime.datetime.utcnow() ) trigger = get_trigger_db_by_ref(trigger_instance.trigger) rules = self._get_sample_rules() rules_matcher = RulesMatcher(trigger_instance, trigger, rules) matching_rules = rules_matcher.get_matching_rules() self.assertTrue(matching_rules is not None) self.assertEqual(len(matching_rules), 1)
def get_matching_rules_for_trigger(self, trigger_instance): trigger = trigger_instance.trigger trigger = get_trigger_db_by_ref(trigger_instance.trigger) rules = Rule.query(trigger=trigger_instance.trigger, enabled=True) LOG.info('Found %d rules defined for trigger %s (type=%s)', len(rules), trigger['name'], trigger['type']) matcher = RulesMatcher(trigger_instance=trigger_instance, trigger=trigger, rules=rules) matching_rules = matcher.get_matching_rules() LOG.info('Matched %s rule(s) for trigger_instance %s (type=%s)', len(matching_rules), trigger['name'], trigger['type']) return matching_rules
def _delete_shadow_trigger(triggertype_db): # shadow Trigger's have the same name as the shadowed TriggerType. triggertype_ref = ResourceReference(name=triggertype_db.name, pack=triggertype_db.pack) trigger_db = TriggerService.get_trigger_db_by_ref(triggertype_ref.ref) if not trigger_db: LOG.warn('No shadow trigger found for %s. Will skip delete.', triggertype_db) return try: Trigger.delete(trigger_db) except Exception: LOG.exception('Database delete encountered exception during delete of id="%s". ', trigger_db.id) extra = {'trigger_db': trigger_db} LOG.audit('Trigger deleted. Trigger.id=%s' % (trigger_db.id), extra=extra)
def test_trigger_instance_payload_with_special_values(self): # Test a rule where TriggerInstance payload contains a dot (".") and $ self._setup_sample_trigger('st2.test.trigger2') trigger_instance = container_utils.create_trigger_instance( 'dummy_pack_1.st2.test.trigger2', {'k1': 't1_p_v', 'k2.k2': 'v2', 'k3.more.nested.deep': 'some.value', 'k4.even.more.nested$': 'foo', 'yep$aaa': 'b'}, date_utils.get_datetime_utc_now() ) trigger = get_trigger_db_by_ref(trigger_instance.trigger) rules = self._get_sample_rules() rules_matcher = RulesMatcher(trigger_instance, trigger, rules) matching_rules = rules_matcher.get_matching_rules() self.assertTrue(matching_rules is not None) self.assertEqual(len(matching_rules), 1)
def test_trigger_instance_payload_with_special_values(self): # Test a rule where TriggerInstance payload contains a dot (".") and $ self._setup_sample_trigger('st2.test.trigger1') self._setup_sample_trigger('st2.test.trigger2') rule_db_1 = self._setup_sample_rule(RULE_1) rule_db_2 = self._setup_sample_rule(RULE_2) rule_db_3 = self._setup_sample_rule(RULE_3) rules = [rule_db_1, rule_db_2, rule_db_3] trigger_instance = container_utils.create_trigger_instance( 'dummy_pack_1.st2.test.trigger2', { 'k1': 't1_p_v', 'k2.k2': 'v2', 'k3.more.nested.deep': 'some.value', 'k4.even.more.nested$': 'foo', 'yep$aaa': 'b' }, date_utils.get_datetime_utc_now()) trigger = get_trigger_db_by_ref(trigger_instance.trigger) rules_matcher = RulesMatcher(trigger_instance, trigger, rules) matching_rules = rules_matcher.get_matching_rules() self.assertIsNotNone(matching_rules) self.assertEqual(len(matching_rules), 1)
def create_trigger_instance(trigger, payload, occurrence_time, raise_on_no_trigger=False): """ This creates a trigger instance object given trigger and payload. Trigger can be just a string reference (pack.name) or a ``dict`` containing 'type' and 'parameters'. :param trigger: Dictionary with trigger query filters. :type trigger: ``dict`` :param payload: Trigger payload. :type payload: ``dict`` """ # TODO: This is nasty, this should take a unique reference and not a dict if isinstance(trigger, six.string_types): trigger_db = TriggerService.get_trigger_db_by_ref(trigger) else: type_ = trigger.get('type', None) parameters = trigger.get('parameters', {}) trigger_db = TriggerService.get_trigger_db_given_type_and_params( type=type_, parameters=parameters) if trigger_db is None: LOG.debug('No trigger in db for %s', trigger) if raise_on_no_trigger: raise StackStormDBObjectNotFoundError('Trigger not found for %s', trigger) return None trigger_ref = trigger_db.get_reference().ref trigger_instance = TriggerInstanceDB() trigger_instance.trigger = trigger_ref trigger_instance.payload = payload trigger_instance.occurrence_time = occurrence_time return TriggerInstance.add_or_update(trigger_instance)
def get_matching_rules_for_trigger(self, trigger_instance): trigger = trigger_instance.trigger trigger_db = get_trigger_db_by_ref(trigger_instance.trigger) if not trigger_db: LOG.error('No matching trigger found in db for trigger instance %s.', trigger_instance) return None rules = get_rules_given_trigger(trigger=trigger) LOG.info('Found %d rules defined for trigger %s', len(rules), trigger_db.get_reference().ref) if len(rules) < 1: return rules matcher = RulesMatcher(trigger_instance=trigger_instance, trigger=trigger_db, rules=rules) matching_rules = matcher.get_matching_rules() LOG.info('Matched %s rule(s) for trigger_instance %s (trigger=%s)', len(matching_rules), trigger_instance['id'], trigger_db.ref) return matching_rules
def validate_trigger_payload(trigger_type_ref, payload, throw_on_inexistent_trigger=False): """ This function validates trigger payload parameters for system and user-defined triggers. :param trigger_type_ref: Reference of a trigger type / trigger / trigger dictionary object. :type trigger_type_ref: ``str`` :param payload: Trigger payload. :type payload: ``dict`` :return: Cleaned payload on success, None if validation is not performed. """ if not trigger_type_ref: return None # NOTE: Due to the awful code in some other places we also need to support a scenario where # this variable is a dictionary and contains various TriggerDB object attributes. if isinstance(trigger_type_ref, dict): if trigger_type_ref.get('type', None): trigger_type_ref = trigger_type_ref['type'] else: trigger_db = triggers.get_trigger_db_by_ref_or_dict( trigger_type_ref) if not trigger_db: # Corresponding TriggerDB not found, likely a corrupted database, skip the # validation. return None trigger_type_ref = trigger_db.type is_system_trigger = trigger_type_ref in SYSTEM_TRIGGER_TYPES if is_system_trigger: # System trigger payload_schema = SYSTEM_TRIGGER_TYPES[trigger_type_ref][ 'payload_schema'] else: # We assume Trigger ref and not TriggerType ref is passed in if second # part (trigger name) is a valid UUID version 4 try: trigger_uuid = uuid.UUID(trigger_type_ref.split('.')[-1]) except ValueError: is_trigger_db = False else: is_trigger_db = (trigger_uuid.version == 4) if is_trigger_db: trigger_db = triggers.get_trigger_db_by_ref(trigger_type_ref) if trigger_db: trigger_type_ref = trigger_db.type trigger_type_db = triggers.get_trigger_type_db(trigger_type_ref) if not trigger_type_db: # Trigger doesn't exist in the database if throw_on_inexistent_trigger: msg = ( 'Trigger type with reference "%s" doesn\'t exist in the database' % (trigger_type_ref)) raise ValueError(msg) return None payload_schema = getattr(trigger_type_db, 'payload_schema', {}) if not payload_schema: # Payload schema not defined for the this trigger return None # We only validate non-system triggers if config option is set (enabled) if not is_system_trigger and not cfg.CONF.system.validate_trigger_payload: LOG.debug( 'Got non-system trigger "%s", but trigger payload validation for non-system' 'triggers is disabled, skipping validation.' % (trigger_type_ref)) return None cleaned = util_schema.validate(instance=payload, schema=payload_schema, cls=util_schema.CustomValidator, use_default=True, allow_default_none=True) return cleaned
def validate_trigger_payload(trigger_type_ref, payload, throw_on_inexistent_trigger=False): """ This function validates trigger payload parameters for system and user-defined triggers. :param trigger_type_ref: Reference of a trigger type / trigger / trigger dictionary object. :type trigger_type_ref: ``str`` :param payload: Trigger payload. :type payload: ``dict`` :return: Cleaned payload on success, None if validation is not performed. """ if not trigger_type_ref: return None # NOTE: Due to the awful code in some other places we also need to support a scenario where # this variable is a dictionary and contains various TriggerDB object attributes. if isinstance(trigger_type_ref, dict): if trigger_type_ref.get('type', None): trigger_type_ref = trigger_type_ref['type'] else: trigger_db = triggers.get_trigger_db_by_ref_or_dict(trigger_type_ref) if not trigger_db: # Corresponding TriggerDB not found, likely a corrupted database, skip the # validation. return None trigger_type_ref = trigger_db.type is_system_trigger = trigger_type_ref in SYSTEM_TRIGGER_TYPES if is_system_trigger: # System trigger payload_schema = SYSTEM_TRIGGER_TYPES[trigger_type_ref]['payload_schema'] else: # We assume Trigger ref and not TriggerType ref is passed in if second # part (trigger name) is a valid UUID version 4 try: trigger_uuid = uuid.UUID(trigger_type_ref.split('.')[-1]) except ValueError: is_trigger_db = False else: is_trigger_db = (trigger_uuid.version == 4) if is_trigger_db: trigger_db = triggers.get_trigger_db_by_ref(trigger_type_ref) if trigger_db: trigger_type_ref = trigger_db.type trigger_type_db = triggers.get_trigger_type_db(trigger_type_ref) if not trigger_type_db: # Trigger doesn't exist in the database if throw_on_inexistent_trigger: msg = ('Trigger type with reference "%s" doesn\'t exist in the database' % (trigger_type_ref)) raise ValueError(msg) return None payload_schema = getattr(trigger_type_db, 'payload_schema', {}) if not payload_schema: # Payload schema not defined for the this trigger return None # We only validate non-system triggers if config option is set (enabled) if not is_system_trigger and not cfg.CONF.system.validate_trigger_payload: LOG.debug('Got non-system trigger "%s", but trigger payload validation for non-system' 'triggers is disabled, skipping validation.' % (trigger_type_ref)) return None cleaned = util_schema.validate(instance=payload, schema=payload_schema, cls=util_schema.CustomValidator, use_default=True, allow_default_none=True) return cleaned