Co-authored-by: Eve832 <eve.meelan@grafana.com>
Co-authored-by: Francisco Montes de Oca <nevermind89x@gmail.com>
Co-authored-by: Ildar Iskhakov <ildar.iskhakov@grafana.com>
Co-authored-by: Innokentii Konstantinov <innokenty.konstantinov@grafana.com>
Co-authored-by: Julia <ferril.darkdiver@gmail.com>
Co-authored-by: maskin25 <kengurek@gmail.com>
Co-authored-by: Matias Bordese <mbordese@gmail.com>
Co-authored-by: Matvey Kukuy <motakuk@gmail.com>
Co-authored-by: Michael Derynck <michael.derynck@grafana.com>
Co-authored-by: Richard Hartmann <richih@richih.org>
Co-authored-by: Robby Milo <robbymilo@fastmail.com>
Co-authored-by: Timur Olzhabayev <timur.olzhabayev@grafana.com>
Co-authored-by: Vadim Stepanov <vadimkerr@gmail.com>
Co-authored-by: Yulia Shanyrova <yulia.shanyrova@grafana.com>
195 lines
7.1 KiB
Python
195 lines
7.1 KiB
Python
import logging
|
|
from datetime import datetime
|
|
|
|
import humanize
|
|
from rest_framework import serializers
|
|
|
|
from apps.alerts.incident_appearance.renderers.web_renderer import AlertGroupWebRenderer
|
|
from apps.alerts.models import AlertGroup
|
|
from common.api_helpers.mixins import EagerLoadingMixin
|
|
|
|
from .alert import AlertSerializer
|
|
from .alert_receive_channel import FastAlertReceiveChannelSerializer
|
|
from .user import FastUserSerializer
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ShortAlertGroupSerializer(serializers.ModelSerializer):
|
|
pk = serializers.CharField(read_only=True, source="public_primary_key")
|
|
alert_receive_channel = FastAlertReceiveChannelSerializer(source="channel")
|
|
render_for_web = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = AlertGroup
|
|
fields = ["pk", "render_for_web", "alert_receive_channel", "inside_organization_number"]
|
|
|
|
def get_render_for_web(self, obj):
|
|
return AlertGroupWebRenderer(obj).render()
|
|
|
|
|
|
class AlertGroupSerializer(EagerLoadingMixin, serializers.ModelSerializer):
|
|
"""
|
|
Attention: It's heavily cached. Make sure to invalidate alertgroup's web cache if you update the format!
|
|
"""
|
|
|
|
pk = serializers.CharField(read_only=True, source="public_primary_key")
|
|
alert_receive_channel = FastAlertReceiveChannelSerializer(source="channel")
|
|
alerts = serializers.SerializerMethodField("get_limited_alerts")
|
|
resolved_by_verbose = serializers.CharField(source="get_resolved_by_display")
|
|
resolved_by_user = FastUserSerializer(required=False)
|
|
acknowledged_by_user = FastUserSerializer(required=False)
|
|
silenced_by_user = FastUserSerializer(required=False)
|
|
related_users = serializers.SerializerMethodField()
|
|
|
|
last_alert_at = serializers.SerializerMethodField()
|
|
|
|
started_at_verbose = serializers.SerializerMethodField()
|
|
acknowledged_at_verbose = serializers.SerializerMethodField()
|
|
resolved_at_verbose = serializers.SerializerMethodField()
|
|
silenced_at_verbose = serializers.SerializerMethodField()
|
|
|
|
dependent_alert_groups = ShortAlertGroupSerializer(many=True)
|
|
root_alert_group = ShortAlertGroupSerializer()
|
|
|
|
alerts_count = serializers.ReadOnlyField()
|
|
|
|
status = serializers.ReadOnlyField()
|
|
render_for_web = serializers.SerializerMethodField()
|
|
|
|
PREFETCH_RELATED = [
|
|
"alerts",
|
|
"dependent_alert_groups",
|
|
"log_records",
|
|
"log_records__author",
|
|
"log_records__escalation_policy",
|
|
"log_records__invitation__invitee",
|
|
]
|
|
|
|
SELECT_RELATED = [
|
|
"slack_message",
|
|
"channel__organization",
|
|
"slack_message___slack_team_identity",
|
|
"acknowledged_by_user",
|
|
"resolved_by_user",
|
|
"silenced_by_user",
|
|
]
|
|
|
|
class Meta:
|
|
model = AlertGroup
|
|
fields = [
|
|
"pk",
|
|
"alerts_count",
|
|
"inside_organization_number",
|
|
"verbose_name",
|
|
"alert_receive_channel",
|
|
"resolved",
|
|
"resolved_by",
|
|
"resolved_by_verbose",
|
|
"resolved_by_user",
|
|
"resolved_at",
|
|
"acknowledged_at",
|
|
"acknowledged",
|
|
"acknowledged_on_source",
|
|
"acknowledged_at",
|
|
"acknowledged_by_user",
|
|
"silenced",
|
|
"silenced_by_user",
|
|
"silenced_at",
|
|
"silenced_at_verbose",
|
|
"silenced_until",
|
|
"started_at",
|
|
"last_alert_at",
|
|
"silenced_until",
|
|
"permalink",
|
|
"alerts",
|
|
"related_users",
|
|
"started_at_verbose",
|
|
"acknowledged_at_verbose",
|
|
"resolved_at_verbose",
|
|
"render_for_web",
|
|
"render_after_resolve_report_json",
|
|
"dependent_alert_groups",
|
|
"root_alert_group",
|
|
"status",
|
|
]
|
|
|
|
def get_last_alert_at(self, obj):
|
|
last_alert = obj.alerts.last()
|
|
# TODO: This is a Hotfix for 0.0.27
|
|
if last_alert is None:
|
|
logger.warning(f"obj {obj} doesn't have last_alert!")
|
|
return ""
|
|
return str(last_alert.created_at)
|
|
|
|
def get_limited_alerts(self, obj):
|
|
"""
|
|
Overriding default alerts because there are alert_groups with thousands of them.
|
|
It's just too slow, we need to cut here.
|
|
"""
|
|
alerts = obj.alerts.all()[:100]
|
|
|
|
if len(alerts) > 90:
|
|
for alert in alerts:
|
|
alert.title = str(alert.title) + " Only last 100 alerts are shown. Use Amixr API to fetch all of them."
|
|
|
|
return AlertSerializer(alerts, many=True).data
|
|
|
|
def get_related_users(self, obj):
|
|
users_ids = set()
|
|
users = []
|
|
|
|
# add resolved and acknowledged by_user explicitly because logs are already prefetched
|
|
# when def acknowledge/resolve are called in view.
|
|
if obj.resolved_by_user:
|
|
users_ids.add(obj.resolved_by_user.public_primary_key)
|
|
users.append(obj.resolved_by_user.short())
|
|
|
|
if obj.acknowledged_by_user and obj.acknowledged_by_user.public_primary_key not in users_ids:
|
|
users_ids.add(obj.acknowledged_by_user.public_primary_key)
|
|
users.append(obj.acknowledged_by_user.short())
|
|
|
|
if obj.silenced_by_user and obj.silenced_by_user.public_primary_key not in users_ids:
|
|
users_ids.add(obj.silenced_by_user.public_primary_key)
|
|
users.append(obj.silenced_by_user.short())
|
|
|
|
for log_record in obj.log_records.all():
|
|
if log_record.author is not None and log_record.author.public_primary_key not in users_ids:
|
|
users.append(log_record.author.short())
|
|
users_ids.add(log_record.author.public_primary_key)
|
|
return users
|
|
|
|
def get_started_at_verbose(self, obj):
|
|
started_at_verbose = None
|
|
if obj.started_at is not None:
|
|
started_at_verbose = humanize.naturaltime(
|
|
datetime.now().replace(tzinfo=None) - obj.started_at.replace(tzinfo=None)
|
|
)
|
|
return started_at_verbose
|
|
|
|
def get_acknowledged_at_verbose(self, obj):
|
|
acknowledged_at_verbose = None
|
|
if obj.acknowledged_at is not None:
|
|
acknowledged_at_verbose = humanize.naturaltime(
|
|
datetime.now().replace(tzinfo=None) - obj.acknowledged_at.replace(tzinfo=None)
|
|
) # TODO: Deal with timezones
|
|
return acknowledged_at_verbose
|
|
|
|
def get_resolved_at_verbose(self, obj):
|
|
resolved_at_verbose = None
|
|
if obj.resolved_at is not None:
|
|
resolved_at_verbose = humanize.naturaltime(
|
|
datetime.now().replace(tzinfo=None) - obj.resolved_at.replace(tzinfo=None)
|
|
) # TODO: Deal with timezones
|
|
return resolved_at_verbose
|
|
|
|
def get_silenced_at_verbose(self, obj):
|
|
silenced_at_verbose = None
|
|
if obj.silenced_at is not None:
|
|
silenced_at_verbose = humanize.naturaltime(
|
|
datetime.now().replace(tzinfo=None) - obj.silenced_at.replace(tzinfo=None)
|
|
) # TODO: Deal with timezones
|
|
return silenced_at_verbose
|
|
|
|
def get_render_for_web(self, obj):
|
|
return AlertGroupWebRenderer(obj).render()
|