示例#1
0
def delete_alert_rule(alert_rule, user=None):
    """
    Marks an alert rule as deleted and fires off a task to actually delete it.
    :param alert_rule:
    """
    if alert_rule.status == AlertRuleStatus.SNAPSHOT.value:
        raise AlreadyDeletedError()

    with transaction.atomic():
        incidents = Incident.objects.filter(alert_rule=alert_rule)
        bulk_delete_snuba_subscriptions(
            list(alert_rule.snuba_query.subscriptions.all()))
        if incidents:
            alert_rule.update(status=AlertRuleStatus.SNAPSHOT.value)
            AlertRuleActivity.objects.create(
                alert_rule=alert_rule,
                user=user,
                type=AlertRuleActivityType.DELETED.value,
            )
        else:
            alert_rule.delete()

    if alert_rule.id:
        # Change the incident status asynchronously, which could take awhile with many incidents due to snapshot creations.
        tasks.auto_resolve_snapshot_incidents.apply_async(
            kwargs={"alert_rule_id": alert_rule.id})
 def test(self):
     subscription = create_snuba_subscription(
         self.project,
         "something",
         QueryDatasets.EVENTS,
         "level:error",
         QueryAggregations.TOTAL,
         timedelta(minutes=10),
         timedelta(minutes=1),
         [],
     )
     other_subscription = create_snuba_subscription(
         self.create_project(organization=self.organization),
         "something",
         QueryDatasets.EVENTS,
         "level:error",
         QueryAggregations.TOTAL,
         timedelta(minutes=10),
         timedelta(minutes=1),
         [],
     )
     subscription_ids = [subscription.id, other_subscription.id]
     bulk_delete_snuba_subscriptions([subscription, other_subscription])
     assert not QuerySubscription.objects.filter(
         id__in=subscription_ids).exists()
示例#3
0
 def test(self):
     with self.tasks():
         snuba_query = create_snuba_query(
             QueryDatasets.EVENTS,
             "level:error",
             QueryAggregations.TOTAL,
             timedelta(minutes=10),
             timedelta(minutes=1),
             None,
         )
         subscription = create_snuba_subscription(self.project, "something",
                                                  snuba_query)
         snuba_query = create_snuba_query(
             QueryDatasets.EVENTS,
             "level:error",
             QueryAggregations.TOTAL,
             timedelta(minutes=10),
             timedelta(minutes=1),
             None,
         )
         other_subscription = create_snuba_subscription(
             self.create_project(organization=self.organization),
             "something", snuba_query)
     subscription_ids = [subscription.id, other_subscription.id]
     bulk_delete_snuba_subscriptions([subscription, other_subscription])
     assert (QuerySubscription.objects.filter(
         id__in=subscription_ids,
         status=QuerySubscription.Status.DELETING.value,
         subscription_id__isnull=False,
     ).count() == 2)
示例#4
0
文件: logic.py 项目: javadmus/sentry
def delete_alert_rule(alert_rule):
    """
    Marks an alert rule as deleted and fires off a task to actually delete it.
    :param alert_rule:
    """
    if alert_rule.status == AlertRuleStatus.SNAPSHOT.value:
        raise AlreadyDeletedError()

    with transaction.atomic():
        incidents = Incident.objects.filter(alert_rule=alert_rule)
        bulk_delete_snuba_subscriptions(
            list(alert_rule.query_subscriptions.all()))
        if incidents:
            alert_rule.update(status=AlertRuleStatus.SNAPSHOT.value)
            for incident in incidents:
                incident.update(status=IncidentStatus.CLOSED.value)
        else:
            alert_rule.delete()
示例#5
0
文件: logic.py 项目: y1024/sentry
def delete_alert_rule(alert_rule):
    """
    Marks an alert rule as deleted and fires off a task to actually delete it.
    :param alert_rule:
    """
    if alert_rule.status in (
        AlertRuleStatus.PENDING_DELETION.value,
        AlertRuleStatus.DELETION_IN_PROGRESS.value,
    ):
        raise AlreadyDeletedError()

    with transaction.atomic():
        alert_rule.update(
            # Randomize the name here so that we don't get unique constraint issues
            # while waiting for the deletion to process
            name=uuid4().hex,
            status=AlertRuleStatus.PENDING_DELETION.value,
        )
        bulk_delete_snuba_subscriptions(list(alert_rule.query_subscriptions.all()))
    tasks.delete_alert_rule.apply_async(kwargs={"alert_rule_id": alert_rule.id})
