Example #1
0
class Rule(models.Model):
    objects = ObjectFilterManager()

    name = models.CharField(max_length=128,
                            unique=True,
                            validators=[validators.alphanumeric])
    clause = models.TextField(help_text='Prometheus query')
    duration = models.CharField(
        max_length=128,
        validators=[validators.duration],
        help_text="Duration field with postfix. Example 30s, 5m, 1d")
    enabled = models.BooleanField(default=True)
    parent = models.ForeignKey('Rule',
                               null=True,
                               related_name='overrides',
                               on_delete=models.SET_NULL)

    content_type = models.ForeignKey(
        ContentType,
        on_delete=models.CASCADE,
        limit_choices_to=(models.Q(app_label='sites', model='site')
                          | models.Q(app_label='promgen', model='project')
                          | models.Q(app_label='promgen', model='service')))
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')
    description = models.TextField(blank=True)

    class Meta:
        ordering = ['content_type', 'object_id', 'name']

    @cached_property
    def labels(self):
        return {obj.name: obj.value for obj in self.rulelabel_set.all()}

    def add_label(self, name, value):
        return RuleLabel.objects.get_or_create(rule=self,
                                               name=name,
                                               value=value)

    def add_annotation(self, name, value):
        return RuleAnnotation.objects.get_or_create(rule=self,
                                                    name=name,
                                                    value=value)

    @cached_property
    def annotations(self):
        _annotations = {
            obj.name: obj.value
            for obj in self.ruleannotation_set.all()
        }
        # Skip when pk is not set, such as when test rendering a rule
        if self.pk and 'rule' not in _annotations:
            _annotations['rule'] = resolve_domain('rule-edit', pk=self.pk)
        return _annotations

    def __str__(self):
        return '{} [{}]'.format(self.name, self.content_object.name)

    def get_absolute_url(self):
        return reverse('rule-edit', kwargs={'pk': self.pk})

    def set_object(self, content_type, object_id):
        self.content_type = ContentType.objects.get(model=content_type,
                                                    app_label='promgen')
        self.object_id = object_id

    def copy_to(self, content_type, object_id):
        '''
        Make a copy under a new service

        It's important that we set pk to None so a new object is created, but we
        also need to ensure the new name is unique by appending some unique data
        to the end of the name
        '''
        with transaction.atomic():
            content_type = ContentType.objects.get(model=content_type,
                                                   app_label='promgen')

            # First check to see if this rule is already overwritten
            for rule in Rule.objects.filter(parent_id=self.pk,
                                            content_type=content_type,
                                            object_id=object_id):
                return rule

            content_object = content_type.get_object_for_this_type(
                pk=object_id)

            orig_pk = self.pk
            self.pk = None
            self.parent_id = orig_pk
            self.name = '{}_{}'.format(self.name,
                                       slugify(content_object.name)).replace(
                                           '-', '_')
            self.content_type = content_type
            self.object_id = object_id
            self.enabled = False
            self.clause = self.clause.replace(
                macro.EXCLUSION_MACRO,
                '{}="{}",{}'.format(content_type.model, content_object.name,
                                    macro.EXCLUSION_MACRO))
            self.save()

            # Add a label to our new rule by default, to help ensure notifications
            # get routed to the notifier we expect
            self.add_label(content_type.model, content_object.name)

            for label in RuleLabel.objects.filter(rule_id=orig_pk):
                # Skip service labels from our previous rule
                if label.name in ['service', 'project']:
                    logger.debug('Skipping %s: %s', label.name, label.value)
                    continue
                logger.debug('Copying %s to %s', label, self)
                label.pk = None
                label.rule = self
                label.save()

            for annotation in RuleAnnotation.objects.filter(rule_id=orig_pk):
                logger.debug('Copying %s to %s', annotation, self)
                annotation.pk = None
                annotation.rule = self
                annotation.save()

        return self
