Skip to content

Commit

Permalink
Provide information about CNAME/target relationship in the record det…
Browse files Browse the repository at this point in the history
…ail view (#217)
  • Loading branch information
peteeckel authored Mar 22, 2024
1 parent 3b1db57 commit f8bd51e
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 24 deletions.
10 changes: 10 additions & 0 deletions netbox_dns/models/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
28 changes: 6 additions & 22 deletions netbox_dns/templates/netbox_dns/record.html
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,6 @@ <h5 class="card-header">Record</h5>
<td><a href="{% url 'ipam:ipaddress' pk=object.ipam_ip_address.pk %}">{{ object.ipam_ip_address }}</td>
</tr>
{% endif %}
{% if object.rfc2317_cname_record %}
<tr>
<th scope="row">RFC2317 CNAME Record</th>
<td><a href="{% url 'plugins:netbox_dns:record' pk=object.rfc2317_cname_record.pk %}">{{ object.rfc2317_cname_record }}</td>
</tr>
{% endif %}
<tr>
<th scope="row">Status</th>
<td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
Expand All @@ -131,23 +125,13 @@ <h5 class="card-header">Record</h5>
</table>
</div>
</div>
{% if object.rfc2317_ptr_records.all|length %}
{% if cname_target_table and cname_target_table.rows|length > 0 %}
<div class="card">
<h5 class="card-header">RFC2317 Targets</h5>
<div class="card-body">
<table class="table table-hover attr-table">
<tr>
<th>Address Record</th>
<th>PTR Record</th>
</tr>
{% for record in object.rfc2317_ptr_records.all %}
<tr>
<td><a href="{% url 'plugins:netbox_dns:record' pk=record.address_record.pk %}">{{ record.address_record }}</td>
<td><a href="{% url 'plugins:netbox_dns:record' pk=record.pk %}">{{ record }}</td>
</td>
{% endfor %}
</table>
</div>
{% include 'inc/panel_table.html' with table=cname_target_table heading='CNAME Targets' %}
</div>
{% elif cname_table and cname_table.rows|length > 0 %}
<div class="card">
{% include 'inc/panel_table.html' with table=cname_table heading='CNAMEs' %}
</div>
{% endif %}
{% if not object.managed %}
Expand Down
78 changes: 76 additions & 2 deletions netbox_dns/views/record.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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


Expand All @@ -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 = {}

Expand All @@ -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


Expand Down

0 comments on commit f8bd51e

Please sign in to comment.