示例#6
0
def update_alert_rule(
    alert_rule,
    projects=None,
    name=None,
    query=None,
    aggregation=None,
    time_window=None,
    environment=None,
    threshold_period=None,
    include_all_projects=None,
    excluded_projects=None,
):
    """
    Updates an alert rule.

    :param alert_rule: The alert rule to update
    :param excluded_projects: List of projects to subscribe to the rule. Ignored if
    `include_all_projects` is True
    :param name: Name for the alert rule. This will be used as part of the
    incident name, and must be unique per project.
    :param query: An event search query to subscribe to and monitor for alerts
    :param aggregation: An AlertRuleAggregation that we want to fetch for this alert rule
    :param time_window: Time period to aggregate over, in minutes.
    :param environment: List of environments that this rule applies to
    :param threshold_period: How many update periods the value of the
    subscription needs to exceed the threshold before triggering
    :param include_all_projects: Whether to include all current and future projects
    from this organization
    :param excluded_projects: List of projects to exclude if we're using
    `include_all_projects`. Ignored otherwise.
    :return: The updated `AlertRule`
    """
    if (name and alert_rule.name != name
            and AlertRule.objects.filter(organization=alert_rule.organization,
                                         name=name).exists()):
        raise AlertRuleNameAlreadyUsedError()

    updated_fields = {}
    if name:
        updated_fields["name"] = name
    if query is not None:
        validate_alert_rule_query(query)
        updated_fields["query"] = query
    if aggregation is not None:
        updated_fields["aggregation"] = aggregation.value
    if time_window:
        updated_fields["time_window"] = time_window
    if threshold_period:
        updated_fields["threshold_period"] = threshold_period
    if include_all_projects is not None:
        updated_fields["include_all_projects"] = include_all_projects

    with transaction.atomic():
        incidents = Incident.objects.filter(alert_rule=alert_rule).exists()
        if incidents:
            snapshot_alert_rule(alert_rule)
        alert_rule.update(**updated_fields)

        existing_subs = []
        if (query is not None or aggregation is not None
                or time_window is not None or projects is not None
                or include_all_projects is not None
                or excluded_projects is not None):
            existing_subs = alert_rule.query_subscriptions.all(
            ).select_related("project")

        new_projects = []
        deleted_subs = []

        if not alert_rule.include_all_projects:
            # We don't want to have any exclusion rows present if we're not in
            # `include_all_projects` mode
            get_excluded_projects_for_alert_rule(alert_rule).delete()

        if alert_rule.include_all_projects:
            if include_all_projects or excluded_projects is not None:
                # If we're in `include_all_projects` mode, we want to just fetch
                # projects that aren't already subscribed, and haven't been excluded so
                # we can add them.
                excluded_project_ids = ({p.id
                                         for p in excluded_projects}
                                        if excluded_projects else set())
                project_exclusions = get_excluded_projects_for_alert_rule(
                    alert_rule)
                project_exclusions.exclude(
                    project_id__in=excluded_project_ids).delete()
                existing_excluded_project_ids = {
                    pe.project_id
                    for pe in project_exclusions
                }
                new_exclusions = [
                    AlertRuleExcludedProjects(alert_rule=alert_rule,
                                              project_id=project_id)
                    for project_id in excluded_project_ids
                    if project_id not in existing_excluded_project_ids
                ]
                AlertRuleExcludedProjects.objects.bulk_create(new_exclusions)

                new_projects = Project.objects.filter(
                    organization=alert_rule.organization).exclude(
                        id__in=set([sub.project_id for sub in existing_subs])
                        | excluded_project_ids)
                # If we're subscribed to any of the excluded projects then we want to
                # remove those subscriptions
                deleted_subs = [
                    sub for sub in existing_subs
                    if sub.project_id in excluded_project_ids
                ]
        elif projects is not None:
            existing_project_slugs = {
                sub.project.slug
                for sub in existing_subs
            }
            # Determine whether we've added any new projects as part of this update
            new_projects = [
                project for project in projects
                if project.slug not in existing_project_slugs
            ]
            updated_project_slugs = {project.slug for project in projects}
            # Find any subscriptions that were removed as part of this update
            deleted_subs = [
                sub for sub in existing_subs
                if sub.project.slug not in updated_project_slugs
            ]

        if new_projects:
            subscribe_projects_to_alert_rule(alert_rule, new_projects)

        if deleted_subs:
            bulk_delete_snuba_subscriptions(deleted_subs)
            # Remove any deleted subscriptions from `existing_subscriptions`, so that
            # if we need to update any subscriptions we don't end up doing it twice. We
            # don't add new subscriptions here since they'll already have the updated
            # values
            existing_subs = [sub for sub in existing_subs if sub.id]

        if environment:
            # Delete rows we don't have present in the updated data.
            AlertRuleEnvironment.objects.filter(alert_rule=alert_rule).exclude(
                environment__in=environment).delete()
            for e in environment:
                AlertRuleEnvironment.objects.get_or_create(
                    alert_rule=alert_rule, environment=e)
        else:
            AlertRuleEnvironment.objects.filter(alert_rule=alert_rule).delete()

        if existing_subs and (query is not None or aggregation is not None
                              or time_window is not None):
            # If updating any subscription details, update related Snuba subscriptions
            # too
            bulk_update_snuba_subscriptions(
                existing_subs,
                alert_rule.query,
                QueryAggregations(alert_rule.aggregation),
                timedelta(minutes=alert_rule.time_window),
                timedelta(minutes=DEFAULT_ALERT_RULE_RESOLUTION),
                list(alert_rule.environment.all()),
            )

    return alert_rule
