netbox-plugin-dns 1.1.4__py3-none-any.whl → 1.1.6__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 +2 -2
- netbox_dns/filtersets/record.py +0 -1
- netbox_dns/filtersets/zone.py +1 -2
- netbox_dns/forms/record.py +12 -8
- netbox_dns/forms/view.py +2 -3
- netbox_dns/forms/zone.py +8 -9
- netbox_dns/forms/zone_template.py +5 -5
- netbox_dns/graphql/types.py +37 -0
- netbox_dns/locale/de/LC_MESSAGES/django.mo +0 -0
- netbox_dns/management/commands/cleanup_database.py +4 -6
- netbox_dns/management/commands/rebuild_dnssync.py +3 -1
- netbox_dns/migrations/0011_rename_related_fields.py +63 -0
- netbox_dns/mixins/object_modification.py +4 -0
- netbox_dns/models/nameserver.py +5 -9
- netbox_dns/models/record.py +23 -38
- netbox_dns/models/record_template.py +4 -4
- netbox_dns/models/registration_contact.py +1 -1
- netbox_dns/models/view.py +2 -3
- netbox_dns/models/zone.py +96 -48
- 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 +5 -5
- netbox_dns/views/record.py +44 -11
- netbox_dns/views/registrar.py +3 -3
- netbox_dns/views/registration_contact.py +2 -2
- netbox_dns/views/view.py +4 -4
- netbox_dns/views/zone.py +50 -21
- {netbox_plugin_dns-1.1.4.dist-info → netbox_plugin_dns-1.1.6.dist-info}/METADATA +2 -1
- {netbox_plugin_dns-1.1.4.dist-info → netbox_plugin_dns-1.1.6.dist-info}/RECORD +41 -38
- {netbox_plugin_dns-1.1.4.dist-info → netbox_plugin_dns-1.1.6.dist-info}/WHEEL +1 -1
- {netbox_plugin_dns-1.1.4.dist-info → netbox_plugin_dns-1.1.6.dist-info}/LICENSE +0 -0
- {netbox_plugin_dns-1.1.4.dist-info → netbox_plugin_dns-1.1.6.dist-info}/top_level.txt +0 -0
netbox_dns/models/record.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import ipaddress
|
|
2
|
+
import netaddr
|
|
2
3
|
|
|
3
4
|
import dns
|
|
4
5
|
from dns import name as dns_name
|
|
@@ -23,11 +24,6 @@ from netbox_dns.validators import validate_generic_name, validate_record_value
|
|
|
23
24
|
from netbox_dns.mixins import ObjectModificationMixin
|
|
24
25
|
from netbox_dns.choices import RecordTypeChoices, RecordStatusChoices
|
|
25
26
|
|
|
26
|
-
# +
|
|
27
|
-
# This is a hack designed to break cyclic imports between Record and Zone
|
|
28
|
-
# -
|
|
29
|
-
from netbox_dns.models import zone
|
|
30
|
-
|
|
31
27
|
|
|
32
28
|
__all__ = (
|
|
33
29
|
"Record",
|
|
@@ -105,14 +101,8 @@ class RecordManager(models.Manager.from_queryset(RestrictedQuerySet)):
|
|
|
105
101
|
.annotate(
|
|
106
102
|
active=ExpressionWrapper(
|
|
107
103
|
Q(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
Q(address_record__isnull=True)
|
|
111
|
-
| Q(
|
|
112
|
-
address_record__zone__status__in=ZONE_ACTIVE_STATUS_LIST
|
|
113
|
-
)
|
|
114
|
-
)
|
|
115
|
-
& Q(status__in=RECORD_ACTIVE_STATUS_LIST)
|
|
104
|
+
zone__status__in=ZONE_ACTIVE_STATUS_LIST,
|
|
105
|
+
status__in=RECORD_ACTIVE_STATUS_LIST,
|
|
116
106
|
),
|
|
117
107
|
output_field=BooleanField(),
|
|
118
108
|
)
|
|
@@ -134,6 +124,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
134
124
|
verbose_name=_("Zone"),
|
|
135
125
|
to="Zone",
|
|
136
126
|
on_delete=models.CASCADE,
|
|
127
|
+
related_name="records",
|
|
137
128
|
)
|
|
138
129
|
fqdn = models.CharField(
|
|
139
130
|
verbose_name=_("FQDN"),
|
|
@@ -269,7 +260,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
269
260
|
|
|
270
261
|
@property
|
|
271
262
|
def value_fqdn(self):
|
|
272
|
-
if self.type
|
|
263
|
+
if self.type not in (RecordTypeChoices.CNAME, RecordTypeChoices.NS):
|
|
273
264
|
return None
|
|
274
265
|
|
|
275
266
|
_zone = dns_name.from_text(self.zone.name)
|
|
@@ -331,8 +322,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
331
322
|
def ptr_zone(self):
|
|
332
323
|
if self.type == RecordTypeChoices.A:
|
|
333
324
|
ptr_zone = (
|
|
334
|
-
zone.
|
|
335
|
-
view=self.zone.view,
|
|
325
|
+
self.zone.view.zones.filter(
|
|
336
326
|
rfc2317_prefix__net_contains=self.value,
|
|
337
327
|
)
|
|
338
328
|
.order_by("rfc2317_prefix__net_mask_length")
|
|
@@ -343,15 +333,17 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
343
333
|
return ptr_zone
|
|
344
334
|
|
|
345
335
|
ptr_zone = (
|
|
346
|
-
zone.
|
|
347
|
-
view=self.zone.view, arpa_network__net_contains=self.value
|
|
348
|
-
)
|
|
336
|
+
self.zone.view.zones.filter(arpa_network__net_contains=self.value)
|
|
349
337
|
.order_by("arpa_network__net_mask_length")
|
|
350
338
|
.last()
|
|
351
339
|
)
|
|
352
340
|
|
|
353
341
|
return ptr_zone
|
|
354
342
|
|
|
343
|
+
@property
|
|
344
|
+
def is_delegation_record(self):
|
|
345
|
+
return self in self.zone.delegation_records
|
|
346
|
+
|
|
355
347
|
def update_ptr_record(self, update_rfc2317_cname=True, save_zone_serial=True):
|
|
356
348
|
ptr_zone = self.ptr_zone
|
|
357
349
|
|
|
@@ -461,10 +453,9 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
461
453
|
|
|
462
454
|
self.remove_from_rfc2317_cname_record(save_zone_serial=save_zone_serial)
|
|
463
455
|
|
|
464
|
-
rfc2317_cname_record =
|
|
456
|
+
rfc2317_cname_record = self.zone.rfc2317_parent_zone.records.filter(
|
|
465
457
|
name=cname_name,
|
|
466
458
|
type=RecordTypeChoices.CNAME,
|
|
467
|
-
zone=self.zone.rfc2317_parent_zone,
|
|
468
459
|
managed=True,
|
|
469
460
|
value=self.fqdn,
|
|
470
461
|
).first()
|
|
@@ -612,8 +603,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
612
603
|
if new_zone is None:
|
|
613
604
|
new_zone = self.zone
|
|
614
605
|
|
|
615
|
-
records =
|
|
616
|
-
zone=new_zone,
|
|
606
|
+
records = new_zone.records.filter(
|
|
617
607
|
name=self.name,
|
|
618
608
|
type=self.type,
|
|
619
609
|
value=self.value,
|
|
@@ -649,8 +639,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
649
639
|
if not get_plugin_config("netbox_dns", "dnssync_conflict_deactivate", False):
|
|
650
640
|
return
|
|
651
641
|
|
|
652
|
-
records =
|
|
653
|
-
zone=self.zone,
|
|
642
|
+
records = self.zone.records.filter(
|
|
654
643
|
name=self.name,
|
|
655
644
|
type=self.type,
|
|
656
645
|
value=self.value,
|
|
@@ -678,8 +667,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
678
667
|
return
|
|
679
668
|
|
|
680
669
|
records = (
|
|
681
|
-
|
|
682
|
-
zone=self.zone,
|
|
670
|
+
self.zone.records.filter(
|
|
683
671
|
name=self.name,
|
|
684
672
|
type=self.type,
|
|
685
673
|
)
|
|
@@ -722,8 +710,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
722
710
|
ttl = self.ttl
|
|
723
711
|
|
|
724
712
|
records = (
|
|
725
|
-
|
|
726
|
-
zone=self.zone,
|
|
713
|
+
self.zone.records.filter(
|
|
727
714
|
name=self.name,
|
|
728
715
|
type=self.type,
|
|
729
716
|
)
|
|
@@ -751,9 +738,9 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
751
738
|
if not self.is_active:
|
|
752
739
|
return
|
|
753
740
|
|
|
754
|
-
records =
|
|
755
|
-
|
|
756
|
-
)
|
|
741
|
+
records = self.zone.records.filter(name=self.name, active=True).exclude(
|
|
742
|
+
pk=self.pk
|
|
743
|
+
)
|
|
757
744
|
|
|
758
745
|
if self.type == RecordTypeChoices.A and not self.disable_ptr:
|
|
759
746
|
ptr_zone = self.ptr_zone
|
|
@@ -770,8 +757,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
770
757
|
)
|
|
771
758
|
|
|
772
759
|
if (
|
|
773
|
-
|
|
774
|
-
zone=ptr_cname_zone,
|
|
760
|
+
ptr_cname_zone.records.filter(
|
|
775
761
|
name=ptr_cname_name,
|
|
776
762
|
active=True,
|
|
777
763
|
)
|
|
@@ -855,7 +841,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
855
841
|
self.ip_address = self.address_from_name
|
|
856
842
|
|
|
857
843
|
elif self.is_address_record:
|
|
858
|
-
self.ip_address = self.value
|
|
844
|
+
self.ip_address = netaddr.IPAddress(self.value)
|
|
859
845
|
else:
|
|
860
846
|
self.ip_address = None
|
|
861
847
|
|
|
@@ -873,9 +859,8 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
873
859
|
if changed_fields is None or changed_fields:
|
|
874
860
|
super().save(*args, **kwargs)
|
|
875
861
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
_zone.update_serial(save_zone_serial=save_zone_serial)
|
|
862
|
+
if self.type != RecordTypeChoices.SOA and self.zone.soa_serial_auto:
|
|
863
|
+
self.zone.update_serial(save_zone_serial=save_zone_serial)
|
|
879
864
|
|
|
880
865
|
def delete(self, *args, save_zone_serial=True, **kwargs):
|
|
881
866
|
if self.rfc2317_cname_record:
|
|
@@ -133,17 +133,17 @@ class RecordTemplate(NetBoxModel):
|
|
|
133
133
|
{
|
|
134
134
|
"record_name": exc,
|
|
135
135
|
}
|
|
136
|
-
)
|
|
136
|
+
)
|
|
137
137
|
|
|
138
138
|
def validate_value(self):
|
|
139
139
|
try:
|
|
140
140
|
validate_record_value(self)
|
|
141
141
|
except ValidationError as exc:
|
|
142
|
-
raise ValidationError({"value": exc})
|
|
142
|
+
raise ValidationError({"value": exc})
|
|
143
143
|
|
|
144
144
|
def matching_records(self, zone):
|
|
145
|
-
return
|
|
146
|
-
|
|
145
|
+
return zone.records.filter(
|
|
146
|
+
name=self.record_name, type=self.type, value=self.value
|
|
147
147
|
)
|
|
148
148
|
|
|
149
149
|
def create_record(self, zone):
|
|
@@ -137,7 +137,7 @@ class RegistrationContact(NetBoxModel):
|
|
|
137
137
|
@property
|
|
138
138
|
def zones(self):
|
|
139
139
|
return (
|
|
140
|
-
set(self.
|
|
140
|
+
set(self.registrant_zones.all())
|
|
141
141
|
| set(self.admin_c_zones.all())
|
|
142
142
|
| set(self.tech_c_zones.all())
|
|
143
143
|
| set(self.billing_c_zones.all())
|
netbox_dns/models/view.py
CHANGED
|
@@ -2,7 +2,6 @@ from django.db import models
|
|
|
2
2
|
from django.urls import reverse
|
|
3
3
|
from django.core.exceptions import ValidationError
|
|
4
4
|
from django.utils.translation import gettext_lazy as _
|
|
5
|
-
from django.utils.translation import pgettext_lazy as _p
|
|
6
5
|
|
|
7
6
|
from netbox.models import NetBoxModel
|
|
8
7
|
from netbox.models.features import ContactsMixin
|
|
@@ -77,8 +76,8 @@ class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
77
76
|
return str(self.name)
|
|
78
77
|
|
|
79
78
|
class Meta:
|
|
80
|
-
verbose_name =
|
|
81
|
-
verbose_name_plural =
|
|
79
|
+
verbose_name = _("View")
|
|
80
|
+
verbose_name_plural = _("Views")
|
|
82
81
|
|
|
83
82
|
ordering = ("name",)
|
|
84
83
|
|
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,9 +85,10 @@ 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,
|
|
91
|
+
related_name="zones",
|
|
90
92
|
null=False,
|
|
91
93
|
)
|
|
92
94
|
name = models.CharField(
|
|
@@ -120,7 +122,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
120
122
|
soa_mname = models.ForeignKey(
|
|
121
123
|
verbose_name=_("SOA MName"),
|
|
122
124
|
to="NameServer",
|
|
123
|
-
related_name="
|
|
125
|
+
related_name="soa_zones",
|
|
124
126
|
on_delete=models.PROTECT,
|
|
125
127
|
blank=False,
|
|
126
128
|
null=False,
|
|
@@ -189,6 +191,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
189
191
|
verbose_name=_("Registrar"),
|
|
190
192
|
to="Registrar",
|
|
191
193
|
on_delete=models.SET_NULL,
|
|
194
|
+
related_name="zones",
|
|
192
195
|
blank=True,
|
|
193
196
|
null=True,
|
|
194
197
|
)
|
|
@@ -202,6 +205,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
202
205
|
verbose_name=_("Registrant"),
|
|
203
206
|
to="RegistrationContact",
|
|
204
207
|
on_delete=models.SET_NULL,
|
|
208
|
+
related_name="registrant_zones",
|
|
205
209
|
blank=True,
|
|
206
210
|
null=True,
|
|
207
211
|
)
|
|
@@ -298,6 +302,10 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
298
302
|
|
|
299
303
|
return str(name)
|
|
300
304
|
|
|
305
|
+
@property
|
|
306
|
+
def fqdn(self):
|
|
307
|
+
return f"{self.name}."
|
|
308
|
+
|
|
301
309
|
@staticmethod
|
|
302
310
|
def get_defaults():
|
|
303
311
|
default_fields = (
|
|
@@ -363,10 +371,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
363
371
|
return None
|
|
364
372
|
|
|
365
373
|
return (
|
|
366
|
-
|
|
367
|
-
view=self.view,
|
|
368
|
-
arpa_network__net_contains=self.rfc2317_prefix,
|
|
369
|
-
)
|
|
374
|
+
self.view.zones.filter(arpa_network__net_contains=self.rfc2317_prefix)
|
|
370
375
|
.order_by("arpa_network__net_mask_length")
|
|
371
376
|
.last()
|
|
372
377
|
)
|
|
@@ -387,25 +392,71 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
387
392
|
|
|
388
393
|
@property
|
|
389
394
|
def child_zones(self):
|
|
390
|
-
return
|
|
391
|
-
|
|
392
|
-
|
|
395
|
+
return self.view.zones.filter(name__iregex=rf"^[^.]+\.{re.escape(self.name)}$")
|
|
396
|
+
|
|
397
|
+
@property
|
|
398
|
+
def descendant_zones(self):
|
|
399
|
+
return self.view.zones.filter(name__endswith=f".{self.name}")
|
|
393
400
|
|
|
394
401
|
@property
|
|
395
402
|
def parent_zone(self):
|
|
396
|
-
parent_name = (
|
|
397
|
-
dns_name.from_text(self.name).parent().relativize(dns_name.root).to_text()
|
|
398
|
-
)
|
|
399
403
|
try:
|
|
400
|
-
return
|
|
401
|
-
except Zone.DoesNotExist:
|
|
404
|
+
return self.view.zones.get(name=get_parent_zone_names(self.name)[-1])
|
|
405
|
+
except (Zone.DoesNotExist, IndexError):
|
|
402
406
|
return None
|
|
403
407
|
|
|
404
|
-
|
|
405
|
-
|
|
408
|
+
@property
|
|
409
|
+
def ancestor_zones(self):
|
|
410
|
+
return (
|
|
411
|
+
self.view.zones.annotate(name_length=Length("name"))
|
|
412
|
+
.filter(name__in=get_parent_zone_names(self.name))
|
|
413
|
+
.order_by("name_length")
|
|
414
|
+
)
|
|
406
415
|
|
|
407
|
-
|
|
408
|
-
|
|
416
|
+
@property
|
|
417
|
+
def delegation_records(self):
|
|
418
|
+
descendant_zone_names = [
|
|
419
|
+
f"{name}." for name in self.descendant_zones.values_list("name", flat=True)
|
|
420
|
+
]
|
|
421
|
+
|
|
422
|
+
ns_records = (
|
|
423
|
+
self.records.filter(type=RecordTypeChoices.NS)
|
|
424
|
+
.exclude(fqdn=self.fqdn)
|
|
425
|
+
.filter(fqdn__in=descendant_zone_names)
|
|
426
|
+
)
|
|
427
|
+
ns_values = [record.value_fqdn for record in ns_records]
|
|
428
|
+
|
|
429
|
+
return (
|
|
430
|
+
ns_records
|
|
431
|
+
| self.records.filter(type=RecordTypeChoices.DS)
|
|
432
|
+
| self.records.filter(
|
|
433
|
+
type__in=(RecordTypeChoices.A, RecordTypeChoices.AAAA),
|
|
434
|
+
fqdn__in=ns_values,
|
|
435
|
+
)
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
@property
|
|
439
|
+
def ancestor_delegation_records(self):
|
|
440
|
+
ancestor_zones = self.ancestor_zones
|
|
441
|
+
|
|
442
|
+
ns_records = Record.objects.filter(
|
|
443
|
+
type=RecordTypeChoices.NS, zone__in=ancestor_zones, fqdn=self.fqdn
|
|
444
|
+
)
|
|
445
|
+
ns_values = [record.value_fqdn for record in ns_records]
|
|
446
|
+
|
|
447
|
+
ds_records = Record.objects.filter(
|
|
448
|
+
type=RecordTypeChoices.DS, zone__in=ancestor_zones, fqdn=self.fqdn
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
return (
|
|
452
|
+
ns_records
|
|
453
|
+
| ds_records
|
|
454
|
+
| Record.objects.filter(
|
|
455
|
+
zone__in=ancestor_zones,
|
|
456
|
+
type__in=(RecordTypeChoices.A, RecordTypeChoices.AAAA),
|
|
457
|
+
fqdn__in=ns_values,
|
|
458
|
+
)
|
|
459
|
+
)
|
|
409
460
|
|
|
410
461
|
def update_soa_record(self):
|
|
411
462
|
soa_name = "@"
|
|
@@ -423,7 +474,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
423
474
|
)
|
|
424
475
|
|
|
425
476
|
try:
|
|
426
|
-
soa_record = self.
|
|
477
|
+
soa_record = self.records.get(type=RecordTypeChoices.SOA, name=soa_name)
|
|
427
478
|
|
|
428
479
|
if soa_record.ttl != soa_ttl or soa_record.value != soa_rdata.to_text():
|
|
429
480
|
soa_record.ttl = soa_ttl
|
|
@@ -446,9 +497,10 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
446
497
|
|
|
447
498
|
nameservers = [f"{nameserver.name}." for nameserver in self.nameservers.all()]
|
|
448
499
|
|
|
449
|
-
self.
|
|
450
|
-
|
|
451
|
-
).
|
|
500
|
+
for ns_record in self.records.filter(
|
|
501
|
+
type=RecordTypeChoices.NS, managed=True
|
|
502
|
+
).exclude(value__in=nameservers):
|
|
503
|
+
ns_record.delete()
|
|
452
504
|
|
|
453
505
|
for ns in nameservers:
|
|
454
506
|
Record.raw_objects.update_or_create(
|
|
@@ -483,8 +535,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
483
535
|
continue
|
|
484
536
|
|
|
485
537
|
relative_name = name.relativize(parent).to_text()
|
|
486
|
-
address_records =
|
|
487
|
-
Q(zone=ns_zone),
|
|
538
|
+
address_records = ns_zone.records.filter(
|
|
488
539
|
Q(status__in=RECORD_ACTIVE_STATUS_LIST),
|
|
489
540
|
Q(Q(name=f"{_nameserver.name}.") | Q(name=relative_name)),
|
|
490
541
|
Q(Q(type=RecordTypeChoices.A) | Q(type=RecordTypeChoices.AAAA)),
|
|
@@ -574,9 +625,9 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
574
625
|
self.rfc2317_parent_zone = rfc2317_parent_zone
|
|
575
626
|
self.save(update_fields=["rfc2317_parent_zone"])
|
|
576
627
|
|
|
577
|
-
ptr_records = self.
|
|
578
|
-
|
|
579
|
-
)
|
|
628
|
+
ptr_records = self.records.filter(type=RecordTypeChoices.PTR).prefetch_related(
|
|
629
|
+
"zone", "rfc2317_cname_record"
|
|
630
|
+
)
|
|
580
631
|
ptr_zones = {ptr_record.zone for ptr_record in ptr_records}
|
|
581
632
|
|
|
582
633
|
if self.rfc2317_parent_managed:
|
|
@@ -641,7 +692,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
641
692
|
{
|
|
642
693
|
"name": str(exc),
|
|
643
694
|
}
|
|
644
|
-
)
|
|
695
|
+
)
|
|
645
696
|
|
|
646
697
|
try:
|
|
647
698
|
validate_domain_name(self.name, zone_name=True)
|
|
@@ -650,7 +701,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
650
701
|
{
|
|
651
702
|
"name": exc,
|
|
652
703
|
}
|
|
653
|
-
)
|
|
704
|
+
)
|
|
654
705
|
|
|
655
706
|
if self.soa_rname in (None, ""):
|
|
656
707
|
raise ValidationError(_("soa_rname not set and no default value defined"))
|
|
@@ -662,7 +713,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
662
713
|
{
|
|
663
714
|
"soa_rname": exc,
|
|
664
715
|
}
|
|
665
|
-
)
|
|
716
|
+
)
|
|
666
717
|
|
|
667
718
|
if not self.soa_serial_auto:
|
|
668
719
|
if self.soa_serial is None:
|
|
@@ -703,7 +754,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
703
754
|
or old_view_id != self.view_id
|
|
704
755
|
):
|
|
705
756
|
ip_addresses = IPAddress.objects.filter(
|
|
706
|
-
netbox_dns_records__in=self.
|
|
757
|
+
netbox_dns_records__in=self.records.filter(
|
|
707
758
|
ipam_ip_address__isnull=False
|
|
708
759
|
)
|
|
709
760
|
)
|
|
@@ -746,8 +797,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
746
797
|
else:
|
|
747
798
|
self.rfc2317_parent_zone = None
|
|
748
799
|
|
|
749
|
-
overlapping_zones =
|
|
750
|
-
view=self.view,
|
|
800
|
+
overlapping_zones = self.view.zones.filter(
|
|
751
801
|
rfc2317_prefix__net_overlap=self.rfc2317_prefix,
|
|
752
802
|
active=True,
|
|
753
803
|
).exclude(pk=self.pk)
|
|
@@ -780,9 +830,8 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
780
830
|
if (
|
|
781
831
|
changed_fields is None or {"name", "view", "status"} & changed_fields
|
|
782
832
|
) and self.is_reverse_zone:
|
|
783
|
-
zones =
|
|
784
|
-
|
|
785
|
-
arpa_network__net_contains_or_equals=self.arpa_network,
|
|
833
|
+
zones = self.view.zones.filter(
|
|
834
|
+
arpa_network__net_contains_or_equals=self.arpa_network
|
|
786
835
|
)
|
|
787
836
|
address_records = Record.objects.filter(
|
|
788
837
|
Q(ptr_record__isnull=True) | Q(ptr_record__zone__in=zones),
|
|
@@ -811,9 +860,8 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
811
860
|
or {"name", "view", "status", "rfc2317_prefix", "rfc2317_parent_managed"}
|
|
812
861
|
& changed_fields
|
|
813
862
|
) and self.is_rfc2317_zone:
|
|
814
|
-
zones =
|
|
815
|
-
|
|
816
|
-
arpa_network__net_contains=self.rfc2317_prefix,
|
|
863
|
+
zones = self.view.zones.filter(
|
|
864
|
+
arpa_network__net_contains=self.rfc2317_prefix
|
|
817
865
|
)
|
|
818
866
|
address_records = Record.objects.filter(
|
|
819
867
|
Q(ptr_record__isnull=True)
|
|
@@ -836,14 +884,14 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
836
884
|
self.update_rfc2317_parent_zone()
|
|
837
885
|
|
|
838
886
|
elif changed_fields is not None and {"name", "view", "status"} & changed_fields:
|
|
839
|
-
for address_record in self.
|
|
887
|
+
for address_record in self.records.filter(
|
|
840
888
|
type__in=(RecordTypeChoices.A, RecordTypeChoices.AAAA),
|
|
841
889
|
ipam_ip_address__isnull=True,
|
|
842
890
|
):
|
|
843
891
|
address_record.save(update_fields=["ptr_record"])
|
|
844
892
|
|
|
845
893
|
if changed_fields is not None and "name" in changed_fields:
|
|
846
|
-
for _record in self.
|
|
894
|
+
for _record in self.records.filter(ipam_ip_address__isnull=True):
|
|
847
895
|
_record.save(
|
|
848
896
|
update_fields=["fqdn"],
|
|
849
897
|
save_zone_serial=False,
|
|
@@ -853,7 +901,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
853
901
|
|
|
854
902
|
if changed_fields is None or {"name", "view"} & changed_fields:
|
|
855
903
|
ip_addresses = IPAddress.objects.filter(
|
|
856
|
-
netbox_dns_records__in=self.
|
|
904
|
+
netbox_dns_records__in=self.records.filter(
|
|
857
905
|
ipam_ip_address__isnull=False
|
|
858
906
|
)
|
|
859
907
|
)
|
|
@@ -867,13 +915,13 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
867
915
|
|
|
868
916
|
def delete(self, *args, **kwargs):
|
|
869
917
|
with transaction.atomic():
|
|
870
|
-
address_records = self.
|
|
918
|
+
address_records = self.records.filter(
|
|
871
919
|
ptr_record__isnull=False
|
|
872
920
|
).prefetch_related("ptr_record")
|
|
873
921
|
for address_record in address_records:
|
|
874
922
|
address_record.ptr_record.delete()
|
|
875
923
|
|
|
876
|
-
ptr_records = self.
|
|
924
|
+
ptr_records = self.records.filter(address_record__isnull=False)
|
|
877
925
|
update_records = list(
|
|
878
926
|
Record.objects.filter(ptr_record__in=ptr_records).values_list(
|
|
879
927
|
"pk", flat=True
|
|
@@ -894,12 +942,12 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
894
942
|
cname_zone.update_soa_record()
|
|
895
943
|
|
|
896
944
|
rfc2317_child_zones = list(
|
|
897
|
-
self.rfc2317_child_zones.
|
|
945
|
+
self.rfc2317_child_zones.values_list("pk", flat=True)
|
|
898
946
|
)
|
|
899
947
|
|
|
900
948
|
ipam_ip_addresses = list(
|
|
901
949
|
IPAddress.objects.filter(
|
|
902
|
-
netbox_dns_records__in=self.
|
|
950
|
+
netbox_dns_records__in=self.records.filter(
|
|
903
951
|
ipam_ip_address__isnull=False
|
|
904
952
|
)
|
|
905
953
|
)
|
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>
|