netbox-plugin-dns 0.22.3__py3-none-any.whl → 0.22.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 +5 -4
- netbox_dns/api/serializers.py +1 -0
- netbox_dns/filters/registrar.py +1 -0
- netbox_dns/forms/registrar.py +11 -1
- netbox_dns/forms/zone.py +39 -21
- netbox_dns/management/commands/cleanup_database.py +1 -1
- netbox_dns/management/commands/cleanup_rrset_ttl.py +62 -0
- netbox_dns/models/record.py +142 -28
- netbox_dns/models/zone.py +85 -19
- netbox_dns/signals/ipam_coupling.py +13 -1
- netbox_dns/templates/netbox_dns/registrar.html +4 -0
- netbox_dns/templates/netbox_dns/zone.html +7 -0
- {netbox_plugin_dns-0.22.3.dist-info → netbox_plugin_dns-0.22.5.dist-info}/METADATA +14 -11
- {netbox_plugin_dns-0.22.3.dist-info → netbox_plugin_dns-0.22.5.dist-info}/RECORD +16 -15
- {netbox_plugin_dns-0.22.3.dist-info → netbox_plugin_dns-0.22.5.dist-info}/WHEEL +1 -1
- {netbox_plugin_dns-0.22.3.dist-info → netbox_plugin_dns-0.22.5.dist-info}/LICENSE +0 -0
netbox_dns/__init__.py
CHANGED
|
@@ -10,7 +10,7 @@ except ImportError:
|
|
|
10
10
|
# NetBox 3.5.8
|
|
11
11
|
from extras.plugins.utils import get_plugin_config
|
|
12
12
|
|
|
13
|
-
__version__ = "0.22.
|
|
13
|
+
__version__ = "0.22.5"
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class DNSConfig(PluginConfig):
|
|
@@ -26,9 +26,9 @@ class DNSConfig(PluginConfig):
|
|
|
26
26
|
"zone_default_ttl": 86400,
|
|
27
27
|
"zone_soa_serial_auto": True,
|
|
28
28
|
"zone_soa_serial": 1,
|
|
29
|
-
"zone_soa_refresh":
|
|
29
|
+
"zone_soa_refresh": 43200,
|
|
30
30
|
"zone_soa_retry": 7200,
|
|
31
|
-
"zone_soa_expire":
|
|
31
|
+
"zone_soa_expire": 2419200,
|
|
32
32
|
"zone_soa_minimum": 3600,
|
|
33
33
|
"feature_ipam_coupling": False,
|
|
34
34
|
"tolerate_underscores_in_hostnames": False,
|
|
@@ -38,7 +38,8 @@ class DNSConfig(PluginConfig):
|
|
|
38
38
|
],
|
|
39
39
|
"tolerate_non_rfc1035_types": [],
|
|
40
40
|
"enable_root_zones": False,
|
|
41
|
-
"enforce_unique_records":
|
|
41
|
+
"enforce_unique_records": True,
|
|
42
|
+
"enforce_unique_rrset_ttl": True,
|
|
42
43
|
}
|
|
43
44
|
base_url = "netbox-dns"
|
|
44
45
|
|
netbox_dns/api/serializers.py
CHANGED
netbox_dns/filters/registrar.py
CHANGED
netbox_dns/forms/registrar.py
CHANGED
|
@@ -17,6 +17,7 @@ class RegistrarForm(NetBoxModelForm):
|
|
|
17
17
|
fields = (
|
|
18
18
|
"name",
|
|
19
19
|
"iana_id",
|
|
20
|
+
"address",
|
|
20
21
|
"referral_url",
|
|
21
22
|
"whois_server",
|
|
22
23
|
"abuse_email",
|
|
@@ -29,12 +30,18 @@ class RegistrarFilterForm(NetBoxModelFilterSetForm):
|
|
|
29
30
|
model = Registrar
|
|
30
31
|
fieldsets = (
|
|
31
32
|
(None, ("q", "name", "iana_id", "tags")),
|
|
32
|
-
(
|
|
33
|
+
(
|
|
34
|
+
"Contact",
|
|
35
|
+
("address", "referral_url", "whois_server", "abuse_email", "abuse_phone"),
|
|
36
|
+
),
|
|
33
37
|
)
|
|
34
38
|
|
|
35
39
|
name = forms.CharField(
|
|
36
40
|
required=False,
|
|
37
41
|
)
|
|
42
|
+
address = forms.CharField(
|
|
43
|
+
required=False,
|
|
44
|
+
)
|
|
38
45
|
iana_id = forms.IntegerField(
|
|
39
46
|
required=False,
|
|
40
47
|
label="IANA ID",
|
|
@@ -64,6 +71,7 @@ class RegistrarImportForm(NetBoxModelImportForm):
|
|
|
64
71
|
fields = (
|
|
65
72
|
"name",
|
|
66
73
|
"iana_id",
|
|
74
|
+
"address",
|
|
67
75
|
"referral_url",
|
|
68
76
|
"whois_server",
|
|
69
77
|
"abuse_email",
|
|
@@ -96,6 +104,7 @@ class RegistrarBulkEditForm(NetBoxModelBulkEditForm):
|
|
|
96
104
|
(
|
|
97
105
|
None,
|
|
98
106
|
(
|
|
107
|
+
"address",
|
|
99
108
|
"referral_url",
|
|
100
109
|
"whois_server",
|
|
101
110
|
"abuse_email",
|
|
@@ -105,6 +114,7 @@ class RegistrarBulkEditForm(NetBoxModelBulkEditForm):
|
|
|
105
114
|
)
|
|
106
115
|
|
|
107
116
|
nullable_fields = (
|
|
117
|
+
"address",
|
|
108
118
|
"referral_url",
|
|
109
119
|
"whois_server",
|
|
110
120
|
"abuse_email",
|
netbox_dns/forms/zone.py
CHANGED
|
@@ -15,6 +15,7 @@ from utilities.forms.fields import (
|
|
|
15
15
|
TagFilterField,
|
|
16
16
|
CSVChoiceField,
|
|
17
17
|
CSVModelChoiceField,
|
|
18
|
+
CSVModelMultipleChoiceField,
|
|
18
19
|
DynamicModelChoiceField,
|
|
19
20
|
)
|
|
20
21
|
from utilities.forms.widgets import BulkEditNullBooleanSelect, APISelect
|
|
@@ -287,6 +288,12 @@ class ZoneImportForm(NetBoxModelImportForm):
|
|
|
287
288
|
required=False,
|
|
288
289
|
help_text="Zone status",
|
|
289
290
|
)
|
|
291
|
+
nameservers = CSVModelMultipleChoiceField(
|
|
292
|
+
queryset=NameServer.objects.all(),
|
|
293
|
+
to_field_name="name",
|
|
294
|
+
required=False,
|
|
295
|
+
help_text="Name servers for the zone",
|
|
296
|
+
)
|
|
290
297
|
default_ttl = forms.IntegerField(
|
|
291
298
|
required=False,
|
|
292
299
|
help_text="Default TTL",
|
|
@@ -308,7 +315,7 @@ class ZoneImportForm(NetBoxModelImportForm):
|
|
|
308
315
|
required=False,
|
|
309
316
|
help_text="Mailbox of the zone's administrator",
|
|
310
317
|
)
|
|
311
|
-
soa_serial_auto = forms.
|
|
318
|
+
soa_serial_auto = forms.NullBooleanField(
|
|
312
319
|
required=False,
|
|
313
320
|
help_text="Generate the SOA serial",
|
|
314
321
|
)
|
|
@@ -436,26 +443,6 @@ class ZoneImportForm(NetBoxModelImportForm):
|
|
|
436
443
|
def clean_soa_rname(self):
|
|
437
444
|
return self._clean_field_with_defaults("soa_rname")
|
|
438
445
|
|
|
439
|
-
def clean_soa_serial_auto(self):
|
|
440
|
-
try:
|
|
441
|
-
return self._clean_field_with_defaults("soa_serial_auto")
|
|
442
|
-
except ValidationError:
|
|
443
|
-
if self.cleaned_data["soa_serial"] or self._get_default_value("soa_serial"):
|
|
444
|
-
return None
|
|
445
|
-
|
|
446
|
-
raise
|
|
447
|
-
|
|
448
|
-
def clean_soa_serial(self):
|
|
449
|
-
try:
|
|
450
|
-
return self._clean_field_with_defaults("soa_serial")
|
|
451
|
-
except ValidationError:
|
|
452
|
-
if self.cleaned_data["soa_serial_auto"] or self._get_default_value(
|
|
453
|
-
"soa_serial_auto"
|
|
454
|
-
):
|
|
455
|
-
return None
|
|
456
|
-
|
|
457
|
-
raise
|
|
458
|
-
|
|
459
446
|
def clean_soa_refresh(self):
|
|
460
447
|
return self._clean_field_with_defaults("soa_refresh")
|
|
461
448
|
|
|
@@ -468,6 +455,36 @@ class ZoneImportForm(NetBoxModelImportForm):
|
|
|
468
455
|
def clean_soa_minimum(self):
|
|
469
456
|
return self._clean_field_with_defaults("soa_minimum")
|
|
470
457
|
|
|
458
|
+
def clean(self, *args, **kwargs):
|
|
459
|
+
super().clean(*args, **kwargs)
|
|
460
|
+
|
|
461
|
+
soa_serial_auto = self.cleaned_data.get("soa_serial_auto")
|
|
462
|
+
soa_serial = self.cleaned_data.get("soa_serial")
|
|
463
|
+
|
|
464
|
+
if soa_serial is None:
|
|
465
|
+
soa_serial = self._get_default_value("soa_serial")
|
|
466
|
+
|
|
467
|
+
if soa_serial_auto is None:
|
|
468
|
+
if self._get_default_value("soa_serial_auto") is not None:
|
|
469
|
+
soa_serial_auto = self._get_default_value("soa_serial_auto")
|
|
470
|
+
|
|
471
|
+
elif soa_serial is not None:
|
|
472
|
+
soa_serial_auto = False
|
|
473
|
+
|
|
474
|
+
else:
|
|
475
|
+
raise ValidationError(
|
|
476
|
+
"SOA Serial Auto not set and no default value and SOA Serial available"
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
if "soa_serial_auto" in self.cleaned_data:
|
|
480
|
+
self.cleaned_data["soa_serial_auto"] = soa_serial_auto
|
|
481
|
+
|
|
482
|
+
if "soa_serial" in self.cleaned_data:
|
|
483
|
+
if soa_serial_auto:
|
|
484
|
+
self.cleaned_data["soa_serial"] = None
|
|
485
|
+
else:
|
|
486
|
+
self.cleaned_data["soa_serial"] = soa_serial
|
|
487
|
+
|
|
471
488
|
class Meta:
|
|
472
489
|
model = Zone
|
|
473
490
|
|
|
@@ -475,6 +492,7 @@ class ZoneImportForm(NetBoxModelImportForm):
|
|
|
475
492
|
"view",
|
|
476
493
|
"name",
|
|
477
494
|
"status",
|
|
495
|
+
"nameservers",
|
|
478
496
|
"default_ttl",
|
|
479
497
|
"description",
|
|
480
498
|
"soa_ttl",
|
|
@@ -141,7 +141,7 @@ def record_update_ptr_records(verbose=False):
|
|
|
141
141
|
for record in Record.objects.filter(
|
|
142
142
|
type__in=(RecordTypeChoices.A, RecordTypeChoices.AAAA)
|
|
143
143
|
):
|
|
144
|
-
record.
|
|
144
|
+
record.save(update_fields=["ptr_record"])
|
|
145
145
|
|
|
146
146
|
|
|
147
147
|
def record_update_ip_address(verbose=False):
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from django.core.management.base import BaseCommand
|
|
2
|
+
from django.db.models import Max, Min
|
|
3
|
+
|
|
4
|
+
from netbox_dns.models import (
|
|
5
|
+
Record,
|
|
6
|
+
RecordTypeChoices,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Command(BaseCommand):
|
|
11
|
+
help = "Clean up the TTLs for RRSets"
|
|
12
|
+
|
|
13
|
+
def add_arguments(self, parser):
|
|
14
|
+
min_max = parser.add_mutually_exclusive_group()
|
|
15
|
+
min_max.add_argument(
|
|
16
|
+
"--min",
|
|
17
|
+
action="store_true",
|
|
18
|
+
help="Use the minimum TTL of an RRSet for all Records",
|
|
19
|
+
)
|
|
20
|
+
min_max.add_argument(
|
|
21
|
+
"--max",
|
|
22
|
+
action="store_true",
|
|
23
|
+
help="Use the maximum TTL of an RRSet for all Records",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def handle(self, *model_names, **options):
|
|
27
|
+
self.cleanup_rrset_ttl(**options)
|
|
28
|
+
|
|
29
|
+
self.stdout.write("RRSet cleanup completed.")
|
|
30
|
+
|
|
31
|
+
def cleanup_rrset_ttl(self, **options):
|
|
32
|
+
verbose = options.get("verbosity") > 1
|
|
33
|
+
|
|
34
|
+
ttl_records = (
|
|
35
|
+
Record.objects.filter(ttl__isnull=False)
|
|
36
|
+
.exclude(type=RecordTypeChoices.SOA)
|
|
37
|
+
.exclude(type=RecordTypeChoices.PTR, maanged=True)
|
|
38
|
+
)
|
|
39
|
+
for record in ttl_records:
|
|
40
|
+
records = Record.objects.filter(
|
|
41
|
+
name=record.name,
|
|
42
|
+
zone=record.zone,
|
|
43
|
+
type=record.type,
|
|
44
|
+
).exclude(type=RecordTypeChoices.PTR, maanged=True)
|
|
45
|
+
|
|
46
|
+
if records.count() == 1:
|
|
47
|
+
if options.get("verbosity") > 2:
|
|
48
|
+
self.stdout.write(f"Ignoring single record {record.id} ({record})")
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
if options.get("max"):
|
|
52
|
+
ttl = records.aggregate(Max("ttl")).get("ttl__max")
|
|
53
|
+
else:
|
|
54
|
+
ttl = records.aggregate(Min("ttl")).get("ttl__min")
|
|
55
|
+
|
|
56
|
+
for record in records.exclude(ttl=ttl):
|
|
57
|
+
if options.get("verbosity") > 1:
|
|
58
|
+
self.stdout.write(
|
|
59
|
+
f"Updating TTL for record {record.id} ({record}) to {ttl}"
|
|
60
|
+
)
|
|
61
|
+
record.ttl = ttl
|
|
62
|
+
record.save(update_fields=["ttl"], update_rrset_ttl=False)
|
netbox_dns/models/record.py
CHANGED
|
@@ -6,7 +6,7 @@ from dns import name as dns_name
|
|
|
6
6
|
|
|
7
7
|
from django.core.exceptions import ValidationError
|
|
8
8
|
from django.db import transaction, models
|
|
9
|
-
from django.db.models import Q, ExpressionWrapper, BooleanField
|
|
9
|
+
from django.db.models import Q, ExpressionWrapper, BooleanField, Min
|
|
10
10
|
from django.db.models.functions import Length
|
|
11
11
|
from django.urls import reverse
|
|
12
12
|
|
|
@@ -38,6 +38,10 @@ from netbox_dns.validators import (
|
|
|
38
38
|
import netbox_dns.models.zone as zone
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
def min_ttl(*ttl_list):
|
|
42
|
+
return min((ttl for ttl in ttl_list if ttl is not None), default=None)
|
|
43
|
+
|
|
44
|
+
|
|
41
45
|
class RecordManager(models.Manager.from_queryset(RestrictedQuerySet)):
|
|
42
46
|
"""Special Manager for records providing the activity status annotation"""
|
|
43
47
|
|
|
@@ -207,7 +211,8 @@ class Record(NetBoxModel):
|
|
|
207
211
|
try:
|
|
208
212
|
name = (
|
|
209
213
|
dns_name.from_text(
|
|
210
|
-
|
|
214
|
+
str(self.name),
|
|
215
|
+
origin=dns_name.from_text(self.zone.name, origin=None),
|
|
211
216
|
)
|
|
212
217
|
.relativize(dns_name.root)
|
|
213
218
|
.to_unicode()
|
|
@@ -309,7 +314,7 @@ class Record(NetBoxModel):
|
|
|
309
314
|
|
|
310
315
|
return ptr_zone
|
|
311
316
|
|
|
312
|
-
def update_ptr_record(self, update_rfc2317_cname=True):
|
|
317
|
+
def update_ptr_record(self, update_rfc2317_cname=True, save_zone_serial=True):
|
|
313
318
|
ptr_zone = self.ptr_zone
|
|
314
319
|
|
|
315
320
|
if (
|
|
@@ -339,14 +344,16 @@ class Record(NetBoxModel):
|
|
|
339
344
|
not ptr_record.zone.is_rfc2317_zone
|
|
340
345
|
and ptr_record.rfc2317_cname_record is not None
|
|
341
346
|
):
|
|
342
|
-
ptr_record.rfc2317_cname_record.delete(
|
|
347
|
+
ptr_record.rfc2317_cname_record.delete(
|
|
348
|
+
save_zone_serial=save_zone_serial
|
|
349
|
+
)
|
|
343
350
|
|
|
344
351
|
with transaction.atomic():
|
|
345
352
|
if ptr_record is not None:
|
|
346
353
|
if ptr_record.zone.pk != ptr_zone.pk:
|
|
347
354
|
if ptr_record.rfc2317_cname_record is not None:
|
|
348
355
|
ptr_record.rfc2317_cname_record.delete()
|
|
349
|
-
ptr_record.delete()
|
|
356
|
+
ptr_record.delete(save_zone_serial=save_zone_serial)
|
|
350
357
|
ptr_record = None
|
|
351
358
|
|
|
352
359
|
else:
|
|
@@ -358,7 +365,7 @@ class Record(NetBoxModel):
|
|
|
358
365
|
ptr_record.name = ptr_name
|
|
359
366
|
ptr_record.value = ptr_value
|
|
360
367
|
ptr_record.ttl = self.ttl
|
|
361
|
-
ptr_record.save()
|
|
368
|
+
ptr_record.save(save_zone_serial=save_zone_serial)
|
|
362
369
|
|
|
363
370
|
if ptr_record is None:
|
|
364
371
|
ptr_record = Record(
|
|
@@ -369,13 +376,14 @@ class Record(NetBoxModel):
|
|
|
369
376
|
value=ptr_value,
|
|
370
377
|
managed=True,
|
|
371
378
|
)
|
|
372
|
-
ptr_record.save(
|
|
379
|
+
ptr_record.save(
|
|
380
|
+
update_rfc2317_cname=update_rfc2317_cname,
|
|
381
|
+
save_zone_serial=save_zone_serial,
|
|
382
|
+
)
|
|
373
383
|
|
|
374
384
|
self.ptr_record = ptr_record
|
|
375
|
-
if self.pk:
|
|
376
|
-
super().save()
|
|
377
385
|
|
|
378
|
-
def update_rfc2317_cname_record(self):
|
|
386
|
+
def update_rfc2317_cname_record(self, save_zone_serial=True):
|
|
379
387
|
if self.zone.rfc2317_parent_managed:
|
|
380
388
|
cname_name = dns_name.from_text(
|
|
381
389
|
ipaddress.ip_address(self.ip_address).reverse_pointer
|
|
@@ -385,7 +393,13 @@ class Record(NetBoxModel):
|
|
|
385
393
|
self.rfc2317_cname_record.name = cname_name
|
|
386
394
|
self.rfc2317_cname_record.zone = self.zone.rfc2317_parent_zone
|
|
387
395
|
self.rfc2317_cname_record.value = self.fqdn
|
|
388
|
-
self.rfc2317_cname_record.
|
|
396
|
+
self.rfc2317_cname_record.ttl = min_ttl(
|
|
397
|
+
self.rfc2317_cname_record.rfc2317_ptr_records.exclude(pk=self.pk)
|
|
398
|
+
.aggregate(Min("ttl"))
|
|
399
|
+
.get("ttl__min"),
|
|
400
|
+
self.ttl,
|
|
401
|
+
)
|
|
402
|
+
self.rfc2317_cname_record.save(save_zone_serial=save_zone_serial)
|
|
389
403
|
else:
|
|
390
404
|
rfc2317_cname_record = Record.objects.filter(
|
|
391
405
|
name=cname_name,
|
|
@@ -394,20 +408,34 @@ class Record(NetBoxModel):
|
|
|
394
408
|
managed=True,
|
|
395
409
|
value=self.fqdn,
|
|
396
410
|
).first()
|
|
397
|
-
|
|
398
|
-
|
|
411
|
+
|
|
412
|
+
if rfc2317_cname_record is not None:
|
|
413
|
+
rfc2317_cname_record.ttl = min_ttl(
|
|
414
|
+
rfc2317_cname_record.rfc2317_ptr_records.exclude(pk=self.pk)
|
|
415
|
+
.aggregate(Min("ttl"))
|
|
416
|
+
.get("ttl__min"),
|
|
417
|
+
self.ttl,
|
|
418
|
+
)
|
|
419
|
+
rfc2317_cname_record.save(
|
|
420
|
+
update_fields=["ttl"], save_zone_serial=save_zone_serial
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
else:
|
|
424
|
+
rfc2317_cname_record = Record(
|
|
399
425
|
name=cname_name,
|
|
400
426
|
type=RecordTypeChoices.CNAME,
|
|
401
427
|
zone=self.zone.rfc2317_parent_zone,
|
|
402
428
|
managed=True,
|
|
403
429
|
value=self.fqdn,
|
|
430
|
+
ttl=self.ttl,
|
|
404
431
|
)
|
|
432
|
+
rfc2317_cname_record.save(save_zone_serial=save_zone_serial)
|
|
405
433
|
|
|
406
434
|
self.rfc2317_cname_record = rfc2317_cname_record
|
|
407
435
|
|
|
408
436
|
else:
|
|
409
437
|
if self.rfc2317_cname_record is not None:
|
|
410
|
-
self.rfc2317_cname_record.delete()
|
|
438
|
+
self.rfc2317_cname_record.delete(save_zone_serial=save_zone_serial)
|
|
411
439
|
self.rfc2317_cname_record = None
|
|
412
440
|
|
|
413
441
|
def validate_name(self):
|
|
@@ -419,7 +447,7 @@ class Record(NetBoxModel):
|
|
|
419
447
|
zone.to_unicode()
|
|
420
448
|
name.to_unicode()
|
|
421
449
|
|
|
422
|
-
self.name = name.to_text()
|
|
450
|
+
self.name = name.relativize(zone).to_text()
|
|
423
451
|
|
|
424
452
|
except dns.exception.DNSException as exc:
|
|
425
453
|
raise ValidationError(
|
|
@@ -477,7 +505,7 @@ class Record(NetBoxModel):
|
|
|
477
505
|
}
|
|
478
506
|
) from None
|
|
479
507
|
|
|
480
|
-
def
|
|
508
|
+
def check_unique_record(self):
|
|
481
509
|
if not get_plugin_config("netbox_dns", "enforce_unique_records", False):
|
|
482
510
|
return
|
|
483
511
|
|
|
@@ -491,6 +519,10 @@ class Record(NetBoxModel):
|
|
|
491
519
|
value=self.value,
|
|
492
520
|
status__in=Record.ACTIVE_STATUS_LIST,
|
|
493
521
|
)
|
|
522
|
+
|
|
523
|
+
if self.pk is not None:
|
|
524
|
+
records = records.exclude(pk=self.pk)
|
|
525
|
+
|
|
494
526
|
if len(records):
|
|
495
527
|
raise ValidationError(
|
|
496
528
|
{
|
|
@@ -498,6 +530,64 @@ class Record(NetBoxModel):
|
|
|
498
530
|
}
|
|
499
531
|
) from None
|
|
500
532
|
|
|
533
|
+
def check_unique_rrset_ttl(self):
|
|
534
|
+
if self.pk is not None:
|
|
535
|
+
return
|
|
536
|
+
|
|
537
|
+
if not get_plugin_config("netbox_dns", "enforce_unique_rrset_ttl", False):
|
|
538
|
+
return
|
|
539
|
+
|
|
540
|
+
if self.type == RecordTypeChoices.PTR and self.managed:
|
|
541
|
+
return
|
|
542
|
+
|
|
543
|
+
records = (
|
|
544
|
+
Record.objects.filter(
|
|
545
|
+
zone=self.zone,
|
|
546
|
+
name=self.name,
|
|
547
|
+
type=self.type,
|
|
548
|
+
)
|
|
549
|
+
.exclude(ttl=self.ttl)
|
|
550
|
+
.exclude(type=RecordTypeChoices.PTR, managed=True)
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
if not records.exists():
|
|
554
|
+
return
|
|
555
|
+
|
|
556
|
+
conflicting_ttls = ", ".join(set(str(record.ttl) for record in records))
|
|
557
|
+
raise ValidationError(
|
|
558
|
+
{
|
|
559
|
+
"ttl": f"There is at least one active {self.type} record for name {self.name} in zone {self.zone} and TTL is different ({conflicting_ttls})."
|
|
560
|
+
}
|
|
561
|
+
) from None
|
|
562
|
+
|
|
563
|
+
def update_rrset_ttl(self, ttl=None):
|
|
564
|
+
if self.pk is None:
|
|
565
|
+
return
|
|
566
|
+
|
|
567
|
+
if not get_plugin_config("netbox_dns", "enforce_unique_rrset_ttl", False):
|
|
568
|
+
return
|
|
569
|
+
|
|
570
|
+
if self.type == RecordTypeChoices.PTR and self.managed:
|
|
571
|
+
return
|
|
572
|
+
|
|
573
|
+
if ttl is None:
|
|
574
|
+
ttl = self.ttl
|
|
575
|
+
|
|
576
|
+
records = (
|
|
577
|
+
Record.objects.filter(
|
|
578
|
+
zone=self.zone,
|
|
579
|
+
name=self.name,
|
|
580
|
+
type=self.type,
|
|
581
|
+
)
|
|
582
|
+
.exclude(pk=self.pk)
|
|
583
|
+
.exclude(ttl=ttl)
|
|
584
|
+
.exclude(type=RecordTypeChoices.PTR, managed=True)
|
|
585
|
+
)
|
|
586
|
+
|
|
587
|
+
for record in records:
|
|
588
|
+
record.ttl = ttl
|
|
589
|
+
record.save(update_fields=["ttl"], update_rrset_ttl=False)
|
|
590
|
+
|
|
501
591
|
def clean_fields(self, *args, **kwargs):
|
|
502
592
|
self.type = self.type.upper()
|
|
503
593
|
super().clean_fields(*args, **kwargs)
|
|
@@ -505,7 +595,9 @@ class Record(NetBoxModel):
|
|
|
505
595
|
def clean(self, *args, **kwargs):
|
|
506
596
|
self.validate_name()
|
|
507
597
|
self.validate_value()
|
|
508
|
-
self.
|
|
598
|
+
self.check_unique_record()
|
|
599
|
+
if self.pk is None:
|
|
600
|
+
self.check_unique_rrset_ttl()
|
|
509
601
|
|
|
510
602
|
if not self.is_active:
|
|
511
603
|
return
|
|
@@ -580,14 +672,24 @@ class Record(NetBoxModel):
|
|
|
580
672
|
}
|
|
581
673
|
) from None
|
|
582
674
|
|
|
583
|
-
def save(
|
|
675
|
+
def save(
|
|
676
|
+
self,
|
|
677
|
+
*args,
|
|
678
|
+
update_rfc2317_cname=True,
|
|
679
|
+
save_zone_serial=True,
|
|
680
|
+
update_rrset_ttl=True,
|
|
681
|
+
**kwargs,
|
|
682
|
+
):
|
|
584
683
|
self.full_clean()
|
|
585
684
|
|
|
685
|
+
if self.pk is not None and update_rrset_ttl:
|
|
686
|
+
self.update_rrset_ttl()
|
|
687
|
+
|
|
586
688
|
if self.is_ptr_record:
|
|
587
689
|
if self.zone.is_rfc2317_zone:
|
|
588
690
|
self.ip_address = self.address_from_rfc2317_name
|
|
589
691
|
if update_rfc2317_cname:
|
|
590
|
-
self.update_rfc2317_cname_record()
|
|
692
|
+
self.update_rfc2317_cname_record(save_zone_serial=save_zone_serial)
|
|
591
693
|
else:
|
|
592
694
|
self.ip_address = self.address_from_name
|
|
593
695
|
|
|
@@ -597,7 +699,10 @@ class Record(NetBoxModel):
|
|
|
597
699
|
self.ip_address = None
|
|
598
700
|
|
|
599
701
|
if self.is_address_record:
|
|
600
|
-
self.update_ptr_record(
|
|
702
|
+
self.update_ptr_record(
|
|
703
|
+
update_rfc2317_cname=update_rfc2317_cname,
|
|
704
|
+
save_zone_serial=save_zone_serial,
|
|
705
|
+
)
|
|
601
706
|
elif self.ptr_record is not None:
|
|
602
707
|
self.ptr_record.delete()
|
|
603
708
|
self.ptr_record = None
|
|
@@ -606,15 +711,24 @@ class Record(NetBoxModel):
|
|
|
606
711
|
|
|
607
712
|
zone = self.zone
|
|
608
713
|
if self.type != RecordTypeChoices.SOA and zone.soa_serial_auto:
|
|
609
|
-
zone.update_serial()
|
|
714
|
+
zone.update_serial(save_zone_serial=save_zone_serial)
|
|
610
715
|
|
|
611
|
-
def delete(self, *args, **kwargs):
|
|
716
|
+
def delete(self, *args, save_zone_serial=True, **kwargs):
|
|
612
717
|
if self.rfc2317_cname_record:
|
|
613
|
-
if
|
|
614
|
-
self.rfc2317_cname_record.
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
718
|
+
if self.rfc2317_cname_record.pk:
|
|
719
|
+
if self.rfc2317_cname_record.rfc2317_ptr_records.count() == 1:
|
|
720
|
+
self.rfc2317_cname_record.delete()
|
|
721
|
+
else:
|
|
722
|
+
self.rfc2317_cname_record.ttl = (
|
|
723
|
+
self.rfc2317_cname_record.rfc2317_ptr_records.exclude(
|
|
724
|
+
pk=self.pk
|
|
725
|
+
)
|
|
726
|
+
.aggregate(Min("ttl"))
|
|
727
|
+
.get("ttl__min")
|
|
728
|
+
)
|
|
729
|
+
self.rfc2317_cname_record.save(
|
|
730
|
+
update_fields=["ttl"], save_zone_serial=save_zone_serial
|
|
731
|
+
)
|
|
618
732
|
|
|
619
733
|
if self.ptr_record:
|
|
620
734
|
self.ptr_record.delete()
|
|
@@ -623,7 +737,7 @@ class Record(NetBoxModel):
|
|
|
623
737
|
|
|
624
738
|
zone = self.zone
|
|
625
739
|
if zone.soa_serial_auto:
|
|
626
|
-
zone.update_serial()
|
|
740
|
+
zone.update_serial(save_zone_serial=save_zone_serial)
|
|
627
741
|
|
|
628
742
|
|
|
629
743
|
@register_search
|
netbox_dns/models/zone.py
CHANGED
|
@@ -249,6 +249,8 @@ class Zone(NetBoxModel):
|
|
|
249
249
|
null=True,
|
|
250
250
|
)
|
|
251
251
|
|
|
252
|
+
soa_serial_dirty = False
|
|
253
|
+
|
|
252
254
|
objects = ZoneManager()
|
|
253
255
|
|
|
254
256
|
clone_fields = [
|
|
@@ -470,11 +472,24 @@ class Zone(NetBoxModel):
|
|
|
470
472
|
|
|
471
473
|
return soa_serial
|
|
472
474
|
|
|
473
|
-
def update_serial(self):
|
|
475
|
+
def update_serial(self, save_zone_serial=True):
|
|
476
|
+
if not self.soa_serial_auto:
|
|
477
|
+
return
|
|
478
|
+
|
|
474
479
|
self.last_updated = datetime.now()
|
|
475
480
|
self.soa_serial = ceil(datetime.now().timestamp())
|
|
476
|
-
|
|
477
|
-
|
|
481
|
+
|
|
482
|
+
if save_zone_serial:
|
|
483
|
+
super().save(update_fields=["soa_serial", "last_updated"])
|
|
484
|
+
self.soa_serial_dirty = False
|
|
485
|
+
self.update_soa_record()
|
|
486
|
+
else:
|
|
487
|
+
self.soa_serial_dirty = True
|
|
488
|
+
|
|
489
|
+
def save_soa_serial(self):
|
|
490
|
+
if self.soa_serial_auto and self.soa_serial_dirty:
|
|
491
|
+
super().save(update_fields=["soa_serial", "last_updated"])
|
|
492
|
+
self.soa_serial_dirty = False
|
|
478
493
|
|
|
479
494
|
@property
|
|
480
495
|
def network_from_name(self):
|
|
@@ -503,28 +518,45 @@ class Zone(NetBoxModel):
|
|
|
503
518
|
if rfc2317_parent_zone is None:
|
|
504
519
|
self.rfc2317_parent_managed = False
|
|
505
520
|
self.rfc2317_parent_zone = None
|
|
506
|
-
self.save(
|
|
521
|
+
self.save(
|
|
522
|
+
update_fields=["rfc2317_parent_zone", "rfc2317_parent_managed"]
|
|
523
|
+
)
|
|
507
524
|
|
|
508
525
|
elif self.rfc2317_parent_zone != rfc2317_parent_zone:
|
|
509
526
|
self.rfc2317_parent_zone = rfc2317_parent_zone
|
|
510
|
-
self.save()
|
|
527
|
+
self.save(update_fields=["rfc2317_parent_zone"])
|
|
511
528
|
|
|
512
|
-
ptr_records = self.record_set.filter(
|
|
529
|
+
ptr_records = self.record_set.filter(
|
|
530
|
+
type=record.RecordTypeChoices.PTR
|
|
531
|
+
).prefetch_related("zone", "rfc2317_cname_record")
|
|
532
|
+
ptr_zones = {ptr_record.zone for ptr_record in ptr_records}
|
|
513
533
|
|
|
514
534
|
if self.rfc2317_parent_managed:
|
|
515
535
|
for ptr_record in ptr_records:
|
|
516
|
-
ptr_record.save()
|
|
536
|
+
ptr_record.save(save_zone_serial=False)
|
|
517
537
|
|
|
538
|
+
self.rfc2317_parent_zone.save_soa_serial()
|
|
539
|
+
self.rfc2317_parent_zone.update_soa_record()
|
|
518
540
|
else:
|
|
519
541
|
cname_records = {
|
|
520
542
|
ptr_record.rfc2317_cname_record
|
|
521
543
|
for ptr_record in ptr_records
|
|
522
544
|
if ptr_record.rfc2317_cname_record is not None
|
|
523
545
|
}
|
|
546
|
+
cname_zones = {cname_record.zone for cname_record in cname_records}
|
|
547
|
+
|
|
524
548
|
for ptr_record in ptr_records:
|
|
525
|
-
ptr_record.save(update_rfc2317_cname=False)
|
|
549
|
+
ptr_record.save(update_rfc2317_cname=False, save_zone_serial=False)
|
|
526
550
|
for cname_record in cname_records:
|
|
527
|
-
cname_record.delete()
|
|
551
|
+
cname_record.delete(save_zone_serial=False)
|
|
552
|
+
|
|
553
|
+
for cname_zone in cname_zones:
|
|
554
|
+
cname_zone.save_soa_serial()
|
|
555
|
+
cname_zone.update_soa_record()
|
|
556
|
+
|
|
557
|
+
for ptr_zone in ptr_zones:
|
|
558
|
+
ptr_zone.save_soa_serial()
|
|
559
|
+
ptr_zone.update_soa_record()
|
|
528
560
|
|
|
529
561
|
def clean(self, *args, **kwargs):
|
|
530
562
|
self.check_name_conflict()
|
|
@@ -643,7 +675,12 @@ class Zone(NetBoxModel):
|
|
|
643
675
|
)
|
|
644
676
|
|
|
645
677
|
for address_record in address_records:
|
|
646
|
-
address_record.
|
|
678
|
+
address_record.save(
|
|
679
|
+
update_fields=["ptr_record"], save_zone_serial=False
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
for zone in zones:
|
|
683
|
+
zone.save_soa_serial()
|
|
647
684
|
|
|
648
685
|
if self.arpa_network.version == 4:
|
|
649
686
|
rfc2317_child_zones = Zone.objects.filter(
|
|
@@ -673,7 +710,14 @@ class Zone(NetBoxModel):
|
|
|
673
710
|
)
|
|
674
711
|
|
|
675
712
|
for address_record in address_records:
|
|
676
|
-
address_record.
|
|
713
|
+
address_record.save(
|
|
714
|
+
update_fields=["ptr_record"],
|
|
715
|
+
update_rfc2317_cname=False,
|
|
716
|
+
save_zone_serial=False,
|
|
717
|
+
)
|
|
718
|
+
|
|
719
|
+
for zone in zones:
|
|
720
|
+
zone.save_soa_serial()
|
|
677
721
|
|
|
678
722
|
self.update_rfc2317_parent_zone()
|
|
679
723
|
|
|
@@ -681,7 +725,7 @@ class Zone(NetBoxModel):
|
|
|
681
725
|
for address_record in self.record_set.filter(
|
|
682
726
|
type__in=(record.RecordTypeChoices.A, record.RecordTypeChoices.AAAA)
|
|
683
727
|
):
|
|
684
|
-
address_record.
|
|
728
|
+
address_record.save(update_fields=["ptr_record"])
|
|
685
729
|
|
|
686
730
|
# Fix name in IP Address when zone name is changed
|
|
687
731
|
if (
|
|
@@ -694,11 +738,14 @@ class Zone(NetBoxModel):
|
|
|
694
738
|
ip.dns_name = f'{ip.custom_field_data["ipaddress_dns_record_name"]}.{self.name}'
|
|
695
739
|
ip.save(update_fields=["dns_name"])
|
|
696
740
|
|
|
741
|
+
self.save_soa_serial()
|
|
697
742
|
self.update_soa_record()
|
|
698
743
|
|
|
699
744
|
def delete(self, *args, **kwargs):
|
|
700
745
|
with transaction.atomic():
|
|
701
|
-
address_records = self.record_set.filter(
|
|
746
|
+
address_records = self.record_set.filter(
|
|
747
|
+
ptr_record__isnull=False
|
|
748
|
+
).prefetch_related("ptr_record")
|
|
702
749
|
for address_record in address_records:
|
|
703
750
|
address_record.ptr_record.delete()
|
|
704
751
|
|
|
@@ -715,8 +762,13 @@ class Zone(NetBoxModel):
|
|
|
715
762
|
for ptr_record in ptr_records
|
|
716
763
|
if ptr_record.rfc2317_cname_record is not None
|
|
717
764
|
}
|
|
765
|
+
cname_zones = {cname_record.zone for cname_record in cname_records}
|
|
766
|
+
|
|
718
767
|
for cname_record in cname_records:
|
|
719
|
-
cname_record.delete()
|
|
768
|
+
cname_record.delete(save_zone_serial=False)
|
|
769
|
+
for cname_zone in cname_zones:
|
|
770
|
+
cname_zone.save_soa_serial()
|
|
771
|
+
cname_zone.update_soa_record()
|
|
720
772
|
|
|
721
773
|
rfc2317_child_zones = [
|
|
722
774
|
child_zone.pk for child_zone in self.rfc2317_child_zones.all()
|
|
@@ -733,11 +785,25 @@ class Zone(NetBoxModel):
|
|
|
733
785
|
|
|
734
786
|
super().delete(*args, **kwargs)
|
|
735
787
|
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
788
|
+
address_records = record.Record.objects.filter(
|
|
789
|
+
pk__in=update_records
|
|
790
|
+
).prefetch_related("zone")
|
|
791
|
+
|
|
792
|
+
for address_record in address_records:
|
|
793
|
+
address_record.save(save_zone_serial=False)
|
|
794
|
+
for address_zone in {address_record.zone for address_record in address_records}:
|
|
795
|
+
address_zone.save_soa_serial()
|
|
796
|
+
address_zone.update_soa_record()
|
|
797
|
+
|
|
798
|
+
rfc2317_child_zones = Zone.objects.filter(pk__in=rfc2317_child_zones)
|
|
799
|
+
if rfc2317_child_zones:
|
|
800
|
+
for child_zone in rfc2317_child_zones:
|
|
801
|
+
child_zone.update_rfc2317_parent_zone()
|
|
802
|
+
|
|
803
|
+
new_rfc2317_parent_zone = rfc2317_child_zones.first().rfc2317_parent_zone
|
|
804
|
+
if new_rfc2317_parent_zone is not None:
|
|
805
|
+
new_rfc2317_parent_zone.save_soa_serial()
|
|
806
|
+
new_rfc2317_parent_zone.update_soa_record()
|
|
741
807
|
|
|
742
808
|
|
|
743
809
|
@receiver(m2m_changed, sender=Zone.nameservers.through)
|
|
@@ -121,15 +121,27 @@ def ip_address_update_dns_information(instance, **kwargs):
|
|
|
121
121
|
|
|
122
122
|
name, ttl, disable_ptr, zone_id = ipaddress_cf_data(instance)
|
|
123
123
|
|
|
124
|
+
previous_zone_id = None
|
|
125
|
+
if instance.pk is not None:
|
|
126
|
+
try:
|
|
127
|
+
old_instance = IPAddress.objects.get(pk=instance.pk)
|
|
128
|
+
previous_zone_id = old_instance.custom_field_data.get(
|
|
129
|
+
"ipaddress_dns_zone_id"
|
|
130
|
+
)
|
|
131
|
+
except IPAddress.DoesNotExist:
|
|
132
|
+
pass
|
|
133
|
+
|
|
124
134
|
if zone_id is not None:
|
|
125
135
|
instance.dns_name = f"{name}.{Zone.objects.get(pk=zone_id).name}"
|
|
126
136
|
else:
|
|
127
|
-
instance.dns_name = ""
|
|
128
137
|
instance.custom_field_data["ipaddress_dns_record_name"] = None
|
|
129
138
|
instance.custom_field_data["ipaddress_dns_record_ttl"] = None
|
|
130
139
|
instance.custom_field_data["ipaddress_dns_record_disable_ptr"] = False
|
|
131
140
|
instance.custom_field_data["ipaddress_dns_zone_id"] = None
|
|
132
141
|
|
|
142
|
+
if previous_zone_id is not None:
|
|
143
|
+
instance.dns_name = ""
|
|
144
|
+
|
|
133
145
|
|
|
134
146
|
#
|
|
135
147
|
# Handle DNS record operation after IPAddress has been created or modified
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
<h5 class="card-header">Contact Details</h5>
|
|
23
23
|
<div class="card-body">
|
|
24
24
|
<table class="table table-hover attr-table">
|
|
25
|
+
<tr>
|
|
26
|
+
<th scope="row">Address</th>
|
|
27
|
+
<td>{{ object.address }}</td>
|
|
28
|
+
</tr>
|
|
25
29
|
<tr>
|
|
26
30
|
<th scope="row">Referral URL</th>
|
|
27
31
|
<td><a href="{{ object.referral_url }}">{{ object.referral_url }}</a></td>
|
|
@@ -120,10 +120,17 @@
|
|
|
120
120
|
<th scope="row">Responsible</th>
|
|
121
121
|
<td>{{ object.soa_rname }}</td>
|
|
122
122
|
</tr>
|
|
123
|
+
{% if object.soa_serial_auto %}
|
|
124
|
+
<tr>
|
|
125
|
+
<th scope="row">Serial (auto-generated)</th>
|
|
126
|
+
<td>{{ object.soa_serial }}</td>
|
|
127
|
+
</tr>
|
|
128
|
+
{% else %}
|
|
123
129
|
<tr>
|
|
124
130
|
<th scope="row">Serial</th>
|
|
125
131
|
<td>{{ object.soa_serial }}</td>
|
|
126
132
|
</tr>
|
|
133
|
+
{% endif %}
|
|
127
134
|
<tr>
|
|
128
135
|
<th scope="row">Refresh</th>
|
|
129
136
|
<td>{{ object.soa_refresh }}</td>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: netbox-plugin-dns
|
|
3
|
-
Version: 0.22.
|
|
3
|
+
Version: 0.22.5
|
|
4
4
|
Summary: NetBox DNS is a NetBox plugin for managing DNS data.
|
|
5
5
|
Home-page: https://github.com/peteeckel/netbox-plugin-dns
|
|
6
6
|
License: MIT
|
|
@@ -8,6 +8,7 @@ Keywords: netbox,netbox-plugin,dns
|
|
|
8
8
|
Author: Peter Eckel
|
|
9
9
|
Author-email: pete@netbox-dns.org
|
|
10
10
|
Requires-Python: >=3.8,<4.0
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
12
|
Classifier: License :: OSI Approved :: MIT License
|
|
12
13
|
Classifier: Programming Language :: Python :: 3
|
|
13
14
|
Classifier: Programming Language :: Python :: 3.8
|
|
@@ -19,28 +20,30 @@ Requires-Dist: dnspython (>=2.2.1,<3.0.0)
|
|
|
19
20
|
Project-URL: Repository, https://github.com/peteeckel/netbox-plugin-dns
|
|
20
21
|
Description-Content-Type: text/markdown
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<p align="center"><i>NetBox DNS is a NetBox plugin for managing DNS views, zones, name servers and records.</i></p>
|
|
23
|
+
# NetBox DNS
|
|
24
|
+
The NetBox DNS plugin enables NetBox to manage operational DNS data such as name servers, zones, records and views, as well as registration data for domains. It can automate tasks like creating PTR records, generating zone serial numbers, NS and SOA records, as well as validate names and values values for resource records to ensure zone data is consistent, current and conforming to the relevant RFCs.
|
|
25
25
|
|
|
26
26
|
<div align="center">
|
|
27
27
|
<a href="https://pypi.org/project/netbox-plugin-dns/"><img src="https://img.shields.io/pypi/v/netbox-plugin-dns" alt="PyPi"/></a>
|
|
28
|
-
<a href="https://github.com/peteeckel/netbox-plugin-dns/stargazers"><img src="https://img.shields.io/github/stars/peteeckel/netbox-plugin-dns" alt="Stars Badge"/></a>
|
|
29
|
-
<a href="https://github.com/peteeckel/netbox-plugin-dns/network/members"><img src="https://img.shields.io/github/forks/peteeckel/netbox-plugin-dns" alt="Forks Badge"/></a>
|
|
30
|
-
<a href="https://github.com/peteeckel/netbox-plugin-dns/pulls"><img src="https://img.shields.io/github/issues-pr/peteeckel/netbox-plugin-dns" alt="Pull Requests Badge"/></a>
|
|
28
|
+
<a href="https://github.com/peteeckel/netbox-plugin-dns/stargazers"><img src="https://img.shields.io/github/stars/peteeckel/netbox-plugin-dns?style=flat" alt="Stars Badge"/></a>
|
|
29
|
+
<a href="https://github.com/peteeckel/netbox-plugin-dns/network/members"><img src="https://img.shields.io/github/forks/peteeckel/netbox-plugin-dns?style=flat" alt="Forks Badge"/></a>
|
|
31
30
|
<a href="https://github.com/peteeckel/netbox-plugin-dns/issues"><img src="https://img.shields.io/github/issues/peteeckel/netbox-plugin-dns" alt="Issues Badge"/></a>
|
|
31
|
+
<a href="https://github.com/peteeckel/netbox-plugin-dns/pulls"><img src="https://img.shields.io/github/issues-pr/peteeckel/netbox-plugin-dns" alt="Pull Requests Badge"/></a>
|
|
32
32
|
<a href="https://github.com/peteeckel/netbox-plugin-dns/graphs/contributors"><img alt="GitHub contributors" src="https://img.shields.io/github/contributors/peteeckel/netbox-plugin-dns?color=2b9348"></a>
|
|
33
33
|
<a href="https://github.com/peteeckel/netbox-plugin-dns/blob/master/LICENSE"><img src="https://img.shields.io/github/license/peteeckel/netbox-plugin-dns?color=2b9348" alt="License Badge"/></a>
|
|
34
|
+
<a href="https://pepy.tech/project/netbox-plugin-dns"><img alt="Downloads" src="https://static.pepy.tech/badge/netbox-plugin-dns"></a>
|
|
35
|
+
<a href="https://pepy.tech/project/netbox-plugin-dns"><img alt="Downloads/Week" src="https://static.pepy.tech/badge/netbox-plugin-dns/month"></a>
|
|
36
|
+
<a href="https://pepy.tech/project/netbox-plugin-dns"><img alt="Downloads/Month" src="https://static.pepy.tech/badge/netbox-plugin-dns/week"></a>
|
|
34
37
|
</div>
|
|
35
38
|
|
|
36
39
|
## Features
|
|
37
40
|
|
|
38
41
|
* Manage name servers, zones and records
|
|
39
42
|
* Automatically generate SOA and NS records for zones
|
|
40
|
-
* Automatically create and update PTR records for IPv4 and IPv6 records
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
43
|
+
* Automatically create and update PTR records for IPv4 and IPv6 address records
|
|
44
|
+
* Organize DNS zones in views for split horizon DNS and multi-site deployments
|
|
45
|
+
* Manage domain registrar and registrant information for domains related to zones
|
|
46
|
+
* Manage RFC2317 reverse zones for IPv4 prefixes with a network mask length longer than 24 bits
|
|
44
47
|
|
|
45
48
|
NetBox DNS is using the standardized NetBox plugin interface, so it also takes advantage of the NetBox tagging and change log features.
|
|
46
49
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
netbox_dns/__init__.py,sha256=
|
|
1
|
+
netbox_dns/__init__.py,sha256=Wnz89UxRISvgUOkm-cZI50tMo6taJh14iZnSb1Wx0L4,2818
|
|
2
2
|
netbox_dns/api/nested_serializers.py,sha256=XB7bcCjVMPYrumJWgRicj06PukQ2UCBjdr84AIUJuVQ,3291
|
|
3
|
-
netbox_dns/api/serializers.py,sha256=
|
|
3
|
+
netbox_dns/api/serializers.py,sha256=H5Vm1O6C4SsxHVzdooYiu5ak3ykxXo-L1mT6ISWVRV8,8183
|
|
4
4
|
netbox_dns/api/urls.py,sha256=R9VmmWtdrjvr35i5d_SfZK2lGn6JzmPuWEKTQlZ8MJo,575
|
|
5
5
|
netbox_dns/api/views.py,sha256=DjovvTfS4F2Fq2Ahea6f4LBJiWr01ajk-wSZHNTya5I,3527
|
|
6
6
|
netbox_dns/apps.py,sha256=JCW5eS-AQBUubDJve1DjP-IRFKTFGQh1NLGWzJpC5MI,151
|
|
@@ -12,16 +12,16 @@ netbox_dns/filters/__init__.py,sha256=Aw8HrCTjaJfu5JSwJsQRHfOUz4zKwAmZNByT9q6BrF
|
|
|
12
12
|
netbox_dns/filters/contact.py,sha256=_onZ6G2KKgfvm9Emg_kng2RPETRMfbTyIR8Tvs9d2YM,1027
|
|
13
13
|
netbox_dns/filters/nameserver.py,sha256=sDKsrluSmUZ0aTw_wagYJAh1g5fKzyzHXbJu6DaK1b8,522
|
|
14
14
|
netbox_dns/filters/record.py,sha256=DH1x4bNkBAN_gVNklAjorcgHoJmH_AXEf1sBNvJS6Kc,1691
|
|
15
|
-
netbox_dns/filters/registrar.py,sha256=
|
|
15
|
+
netbox_dns/filters/registrar.py,sha256=8UXJXUb0XmkPyPQ2oACAhzhmZtAmvM2c4hTWMDLuBEo,911
|
|
16
16
|
netbox_dns/filters/view.py,sha256=Sji4DKy0VKk8BLEdk8xCe8su3rUBtXeJUsUAee0IsOs,497
|
|
17
17
|
netbox_dns/filters/zone.py,sha256=gNKTLll0nvdkzjEVl5c4O1hncU13IFVKn61iXThLYJA,3556
|
|
18
18
|
netbox_dns/forms/__init__.py,sha256=Aw8HrCTjaJfu5JSwJsQRHfOUz4zKwAmZNByT9q6BrFU,136
|
|
19
19
|
netbox_dns/forms/contact.py,sha256=Z8llYOpcGns8ZerOLsldpY836pfjO7427rmAZ4T8Kyg,4492
|
|
20
20
|
netbox_dns/forms/nameserver.py,sha256=LUYV_tna667flgPdQevDFniMdXZUYRUG8iqCx7HKdxQ,2027
|
|
21
21
|
netbox_dns/forms/record.py,sha256=TAQ-rZ55lmWE_piogiE5TwTv1v4r5T2yZpNT8K7GrPU,6171
|
|
22
|
-
netbox_dns/forms/registrar.py,sha256=
|
|
22
|
+
netbox_dns/forms/registrar.py,sha256=Vkw8cafXQRDHf7K7OFPn62R-_BzjNOofnhLmL2Hw7mg,2636
|
|
23
23
|
netbox_dns/forms/view.py,sha256=KEWlUo8-oXP_QsqT5fW_UK-ZoX9g-f4CqRnJ_P6LHco,1627
|
|
24
|
-
netbox_dns/forms/zone.py,sha256=
|
|
24
|
+
netbox_dns/forms/zone.py,sha256=Y9KfeSCzFrJU1LxQjxPcLANu3NEcaHjvxnyMqgna_FY,21702
|
|
25
25
|
netbox_dns/graphql/__init__.py,sha256=v3lLvVoP-JQbEZ1NY49u1TNqvanF-4TqOtjHXrbsTvU,811
|
|
26
26
|
netbox_dns/graphql/contact.py,sha256=2iyuvCxG09CLDUMhan5vR3uFZxCVzKNZBrotr_zoUVY,497
|
|
27
27
|
netbox_dns/graphql/nameserver.py,sha256=mlQw1Po_Ax_fjyyXVBetyxlFLrCqmptYDgOspZvYtP4,527
|
|
@@ -30,7 +30,8 @@ netbox_dns/graphql/registrar.py,sha256=TEQSNT8CeIqquzySMRluGk3I2t9rYgQCgDbLJ0gnL
|
|
|
30
30
|
netbox_dns/graphql/schema.py,sha256=D_dDusogaI55tmGx_dr1owsgzXS5fd2S-kPZ7xcXxs8,221
|
|
31
31
|
netbox_dns/graphql/view.py,sha256=S_61hYlQCtPQen1lI1UQs38UBWKQTaWfUxzlbpO07zA,467
|
|
32
32
|
netbox_dns/graphql/zone.py,sha256=QDBxocezhLSHBGDV4RJnmarBfOsiUTeE9KzBGJ3gJi8,467
|
|
33
|
-
netbox_dns/management/commands/cleanup_database.py,sha256=
|
|
33
|
+
netbox_dns/management/commands/cleanup_database.py,sha256=eggyMZrRg--cXDQJ-boofHboG1gJTs8j-oldhn6EuCo,6050
|
|
34
|
+
netbox_dns/management/commands/cleanup_rrset_ttl.py,sha256=gCvwH6hGw32YJtcy_pEWIP29PSRMW23-qzB8SHEdixQ,2077
|
|
34
35
|
netbox_dns/management/commands/setup_coupling.py,sha256=Yn1nffPR7fBgB6WWBdDqdnp3k8z1QK8k6qi9xbx4U6Y,4580
|
|
35
36
|
netbox_dns/management/commands/update_soa.py,sha256=qvlApMngTVpauj0CU0yeOy9r3lxxDciKorMxFsyvQhs,661
|
|
36
37
|
netbox_dns/migrations/0001_initial.py,sha256=R9FbIQ7nO1ROb12NL8YQDkFQuP1r6-TtMcPwg4rwjus,4153
|
|
@@ -66,13 +67,13 @@ netbox_dns/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
|
|
|
66
67
|
netbox_dns/models/__init__.py,sha256=Q7UIEe2vGh18AZN4er6CykciwXPQGgUq0L-9718wZqU,182
|
|
67
68
|
netbox_dns/models/contact.py,sha256=Mzj4SR3fczOE96etWjObJElk0RVQetNuXZVtm8sS1h8,2849
|
|
68
69
|
netbox_dns/models/nameserver.py,sha256=SjKUbMRNE3TSDzmxbKFR9b4AfYPS0UPj0QeO7XpwNRk,2941
|
|
69
|
-
netbox_dns/models/record.py,sha256=
|
|
70
|
+
netbox_dns/models/record.py,sha256=nOKjaoVuyMVIfwNw_V4hYYd71fcE4UyXThU983KrXaY,23629
|
|
70
71
|
netbox_dns/models/registrar.py,sha256=ByKHeqH5KfswqOLjya8DZExJ1omSKFHMfCjIIYfnwTo,1416
|
|
71
72
|
netbox_dns/models/view.py,sha256=ljs3Q2xQR63ZOSyja5H7DEdFbm7MX2ZjlR6uNVrAsVo,920
|
|
72
|
-
netbox_dns/models/zone.py,sha256=
|
|
73
|
+
netbox_dns/models/zone.py,sha256=Ek7Jui3AldNHVAHowbQhC47QoXv0IgkhLzFziN14UUI,26333
|
|
73
74
|
netbox_dns/navigation.py,sha256=IutEr_TcPgDGzqTT1ZzV4IUABLSOFU9v364BfpCqbro,4587
|
|
74
75
|
netbox_dns/signals/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
75
|
-
netbox_dns/signals/ipam_coupling.py,sha256=
|
|
76
|
+
netbox_dns/signals/ipam_coupling.py,sha256=xbb37_77ZqtgT3mKXZp18QRduqZuiAuYGPk8R9d7SVc,5727
|
|
76
77
|
netbox_dns/tables/__init__.py,sha256=Aw8HrCTjaJfu5JSwJsQRHfOUz4zKwAmZNByT9q6BrFU,136
|
|
77
78
|
netbox_dns/tables/contact.py,sha256=HpblHKrlQqQAPC59gvw8wUV3tc6UBAphHPE6CfN2uVM,747
|
|
78
79
|
netbox_dns/tables/nameserver.py,sha256=7Ap0px5gAHtgk5XFMpZCHccy9N-v8UKy19GP7fdWuPc,819
|
|
@@ -86,7 +87,7 @@ netbox_dns/templates/netbox_dns/nameserver.html,sha256=3AJVbsuhKg4Jy74rlvwrGSHd_
|
|
|
86
87
|
netbox_dns/templates/netbox_dns/record/managed.html,sha256=G6LPG1koUGuzUiwYdv1okdVa4sKaofiQegDBnsFL0kA,89
|
|
87
88
|
netbox_dns/templates/netbox_dns/record/related.html,sha256=Aqor8uGcuHQTHjlX-Xmni2Yp4N7lOBrMOqQiszrQOC0,742
|
|
88
89
|
netbox_dns/templates/netbox_dns/record.html,sha256=1XOp9Rf7KiV736RKGh7A8LAYkrfI7veG5SsEhaeMghw,6613
|
|
89
|
-
netbox_dns/templates/netbox_dns/registrar.html,sha256=
|
|
90
|
+
netbox_dns/templates/netbox_dns/registrar.html,sha256=rSShbH68nP0r8EUHn0-TZOsUj6pg7hmfvM7h2tqouUA,2130
|
|
90
91
|
netbox_dns/templates/netbox_dns/related_dns_objects.html,sha256=KSzlnw1cStrJa3poKkwrt_ycIH0oH0STWIHRNy3ks4g,806
|
|
91
92
|
netbox_dns/templates/netbox_dns/view.html,sha256=_lDjd2xY3upfGpNpemXXMzgsaKZlX3-PzPPAdYkIjvs,1350
|
|
92
93
|
netbox_dns/templates/netbox_dns/zone/base.html,sha256=pucf_b7iGg4hCXqwIbR0_WtEQdeH2LQtTCRTSoeBhFc,460
|
|
@@ -95,7 +96,7 @@ netbox_dns/templates/netbox_dns/zone/managed_record.html,sha256=5P85eJuQOB7omih2
|
|
|
95
96
|
netbox_dns/templates/netbox_dns/zone/record.html,sha256=1oIRGXOZAjwmTMkTgArfKyVrmL54Sh_IN7IAF3qYEKM,2218
|
|
96
97
|
netbox_dns/templates/netbox_dns/zone/registration.html,sha256=3R5uqLXZxIJkp9_LVnTTgTV61mITfDPDDNofV7s4d1k,1283
|
|
97
98
|
netbox_dns/templates/netbox_dns/zone/rfc2317_child_zone.html,sha256=kxIrhn0gghb4l1eZhpsICdfkSYegqSXR9zFIWiZLypA,527
|
|
98
|
-
netbox_dns/templates/netbox_dns/zone.html,sha256=
|
|
99
|
+
netbox_dns/templates/netbox_dns/zone.html,sha256=_lQhr00_V8ThadlTuTm5zPkcvyjYQxxYGHQsfTOgDCs,7187
|
|
99
100
|
netbox_dns/urls.py,sha256=NxqvnRxLM6VwUBTY6KA8u-PRjNklmBai7gy6NXc-1xo,9172
|
|
100
101
|
netbox_dns/utilities/__init__.py,sha256=8DqDx-99kejykwaIt_29W5KgY8RvWBOk6526vrf22UQ,1967
|
|
101
102
|
netbox_dns/utilities/ipam_coupling.py,sha256=LhdFafF4OAOU8rgROOqY5Dt7XDT_SUZP2JuIqCQYVqw,3573
|
|
@@ -109,7 +110,7 @@ netbox_dns/views/record.py,sha256=2xzZnCWmCZIyEVjQfYo0rByhBNItPxNFiehOwDl8U6w,25
|
|
|
109
110
|
netbox_dns/views/registrar.py,sha256=aznSKt1L5tILMLGgcZiBR7u7B8rNl-jM1B2-N0fTeK8,2072
|
|
110
111
|
netbox_dns/views/view.py,sha256=uUvtlNEh5MYoEALvWWaCOqj_Zj8dpGOL2PUyg-UPfEA,1895
|
|
111
112
|
netbox_dns/views/zone.py,sha256=SyttTAgrPPzf1jIT1B4RexCLdXYjSmPIZsefO_zog1Q,4587
|
|
112
|
-
netbox_plugin_dns-0.22.
|
|
113
|
-
netbox_plugin_dns-0.22.
|
|
114
|
-
netbox_plugin_dns-0.22.
|
|
115
|
-
netbox_plugin_dns-0.22.
|
|
113
|
+
netbox_plugin_dns-0.22.5.dist-info/LICENSE,sha256=tziMJKpkMbySr09L6bIwsu7Ca9ICoqpMO3yAXgEMQA4,1076
|
|
114
|
+
netbox_plugin_dns-0.22.5.dist-info/METADATA,sha256=U6WLJ-pstL29t54fKW1LAJMkq8r-rtSR8AtutZqxnHg,4572
|
|
115
|
+
netbox_plugin_dns-0.22.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
116
|
+
netbox_plugin_dns-0.22.5.dist-info/RECORD,,
|
|
File without changes
|