示例#7
0
def update_alert_rule(
    alert_rule,
    projects=None,
    name=None,
    query=None,
    aggregate=None,
    time_window=None,
    environment=None,
    threshold_period=None,
    include_all_projects=None,
    excluded_projects=None,
):
    """
    Updates an alert rule.

    :param alert_rule: The alert rule to update
    :param excluded_projects: List of projects to subscribe to the rule. Ignored if
    `include_all_projects` is True
    :param name: Name for the alert rule. This will be used as part of the
    incident name, and must be unique per project.
    :param query: An event search query to subscribe to and monitor for alerts
    :param aggregate: A string representing the aggregate used in this alert rule
    :param time_window: Time period to aggregate over, in minutes.
    :param environment: An optional environment that this rule applies to
    :param threshold_period: How many update periods the value of the
    subscription needs to exceed the threshold before triggering
    :param include_all_projects: Whether to include all current and future projects
    from this organization
    :param excluded_projects: List of projects to exclude if we're using
    `include_all_projects`. Ignored otherwise.
    :return: The updated `AlertRule`
    """
    if (
        name
        and alert_rule.name != name
        and AlertRule.objects.filter(organization=alert_rule.organization, name=name).exists()
    ):
        raise AlertRuleNameAlreadyUsedError()

    updated_fields = {}
    updated_query_fields = {}
    if name:
        updated_fields["name"] = name
    if query is not None:
        validate_alert_rule_query(query)
        updated_query_fields["query"] = query
    if aggregate is not None:
        updated_query_fields["aggregate"] = aggregate
    if time_window:
        updated_query_fields["time_window"] = timedelta(minutes=time_window)
    if threshold_period:
        updated_fields["threshold_period"] = threshold_period
    if include_all_projects is not None:
        updated_fields["include_all_projects"] = include_all_projects

    with transaction.atomic():
        incidents = Incident.objects.filter(alert_rule=alert_rule).exists()
        if incidents:
            snapshot_alert_rule(alert_rule)
        alert_rule.update(**updated_fields)

        if updated_query_fields or environment != alert_rule.snuba_query.environment:
            snuba_query = alert_rule.snuba_query
            updated_query_fields.setdefault("query", snuba_query.query)
            # XXX: We use the alert rule aggregation here since currently we're
            # expecting the enum value to be passed.
            updated_query_fields.setdefault("aggregate", snuba_query.aggregate)
            updated_query_fields.setdefault(
                "time_window", timedelta(seconds=snuba_query.time_window)
            )
            update_snuba_query(
                alert_rule.snuba_query,
                resolution=timedelta(minutes=DEFAULT_ALERT_RULE_RESOLUTION),
                environment=environment,
                **updated_query_fields
            )

        existing_subs = []
        if (
            query is not None
            or aggregate is not None
            or time_window is not None
            or projects is not None
            or include_all_projects is not None
            or excluded_projects is not None
        ):
            existing_subs = alert_rule.snuba_query.subscriptions.all().select_related("project")

        new_projects = []
        deleted_subs = []

        if not alert_rule.include_all_projects:
            # We don't want to have any exclusion rows present if we're not in
            # `include_all_projects` mode
            get_excluded_projects_for_alert_rule(alert_rule).delete()

        if alert_rule.include_all_projects:
            if include_all_projects or excluded_projects is not None:
                # If we're in `include_all_projects` mode, we want to just fetch
                # projects that aren't already subscribed, and haven't been excluded so
                # we can add them.
                excluded_project_ids = (
                    {p.id for p in excluded_projects} if excluded_projects else set()
                )
                project_exclusions = get_excluded_projects_for_alert_rule(alert_rule)
                project_exclusions.exclude(project_id__in=excluded_project_ids).delete()
                existing_excluded_project_ids = {pe.project_id for pe in project_exclusions}
                new_exclusions = [
                    AlertRuleExcludedProjects(alert_rule=alert_rule, project_id=project_id)
                    for project_id in excluded_project_ids
                    if project_id not in existing_excluded_project_ids
                ]
                AlertRuleExcludedProjects.objects.bulk_create(new_exclusions)

                new_projects = Project.objects.filter(organization=alert_rule.organization).exclude(
                    id__in=set([sub.project_id for sub in existing_subs]) | excluded_project_ids
                )
                # If we're subscribed to any of the excluded projects then we want to
                # remove those subscriptions
                deleted_subs = [
                    sub for sub in existing_subs if sub.project_id in excluded_project_ids
                ]
        elif projects is not None:
            existing_project_slugs = {sub.project.slug for sub in existing_subs}
            # Determine whether we've added any new projects as part of this update
            new_projects = [
                project for project in projects if project.slug not in existing_project_slugs
            ]
            updated_project_slugs = {project.slug for project in projects}
            # Find any subscriptions that were removed as part of this update
            deleted_subs = [
                sub for sub in existing_subs if sub.project.slug not in updated_project_slugs
            ]

        if new_projects:
            subscribe_projects_to_alert_rule(alert_rule, new_projects)

        if deleted_subs:
            bulk_delete_snuba_subscriptions(deleted_subs)

    return alert_rule
