netbox-plugin-dns 1.1.2__py3-none-any.whl → 1.1.3__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 -5
- netbox_dns/api/nested_serializers.py +3 -2
- netbox_dns/api/serializers_/nameserver.py +2 -1
- netbox_dns/api/serializers_/record.py +5 -4
- netbox_dns/api/serializers_/record_template.py +2 -1
- netbox_dns/api/serializers_/view.py +2 -1
- netbox_dns/api/serializers_/zone.py +12 -11
- netbox_dns/api/serializers_/zone_template.py +8 -7
- netbox_dns/api/views.py +9 -4
- netbox_dns/choices/record.py +4 -2
- netbox_dns/choices/zone.py +6 -4
- netbox_dns/fields/address.py +2 -1
- netbox_dns/fields/network.py +2 -1
- netbox_dns/fields/rfc2317.py +7 -3
- netbox_dns/filtersets/nameserver.py +3 -2
- netbox_dns/filtersets/record.py +10 -9
- netbox_dns/filtersets/record_template.py +3 -2
- netbox_dns/filtersets/view.py +3 -2
- netbox_dns/filtersets/zone.py +24 -22
- netbox_dns/filtersets/zone_template.py +15 -14
- netbox_dns/forms/nameserver.py +41 -17
- netbox_dns/forms/record.py +43 -26
- netbox_dns/forms/record_template.py +49 -28
- netbox_dns/forms/registrar.py +21 -17
- netbox_dns/forms/registration_contact.py +37 -25
- netbox_dns/forms/view.py +49 -27
- netbox_dns/forms/zone.py +167 -120
- netbox_dns/forms/zone_template.py +53 -43
- netbox_dns/locale/de/LC_MESSAGES/django.mo +0 -0
- netbox_dns/locale/en/LC_MESSAGES/django.mo +0 -0
- netbox_dns/management/commands/rebuild_dnssync.py +14 -1
- netbox_dns/models/nameserver.py +6 -2
- netbox_dns/models/record.py +63 -30
- netbox_dns/models/record_template.py +16 -8
- netbox_dns/models/registrar.py +11 -7
- netbox_dns/models/registration_contact.py +23 -11
- netbox_dns/models/view.py +15 -6
- netbox_dns/models/zone.py +65 -43
- netbox_dns/models/zone_template.py +12 -10
- netbox_dns/navigation.py +30 -28
- netbox_dns/signals/ipam_dnssync.py +21 -14
- netbox_dns/tables/ipam_dnssync.py +2 -1
- netbox_dns/tables/nameserver.py +2 -0
- netbox_dns/tables/record.py +21 -11
- netbox_dns/tables/record_template.py +12 -5
- netbox_dns/tables/registrar.py +2 -0
- netbox_dns/tables/registration_contact.py +2 -0
- netbox_dns/tables/view.py +3 -1
- netbox_dns/tables/zone.py +15 -2
- netbox_dns/tables/zone_template.py +7 -0
- netbox_dns/templates/netbox_dns/nameserver.html +6 -5
- netbox_dns/templates/netbox_dns/record/managed.html +2 -1
- netbox_dns/templates/netbox_dns/record/related.html +26 -14
- netbox_dns/templates/netbox_dns/record.html +39 -20
- netbox_dns/templates/netbox_dns/recordtemplate.html +27 -15
- netbox_dns/templates/netbox_dns/registrar.html +11 -10
- netbox_dns/templates/netbox_dns/registrationcontact.html +16 -15
- netbox_dns/templates/netbox_dns/view/button.html +2 -1
- netbox_dns/templates/netbox_dns/view/prefix.html +7 -4
- netbox_dns/templates/netbox_dns/view/related.html +26 -10
- netbox_dns/templates/netbox_dns/view.html +11 -14
- netbox_dns/templates/netbox_dns/zone/base.html +2 -1
- netbox_dns/templates/netbox_dns/zone/child.html +3 -2
- netbox_dns/templates/netbox_dns/zone/record.html +3 -2
- netbox_dns/templates/netbox_dns/zone/registration.html +8 -7
- netbox_dns/templates/netbox_dns/zone.html +28 -30
- netbox_dns/templates/netbox_dns/zonetemplate.html +27 -17
- netbox_dns/utilities/ipam_dnssync.py +15 -4
- netbox_dns/validators/dns_name.py +11 -4
- netbox_dns/validators/dns_value.py +9 -4
- netbox_dns/validators/rfc2317.py +6 -3
- netbox_dns/views/nameserver.py +4 -2
- netbox_dns/views/record_template.py +4 -3
- netbox_dns/views/registrar.py +3 -1
- netbox_dns/views/registration_contact.py +2 -1
- netbox_dns/views/view.py +2 -1
- netbox_dns/views/zone.py +6 -4
- netbox_dns/views/zone_template.py +8 -7
- {netbox_plugin_dns-1.1.2.dist-info → netbox_plugin_dns-1.1.3.dist-info}/METADATA +1 -1
- netbox_plugin_dns-1.1.3.dist-info/RECORD +150 -0
- netbox_plugin_dns-1.1.2.dist-info/RECORD +0 -148
- {netbox_plugin_dns-1.1.2.dist-info → netbox_plugin_dns-1.1.3.dist-info}/LICENSE +0 -0
- {netbox_plugin_dns-1.1.2.dist-info → netbox_plugin_dns-1.1.3.dist-info}/WHEEL +0 -0
- {netbox_plugin_dns-1.1.2.dist-info → netbox_plugin_dns-1.1.3.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from django import forms
|
|
2
|
+
from django.utils.translation import gettext_lazy as _
|
|
2
3
|
|
|
3
4
|
from netbox.forms import (
|
|
4
5
|
NetBoxModelBulkEditForm,
|
|
@@ -14,7 +15,7 @@ from utilities.forms.fields import (
|
|
|
14
15
|
DynamicModelChoiceField,
|
|
15
16
|
)
|
|
16
17
|
from utilities.forms.rendering import FieldSet
|
|
17
|
-
from tenancy.models import Tenant
|
|
18
|
+
from tenancy.models import Tenant, TenantGroup
|
|
18
19
|
from tenancy.forms import TenancyForm, TenancyFilterForm
|
|
19
20
|
|
|
20
21
|
from netbox_dns.models import (
|
|
@@ -45,18 +46,18 @@ class ZoneTemplateForm(TenancyForm, NetBoxModelForm):
|
|
|
45
46
|
)
|
|
46
47
|
|
|
47
48
|
fieldsets = (
|
|
48
|
-
FieldSet("name", "description", "nameservers", name="Zone Template"),
|
|
49
|
-
FieldSet("record_templates", name="Record Templates"),
|
|
49
|
+
FieldSet("name", "description", "nameservers", name=_("Zone Template")),
|
|
50
|
+
FieldSet("record_templates", name=_("Record Templates")),
|
|
50
51
|
FieldSet(
|
|
51
52
|
"registrar",
|
|
52
53
|
"registrant",
|
|
53
54
|
"admin_c",
|
|
54
55
|
"tech_c",
|
|
55
56
|
"billing_c",
|
|
56
|
-
name="Domain Registration",
|
|
57
|
+
name=_("Domain Registration"),
|
|
57
58
|
),
|
|
58
|
-
FieldSet("
|
|
59
|
-
FieldSet("
|
|
59
|
+
FieldSet("tenant_group", "tenant", name=_("Tenancy")),
|
|
60
|
+
FieldSet("tags", name=_("Tags")),
|
|
60
61
|
)
|
|
61
62
|
|
|
62
63
|
class Meta:
|
|
@@ -67,13 +68,14 @@ class ZoneTemplateForm(TenancyForm, NetBoxModelForm):
|
|
|
67
68
|
"nameservers",
|
|
68
69
|
"record_templates",
|
|
69
70
|
"description",
|
|
70
|
-
"tags",
|
|
71
71
|
"registrar",
|
|
72
72
|
"registrant",
|
|
73
73
|
"admin_c",
|
|
74
74
|
"tech_c",
|
|
75
75
|
"billing_c",
|
|
76
|
+
"tenant_group",
|
|
76
77
|
"tenant",
|
|
78
|
+
"tags",
|
|
77
79
|
)
|
|
78
80
|
|
|
79
81
|
|
|
@@ -81,32 +83,32 @@ class ZoneTemplateFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|
|
81
83
|
model = ZoneTemplate
|
|
82
84
|
fieldsets = (
|
|
83
85
|
FieldSet("q", "filter_id", "tag"),
|
|
84
|
-
FieldSet("name", "nameserver_id", "description", name="Attributes"),
|
|
85
|
-
FieldSet("record_template_id", name="Record Templates"),
|
|
86
|
+
FieldSet("name", "nameserver_id", "description", name=_("Attributes")),
|
|
87
|
+
FieldSet("record_template_id", name=_("Record Templates")),
|
|
86
88
|
FieldSet(
|
|
87
89
|
"registrar_id",
|
|
88
90
|
"registrant_id",
|
|
89
91
|
"admin_c_id",
|
|
90
92
|
"tech_c_id",
|
|
91
93
|
"billing_c_id",
|
|
92
|
-
name="Registration",
|
|
94
|
+
name=_("Registration"),
|
|
93
95
|
),
|
|
94
|
-
FieldSet("tenant_group_id", "tenant_id", name="Tenancy"),
|
|
96
|
+
FieldSet("tenant_group_id", "tenant_id", name=_("Tenancy")),
|
|
95
97
|
)
|
|
96
98
|
|
|
97
99
|
name = forms.CharField(
|
|
98
100
|
required=False,
|
|
99
|
-
label="Template
|
|
101
|
+
label=_("Template Name"),
|
|
100
102
|
)
|
|
101
103
|
nameserver_id = DynamicModelMultipleChoiceField(
|
|
102
104
|
queryset=NameServer.objects.all(),
|
|
103
105
|
required=False,
|
|
104
|
-
label="Nameservers",
|
|
106
|
+
label=_("Nameservers"),
|
|
105
107
|
)
|
|
106
108
|
record_template_id = DynamicModelMultipleChoiceField(
|
|
107
109
|
queryset=RecordTemplate.objects.all(),
|
|
108
110
|
required=False,
|
|
109
|
-
label="Record
|
|
111
|
+
label=_("Record Templates"),
|
|
110
112
|
)
|
|
111
113
|
description = forms.CharField(
|
|
112
114
|
required=False,
|
|
@@ -114,27 +116,27 @@ class ZoneTemplateFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|
|
114
116
|
registrar_id = DynamicModelMultipleChoiceField(
|
|
115
117
|
queryset=Registrar.objects.all(),
|
|
116
118
|
required=False,
|
|
117
|
-
label="Registrar",
|
|
119
|
+
label=_("Registrar"),
|
|
118
120
|
)
|
|
119
121
|
registrant_id = DynamicModelMultipleChoiceField(
|
|
120
122
|
queryset=RegistrationContact.objects.all(),
|
|
121
123
|
required=False,
|
|
122
|
-
label="Registrant",
|
|
124
|
+
label=_("Registrant"),
|
|
123
125
|
)
|
|
124
126
|
admin_c_id = DynamicModelMultipleChoiceField(
|
|
125
127
|
queryset=RegistrationContact.objects.all(),
|
|
126
128
|
required=False,
|
|
127
|
-
label="
|
|
129
|
+
label=_("Administrative Contact"),
|
|
128
130
|
)
|
|
129
131
|
tech_c_id = DynamicModelMultipleChoiceField(
|
|
130
132
|
queryset=RegistrationContact.objects.all(),
|
|
131
133
|
required=False,
|
|
132
|
-
label="
|
|
134
|
+
label=_("Technical Contact"),
|
|
133
135
|
)
|
|
134
136
|
billing_c_id = DynamicModelMultipleChoiceField(
|
|
135
137
|
queryset=RegistrationContact.objects.all(),
|
|
136
138
|
required=False,
|
|
137
|
-
label="Billing
|
|
139
|
+
label=_("Billing Contact"),
|
|
138
140
|
)
|
|
139
141
|
tag = TagFilterField(ZoneTemplate)
|
|
140
142
|
|
|
@@ -144,64 +146,64 @@ class ZoneTemplateImportForm(NetBoxModelImportForm):
|
|
|
144
146
|
queryset=NameServer.objects.all(),
|
|
145
147
|
to_field_name="name",
|
|
146
148
|
required=False,
|
|
147
|
-
|
|
149
|
+
label=_("Nameservers"),
|
|
148
150
|
)
|
|
149
151
|
record_templates = CSVModelMultipleChoiceField(
|
|
150
152
|
queryset=RecordTemplate.objects.all(),
|
|
151
153
|
to_field_name="name",
|
|
152
154
|
required=False,
|
|
153
|
-
|
|
155
|
+
label=_("Record Remplates"),
|
|
154
156
|
)
|
|
155
157
|
registrar = CSVModelChoiceField(
|
|
156
158
|
queryset=Registrar.objects.all(),
|
|
157
159
|
required=False,
|
|
158
160
|
to_field_name="name",
|
|
159
|
-
help_text="Registrar the domain is registered with",
|
|
160
161
|
error_messages={
|
|
161
|
-
"invalid_choice": "Registrar not found.",
|
|
162
|
+
"invalid_choice": _("Registrar not found."),
|
|
162
163
|
},
|
|
164
|
+
label=_("Registrar"),
|
|
163
165
|
)
|
|
164
166
|
registrant = CSVModelChoiceField(
|
|
165
167
|
queryset=RegistrationContact.objects.all(),
|
|
166
168
|
required=False,
|
|
167
169
|
to_field_name="contact_id",
|
|
168
|
-
help_text="Owner of the domain",
|
|
169
170
|
error_messages={
|
|
170
|
-
"invalid_choice": "Registrant contact ID not found",
|
|
171
|
+
"invalid_choice": _("Registrant contact ID not found"),
|
|
171
172
|
},
|
|
173
|
+
label=_("Registrant"),
|
|
172
174
|
)
|
|
173
175
|
admin_c = CSVModelChoiceField(
|
|
174
176
|
queryset=RegistrationContact.objects.all(),
|
|
175
177
|
required=False,
|
|
176
178
|
to_field_name="contact_id",
|
|
177
|
-
help_text="Administrative contact for the domain",
|
|
178
179
|
error_messages={
|
|
179
|
-
"invalid_choice": "Administrative contact ID not found",
|
|
180
|
+
"invalid_choice": _("Administrative contact ID not found"),
|
|
180
181
|
},
|
|
182
|
+
label=_("Administrative Contact"),
|
|
181
183
|
)
|
|
182
184
|
tech_c = CSVModelChoiceField(
|
|
183
185
|
queryset=RegistrationContact.objects.all(),
|
|
184
186
|
required=False,
|
|
185
187
|
to_field_name="contact_id",
|
|
186
|
-
help_text="Technical contact for the domain",
|
|
187
188
|
error_messages={
|
|
188
|
-
"invalid_choice": "Technical contact ID not found",
|
|
189
|
+
"invalid_choice": _("Technical contact ID not found"),
|
|
189
190
|
},
|
|
191
|
+
label=_("Technical Contact"),
|
|
190
192
|
)
|
|
191
193
|
billing_c = CSVModelChoiceField(
|
|
192
194
|
queryset=RegistrationContact.objects.all(),
|
|
193
195
|
required=False,
|
|
194
196
|
to_field_name="contact_id",
|
|
195
|
-
help_text="Billing contact for the domain",
|
|
196
197
|
error_messages={
|
|
197
|
-
"invalid_choice": "Billing contact ID not found",
|
|
198
|
+
"invalid_choice": _("Billing contact ID not found"),
|
|
198
199
|
},
|
|
200
|
+
label=_("Billing Contact"),
|
|
199
201
|
)
|
|
200
202
|
tenant = CSVModelChoiceField(
|
|
201
203
|
queryset=Tenant.objects.all(),
|
|
202
204
|
required=False,
|
|
203
205
|
to_field_name="name",
|
|
204
|
-
|
|
206
|
+
label=_("Tenant"),
|
|
205
207
|
)
|
|
206
208
|
|
|
207
209
|
class Meta:
|
|
@@ -226,42 +228,49 @@ class ZoneTemplateBulkEditForm(NetBoxModelBulkEditForm):
|
|
|
226
228
|
nameservers = DynamicModelMultipleChoiceField(
|
|
227
229
|
queryset=NameServer.objects.all(),
|
|
228
230
|
required=False,
|
|
231
|
+
label=_("Nameservers"),
|
|
229
232
|
)
|
|
230
233
|
record_templates = DynamicModelMultipleChoiceField(
|
|
231
234
|
queryset=RecordTemplate.objects.all(),
|
|
232
235
|
required=False,
|
|
236
|
+
label=_("Record Templates"),
|
|
233
237
|
)
|
|
234
238
|
description = forms.CharField(max_length=200, required=False)
|
|
235
239
|
registrar = DynamicModelChoiceField(
|
|
236
240
|
queryset=Registrar.objects.all(),
|
|
237
241
|
required=False,
|
|
242
|
+
label=_("Registrar"),
|
|
238
243
|
)
|
|
239
244
|
registrant = DynamicModelChoiceField(
|
|
240
245
|
queryset=RegistrationContact.objects.all(),
|
|
241
246
|
required=False,
|
|
247
|
+
label=_("Registrant"),
|
|
242
248
|
)
|
|
243
249
|
admin_c = DynamicModelChoiceField(
|
|
244
250
|
queryset=RegistrationContact.objects.all(),
|
|
245
251
|
required=False,
|
|
246
|
-
label="Administrative Contact",
|
|
252
|
+
label=_("Administrative Contact"),
|
|
247
253
|
)
|
|
248
254
|
tech_c = DynamicModelChoiceField(
|
|
249
255
|
queryset=RegistrationContact.objects.all(),
|
|
250
256
|
required=False,
|
|
251
|
-
label="Technical Contact",
|
|
257
|
+
label=_("Technical Contact"),
|
|
252
258
|
)
|
|
253
259
|
billing_c = DynamicModelChoiceField(
|
|
254
260
|
queryset=RegistrationContact.objects.all(),
|
|
255
261
|
required=False,
|
|
256
|
-
label="Billing Contact",
|
|
262
|
+
label=_("Billing Contact"),
|
|
257
263
|
)
|
|
258
|
-
|
|
264
|
+
tenant_group = DynamicModelChoiceField(
|
|
265
|
+
queryset=TenantGroup.objects.all(),
|
|
266
|
+
required=False,
|
|
267
|
+
label=_("Tenant Group"),
|
|
268
|
+
)
|
|
269
|
+
tenant = DynamicModelChoiceField(
|
|
259
270
|
queryset=Tenant.objects.all(),
|
|
260
271
|
required=False,
|
|
261
|
-
|
|
262
|
-
help_text="Assigned tenant",
|
|
272
|
+
label=_("Tenant"),
|
|
263
273
|
)
|
|
264
|
-
tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False)
|
|
265
274
|
|
|
266
275
|
model = ZoneTemplate
|
|
267
276
|
|
|
@@ -269,11 +278,11 @@ class ZoneTemplateBulkEditForm(NetBoxModelBulkEditForm):
|
|
|
269
278
|
FieldSet(
|
|
270
279
|
"nameservers",
|
|
271
280
|
"description",
|
|
272
|
-
name="Attributes",
|
|
281
|
+
name=_("Attributes"),
|
|
273
282
|
),
|
|
274
283
|
FieldSet(
|
|
275
284
|
"record_templates",
|
|
276
|
-
name="Record Templates",
|
|
285
|
+
name=_("Record Templates"),
|
|
277
286
|
),
|
|
278
287
|
FieldSet(
|
|
279
288
|
"registrar",
|
|
@@ -281,9 +290,9 @@ class ZoneTemplateBulkEditForm(NetBoxModelBulkEditForm):
|
|
|
281
290
|
"admin_c",
|
|
282
291
|
"tech_c",
|
|
283
292
|
"billing_c",
|
|
284
|
-
name="Domain Registration",
|
|
293
|
+
name=_("Domain Registration"),
|
|
285
294
|
),
|
|
286
|
-
FieldSet("tenant_group", "tenant", name="Tenancy"),
|
|
295
|
+
FieldSet("tenant_group", "tenant", name=_("Tenancy")),
|
|
287
296
|
)
|
|
288
297
|
|
|
289
298
|
nullable_fields = (
|
|
@@ -295,4 +304,5 @@ class ZoneTemplateBulkEditForm(NetBoxModelBulkEditForm):
|
|
|
295
304
|
"admin_c",
|
|
296
305
|
"tech_c",
|
|
297
306
|
"billing_c",
|
|
307
|
+
"tenant",
|
|
298
308
|
)
|
|
Binary file
|
|
Binary file
|
|
@@ -8,6 +8,13 @@ from netbox_dns.utilities import update_dns_records
|
|
|
8
8
|
class Command(BaseCommand):
|
|
9
9
|
help = "Rebuild DNSsync relationships between IP addresses and records"
|
|
10
10
|
|
|
11
|
+
def add_arguments(self, parser):
|
|
12
|
+
parser.add_argument(
|
|
13
|
+
"--force",
|
|
14
|
+
action="store_true",
|
|
15
|
+
help="Update records even if DNS name was not changed (required for rebuilding filtered views",
|
|
16
|
+
)
|
|
17
|
+
|
|
11
18
|
def handle(self, *model_names, **options):
|
|
12
19
|
ip_addresses = IPAddress.objects.all()
|
|
13
20
|
for ip_address in ip_addresses:
|
|
@@ -15,4 +22,10 @@ class Command(BaseCommand):
|
|
|
15
22
|
self.stdout.write(
|
|
16
23
|
f"Updating DNS records for IP Address {ip_address}, VRF {ip_address.vrf}"
|
|
17
24
|
)
|
|
18
|
-
|
|
25
|
+
if (
|
|
26
|
+
update_dns_records(ip_address, force=options.get("force"))
|
|
27
|
+
and options.get("verbosity") >= 1
|
|
28
|
+
):
|
|
29
|
+
self.stdout.write(
|
|
30
|
+
f"Updated DNS records for IP Address {ip_address}, VRF {ip_address.vrf}"
|
|
31
|
+
)
|
netbox_dns/models/nameserver.py
CHANGED
|
@@ -4,6 +4,7 @@ from django.core.exceptions import ValidationError
|
|
|
4
4
|
from django.db import models, transaction
|
|
5
5
|
from django.db.models import Q
|
|
6
6
|
from django.urls import reverse
|
|
7
|
+
from django.utils.translation import gettext_lazy as _
|
|
7
8
|
|
|
8
9
|
from netbox.models import NetBoxModel
|
|
9
10
|
from netbox.search import SearchIndex, register_search
|
|
@@ -29,14 +30,17 @@ __all__ = (
|
|
|
29
30
|
|
|
30
31
|
class NameServer(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
31
32
|
name = models.CharField(
|
|
33
|
+
verbose_name=_("Name"),
|
|
32
34
|
unique=True,
|
|
33
35
|
max_length=255,
|
|
34
36
|
)
|
|
35
37
|
description = models.CharField(
|
|
38
|
+
verbose_name=_("Description"),
|
|
36
39
|
max_length=200,
|
|
37
40
|
blank=True,
|
|
38
41
|
)
|
|
39
42
|
tenant = models.ForeignKey(
|
|
43
|
+
verbose_name=_("Tenant"),
|
|
40
44
|
to="tenancy.Tenant",
|
|
41
45
|
on_delete=models.PROTECT,
|
|
42
46
|
related_name="netbox_dns_nameservers",
|
|
@@ -50,8 +54,8 @@ class NameServer(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
50
54
|
)
|
|
51
55
|
|
|
52
56
|
class Meta:
|
|
53
|
-
verbose_name = "Nameserver"
|
|
54
|
-
verbose_name_plural = "Nameservers"
|
|
57
|
+
verbose_name = _("Nameserver")
|
|
58
|
+
verbose_name_plural = _("Nameservers")
|
|
55
59
|
|
|
56
60
|
ordering = ("name",)
|
|
57
61
|
|
netbox_dns/models/record.py
CHANGED
|
@@ -8,6 +8,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
10
|
from django.conf import settings
|
|
11
|
+
from django.utils.translation import gettext_lazy as _
|
|
11
12
|
|
|
12
13
|
from netbox.models import NetBoxModel
|
|
13
14
|
from ipam.models import IPAddress
|
|
@@ -125,58 +126,67 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
125
126
|
)
|
|
126
127
|
|
|
127
128
|
name = models.CharField(
|
|
129
|
+
verbose_name=_("Name"),
|
|
128
130
|
max_length=255,
|
|
129
131
|
)
|
|
130
132
|
zone = models.ForeignKey(
|
|
131
|
-
"Zone",
|
|
133
|
+
verbose_name=_("Zone"),
|
|
134
|
+
to="Zone",
|
|
132
135
|
on_delete=models.CASCADE,
|
|
133
136
|
)
|
|
134
137
|
fqdn = models.CharField(
|
|
138
|
+
verbose_name=_("FQDN"),
|
|
135
139
|
max_length=255,
|
|
136
140
|
null=True,
|
|
137
141
|
blank=True,
|
|
138
142
|
default=None,
|
|
139
143
|
)
|
|
140
144
|
type = models.CharField(
|
|
145
|
+
verbose_name=_("Type"),
|
|
141
146
|
choices=RecordTypeChoices,
|
|
142
147
|
max_length=10,
|
|
143
148
|
)
|
|
144
149
|
value = models.CharField(
|
|
150
|
+
verbose_name=_("Value"),
|
|
145
151
|
max_length=65535,
|
|
146
152
|
)
|
|
147
153
|
status = models.CharField(
|
|
154
|
+
verbose_name=_("Status"),
|
|
148
155
|
max_length=50,
|
|
149
156
|
choices=RecordStatusChoices,
|
|
150
157
|
default=RecordStatusChoices.STATUS_ACTIVE,
|
|
151
158
|
blank=False,
|
|
152
159
|
)
|
|
153
160
|
ttl = models.PositiveIntegerField(
|
|
154
|
-
verbose_name="TTL",
|
|
161
|
+
verbose_name=_("TTL"),
|
|
155
162
|
null=True,
|
|
156
163
|
blank=True,
|
|
157
164
|
)
|
|
158
165
|
managed = models.BooleanField(
|
|
166
|
+
verbose_name=_("Managed"),
|
|
159
167
|
null=False,
|
|
160
168
|
default=False,
|
|
161
169
|
)
|
|
162
170
|
ptr_record = models.OneToOneField(
|
|
163
|
-
"
|
|
171
|
+
verbose_name="PTR Record",
|
|
172
|
+
to="self",
|
|
164
173
|
on_delete=models.SET_NULL,
|
|
165
174
|
related_name="address_record",
|
|
166
|
-
verbose_name="PTR record",
|
|
167
175
|
null=True,
|
|
168
176
|
blank=True,
|
|
169
177
|
)
|
|
170
178
|
disable_ptr = models.BooleanField(
|
|
171
|
-
verbose_name="Disable PTR",
|
|
172
|
-
help_text="Disable PTR record creation",
|
|
179
|
+
verbose_name=_("Disable PTR"),
|
|
180
|
+
help_text=_("Disable PTR record creation"),
|
|
173
181
|
default=False,
|
|
174
182
|
)
|
|
175
183
|
description = models.CharField(
|
|
184
|
+
verbose_name=_("Description"),
|
|
176
185
|
max_length=200,
|
|
177
186
|
blank=True,
|
|
178
187
|
)
|
|
179
188
|
tenant = models.ForeignKey(
|
|
189
|
+
verbose_name=_("Tenant"),
|
|
180
190
|
to="tenancy.Tenant",
|
|
181
191
|
on_delete=models.PROTECT,
|
|
182
192
|
related_name="netbox_dns_records",
|
|
@@ -184,13 +194,13 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
184
194
|
null=True,
|
|
185
195
|
)
|
|
186
196
|
ip_address = AddressField(
|
|
187
|
-
verbose_name="Related IP Address",
|
|
188
|
-
help_text="IP address related to an address (A/AAAA) or PTR record",
|
|
197
|
+
verbose_name=_("Related IP Address"),
|
|
198
|
+
help_text=_("IP address related to an address (A/AAAA) or PTR record"),
|
|
189
199
|
blank=True,
|
|
190
200
|
null=True,
|
|
191
201
|
)
|
|
192
202
|
ipam_ip_address = models.ForeignKey(
|
|
193
|
-
verbose_name="IPAM IP Address",
|
|
203
|
+
verbose_name=_("IPAM IP Address"),
|
|
194
204
|
to="ipam.IPAddress",
|
|
195
205
|
on_delete=models.CASCADE,
|
|
196
206
|
related_name="netbox_dns_records",
|
|
@@ -198,10 +208,10 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
198
208
|
null=True,
|
|
199
209
|
)
|
|
200
210
|
rfc2317_cname_record = models.ForeignKey(
|
|
201
|
-
"
|
|
211
|
+
verbose_name=_("RFC2317 CNAME Record"),
|
|
212
|
+
to="self",
|
|
202
213
|
on_delete=models.SET_NULL,
|
|
203
214
|
related_name="rfc2317_ptr_records",
|
|
204
|
-
verbose_name="RFC2317 CNAME record",
|
|
205
215
|
null=True,
|
|
206
216
|
blank=True,
|
|
207
217
|
)
|
|
@@ -221,8 +231,8 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
221
231
|
)
|
|
222
232
|
|
|
223
233
|
class Meta:
|
|
224
|
-
verbose_name = "Record"
|
|
225
|
-
verbose_name_plural = "Records"
|
|
234
|
+
verbose_name = _("Record")
|
|
235
|
+
verbose_name_plural = _("Records")
|
|
226
236
|
|
|
227
237
|
ordering = (
|
|
228
238
|
"fqdn",
|
|
@@ -537,7 +547,9 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
537
547
|
if not fqdn.is_subdomain(_zone):
|
|
538
548
|
raise ValidationError(
|
|
539
549
|
{
|
|
540
|
-
"name":
|
|
550
|
+
"name": _("{name} is not a name in {zone}").format(
|
|
551
|
+
name=self.name, zone=zone.name
|
|
552
|
+
),
|
|
541
553
|
}
|
|
542
554
|
)
|
|
543
555
|
|
|
@@ -581,13 +593,13 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
581
593
|
{
|
|
582
594
|
"name": exc,
|
|
583
595
|
}
|
|
584
|
-
)
|
|
596
|
+
)
|
|
585
597
|
|
|
586
598
|
def validate_value(self):
|
|
587
599
|
try:
|
|
588
600
|
validate_record_value(self.type, self.value)
|
|
589
601
|
except ValidationError as exc:
|
|
590
|
-
raise ValidationError({"value": exc})
|
|
602
|
+
raise ValidationError({"value": exc})
|
|
591
603
|
|
|
592
604
|
def check_unique_record(self, new_zone=None):
|
|
593
605
|
if not get_plugin_config("netbox_dns", "enforce_unique_records", False):
|
|
@@ -621,9 +633,13 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
621
633
|
|
|
622
634
|
raise ValidationError(
|
|
623
635
|
{
|
|
624
|
-
"value":
|
|
636
|
+
"value": _(
|
|
637
|
+
"There is already an active {type} record for name {name} in zone {zone} with value {value}."
|
|
638
|
+
).format(
|
|
639
|
+
type=self.type, name=self.name, zone=self.zone, value=self.value
|
|
640
|
+
)
|
|
625
641
|
}
|
|
626
|
-
)
|
|
642
|
+
)
|
|
627
643
|
|
|
628
644
|
def handle_conflicting_address_records(self):
|
|
629
645
|
if self.ipam_ip_address is None or not self.is_active:
|
|
@@ -680,9 +696,16 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
680
696
|
conflicting_ttls = ", ".join({str(record.ttl) for record in records})
|
|
681
697
|
raise ValidationError(
|
|
682
698
|
{
|
|
683
|
-
"ttl":
|
|
699
|
+
"ttl": _(
|
|
700
|
+
"There is at least one active {type} record for name {name} in zone {zone} and TTL is different ({ttls})."
|
|
701
|
+
).format(
|
|
702
|
+
type=self.type,
|
|
703
|
+
name=self.name,
|
|
704
|
+
zone=self.zone,
|
|
705
|
+
ttls=conflicting_ttls,
|
|
706
|
+
)
|
|
684
707
|
}
|
|
685
|
-
)
|
|
708
|
+
)
|
|
686
709
|
|
|
687
710
|
def update_rrset_ttl(self, ttl=None):
|
|
688
711
|
if self._state.adding:
|
|
@@ -760,24 +783,30 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
760
783
|
):
|
|
761
784
|
raise ValidationError(
|
|
762
785
|
{
|
|
763
|
-
"value":
|
|
786
|
+
"value": _(
|
|
787
|
+
"There is already an active record for name {name} in zone {zone}, RFC2317 CNAME is not allowed."
|
|
788
|
+
).format(name=ptr_cname_name, zone=ptr_cname_zone)
|
|
764
789
|
}
|
|
765
|
-
)
|
|
790
|
+
)
|
|
766
791
|
|
|
767
792
|
if self.type == RecordTypeChoices.SOA and self.name != "@":
|
|
768
793
|
raise ValidationError(
|
|
769
794
|
{
|
|
770
|
-
"name":
|
|
795
|
+
"name": _(
|
|
796
|
+
"SOA records are only allowed with name @ and are created automatically by NetBox DNS"
|
|
797
|
+
)
|
|
771
798
|
}
|
|
772
|
-
)
|
|
799
|
+
)
|
|
773
800
|
|
|
774
801
|
if self.type == RecordTypeChoices.CNAME:
|
|
775
802
|
if records.exclude(type=RecordTypeChoices.NSEC).exists():
|
|
776
803
|
raise ValidationError(
|
|
777
804
|
{
|
|
778
|
-
"type":
|
|
805
|
+
"type": _(
|
|
806
|
+
"There is already an active record for name {name} in zone {zone}, CNAME is not allowed."
|
|
807
|
+
).format(name=self.name, zone=self.zone)
|
|
779
808
|
}
|
|
780
|
-
)
|
|
809
|
+
)
|
|
781
810
|
|
|
782
811
|
elif (
|
|
783
812
|
records.filter(type=RecordTypeChoices.CNAME).exists()
|
|
@@ -785,17 +814,21 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
785
814
|
):
|
|
786
815
|
raise ValidationError(
|
|
787
816
|
{
|
|
788
|
-
"type":
|
|
817
|
+
"type": _(
|
|
818
|
+
"There is already an active CNAME record for name {name} in zone {zone}, no other record allowed."
|
|
819
|
+
).format(name=self.name, zone=self.zone)
|
|
789
820
|
}
|
|
790
|
-
)
|
|
821
|
+
)
|
|
791
822
|
|
|
792
823
|
elif self.type in RecordTypeChoices.SINGLETONS:
|
|
793
824
|
if records.filter(type=self.type).exists():
|
|
794
825
|
raise ValidationError(
|
|
795
826
|
{
|
|
796
|
-
"type":
|
|
827
|
+
"type": _(
|
|
828
|
+
"There is already an active {type} record for name {name} in zone {zone}, more than one are not allowed."
|
|
829
|
+
).format(type=self.type, name=self.name, zone=self.zone)
|
|
797
830
|
}
|
|
798
|
-
)
|
|
831
|
+
)
|
|
799
832
|
|
|
800
833
|
super().clean(*args, **kwargs)
|
|
801
834
|
|