diff --git a/engine/apps/alerts/integration_options_mixin.py b/engine/apps/alerts/integration_options_mixin.py index a0a81bab..b5b00a41 100644 --- a/engine/apps/alerts/integration_options_mixin.py +++ b/engine/apps/alerts/integration_options_mixin.py @@ -69,6 +69,7 @@ class IntegrationOptionsMixin: "grouping_id", "resolve_condition", "acknowledge_condition", + "group_verbose_name", "source_link", ] diff --git a/engine/apps/alerts/migrations/0007_populate_verbose_name.py b/engine/apps/alerts/migrations/0007_populate_verbose_name.py deleted file mode 100644 index 89d6fc44..00000000 --- a/engine/apps/alerts/migrations/0007_populate_verbose_name.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.15 on 2022-09-01 16:54 - -from django.db import migrations - -from apps.alerts.models import AlertReceiveChannel -from apps.alerts.tasks import update_verbose_name_for_alert_receive_channel - - -def populate_verbose_name(apps, _): - pks = AlertReceiveChannel.objects_with_deleted.values_list("pk", flat=True) - for pk in pks: - update_verbose_name_for_alert_receive_channel.delay(pk) - - -class Migration(migrations.Migration): - - dependencies = [ - ('alerts', '0006_alertgroup_alerts_aler_channel_ee84a7_idx'), - ] - - operations = [ - migrations.RunPython(populate_verbose_name, migrations.RunPython.noop), - ] diff --git a/engine/apps/alerts/models/alert.py b/engine/apps/alerts/models/alert.py index a41d3f12..8f5b272a 100644 --- a/engine/apps/alerts/models/alert.py +++ b/engine/apps/alerts/models/alert.py @@ -179,19 +179,19 @@ class Alert(models.Model): is_resolve_signal = False is_acknowledge_signal = False group_distinction = None + group_verbose_name = "Incident" acknowledge_condition_template = template_manager.get_attr_template( "acknowledge_condition", alert_receive_channel ) resolve_condition_template = template_manager.get_attr_template("resolve_condition", alert_receive_channel) grouping_id_template = template_manager.get_attr_template("grouping_id", alert_receive_channel) - - # set verbose_name to web title to allow alert group searching based on verbose_name - web_title_template = template_manager.get_attr_template("title", alert_receive_channel, render_for="web") - if web_title_template: - group_verbose_name = apply_jinja_template(web_title_template, raw_request_data)[0] or None - else: - group_verbose_name = None + # use get_default_attr_template because there is no ability to customize group_verbose_name, only default value + group_verbose_name_template = template_manager.get_default_attr_template( + "group_verbose_name", alert_receive_channel + ) + if group_verbose_name_template is not None: + group_verbose_name, _ = apply_jinja_template(group_verbose_name_template, raw_request_data) if grouping_id_template is not None: group_distinction, _ = apply_jinja_template(grouping_id_template, raw_request_data) diff --git a/engine/apps/alerts/models/alert_group.py b/engine/apps/alerts/models/alert_group.py index 1f60ce91..84a0a9aa 100644 --- a/engine/apps/alerts/models/alert_group.py +++ b/engine/apps/alerts/models/alert_group.py @@ -899,7 +899,7 @@ class AlertGroup(AlertGroupSlackRenderingMixin, EscalationSnapshotMixin, models. self.resolve(resolved_by=AlertGroup.WIPED) self.stop_escalation() self.distinction = "" - self.verbose_name = None + self.verbose_name = "Wiped incident" self.wiped_at = timezone.now() self.wiped_by = user for alert in self.alerts.all(): diff --git a/engine/apps/alerts/tasks/__init__.py b/engine/apps/alerts/tasks/__init__.py index 48a30b7a..bf1ad097 100644 --- a/engine/apps/alerts/tasks/__init__.py +++ b/engine/apps/alerts/tasks/__init__.py @@ -1,5 +1,4 @@ from .acknowledge_reminder import acknowledge_reminder_task # noqa: F401 -from .alert_group_verbose_name import update_verbose_name, update_verbose_name_for_alert_receive_channel # noqa:F401 from .calculcate_escalation_finish_time import calculate_escalation_finish_time # noqa from .call_ack_url import call_ack_url # noqa: F401 from .check_escalation_finished import check_escalation_finished_task # noqa: F401 diff --git a/engine/apps/alerts/tasks/alert_group_verbose_name.py b/engine/apps/alerts/tasks/alert_group_verbose_name.py deleted file mode 100644 index 202df5bc..00000000 --- a/engine/apps/alerts/tasks/alert_group_verbose_name.py +++ /dev/null @@ -1,72 +0,0 @@ -from django.db.models import Min - -from apps.alerts.incident_appearance.templaters import TemplateLoader -from apps.alerts.tasks.task_logger import task_logger -from common.custom_celery_tasks import shared_dedicated_queue_retry_task -from common.jinja_templater import apply_jinja_template - -# BATCH_SIZE is how many alert groups will be processed per second (for every individual alert receive channel) -BATCH_SIZE = 1000 - - -def batch_ids(queryset, cursor): - return list(queryset.filter(id__gt=cursor).order_by("id").values_list("id", flat=True)[:BATCH_SIZE]) - - -@shared_dedicated_queue_retry_task -def update_verbose_name_for_alert_receive_channel(alert_receive_channel_pk): - from apps.alerts.models import AlertGroup - - countdown = 0 - cursor = 0 - queryset = AlertGroup.all_objects.filter(channel_id=alert_receive_channel_pk) - ids = batch_ids(queryset, cursor) - - while ids: - update_verbose_name.apply_async((alert_receive_channel_pk, ids[0], ids[-1]), countdown=countdown) - - cursor = ids[-1] - ids = batch_ids(queryset, cursor) - countdown += 1 - - -@shared_dedicated_queue_retry_task -def update_verbose_name(alert_receive_channel_pk, alert_group_pk_start, alert_group_pk_end): - from apps.alerts.models import Alert, AlertGroup, AlertReceiveChannel - - try: - alert_receive_channel = AlertReceiveChannel.objects_with_deleted.get(pk=alert_receive_channel_pk) - except AlertReceiveChannel.DoesNotExist: - task_logger.warning(f"AlertReceiveChannel {alert_receive_channel_pk} doesn't exist") - return - - alert_groups = AlertGroup.all_objects.filter(pk__gte=alert_group_pk_start, pk__lte=alert_group_pk_end).only("pk") - - # get first alerts in 2 SQL queries - alerts_info = ( - Alert.objects.values("group_id") - .filter(group_id__gte=alert_group_pk_start, group_id__lte=alert_group_pk_end) - .annotate(first_alert_id=Min("id")) - ) - alerts_info_map = {info["group_id"]: info for info in alerts_info} - - first_alert_ids = [info["first_alert_id"] for info in alerts_info_map.values()] - first_alerts = Alert.objects.filter(pk__in=first_alert_ids).values("group_id", "raw_request_data") - first_alert_map = {alert["group_id"]: alert for alert in first_alerts} - - template_manager = TemplateLoader() - web_title_template = template_manager.get_attr_template("title", alert_receive_channel, render_for="web") - - for alert_group in alert_groups: - if web_title_template: - if alert_group.pk in first_alert_map: - raw_request_data = first_alert_map[alert_group.pk]["raw_request_data"] - verbose_name = apply_jinja_template(web_title_template, raw_request_data)[0] or None - else: - verbose_name = None - else: - verbose_name = None - - alert_group.verbose_name = verbose_name - - AlertGroup.all_objects.bulk_update(alert_groups, ["verbose_name"]) diff --git a/engine/apps/alerts/tests/test_default_templates.py b/engine/apps/alerts/tests/test_default_templates.py index 259aa051..63cfd0b8 100644 --- a/engine/apps/alerts/tests/test_default_templates.py +++ b/engine/apps/alerts/tests/test_default_templates.py @@ -92,6 +92,7 @@ def test_render_group_data_templates( assert group_data.group_distinction == template_module.tests.get("group_distinction") assert group_data.is_resolve_signal == template_module.tests.get("is_resolve_signal") assert group_data.is_acknowledge_signal == template_module.tests.get("is_acknowledge_signal") + assert group_data.group_verbose_name == template_module.tests.get("group_verbose_name") def test_default_templates_are_valid(): diff --git a/engine/apps/api/serializers/alert_group.py b/engine/apps/api/serializers/alert_group.py index f9ecf443..df5583c4 100644 --- a/engine/apps/api/serializers/alert_group.py +++ b/engine/apps/api/serializers/alert_group.py @@ -61,6 +61,7 @@ class AlertGroupListSerializer(EagerLoadingMixin, serializers.ModelSerializer): "pk", "alerts_count", "inside_organization_number", + "verbose_name", "alert_receive_channel", "resolved", "resolved_by", diff --git a/engine/apps/api/views/alert_group.py b/engine/apps/api/views/alert_group.py index 0d44e150..9fd72296 100644 --- a/engine/apps/api/views/alert_group.py +++ b/engine/apps/api/views/alert_group.py @@ -192,7 +192,7 @@ class AlertGroupView( filter_backends = [SearchFilter, filters.DjangoFilterBackend] # todo: add ability to search by templated title - search_fields = ["public_primary_key", "inside_organization_number", "verbose_name"] + search_fields = ["public_primary_key", "inside_organization_number"] filterset_class = AlertGroupFilter diff --git a/engine/apps/api/views/alert_receive_channel_template.py b/engine/apps/api/views/alert_receive_channel_template.py index b1fac13b..ff8cd923 100644 --- a/engine/apps/api/views/alert_receive_channel_template.py +++ b/engine/apps/api/views/alert_receive_channel_template.py @@ -2,7 +2,6 @@ from rest_framework import mixins, viewsets from rest_framework.permissions import IsAuthenticated from apps.alerts.models import AlertReceiveChannel -from apps.alerts.tasks import update_verbose_name_for_alert_receive_channel from apps.api.permissions import MODIFY_ACTIONS, READ_ACTIONS, ActionPermission, AnyRole, IsAdmin from apps.api.serializers.alert_receive_channel import AlertReceiveChannelTemplatesSerializer from apps.auth_token.auth import PluginAuthentication @@ -37,14 +36,9 @@ class AlertReceiveChannelTemplateView( def update(self, request, *args, **kwargs): instance = self.get_object() prev_state = instance.insight_logs_serialized - prev_web_title_template = instance.web_title_template - result = super().update(request, *args, **kwargs) - instance = self.get_object() new_state = instance.insight_logs_serialized - new_web_title_template = instance.web_title_template - write_resource_insight_log( instance=instance, author=self.request.user, @@ -52,8 +46,4 @@ class AlertReceiveChannelTemplateView( prev_state=prev_state, new_state=new_state, ) - - if new_web_title_template != prev_web_title_template: - update_verbose_name_for_alert_receive_channel.delay(instance.pk) - return result diff --git a/engine/apps/public_api/views/integrations.py b/engine/apps/public_api/views/integrations.py index 5c5df6f3..36ef6ea3 100644 --- a/engine/apps/public_api/views/integrations.py +++ b/engine/apps/public_api/views/integrations.py @@ -5,7 +5,6 @@ from rest_framework.permissions import IsAuthenticated from rest_framework.viewsets import ModelViewSet from apps.alerts.models import AlertReceiveChannel -from apps.alerts.tasks import update_verbose_name_for_alert_receive_channel from apps.auth_token.auth import ApiTokenAuthentication from apps.public_api.serializers import IntegrationSerializer, IntegrationUpdateSerializer from apps.public_api.throttlers.user_throttle import UserThrottle @@ -59,27 +58,17 @@ class IntegrationView( raise NotFound def perform_update(self, serializer): - instance = serializer.instance - - prev_state = instance.insight_logs_serialized - prev_web_title_template = instance.web_title_template - + prev_state = serializer.instance.insight_logs_serialized serializer.save() - - new_state = instance.insight_logs_serialized - new_web_title_template = instance.web_title_template - + new_state = serializer.instance.insight_logs_serialized write_resource_insight_log( - instance=instance, + instance=serializer.instance, author=self.request.user, event=EntityEvent.UPDATED, prev_state=prev_state, new_state=new_state, ) - if new_web_title_template != prev_web_title_template: - update_verbose_name_for_alert_receive_channel.delay(instance.pk) - def perform_destroy(self, instance): write_resource_insight_log(instance=instance, author=self.request.user, event=EntityEvent.DELETED) instance.delete() diff --git a/engine/config_integrations/alertmanager.py b/engine/config_integrations/alertmanager.py index bfdcff2e..cb2fa9b6 100644 --- a/engine/config_integrations/alertmanager.py +++ b/engine/config_integrations/alertmanager.py @@ -116,6 +116,8 @@ resolve_condition = """\ acknowledge_condition = None +group_verbose_name = "Incident" + tests = { "payload": { "endsAt": "0001-01-01T00:00:00Z", diff --git a/engine/config_integrations/elastalert.py b/engine/config_integrations/elastalert.py index 73320d53..90e9bfcc 100644 --- a/engine/config_integrations/elastalert.py +++ b/engine/config_integrations/elastalert.py @@ -61,4 +61,6 @@ resolve_condition = """\ acknowledge_condition = None +group_verbose_name = "Incident" + example_payload = {"message": "This alert was sent by user for the demonstration purposes"} diff --git a/engine/config_integrations/formatted_webhook.py b/engine/config_integrations/formatted_webhook.py index 6847639f..6f712a23 100644 --- a/engine/config_integrations/formatted_webhook.py +++ b/engine/config_integrations/formatted_webhook.py @@ -50,6 +50,8 @@ resolve_condition = '{{ payload.get("state", "").upper() == "OK" }}' acknowledge_condition = None +group_verbose_name = web_title + example_payload = { "alert_uid": "08d6891a-835c-e661-39fa-96b6a9e26552", "title": "TestAlert: The whole system is down", diff --git a/engine/config_integrations/grafana.py b/engine/config_integrations/grafana.py index 4feefd61..383390c4 100644 --- a/engine/config_integrations/grafana.py +++ b/engine/config_integrations/grafana.py @@ -143,6 +143,10 @@ resolve_condition = """\ acknowledge_condition = None +group_verbose_name = """\ +{{ payload.get("ruleName", "Incident") }} +""" + tests = { "payload": { "endsAt": "0001-01-01T00:00:00Z", @@ -253,6 +257,7 @@ tests = { "group_distinction": "c6bf5494a2d3052459b4dac837e41455", "is_resolve_signal": False, "is_acknowledge_signal": False, + "group_verbose_name": "Incident", } # Miscellaneous diff --git a/engine/config_integrations/grafana_alerting.py b/engine/config_integrations/grafana_alerting.py index e8942b1e..ae07e12e 100644 --- a/engine/config_integrations/grafana_alerting.py +++ b/engine/config_integrations/grafana_alerting.py @@ -120,6 +120,8 @@ resolve_condition = """\ acknowledge_condition = None +group_verbose_name = "Incident" + tests = { "payload": { "endsAt": "0001-01-01T00:00:00Z", diff --git a/engine/config_integrations/heartbeat.py b/engine/config_integrations/heartbeat.py index e339b56f..f051a44c 100644 --- a/engine/config_integrations/heartbeat.py +++ b/engine/config_integrations/heartbeat.py @@ -26,4 +26,6 @@ resolve_condition = '{{ payload.get("is_resolve", False) == True }}' acknowledge_condition = None +group_verbose_name = '{{ payload.get("title", "Title") }}' + example_payload = {"foo": "bar"} diff --git a/engine/config_integrations/inbound_email.py b/engine/config_integrations/inbound_email.py index 4ecac8e4..b934e35a 100644 --- a/engine/config_integrations/inbound_email.py +++ b/engine/config_integrations/inbound_email.py @@ -49,3 +49,5 @@ grouping_id = '{{ payload.get("title", "")}}' resolve_condition = '{{ payload.get("state", "").upper() == "OK" }}' acknowledge_condition = None + +group_verbose_name = web_title diff --git a/engine/config_integrations/kapacitor.py b/engine/config_integrations/kapacitor.py index 3d761766..d5f013fe 100644 --- a/engine/config_integrations/kapacitor.py +++ b/engine/config_integrations/kapacitor.py @@ -56,6 +56,8 @@ resolve_condition = '{{ payload.get("level", "").startswith("OK") }}' acknowledge_condition = None +group_verbose_name = '{{ payload.get("id", "") }}' + example_payload = { "id": "TestAlert", "message": "This alert was sent by user for the demonstration purposes", diff --git a/engine/config_integrations/maintenance.py b/engine/config_integrations/maintenance.py index d27405ef..957e53e9 100644 --- a/engine/config_integrations/maintenance.py +++ b/engine/config_integrations/maintenance.py @@ -49,3 +49,5 @@ grouping_id = None resolve_condition = None acknowledge_condition = None + +group_verbose_name = "Incident" diff --git a/engine/config_integrations/manual.py b/engine/config_integrations/manual.py index fdcaadaa..43f4852b 100644 --- a/engine/config_integrations/manual.py +++ b/engine/config_integrations/manual.py @@ -58,3 +58,5 @@ grouping_id = """{{ payload }}""" resolve_condition = None acknowledge_condition = None + +group_verbose_name = web_title diff --git a/engine/config_integrations/slack_channel.py b/engine/config_integrations/slack_channel.py index cd8ef14f..d01c186b 100644 --- a/engine/config_integrations/slack_channel.py +++ b/engine/config_integrations/slack_channel.py @@ -39,4 +39,6 @@ resolve_condition = None acknowledge_condition = None +group_verbose_name = '<#{{ payload.get("channel", "") }}>' + source_link = '{{ payload.get("amixr_mixin", {}).get("permalink", "")}}' diff --git a/engine/config_integrations/webhook.py b/engine/config_integrations/webhook.py index 4a3b0b73..113efc56 100644 --- a/engine/config_integrations/webhook.py +++ b/engine/config_integrations/webhook.py @@ -60,4 +60,6 @@ resolve_condition = """\ {%- endif %}""" acknowledge_condition = None +group_verbose_name = web_title + example_payload = {"message": "This alert was sent by user for the demonstration purposes"} diff --git a/engine/settings/prod_without_db.py b/engine/settings/prod_without_db.py index fe99bed1..88261cbb 100644 --- a/engine/settings/prod_without_db.py +++ b/engine/settings/prod_without_db.py @@ -139,8 +139,6 @@ CELERY_TASK_ROUTES = { "apps.schedules.tasks.drop_cached_ical.drop_cached_ical_for_custom_events_for_organization": {"queue": "critical"}, "apps.schedules.tasks.drop_cached_ical.drop_cached_ical_task": {"queue": "critical"}, # LONG - "apps.alerts.tasks.alert_group_verbose_name.update_verbose_name_for_alert_receive_channel": {"queue": "long"}, - "apps.alerts.tasks.alert_group_verbose_name.update_verbose_name": {"queue": "long"}, "apps.alerts.tasks.check_escalation_finished.check_escalation_finished_task": {"queue": "long"}, "apps.grafana_plugin.tasks.sync.start_sync_organizations": {"queue": "long"}, "apps.grafana_plugin.tasks.sync.sync_organization_async": {"queue": "long"}, diff --git a/grafana-plugin/src/models/alertgroup/alertgroup.types.ts b/grafana-plugin/src/models/alertgroup/alertgroup.types.ts index d704c34e..8f5e231b 100644 --- a/grafana-plugin/src/models/alertgroup/alertgroup.types.ts +++ b/grafana-plugin/src/models/alertgroup/alertgroup.types.ts @@ -72,6 +72,7 @@ export interface Alert { silenced_until: string; started_at: string; last_alert_at: string; + verbose_name: string; dependent_alert_groups: Alert[]; status: IncidentStatus; short?: boolean;