netbox-plugin-dns 1.1.0__py3-none-any.whl → 1.1.0b2__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 +6 -8
- netbox_dns/api/nested_serializers.py +16 -17
- netbox_dns/api/serializers.py +1 -1
- netbox_dns/api/serializers_/{registration_contact.py → contact.py} +5 -5
- netbox_dns/api/serializers_/record.py +0 -1
- netbox_dns/api/serializers_/zone.py +5 -5
- netbox_dns/api/serializers_/zone_template.py +5 -5
- netbox_dns/api/urls.py +2 -2
- netbox_dns/api/views.py +35 -9
- netbox_dns/fields/ipam.py +3 -0
- netbox_dns/filtersets/__init__.py +1 -1
- netbox_dns/filtersets/{registration_contact.py → contact.py} +4 -4
- netbox_dns/filtersets/record.py +1 -1
- netbox_dns/filtersets/zone.py +15 -15
- netbox_dns/filtersets/zone_template.py +15 -15
- netbox_dns/forms/__init__.py +1 -1
- netbox_dns/forms/{registration_contact.py → contact.py} +16 -16
- netbox_dns/forms/view.py +4 -91
- netbox_dns/forms/zone.py +17 -22
- netbox_dns/forms/zone_template.py +13 -13
- netbox_dns/graphql/__init__.py +2 -2
- netbox_dns/graphql/filters.py +5 -5
- netbox_dns/graphql/schema.py +44 -24
- netbox_dns/graphql/types.py +12 -40
- netbox_dns/management/commands/{setup_dnssync.py → setup_autodns.py} +15 -19
- netbox_dns/migrations/{0008_view_prefixes.py → 0007_view_prefixes.py} +1 -1
- netbox_dns/models/__init__.py +1 -1
- netbox_dns/models/{registration_contact.py → contact.py} +9 -15
- netbox_dns/models/nameserver.py +3 -8
- netbox_dns/models/record.py +29 -60
- netbox_dns/models/record_template.py +1 -4
- netbox_dns/models/registrar.py +1 -7
- netbox_dns/models/view.py +2 -9
- netbox_dns/models/zone.py +28 -51
- netbox_dns/models/zone_template.py +9 -12
- netbox_dns/navigation.py +7 -7
- netbox_dns/signals/{ipam_dnssync.py → ipam_autodns.py} +24 -78
- netbox_dns/tables/__init__.py +1 -1
- netbox_dns/tables/{registration_contact.py → contact.py} +6 -5
- netbox_dns/tables/nameserver.py +7 -1
- netbox_dns/tables/record.py +30 -43
- netbox_dns/tables/record_template.py +17 -0
- netbox_dns/tables/registrar.py +2 -0
- netbox_dns/tables/view.py +8 -1
- netbox_dns/tables/zone.py +15 -0
- netbox_dns/tables/zone_template.py +16 -2
- netbox_dns/template_content.py +2 -14
- netbox_dns/templates/netbox_dns/{registrationcontact.html → contact.html} +1 -1
- netbox_dns/templates/netbox_dns/view.html +1 -1
- netbox_dns/urls/__init__.py +2 -2
- netbox_dns/urls/contact.py +51 -0
- netbox_dns/urls/nameserver.py +38 -14
- netbox_dns/urls/record.py +19 -7
- netbox_dns/urls/record_template.py +27 -18
- netbox_dns/urls/registrar.py +35 -11
- netbox_dns/urls/view.py +20 -12
- netbox_dns/urls/zone.py +46 -8
- netbox_dns/urls/zone_template.py +26 -16
- netbox_dns/utilities/__init__.py +1 -1
- netbox_dns/utilities/{ipam_dnssync.py → ipam_autodns.py} +41 -80
- netbox_dns/validators/dns_name.py +0 -9
- netbox_dns/views/__init__.py +1 -1
- netbox_dns/views/contact.py +95 -0
- netbox_dns/views/nameserver.py +3 -7
- netbox_dns/views/record.py +7 -12
- netbox_dns/views/record_template.py +1 -1
- netbox_dns/views/registrar.py +1 -0
- netbox_dns/views/view.py +2 -32
- netbox_dns/views/zone.py +6 -7
- netbox_dns/views/zone_template.py +2 -2
- {netbox_plugin_dns-1.1.0.dist-info → netbox_plugin_dns-1.1.0b2.dist-info}/METADATA +1 -2
- netbox_plugin_dns-1.1.0b2.dist-info/RECORD +140 -0
- netbox_dns/management/commands/rebuild_dnssync.py +0 -18
- netbox_dns/migrations/0007_alter_ordering_options.py +0 -25
- netbox_dns/migrations/0009_rename_contact_registrationcontact.py +0 -27
- netbox_dns/tables/ipam_dnssync.py +0 -11
- netbox_dns/templates/netbox_dns/view/button.html +0 -9
- netbox_dns/templates/netbox_dns/view/prefix.html +0 -41
- netbox_dns/urls/registration_contact.py +0 -60
- netbox_dns/views/registration_contact.py +0 -94
- netbox_plugin_dns-1.1.0.dist-info/RECORD +0 -146
- {netbox_plugin_dns-1.1.0.dist-info → netbox_plugin_dns-1.1.0b2.dist-info}/LICENSE +0 -0
- {netbox_plugin_dns-1.1.0.dist-info → netbox_plugin_dns-1.1.0b2.dist-info}/WHEEL +0 -0
netbox_dns/models/zone.py
CHANGED
|
@@ -3,8 +3,9 @@ from math import ceil
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
|
|
5
5
|
from dns import name as dns_name
|
|
6
|
-
from dns.exception import DNSException
|
|
7
6
|
from dns.rdtypes.ANY import SOA
|
|
7
|
+
from dns.exception import DNSException
|
|
8
|
+
|
|
8
9
|
from django.core.validators import (
|
|
9
10
|
MinValueValidator,
|
|
10
11
|
MaxValueValidator,
|
|
@@ -18,7 +19,6 @@ from django.dispatch import receiver
|
|
|
18
19
|
from django.conf import settings
|
|
19
20
|
|
|
20
21
|
from netbox.models import NetBoxModel
|
|
21
|
-
from netbox.models.features import ContactsMixin
|
|
22
22
|
from netbox.search import SearchIndex, register_search
|
|
23
23
|
from netbox.plugins.utils import get_plugin_config
|
|
24
24
|
from utilities.querysets import RestrictedQuerySet
|
|
@@ -36,7 +36,7 @@ from netbox_dns.utilities import (
|
|
|
36
36
|
NameFormatError,
|
|
37
37
|
)
|
|
38
38
|
from netbox_dns.validators import (
|
|
39
|
-
|
|
39
|
+
validate_fqdn,
|
|
40
40
|
validate_domain_name,
|
|
41
41
|
)
|
|
42
42
|
from netbox_dns.mixins import ObjectModificationMixin
|
|
@@ -69,7 +69,7 @@ class ZoneManager(models.Manager.from_queryset(RestrictedQuerySet)):
|
|
|
69
69
|
)
|
|
70
70
|
|
|
71
71
|
|
|
72
|
-
class Zone(ObjectModificationMixin,
|
|
72
|
+
class Zone(ObjectModificationMixin, NetBoxModel):
|
|
73
73
|
ACTIVE_STATUS_LIST = (ZoneStatusChoices.STATUS_ACTIVE,)
|
|
74
74
|
|
|
75
75
|
def __init__(self, *args, **kwargs):
|
|
@@ -78,7 +78,6 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
78
78
|
super().__init__(*args, **kwargs)
|
|
79
79
|
|
|
80
80
|
self._soa_serial_dirty = False
|
|
81
|
-
self._ip_addresses_checked = False
|
|
82
81
|
|
|
83
82
|
view = models.ForeignKey(
|
|
84
83
|
to="View",
|
|
@@ -192,7 +191,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
192
191
|
null=True,
|
|
193
192
|
)
|
|
194
193
|
registrant = models.ForeignKey(
|
|
195
|
-
to="
|
|
194
|
+
to="Contact",
|
|
196
195
|
on_delete=models.SET_NULL,
|
|
197
196
|
verbose_name="Registrant",
|
|
198
197
|
help_text="The owner of the domain",
|
|
@@ -200,7 +199,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
200
199
|
null=True,
|
|
201
200
|
)
|
|
202
201
|
admin_c = models.ForeignKey(
|
|
203
|
-
to="
|
|
202
|
+
to="Contact",
|
|
204
203
|
on_delete=models.SET_NULL,
|
|
205
204
|
verbose_name="Admin Contact",
|
|
206
205
|
related_name="admin_c_zones",
|
|
@@ -209,16 +208,16 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
209
208
|
null=True,
|
|
210
209
|
)
|
|
211
210
|
tech_c = models.ForeignKey(
|
|
212
|
-
to="
|
|
211
|
+
to="Contact",
|
|
213
212
|
on_delete=models.SET_NULL,
|
|
214
|
-
verbose_name="
|
|
213
|
+
verbose_name="Tech Contact",
|
|
215
214
|
related_name="tech_c_zones",
|
|
216
215
|
help_text="The technical contact for the domain",
|
|
217
216
|
blank=True,
|
|
218
217
|
null=True,
|
|
219
218
|
)
|
|
220
219
|
billing_c = models.ForeignKey(
|
|
221
|
-
to="
|
|
220
|
+
to="Contact",
|
|
222
221
|
on_delete=models.SET_NULL,
|
|
223
222
|
verbose_name="Billing Contact",
|
|
224
223
|
related_name="billing_c_zones",
|
|
@@ -249,7 +248,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
249
248
|
|
|
250
249
|
objects = ZoneManager()
|
|
251
250
|
|
|
252
|
-
clone_fields =
|
|
251
|
+
clone_fields = [
|
|
253
252
|
"view",
|
|
254
253
|
"name",
|
|
255
254
|
"status",
|
|
@@ -263,12 +262,9 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
263
262
|
"soa_expire",
|
|
264
263
|
"soa_minimum",
|
|
265
264
|
"description",
|
|
266
|
-
|
|
265
|
+
]
|
|
267
266
|
|
|
268
267
|
class Meta:
|
|
269
|
-
verbose_name = "Zone"
|
|
270
|
-
verbose_name_plural = "Zones"
|
|
271
|
-
|
|
272
268
|
ordering = (
|
|
273
269
|
"view",
|
|
274
270
|
"name",
|
|
@@ -284,7 +280,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
284
280
|
else:
|
|
285
281
|
try:
|
|
286
282
|
name = dns_name.from_text(self.name, origin=None).to_unicode()
|
|
287
|
-
except
|
|
283
|
+
except dns_name.IDNAException:
|
|
288
284
|
name = self.name
|
|
289
285
|
|
|
290
286
|
try:
|
|
@@ -312,14 +308,6 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
312
308
|
def soa_serial_dirty(self, soa_serial_dirty):
|
|
313
309
|
self._soa_serial_dirty = soa_serial_dirty
|
|
314
310
|
|
|
315
|
-
@property
|
|
316
|
-
def ip_addresses_checked(self):
|
|
317
|
-
return self._ip_addresses_checked
|
|
318
|
-
|
|
319
|
-
@ip_addresses_checked.setter
|
|
320
|
-
def ip_addresses_checked(self, ip_addresses_checked):
|
|
321
|
-
self._ip_addresses_checked = ip_addresses_checked
|
|
322
|
-
|
|
323
311
|
@property
|
|
324
312
|
def display_name(self):
|
|
325
313
|
return name_to_unicode(self.name)
|
|
@@ -633,7 +621,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
633
621
|
raise ValidationError("soa_rname not set and no default value defined")
|
|
634
622
|
try:
|
|
635
623
|
dns_name.from_text(self.soa_rname, origin=dns_name.root)
|
|
636
|
-
|
|
624
|
+
validate_fqdn(self.soa_rname)
|
|
637
625
|
except (DNSException, ValidationError) as exc:
|
|
638
626
|
raise ValidationError(
|
|
639
627
|
{
|
|
@@ -653,7 +641,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
653
641
|
old_zone = Zone.objects.get(pk=self.pk)
|
|
654
642
|
if not self.soa_serial_auto:
|
|
655
643
|
self.check_soa_serial_increment(old_zone.soa_serial, self.soa_serial)
|
|
656
|
-
|
|
644
|
+
else:
|
|
657
645
|
try:
|
|
658
646
|
self.check_soa_serial_increment(
|
|
659
647
|
old_zone.soa_serial, self.get_auto_serial()
|
|
@@ -665,26 +653,19 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
665
653
|
}
|
|
666
654
|
)
|
|
667
655
|
|
|
668
|
-
if
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
or old_zone.view != self.view
|
|
672
|
-
):
|
|
673
|
-
ip_addresses = IPAddress.objects.filter(
|
|
674
|
-
netbox_dns_records__in=self.record_set.filter(
|
|
656
|
+
if old_zone.name != self.name or old_zone.view != self.view:
|
|
657
|
+
update_ip_addresses = IPAddress.objects.filter(
|
|
658
|
+
pk__in=self.record_set.filter(
|
|
675
659
|
ipam_ip_address__isnull=False
|
|
676
|
-
)
|
|
660
|
+
).values_list("ipam_ip_address", flat=True)
|
|
677
661
|
)
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
for ip_address in ip_addresses.distinct():
|
|
662
|
+
update_ip_addresses |= get_ip_addresses_by_zone(self)
|
|
663
|
+
for ip_address in update_ip_addresses:
|
|
681
664
|
try:
|
|
682
665
|
check_dns_records(ip_address, zone=self)
|
|
683
666
|
except ValidationError as exc:
|
|
684
667
|
raise ValidationError(exc.messages)
|
|
685
668
|
|
|
686
|
-
self.ip_addresses_checked = True
|
|
687
|
-
|
|
688
669
|
if self.is_reverse_zone:
|
|
689
670
|
self.arpa_network = self.network_from_name
|
|
690
671
|
|
|
@@ -799,13 +780,12 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
799
780
|
|
|
800
781
|
elif changed_fields is not None and {"name", "view", "status"} & changed_fields:
|
|
801
782
|
for address_record in self.record_set.filter(
|
|
802
|
-
type__in=(RecordTypeChoices.A, RecordTypeChoices.AAAA)
|
|
803
|
-
ipam_ip_address__isnull=True,
|
|
783
|
+
type__in=(RecordTypeChoices.A, RecordTypeChoices.AAAA)
|
|
804
784
|
):
|
|
805
785
|
address_record.save(update_fields=["ptr_record"])
|
|
806
786
|
|
|
807
787
|
if changed_fields is not None and "name" in changed_fields:
|
|
808
|
-
for _record in self.record_set.
|
|
788
|
+
for _record in self.record_set.all():
|
|
809
789
|
_record.save(
|
|
810
790
|
update_fields=["fqdn"],
|
|
811
791
|
save_zone_serial=False,
|
|
@@ -814,14 +794,13 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
814
794
|
)
|
|
815
795
|
|
|
816
796
|
if changed_fields is None or {"name", "view"} & changed_fields:
|
|
817
|
-
|
|
818
|
-
|
|
797
|
+
update_ip_addresses = IPAddress.objects.filter(
|
|
798
|
+
pk__in=self.record_set.filter(
|
|
819
799
|
ipam_ip_address__isnull=False
|
|
820
|
-
)
|
|
800
|
+
).values_list("ipam_ip_address", flat=True)
|
|
821
801
|
)
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
for ip_address in ip_addresses.distinct():
|
|
802
|
+
update_ip_addresses |= get_ip_addresses_by_zone(self)
|
|
803
|
+
for ip_address in update_ip_addresses:
|
|
825
804
|
update_dns_records(ip_address)
|
|
826
805
|
|
|
827
806
|
self.save_soa_serial()
|
|
@@ -864,9 +843,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
864
843
|
netbox_dns_records__in=self.record_set.filter(
|
|
865
844
|
ipam_ip_address__isnull=False
|
|
866
845
|
)
|
|
867
|
-
)
|
|
868
|
-
.distinct()
|
|
869
|
-
.values_list("pk", flat=True)
|
|
846
|
+
).values_list("pk", flat=True)
|
|
870
847
|
)
|
|
871
848
|
|
|
872
849
|
super().delete(*args, **kwargs)
|
|
@@ -47,7 +47,7 @@ class ZoneTemplate(NetBoxModel):
|
|
|
47
47
|
null=True,
|
|
48
48
|
)
|
|
49
49
|
registrant = models.ForeignKey(
|
|
50
|
-
to="
|
|
50
|
+
to="Contact",
|
|
51
51
|
on_delete=models.SET_NULL,
|
|
52
52
|
related_name="+",
|
|
53
53
|
help_text="The owner of the domain",
|
|
@@ -55,7 +55,7 @@ class ZoneTemplate(NetBoxModel):
|
|
|
55
55
|
null=True,
|
|
56
56
|
)
|
|
57
57
|
admin_c = models.ForeignKey(
|
|
58
|
-
to="
|
|
58
|
+
to="Contact",
|
|
59
59
|
on_delete=models.SET_NULL,
|
|
60
60
|
verbose_name="Admin contact",
|
|
61
61
|
related_name="+",
|
|
@@ -64,7 +64,7 @@ class ZoneTemplate(NetBoxModel):
|
|
|
64
64
|
null=True,
|
|
65
65
|
)
|
|
66
66
|
tech_c = models.ForeignKey(
|
|
67
|
-
to="
|
|
67
|
+
to="Contact",
|
|
68
68
|
on_delete=models.SET_NULL,
|
|
69
69
|
verbose_name="Tech contact",
|
|
70
70
|
related_name="+",
|
|
@@ -73,7 +73,7 @@ class ZoneTemplate(NetBoxModel):
|
|
|
73
73
|
null=True,
|
|
74
74
|
)
|
|
75
75
|
billing_c = models.ForeignKey(
|
|
76
|
-
to="
|
|
76
|
+
to="Contact",
|
|
77
77
|
on_delete=models.SET_NULL,
|
|
78
78
|
verbose_name="Billing contact",
|
|
79
79
|
related_name="+",
|
|
@@ -82,7 +82,7 @@ class ZoneTemplate(NetBoxModel):
|
|
|
82
82
|
null=True,
|
|
83
83
|
)
|
|
84
84
|
|
|
85
|
-
clone_fields =
|
|
85
|
+
clone_fields = [
|
|
86
86
|
"description",
|
|
87
87
|
"nameservers",
|
|
88
88
|
"record_templates",
|
|
@@ -92,22 +92,19 @@ class ZoneTemplate(NetBoxModel):
|
|
|
92
92
|
"admin_c",
|
|
93
93
|
"tech_c",
|
|
94
94
|
"billing_c",
|
|
95
|
-
|
|
95
|
+
]
|
|
96
96
|
|
|
97
|
-
template_fields =
|
|
97
|
+
template_fields = [
|
|
98
98
|
"tenant",
|
|
99
99
|
"registrar",
|
|
100
100
|
"registrant",
|
|
101
101
|
"admin_c",
|
|
102
102
|
"tech_c",
|
|
103
103
|
"billing_c",
|
|
104
|
-
|
|
104
|
+
]
|
|
105
105
|
|
|
106
106
|
class Meta:
|
|
107
|
-
|
|
108
|
-
verbose_name_plural = "Zone Templates"
|
|
109
|
-
|
|
110
|
-
ordering = ("name",)
|
|
107
|
+
ordering = ["name"]
|
|
111
108
|
|
|
112
109
|
def __str__(self):
|
|
113
110
|
return str(self.name)
|
netbox_dns/navigation.py
CHANGED
|
@@ -151,21 +151,21 @@ registrar_menu_item = PluginMenuItem(
|
|
|
151
151
|
)
|
|
152
152
|
|
|
153
153
|
contact_menu_item = PluginMenuItem(
|
|
154
|
-
link="plugins:netbox_dns:
|
|
155
|
-
link_text="
|
|
156
|
-
permissions=["netbox_dns.
|
|
154
|
+
link="plugins:netbox_dns:contact_list",
|
|
155
|
+
link_text="Contacts",
|
|
156
|
+
permissions=["netbox_dns.view_contact"],
|
|
157
157
|
buttons=(
|
|
158
158
|
PluginMenuButton(
|
|
159
|
-
"plugins:netbox_dns:
|
|
159
|
+
"plugins:netbox_dns:contact_add",
|
|
160
160
|
"Add",
|
|
161
161
|
"mdi mdi-plus-thick",
|
|
162
|
-
permissions=["netbox_dns.
|
|
162
|
+
permissions=["netbox_dns.add_contact"],
|
|
163
163
|
),
|
|
164
164
|
PluginMenuButton(
|
|
165
|
-
"plugins:netbox_dns:
|
|
165
|
+
"plugins:netbox_dns:contact_import",
|
|
166
166
|
"Import",
|
|
167
167
|
"mdi mdi-upload",
|
|
168
|
-
permissions=["netbox_dns.
|
|
168
|
+
permissions=["netbox_dns.add_contact"],
|
|
169
169
|
),
|
|
170
170
|
),
|
|
171
171
|
)
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from netaddr import IPNetwork
|
|
2
2
|
|
|
3
|
-
from django.conf import settings
|
|
4
3
|
from django.dispatch import receiver
|
|
5
4
|
from django.db.models.signals import pre_delete, pre_save, post_save, m2m_changed
|
|
6
5
|
from django.core.exceptions import ValidationError
|
|
@@ -18,55 +17,21 @@ from netbox_dns.utilities import (
|
|
|
18
17
|
delete_dns_records,
|
|
19
18
|
get_views_by_prefix,
|
|
20
19
|
get_ip_addresses_by_prefix,
|
|
20
|
+
get_ip_addresses_by_view,
|
|
21
21
|
)
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
AUTODNS_CUSTOM_FIELDS = {
|
|
24
24
|
"ipaddress_dns_disabled": False,
|
|
25
25
|
"ipaddress_dns_record_ttl": None,
|
|
26
26
|
"ipaddress_dns_record_disable_ptr": False,
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
IPADDRESS_ACTIVE_STATUS = settings.PLUGINS_CONFIG["netbox_dns"][
|
|
30
|
-
"dnssync_ipaddress_active_status"
|
|
31
|
-
]
|
|
32
|
-
ENFORCE_UNIQUE_RECORDS = settings.PLUGINS_CONFIG["netbox_dns"]["enforce_unique_records"]
|
|
33
|
-
|
|
34
29
|
|
|
35
30
|
@receiver(post_clean, sender=IPAddress)
|
|
36
|
-
def
|
|
31
|
+
def ipam_autodns_ipaddress_post_clean(instance, **kwargs):
|
|
37
32
|
if not isinstance(instance.address, IPNetwork):
|
|
38
33
|
return
|
|
39
34
|
|
|
40
|
-
if instance.custom_field_data.get("ipaddress_dns_disabled"):
|
|
41
|
-
return
|
|
42
|
-
|
|
43
|
-
# +
|
|
44
|
-
# Check for uniqueness of IP address and dns_name. If unique records are
|
|
45
|
-
# enforced, report an error when trying to create the same IP address with
|
|
46
|
-
# the same dns_name. Ignore existing IP addresses that have their CF
|
|
47
|
-
# "ipaddress_dns_disabled" set to "True".
|
|
48
|
-
# -
|
|
49
|
-
duplicate_addresses = IPAddress.objects.filter(
|
|
50
|
-
address=instance.address,
|
|
51
|
-
vrf=instance.vrf,
|
|
52
|
-
dns_name=instance.dns_name,
|
|
53
|
-
status__in=IPADDRESS_ACTIVE_STATUS,
|
|
54
|
-
)
|
|
55
|
-
if instance.pk is not None:
|
|
56
|
-
duplicate_addresses = duplicate_addresses.exclude(pk=instance.pk)
|
|
57
|
-
|
|
58
|
-
if ENFORCE_UNIQUE_RECORDS and instance.status in IPADDRESS_ACTIVE_STATUS:
|
|
59
|
-
for ip_address in duplicate_addresses.only("custom_field_data"):
|
|
60
|
-
if not ip_address.custom_field_data.get("ipaddress_dns_disabled"):
|
|
61
|
-
raise ValidationError(
|
|
62
|
-
{
|
|
63
|
-
"dns_name": "Unique DNS records are enforced and there is already "
|
|
64
|
-
f"an active IP address {instance.address} with DNS name {instance.dns_name}. "
|
|
65
|
-
"Plesase choose a different name or disable record creation for this "
|
|
66
|
-
"IP address."
|
|
67
|
-
}
|
|
68
|
-
)
|
|
69
|
-
|
|
70
35
|
# +
|
|
71
36
|
# Check NetBox DNS record permission for changes to IPAddress custom fields
|
|
72
37
|
#
|
|
@@ -77,26 +42,24 @@ def ipam_dnssync_ipaddress_post_clean(instance, **kwargs):
|
|
|
77
42
|
instance.pk is not None
|
|
78
43
|
and any(
|
|
79
44
|
(
|
|
80
|
-
cf_data.get(cf
|
|
81
|
-
!= IPAddress.objects.get(pk=instance.pk).custom_field_data.get(
|
|
82
|
-
|
|
83
|
-
)
|
|
84
|
-
for cf, cf_default in DNSSYNC_CUSTOM_FIELDS.items()
|
|
45
|
+
cf_data.get(cf)
|
|
46
|
+
!= IPAddress.objects.get(pk=instance.pk).custom_field_data.get(cf)
|
|
47
|
+
for cf in AUTODNS_CUSTOM_FIELDS.keys()
|
|
85
48
|
)
|
|
86
49
|
)
|
|
87
|
-
and not check_record_permission()
|
|
50
|
+
and not check_record_permission(request)
|
|
88
51
|
) or (
|
|
89
52
|
instance.pk is None
|
|
90
53
|
and any(
|
|
91
54
|
(
|
|
92
|
-
cf_data.get(cf
|
|
93
|
-
for cf, cf_default in
|
|
55
|
+
cf_data.get(cf) != cf_default
|
|
56
|
+
for cf, cf_default in AUTODNS_CUSTOM_FIELDS.items()
|
|
94
57
|
)
|
|
95
58
|
)
|
|
96
|
-
and not check_record_permission(change=False, delete=False)
|
|
59
|
+
and not check_record_permission(request, change=False, delete=False)
|
|
97
60
|
):
|
|
98
61
|
raise ValidationError(
|
|
99
|
-
f"User '{request.user}' is not allowed to alter
|
|
62
|
+
f"User '{request.user}' is not allowed to alter AutoDNS custom fields"
|
|
100
63
|
)
|
|
101
64
|
|
|
102
65
|
try:
|
|
@@ -106,51 +69,38 @@ def ipam_dnssync_ipaddress_post_clean(instance, **kwargs):
|
|
|
106
69
|
|
|
107
70
|
|
|
108
71
|
@receiver(pre_delete, sender=IPAddress)
|
|
109
|
-
def
|
|
72
|
+
def ipam_autodns_ipaddress_pre_delete(instance, **kwargs):
|
|
110
73
|
delete_dns_records(instance)
|
|
111
74
|
|
|
112
75
|
|
|
113
76
|
@receiver(pre_save, sender=IPAddress)
|
|
114
|
-
def
|
|
77
|
+
def ipam_autodns_ipaddress_pre_save(instance, **kwargs):
|
|
115
78
|
check_dns_records(instance)
|
|
116
79
|
|
|
117
80
|
|
|
118
81
|
@receiver(post_save, sender=IPAddress)
|
|
119
|
-
def
|
|
82
|
+
def ipam_autodns_ipaddress_post_save(instance, **kwargs):
|
|
120
83
|
update_dns_records(instance)
|
|
121
84
|
|
|
122
85
|
|
|
123
86
|
@receiver(pre_save, sender=Prefix)
|
|
124
|
-
def
|
|
87
|
+
def ipam_autodns_prefix_pre_save(instance, **kwargs):
|
|
125
88
|
"""
|
|
126
89
|
Changes that modify the prefix hierarchy cannot be validated properly before
|
|
127
|
-
commiting them. So the solution in this case is to
|
|
128
|
-
|
|
90
|
+
commiting them. So the solution in this case is to remove a prefix whose
|
|
91
|
+
VRF or network has changed from all views it currently is assigned to.
|
|
129
92
|
"""
|
|
130
|
-
request = current_request.get()
|
|
131
|
-
|
|
132
93
|
if instance.pk is None or not instance.netbox_dns_views.exists():
|
|
133
94
|
return
|
|
134
95
|
|
|
135
|
-
saved_prefix = Prefix.objects.
|
|
136
|
-
pk=instance.pk
|
|
137
|
-
)
|
|
96
|
+
saved_prefix = Prefix.objects.get(pk=instance.pk)
|
|
138
97
|
if saved_prefix.prefix != instance.prefix or saved_prefix.vrf != instance.vrf:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
raise AbortRequest(
|
|
142
|
-
f"This prefix is currently assigned to the following DNS views: {dns_views}"
|
|
143
|
-
f"Please deassign it from these views before making changes to the prefix "
|
|
144
|
-
f"or VRF."
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
raise ValidationError(
|
|
148
|
-
f"Prefix is assigned to DNS views {dns_views}. Prefix and VRF must not be changed"
|
|
149
|
-
)
|
|
98
|
+
for view in saved_prefix.netbox_dns_views.all():
|
|
99
|
+
view.prefixes.remove(saved_prefix)
|
|
150
100
|
|
|
151
101
|
|
|
152
102
|
@receiver(pre_delete, sender=Prefix)
|
|
153
|
-
def
|
|
103
|
+
def ipam_autodns_prefix_pre_delete(instance, **kwargs):
|
|
154
104
|
parent = instance.get_parents().last()
|
|
155
105
|
request = current_request.get()
|
|
156
106
|
|
|
@@ -194,7 +144,7 @@ def ipam_dnssync_prefix_pre_delete(instance, **kwargs):
|
|
|
194
144
|
|
|
195
145
|
|
|
196
146
|
@receiver(m2m_changed, sender=_view.View.prefixes.through)
|
|
197
|
-
def
|
|
147
|
+
def ipam_autodns_view_prefix_changed(**kwargs):
|
|
198
148
|
action = kwargs.get("action")
|
|
199
149
|
request = current_request.get()
|
|
200
150
|
|
|
@@ -215,10 +165,6 @@ def ipam_dnssync_view_prefix_changed(**kwargs):
|
|
|
215
165
|
return
|
|
216
166
|
|
|
217
167
|
check_view = action != "post_remove"
|
|
218
|
-
|
|
219
|
-
ip_addresses = IPAddress.objects.none()
|
|
220
168
|
for prefix in Prefix.objects.filter(pk__in=kwargs.get("pk_set")):
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
for ip_address in ip_addresses.distinct():
|
|
224
|
-
update_dns_records(ip_address)
|
|
169
|
+
for ip_address in get_ip_addresses_by_prefix(prefix, check_view=check_view):
|
|
170
|
+
update_dns_records(ip_address)
|
netbox_dns/tables/__init__.py
CHANGED
|
@@ -2,23 +2,24 @@ import django_tables2 as tables
|
|
|
2
2
|
|
|
3
3
|
from netbox.tables import NetBoxTable, TagColumn
|
|
4
4
|
|
|
5
|
-
from netbox_dns.models import
|
|
5
|
+
from netbox_dns.models import Contact
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
__all__ = ("
|
|
8
|
+
__all__ = ("ContactTable",)
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
class
|
|
11
|
+
class ContactTable(NetBoxTable):
|
|
12
12
|
contact_id = tables.Column(
|
|
13
13
|
linkify=True,
|
|
14
14
|
)
|
|
15
15
|
tags = TagColumn(
|
|
16
|
-
url_name="plugins:netbox_dns:
|
|
16
|
+
url_name="plugins:netbox_dns:contact_list",
|
|
17
17
|
)
|
|
18
18
|
|
|
19
19
|
class Meta(NetBoxTable.Meta):
|
|
20
|
-
model =
|
|
20
|
+
model = Contact
|
|
21
21
|
fields = (
|
|
22
|
+
"contact_id",
|
|
22
23
|
"name",
|
|
23
24
|
"description",
|
|
24
25
|
"organization",
|
netbox_dns/tables/nameserver.py
CHANGED
|
@@ -22,7 +22,13 @@ class NameServerTable(TenancyColumnsMixin, NetBoxTable):
|
|
|
22
22
|
|
|
23
23
|
class Meta(NetBoxTable.Meta):
|
|
24
24
|
model = NameServer
|
|
25
|
-
fields = (
|
|
25
|
+
fields = (
|
|
26
|
+
"name",
|
|
27
|
+
"description",
|
|
28
|
+
"tags",
|
|
29
|
+
"tenant",
|
|
30
|
+
"tenant_group",
|
|
31
|
+
)
|
|
26
32
|
default_columns = (
|
|
27
33
|
"name",
|
|
28
34
|
"tags",
|
netbox_dns/tables/record.py
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import django_tables2 as tables
|
|
2
|
-
from django.utils.html import format_html
|
|
3
|
-
|
|
4
2
|
|
|
5
3
|
from netbox.tables import (
|
|
6
4
|
NetBoxTable,
|
|
@@ -13,10 +11,6 @@ from tenancy.tables import TenancyColumnsMixin
|
|
|
13
11
|
from netbox_dns.models import Record
|
|
14
12
|
from netbox_dns.utilities import value_to_unicode
|
|
15
13
|
|
|
16
|
-
import logging
|
|
17
|
-
|
|
18
|
-
logger = logging.getLogger("netbox_dns")
|
|
19
|
-
|
|
20
14
|
|
|
21
15
|
__all__ = (
|
|
22
16
|
"RecordTable",
|
|
@@ -29,18 +23,10 @@ class RecordBaseTable(TenancyColumnsMixin, NetBoxTable):
|
|
|
29
23
|
zone = tables.Column(
|
|
30
24
|
linkify=True,
|
|
31
25
|
)
|
|
32
|
-
view = tables.Column(
|
|
33
|
-
accessor="zone__view",
|
|
34
|
-
linkify=True,
|
|
35
|
-
)
|
|
36
26
|
type = tables.Column()
|
|
37
27
|
name = tables.Column(
|
|
38
28
|
linkify=True,
|
|
39
29
|
)
|
|
40
|
-
fqdn = tables.Column(
|
|
41
|
-
verbose_name="FQDN",
|
|
42
|
-
linkify=True,
|
|
43
|
-
)
|
|
44
30
|
value = tables.TemplateColumn(
|
|
45
31
|
template_code="{{ value|truncatechars:64 }}",
|
|
46
32
|
)
|
|
@@ -79,8 +65,20 @@ class RecordTable(RecordBaseTable):
|
|
|
79
65
|
class Meta(NetBoxTable.Meta):
|
|
80
66
|
model = Record
|
|
81
67
|
fields = (
|
|
68
|
+
"name",
|
|
69
|
+
"zone",
|
|
70
|
+
"ttl",
|
|
71
|
+
"type",
|
|
72
|
+
"value",
|
|
73
|
+
"unicode_value",
|
|
82
74
|
"status",
|
|
75
|
+
"disable_ptr",
|
|
76
|
+
"ptr_record",
|
|
77
|
+
"tags",
|
|
78
|
+
"active",
|
|
83
79
|
"description",
|
|
80
|
+
"tenant",
|
|
81
|
+
"tenant_group",
|
|
84
82
|
)
|
|
85
83
|
default_columns = (
|
|
86
84
|
"name",
|
|
@@ -102,16 +100,21 @@ class ManagedRecordTable(RecordBaseTable):
|
|
|
102
100
|
verbose_name="IPAM IP Address",
|
|
103
101
|
linkify=True,
|
|
104
102
|
)
|
|
105
|
-
related_ip_address = tables.Column(
|
|
106
|
-
verbose_name="Related IP Address",
|
|
107
|
-
empty_values=(),
|
|
108
|
-
orderable=False,
|
|
109
|
-
)
|
|
110
103
|
actions = ActionsColumn(actions=("changelog",))
|
|
111
104
|
|
|
112
105
|
class Meta(NetBoxTable.Meta):
|
|
113
106
|
model = Record
|
|
114
|
-
fields = (
|
|
107
|
+
fields = (
|
|
108
|
+
"name",
|
|
109
|
+
"zone",
|
|
110
|
+
"ttl",
|
|
111
|
+
"type",
|
|
112
|
+
"value",
|
|
113
|
+
"unicode_value",
|
|
114
|
+
"address_record",
|
|
115
|
+
"ipam_ip_address",
|
|
116
|
+
"active",
|
|
117
|
+
)
|
|
115
118
|
default_columns = (
|
|
116
119
|
"name",
|
|
117
120
|
"zone",
|
|
@@ -121,35 +124,19 @@ class ManagedRecordTable(RecordBaseTable):
|
|
|
121
124
|
"active",
|
|
122
125
|
)
|
|
123
126
|
|
|
124
|
-
def render_related_ip_address(self, record):
|
|
125
|
-
if record.ipam_ip_address is not None:
|
|
126
|
-
address = record.ipam_ip_address
|
|
127
|
-
elif (
|
|
128
|
-
hasattr(record, "address_record")
|
|
129
|
-
and record.address_record.ipam_ip_address is not None
|
|
130
|
-
):
|
|
131
|
-
address = record.address_record.ipam_ip_address
|
|
132
|
-
else:
|
|
133
|
-
return format_html("—")
|
|
134
|
-
|
|
135
|
-
return format_html(f"<a href='{address.get_absolute_url()}'>{address}</a>")
|
|
136
|
-
|
|
137
|
-
def value_related_ip_address(self, record):
|
|
138
|
-
if record.ipam_ip_address is not None:
|
|
139
|
-
return record.ipam_ip_address
|
|
140
|
-
elif (
|
|
141
|
-
hasattr(record, "address_record")
|
|
142
|
-
and record.address_record.ipam_ip_address is not None
|
|
143
|
-
):
|
|
144
|
-
return record.address_record.ipam_ip_address
|
|
145
|
-
|
|
146
127
|
|
|
147
128
|
class RelatedRecordTable(RecordBaseTable):
|
|
148
129
|
actions = ActionsColumn(actions=())
|
|
149
130
|
|
|
150
131
|
class Meta(NetBoxTable.Meta):
|
|
151
132
|
model = Record
|
|
152
|
-
fields = (
|
|
133
|
+
fields = (
|
|
134
|
+
"name",
|
|
135
|
+
"zone",
|
|
136
|
+
"type",
|
|
137
|
+
"value",
|
|
138
|
+
"unicode_value",
|
|
139
|
+
)
|
|
153
140
|
default_columns = (
|
|
154
141
|
"name",
|
|
155
142
|
"zone",
|