netbox-plugin-dns 1.1.4__py3-none-any.whl → 1.1.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of netbox-plugin-dns might be problematic. Click here for more details.
- netbox_dns/__init__.py +18 -4
- netbox_dns/api/views.py +1 -1
- netbox_dns/filtersets/record.py +0 -1
- netbox_dns/filtersets/zone.py +1 -2
- netbox_dns/forms/record.py +11 -7
- netbox_dns/forms/view.py +2 -3
- netbox_dns/forms/zone.py +8 -9
- netbox_dns/forms/zone_template.py +5 -5
- netbox_dns/locale/de/LC_MESSAGES/django.mo +0 -0
- netbox_dns/models/nameserver.py +4 -8
- netbox_dns/models/record.py +18 -34
- netbox_dns/models/record_template.py +4 -4
- netbox_dns/models/view.py +2 -3
- netbox_dns/models/zone.py +78 -32
- netbox_dns/tables/record.py +14 -2
- netbox_dns/tables/zone.py +1 -2
- netbox_dns/template_content.py +16 -0
- netbox_dns/templates/netbox_dns/record.html +12 -0
- netbox_dns/templates/netbox_dns/view.html +1 -1
- netbox_dns/templates/netbox_dns/zone/delegation_record.html +18 -0
- netbox_dns/templates/netbox_dns/zone.html +1 -1
- netbox_dns/utilities/__init__.py +1 -0
- netbox_dns/utilities/dns.py +12 -0
- netbox_dns/utilities/ipam_dnssync.py +10 -13
- netbox_dns/views/nameserver.py +3 -3
- netbox_dns/views/record.py +44 -11
- netbox_dns/views/registrar.py +1 -1
- netbox_dns/views/registration_contact.py +1 -1
- netbox_dns/views/view.py +2 -2
- netbox_dns/views/zone.py +49 -20
- {netbox_plugin_dns-1.1.4.dist-info → netbox_plugin_dns-1.1.5.dist-info}/METADATA +2 -1
- {netbox_plugin_dns-1.1.4.dist-info → netbox_plugin_dns-1.1.5.dist-info}/RECORD +35 -33
- {netbox_plugin_dns-1.1.4.dist-info → netbox_plugin_dns-1.1.5.dist-info}/WHEEL +1 -1
- {netbox_plugin_dns-1.1.4.dist-info → netbox_plugin_dns-1.1.5.dist-info}/LICENSE +0 -0
- {netbox_plugin_dns-1.1.4.dist-info → netbox_plugin_dns-1.1.5.dist-info}/top_level.txt +0 -0
netbox_dns/models/zone.py
CHANGED
|
@@ -12,12 +12,12 @@ from django.core.validators import (
|
|
|
12
12
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
|
13
13
|
from django.db import models, transaction
|
|
14
14
|
from django.db.models import Q, Max, ExpressionWrapper, BooleanField
|
|
15
|
-
from django.
|
|
15
|
+
from django.db.models.functions import Length
|
|
16
16
|
from django.db.models.signals import m2m_changed
|
|
17
|
+
from django.urls import reverse
|
|
17
18
|
from django.dispatch import receiver
|
|
18
19
|
from django.conf import settings
|
|
19
20
|
from django.utils.translation import gettext_lazy as _
|
|
20
|
-
from django.utils.translation import pgettext_lazy as _p
|
|
21
21
|
|
|
22
22
|
from netbox.models import NetBoxModel
|
|
23
23
|
from netbox.models.features import ContactsMixin
|
|
@@ -35,6 +35,7 @@ from netbox_dns.utilities import (
|
|
|
35
35
|
arpa_to_prefix,
|
|
36
36
|
name_to_unicode,
|
|
37
37
|
normalize_name,
|
|
38
|
+
get_parent_zone_names,
|
|
38
39
|
NameFormatError,
|
|
39
40
|
)
|
|
40
41
|
from netbox_dns.validators import (
|
|
@@ -84,7 +85,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
84
85
|
self._ip_addresses_checked = False
|
|
85
86
|
|
|
86
87
|
view = models.ForeignKey(
|
|
87
|
-
verbose_name=
|
|
88
|
+
verbose_name=_("View"),
|
|
88
89
|
to="View",
|
|
89
90
|
on_delete=models.PROTECT,
|
|
90
91
|
null=False,
|
|
@@ -298,6 +299,10 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
298
299
|
|
|
299
300
|
return str(name)
|
|
300
301
|
|
|
302
|
+
@property
|
|
303
|
+
def fqdn(self):
|
|
304
|
+
return f"{self.name}."
|
|
305
|
+
|
|
301
306
|
@staticmethod
|
|
302
307
|
def get_defaults():
|
|
303
308
|
default_fields = (
|
|
@@ -363,10 +368,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
363
368
|
return None
|
|
364
369
|
|
|
365
370
|
return (
|
|
366
|
-
|
|
367
|
-
view=self.view,
|
|
368
|
-
arpa_network__net_contains=self.rfc2317_prefix,
|
|
369
|
-
)
|
|
371
|
+
self.view.zone_set.filter(arpa_network__net_contains=self.rfc2317_prefix)
|
|
370
372
|
.order_by("arpa_network__net_mask_length")
|
|
371
373
|
.last()
|
|
372
374
|
)
|
|
@@ -387,25 +389,73 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
387
389
|
|
|
388
390
|
@property
|
|
389
391
|
def child_zones(self):
|
|
390
|
-
return
|
|
391
|
-
name__iregex=rf"^[^.]+\.{re.escape(self.name)}$"
|
|
392
|
+
return self.view.zone_set.filter(
|
|
393
|
+
name__iregex=rf"^[^.]+\.{re.escape(self.name)}$"
|
|
392
394
|
)
|
|
393
395
|
|
|
396
|
+
@property
|
|
397
|
+
def descendant_zones(self):
|
|
398
|
+
return self.view.zone_set.filter(name__endswith=f".{self.name}")
|
|
399
|
+
|
|
394
400
|
@property
|
|
395
401
|
def parent_zone(self):
|
|
396
|
-
parent_name = (
|
|
397
|
-
dns_name.from_text(self.name).parent().relativize(dns_name.root).to_text()
|
|
398
|
-
)
|
|
399
402
|
try:
|
|
400
|
-
return
|
|
401
|
-
except Zone.DoesNotExist:
|
|
403
|
+
return self.view.zone_set.get(name=get_parent_zone_names(self.name)[-1])
|
|
404
|
+
except (Zone.DoesNotExist, IndexError):
|
|
402
405
|
return None
|
|
403
406
|
|
|
404
|
-
|
|
405
|
-
|
|
407
|
+
@property
|
|
408
|
+
def ancestor_zones(self):
|
|
409
|
+
return (
|
|
410
|
+
self.view.zone_set.annotate(name_length=Length("name"))
|
|
411
|
+
.filter(name__in=get_parent_zone_names(self.name))
|
|
412
|
+
.order_by("name_length")
|
|
413
|
+
)
|
|
406
414
|
|
|
407
|
-
|
|
408
|
-
|
|
415
|
+
@property
|
|
416
|
+
def delegation_records(self):
|
|
417
|
+
descendant_zone_names = [
|
|
418
|
+
f"{name}." for name in self.descendant_zones.values_list("name", flat=True)
|
|
419
|
+
]
|
|
420
|
+
|
|
421
|
+
ns_records = (
|
|
422
|
+
self.record_set.filter(type=RecordTypeChoices.NS)
|
|
423
|
+
.exclude(fqdn=self.fqdn)
|
|
424
|
+
.filter(fqdn__in=descendant_zone_names)
|
|
425
|
+
)
|
|
426
|
+
ns_values = [record.value_fqdn for record in ns_records]
|
|
427
|
+
|
|
428
|
+
return (
|
|
429
|
+
ns_records
|
|
430
|
+
| self.record_set.filter(type=RecordTypeChoices.DS)
|
|
431
|
+
| self.record_set.filter(
|
|
432
|
+
type__in=(RecordTypeChoices.A, RecordTypeChoices.AAAA),
|
|
433
|
+
fqdn__in=ns_values,
|
|
434
|
+
)
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
@property
|
|
438
|
+
def ancestor_delegation_records(self):
|
|
439
|
+
ancestor_zones = self.ancestor_zones
|
|
440
|
+
|
|
441
|
+
ns_records = Record.objects.filter(
|
|
442
|
+
type=RecordTypeChoices.NS, zone__in=ancestor_zones, fqdn=self.fqdn
|
|
443
|
+
)
|
|
444
|
+
ns_values = [record.value_fqdn for record in ns_records]
|
|
445
|
+
|
|
446
|
+
ds_records = Record.objects.filter(
|
|
447
|
+
type=RecordTypeChoices.DS, zone__in=ancestor_zones, fqdn=self.fqdn
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
return (
|
|
451
|
+
ns_records
|
|
452
|
+
| ds_records
|
|
453
|
+
| Record.objects.filter(
|
|
454
|
+
zone__in=ancestor_zones,
|
|
455
|
+
type__in=(RecordTypeChoices.A, RecordTypeChoices.AAAA),
|
|
456
|
+
fqdn__in=ns_values,
|
|
457
|
+
)
|
|
458
|
+
)
|
|
409
459
|
|
|
410
460
|
def update_soa_record(self):
|
|
411
461
|
soa_name = "@"
|
|
@@ -483,8 +533,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
483
533
|
continue
|
|
484
534
|
|
|
485
535
|
relative_name = name.relativize(parent).to_text()
|
|
486
|
-
address_records =
|
|
487
|
-
Q(zone=ns_zone),
|
|
536
|
+
address_records = ns_zone.record_set.filter(
|
|
488
537
|
Q(status__in=RECORD_ACTIVE_STATUS_LIST),
|
|
489
538
|
Q(Q(name=f"{_nameserver.name}.") | Q(name=relative_name)),
|
|
490
539
|
Q(Q(type=RecordTypeChoices.A) | Q(type=RecordTypeChoices.AAAA)),
|
|
@@ -641,7 +690,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
641
690
|
{
|
|
642
691
|
"name": str(exc),
|
|
643
692
|
}
|
|
644
|
-
)
|
|
693
|
+
)
|
|
645
694
|
|
|
646
695
|
try:
|
|
647
696
|
validate_domain_name(self.name, zone_name=True)
|
|
@@ -650,7 +699,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
650
699
|
{
|
|
651
700
|
"name": exc,
|
|
652
701
|
}
|
|
653
|
-
)
|
|
702
|
+
)
|
|
654
703
|
|
|
655
704
|
if self.soa_rname in (None, ""):
|
|
656
705
|
raise ValidationError(_("soa_rname not set and no default value defined"))
|
|
@@ -662,7 +711,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
662
711
|
{
|
|
663
712
|
"soa_rname": exc,
|
|
664
713
|
}
|
|
665
|
-
)
|
|
714
|
+
)
|
|
666
715
|
|
|
667
716
|
if not self.soa_serial_auto:
|
|
668
717
|
if self.soa_serial is None:
|
|
@@ -746,8 +795,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
746
795
|
else:
|
|
747
796
|
self.rfc2317_parent_zone = None
|
|
748
797
|
|
|
749
|
-
overlapping_zones =
|
|
750
|
-
view=self.view,
|
|
798
|
+
overlapping_zones = self.view.zone_set.filter(
|
|
751
799
|
rfc2317_prefix__net_overlap=self.rfc2317_prefix,
|
|
752
800
|
active=True,
|
|
753
801
|
).exclude(pk=self.pk)
|
|
@@ -780,9 +828,8 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
780
828
|
if (
|
|
781
829
|
changed_fields is None or {"name", "view", "status"} & changed_fields
|
|
782
830
|
) and self.is_reverse_zone:
|
|
783
|
-
zones =
|
|
784
|
-
|
|
785
|
-
arpa_network__net_contains_or_equals=self.arpa_network,
|
|
831
|
+
zones = self.view.zone_set.filter(
|
|
832
|
+
arpa_network__net_contains_or_equals=self.arpa_network
|
|
786
833
|
)
|
|
787
834
|
address_records = Record.objects.filter(
|
|
788
835
|
Q(ptr_record__isnull=True) | Q(ptr_record__zone__in=zones),
|
|
@@ -811,9 +858,8 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
811
858
|
or {"name", "view", "status", "rfc2317_prefix", "rfc2317_parent_managed"}
|
|
812
859
|
& changed_fields
|
|
813
860
|
) and self.is_rfc2317_zone:
|
|
814
|
-
zones =
|
|
815
|
-
|
|
816
|
-
arpa_network__net_contains=self.rfc2317_prefix,
|
|
861
|
+
zones = self.view.zone_set.filter(
|
|
862
|
+
arpa_network__net_contains=self.rfc2317_prefix
|
|
817
863
|
)
|
|
818
864
|
address_records = Record.objects.filter(
|
|
819
865
|
Q(ptr_record__isnull=True)
|
|
@@ -894,7 +940,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
894
940
|
cname_zone.update_soa_record()
|
|
895
941
|
|
|
896
942
|
rfc2317_child_zones = list(
|
|
897
|
-
self.rfc2317_child_zones.
|
|
943
|
+
self.rfc2317_child_zones.values_list("pk", flat=True)
|
|
898
944
|
)
|
|
899
945
|
|
|
900
946
|
ipam_ip_addresses = list(
|
netbox_dns/tables/record.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import django_tables2 as tables
|
|
2
2
|
from django.utils.html import format_html
|
|
3
3
|
from django.utils.translation import gettext_lazy as _
|
|
4
|
-
from django.utils.translation import pgettext_lazy as _p
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
from netbox.tables import (
|
|
@@ -20,6 +19,7 @@ __all__ = (
|
|
|
20
19
|
"RecordTable",
|
|
21
20
|
"ManagedRecordTable",
|
|
22
21
|
"RelatedRecordTable",
|
|
22
|
+
"DelegationRecordTable",
|
|
23
23
|
)
|
|
24
24
|
|
|
25
25
|
|
|
@@ -29,7 +29,7 @@ class RecordBaseTable(TenancyColumnsMixin, NetBoxTable):
|
|
|
29
29
|
linkify=True,
|
|
30
30
|
)
|
|
31
31
|
view = tables.Column(
|
|
32
|
-
verbose_name=
|
|
32
|
+
verbose_name=_("View"),
|
|
33
33
|
accessor="zone__view",
|
|
34
34
|
linkify=True,
|
|
35
35
|
)
|
|
@@ -162,3 +162,15 @@ class RelatedRecordTable(RecordBaseTable):
|
|
|
162
162
|
"type",
|
|
163
163
|
"value",
|
|
164
164
|
)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class DelegationRecordTable(RecordBaseTable):
|
|
168
|
+
class Meta(NetBoxTable.Meta):
|
|
169
|
+
model = Record
|
|
170
|
+
fields = ()
|
|
171
|
+
default_columns = (
|
|
172
|
+
"name",
|
|
173
|
+
"zone",
|
|
174
|
+
"type",
|
|
175
|
+
"value",
|
|
176
|
+
)
|
netbox_dns/tables/zone.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import django_tables2 as tables
|
|
2
2
|
from django.utils.translation import gettext_lazy as _
|
|
3
|
-
from django.utils.translation import pgettext_lazy as _p
|
|
4
3
|
|
|
5
4
|
from netbox.tables import (
|
|
6
5
|
ChoiceFieldColumn,
|
|
@@ -21,7 +20,7 @@ class ZoneTable(TenancyColumnsMixin, NetBoxTable):
|
|
|
21
20
|
linkify=True,
|
|
22
21
|
)
|
|
23
22
|
view = tables.Column(
|
|
24
|
-
verbose_name=
|
|
23
|
+
verbose_name=_("View"),
|
|
25
24
|
linkify=True,
|
|
26
25
|
)
|
|
27
26
|
soa_mname = tables.Column(
|
netbox_dns/template_content.py
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import django_tables2 as tables
|
|
2
|
+
|
|
1
3
|
from django.conf import settings
|
|
2
4
|
from django.urls import reverse
|
|
3
5
|
|
|
4
6
|
from netbox.plugins.utils import get_plugin_config
|
|
5
7
|
from netbox.plugins import PluginTemplateExtension
|
|
8
|
+
from utilities.tables import register_table_column
|
|
9
|
+
from ipam.tables import IPAddressTable
|
|
6
10
|
|
|
7
11
|
from netbox_dns.models import Record
|
|
8
12
|
from netbox_dns.choices import RecordTypeChoices
|
|
@@ -114,8 +118,20 @@ class IPRelatedDNSRecords(PluginTemplateExtension):
|
|
|
114
118
|
)
|
|
115
119
|
|
|
116
120
|
|
|
121
|
+
address_records = tables.ManyToManyColumn(
|
|
122
|
+
verbose_name="DNS Address Records",
|
|
123
|
+
accessor="netbox_dns_records",
|
|
124
|
+
linkify_item=True,
|
|
125
|
+
transform=lambda obj: (
|
|
126
|
+
obj.fqdn.rstrip(".")
|
|
127
|
+
if obj.zone.view.default_view
|
|
128
|
+
else f"[{obj.zone.view.name}] {obj.fqdn.rstrip('.')}"
|
|
129
|
+
),
|
|
130
|
+
)
|
|
131
|
+
|
|
117
132
|
if not settings.PLUGINS_CONFIG["netbox_dns"].get("dnssync_disabled"):
|
|
118
133
|
template_extensions = [RelatedDNSRecords, RelatedDNSViews]
|
|
134
|
+
register_table_column(address_records, "address_records", IPAddressTable)
|
|
119
135
|
else:
|
|
120
136
|
template_extensions = []
|
|
121
137
|
|
|
@@ -43,6 +43,12 @@
|
|
|
43
43
|
<th scope="row">{% trans "Name" %}</th>
|
|
44
44
|
<td style="word-break:break-all;">{{ object.name }}</td>
|
|
45
45
|
</tr>
|
|
46
|
+
{% if mask_warning %}
|
|
47
|
+
<tr class="text-warning">
|
|
48
|
+
<th scope="row">{% trans "Warning" %}</th>
|
|
49
|
+
<td>{{ mask_warning }}</td>
|
|
50
|
+
</tr>
|
|
51
|
+
{% endif %}
|
|
46
52
|
{% if unicode_name %}
|
|
47
53
|
<tr>
|
|
48
54
|
<th scope="row">{% trans "IDN" %}</th>
|
|
@@ -76,6 +82,12 @@
|
|
|
76
82
|
<th scope="row">{% trans "Value" %}</th>
|
|
77
83
|
<td style="word-break:break-all;">{{ object.value }}</td>
|
|
78
84
|
</tr>
|
|
85
|
+
{% if cname_warning %}
|
|
86
|
+
<tr class="text-warning">
|
|
87
|
+
<th scope="row">{% trans "Warning" %}</th>
|
|
88
|
+
<td>{{ cname_warning }}</td>
|
|
89
|
+
</tr>
|
|
90
|
+
{% endif %}
|
|
79
91
|
{% if unicode_value %}
|
|
80
92
|
<tr>
|
|
81
93
|
<th scope="row">{% trans "Unicode Value" %}</th>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<div class="row">
|
|
6
6
|
<div class="col col-md-6">
|
|
7
7
|
<div class="card">
|
|
8
|
-
<h5 class="card-header">{% trans "View"
|
|
8
|
+
<h5 class="card-header">{% trans "View" %}</h5>
|
|
9
9
|
<table class="table table-hover attr-table">
|
|
10
10
|
<tr>
|
|
11
11
|
<th scope="row">{% trans "Name" %}</th>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{% extends 'netbox_dns/zone/base.html' %}
|
|
2
|
+
{% load helpers %}
|
|
3
|
+
{% load render_table from django_tables2 %}
|
|
4
|
+
{% load perms %}
|
|
5
|
+
|
|
6
|
+
{% block content %}
|
|
7
|
+
{% include 'inc/table_controls_htmx.html' with table_modal="DelegationRecordTable_config" %}
|
|
8
|
+
<div class="card">
|
|
9
|
+
<div class="htmx-container table-responsive" id="object_list">
|
|
10
|
+
{% include 'htmx/table.html' %}
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
{% endblock %}
|
|
14
|
+
|
|
15
|
+
{% block modals %}
|
|
16
|
+
{{ block.super }}
|
|
17
|
+
{% table_config_form table %}
|
|
18
|
+
{% endblock modals %}
|
netbox_dns/utilities/__init__.py
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from dns import name as dns_name
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
__all__ = ("get_parent_zone_names",)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_parent_zone_names(name, min_labels=1, include_self=False):
|
|
8
|
+
fqdn = dns_name.from_text(name)
|
|
9
|
+
return [
|
|
10
|
+
fqdn.split(i)[1].to_text().rstrip(".")
|
|
11
|
+
for i in range(min_labels + 1, len(fqdn.labels) + include_self)
|
|
12
|
+
]
|
|
@@ -12,6 +12,8 @@ from ipam.models import IPAddress, Prefix
|
|
|
12
12
|
|
|
13
13
|
from netbox_dns.choices import RecordStatusChoices
|
|
14
14
|
|
|
15
|
+
from .dns import get_parent_zone_names
|
|
16
|
+
|
|
15
17
|
|
|
16
18
|
__all__ = (
|
|
17
19
|
"get_zones",
|
|
@@ -85,15 +87,12 @@ def get_zones(ip_address, view=None, old_zone=None):
|
|
|
85
87
|
min_labels = settings.PLUGINS_CONFIG["netbox_dns"].get(
|
|
86
88
|
"dnssync_minimum_zone_labels", 2
|
|
87
89
|
)
|
|
88
|
-
fqdn = dns_name.from_text(ip_address.dns_name)
|
|
89
|
-
zone_name_candidates = [
|
|
90
|
-
fqdn.split(i)[1].to_text().rstrip(".")
|
|
91
|
-
for i in range(min_labels + 1, len(fqdn.labels) + 1)
|
|
92
|
-
]
|
|
93
90
|
|
|
94
91
|
zones = Zone.objects.filter(
|
|
95
92
|
view__in=views,
|
|
96
|
-
name__in=
|
|
93
|
+
name__in=get_parent_zone_names(
|
|
94
|
+
ip_address.dns_name, min_labels=min_labels, include_self=True
|
|
95
|
+
),
|
|
97
96
|
active=True,
|
|
98
97
|
)
|
|
99
98
|
|
|
@@ -194,9 +193,7 @@ def update_dns_records(ip_address, view=None, force=False):
|
|
|
194
193
|
updated = True
|
|
195
194
|
|
|
196
195
|
zones = Zone.objects.filter(pk__in=[zone.pk for zone in zones]).exclude(
|
|
197
|
-
pk__in=set(
|
|
198
|
-
ip_address.netbox_dns_records.all().values_list("zone", flat=True)
|
|
199
|
-
)
|
|
196
|
+
pk__in=set(ip_address.netbox_dns_records.values_list("zone", flat=True))
|
|
200
197
|
)
|
|
201
198
|
|
|
202
199
|
for zone in zones:
|
|
@@ -213,18 +210,18 @@ def update_dns_records(ip_address, view=None, force=False):
|
|
|
213
210
|
|
|
214
211
|
|
|
215
212
|
def delete_dns_records(ip_address, view=None):
|
|
216
|
-
deleted = False
|
|
217
|
-
|
|
218
213
|
if view is None:
|
|
219
214
|
address_records = ip_address.netbox_dns_records.all()
|
|
220
215
|
else:
|
|
221
216
|
address_records = ip_address.netbox_dns_records.filter(zone__view=view)
|
|
222
217
|
|
|
218
|
+
if not address_records.exists():
|
|
219
|
+
return False
|
|
220
|
+
|
|
223
221
|
for record in address_records:
|
|
224
222
|
record.delete()
|
|
225
|
-
deleted = True
|
|
226
223
|
|
|
227
|
-
return
|
|
224
|
+
return True
|
|
228
225
|
|
|
229
226
|
|
|
230
227
|
def get_views_by_prefix(prefix):
|
netbox_dns/views/nameserver.py
CHANGED
|
@@ -36,7 +36,7 @@ class NameServerListView(generic.ObjectListView):
|
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
class NameServerView(generic.ObjectView):
|
|
39
|
-
queryset = NameServer.objects.
|
|
39
|
+
queryset = NameServer.objects.prefetch_related("zones")
|
|
40
40
|
|
|
41
41
|
def get_extra_context(self, request, instance):
|
|
42
42
|
name = dns_name.from_text(instance.name)
|
|
@@ -85,7 +85,7 @@ class NameServerContactsView(ObjectContactsView):
|
|
|
85
85
|
|
|
86
86
|
@register_model_view(NameServer, "zones")
|
|
87
87
|
class NameServerZoneListView(generic.ObjectChildrenView):
|
|
88
|
-
queryset = NameServer.objects.
|
|
88
|
+
queryset = NameServer.objects.prefetch_related("zones")
|
|
89
89
|
child_model = Zone
|
|
90
90
|
table = ZoneTable
|
|
91
91
|
filterset = ZoneFilterSet
|
|
@@ -105,7 +105,7 @@ class NameServerZoneListView(generic.ObjectChildrenView):
|
|
|
105
105
|
|
|
106
106
|
@register_model_view(NameServer, "soa_zones")
|
|
107
107
|
class NameServerSOAZoneListView(generic.ObjectChildrenView):
|
|
108
|
-
queryset = NameServer.objects.
|
|
108
|
+
queryset = NameServer.objects.prefetch_related("zones_soa")
|
|
109
109
|
child_model = Zone
|
|
110
110
|
table = ZoneTable
|
|
111
111
|
filterset = ZoneFilterSet
|
netbox_dns/views/record.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from dns import name as dns_name
|
|
2
2
|
|
|
3
|
+
from django.utils.translation import gettext_lazy as _
|
|
4
|
+
|
|
3
5
|
from netbox.views import generic
|
|
4
6
|
from utilities.views import register_model_view
|
|
5
7
|
from tenancy.views import ObjectContactsView
|
|
@@ -14,7 +16,7 @@ from netbox_dns.forms import (
|
|
|
14
16
|
from netbox_dns.models import Record, Zone
|
|
15
17
|
from netbox_dns.choices import RecordTypeChoices
|
|
16
18
|
from netbox_dns.tables import RecordTable, ManagedRecordTable, RelatedRecordTable
|
|
17
|
-
from netbox_dns.utilities import value_to_unicode
|
|
19
|
+
from netbox_dns.utilities import value_to_unicode, get_parent_zone_names
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
__all__ = (
|
|
@@ -29,6 +31,10 @@ __all__ = (
|
|
|
29
31
|
)
|
|
30
32
|
|
|
31
33
|
|
|
34
|
+
class CNAMEWarning(Exception):
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
32
38
|
class RecordListView(generic.ObjectListView):
|
|
33
39
|
queryset = Record.objects.filter(managed=False).prefetch_related(
|
|
34
40
|
"zone", "ptr_record"
|
|
@@ -50,7 +56,7 @@ class ManagedRecordListView(generic.ObjectListView):
|
|
|
50
56
|
|
|
51
57
|
|
|
52
58
|
class RecordView(generic.ObjectView):
|
|
53
|
-
queryset = Record.objects.
|
|
59
|
+
queryset = Record.objects.prefetch_related("zone", "ptr_record")
|
|
54
60
|
|
|
55
61
|
def get_value_records(self, instance):
|
|
56
62
|
value_fqdn = dns_name.from_text(instance.value_fqdn)
|
|
@@ -64,6 +70,18 @@ class RecordView(generic.ObjectView):
|
|
|
64
70
|
data=cname_targets,
|
|
65
71
|
)
|
|
66
72
|
|
|
73
|
+
if instance.zone.view.zone_set.filter(
|
|
74
|
+
name__in=get_parent_zone_names(instance.value_fqdn, min_labels=1),
|
|
75
|
+
active=True,
|
|
76
|
+
).exists():
|
|
77
|
+
raise (
|
|
78
|
+
CNAMEWarning(
|
|
79
|
+
_(
|
|
80
|
+
"There is no matching target record for CNAME value {value}"
|
|
81
|
+
).format(value=instance.value_fqdn)
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
|
|
67
85
|
return None
|
|
68
86
|
|
|
69
87
|
def get_cname_records(self, instance):
|
|
@@ -75,14 +93,8 @@ class RecordView(generic.ObjectView):
|
|
|
75
93
|
)
|
|
76
94
|
)
|
|
77
95
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
fqdn.split(length)[1].to_text().rstrip(".")
|
|
81
|
-
for length in range(1, len(fqdn) + 1)
|
|
82
|
-
]
|
|
83
|
-
|
|
84
|
-
parent_zones = Zone.objects.filter(
|
|
85
|
-
view=instance.zone.view, name__in=parent_zone_names
|
|
96
|
+
parent_zones = instance.zone.view.zone_set.filter(
|
|
97
|
+
name__in=get_parent_zone_names(instance.fqdn, include_self=True),
|
|
86
98
|
)
|
|
87
99
|
|
|
88
100
|
for parent_zone in parent_zones:
|
|
@@ -118,10 +130,31 @@ class RecordView(generic.ObjectView):
|
|
|
118
130
|
context["unicode_value"] = unicode_value
|
|
119
131
|
|
|
120
132
|
if instance.type == RecordTypeChoices.CNAME:
|
|
121
|
-
|
|
133
|
+
try:
|
|
134
|
+
context["cname_target_table"] = self.get_value_records(instance)
|
|
135
|
+
except CNAMEWarning as exc:
|
|
136
|
+
context["cname_warning"] = str(exc)
|
|
122
137
|
else:
|
|
123
138
|
context["cname_table"] = self.get_cname_records(instance)
|
|
124
139
|
|
|
140
|
+
if not instance.managed:
|
|
141
|
+
name = dns_name.from_text(instance.name, origin=None)
|
|
142
|
+
|
|
143
|
+
if not instance.is_delegation_record:
|
|
144
|
+
fqdn = dns_name.from_text(instance.fqdn)
|
|
145
|
+
|
|
146
|
+
if Zone.objects.filter(
|
|
147
|
+
active=True,
|
|
148
|
+
name__in=get_parent_zone_names(
|
|
149
|
+
instance.fqdn,
|
|
150
|
+
min_labels=len(fqdn) - len(name),
|
|
151
|
+
include_self=True,
|
|
152
|
+
),
|
|
153
|
+
).exists():
|
|
154
|
+
context["mask_warning"] = _(
|
|
155
|
+
"Record is masked by a child zone and may not be visible in DNS"
|
|
156
|
+
)
|
|
157
|
+
|
|
125
158
|
return context
|
|
126
159
|
|
|
127
160
|
|
netbox_dns/views/registrar.py
CHANGED
|
@@ -69,7 +69,7 @@ class RegistrarBulkDeleteView(generic.BulkDeleteView):
|
|
|
69
69
|
|
|
70
70
|
@register_model_view(Registrar, "zones")
|
|
71
71
|
class RegistrarZoneListView(generic.ObjectChildrenView):
|
|
72
|
-
queryset = Registrar.objects.
|
|
72
|
+
queryset = Registrar.objects.prefetch_related("zone_set")
|
|
73
73
|
child_model = Zone
|
|
74
74
|
table = ZoneTable
|
|
75
75
|
filterset = ZoneFilterSet
|
|
@@ -70,7 +70,7 @@ class RegistrationContactBulkDeleteView(generic.BulkDeleteView):
|
|
|
70
70
|
|
|
71
71
|
@register_model_view(RegistrationContact, "zones")
|
|
72
72
|
class RegistrationContactZoneListView(generic.ObjectChildrenView):
|
|
73
|
-
queryset = RegistrationContact.objects.
|
|
73
|
+
queryset = RegistrationContact.objects.prefetch_related(
|
|
74
74
|
"zone_set", "admin_c_zones", "tech_c_zones", "billing_c_zones"
|
|
75
75
|
)
|
|
76
76
|
child_model = Zone
|
netbox_dns/views/view.py
CHANGED
|
@@ -31,7 +31,7 @@ __all__ = (
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class ViewView(generic.ObjectView):
|
|
34
|
-
queryset = View.objects.
|
|
34
|
+
queryset = View.objects.prefetch_related("zone_set")
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
class ViewListView(generic.ObjectListView):
|
|
@@ -89,7 +89,7 @@ class ViewPrefixEditView(generic.ObjectEditView):
|
|
|
89
89
|
|
|
90
90
|
@register_model_view(View, "zones")
|
|
91
91
|
class ViewZoneListView(generic.ObjectChildrenView):
|
|
92
|
-
queryset = View.objects.
|
|
92
|
+
queryset = View.objects.prefetch_related("zone_set")
|
|
93
93
|
child_model = Zone
|
|
94
94
|
table = ZoneTable
|
|
95
95
|
filterset = ZoneFilterSet
|