netbox-plugin-dns 1.0.7__py3-none-any.whl → 1.1.0b1__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 +21 -5
- netbox_dns/api/nested_serializers.py +16 -17
- netbox_dns/api/serializers.py +1 -0
- netbox_dns/api/serializers_/prefix.py +18 -0
- netbox_dns/api/serializers_/record.py +0 -1
- netbox_dns/api/serializers_/view.py +34 -2
- netbox_dns/api/urls.py +3 -0
- netbox_dns/api/views.py +36 -0
- netbox_dns/fields/__init__.py +1 -0
- netbox_dns/fields/ipam.py +18 -0
- netbox_dns/filtersets/record.py +1 -1
- netbox_dns/filtersets/view.py +16 -0
- netbox_dns/forms/view.py +116 -4
- netbox_dns/forms/zone.py +0 -8
- netbox_dns/graphql/schema.py +40 -16
- netbox_dns/graphql/types.py +1 -0
- netbox_dns/management/commands/setup_autodns.py +120 -0
- netbox_dns/migrations/0007_view_prefixes.py +18 -0
- netbox_dns/models/__init__.py +0 -2
- netbox_dns/models/contact.py +3 -9
- netbox_dns/models/nameserver.py +3 -8
- netbox_dns/models/record.py +71 -17
- netbox_dns/models/record_template.py +1 -4
- netbox_dns/models/registrar.py +1 -7
- netbox_dns/models/view.py +7 -9
- netbox_dns/models/zone.py +28 -29
- netbox_dns/models/zone_template.py +5 -8
- netbox_dns/signals/ipam_autodns.py +138 -0
- netbox_dns/tables/contact.py +1 -0
- netbox_dns/tables/nameserver.py +7 -1
- netbox_dns/tables/record.py +30 -10
- netbox_dns/tables/record_template.py +17 -0
- netbox_dns/tables/registrar.py +2 -0
- netbox_dns/tables/view.py +32 -3
- netbox_dns/tables/zone.py +15 -0
- netbox_dns/tables/zone_template.py +16 -2
- netbox_dns/template_content.py +28 -39
- netbox_dns/templates/netbox_dns/record.html +6 -6
- netbox_dns/templates/netbox_dns/view/related.html +17 -0
- netbox_dns/templates/netbox_dns/view.html +29 -0
- netbox_dns/urls/contact.py +32 -10
- 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 +22 -8
- netbox_dns/urls/zone.py +46 -8
- netbox_dns/urls/zone_template.py +26 -16
- netbox_dns/utilities/__init__.py +2 -74
- netbox_dns/utilities/conversions.py +83 -0
- netbox_dns/utilities/ipam_autodns.py +205 -0
- netbox_dns/validators/dns_name.py +0 -9
- netbox_dns/views/contact.py +1 -0
- netbox_dns/views/nameserver.py +3 -7
- netbox_dns/views/record.py +2 -9
- netbox_dns/views/record_template.py +1 -1
- netbox_dns/views/registrar.py +1 -0
- netbox_dns/views/view.py +1 -6
- netbox_dns/views/zone.py +6 -7
- netbox_dns/views/zone_template.py +2 -2
- {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0b1.dist-info}/METADATA +2 -2
- {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0b1.dist-info}/RECORD +64 -61
- netbox_dns/management/commands/setup_coupling.py +0 -109
- netbox_dns/migrations/0007_alter_ordering_options.py +0 -25
- netbox_dns/signals/ipam_coupling.py +0 -168
- netbox_dns/templates/netbox_dns/related_dns_objects.html +0 -21
- netbox_dns/utilities/ipam_coupling.py +0 -112
- {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0b1.dist-info}/LICENSE +0 -0
- {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0b1.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from django.core.management.base import BaseCommand
|
|
2
|
+
|
|
3
|
+
from core.models import ObjectType
|
|
4
|
+
from extras.models import CustomField
|
|
5
|
+
from extras.choices import CustomFieldTypeChoices
|
|
6
|
+
from ipam.models import IPAddress
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Command(BaseCommand):
|
|
10
|
+
help = "Setup IPAddress custom fields for IPAM AutoDNS"
|
|
11
|
+
|
|
12
|
+
def add_arguments(self, parser):
|
|
13
|
+
parser.add_argument(
|
|
14
|
+
"--remove", action="store_true", default=False, help="Remove custom fields"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
def handle(self, *model_names, **options):
|
|
18
|
+
ipaddress_object_type = ObjectType.objects.get_for_model(IPAddress)
|
|
19
|
+
|
|
20
|
+
if options.get("remove"):
|
|
21
|
+
if options.get("verbosity"):
|
|
22
|
+
self.stdout.write(f"Trying to remove IPAM AutoDNS custom fields")
|
|
23
|
+
for cf in (
|
|
24
|
+
"ipaddress_dns_disabled",
|
|
25
|
+
"ipaddress_dns_record_ttl",
|
|
26
|
+
"ipaddress_dns_record_disable_ptr",
|
|
27
|
+
):
|
|
28
|
+
try:
|
|
29
|
+
CustomField.objects.get(
|
|
30
|
+
name=cf, object_types=ipaddress_object_type
|
|
31
|
+
).delete()
|
|
32
|
+
if options.get("verbosity") >= 2:
|
|
33
|
+
self.stdout.write(f"Custom field '{cf}' removed")
|
|
34
|
+
except CustomField.DoesNotExist:
|
|
35
|
+
pass
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
# +
|
|
39
|
+
# Remove pre-existing IPAM Coupling custom fields
|
|
40
|
+
# -
|
|
41
|
+
if options.get("verbosity"):
|
|
42
|
+
self.stdout.write(f"Trying to remove obsolete IPAM Coupling custom fields")
|
|
43
|
+
for cf in (
|
|
44
|
+
"ipaddress_dns_record_name",
|
|
45
|
+
"ipaddress_dns_zone_id",
|
|
46
|
+
):
|
|
47
|
+
try:
|
|
48
|
+
CustomField.objects.get(
|
|
49
|
+
name=cf, object_types=ipaddress_object_type
|
|
50
|
+
).delete()
|
|
51
|
+
if options.get("verbosity") >= 2:
|
|
52
|
+
self.stdout.write(f"Removed custom field '{cf}'")
|
|
53
|
+
except CustomField.DoesNotExist:
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
if options.get("verbosity"):
|
|
57
|
+
self.stdout.write(f"Creating IPAM AutoDNS custom fields")
|
|
58
|
+
|
|
59
|
+
if not CustomField.objects.filter(
|
|
60
|
+
name="ipaddress_dns_disabled",
|
|
61
|
+
type=CustomFieldTypeChoices.TYPE_BOOLEAN,
|
|
62
|
+
object_types=ipaddress_object_type,
|
|
63
|
+
).exists():
|
|
64
|
+
cf_disable_ptr = CustomField.objects.create(
|
|
65
|
+
name="ipaddress_dns_disabled",
|
|
66
|
+
label="Disable AutoDNS",
|
|
67
|
+
description="Disable DNS address and pointer record generation for this address",
|
|
68
|
+
type=CustomFieldTypeChoices.TYPE_BOOLEAN,
|
|
69
|
+
required=False,
|
|
70
|
+
default=False,
|
|
71
|
+
group_name="AutoDNS",
|
|
72
|
+
is_cloneable=True,
|
|
73
|
+
weight=100,
|
|
74
|
+
)
|
|
75
|
+
cf_disable_ptr.object_types.set([ipaddress_object_type])
|
|
76
|
+
if options.get("verbosity") >= 2:
|
|
77
|
+
self.stdout.write("Created custom field 'ipaddress_dns_disabled'")
|
|
78
|
+
|
|
79
|
+
if not CustomField.objects.filter(
|
|
80
|
+
name="ipaddress_dns_record_ttl",
|
|
81
|
+
type=CustomFieldTypeChoices.TYPE_INTEGER,
|
|
82
|
+
object_types=ipaddress_object_type,
|
|
83
|
+
).exists():
|
|
84
|
+
cf_ttl = CustomField.objects.create(
|
|
85
|
+
name="ipaddress_dns_record_ttl",
|
|
86
|
+
description="TTL for DNS records created for this address",
|
|
87
|
+
label="TTL",
|
|
88
|
+
type=CustomFieldTypeChoices.TYPE_INTEGER,
|
|
89
|
+
validation_minimum=0,
|
|
90
|
+
validation_maximum=2147483647,
|
|
91
|
+
required=False,
|
|
92
|
+
group_name="AutoDNS",
|
|
93
|
+
is_cloneable=True,
|
|
94
|
+
weight=200,
|
|
95
|
+
)
|
|
96
|
+
cf_ttl.object_types.set([ipaddress_object_type])
|
|
97
|
+
if options.get("verbosity") >= 2:
|
|
98
|
+
self.stdout.write("Created custom field 'ipaddress_dns_record_ttl'")
|
|
99
|
+
|
|
100
|
+
if not CustomField.objects.filter(
|
|
101
|
+
name="ipaddress_dns_record_disable_ptr",
|
|
102
|
+
type=CustomFieldTypeChoices.TYPE_BOOLEAN,
|
|
103
|
+
object_types=ipaddress_object_type,
|
|
104
|
+
).exists():
|
|
105
|
+
cf_disable_ptr = CustomField.objects.create(
|
|
106
|
+
name="ipaddress_dns_record_disable_ptr",
|
|
107
|
+
description="Disable DNS PTR record generation for this address",
|
|
108
|
+
label="Disable PTR",
|
|
109
|
+
type=CustomFieldTypeChoices.TYPE_BOOLEAN,
|
|
110
|
+
required=False,
|
|
111
|
+
default=False,
|
|
112
|
+
group_name="AutoDNS",
|
|
113
|
+
is_cloneable=True,
|
|
114
|
+
weight=300,
|
|
115
|
+
)
|
|
116
|
+
cf_disable_ptr.object_types.set([ipaddress_object_type])
|
|
117
|
+
if options.get("verbosity") >= 2:
|
|
118
|
+
self.stdout.write(
|
|
119
|
+
"Created custom field 'ipaddress_dns_record_disable_ptr'"
|
|
120
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from django.db import migrations, models
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Migration(migrations.Migration):
|
|
5
|
+
dependencies = [
|
|
6
|
+
("ipam", "0067_ipaddress_index_host"),
|
|
7
|
+
("netbox_dns", "0006_templating"),
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
operations = [
|
|
11
|
+
migrations.AddField(
|
|
12
|
+
model_name="view",
|
|
13
|
+
name="prefixes",
|
|
14
|
+
field=models.ManyToManyField(
|
|
15
|
+
blank=True, related_name="netbox_dns_views", to="ipam.prefix"
|
|
16
|
+
),
|
|
17
|
+
),
|
|
18
|
+
]
|
netbox_dns/models/__init__.py
CHANGED
netbox_dns/models/contact.py
CHANGED
|
@@ -87,7 +87,7 @@ class Contact(NetBoxModel):
|
|
|
87
87
|
related_name="netbox_dns_contact_set",
|
|
88
88
|
)
|
|
89
89
|
|
|
90
|
-
clone_fields =
|
|
90
|
+
clone_fields = [
|
|
91
91
|
"name",
|
|
92
92
|
"description",
|
|
93
93
|
"organization",
|
|
@@ -102,7 +102,7 @@ class Contact(NetBoxModel):
|
|
|
102
102
|
"fax_ext",
|
|
103
103
|
"email",
|
|
104
104
|
"tags",
|
|
105
|
-
|
|
105
|
+
]
|
|
106
106
|
|
|
107
107
|
def get_absolute_url(self):
|
|
108
108
|
return reverse("plugins:netbox_dns:contact", kwargs={"pk": self.pk})
|
|
@@ -114,13 +114,7 @@ class Contact(NetBoxModel):
|
|
|
114
114
|
return self.contact_id
|
|
115
115
|
|
|
116
116
|
class Meta:
|
|
117
|
-
|
|
118
|
-
verbose_name_plural = "Contacts"
|
|
119
|
-
|
|
120
|
-
ordering = (
|
|
121
|
-
"name",
|
|
122
|
-
"contact_id",
|
|
123
|
-
)
|
|
117
|
+
ordering = ("name", "contact_id")
|
|
124
118
|
|
|
125
119
|
@property
|
|
126
120
|
def zones(self):
|
netbox_dns/models/nameserver.py
CHANGED
|
@@ -7,7 +7,6 @@ from django.urls import reverse
|
|
|
7
7
|
|
|
8
8
|
from netbox.models import NetBoxModel
|
|
9
9
|
from netbox.search import SearchIndex, register_search
|
|
10
|
-
from netbox.models.features import ContactsMixin
|
|
11
10
|
|
|
12
11
|
from netbox_dns.utilities import (
|
|
13
12
|
name_to_unicode,
|
|
@@ -27,7 +26,7 @@ __all__ = (
|
|
|
27
26
|
)
|
|
28
27
|
|
|
29
28
|
|
|
30
|
-
class NameServer(ObjectModificationMixin,
|
|
29
|
+
class NameServer(ObjectModificationMixin, NetBoxModel):
|
|
31
30
|
name = models.CharField(
|
|
32
31
|
unique=True,
|
|
33
32
|
max_length=255,
|
|
@@ -44,17 +43,13 @@ class NameServer(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
44
43
|
null=True,
|
|
45
44
|
)
|
|
46
45
|
|
|
47
|
-
clone_fields =
|
|
48
|
-
"name",
|
|
49
|
-
"description",
|
|
50
|
-
)
|
|
46
|
+
clone_fields = ["name", "description"]
|
|
51
47
|
|
|
52
48
|
class Meta:
|
|
49
|
+
ordering = ("name",)
|
|
53
50
|
verbose_name = "Nameserver"
|
|
54
51
|
verbose_name_plural = "Nameservers"
|
|
55
52
|
|
|
56
|
-
ordering = ("name",)
|
|
57
|
-
|
|
58
53
|
def __str__(self):
|
|
59
54
|
try:
|
|
60
55
|
return dns_name.from_text(self.name, origin=None).to_unicode()
|
netbox_dns/models/record.py
CHANGED
|
@@ -7,9 +7,9 @@ from django.core.exceptions import ValidationError
|
|
|
7
7
|
from django.db import transaction, models
|
|
8
8
|
from django.db.models import Q, ExpressionWrapper, BooleanField, Min
|
|
9
9
|
from django.urls import reverse
|
|
10
|
+
from django.conf import settings
|
|
10
11
|
|
|
11
12
|
from netbox.models import NetBoxModel
|
|
12
|
-
from netbox.models.features import ContactsMixin
|
|
13
13
|
from netbox.search import SearchIndex, register_search
|
|
14
14
|
from netbox.plugins.utils import get_plugin_config
|
|
15
15
|
from utilities.querysets import RestrictedQuerySet
|
|
@@ -36,6 +36,43 @@ def min_ttl(*ttl_list):
|
|
|
36
36
|
return min((ttl for ttl in ttl_list if ttl is not None), default=None)
|
|
37
37
|
|
|
38
38
|
|
|
39
|
+
def record_data_from_ip_address(ip_address, zone):
|
|
40
|
+
cf_data = ip_address.custom_field_data
|
|
41
|
+
|
|
42
|
+
if cf_data.get("ipaddress_dns_disabled"):
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
data = {
|
|
46
|
+
"name": (
|
|
47
|
+
dns_name.from_text(ip_address.dns_name)
|
|
48
|
+
.relativize(dns_name.from_text(zone.name))
|
|
49
|
+
.to_text()
|
|
50
|
+
),
|
|
51
|
+
"type": (
|
|
52
|
+
RecordTypeChoices.A
|
|
53
|
+
if ip_address.address.version == 4
|
|
54
|
+
else RecordTypeChoices.AAAA
|
|
55
|
+
),
|
|
56
|
+
"value": str(ip_address.address.ip),
|
|
57
|
+
"status": (
|
|
58
|
+
RecordStatusChoices.STATUS_ACTIVE
|
|
59
|
+
if ip_address.status
|
|
60
|
+
in settings.PLUGINS_CONFIG["netbox_dns"].get(
|
|
61
|
+
"autodns_ipaddress_active_status", []
|
|
62
|
+
)
|
|
63
|
+
else RecordStatusChoices.STATUS_INACTIVE
|
|
64
|
+
),
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if "ipaddress_dns_record_ttl" in cf_data:
|
|
68
|
+
data["ttl"] = cf_data.get("ipaddress_dns_record_ttl")
|
|
69
|
+
|
|
70
|
+
if (disable_ptr := cf_data.get("ipaddress_dns_record_disable_ptr")) is not None:
|
|
71
|
+
data["disable_ptr"] = disable_ptr
|
|
72
|
+
|
|
73
|
+
return data
|
|
74
|
+
|
|
75
|
+
|
|
39
76
|
class RecordManager(models.Manager.from_queryset(RestrictedQuerySet)):
|
|
40
77
|
"""
|
|
41
78
|
Custom manager for records providing the activity status annotation
|
|
@@ -63,7 +100,7 @@ class RecordManager(models.Manager.from_queryset(RestrictedQuerySet)):
|
|
|
63
100
|
)
|
|
64
101
|
|
|
65
102
|
|
|
66
|
-
class Record(ObjectModificationMixin,
|
|
103
|
+
class Record(ObjectModificationMixin, NetBoxModel):
|
|
67
104
|
ACTIVE_STATUS_LIST = (RecordStatusChoices.STATUS_ACTIVE,)
|
|
68
105
|
|
|
69
106
|
unique_ptr_qs = Q(
|
|
@@ -156,7 +193,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
156
193
|
objects = RecordManager()
|
|
157
194
|
raw_objects = RestrictedQuerySet.as_manager()
|
|
158
195
|
|
|
159
|
-
clone_fields =
|
|
196
|
+
clone_fields = [
|
|
160
197
|
"zone",
|
|
161
198
|
"type",
|
|
162
199
|
"name",
|
|
@@ -165,20 +202,10 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
165
202
|
"ttl",
|
|
166
203
|
"disable_ptr",
|
|
167
204
|
"description",
|
|
168
|
-
|
|
205
|
+
]
|
|
169
206
|
|
|
170
207
|
class Meta:
|
|
171
|
-
|
|
172
|
-
verbose_name_plural = "Records"
|
|
173
|
-
|
|
174
|
-
ordering = (
|
|
175
|
-
"fqdn",
|
|
176
|
-
"zone",
|
|
177
|
-
"name",
|
|
178
|
-
"type",
|
|
179
|
-
"value",
|
|
180
|
-
"status",
|
|
181
|
-
)
|
|
208
|
+
ordering = ("zone", "name", "type", "value", "status")
|
|
182
209
|
|
|
183
210
|
def __str__(self):
|
|
184
211
|
try:
|
|
@@ -434,6 +461,35 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
434
461
|
self.rfc2317_cname_record.delete(save_zone_serial=save_zone_serial)
|
|
435
462
|
self.rfc2317_cname_record = None
|
|
436
463
|
|
|
464
|
+
def update_from_ip_address(self, ip_address):
|
|
465
|
+
data = record_data_from_ip_address(ip_address, self.zone)
|
|
466
|
+
|
|
467
|
+
if data is None:
|
|
468
|
+
self.delete()
|
|
469
|
+
return
|
|
470
|
+
|
|
471
|
+
if all((getattr(self, attr) == data[attr] for attr in data.keys())):
|
|
472
|
+
return
|
|
473
|
+
|
|
474
|
+
for attr, value in data.items():
|
|
475
|
+
setattr(self, attr, value)
|
|
476
|
+
|
|
477
|
+
return self
|
|
478
|
+
|
|
479
|
+
@classmethod
|
|
480
|
+
def create_from_ip_address(cls, ip_address, zone):
|
|
481
|
+
data = record_data_from_ip_address(ip_address, zone)
|
|
482
|
+
|
|
483
|
+
if data is None:
|
|
484
|
+
return
|
|
485
|
+
|
|
486
|
+
return Record(
|
|
487
|
+
zone=zone,
|
|
488
|
+
managed=True,
|
|
489
|
+
ipam_ip_address=ip_address,
|
|
490
|
+
**data,
|
|
491
|
+
)
|
|
492
|
+
|
|
437
493
|
def validate_name(self):
|
|
438
494
|
try:
|
|
439
495
|
_zone = dns_name.from_text(self.zone.name, origin=dns_name.root)
|
|
@@ -531,7 +587,6 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
531
587
|
)
|
|
532
588
|
.exclude(ttl=self.ttl)
|
|
533
589
|
.exclude(type=RecordTypeChoices.PTR, managed=True)
|
|
534
|
-
.exclude(status=RecordStatusChoices.STATUS_INACTIVE)
|
|
535
590
|
)
|
|
536
591
|
|
|
537
592
|
if not records.exists():
|
|
@@ -566,7 +621,6 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
566
621
|
.exclude(pk=self.pk)
|
|
567
622
|
.exclude(ttl=ttl)
|
|
568
623
|
.exclude(type=RecordTypeChoices.PTR, managed=True)
|
|
569
|
-
.exclude(status=RecordStatusChoices.STATUS_INACTIVE)
|
|
570
624
|
)
|
|
571
625
|
|
|
572
626
|
for record in records:
|
netbox_dns/models/registrar.py
CHANGED
|
@@ -59,13 +59,7 @@ class Registrar(NetBoxModel):
|
|
|
59
59
|
return str(self.name)
|
|
60
60
|
|
|
61
61
|
class Meta:
|
|
62
|
-
|
|
63
|
-
verbose_name_plural = "Registrars"
|
|
64
|
-
|
|
65
|
-
ordering = (
|
|
66
|
-
"name",
|
|
67
|
-
"iana_id",
|
|
68
|
-
)
|
|
62
|
+
ordering = ("name", "iana_id")
|
|
69
63
|
|
|
70
64
|
|
|
71
65
|
@register_search
|
netbox_dns/models/view.py
CHANGED
|
@@ -3,7 +3,6 @@ from django.urls import reverse
|
|
|
3
3
|
from django.core.exceptions import ValidationError
|
|
4
4
|
|
|
5
5
|
from netbox.models import NetBoxModel
|
|
6
|
-
from netbox.models.features import ContactsMixin
|
|
7
6
|
from netbox.search import SearchIndex, register_search
|
|
8
7
|
from netbox.context import current_request
|
|
9
8
|
from utilities.exceptions import AbortRequest
|
|
@@ -17,7 +16,7 @@ __all__ = (
|
|
|
17
16
|
)
|
|
18
17
|
|
|
19
18
|
|
|
20
|
-
class View(ObjectModificationMixin,
|
|
19
|
+
class View(ObjectModificationMixin, NetBoxModel):
|
|
21
20
|
name = models.CharField(
|
|
22
21
|
unique=True,
|
|
23
22
|
max_length=255,
|
|
@@ -29,6 +28,11 @@ class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
29
28
|
default_view = models.BooleanField(
|
|
30
29
|
default=False,
|
|
31
30
|
)
|
|
31
|
+
prefixes = models.ManyToManyField(
|
|
32
|
+
to="ipam.Prefix",
|
|
33
|
+
related_name="netbox_dns_views",
|
|
34
|
+
blank=True,
|
|
35
|
+
)
|
|
32
36
|
tenant = models.ForeignKey(
|
|
33
37
|
to="tenancy.Tenant",
|
|
34
38
|
on_delete=models.PROTECT,
|
|
@@ -37,10 +41,7 @@ class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
37
41
|
null=True,
|
|
38
42
|
)
|
|
39
43
|
|
|
40
|
-
clone_fields =
|
|
41
|
-
"name",
|
|
42
|
-
"description",
|
|
43
|
-
)
|
|
44
|
+
clone_fields = ["name", "description"]
|
|
44
45
|
|
|
45
46
|
@classmethod
|
|
46
47
|
def get_default_view(cls):
|
|
@@ -53,9 +54,6 @@ class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
53
54
|
return str(self.name)
|
|
54
55
|
|
|
55
56
|
class Meta:
|
|
56
|
-
verbose_name = "View"
|
|
57
|
-
verbose_name_plural = "Views"
|
|
58
|
-
|
|
59
57
|
ordering = ("name",)
|
|
60
58
|
|
|
61
59
|
def delete(self, *args, **kwargs):
|
netbox_dns/models/zone.py
CHANGED
|
@@ -19,7 +19,6 @@ from django.dispatch import receiver
|
|
|
19
19
|
from django.conf import settings
|
|
20
20
|
|
|
21
21
|
from netbox.models import NetBoxModel
|
|
22
|
-
from netbox.models.features import ContactsMixin
|
|
23
22
|
from netbox.search import SearchIndex, register_search
|
|
24
23
|
from netbox.plugins.utils import get_plugin_config
|
|
25
24
|
from utilities.querysets import RestrictedQuerySet
|
|
@@ -28,6 +27,8 @@ from ipam.models import IPAddress
|
|
|
28
27
|
from netbox_dns.choices import RecordClassChoices, RecordTypeChoices, ZoneStatusChoices
|
|
29
28
|
from netbox_dns.fields import NetworkField, RFC2317NetworkField
|
|
30
29
|
from netbox_dns.utilities import (
|
|
30
|
+
update_dns_records,
|
|
31
|
+
get_ip_addresses_by_zone,
|
|
31
32
|
arpa_to_prefix,
|
|
32
33
|
name_to_unicode,
|
|
33
34
|
normalize_name,
|
|
@@ -35,7 +36,6 @@ from netbox_dns.utilities import (
|
|
|
35
36
|
)
|
|
36
37
|
from netbox_dns.validators import (
|
|
37
38
|
validate_fqdn,
|
|
38
|
-
validate_rname,
|
|
39
39
|
validate_domain_name,
|
|
40
40
|
)
|
|
41
41
|
from netbox_dns.mixins import ObjectModificationMixin
|
|
@@ -68,7 +68,7 @@ class ZoneManager(models.Manager.from_queryset(RestrictedQuerySet)):
|
|
|
68
68
|
)
|
|
69
69
|
|
|
70
70
|
|
|
71
|
-
class Zone(ObjectModificationMixin,
|
|
71
|
+
class Zone(ObjectModificationMixin, NetBoxModel):
|
|
72
72
|
ACTIVE_STATUS_LIST = (ZoneStatusChoices.STATUS_ACTIVE,)
|
|
73
73
|
|
|
74
74
|
def __init__(self, *args, **kwargs):
|
|
@@ -247,7 +247,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
247
247
|
|
|
248
248
|
objects = ZoneManager()
|
|
249
249
|
|
|
250
|
-
clone_fields =
|
|
250
|
+
clone_fields = [
|
|
251
251
|
"view",
|
|
252
252
|
"name",
|
|
253
253
|
"status",
|
|
@@ -261,12 +261,9 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
261
261
|
"soa_expire",
|
|
262
262
|
"soa_minimum",
|
|
263
263
|
"description",
|
|
264
|
-
|
|
264
|
+
]
|
|
265
265
|
|
|
266
266
|
class Meta:
|
|
267
|
-
verbose_name = "Zone"
|
|
268
|
-
verbose_name_plural = "Zones"
|
|
269
|
-
|
|
270
267
|
ordering = (
|
|
271
268
|
"view",
|
|
272
269
|
"name",
|
|
@@ -623,7 +620,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
623
620
|
raise ValidationError("soa_rname not set and no default value defined")
|
|
624
621
|
try:
|
|
625
622
|
dns_name.from_text(self.soa_rname, origin=dns_name.root)
|
|
626
|
-
|
|
623
|
+
validate_fqdn(self.soa_rname)
|
|
627
624
|
except (DNSException, ValidationError) as exc:
|
|
628
625
|
raise ValidationError(
|
|
629
626
|
{
|
|
@@ -643,7 +640,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
643
640
|
old_zone = Zone.objects.get(pk=self.pk)
|
|
644
641
|
if not self.soa_serial_auto:
|
|
645
642
|
self.check_soa_serial_increment(old_zone.soa_serial, self.soa_serial)
|
|
646
|
-
|
|
643
|
+
else:
|
|
647
644
|
try:
|
|
648
645
|
self.check_soa_serial_increment(
|
|
649
646
|
old_zone.soa_serial, self.get_auto_serial()
|
|
@@ -773,17 +770,6 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
773
770
|
):
|
|
774
771
|
address_record.save(update_fields=["ptr_record"])
|
|
775
772
|
|
|
776
|
-
# Fix name in IP Address when zone name is changed
|
|
777
|
-
if (
|
|
778
|
-
get_plugin_config("netbox_dns", "feature_ipam_coupling")
|
|
779
|
-
and "name" in changed_fields
|
|
780
|
-
):
|
|
781
|
-
for ip in IPAddress.objects.filter(
|
|
782
|
-
custom_field_data__ipaddress_dns_zone_id=self.pk
|
|
783
|
-
):
|
|
784
|
-
ip.dns_name = f'{ip.custom_field_data["ipaddress_dns_record_name"]}.{self.name}'
|
|
785
|
-
ip.save(update_fields=["dns_name"])
|
|
786
|
-
|
|
787
773
|
if changed_fields is not None and "name" in changed_fields:
|
|
788
774
|
for _record in self.record_set.all():
|
|
789
775
|
_record.save(
|
|
@@ -793,6 +779,16 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
793
779
|
update_rfc2317_cname=False,
|
|
794
780
|
)
|
|
795
781
|
|
|
782
|
+
if changed_fields is None or {"name", "view"} & changed_fields:
|
|
783
|
+
update_ip_addresses = IPAddress.objects.filter(
|
|
784
|
+
pk__in=self.record_set.filter(
|
|
785
|
+
ipam_ip_address__isnull=False
|
|
786
|
+
).values_list("ipam_ip_address", flat=True)
|
|
787
|
+
)
|
|
788
|
+
update_ip_addresses |= get_ip_addresses_by_zone(self)
|
|
789
|
+
for ip_address in update_ip_addresses:
|
|
790
|
+
update_dns_records(ip_address)
|
|
791
|
+
|
|
796
792
|
self.save_soa_serial()
|
|
797
793
|
self.update_soa_record()
|
|
798
794
|
|
|
@@ -828,14 +824,13 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
828
824
|
self.rfc2317_child_zones.all().values_list("pk", flat=True)
|
|
829
825
|
)
|
|
830
826
|
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
ip.save(update_fields=["dns_name", "custom_field_data"])
|
|
827
|
+
ipam_ip_addresses = list(
|
|
828
|
+
IPAddress.objects.filter(
|
|
829
|
+
netbox_dns_records__in=self.record_set.filter(
|
|
830
|
+
ipam_ip_address__isnull=False
|
|
831
|
+
)
|
|
832
|
+
).values_list("pk", flat=True)
|
|
833
|
+
)
|
|
839
834
|
|
|
840
835
|
super().delete(*args, **kwargs)
|
|
841
836
|
|
|
@@ -849,6 +844,10 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
849
844
|
address_zone.save_soa_serial()
|
|
850
845
|
address_zone.update_soa_record()
|
|
851
846
|
|
|
847
|
+
ip_addresses = IPAddress.objects.filter(pk__in=ipam_ip_addresses)
|
|
848
|
+
for ip_address in ip_addresses:
|
|
849
|
+
update_dns_records(ip_address)
|
|
850
|
+
|
|
852
851
|
rfc2317_child_zones = Zone.objects.filter(pk__in=rfc2317_child_zones)
|
|
853
852
|
if rfc2317_child_zones:
|
|
854
853
|
for child_zone in rfc2317_child_zones:
|
|
@@ -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)
|