Example #2
0
class Sender(models.Model):
    objects = ObjectFilterManager()

    sender = models.CharField(max_length=128)
    value = models.CharField(max_length=128)
    alias = models.CharField(max_length=128, blank=True)

    content_type = models.ForeignKey(
        ContentType,
        on_delete=models.CASCADE,
        limit_choices_to=(models.Q(app_label='auth', model='user')
                          | models.Q(app_label='promgen', model='project')
                          | models.Q(app_label='promgen', model='service')))
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    owner = models.ForeignKey(settings.AUTH_USER_MODEL,
                              on_delete=models.CASCADE,
                              null=True)

    def show_value(self):
        if self.alias:
            return self.alias
        return self.value

    show_value.short_description = 'Value'

    def __str__(self):
        return '{}:{}'.format(self.sender, self.show_value())

    @classmethod
    def driver_set(cls):
        '''Return the list of drivers for Sender model'''
        for entry in plugins.notifications():
            try:
                yield entry.module_name, entry.load()
            except ImportError:
                logger.warning('Error importing %s', entry.module_name)

    __driver = {}

    @property
    def driver(self):
        '''Return configured driver for Sender model instance'''
        if self.sender in self.__driver:
            return self.__driver[self.sender]

        for entry in plugins.notifications():
            try:
                self.__driver[entry.module_name] = entry.load()()
            except ImportError:
                logger.warning('Error importing %s', entry.module_name)
        return self.__driver[self.sender]

    def test(self):
        '''
        Test sender plugin

        Uses the same test json from our unittests but subs in the currently
        tested object as part of the test data
        '''
        data = tests.PromgenTest.data_json('examples', 'alertmanager.json')
        if hasattr(self.content_object, 'name'):
            data['commonLabels'][
                self.content_type.name] = self.content_object.name
            for alert in data.get('alerts', []):
                alert['labels'][
                    self.content_type.name] = self.content_object.name

        from promgen import tasks
        tasks.send_alert(self.sender, self.value, data)

    def filtered(self, alert):
        """
        Check filters for a specific sender

        If no filters are defined, then we let the message through
        If filters are defined, then we check to see if at least one filter matches
        If no filters match, then we assume it's filtered out
        """
        logger.debug("Checking labels %s", alert["commonLabels"])
        # If we have no specific whitelist, then we let everything through
        if self.filter_set.count() == 0:
            return False

        # If we have filters defined, then we need to check to see if our
        # filters match
        for f in self.filter_set.all():
            logger.debug("Checking filter %s %s", f.name, f.value)
            if alert["commonLabels"].get(f.name) == f.value:
                return False
        # If none of our filters match, then we blacklist this sender
        return True
Example #3
0
class Sender(DynamicParent):
    sender = models.CharField(max_length=128)
    value = models.CharField(max_length=128)
    alias = models.CharField(max_length=128, blank=True)

    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, limit_choices_to=(
        models.Q(app_label='auth', model='user') |
        models.Q(app_label='promgen', model='project') | models.Q(app_label='promgen', model='service'))
    )
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True)

    def show_value(self):
        if self.alias:
            return self.alias
        return self.value

    show_value.short_description = 'Value'

    def __str__(self):
        return '{}:{}'.format(self.sender, self.show_value())

    @classmethod
    def driver_set(cls):
        '''Return the list of drivers for Sender model'''
        for entry in plugins.notifications():
            try:
                yield entry.module_name, entry.load()
            except ImportError:
                logger.warning('Error importing %s', entry.module_name)

    __driver = {}
    @property
    def driver(self):
        '''Return configured driver for Sender model instance'''
        if self.sender in self.__driver:
            return self.__driver[self.sender]

        for entry in plugins.notifications():
            try:
                self.__driver[entry.module_name] = entry.load()()
            except ImportError:
                logger.warning('Error importing %s', entry.module_name)
        return self.__driver[self.sender]

    def test(self):
        '''
        Test sender plugin

        Uses the same test json from our unittests but subs in the currently
        tested object as part of the test data
        '''
        data = tests.PromgenTest.data_json('examples', 'alertmanager.json')
        if hasattr(self.content_object, 'name'):
            data['commonLabels'][self.content_type.name] = self.content_object.name
            for alert in data.get('alerts', []):
                alert['labels'][self.content_type.name] = self.content_object.name

        from promgen import tasks
        tasks.send_alert(self.sender, self.value, data)