示例#8
0
def update_alert_rule(
    alert_rule,
    projects=None,
    name=None,
    threshold_type=None,
    query=None,
    aggregation=None,
    time_window=None,
    alert_threshold=None,
    resolve_threshold=None,
    threshold_period=None,
):
    """
    Updates an alert rule.

    :param alert_rule: The alert rule to update
    :param name: Name for the alert rule. This will be used as part of the
    incident name, and must be unique per project.
    :param threshold_type: An AlertRuleThresholdType
    :param query: An event search query to subscribe to and monitor for alerts
    :param aggregation: An AlertRuleAggregation that we want to fetch for this alert rule
    :param time_window: Time period to aggregate over, in minutes.
    :param alert_threshold: Value that the subscription needs to reach to
    trigger the alert
    :param resolve_threshold: Value that the subscription needs to reach to
    resolve the alert
    :param threshold_period: How many update periods the value of the
    subscription needs to exceed the threshold before triggering
    :return: The updated `AlertRule`
    """
    if (name and alert_rule.name != name
            and AlertRule.objects.filter(organization=alert_rule.organization,
                                         name=name).exists()):
        raise AlertRuleNameAlreadyUsedError()

    updated_fields = {}
    if name:
        updated_fields["name"] = name
    if threshold_type:
        updated_fields["threshold_type"] = threshold_type.value
    if query is not None:
        validate_alert_rule_query(query)
        updated_fields["query"] = query
    if aggregation is not None:
        updated_fields["aggregation"] = aggregation.value
    if time_window:
        updated_fields["time_window"] = time_window
    if alert_threshold is not None:
        updated_fields["alert_threshold"] = alert_threshold
    if resolve_threshold is not None:
        updated_fields["resolve_threshold"] = resolve_threshold
    if threshold_period:
        updated_fields["threshold_period"] = threshold_period

    with transaction.atomic():
        alert_rule.update(**updated_fields)
        existing_subs = []
        if (query is not None or aggregation is not None
                or time_window is not None or projects is not None):
            existing_subs = alert_rule.query_subscriptions.all(
            ).select_related("project")

        if projects is not None:
            existing_project_slugs = {
                sub.project.slug
                for sub in existing_subs
            }
            # Determine whether we've added any new projects as part of this update
            new_projects = [
                project for project in projects
                if project.slug not in existing_project_slugs
            ]
            updated_project_slugs = {project.slug for project in projects}
            # Find any subscriptions that were removed as part of this update
            deleted_subs = [
                sub for sub in existing_subs
                if sub.project.slug not in updated_project_slugs
            ]
            if new_projects:
                new_subscriptions = bulk_create_snuba_subscriptions(
                    new_projects,
                    tasks.INCIDENTS_SNUBA_SUBSCRIPTION_TYPE,
                    QueryDatasets(alert_rule.dataset),
                    alert_rule.query,
                    QueryAggregations(alert_rule.aggregation),
                    alert_rule.time_window,
                    DEFAULT_ALERT_RULE_RESOLUTION,
                )
                subscription_links = [
                    AlertRuleQuerySubscription(query_subscription=subscription,
                                               alert_rule=alert_rule)
                    for subscription in new_subscriptions
                ]
                AlertRuleQuerySubscription.objects.bulk_create(
                    subscription_links)

            if deleted_subs:
                bulk_delete_snuba_subscriptions(deleted_subs)

            # Remove any deleted subscriptions from `existing_subscriptions`, so that
            # if we need to update any subscriptions we don't end up doing it twice. We
            # don't add new subscriptions here since they'll already have the updated
            # values
            existing_subs = [sub for sub in existing_subs if sub.id]

        if existing_subs and (query is not None or aggregation is not None
                              or time_window is not None):
            # If updating any subscription details, update related Snuba subscriptions
            # too
            bulk_update_snuba_subscriptions(
                existing_subs,
                alert_rule.query,
                QueryAggregations(alert_rule.aggregation),
                alert_rule.time_window,
                DEFAULT_ALERT_RULE_RESOLUTION,
            )

    return alert_rule