Browse Source

feat(api): add metrics for RRsetSerializer

Peter Thomassen 2 years ago
parent
commit
25e84c9d89
2 changed files with 25 additions and 7 deletions
  1. 13 2
      api/desecapi/metrics.py
  2. 12 5
      api/desecapi/serializers/records.py

+ 13 - 2
api/desecapi/metrics.py

@@ -15,7 +15,7 @@ def set_histogram(name, *args, **kwargs):
     metrics[name] = Histogram(name, *args, **kwargs)
 
 
-# models.py metrics
+# models metrics
 set_counter(
     "desecapi_captcha_content_created",
     "number of times captcha content created",
@@ -30,7 +30,7 @@ set_histogram(
     buckets=[0, 1, float("inf")],
 )
 
-# views.py metrics
+# views metrics
 set_counter(
     "desecapi_dynDNS12_domain_not_found", "number of times dynDNS12 domain is not found"
 )
@@ -47,6 +47,17 @@ set_counter(
     ["context"],
 )
 
+# serializers metrics
+set_counter(
+    "desecapi_records_serializer_validate_length",
+    "number of attempts to provision an overly long RRset",
+)
+set_counter(
+    "desecapi_records_serializer_validate_blocked_subnet",
+    "number of attempts to provision addresses from a blocked subnet",
+    ["blocked_subnet"],
+)
+
 # exception_handlers.py metrics
 set_counter("desecapi_database_unavailable", "number of times database was unavailable")
 

+ 12 - 5
api/desecapi/serializers/records.py

@@ -4,14 +4,15 @@ import django.core.exceptions
 import dns.name
 import dns.zone
 from django.core.validators import MinValueValidator
-from django.db.models import Q
+from django.db.models import F, Q
 from django.utils import timezone
+from netfields.functions import Masklen
 from rest_framework import serializers
 from rest_framework.settings import api_settings
 from rest_framework.validators import UniqueTogetherValidator
 
 from api import settings
-from desecapi import models
+from desecapi import metrics, models
 from desecapi.validators import ExclusionConstraintValidator, ReadOnlyOnUpdateValidator
 
 
@@ -483,6 +484,7 @@ class RRsetSerializer(ConditionalExistenceModelSerializer):
 
         excess_length = conservative_total_length - 65535  # max response size
         if excess_length > 0:
+            metrics.get("desecapi_records_serializer_validate_length").inc()
             raise serializers.ValidationError(
                 f"Total length of RRset exceeds limit by {excess_length} bytes.",
                 code="max_length",
@@ -492,10 +494,15 @@ class RRsetSerializer(ConditionalExistenceModelSerializer):
     def _validate_blocked_content(self, attrs, type_):
         # Reject IP addresses from blocked IP ranges
         if type_ == "A" and self.domain.is_locally_registrable:
+            qs = models.BlockedSubnet.objects.values_list("subnet", flat=True).order_by(
+                Masklen(F("subnet")).desc()
+            )
             for record in attrs["records"]:
-                if models.BlockedSubnet.objects.filter(
-                    subnet__net_contains=record["content"]
-                ).exists():
+                subnet = qs.filter(subnet__net_contains=record["content"]).first()
+                if subnet:
+                    metrics.get(
+                        "desecapi_records_serializer_validate_blocked_subnet"
+                    ).labels(str(subnet)).inc()
                     raise serializers.ValidationError(
                         f"IP address {record['content']} not allowed."
                     )