netbox-plugin-dns 1.2.3__py3-none-any.whl → 1.2.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 +2 -1
- netbox_dns/api/serializers_/zone.py +14 -2
- netbox_dns/api/serializers_/zone_template.py +9 -0
- netbox_dns/filtersets/zone_template.py +14 -0
- netbox_dns/forms/record_template.py +4 -0
- netbox_dns/forms/zone.py +42 -9
- netbox_dns/forms/zone_template.py +54 -2
- netbox_dns/graphql/types.py +7 -0
- netbox_dns/locale/de/LC_MESSAGES/django.mo +0 -0
- netbox_dns/migrations/0013_zonetemplate_soa_mname_zonetemplate_soa_rname.py +30 -0
- netbox_dns/migrations/0014_alter_unique_constraints_lowercase.py +42 -0
- netbox_dns/models/nameserver.py +20 -2
- netbox_dns/models/record.py +20 -10
- netbox_dns/models/record_template.py +11 -5
- netbox_dns/models/registrar.py +1 -0
- netbox_dns/models/registration_contact.py +1 -0
- netbox_dns/models/view.py +1 -0
- netbox_dns/models/zone.py +54 -21
- netbox_dns/models/zone_template.py +44 -10
- netbox_dns/tables/zone_template.py +8 -1
- netbox_dns/templates/netbox_dns/zonetemplate.html +8 -0
- netbox_dns/utilities/conversions.py +5 -0
- netbox_dns/utilities/dns.py +1 -2
- netbox_dns/utilities/ipam_dnssync.py +5 -2
- netbox_dns/views/record.py +17 -7
- {netbox_plugin_dns-1.2.3.dist-info → netbox_plugin_dns-1.2.5.dist-info}/METADATA +2 -2
- {netbox_plugin_dns-1.2.3.dist-info → netbox_plugin_dns-1.2.5.dist-info}/RECORD +30 -28
- {netbox_plugin_dns-1.2.3.dist-info → netbox_plugin_dns-1.2.5.dist-info}/WHEEL +1 -1
- {netbox_plugin_dns-1.2.3.dist-info → netbox_plugin_dns-1.2.5.dist-info}/LICENSE +0 -0
- {netbox_plugin_dns-1.2.3.dist-info → netbox_plugin_dns-1.2.5.dist-info}/top_level.txt +0 -0
netbox_dns/__init__.py
CHANGED
|
@@ -7,7 +7,7 @@ from ipam.choices import IPAddressStatusChoices
|
|
|
7
7
|
|
|
8
8
|
from netbox_dns.choices import RecordTypeChoices, RecordStatusChoices, ZoneStatusChoices
|
|
9
9
|
|
|
10
|
-
__version__ = "1.2.
|
|
10
|
+
__version__ = "1.2.5"
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def _check_list(setting):
|
|
@@ -64,6 +64,7 @@ class DNSConfig(PluginConfig):
|
|
|
64
64
|
"enforce_unique_rrset_ttl": True,
|
|
65
65
|
"menu_name": "DNS",
|
|
66
66
|
"top_level_menu": True,
|
|
67
|
+
"convert_names_to_lowercase": False,
|
|
67
68
|
}
|
|
68
69
|
base_url = "netbox-dns"
|
|
69
70
|
|
|
@@ -44,6 +44,12 @@ class ZoneSerializer(NetBoxModelSerializer):
|
|
|
44
44
|
required=False,
|
|
45
45
|
help_text=_("Primary nameserver for the zone"),
|
|
46
46
|
)
|
|
47
|
+
soa_rname = serializers.CharField(
|
|
48
|
+
max_length=255,
|
|
49
|
+
read_only=False,
|
|
50
|
+
required=False,
|
|
51
|
+
help_text=_("Contact email for the zone"),
|
|
52
|
+
)
|
|
47
53
|
rfc2317_parent_zone = NestedZoneSerializer(
|
|
48
54
|
many=False,
|
|
49
55
|
read_only=True,
|
|
@@ -105,6 +111,12 @@ class ZoneSerializer(NetBoxModelSerializer):
|
|
|
105
111
|
)
|
|
106
112
|
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
|
107
113
|
|
|
114
|
+
def validate(self, data):
|
|
115
|
+
if (template := data.get("template")) is not None:
|
|
116
|
+
template.apply_to_zone_data(data)
|
|
117
|
+
|
|
118
|
+
return super().validate(data)
|
|
119
|
+
|
|
108
120
|
def create(self, validated_data):
|
|
109
121
|
template = validated_data.pop("template", None)
|
|
110
122
|
nameservers = validated_data.pop("nameservers", None)
|
|
@@ -115,7 +127,7 @@ class ZoneSerializer(NetBoxModelSerializer):
|
|
|
115
127
|
zone.nameservers.set(nameservers)
|
|
116
128
|
|
|
117
129
|
if template is not None:
|
|
118
|
-
template.
|
|
130
|
+
template.apply_to_zone_relations(zone)
|
|
119
131
|
|
|
120
132
|
return zone
|
|
121
133
|
|
|
@@ -129,7 +141,7 @@ class ZoneSerializer(NetBoxModelSerializer):
|
|
|
129
141
|
zone.nameservers.set(nameservers)
|
|
130
142
|
|
|
131
143
|
if template is not None:
|
|
132
|
-
template.
|
|
144
|
+
template.apply_to_zone_relations(zone)
|
|
133
145
|
|
|
134
146
|
return zone
|
|
135
147
|
|
|
@@ -26,6 +26,13 @@ class ZoneTemplateSerializer(NetBoxModelSerializer):
|
|
|
26
26
|
required=False,
|
|
27
27
|
help_text=_("Nameservers for the zone"),
|
|
28
28
|
)
|
|
29
|
+
soa_mname = NameServerSerializer(
|
|
30
|
+
nested=True,
|
|
31
|
+
many=False,
|
|
32
|
+
read_only=False,
|
|
33
|
+
required=False,
|
|
34
|
+
help_text=_("Primary nameserver for the zone"),
|
|
35
|
+
)
|
|
29
36
|
record_templates = NestedRecordTemplateSerializer(
|
|
30
37
|
many=True,
|
|
31
38
|
read_only=False,
|
|
@@ -108,6 +115,8 @@ class ZoneTemplateSerializer(NetBoxModelSerializer):
|
|
|
108
115
|
"name",
|
|
109
116
|
"display",
|
|
110
117
|
"nameservers",
|
|
118
|
+
"soa_mname",
|
|
119
|
+
"soa_rname",
|
|
111
120
|
"description",
|
|
112
121
|
"tags",
|
|
113
122
|
"created",
|
|
@@ -43,6 +43,18 @@ class ZoneTemplateFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
|
|
|
43
43
|
to_field_name="name",
|
|
44
44
|
label=_("Nameserver"),
|
|
45
45
|
)
|
|
46
|
+
soa_mname_id = django_filters.ModelMultipleChoiceFilter(
|
|
47
|
+
queryset=NameServer.objects.all(),
|
|
48
|
+
field_name="soa_mname",
|
|
49
|
+
to_field_name="id",
|
|
50
|
+
label=_("SOA MNAME ID"),
|
|
51
|
+
)
|
|
52
|
+
soa_mname = django_filters.ModelMultipleChoiceFilter(
|
|
53
|
+
queryset=NameServer.objects.all(),
|
|
54
|
+
field_name="soa_mname__name",
|
|
55
|
+
to_field_name="name",
|
|
56
|
+
label=_("SOA MNAME"),
|
|
57
|
+
)
|
|
46
58
|
registrar_id = django_filters.ModelMultipleChoiceFilter(
|
|
47
59
|
queryset=Registrar.objects.all(),
|
|
48
60
|
label=_("Registrar ID"),
|
|
@@ -100,6 +112,7 @@ class ZoneTemplateFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
|
|
|
100
112
|
"id",
|
|
101
113
|
"name",
|
|
102
114
|
"description",
|
|
115
|
+
"soa_rname",
|
|
103
116
|
)
|
|
104
117
|
|
|
105
118
|
def search(self, queryset, name, value):
|
|
@@ -107,6 +120,7 @@ class ZoneTemplateFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
|
|
|
107
120
|
return queryset
|
|
108
121
|
qs_filter = (
|
|
109
122
|
Q(name__icontains=value)
|
|
123
|
+
| Q(soa_rname__icontains=value)
|
|
110
124
|
| Q(registrar__name__icontains=value)
|
|
111
125
|
| Q(registry_domain_id__icontains=value)
|
|
112
126
|
| Q(registrant__name__icontains=value)
|
|
@@ -191,6 +191,10 @@ class RecordTemplateImportForm(NetBoxModelImportForm):
|
|
|
191
191
|
class RecordTemplateBulkEditForm(NetBoxModelBulkEditForm):
|
|
192
192
|
model = RecordTemplate
|
|
193
193
|
|
|
194
|
+
record_name = forms.CharField(
|
|
195
|
+
required=False,
|
|
196
|
+
label=_("Record Name"),
|
|
197
|
+
)
|
|
194
198
|
type = forms.ChoiceField(
|
|
195
199
|
choices=add_blank_choice(RecordSelectableTypeChoices),
|
|
196
200
|
required=False,
|
netbox_dns/forms/zone.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from packaging.version import Version
|
|
2
|
+
|
|
1
3
|
from django import forms
|
|
2
4
|
from django.db import transaction
|
|
3
5
|
from django.conf import settings
|
|
@@ -20,6 +22,7 @@ from utilities.forms.fields import (
|
|
|
20
22
|
CSVModelMultipleChoiceField,
|
|
21
23
|
DynamicModelChoiceField,
|
|
22
24
|
)
|
|
25
|
+
from utilities.release import load_release_data
|
|
23
26
|
from utilities.forms.widgets import BulkEditNullBooleanSelect
|
|
24
27
|
from utilities.forms.rendering import FieldSet
|
|
25
28
|
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, add_blank_choice
|
|
@@ -47,6 +50,8 @@ __all__ = (
|
|
|
47
50
|
"ZoneBulkEditForm",
|
|
48
51
|
)
|
|
49
52
|
|
|
53
|
+
QUICK_ADD = Version(load_release_data().version) >= Version("4.2.5")
|
|
54
|
+
|
|
50
55
|
|
|
51
56
|
class RollbackTransaction(Exception):
|
|
52
57
|
pass
|
|
@@ -66,13 +71,20 @@ class ZoneTemplateUpdateMixin:
|
|
|
66
71
|
self.cleaned_data["tags"] = template.tags.all()
|
|
67
72
|
|
|
68
73
|
for field in template.template_fields:
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
):
|
|
74
|
+
if self.cleaned_data.get(field) in (None, "") and getattr(
|
|
75
|
+
template, field
|
|
76
|
+
) not in (None, ""):
|
|
73
77
|
self.cleaned_data[field] = getattr(template, field)
|
|
74
78
|
|
|
75
|
-
|
|
79
|
+
if self.cleaned_data.get("soa_mname") is None:
|
|
80
|
+
self.add_error(
|
|
81
|
+
"soa_mname",
|
|
82
|
+
_("soa_mname not set and no template or default value defined"),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if self.errors:
|
|
86
|
+
return
|
|
87
|
+
|
|
76
88
|
saved_events_queue = events_queue.get()
|
|
77
89
|
|
|
78
90
|
try:
|
|
@@ -106,13 +118,20 @@ class ZoneTemplateUpdateMixin:
|
|
|
106
118
|
raise RollbackTransaction
|
|
107
119
|
|
|
108
120
|
except ValidationError as exc:
|
|
109
|
-
|
|
121
|
+
if hasattr(exc, "error_dict"):
|
|
122
|
+
for field_name in self.fields.keys():
|
|
123
|
+
exc.error_dict.pop(field_name, None)
|
|
124
|
+
errors = exc.error_dict.values()
|
|
125
|
+
else:
|
|
126
|
+
errors = exc.messages
|
|
127
|
+
|
|
128
|
+
for error in errors:
|
|
129
|
+
self.add_error("template", error)
|
|
130
|
+
|
|
110
131
|
except RollbackTransaction:
|
|
111
132
|
pass
|
|
112
133
|
|
|
113
134
|
events_queue.set(saved_events_queue)
|
|
114
|
-
if template_error is not None:
|
|
115
|
-
raise ValidationError({"template": template_error})
|
|
116
135
|
|
|
117
136
|
return self.cleaned_data
|
|
118
137
|
|
|
@@ -126,6 +145,12 @@ class ZoneTemplateUpdateMixin:
|
|
|
126
145
|
|
|
127
146
|
|
|
128
147
|
class ZoneForm(ZoneTemplateUpdateMixin, TenancyForm, NetBoxModelForm):
|
|
148
|
+
view = DynamicModelChoiceField(
|
|
149
|
+
queryset=View.objects.all(),
|
|
150
|
+
required=True,
|
|
151
|
+
label=_("View"),
|
|
152
|
+
quick_add=QUICK_ADD,
|
|
153
|
+
)
|
|
129
154
|
name = forms.CharField(
|
|
130
155
|
required=True,
|
|
131
156
|
label=_("Name"),
|
|
@@ -144,6 +169,7 @@ class ZoneForm(ZoneTemplateUpdateMixin, TenancyForm, NetBoxModelForm):
|
|
|
144
169
|
queryset=NameServer.objects.all(),
|
|
145
170
|
required=False,
|
|
146
171
|
label=_("Nameservers"),
|
|
172
|
+
quick_add=QUICK_ADD,
|
|
147
173
|
)
|
|
148
174
|
default_ttl = forms.IntegerField(
|
|
149
175
|
required=False,
|
|
@@ -161,8 +187,15 @@ class ZoneForm(ZoneTemplateUpdateMixin, TenancyForm, NetBoxModelForm):
|
|
|
161
187
|
validators=[MinValueValidator(1)],
|
|
162
188
|
label=_("SOA TTL"),
|
|
163
189
|
)
|
|
190
|
+
soa_mname = DynamicModelChoiceField(
|
|
191
|
+
queryset=NameServer.objects.all(),
|
|
192
|
+
help_text=_("Primary nameserver this zone"),
|
|
193
|
+
required=False,
|
|
194
|
+
label=_("SOA MName"),
|
|
195
|
+
quick_add=QUICK_ADD,
|
|
196
|
+
)
|
|
164
197
|
soa_rname = forms.CharField(
|
|
165
|
-
required=
|
|
198
|
+
required=False,
|
|
166
199
|
help_text=_("Mailbox of the zone's administrator"),
|
|
167
200
|
label=_("SOA RName"),
|
|
168
201
|
)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from packaging.version import Version
|
|
2
|
+
|
|
1
3
|
from django import forms
|
|
2
4
|
from django.utils.translation import gettext_lazy as _
|
|
3
5
|
|
|
@@ -7,6 +9,7 @@ from netbox.forms import (
|
|
|
7
9
|
NetBoxModelImportForm,
|
|
8
10
|
NetBoxModelForm,
|
|
9
11
|
)
|
|
12
|
+
from utilities.release import load_release_data
|
|
10
13
|
from utilities.forms.fields import (
|
|
11
14
|
DynamicModelMultipleChoiceField,
|
|
12
15
|
TagFilterField,
|
|
@@ -34,19 +37,30 @@ __all__ = (
|
|
|
34
37
|
"ZoneTemplateBulkEditForm",
|
|
35
38
|
)
|
|
36
39
|
|
|
40
|
+
QUICK_ADD = Version(load_release_data().version) >= Version("4.2.5")
|
|
41
|
+
|
|
37
42
|
|
|
38
43
|
class ZoneTemplateForm(TenancyForm, NetBoxModelForm):
|
|
39
44
|
nameservers = DynamicModelMultipleChoiceField(
|
|
40
45
|
queryset=NameServer.objects.all(),
|
|
41
46
|
required=False,
|
|
47
|
+
quick_add=QUICK_ADD,
|
|
48
|
+
)
|
|
49
|
+
soa_mname = DynamicModelChoiceField(
|
|
50
|
+
queryset=NameServer.objects.all(),
|
|
51
|
+
required=False,
|
|
52
|
+
label=_("MName"),
|
|
53
|
+
quick_add=QUICK_ADD,
|
|
42
54
|
)
|
|
43
55
|
record_templates = DynamicModelMultipleChoiceField(
|
|
44
56
|
queryset=RecordTemplate.objects.all(),
|
|
45
57
|
required=False,
|
|
58
|
+
quick_add=QUICK_ADD,
|
|
46
59
|
)
|
|
47
60
|
|
|
48
61
|
fieldsets = (
|
|
49
62
|
FieldSet("name", "description", "nameservers", name=_("Zone Template")),
|
|
63
|
+
FieldSet("soa_mname", "soa_rname", name=_("SOA")),
|
|
50
64
|
FieldSet("record_templates", name=_("Record Templates")),
|
|
51
65
|
FieldSet(
|
|
52
66
|
"registrar",
|
|
@@ -66,6 +80,8 @@ class ZoneTemplateForm(TenancyForm, NetBoxModelForm):
|
|
|
66
80
|
fields = (
|
|
67
81
|
"name",
|
|
68
82
|
"nameservers",
|
|
83
|
+
"soa_mname",
|
|
84
|
+
"soa_rname",
|
|
69
85
|
"record_templates",
|
|
70
86
|
"description",
|
|
71
87
|
"registrar",
|
|
@@ -77,6 +93,9 @@ class ZoneTemplateForm(TenancyForm, NetBoxModelForm):
|
|
|
77
93
|
"tenant",
|
|
78
94
|
"tags",
|
|
79
95
|
)
|
|
96
|
+
labels = {
|
|
97
|
+
"soa_rname": _("RName"),
|
|
98
|
+
}
|
|
80
99
|
|
|
81
100
|
|
|
82
101
|
class ZoneTemplateFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|
@@ -84,6 +103,7 @@ class ZoneTemplateFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|
|
84
103
|
fieldsets = (
|
|
85
104
|
FieldSet("q", "filter_id", "tag"),
|
|
86
105
|
FieldSet("name", "nameserver_id", "description", name=_("Attributes")),
|
|
106
|
+
FieldSet("soa_mname_id", "soa_rname", name=_("SOA")),
|
|
87
107
|
FieldSet("record_template_id", name=_("Record Templates")),
|
|
88
108
|
FieldSet(
|
|
89
109
|
"registrar_id",
|
|
@@ -105,6 +125,15 @@ class ZoneTemplateFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|
|
105
125
|
required=False,
|
|
106
126
|
label=_("Nameservers"),
|
|
107
127
|
)
|
|
128
|
+
soa_mname_id = DynamicModelMultipleChoiceField(
|
|
129
|
+
queryset=NameServer.objects.all(),
|
|
130
|
+
required=False,
|
|
131
|
+
label=_("MName"),
|
|
132
|
+
)
|
|
133
|
+
soa_rname = forms.CharField(
|
|
134
|
+
required=False,
|
|
135
|
+
label=_("RName"),
|
|
136
|
+
)
|
|
108
137
|
record_template_id = DynamicModelMultipleChoiceField(
|
|
109
138
|
queryset=RecordTemplate.objects.all(),
|
|
110
139
|
required=False,
|
|
@@ -148,11 +177,17 @@ class ZoneTemplateImportForm(NetBoxModelImportForm):
|
|
|
148
177
|
required=False,
|
|
149
178
|
label=_("Nameservers"),
|
|
150
179
|
)
|
|
180
|
+
soa_mname = CSVModelChoiceField(
|
|
181
|
+
queryset=NameServer.objects.all(),
|
|
182
|
+
to_field_name="name",
|
|
183
|
+
required=False,
|
|
184
|
+
label=_("SOA MName"),
|
|
185
|
+
)
|
|
151
186
|
record_templates = CSVModelMultipleChoiceField(
|
|
152
187
|
queryset=RecordTemplate.objects.all(),
|
|
153
188
|
to_field_name="name",
|
|
154
189
|
required=False,
|
|
155
|
-
label=_("Record
|
|
190
|
+
label=_("Record Templates"),
|
|
156
191
|
)
|
|
157
192
|
registrar = CSVModelChoiceField(
|
|
158
193
|
queryset=Registrar.objects.all(),
|
|
@@ -212,6 +247,8 @@ class ZoneTemplateImportForm(NetBoxModelImportForm):
|
|
|
212
247
|
fields = (
|
|
213
248
|
"name",
|
|
214
249
|
"nameservers",
|
|
250
|
+
"soa_mname",
|
|
251
|
+
"soa_rname",
|
|
215
252
|
"record_templates",
|
|
216
253
|
"description",
|
|
217
254
|
"registrar",
|
|
@@ -230,12 +267,20 @@ class ZoneTemplateBulkEditForm(NetBoxModelBulkEditForm):
|
|
|
230
267
|
required=False,
|
|
231
268
|
label=_("Nameservers"),
|
|
232
269
|
)
|
|
270
|
+
soa_mname = DynamicModelChoiceField(
|
|
271
|
+
queryset=NameServer.objects.all(),
|
|
272
|
+
required=False,
|
|
273
|
+
label=_("MName"),
|
|
274
|
+
)
|
|
275
|
+
soa_rname = forms.CharField(max_length=255, required=False, label=_("RName"))
|
|
233
276
|
record_templates = DynamicModelMultipleChoiceField(
|
|
234
277
|
queryset=RecordTemplate.objects.all(),
|
|
235
278
|
required=False,
|
|
236
279
|
label=_("Record Templates"),
|
|
237
280
|
)
|
|
238
|
-
description = forms.CharField(
|
|
281
|
+
description = forms.CharField(
|
|
282
|
+
max_length=200, required=False, label=_("Description")
|
|
283
|
+
)
|
|
239
284
|
registrar = DynamicModelChoiceField(
|
|
240
285
|
queryset=Registrar.objects.all(),
|
|
241
286
|
required=False,
|
|
@@ -280,6 +325,11 @@ class ZoneTemplateBulkEditForm(NetBoxModelBulkEditForm):
|
|
|
280
325
|
"description",
|
|
281
326
|
name=_("Attributes"),
|
|
282
327
|
),
|
|
328
|
+
FieldSet(
|
|
329
|
+
"soa_mname",
|
|
330
|
+
"soa_rname",
|
|
331
|
+
name=_("SOA"),
|
|
332
|
+
),
|
|
283
333
|
FieldSet(
|
|
284
334
|
"record_templates",
|
|
285
335
|
name=_("Record Templates"),
|
|
@@ -298,6 +348,8 @@ class ZoneTemplateBulkEditForm(NetBoxModelBulkEditForm):
|
|
|
298
348
|
nullable_fields = (
|
|
299
349
|
"description",
|
|
300
350
|
"nameservers",
|
|
351
|
+
"soa_mname",
|
|
352
|
+
"soa_rname",
|
|
301
353
|
"record_templates",
|
|
302
354
|
"registrar",
|
|
303
355
|
"registrant",
|
netbox_dns/graphql/types.py
CHANGED
|
@@ -219,6 +219,13 @@ class NetBoxDNSZoneTemplateType(NetBoxObjectType):
|
|
|
219
219
|
"NetBoxDNSNameServerType", strawberry.lazy("netbox_dns.graphql.types")
|
|
220
220
|
]
|
|
221
221
|
]
|
|
222
|
+
soa_mname: (
|
|
223
|
+
Annotated[
|
|
224
|
+
"NetBoxDNSNameServerType", strawberry.lazy("netbox_dns.graphql.types")
|
|
225
|
+
]
|
|
226
|
+
| None
|
|
227
|
+
)
|
|
228
|
+
soa_rname: str | None
|
|
222
229
|
record_templates: List[
|
|
223
230
|
Annotated[
|
|
224
231
|
"NetBoxDNSRecordTemplateType", strawberry.lazy("netbox_dns.graphql.types")
|
|
Binary file
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Generated by Django 5.1.5 on 2025-02-17 11:37
|
|
2
|
+
|
|
3
|
+
import django.db.models.deletion
|
|
4
|
+
from django.db import migrations, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
("netbox_dns", "0012_natural_ordering"),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.AddField(
|
|
15
|
+
model_name="zonetemplate",
|
|
16
|
+
name="soa_mname",
|
|
17
|
+
field=models.ForeignKey(
|
|
18
|
+
blank=True,
|
|
19
|
+
null=True,
|
|
20
|
+
on_delete=django.db.models.deletion.PROTECT,
|
|
21
|
+
related_name="+",
|
|
22
|
+
to="netbox_dns.nameserver",
|
|
23
|
+
),
|
|
24
|
+
),
|
|
25
|
+
migrations.AddField(
|
|
26
|
+
model_name="zonetemplate",
|
|
27
|
+
name="soa_rname",
|
|
28
|
+
field=models.CharField(blank=True, max_length=255),
|
|
29
|
+
),
|
|
30
|
+
]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Generated by Django 5.1.6 on 2025-02-27 19:36
|
|
2
|
+
|
|
3
|
+
import django.db.models.functions.text
|
|
4
|
+
from django.db import migrations, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
("extras", "0122_charfield_null_choices"),
|
|
11
|
+
("netbox_dns", "0013_zonetemplate_soa_mname_zonetemplate_soa_rname"),
|
|
12
|
+
("tenancy", "0017_natural_ordering"),
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
operations = [
|
|
16
|
+
migrations.AlterUniqueTogether(
|
|
17
|
+
name="zone",
|
|
18
|
+
unique_together=set(),
|
|
19
|
+
),
|
|
20
|
+
migrations.AlterField(
|
|
21
|
+
model_name="nameserver",
|
|
22
|
+
name="name",
|
|
23
|
+
field=models.CharField(db_collation="natural_sort", max_length=255),
|
|
24
|
+
),
|
|
25
|
+
migrations.AddConstraint(
|
|
26
|
+
model_name="nameserver",
|
|
27
|
+
constraint=models.UniqueConstraint(
|
|
28
|
+
django.db.models.functions.text.Lower("name"),
|
|
29
|
+
name="name_unique_ci",
|
|
30
|
+
violation_error_message="There is already a nameserver with this name",
|
|
31
|
+
),
|
|
32
|
+
),
|
|
33
|
+
migrations.AddConstraint(
|
|
34
|
+
model_name="zone",
|
|
35
|
+
constraint=models.UniqueConstraint(
|
|
36
|
+
django.db.models.functions.text.Lower("name"),
|
|
37
|
+
models.F("view"),
|
|
38
|
+
name="name_view_unique_ci",
|
|
39
|
+
violation_error_message="There is already a zone with the same name in this view",
|
|
40
|
+
),
|
|
41
|
+
),
|
|
42
|
+
]
|
netbox_dns/models/nameserver.py
CHANGED
|
@@ -2,13 +2,15 @@ from dns import name as dns_name
|
|
|
2
2
|
|
|
3
3
|
from django.core.exceptions import ValidationError
|
|
4
4
|
from django.db import models, transaction
|
|
5
|
-
from django.db.models import Q
|
|
5
|
+
from django.db.models import Q, UniqueConstraint
|
|
6
|
+
from django.db.models.functions import Lower
|
|
6
7
|
from django.urls import reverse
|
|
7
8
|
from django.utils.translation import gettext_lazy as _
|
|
8
9
|
|
|
9
10
|
from netbox.models import NetBoxModel
|
|
10
11
|
from netbox.search import SearchIndex, register_search
|
|
11
12
|
from netbox.models.features import ContactsMixin
|
|
13
|
+
from netbox.plugins.utils import get_plugin_config
|
|
12
14
|
|
|
13
15
|
from netbox_dns.utilities import (
|
|
14
16
|
name_to_unicode,
|
|
@@ -29,7 +31,6 @@ __all__ = (
|
|
|
29
31
|
class NameServer(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
30
32
|
name = models.CharField(
|
|
31
33
|
verbose_name=_("Name"),
|
|
32
|
-
unique=True,
|
|
33
34
|
max_length=255,
|
|
34
35
|
db_collation="natural_sort",
|
|
35
36
|
)
|
|
@@ -59,6 +60,16 @@ class NameServer(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
59
60
|
|
|
60
61
|
ordering = ("name",)
|
|
61
62
|
|
|
63
|
+
constraints = [
|
|
64
|
+
UniqueConstraint(
|
|
65
|
+
Lower("name"),
|
|
66
|
+
name="name_unique_ci",
|
|
67
|
+
violation_error_message=_(
|
|
68
|
+
"There is already a nameserver with this name"
|
|
69
|
+
),
|
|
70
|
+
),
|
|
71
|
+
]
|
|
72
|
+
|
|
62
73
|
def __str__(self):
|
|
63
74
|
try:
|
|
64
75
|
return dns_name.from_text(self.name, origin=None).to_unicode()
|
|
@@ -69,9 +80,16 @@ class NameServer(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
69
80
|
def display_name(self):
|
|
70
81
|
return name_to_unicode(self.name)
|
|
71
82
|
|
|
83
|
+
# TODO: Remove in version 1.3.0 (NetBox #18555)
|
|
72
84
|
def get_absolute_url(self):
|
|
73
85
|
return reverse("plugins:netbox_dns:nameserver", kwargs={"pk": self.pk})
|
|
74
86
|
|
|
87
|
+
def clean_fields(self, exclude=None):
|
|
88
|
+
if get_plugin_config("netbox_dns", "convert_names_to_lowercase", False):
|
|
89
|
+
self.name = self.name.lower()
|
|
90
|
+
|
|
91
|
+
super().clean_fields(exclude=exclude)
|
|
92
|
+
|
|
75
93
|
def clean(self, *args, **kwargs):
|
|
76
94
|
try:
|
|
77
95
|
self.name = normalize_name(self.name)
|
netbox_dns/models/record.py
CHANGED
|
@@ -263,6 +263,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
263
263
|
def get_status_color(self):
|
|
264
264
|
return RecordStatusChoices.colors.get(self.status)
|
|
265
265
|
|
|
266
|
+
# TODO: Remove in version 1.3.0 (NetBox #18555)
|
|
266
267
|
def get_absolute_url(self):
|
|
267
268
|
return reverse("plugins:netbox_dns:record", kwargs={"pk": self.pk})
|
|
268
269
|
|
|
@@ -370,9 +371,11 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
370
371
|
if ptr_zone.is_rfc2317_zone:
|
|
371
372
|
ptr_name = self.rfc2317_ptr_name
|
|
372
373
|
else:
|
|
373
|
-
ptr_name =
|
|
374
|
-
ipaddress.ip_address(self.value).reverse_pointer
|
|
375
|
-
|
|
374
|
+
ptr_name = (
|
|
375
|
+
dns_name.from_text(ipaddress.ip_address(self.value).reverse_pointer)
|
|
376
|
+
.relativize(dns_name.from_text(ptr_zone.name))
|
|
377
|
+
.to_text()
|
|
378
|
+
)
|
|
376
379
|
|
|
377
380
|
ptr_value = self.fqdn
|
|
378
381
|
ptr_record = self.ptr_record
|
|
@@ -439,12 +442,16 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
439
442
|
|
|
440
443
|
def update_rfc2317_cname_record(self, save_zone_serial=True):
|
|
441
444
|
if self.zone.rfc2317_parent_managed:
|
|
442
|
-
cname_name =
|
|
443
|
-
|
|
444
|
-
|
|
445
|
+
cname_name = (
|
|
446
|
+
dns_name.from_text(
|
|
447
|
+
ipaddress.ip_address(self.ip_address).reverse_pointer
|
|
448
|
+
)
|
|
449
|
+
.relativize(dns_name.from_text(self.zone.rfc2317_parent_zone.name))
|
|
450
|
+
.to_text()
|
|
451
|
+
)
|
|
445
452
|
|
|
446
453
|
if self.rfc2317_cname_record is not None:
|
|
447
|
-
if self.rfc2317_cname_record.name == cname_name
|
|
454
|
+
if self.rfc2317_cname_record.name == cname_name:
|
|
448
455
|
self.rfc2317_cname_record.zone = self.zone.rfc2317_parent_zone
|
|
449
456
|
self.rfc2317_cname_record.value = self.fqdn
|
|
450
457
|
self.rfc2317_cname_record.ttl = min_ttl(
|
|
@@ -612,7 +619,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
612
619
|
new_zone = self.zone
|
|
613
620
|
|
|
614
621
|
records = new_zone.records.filter(
|
|
615
|
-
|
|
622
|
+
name__iexact=self.name,
|
|
616
623
|
type=self.type,
|
|
617
624
|
value=self.value,
|
|
618
625
|
status__in=RECORD_ACTIVE_STATUS_LIST,
|
|
@@ -769,9 +776,12 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
769
776
|
record.ttl = ttl
|
|
770
777
|
record.save(update_fields=["ttl"], update_rrset_ttl=False)
|
|
771
778
|
|
|
772
|
-
def clean_fields(self,
|
|
779
|
+
def clean_fields(self, exclude=None):
|
|
773
780
|
self.type = self.type.upper()
|
|
774
|
-
|
|
781
|
+
if get_plugin_config("netbox_dns", "convert_names_to_lowercase", False):
|
|
782
|
+
self.name = self.name.lower()
|
|
783
|
+
|
|
784
|
+
super().clean_fields(exclude=exclude)
|
|
775
785
|
|
|
776
786
|
def clean(self, *args, new_zone=None, **kwargs):
|
|
777
787
|
self.validate_name(new_zone=new_zone)
|
|
@@ -104,6 +104,7 @@ class RecordTemplate(NetBoxModel):
|
|
|
104
104
|
def get_status_color(self):
|
|
105
105
|
return RecordStatusChoices.colors.get(self.status)
|
|
106
106
|
|
|
107
|
+
# TODO: Remove in version 1.3.0 (NetBox #18555)
|
|
107
108
|
def get_absolute_url(self):
|
|
108
109
|
return reverse("plugins:netbox_dns:recordtemplate", kwargs={"pk": self.pk})
|
|
109
110
|
|
|
@@ -163,17 +164,22 @@ class RecordTemplate(NetBoxModel):
|
|
|
163
164
|
record = Record.objects.create(**record_data)
|
|
164
165
|
except ValidationError as exc:
|
|
165
166
|
raise ValidationError(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
{
|
|
168
|
+
None: _(
|
|
169
|
+
"Error while processing record template {template}: {error}"
|
|
170
|
+
).format(template=self, error=exc.messages[0])
|
|
171
|
+
}
|
|
169
172
|
)
|
|
170
173
|
|
|
171
174
|
if tags := self.tags.all():
|
|
172
175
|
record.tags.set(tags)
|
|
173
176
|
|
|
174
|
-
def clean_fields(self,
|
|
177
|
+
def clean_fields(self, exclude=None):
|
|
175
178
|
self.type = self.type.upper()
|
|
176
|
-
|
|
179
|
+
if get_plugin_config("netbox_dns", "convert_names_to_lowercase", False):
|
|
180
|
+
self.record_name = self.record_name.lower()
|
|
181
|
+
|
|
182
|
+
super().clean_fields(exclude=exclude)
|
|
177
183
|
|
|
178
184
|
def clean(self, *args, **kwargs):
|
|
179
185
|
self.validate_name()
|
netbox_dns/models/registrar.py
CHANGED
netbox_dns/models/view.py
CHANGED
|
@@ -71,6 +71,7 @@ class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
71
71
|
def get_default_view(cls):
|
|
72
72
|
return cls.objects.get(default_view=True)
|
|
73
73
|
|
|
74
|
+
# TODO: Remove in version 1.3.0 (NetBox #18555)
|
|
74
75
|
def get_absolute_url(self):
|
|
75
76
|
return reverse("plugins:netbox_dns:view", kwargs={"pk": self.pk})
|
|
76
77
|
|
netbox_dns/models/zone.py
CHANGED
|
@@ -11,8 +11,8 @@ from django.core.validators import (
|
|
|
11
11
|
)
|
|
12
12
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
|
13
13
|
from django.db import models, transaction
|
|
14
|
-
from django.db.models import Q, Max, ExpressionWrapper, BooleanField
|
|
15
|
-
from django.db.models.functions import Length
|
|
14
|
+
from django.db.models import Q, Max, ExpressionWrapper, BooleanField, UniqueConstraint
|
|
15
|
+
from django.db.models.functions import Length, Lower
|
|
16
16
|
from django.db.models.signals import m2m_changed
|
|
17
17
|
from django.urls import reverse
|
|
18
18
|
from django.dispatch import receiver
|
|
@@ -36,6 +36,7 @@ from netbox_dns.utilities import (
|
|
|
36
36
|
name_to_unicode,
|
|
37
37
|
normalize_name,
|
|
38
38
|
get_parent_zone_names,
|
|
39
|
+
regex_from_list,
|
|
39
40
|
NameFormatError,
|
|
40
41
|
)
|
|
41
42
|
from netbox_dns.validators import (
|
|
@@ -282,10 +283,16 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
282
283
|
"view",
|
|
283
284
|
"name",
|
|
284
285
|
)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
286
|
+
constraints = [
|
|
287
|
+
UniqueConstraint(
|
|
288
|
+
Lower("name"),
|
|
289
|
+
"view",
|
|
290
|
+
name="name_view_unique_ci",
|
|
291
|
+
violation_error_message=_(
|
|
292
|
+
"There is already a zone with the same name in this view"
|
|
293
|
+
),
|
|
294
|
+
),
|
|
295
|
+
]
|
|
289
296
|
|
|
290
297
|
def __str__(self):
|
|
291
298
|
if self.name == "." and get_plugin_config("netbox_dns", "enable_root_zones"):
|
|
@@ -350,6 +357,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
350
357
|
def get_status_color(self):
|
|
351
358
|
return ZoneStatusChoices.colors.get(self.status)
|
|
352
359
|
|
|
360
|
+
# TODO: Remove in version 1.3.0 (NetBox #18555)
|
|
353
361
|
def get_absolute_url(self):
|
|
354
362
|
return reverse("plugins:netbox_dns:zone", kwargs={"pk": self.pk})
|
|
355
363
|
|
|
@@ -398,12 +406,14 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
398
406
|
|
|
399
407
|
@property
|
|
400
408
|
def descendant_zones(self):
|
|
401
|
-
return self.view.zones.filter(
|
|
409
|
+
return self.view.zones.filter(name__iendswith=f".{self.name}")
|
|
402
410
|
|
|
403
411
|
@property
|
|
404
412
|
def parent_zone(self):
|
|
405
413
|
try:
|
|
406
|
-
return self.view.zones.get(
|
|
414
|
+
return self.view.zones.get(
|
|
415
|
+
name__iexact=get_parent_zone_names(self.name)[-1]
|
|
416
|
+
)
|
|
407
417
|
except (Zone.DoesNotExist, IndexError):
|
|
408
418
|
return None
|
|
409
419
|
|
|
@@ -411,20 +421,24 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
411
421
|
def ancestor_zones(self):
|
|
412
422
|
return (
|
|
413
423
|
self.view.zones.annotate(name_length=Length("name"))
|
|
414
|
-
.filter(
|
|
424
|
+
.filter(name__iregex=regex_from_list(get_parent_zone_names(self.name)))
|
|
415
425
|
.order_by("name_length")
|
|
416
426
|
)
|
|
417
427
|
|
|
418
428
|
@property
|
|
419
429
|
def delegation_records(self):
|
|
420
430
|
descendant_zone_names = [
|
|
421
|
-
|
|
431
|
+
rf"{name}."
|
|
432
|
+
for name in (
|
|
433
|
+
name.lower()
|
|
434
|
+
for name in self.descendant_zones.values_list("name", flat=True)
|
|
435
|
+
)
|
|
422
436
|
]
|
|
423
437
|
|
|
424
438
|
ns_records = (
|
|
425
439
|
self.records.filter(type=RecordTypeChoices.NS)
|
|
426
|
-
.exclude(
|
|
427
|
-
.filter(
|
|
440
|
+
.exclude(fqdn__iexact=self.fqdn)
|
|
441
|
+
.filter(fqdn__iregex=regex_from_list(descendant_zone_names))
|
|
428
442
|
)
|
|
429
443
|
ns_values = [record.value_fqdn for record in ns_records]
|
|
430
444
|
|
|
@@ -662,6 +676,9 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
662
676
|
def clean_fields(self, exclude=None):
|
|
663
677
|
defaults = settings.PLUGINS_CONFIG.get("netbox_dns")
|
|
664
678
|
|
|
679
|
+
if get_plugin_config("netbox_dns", "convert_names_to_lowercase", False):
|
|
680
|
+
self.name = self.name.lower()
|
|
681
|
+
|
|
665
682
|
if self.view_id is None:
|
|
666
683
|
self.view_id = View.get_default_view().pk
|
|
667
684
|
|
|
@@ -670,15 +687,25 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
670
687
|
if value not in (None, ""):
|
|
671
688
|
setattr(self, field, value)
|
|
672
689
|
|
|
673
|
-
if self.soa_mname_id is None:
|
|
674
|
-
default_soa_mname
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
690
|
+
if self.soa_mname_id is None and "soa_mname" not in exclude:
|
|
691
|
+
if default_soa_mname := defaults.get("zone_soa_mname"):
|
|
692
|
+
try:
|
|
693
|
+
self.soa_mname = NameServer.objects.get(name=default_soa_mname)
|
|
694
|
+
except NameServer.DoesNotExist:
|
|
695
|
+
raise ValidationError(
|
|
696
|
+
{
|
|
697
|
+
"soa_mname": _(
|
|
698
|
+
"Default soa_mname instance {nameserver} does not exist"
|
|
699
|
+
).format(nameserver=default_soa_mname)
|
|
700
|
+
}
|
|
681
701
|
)
|
|
702
|
+
else:
|
|
703
|
+
raise ValidationError(
|
|
704
|
+
{
|
|
705
|
+
"soa_mname": _(
|
|
706
|
+
"soa_mname not set and no template or default value defined"
|
|
707
|
+
)
|
|
708
|
+
}
|
|
682
709
|
)
|
|
683
710
|
|
|
684
711
|
super().clean_fields(exclude=exclude)
|
|
@@ -706,7 +733,13 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
706
733
|
)
|
|
707
734
|
|
|
708
735
|
if self.soa_rname in (None, ""):
|
|
709
|
-
raise ValidationError(
|
|
736
|
+
raise ValidationError(
|
|
737
|
+
{
|
|
738
|
+
"soa_rname": _(
|
|
739
|
+
"soa_rname not set and no template or default value defined"
|
|
740
|
+
),
|
|
741
|
+
}
|
|
742
|
+
)
|
|
710
743
|
try:
|
|
711
744
|
dns_name.from_text(self.soa_rname, origin=dns_name.root)
|
|
712
745
|
validate_rname(self.soa_rname)
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
from dns import name as dns_name
|
|
2
|
+
from dns.exception import DNSException
|
|
3
|
+
|
|
1
4
|
from django.db import models
|
|
2
5
|
from django.urls import reverse
|
|
3
6
|
from django.utils.translation import gettext_lazy as _
|
|
7
|
+
from django.core.exceptions import ValidationError
|
|
4
8
|
|
|
5
9
|
from netbox.models import NetBoxModel
|
|
6
10
|
from netbox.search import SearchIndex, register_search
|
|
7
11
|
|
|
12
|
+
from netbox_dns.validators import validate_rname
|
|
13
|
+
|
|
8
14
|
|
|
9
15
|
__all__ = (
|
|
10
16
|
"ZoneTemplate",
|
|
@@ -30,6 +36,19 @@ class ZoneTemplate(NetBoxModel):
|
|
|
30
36
|
related_name="+",
|
|
31
37
|
blank=True,
|
|
32
38
|
)
|
|
39
|
+
soa_mname = models.ForeignKey(
|
|
40
|
+
verbose_name=_("SOA MName"),
|
|
41
|
+
to="NameServer",
|
|
42
|
+
related_name="+",
|
|
43
|
+
on_delete=models.PROTECT,
|
|
44
|
+
blank=True,
|
|
45
|
+
null=True,
|
|
46
|
+
)
|
|
47
|
+
soa_rname = models.CharField(
|
|
48
|
+
verbose_name=_("SOA RName"),
|
|
49
|
+
max_length=255,
|
|
50
|
+
blank=True,
|
|
51
|
+
)
|
|
33
52
|
record_templates = models.ManyToManyField(
|
|
34
53
|
verbose_name=_("Record Templates"),
|
|
35
54
|
to="RecordTemplate",
|
|
@@ -98,6 +117,8 @@ class ZoneTemplate(NetBoxModel):
|
|
|
98
117
|
)
|
|
99
118
|
|
|
100
119
|
template_fields = (
|
|
120
|
+
"soa_mname",
|
|
121
|
+
"soa_rname",
|
|
101
122
|
"tenant",
|
|
102
123
|
"registrar",
|
|
103
124
|
"registrant",
|
|
@@ -115,31 +136,44 @@ class ZoneTemplate(NetBoxModel):
|
|
|
115
136
|
def __str__(self):
|
|
116
137
|
return str(self.name)
|
|
117
138
|
|
|
139
|
+
# TODO: Remove in version 1.3.0 (NetBox #18555)
|
|
118
140
|
def get_absolute_url(self):
|
|
119
141
|
return reverse("plugins:netbox_dns:zonetemplate", kwargs={"pk": self.pk})
|
|
120
142
|
|
|
121
|
-
def
|
|
143
|
+
def apply_to_zone_data(self, data):
|
|
144
|
+
fields_changed = False
|
|
145
|
+
for field in self.template_fields:
|
|
146
|
+
if data.get(field) in (None, "") and getattr(self, field) not in (None, ""):
|
|
147
|
+
fields_changed = True
|
|
148
|
+
data[field] = getattr(self, field)
|
|
149
|
+
|
|
150
|
+
return fields_changed
|
|
151
|
+
|
|
152
|
+
def apply_to_zone_relations(self, zone):
|
|
122
153
|
if not zone.nameservers.all() and self.nameservers.all():
|
|
123
154
|
zone.nameservers.set(self.nameservers.all())
|
|
124
155
|
|
|
125
156
|
if not zone.tags.all() and self.tags.all():
|
|
126
157
|
zone.tags.set(self.tags.all())
|
|
127
158
|
|
|
128
|
-
fields_changed = True
|
|
129
|
-
for field in self.template_fields:
|
|
130
|
-
if getattr(zone, field) is None and getattr(self, field) is not None:
|
|
131
|
-
fields_changed = True
|
|
132
|
-
setattr(zone, field, getattr(self, field))
|
|
133
|
-
|
|
134
|
-
if fields_changed:
|
|
135
|
-
zone.save()
|
|
136
|
-
|
|
137
159
|
self.create_records(zone)
|
|
138
160
|
|
|
139
161
|
def create_records(self, zone):
|
|
140
162
|
for record_template in self.record_templates.all():
|
|
141
163
|
record_template.create_record(zone=zone)
|
|
142
164
|
|
|
165
|
+
def clean(self, *args, **kwargs):
|
|
166
|
+
if self.soa_rname:
|
|
167
|
+
try:
|
|
168
|
+
dns_name.from_text(self.soa_rname, origin=dns_name.root)
|
|
169
|
+
validate_rname(self.soa_rname)
|
|
170
|
+
except (DNSException, ValidationError) as exc:
|
|
171
|
+
raise ValidationError(
|
|
172
|
+
{
|
|
173
|
+
"soa_rname": exc,
|
|
174
|
+
}
|
|
175
|
+
)
|
|
176
|
+
|
|
143
177
|
|
|
144
178
|
@register_search
|
|
145
179
|
class ZoneTemplateIndex(SearchIndex):
|
|
@@ -18,6 +18,10 @@ class ZoneTemplateTable(TenancyColumnsMixin, NetBoxTable):
|
|
|
18
18
|
verbose_name=_("Name"),
|
|
19
19
|
linkify=True,
|
|
20
20
|
)
|
|
21
|
+
soa_mname = tables.Column(
|
|
22
|
+
verbose_name=_("SOA MName"),
|
|
23
|
+
linkify=True,
|
|
24
|
+
)
|
|
21
25
|
tags = TagColumn(
|
|
22
26
|
url_name="plugins:netbox_dns:zonetemplate_list",
|
|
23
27
|
)
|
|
@@ -44,7 +48,10 @@ class ZoneTemplateTable(TenancyColumnsMixin, NetBoxTable):
|
|
|
44
48
|
|
|
45
49
|
class Meta(NetBoxTable.Meta):
|
|
46
50
|
model = ZoneTemplate
|
|
47
|
-
fields = (
|
|
51
|
+
fields = (
|
|
52
|
+
"soa_rname",
|
|
53
|
+
"description",
|
|
54
|
+
)
|
|
48
55
|
default_columns = (
|
|
49
56
|
"name",
|
|
50
57
|
"tags",
|
|
@@ -42,6 +42,14 @@
|
|
|
42
42
|
</table>
|
|
43
43
|
</td>
|
|
44
44
|
</tr>
|
|
45
|
+
<tr>
|
|
46
|
+
<th scope="row">{% trans "SOA MName" %}</th>
|
|
47
|
+
<td>{{ object.soa_mname|linkify }}</td>
|
|
48
|
+
</tr>
|
|
49
|
+
<tr>
|
|
50
|
+
<th scope="row">{% trans "SOA RName" %}</th>
|
|
51
|
+
<td>{{ object.soa_rname }}</td>
|
|
52
|
+
</tr>
|
|
45
53
|
</table>
|
|
46
54
|
</div>
|
|
47
55
|
|
|
@@ -14,6 +14,7 @@ __all__ = (
|
|
|
14
14
|
"value_to_unicode",
|
|
15
15
|
"normalize_name",
|
|
16
16
|
"network_to_reverse",
|
|
17
|
+
"regex_from_list",
|
|
17
18
|
)
|
|
18
19
|
|
|
19
20
|
|
|
@@ -106,3 +107,7 @@ def network_to_reverse(network):
|
|
|
106
107
|
|
|
107
108
|
if labels:
|
|
108
109
|
return ".".join(ip_network[0].reverse_dns.split(".")[-labels:])
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def regex_from_list(names):
|
|
113
|
+
return f"^({'|'.join(re.escape(name) for name in names)})$"
|
netbox_dns/utilities/dns.py
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
from dns import name as dns_name
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
__all__ = ("get_parent_zone_names",)
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
def get_parent_zone_names(name, min_labels=1, include_self=False):
|
|
8
|
-
fqdn = dns_name.from_text(name)
|
|
7
|
+
fqdn = dns_name.from_text(name.lower())
|
|
9
8
|
return [
|
|
10
9
|
fqdn.split(i)[1].to_text().rstrip(".")
|
|
11
10
|
for i in range(min_labels + 1, len(fqdn.labels) + include_self)
|
|
@@ -13,6 +13,7 @@ from ipam.models import IPAddress, Prefix
|
|
|
13
13
|
from netbox_dns.choices import RecordStatusChoices
|
|
14
14
|
|
|
15
15
|
from .dns import get_parent_zone_names
|
|
16
|
+
from .conversions import regex_from_list
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
__all__ = (
|
|
@@ -90,8 +91,10 @@ def get_zones(ip_address, view=None, old_zone=None):
|
|
|
90
91
|
|
|
91
92
|
zones = Zone.objects.filter(
|
|
92
93
|
view__in=views,
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
name__iregex=regex_from_list(
|
|
95
|
+
get_parent_zone_names(
|
|
96
|
+
ip_address.dns_name, min_labels=min_labels, include_self=True
|
|
97
|
+
)
|
|
95
98
|
),
|
|
96
99
|
active=True,
|
|
97
100
|
)
|
netbox_dns/views/record.py
CHANGED
|
@@ -16,7 +16,11 @@ from netbox_dns.forms import (
|
|
|
16
16
|
from netbox_dns.models import Record, Zone
|
|
17
17
|
from netbox_dns.choices import RecordTypeChoices
|
|
18
18
|
from netbox_dns.tables import RecordTable, ManagedRecordTable, RelatedRecordTable
|
|
19
|
-
from netbox_dns.utilities import
|
|
19
|
+
from netbox_dns.utilities import (
|
|
20
|
+
value_to_unicode,
|
|
21
|
+
get_parent_zone_names,
|
|
22
|
+
regex_from_list,
|
|
23
|
+
)
|
|
20
24
|
|
|
21
25
|
|
|
22
26
|
__all__ = (
|
|
@@ -74,7 +78,9 @@ class RecordView(generic.ObjectView):
|
|
|
74
78
|
)
|
|
75
79
|
|
|
76
80
|
if instance.zone.view.zones.filter(
|
|
77
|
-
|
|
81
|
+
name__iregex=regex_from_list(
|
|
82
|
+
get_parent_zone_names(instance.value_fqdn, min_labels=1)
|
|
83
|
+
),
|
|
78
84
|
active=True,
|
|
79
85
|
).exists():
|
|
80
86
|
raise (
|
|
@@ -97,7 +103,9 @@ class RecordView(generic.ObjectView):
|
|
|
97
103
|
)
|
|
98
104
|
|
|
99
105
|
parent_zones = instance.zone.view.zones.filter(
|
|
100
|
-
|
|
106
|
+
name__iregex=regex_from_list(
|
|
107
|
+
get_parent_zone_names(instance.fqdn, include_self=True)
|
|
108
|
+
),
|
|
101
109
|
)
|
|
102
110
|
|
|
103
111
|
for parent_zone in parent_zones:
|
|
@@ -148,10 +156,12 @@ class RecordView(generic.ObjectView):
|
|
|
148
156
|
|
|
149
157
|
if Zone.objects.filter(
|
|
150
158
|
active=True,
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
159
|
+
name__iregex=regex_from_list(
|
|
160
|
+
get_parent_zone_names(
|
|
161
|
+
instance.fqdn,
|
|
162
|
+
min_labels=len(fqdn) - len(name),
|
|
163
|
+
include_self=True,
|
|
164
|
+
)
|
|
155
165
|
),
|
|
156
166
|
).exists():
|
|
157
167
|
context["mask_warning"] = _(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: netbox-plugin-dns
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.5
|
|
4
4
|
Summary: NetBox DNS is a NetBox plugin for managing DNS data.
|
|
5
5
|
Author-email: Peter Eckel <pete@netbox-dns.org>
|
|
6
6
|
Project-URL: Homepage, https://github.com/peteeckel/netbox-plugin-dns
|
|
@@ -99,7 +99,7 @@ PLUGINS = [
|
|
|
99
99
|
]
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
-
To permanently keep the plugin installed when updating NetBox via `
|
|
102
|
+
To permanently keep the plugin installed when updating NetBox via `upgrade.sh`:
|
|
103
103
|
|
|
104
104
|
```
|
|
105
105
|
echo netbox-plugin-dns >> ~/netbox/local_requirements.txt
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
netbox_dns/__init__.py,sha256=
|
|
1
|
+
netbox_dns/__init__.py,sha256=nnD4iq2_TUmMshut8Z3viAIlrY0RSc9YH6fllV11R4E,3098
|
|
2
2
|
netbox_dns/apps.py,sha256=JCW5eS-AQBUubDJve1DjP-IRFKTFGQh1NLGWzJpC5MI,151
|
|
3
3
|
netbox_dns/navigation.py,sha256=36clAzlWftW94_VZ3EHu8_btzzA_dah50CLTfoov-O4,6226
|
|
4
4
|
netbox_dns/template_content.py,sha256=T06L7-m4eGrLMeGsCvPpQLAGfn3S2FL7z0Cd1hhbisY,4225
|
|
@@ -15,8 +15,8 @@ netbox_dns/api/serializers_/record_template.py,sha256=WAHua_O7v8IB7QL_hOPWjItMtA
|
|
|
15
15
|
netbox_dns/api/serializers_/registrar.py,sha256=xLIaeBJ5ckV1Jf-uyCTFcvsLlsRMlpDtIg6q79vXZic,842
|
|
16
16
|
netbox_dns/api/serializers_/registration_contact.py,sha256=3IGWW5xB9XEBGApCGZCZIxpCmy1Y5jQUbA4GzmtaCik,1024
|
|
17
17
|
netbox_dns/api/serializers_/view.py,sha256=nnWeQugoqMdn-NGGC7ykbVPwmBrcBma_ZKwdDxUgJ24,1809
|
|
18
|
-
netbox_dns/api/serializers_/zone.py,sha256=
|
|
19
|
-
netbox_dns/api/serializers_/zone_template.py,sha256=
|
|
18
|
+
netbox_dns/api/serializers_/zone.py,sha256=CctZ4aABInB7taj5MvWIF8KKAA6qsc0d1UyBPHdoOTc,5367
|
|
19
|
+
netbox_dns/api/serializers_/zone_template.py,sha256=NQFycXBNcHSMPID62o6w6EKbPkj_xCfNhXCUiwt2xic,4087
|
|
20
20
|
netbox_dns/choices/__init__.py,sha256=jOVs2VGV5SVADRlqVnrFeAy26i8BIeEAbGpiX7K8bL8,42
|
|
21
21
|
netbox_dns/choices/record.py,sha256=ZSpyiZE2YCsF2wF53A5DFWgwCIhkFhgOKt__RJ0KxSk,2084
|
|
22
22
|
netbox_dns/choices/zone.py,sha256=Vblm5RUtNtPNkULh8U1NxBMme1iHPllD6B6LkQkWZW4,621
|
|
@@ -33,21 +33,21 @@ netbox_dns/filtersets/registrar.py,sha256=Wh_l-IXRHnJhW7Pyokp3czQZISDKzXnWeSQKp5
|
|
|
33
33
|
netbox_dns/filtersets/registration_contact.py,sha256=903sOcHPRCI0dVzqn1i0pn5VPr_4YpHPh5QE2-akR-Y,1139
|
|
34
34
|
netbox_dns/filtersets/view.py,sha256=IlQz3k2J_N6eSbT9op0KOu3sKLrn-HTsJCcrIqoYgyY,1047
|
|
35
35
|
netbox_dns/filtersets/zone.py,sha256=zl39SOiYIZxAi3G1wx0s9UEIgh8hG9Bdb46qIXLwMr8,6334
|
|
36
|
-
netbox_dns/filtersets/zone_template.py,sha256=
|
|
36
|
+
netbox_dns/filtersets/zone_template.py,sha256=So-sxWeDhlm-DTtujYp5B_gDbnAVUHnLdRZgw7cOc4o,4347
|
|
37
37
|
netbox_dns/forms/__init__.py,sha256=axENVF9vX9BtDKCNxrapRjye1NnygUg9BS0BBj6a0io,209
|
|
38
38
|
netbox_dns/forms/nameserver.py,sha256=GJe3ece4yIGwMtLZ6wQihBrJu1dk_ZSiwX-vSU0fRa0,3397
|
|
39
39
|
netbox_dns/forms/record.py,sha256=QNGLqWprhsGFTSlH2YAe-SHmCx1K1QbT_osAhCegyJg,8252
|
|
40
|
-
netbox_dns/forms/record_template.py,sha256=
|
|
40
|
+
netbox_dns/forms/record_template.py,sha256=UcB-AlK-ZDoNmIMJhUrxfr76oGkwJ8d7JhkDj9vbMDI,6337
|
|
41
41
|
netbox_dns/forms/registrar.py,sha256=GaRH3w5zlhrpwy_U0pxlrl1DrAEaMB78MUlnGxBRwZI,3949
|
|
42
42
|
netbox_dns/forms/registration_contact.py,sha256=IhNAqElY7hOdpDG0jwWMdy3y2mB43xmjUhj3lsgJ3SE,5906
|
|
43
43
|
netbox_dns/forms/view.py,sha256=GacwKHXSDvxQEs-d3ys7rietqA_MzpSd0XjWaSsIbU0,10339
|
|
44
|
-
netbox_dns/forms/zone.py,sha256=
|
|
45
|
-
netbox_dns/forms/zone_template.py,sha256=
|
|
44
|
+
netbox_dns/forms/zone.py,sha256=b63kCukS4uFgkxGnQ_h-i8d-d8GaSINfppDJlrBuXJA,26195
|
|
45
|
+
netbox_dns/forms/zone_template.py,sha256=P7jdEz0MI_tjD_fuVDuKOIFCInqGI4opf7l_qaDmG1g,10098
|
|
46
46
|
netbox_dns/graphql/__init__.py,sha256=jghYD6uOSAis6YyLbtI3YJGZfwPw1uL2FBRsHs1EhNk,514
|
|
47
47
|
netbox_dns/graphql/filters.py,sha256=fHCjFIwbPBJJMk2W7HI8LhrfFhCtQtCM9IE8ZMgVafc,1766
|
|
48
48
|
netbox_dns/graphql/schema.py,sha256=q9DQ_hfRB0e6Znq4-IS6UEeTOfMkZmrWkwxcAql1uOA,2270
|
|
49
|
-
netbox_dns/graphql/types.py,sha256=
|
|
50
|
-
netbox_dns/locale/de/LC_MESSAGES/django.mo,sha256=
|
|
49
|
+
netbox_dns/graphql/types.py,sha256=zAH8bkMCQdp9_g8HzBdrxSS0spxLwvqHhMA61kp65gk,8268
|
|
50
|
+
netbox_dns/locale/de/LC_MESSAGES/django.mo,sha256=L0qwlTBiL4M5IRoN33eejQRgzP11oinumTdGzrsfKEA,20148
|
|
51
51
|
netbox_dns/locale/en/LC_MESSAGES/django.mo,sha256=GDnSZkfHs3yjtTsll7dksEEej4B50F8pc9RGytZNubM,393
|
|
52
52
|
netbox_dns/management/commands/cleanup_database.py,sha256=1-tAl0Sht80qaNZyfFyUW19Eh9gBUuc7GdbHN4aemGU,5935
|
|
53
53
|
netbox_dns/management/commands/cleanup_rrset_ttl.py,sha256=UFRURLBcFeGHUS2lrYFv7UWIebjI72aG1EUQJt0XsXw,2046
|
|
@@ -67,6 +67,8 @@ netbox_dns/migrations/0009_rename_contact_registrationcontact.py,sha256=-8IknnaI
|
|
|
67
67
|
netbox_dns/migrations/0010_view_ip_address_filter.py,sha256=uARQADJB7u1vpx0TBlOfGTkqMCF4xZclMhITESHm-ok,420
|
|
68
68
|
netbox_dns/migrations/0011_rename_related_fields.py,sha256=j9lI-QBmTSzOrAxDl02SdgHZtv9nRfJ3cZX_wjj5urM,1881
|
|
69
69
|
netbox_dns/migrations/0012_natural_ordering.py,sha256=h5XVSmRwisUqz5OJzkBW41dwHIBlu08zqG2-1mxiiw4,2725
|
|
70
|
+
netbox_dns/migrations/0013_zonetemplate_soa_mname_zonetemplate_soa_rname.py,sha256=Y6TdD_dUZ-Pb1kuRR3l3kSwObn_Cpcmp3tm75qSkc5g,795
|
|
71
|
+
netbox_dns/migrations/0014_alter_unique_constraints_lowercase.py,sha256=Ueesv7uoB2ZQ1-7kG_qsMlPv0mn3mdDeI8OoAKIschM,1409
|
|
70
72
|
netbox_dns/migrations/0020_netbox_3_4.py,sha256=UMcHdn8ZAuQjUaM_3rEGpktYrM0TuvhccD7Jt7WQnPs,1271
|
|
71
73
|
netbox_dns/migrations/0021_record_ip_address.py,sha256=EqdhWXmq7aiK4X79xTRUZng3zFncCl-8JoO65HqlJKw,3244
|
|
72
74
|
netbox_dns/migrations/0022_search.py,sha256=KW1ffEZ4-0dppGQ_KD1EN7iw8eQJOnDco-xfJFRZqKQ,172
|
|
@@ -81,14 +83,14 @@ netbox_dns/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
|
|
|
81
83
|
netbox_dns/mixins/__init__.py,sha256=LxTEfpod_RHCyMtnzDljv0_dwqp2z3Q6tqbXW8LTGD8,35
|
|
82
84
|
netbox_dns/mixins/object_modification.py,sha256=AR64fU5f7g-scNAj9b54eSoS9dpjyOpqrxXVXPcOhY8,1807
|
|
83
85
|
netbox_dns/models/__init__.py,sha256=iTTJNgUfPAmU4n41usqDSGPvncd4Wpsb9f43ryVDDOs,209
|
|
84
|
-
netbox_dns/models/nameserver.py,sha256=
|
|
85
|
-
netbox_dns/models/record.py,sha256=
|
|
86
|
-
netbox_dns/models/record_template.py,sha256=
|
|
87
|
-
netbox_dns/models/registrar.py,sha256=
|
|
88
|
-
netbox_dns/models/registration_contact.py,sha256=
|
|
89
|
-
netbox_dns/models/view.py,sha256=
|
|
90
|
-
netbox_dns/models/zone.py,sha256=
|
|
91
|
-
netbox_dns/models/zone_template.py,sha256=
|
|
86
|
+
netbox_dns/models/nameserver.py,sha256=ivZpIVfgQLdDhrtqYPi-zRbygVgl3aff2FMsq1M3qA8,4044
|
|
87
|
+
netbox_dns/models/record.py,sha256=ot2f5LVxj4ZjNanE29y-30iUK4YZS7-0-37ds3hWtjo,29716
|
|
88
|
+
netbox_dns/models/record_template.py,sha256=kt-_sMFSMKmuKU8voVqz1-Lh7Wi7lPcA2ExPFQYLoxM,5345
|
|
89
|
+
netbox_dns/models/registrar.py,sha256=L5tbO8rtOa0VCs_y90nHYLKSRKBnnUhh_6sxZ3Mm2kk,1942
|
|
90
|
+
netbox_dns/models/registration_contact.py,sha256=O7T1clUjuilZnDjvhJKaHZdmNEF4aLg2h8K5p4llWOs,3825
|
|
91
|
+
netbox_dns/models/view.py,sha256=gQvKNr_FmhG2EMz2T8kWbdK4b8CyqI-Qc67-Dgrx2SI,4808
|
|
92
|
+
netbox_dns/models/zone.py,sha256=GhFtsOkA0zPB0VMfXtqFgJZrnrLul-SqgouZbMBcc50,33465
|
|
93
|
+
netbox_dns/models/zone_template.py,sha256=QjjOvSZktH_6l64bCZzVudnL1s9qU6_ZVDkhrhW1zqc,4970
|
|
92
94
|
netbox_dns/signals/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
93
95
|
netbox_dns/signals/ipam_dnssync.py,sha256=1zhlf4cMcJLlFosX7YzyqVYdFFHV4MFwTz5KCdL8xQc,7730
|
|
94
96
|
netbox_dns/tables/__init__.py,sha256=axENVF9vX9BtDKCNxrapRjye1NnygUg9BS0BBj6a0io,209
|
|
@@ -100,7 +102,7 @@ netbox_dns/tables/registrar.py,sha256=XQtJj0c4O4gpCdUp903GSD0tIuARmJw13Nwosw9pTF
|
|
|
100
102
|
netbox_dns/tables/registration_contact.py,sha256=n_FKE90j6KNgPKRVq1WXg4vnOzFE238oXsi_NYVAU9M,931
|
|
101
103
|
netbox_dns/tables/view.py,sha256=gsuWQWAk3RstNIKzBDOHNHR2D3ykX6WigYLMj0VhQFs,1148
|
|
102
104
|
netbox_dns/tables/zone.py,sha256=_WihxcaUoQ2pgNyufXau8-yDqgIUMU6HAmbK5jxfIFM,1965
|
|
103
|
-
netbox_dns/tables/zone_template.py,sha256=
|
|
105
|
+
netbox_dns/tables/zone_template.py,sha256=6Cls0YZ1sI1nyQn9Yu2EMW5pTwOAcHsqNxhc6WuqXac,1647
|
|
104
106
|
netbox_dns/templates/netbox_dns/nameserver.html,sha256=MawPiuAmjFrbv0zRi-7xkm8vr-dT1tlEno8EcoQ9peU,1714
|
|
105
107
|
netbox_dns/templates/netbox_dns/record.html,sha256=1KBT4xDooTX9kt1cUoPD2-6QnMizPmbItA0JAAgRzfw,6550
|
|
106
108
|
netbox_dns/templates/netbox_dns/recordtemplate.html,sha256=a29PAUl-KI_I1lxWpVdPp2loJtzgis9DG9erOWrOZM0,3708
|
|
@@ -108,7 +110,7 @@ netbox_dns/templates/netbox_dns/registrar.html,sha256=4kJuj3biiDxQrIMQEQUEmF4iGR
|
|
|
108
110
|
netbox_dns/templates/netbox_dns/registrationcontact.html,sha256=sljVp_MrPSJRc2vJCPFXq9MiWOw4wjbr1kI_YStBntw,3094
|
|
109
111
|
netbox_dns/templates/netbox_dns/view.html,sha256=1MuzOYNQezRrryNjlklgxErjGTFoVnwqcxf4qceuglw,3320
|
|
110
112
|
netbox_dns/templates/netbox_dns/zone.html,sha256=Ci8MbZgd34vJh67F44_f7Tb4VvV4N14-H-Zh6-qDZsM,6894
|
|
111
|
-
netbox_dns/templates/netbox_dns/zonetemplate.html,sha256=
|
|
113
|
+
netbox_dns/templates/netbox_dns/zonetemplate.html,sha256=iE2Dzl3v9AqjUmPuqA5jhPnO94RWxtJgwX1NAr-wimE,3898
|
|
112
114
|
netbox_dns/templates/netbox_dns/record/managed.html,sha256=uwpxQTxyfAXkWqThLT-T2ZssKNUhXTDDMnLWJSVuDNU,119
|
|
113
115
|
netbox_dns/templates/netbox_dns/record/related.html,sha256=R59aPhE4CyIZtTH0ncwDyS6_wAe_Y-oZjuN_j4qk8iA,1158
|
|
114
116
|
netbox_dns/templates/netbox_dns/view/button.html,sha256=EMOB5x78XpyfN1qi-pY1CKKKLjyHo9rFUa4Uhq6rFMc,322
|
|
@@ -125,24 +127,24 @@ netbox_dns/templates/netbox_dns/zone/rfc2317_child_zone.html,sha256=rWlmb3zRQbLY
|
|
|
125
127
|
netbox_dns/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
126
128
|
netbox_dns/templatetags/netbox_dns.py,sha256=DND1DMPzv636Rak3M6Hor_Vw6pjqUfSTquofIw4dIsA,223
|
|
127
129
|
netbox_dns/utilities/__init__.py,sha256=cSGf-nGaRWx9b-Xrh3dLMJYoWNsZ6FF-qdmV4F1uOgg,74
|
|
128
|
-
netbox_dns/utilities/conversions.py,sha256=
|
|
129
|
-
netbox_dns/utilities/dns.py,sha256=
|
|
130
|
-
netbox_dns/utilities/ipam_dnssync.py,sha256=
|
|
130
|
+
netbox_dns/utilities/conversions.py,sha256=eKA17FSU-Us3cfda9DAgtZgmr3r2o5UbJ_1giD3LLvE,2713
|
|
131
|
+
netbox_dns/utilities/dns.py,sha256=UBiyQe8thiOTnKOmU9e2iRHHnGF9toVLe4efU623kX4,322
|
|
132
|
+
netbox_dns/utilities/ipam_dnssync.py,sha256=_yuHoah_QN-opsZB51yGCkwjkij7nrmTgKHUZ-bQrBI,9625
|
|
131
133
|
netbox_dns/validators/__init__.py,sha256=Mr8TvmcJTa8Pubj8TzbFBKfbHhEmGcr5JdQvczEJ39A,72
|
|
132
134
|
netbox_dns/validators/dns_name.py,sha256=Sil68Av49jfZPzgFMV_1qEcLnuuAWXmbxfAJPDXUsGg,3766
|
|
133
135
|
netbox_dns/validators/dns_value.py,sha256=-mc62mth-hlbPUPe_RlCR7vo1KSD6_gQDXiE8rjB-Cc,5206
|
|
134
136
|
netbox_dns/validators/rfc2317.py,sha256=uKkwxpakiFFKdYA0qy8WSlEnbFwJD4MDw6gGV4F6skg,706
|
|
135
137
|
netbox_dns/views/__init__.py,sha256=axENVF9vX9BtDKCNxrapRjye1NnygUg9BS0BBj6a0io,209
|
|
136
138
|
netbox_dns/views/nameserver.py,sha256=6lHg8fqBjc_SoITzFj1FiRARpPF7nSn9knAZxe9x5Rg,3932
|
|
137
|
-
netbox_dns/views/record.py,sha256=
|
|
139
|
+
netbox_dns/views/record.py,sha256=6tOTC7BbQ5XOC7wr94LjFMR3epOi47HP5qIETNvj5sE,6715
|
|
138
140
|
netbox_dns/views/record_template.py,sha256=CbSyckBvyEvcZCeZgK3q0fJsa1_5HbwUflh_iM7JjH0,3134
|
|
139
141
|
netbox_dns/views/registrar.py,sha256=Um_2wnzmP2bqbdMUhBPhny2My0R8fMXScQ9GLiTCrvg,2808
|
|
140
142
|
netbox_dns/views/registration_contact.py,sha256=c9KrNkfFNsb55pL74A5rN1CNx32M82V6mdwBYduNxas,3596
|
|
141
143
|
netbox_dns/views/view.py,sha256=VfrKaLC9D_KNZNmRyFVohRlmMlMbtblAuPgNg0LNyf8,3421
|
|
142
144
|
netbox_dns/views/zone.py,sha256=W66Miyaf4RKW-8z5wMrerrtmHclhht3h-lPqTWFpiOw,7163
|
|
143
145
|
netbox_dns/views/zone_template.py,sha256=IIW1lr6RQmhShtqJu6A6LnHdxdBrkkZQHxIDSTqQeyc,2705
|
|
144
|
-
netbox_plugin_dns-1.2.
|
|
145
|
-
netbox_plugin_dns-1.2.
|
|
146
|
-
netbox_plugin_dns-1.2.
|
|
147
|
-
netbox_plugin_dns-1.2.
|
|
148
|
-
netbox_plugin_dns-1.2.
|
|
146
|
+
netbox_plugin_dns-1.2.5.dist-info/LICENSE,sha256=I3tDu11bZfhFm3EkV4zOD5TmWgLjnUNLEFwrdjniZYs,1112
|
|
147
|
+
netbox_plugin_dns-1.2.5.dist-info/METADATA,sha256=CmCyW64s9xmp49ADdEBc48ns2IOYXysCOA8zL9-Ad_0,7636
|
|
148
|
+
netbox_plugin_dns-1.2.5.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
|
149
|
+
netbox_plugin_dns-1.2.5.dist-info/top_level.txt,sha256=sA1Rwl1mRKvMC6XHe2ylZ1GF-Q1NGd08XedK9Y4xZc4,11
|
|
150
|
+
netbox_plugin_dns-1.2.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|