From f8bd51e95fa2f0ff4b6d46312fb79fdb44713d78 Mon Sep 17 00:00:00 2001 From: Peter Eckel <6815386+peteeckel@users.noreply.github.com> Date: Fri, 22 Mar 2024 13:10:58 +0100 Subject: [PATCH] Provide information about CNAME/target relationship in the record detail view (#217) --- netbox_dns/models/record.py | 10 +++ netbox_dns/templates/netbox_dns/record.html | 28 ++------ netbox_dns/views/record.py | 78 ++++++++++++++++++++- 3 files changed, 92 insertions(+), 24 deletions(-) diff --git a/netbox_dns/models/record.py b/netbox_dns/models/record.py index 279ed613..749cb477 100644 --- a/netbox_dns/models/record.py +++ b/netbox_dns/models/record.py @@ -241,6 +241,16 @@ def fqdn(self): return name.to_text() + @property + def value_fqdn(self): + if self.type != RecordTypeChoices.CNAME: + return None + + zone = dns_name.from_text(self.zone.name) + value_fqdn = dns_name.from_text(self.value, origin=zone) + + return value_fqdn.to_text() + @property def address_from_name(self): prefix = arpa_to_prefix(self.fqdn) diff --git a/netbox_dns/templates/netbox_dns/record.html b/netbox_dns/templates/netbox_dns/record.html index f6f6201d..efca0fa9 100644 --- a/netbox_dns/templates/netbox_dns/record.html +++ b/netbox_dns/templates/netbox_dns/record.html @@ -112,12 +112,6 @@
Record
{{ object.ipam_ip_address }} {% endif %} - {% if object.rfc2317_cname_record %} - - RFC2317 CNAME Record - {{ object.rfc2317_cname_record }} - - {% endif %} Status {% badge object.get_status_display bg_color=object.get_status_color %} @@ -131,23 +125,13 @@
Record
- {% if object.rfc2317_ptr_records.all|length %} + {% if cname_target_table and cname_target_table.rows|length > 0 %}
-
RFC2317 Targets
-
- - - - - - {% for record in object.rfc2317_ptr_records.all %} - - - - - {% endfor %} -
Address RecordPTR Record
{{ record.address_record }}{{ record }}
-
+ {% include 'inc/panel_table.html' with table=cname_target_table heading='CNAME Targets' %} +
+ {% elif cname_table and cname_table.rows|length > 0 %} +
+ {% include 'inc/panel_table.html' with table=cname_table heading='CNAMEs' %}
{% endif %} {% if not object.managed %} diff --git a/netbox_dns/views/record.py b/netbox_dns/views/record.py index 8fccb98e..2f155999 100644 --- a/netbox_dns/views/record.py +++ b/netbox_dns/views/record.py @@ -1,5 +1,8 @@ from dns import name as dns_name +from django.db.models import Q +from django.db.models.functions import Length + from netbox.views import generic from netbox_dns.filters import RecordFilter @@ -9,8 +12,8 @@ RecordForm, RecordBulkEditForm, ) -from netbox_dns.models import Record -from netbox_dns.tables import RecordTable, ManagedRecordTable +from netbox_dns.models import Record, RecordTypeChoices, Zone +from netbox_dns.tables import RecordTable, ManagedRecordTable, RelatedRecordTable from netbox_dns.utilities import value_to_unicode @@ -37,6 +40,72 @@ class ManagedRecordListView(generic.ObjectListView): class RecordView(generic.ObjectView): queryset = Record.objects.all().prefetch_related("zone", "ptr_record") + def get_value_records(self, instance): + value_fqdn = dns_name.from_text(instance.value_fqdn) + value_zone_names = [ + value_fqdn.split(length)[1].to_text().rstrip(".") + for length in range(2, len(value_fqdn)) + ] + + value_zone = ( + Zone.objects.filter(instance.zone.view_filter, name__in=value_zone_names) + .order_by(Length("name").desc()) + .first() + ) + if not value_zone: + return None + + value_name = value_fqdn.relativize(dns_name.from_text(value_zone.name)) + cname_targets = Record.objects.filter(zone=value_zone, name=value_name) + + if cname_targets: + return RelatedRecordTable( + data=cname_targets, + ) + + return None + + def get_cname_records(self, instance): + view_filter = ( + Q(zone__view__isnull=True) + if instance.zone.view is None + else Q(zone__view=instance.zone.view) + ) + cname_records = set( + Record.objects.filter( + view_filter, value=instance.fqdn, type=RecordTypeChoices.CNAME + ) + ) + + fqdn = dns_name.from_text(instance.fqdn) + parent_zone_names = [ + fqdn.split(length)[1].to_text().rstrip(".") + for length in range(1, len(fqdn)) + ] + + parent_zones = Zone.objects.filter( + instance.zone.view_filter, name__in=parent_zone_names + ) + + for parent_zone in parent_zones: + parent_cname_records = Record.objects.filter( + view_filter, type=RecordTypeChoices.CNAME, zone=parent_zone + ) + cname_records = cname_records.union( + set( + record + for record in parent_cname_records + if record.value_fqdn == instance.fqdn + ) + ) + + if cname_records: + return RelatedRecordTable( + data=cname_records, + ) + + return None + def get_extra_context(self, request, instance): context = {} @@ -48,6 +117,11 @@ def get_extra_context(self, request, instance): if instance.value != unicode_value: context["unicode_value"] = unicode_value + if instance.type == RecordTypeChoices.CNAME: + context["cname_target_table"] = self.get_value_records(instance) + else: + context["cname_table"] = self.get_cname_records(instance) + return context