netbox-plugin-dns 1.2.2__py3-none-any.whl → 1.2.4__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.

Files changed (40) hide show
  1. netbox_dns/__init__.py +1 -1
  2. netbox_dns/api/serializers_/zone.py +14 -2
  3. netbox_dns/api/serializers_/zone_template.py +9 -0
  4. netbox_dns/filtersets/zone_template.py +14 -0
  5. netbox_dns/forms/zone.py +37 -10
  6. netbox_dns/forms/zone_template.py +46 -2
  7. netbox_dns/graphql/types.py +7 -0
  8. netbox_dns/locale/de/LC_MESSAGES/django.mo +0 -0
  9. netbox_dns/migrations/0013_zonetemplate_soa_mname_zonetemplate_soa_rname.py +30 -0
  10. netbox_dns/models/__init__.py +0 -5
  11. netbox_dns/models/nameserver.py +1 -0
  12. netbox_dns/models/record.py +1 -0
  13. netbox_dns/models/record_template.py +6 -3
  14. netbox_dns/models/registrar.py +1 -0
  15. netbox_dns/models/registration_contact.py +1 -0
  16. netbox_dns/models/view.py +1 -0
  17. netbox_dns/models/zone.py +26 -9
  18. netbox_dns/models/zone_template.py +44 -10
  19. netbox_dns/tables/zone_template.py +8 -1
  20. netbox_dns/templates/netbox_dns/nameserver.html +2 -0
  21. netbox_dns/templates/netbox_dns/record.html +20 -20
  22. netbox_dns/templates/netbox_dns/recordtemplate.html +6 -6
  23. netbox_dns/templates/netbox_dns/view.html +2 -0
  24. netbox_dns/templates/netbox_dns/zone.html +2 -4
  25. netbox_dns/templates/netbox_dns/zonetemplate.html +8 -2
  26. netbox_dns/utilities/conversions.py +25 -0
  27. netbox_dns/validators/dns_name.py +9 -0
  28. netbox_dns/views/nameserver.py +1 -0
  29. netbox_dns/views/record.py +1 -0
  30. netbox_dns/views/record_template.py +1 -0
  31. netbox_dns/views/registrar.py +1 -0
  32. netbox_dns/views/registration_contact.py +1 -0
  33. netbox_dns/views/view.py +1 -0
  34. netbox_dns/views/zone.py +1 -0
  35. netbox_dns/views/zone_template.py +1 -0
  36. {netbox_plugin_dns-1.2.2.dist-info → netbox_plugin_dns-1.2.4.dist-info}/METADATA +4 -3
  37. {netbox_plugin_dns-1.2.2.dist-info → netbox_plugin_dns-1.2.4.dist-info}/RECORD +40 -39
  38. {netbox_plugin_dns-1.2.2.dist-info → netbox_plugin_dns-1.2.4.dist-info}/LICENSE +0 -0
  39. {netbox_plugin_dns-1.2.2.dist-info → netbox_plugin_dns-1.2.4.dist-info}/WHEEL +0 -0
  40. {netbox_plugin_dns-1.2.2.dist-info → netbox_plugin_dns-1.2.4.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.2"
10
+ __version__ = "1.2.4"
11
11
 
12
12
 
13
13
  def _check_list(setting):
@@ -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.apply_to_zone(zone)
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.apply_to_zone(zone)
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)
netbox_dns/forms/zone.py CHANGED
@@ -35,7 +35,7 @@ from netbox_dns.models import (
35
35
  ZoneTemplate,
36
36
  )
37
37
  from netbox_dns.choices import ZoneStatusChoices
38
- from netbox_dns.utilities import name_to_unicode
38
+ from netbox_dns.utilities import name_to_unicode, network_to_reverse
39
39
  from netbox_dns.fields import RFC2317NetworkFormField
40
40
  from netbox_dns.validators import validate_ipv4, validate_prefix, validate_rfc2317
41
41
 
@@ -66,13 +66,20 @@ class ZoneTemplateUpdateMixin:
66
66
  self.cleaned_data["tags"] = template.tags.all()
67
67
 
68
68
  for field in template.template_fields:
69
- if (
70
- self.cleaned_data.get(field) is None
71
- and getattr(template, field) is not None
72
- ):
69
+ if self.cleaned_data.get(field) in (None, "") and getattr(
70
+ template, field
71
+ ) not in (None, ""):
73
72
  self.cleaned_data[field] = getattr(template, field)
74
73
 
75
- template_error = None
74
+ if self.cleaned_data.get("soa_mname") is None:
75
+ self.add_error(
76
+ "soa_mname",
77
+ _("soa_mname not set and no template or default value defined"),
78
+ )
79
+
80
+ if self.errors:
81
+ return
82
+
76
83
  saved_events_queue = events_queue.get()
77
84
 
78
85
  try:
@@ -106,13 +113,20 @@ class ZoneTemplateUpdateMixin:
106
113
  raise RollbackTransaction
107
114
 
108
115
  except ValidationError as exc:
109
- self.add_error("template", exc.messages)
116
+ if hasattr(exc, "error_dict"):
117
+ for field_name in self.fields.keys():
118
+ exc.error_dict.pop(field_name, None)
119
+ errors = exc.error_dict.values()
120
+ else:
121
+ errors = exc.messages
122
+
123
+ for error in errors:
124
+ self.add_error("template", error)
125
+
110
126
  except RollbackTransaction:
111
127
  pass
112
128
 
113
129
  events_queue.set(saved_events_queue)
114
- if template_error is not None:
115
- raise ValidationError({"template": template_error})
116
130
 
117
131
  return self.cleaned_data
118
132
 
@@ -161,8 +175,14 @@ class ZoneForm(ZoneTemplateUpdateMixin, TenancyForm, NetBoxModelForm):
161
175
  validators=[MinValueValidator(1)],
162
176
  label=_("SOA TTL"),
163
177
  )
178
+ soa_mname = DynamicModelChoiceField(
179
+ queryset=NameServer.objects.all(),
180
+ help_text=_("Primary nameserver this zone"),
181
+ required=False,
182
+ label=_("SOA MName"),
183
+ )
164
184
  soa_rname = forms.CharField(
165
- required=True,
185
+ required=False,
166
186
  help_text=_("Mailbox of the zone's administrator"),
167
187
  label=_("SOA RName"),
168
188
  )
@@ -307,6 +327,13 @@ class ZoneForm(ZoneTemplateUpdateMixin, TenancyForm, NetBoxModelForm):
307
327
  else self.initial["default_ttl"]
308
328
  )
309
329
 
330
+ def clean_name(self):
331
+ name = self.cleaned_data["name"]
332
+ if reverse_name := network_to_reverse(name):
333
+ return reverse_name
334
+ else:
335
+ return name
336
+
310
337
  class Meta:
311
338
  model = Zone
312
339
 
@@ -40,6 +40,11 @@ class ZoneTemplateForm(TenancyForm, NetBoxModelForm):
40
40
  queryset=NameServer.objects.all(),
41
41
  required=False,
42
42
  )
43
+ soa_mname = DynamicModelChoiceField(
44
+ queryset=NameServer.objects.all(),
45
+ required=False,
46
+ label=_("MName"),
47
+ )
43
48
  record_templates = DynamicModelMultipleChoiceField(
44
49
  queryset=RecordTemplate.objects.all(),
45
50
  required=False,
@@ -47,6 +52,7 @@ class ZoneTemplateForm(TenancyForm, NetBoxModelForm):
47
52
 
48
53
  fieldsets = (
49
54
  FieldSet("name", "description", "nameservers", name=_("Zone Template")),
55
+ FieldSet("soa_mname", "soa_rname", name=_("SOA")),
50
56
  FieldSet("record_templates", name=_("Record Templates")),
51
57
  FieldSet(
52
58
  "registrar",
@@ -66,6 +72,8 @@ class ZoneTemplateForm(TenancyForm, NetBoxModelForm):
66
72
  fields = (
67
73
  "name",
68
74
  "nameservers",
75
+ "soa_mname",
76
+ "soa_rname",
69
77
  "record_templates",
70
78
  "description",
71
79
  "registrar",
@@ -77,6 +85,9 @@ class ZoneTemplateForm(TenancyForm, NetBoxModelForm):
77
85
  "tenant",
78
86
  "tags",
79
87
  )
88
+ labels = {
89
+ "soa_rname": _("RName"),
90
+ }
80
91
 
81
92
 
82
93
  class ZoneTemplateFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
@@ -84,6 +95,7 @@ class ZoneTemplateFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
84
95
  fieldsets = (
85
96
  FieldSet("q", "filter_id", "tag"),
86
97
  FieldSet("name", "nameserver_id", "description", name=_("Attributes")),
98
+ FieldSet("soa_mname_id", "soa_rname", name=_("SOA")),
87
99
  FieldSet("record_template_id", name=_("Record Templates")),
88
100
  FieldSet(
89
101
  "registrar_id",
@@ -105,6 +117,15 @@ class ZoneTemplateFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
105
117
  required=False,
106
118
  label=_("Nameservers"),
107
119
  )
120
+ soa_mname_id = DynamicModelMultipleChoiceField(
121
+ queryset=NameServer.objects.all(),
122
+ required=False,
123
+ label=_("MName"),
124
+ )
125
+ soa_rname = forms.CharField(
126
+ required=False,
127
+ label=_("RName"),
128
+ )
108
129
  record_template_id = DynamicModelMultipleChoiceField(
109
130
  queryset=RecordTemplate.objects.all(),
110
131
  required=False,
@@ -148,11 +169,17 @@ class ZoneTemplateImportForm(NetBoxModelImportForm):
148
169
  required=False,
149
170
  label=_("Nameservers"),
150
171
  )
172
+ soa_mname = CSVModelChoiceField(
173
+ queryset=NameServer.objects.all(),
174
+ to_field_name="name",
175
+ required=False,
176
+ label=_("SOA MName"),
177
+ )
151
178
  record_templates = CSVModelMultipleChoiceField(
152
179
  queryset=RecordTemplate.objects.all(),
153
180
  to_field_name="name",
154
181
  required=False,
155
- label=_("Record Remplates"),
182
+ label=_("Record Templates"),
156
183
  )
157
184
  registrar = CSVModelChoiceField(
158
185
  queryset=Registrar.objects.all(),
@@ -212,6 +239,8 @@ class ZoneTemplateImportForm(NetBoxModelImportForm):
212
239
  fields = (
213
240
  "name",
214
241
  "nameservers",
242
+ "soa_mname",
243
+ "soa_rname",
215
244
  "record_templates",
216
245
  "description",
217
246
  "registrar",
@@ -230,12 +259,20 @@ class ZoneTemplateBulkEditForm(NetBoxModelBulkEditForm):
230
259
  required=False,
231
260
  label=_("Nameservers"),
232
261
  )
262
+ soa_mname = DynamicModelChoiceField(
263
+ queryset=NameServer.objects.all(),
264
+ required=False,
265
+ label=_("MName"),
266
+ )
267
+ soa_rname = forms.CharField(max_length=255, required=False, label=_("RName"))
233
268
  record_templates = DynamicModelMultipleChoiceField(
234
269
  queryset=RecordTemplate.objects.all(),
235
270
  required=False,
236
271
  label=_("Record Templates"),
237
272
  )
238
- description = forms.CharField(max_length=200, required=False)
273
+ description = forms.CharField(
274
+ max_length=200, required=False, label=_("Description")
275
+ )
239
276
  registrar = DynamicModelChoiceField(
240
277
  queryset=Registrar.objects.all(),
241
278
  required=False,
@@ -280,6 +317,11 @@ class ZoneTemplateBulkEditForm(NetBoxModelBulkEditForm):
280
317
  "description",
281
318
  name=_("Attributes"),
282
319
  ),
320
+ FieldSet(
321
+ "soa_mname",
322
+ "soa_rname",
323
+ name=_("SOA"),
324
+ ),
283
325
  FieldSet(
284
326
  "record_templates",
285
327
  name=_("Record Templates"),
@@ -298,6 +340,8 @@ class ZoneTemplateBulkEditForm(NetBoxModelBulkEditForm):
298
340
  nullable_fields = (
299
341
  "description",
300
342
  "nameservers",
343
+ "soa_mname",
344
+ "soa_rname",
301
345
  "record_templates",
302
346
  "registrar",
303
347
  "registrant",
@@ -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
+ ]
@@ -6,8 +6,3 @@ from .registration_contact import *
6
6
  from .registrar import *
7
7
  from .zone_template import *
8
8
  from .record_template import *
9
-
10
- # +
11
- # Backwards compatibility fix, will be removed in version 1.1
12
- # -
13
- from netbox_dns.choices import *
@@ -69,6 +69,7 @@ class NameServer(ObjectModificationMixin, ContactsMixin, NetBoxModel):
69
69
  def display_name(self):
70
70
  return name_to_unicode(self.name)
71
71
 
72
+ # TODO: Remove in version 1.3.0 (NetBox #18555)
72
73
  def get_absolute_url(self):
73
74
  return reverse("plugins:netbox_dns:nameserver", kwargs={"pk": self.pk})
74
75
 
@@ -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
 
@@ -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,9 +164,11 @@ class RecordTemplate(NetBoxModel):
163
164
  record = Record.objects.create(**record_data)
164
165
  except ValidationError as exc:
165
166
  raise ValidationError(
166
- _("Error while processing record template {template}: {error}").format(
167
- template=self, error=exc.messages[0]
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():
@@ -57,6 +57,7 @@ class Registrar(NetBoxModel):
57
57
  blank=True,
58
58
  )
59
59
 
60
+ # TODO: Remove in version 1.3.0 (NetBox #18555)
60
61
  def get_absolute_url(self):
61
62
  return reverse("plugins:netbox_dns:registrar", kwargs={"pk": self.pk})
62
63
 
@@ -118,6 +118,7 @@ class RegistrationContact(NetBoxModel):
118
118
  "tags",
119
119
  )
120
120
 
121
+ # TODO: Remove in version 1.3.0 (NetBox #18555)
121
122
  def get_absolute_url(self):
122
123
  return reverse("plugins:netbox_dns:registrationcontact", kwargs={"pk": self.pk})
123
124
 
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
@@ -350,6 +350,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
350
350
  def get_status_color(self):
351
351
  return ZoneStatusChoices.colors.get(self.status)
352
352
 
353
+ # TODO: Remove in version 1.3.0 (NetBox #18555)
353
354
  def get_absolute_url(self):
354
355
  return reverse("plugins:netbox_dns:zone", kwargs={"pk": self.pk})
355
356
 
@@ -670,15 +671,25 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
670
671
  if value not in (None, ""):
671
672
  setattr(self, field, value)
672
673
 
673
- if self.soa_mname_id is None:
674
- default_soa_mname = defaults.get("zone_soa_mname")
675
- try:
676
- self.soa_mname = NameServer.objects.get(name=default_soa_mname)
677
- except NameServer.DoesNotExist:
678
- raise ValidationError(
679
- _("Default soa_mname instance {nameserver} does not exist").format(
680
- nameserver=default_soa_mname
674
+ if self.soa_mname_id is None and "soa_mname" not in exclude:
675
+ if default_soa_mname := defaults.get("zone_soa_mname"):
676
+ try:
677
+ self.soa_mname = NameServer.objects.get(name=default_soa_mname)
678
+ except NameServer.DoesNotExist:
679
+ raise ValidationError(
680
+ {
681
+ "soa_mname": _(
682
+ "Default soa_mname instance {nameserver} does not exist"
683
+ ).format(nameserver=default_soa_mname)
684
+ }
681
685
  )
686
+ else:
687
+ raise ValidationError(
688
+ {
689
+ "soa_mname": _(
690
+ "soa_mname not set and no template or default value defined"
691
+ )
692
+ }
682
693
  )
683
694
 
684
695
  super().clean_fields(exclude=exclude)
@@ -706,7 +717,13 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
706
717
  )
707
718
 
708
719
  if self.soa_rname in (None, ""):
709
- raise ValidationError(_("soa_rname not set and no default value defined"))
720
+ raise ValidationError(
721
+ {
722
+ "soa_rname": _(
723
+ "soa_rname not set and no template or default value defined"
724
+ ),
725
+ }
726
+ )
710
727
  try:
711
728
  dns_name.from_text(self.soa_rname, origin=dns_name.root)
712
729
  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 apply_to_zone(self, zone):
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 = ("description",)
51
+ fields = (
52
+ "soa_rname",
53
+ "description",
54
+ )
48
55
  default_columns = (
49
56
  "name",
50
57
  "tags",
@@ -23,6 +23,7 @@
23
23
  <td style="word-break:break-all;">{{ object.description }}</td>
24
24
  </tr>
25
25
  {% endif %}
26
+ {% if object.tenant %}
26
27
  <tr>
27
28
  <th scope="row">{% trans "Tenant" %}</th>
28
29
  <td>
@@ -32,6 +33,7 @@
32
33
  {{ object.tenant|linkify|placeholder }}
33
34
  </td>
34
35
  </tr>
36
+ {% endif %}
35
37
  </table>
36
38
  </div>
37
39
  {% include 'inc/panels/custom_fields.html' %}
@@ -63,17 +63,6 @@
63
63
  <td><a href="{% url 'plugins:netbox_dns:zone_records' pk=object.zone.pk %}">{{ object.zone }}</a></td>
64
64
  {% endif %}
65
65
  </tr>
66
- {% if not object.managed or object.tenant %}
67
- <tr>
68
- <th scope="row">{% trans "Tenant" %}</th>
69
- <td>
70
- {% if object.tenant.group %}
71
- {{ object.tenant.group|linkify }} /
72
- {% endif %}
73
- {{ object.tenant|linkify|placeholder }}
74
- </td>
75
- </tr>
76
- {% endif %}
77
66
  <tr>
78
67
  <th scope="row">{% trans "Type" %}</th>
79
68
  <td>{{ object.type }}</td>
@@ -82,16 +71,33 @@
82
71
  <th scope="row">{% trans "Value" %}</th>
83
72
  <td style="word-break:break-all;">{{ object.value }}</td>
84
73
  </tr>
74
+ {% if unicode_value %}
75
+ <tr>
76
+ <th scope="row">{% trans "Unicode Value" %}</th>
77
+ <td style="word-break:break-all;">{{ unicode_value }}</td>
78
+ </tr>
79
+ {% endif %}
85
80
  {% if cname_warning %}
86
81
  <tr class="text-warning">
87
82
  <th scope="row">{% trans "Warning" %}</th>
88
83
  <td>{{ cname_warning }}</td>
89
84
  </tr>
90
85
  {% endif %}
91
- {% if unicode_value %}
86
+ {% if object.description %}
92
87
  <tr>
93
- <th scope="row">{% trans "Unicode Value" %}</th>
94
- <td style="word-break:break-all;">{{ unicode_value }}</td>
88
+ <th scope="row">{% trans "Description" %}</th>
89
+ <td style="word-break:break-all;">{{ object.description }}</td>
90
+ </tr>
91
+ {% endif %}
92
+ {% if object.tenant %}
93
+ <tr>
94
+ <th scope="row">{% trans "Tenant" %}</th>
95
+ <td>
96
+ {% if object.tenant.group %}
97
+ {{ object.tenant.group|linkify }} /
98
+ {% endif %}
99
+ {{ object.tenant|linkify|placeholder }}
100
+ </td>
95
101
  </tr>
96
102
  {% endif %}
97
103
  <tr>
@@ -132,12 +138,6 @@
132
138
  <th scope="row">Status</th>
133
139
  <td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
134
140
  </tr>
135
- {% if object.description %}
136
- <tr>
137
- <th scope="row">{% trans "Description" %}</th>
138
- <td style="word-break:break-all;">{{ object.description }}</td>
139
- </tr>
140
- {% endif %}
141
141
  </table>
142
142
  </div>
143
143
  {% if cname_target_table %}
@@ -25,6 +25,12 @@
25
25
  <td style="word-break:break-all;">{{ unicode_name }}</td>
26
26
  </tr>
27
27
  {% endif %}
28
+ {% if object.description %}
29
+ <tr>
30
+ <th scope="row">{% trans "Description" %}</th>
31
+ <td style="word-break:break-all;">{{ object.description }}</td>
32
+ </tr>
33
+ {% endif %}
28
34
  {% if object.tenant %}
29
35
  <tr>
30
36
  <th scope="row">{% trans "Tenant" %}</th>
@@ -64,12 +70,6 @@
64
70
  <th scope="row">{% trans "Status" %}</th>
65
71
  <td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
66
72
  </tr>
67
- {% if object.description %}
68
- <tr>
69
- <th scope="row">{% trans "Description" %}</th>
70
- <td style="word-break:break-all;">{{ object.description }}</td>
71
- </tr>
72
- {% endif %}
73
73
  </table>
74
74
  </div>
75
75
  {% include 'inc/panels/custom_fields.html' %}
@@ -21,6 +21,7 @@
21
21
  <td style="word-break:break-all;">{{ object.description }}</td>
22
22
  </tr>
23
23
  {% endif %}
24
+ {% if object.tenant %}
24
25
  <tr>
25
26
  <th scope="row">{% trans "Tenant" %}</th>
26
27
  <td>
@@ -30,6 +31,7 @@
30
31
  {{ object.tenant|linkify|placeholder }}
31
32
  </td>
32
33
  </tr>
34
+ {% endif %}
33
35
  </table>
34
36
  </div>
35
37
  {% include 'inc/panels/custom_fields.html' %}
@@ -36,6 +36,7 @@
36
36
  <td style="word-break:break-all;">{{ object.description }}</td>
37
37
  </tr>
38
38
  {% endif %}
39
+ {% if object.tenant %}
39
40
  <tr>
40
41
  <th scope="row">{% trans "Tenant" %}</th>
41
42
  <td>
@@ -45,6 +46,7 @@
45
46
  {{ object.tenant|linkify|placeholder }}
46
47
  </td>
47
48
  </tr>
49
+ {% endif %}
48
50
  <tr>
49
51
  <th scope="row">{% trans "Status" %}</th>
50
52
  <td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
@@ -91,10 +93,6 @@
91
93
  <th scope="row">{% trans "Default TTL" %}</th>
92
94
  <td>{{ object.default_ttl }}</td>
93
95
  </tr>
94
- <tr>
95
- <th scope="row">{% trans "Description" %}</th>
96
- <td>{{ object.description }}</td>
97
- </tr>
98
96
  </table>
99
97
  </div>
100
98
 
@@ -21,6 +21,7 @@
21
21
  <td style="word-break:break-all;">{{ object.description }}</td>
22
22
  </tr>
23
23
  {% endif %}
24
+ {% if object.tenant %}
24
25
  <tr>
25
26
  <th scope="row">{% trans "Tenant" %}</th>
26
27
  <td>
@@ -30,6 +31,7 @@
30
31
  {{ object.tenant|linkify|placeholder }}
31
32
  </td>
32
33
  </tr>
34
+ {% endif %}
33
35
  <tr>
34
36
  <th scope="row">{% trans "Nameservers" %}</th>
35
37
  <td>
@@ -41,8 +43,12 @@
41
43
  </td>
42
44
  </tr>
43
45
  <tr>
44
- <th scope="row">{% trans "Description" %}</th>
45
- <td>{{ object.description }}</td>
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>
46
52
  </tr>
47
53
  </table>
48
54
  </div>
@@ -13,6 +13,7 @@ __all__ = (
13
13
  "name_to_unicode",
14
14
  "value_to_unicode",
15
15
  "normalize_name",
16
+ "network_to_reverse",
16
17
  )
17
18
 
18
19
 
@@ -81,3 +82,27 @@ def normalize_name(name):
81
82
 
82
83
  except DNSException as exc:
83
84
  raise NameFormatError from exc
85
+
86
+
87
+ def network_to_reverse(network):
88
+ try:
89
+ ip_network = IPNetwork(network)
90
+ except AddrFormatError:
91
+ return
92
+
93
+ if ip_network.first == ip_network.last:
94
+ return
95
+
96
+ labels = None
97
+ match ip_network.version:
98
+ case 4:
99
+ if not ip_network.prefixlen % 8:
100
+ labels = 3 + ip_network.prefixlen // 8
101
+ case 6:
102
+ if not ip_network.prefixlen % 4:
103
+ labels = 3 + ip_network.prefixlen // 4
104
+ case _:
105
+ return
106
+
107
+ if labels:
108
+ return ".".join(ip_network[0].reverse_dns.split(".")[-labels:])
@@ -1,4 +1,5 @@
1
1
  import re
2
+ from socket import inet_aton
2
3
 
3
4
  from django.core.exceptions import ValidationError
4
5
  from django.utils.translation import gettext as _
@@ -95,6 +96,14 @@ def validate_domain_name(
95
96
  ):
96
97
  return
97
98
 
99
+ try:
100
+ inet_aton(name)
101
+ raise ValidationError(
102
+ _("{name} is not a valid DNS domain name").format(name=name)
103
+ )
104
+ except OSError:
105
+ pass
106
+
98
107
  label, zone_label = _get_label(always_tolerant=always_tolerant)
99
108
  if zone_name:
100
109
  regex = rf"^{zone_label}(\.{zone_label})*\.?$"
@@ -83,6 +83,7 @@ class NameServerBulkEditView(generic.BulkEditView):
83
83
  @register_model_view(NameServer, "bulk_delete", path="delete", detail=False)
84
84
  class NameServerBulkDeleteView(generic.BulkDeleteView):
85
85
  queryset = NameServer.objects.all()
86
+ filterset = NameServerFilterSet
86
87
  table = NameServerTable
87
88
 
88
89
 
@@ -198,6 +198,7 @@ class RecordBulkEditView(generic.BulkEditView):
198
198
  @register_model_view(Record, "bulk_delete", path="delete", detail=False)
199
199
  class RecordBulkDeleteView(generic.BulkDeleteView):
200
200
  queryset = Record.objects.filter(managed=False)
201
+ filterset = RecordFilterSet
201
202
  table = RecordTable
202
203
 
203
204
 
@@ -90,4 +90,5 @@ class RecordTemplateBulkEditView(generic.BulkEditView):
90
90
  @register_model_view(RecordTemplate, "bulk_delete", path="delete", detail=False)
91
91
  class RecordTemplateBulkDeleteView(generic.BulkDeleteView):
92
92
  queryset = RecordTemplate.objects.all()
93
+ filterset = RecordTemplateFilterSet
93
94
  table = RecordTemplateTable
@@ -72,6 +72,7 @@ class RegistrarBulkEditView(generic.BulkEditView):
72
72
  @register_model_view(Registrar, "bulk_delete", path="delete", detail=False)
73
73
  class RegistrarBulkDeleteView(generic.BulkDeleteView):
74
74
  queryset = Registrar.objects.all()
75
+ filterset = RegistrarFilterSet
75
76
  table = RegistrarTable
76
77
 
77
78
 
@@ -73,6 +73,7 @@ class RegistrationContactBulkEditView(generic.BulkEditView):
73
73
  @register_model_view(RegistrationContact, "bulk_delete", path="delete", detail=False)
74
74
  class RegistrationContactBulkDeleteView(generic.BulkDeleteView):
75
75
  queryset = RegistrationContact.objects.all()
76
+ filterset = RegistrationContactFilterSet
76
77
  table = RegistrationContactTable
77
78
 
78
79
 
netbox_dns/views/view.py CHANGED
@@ -76,6 +76,7 @@ class ViewBulkEditView(generic.BulkEditView):
76
76
  @register_model_view(View, "bulk_delete", path="delete", detail=False)
77
77
  class ViewBulkDeleteView(generic.BulkDeleteView):
78
78
  queryset = View.objects.all()
79
+ filterset = ViewFilterSet
79
80
  table = ViewTable
80
81
 
81
82
 
netbox_dns/views/zone.py CHANGED
@@ -101,6 +101,7 @@ class ZoneBulkEditView(generic.BulkEditView):
101
101
  @register_model_view(Zone, "bulk_delete", path="delete", detail=False)
102
102
  class ZoneBulkDeleteView(generic.BulkDeleteView):
103
103
  queryset = Zone.objects.all()
104
+ filterset = ZoneFilterSet
104
105
  table = ZoneTable
105
106
 
106
107
 
@@ -80,4 +80,5 @@ class ZoneTemplateBulkEditView(generic.BulkEditView):
80
80
  @register_model_view(ZoneTemplate, "bulk_delete", path="delete", detail=False)
81
81
  class ZoneTemplateBulkDeleteView(generic.BulkDeleteView):
82
82
  queryset = ZoneTemplate.objects.all()
83
+ filterset = ZoneTemplateFilterSet
83
84
  table = ZoneTemplateTable
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: netbox-plugin-dns
3
- Version: 1.2.2
3
+ Version: 1.2.4
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
@@ -69,7 +69,7 @@ For integration with a large number of DNS server implementations integration to
69
69
  * NetBox 4.2.0 or higher
70
70
  * Python 3.10 or higher
71
71
 
72
- ## Compatibility with earlier NetBox Versions
72
+ ## Compatibility with NetBox Versions
73
73
 
74
74
  NetBox Version | NetBox DNS Version | Comment
75
75
  -------------- | ------------------ | -------
@@ -78,6 +78,7 @@ NetBox Version | NetBox DNS Version | Comment
78
78
  3.7 | 0.22 |
79
79
  4.0 - 4.1 | 1.0 | Supports legacy IPAM Coupling
80
80
  4.0 - 4.1 | 1.1 | Supports IPAM DNSsync
81
+ 4.2 | 1.2 |
81
82
 
82
83
  ## Installation & Configuration
83
84
 
@@ -98,7 +99,7 @@ PLUGINS = [
98
99
  ]
99
100
  ```
100
101
 
101
- To permanently keep the plugin installed when updating NetBox via `update.sh`:
102
+ To permanently keep the plugin installed when updating NetBox via `upgrade.sh`:
102
103
 
103
104
  ```
104
105
  echo netbox-plugin-dns >> ~/netbox/local_requirements.txt
@@ -1,4 +1,4 @@
1
- netbox_dns/__init__.py,sha256=WLiNzOodcLZS1ifW8vje3J16M5o4dDYyPmtG8GcMbpY,3053
1
+ netbox_dns/__init__.py,sha256=IK52Nv5uZ8o5PxRMYHWrkVWja58odhfoYaHhG3rQgn4,3053
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=ELAis8nj9PZlge6zqTQ23P0vC4IJBDa3hh5kZ9T8G_4,5003
19
- netbox_dns/api/serializers_/zone_template.py,sha256=w0TsrqS_DgIIAUozCC-gc9lsQ67lpVkvbyphyuRzq6Q,3847
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,7 +33,7 @@ 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=Sm40P33IhN0sOqtjz4JzoBbEK-dTLpfQqYGcM_Xb7KM,3870
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
@@ -41,13 +41,13 @@ netbox_dns/forms/record_template.py,sha256=uN6ZSepNilQuqyfPpW-pMfmTRWo0IrDxp1LdO
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=8_-C2iy_ByMbjIea33xiGDYXhC69NdqhjQz-nOPwoMw,25011
45
- netbox_dns/forms/zone_template.py,sha256=49vhM-Lc4JAGZD-al4QpPDLfwmpu82JNuX-bxpwabmc,8609
44
+ netbox_dns/forms/zone.py,sha256=EAI16GZ1gjt8nn8AcS75ZmncL6K27f4h6XLbVNJKZB4,25824
45
+ netbox_dns/forms/zone_template.py,sha256=z9Ys8uNCH9Inxmvz7qLLoA7enb3-pxQ_9-O0vcQ-y94,9854
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=8DjYxWOfjmS5HFW-eaQLl9E12pbg2xlWNwV5CH6ptdY,8092
50
- netbox_dns/locale/de/LC_MESSAGES/django.mo,sha256=0ij8AzrkWdwtUejXTOTdJJcIRweZfQT3iWjAXrf6hyM,20335
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,7 @@ 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
70
71
  netbox_dns/migrations/0020_netbox_3_4.py,sha256=UMcHdn8ZAuQjUaM_3rEGpktYrM0TuvhccD7Jt7WQnPs,1271
71
72
  netbox_dns/migrations/0021_record_ip_address.py,sha256=EqdhWXmq7aiK4X79xTRUZng3zFncCl-8JoO65HqlJKw,3244
72
73
  netbox_dns/migrations/0022_search.py,sha256=KW1ffEZ4-0dppGQ_KD1EN7iw8eQJOnDco-xfJFRZqKQ,172
@@ -80,15 +81,15 @@ netbox_dns/migrations/0029_record_fqdn.py,sha256=UAAU38ekKQyiYDOJlcrz6Qbk4bqZfSH
80
81
  netbox_dns/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
82
  netbox_dns/mixins/__init__.py,sha256=LxTEfpod_RHCyMtnzDljv0_dwqp2z3Q6tqbXW8LTGD8,35
82
83
  netbox_dns/mixins/object_modification.py,sha256=AR64fU5f7g-scNAj9b54eSoS9dpjyOpqrxXVXPcOhY8,1807
83
- netbox_dns/models/__init__.py,sha256=5Ns9RaemTe5L0L3c6a38RxembWhV-sX9cqfjl05aPQw,313
84
- netbox_dns/models/nameserver.py,sha256=GKCWPKqg8WLVQS6UYRUyTdVb_865p2io06yYp5Z9b80,3407
85
- netbox_dns/models/record.py,sha256=7LCLc3mLbgHx5yCMzIItQTB5Jzg8P-1uWGhfYeGpN4U,29415
86
- netbox_dns/models/record_template.py,sha256=PC4369q_LIJkImp1_jhiTTwy083MXIGpGADnbDHMbqI,5104
87
- netbox_dns/models/registrar.py,sha256=bjgYgeUtWGg_seDRN1-VV4Pe450ZK85lbALo4J_Zuic,1890
88
- netbox_dns/models/registration_contact.py,sha256=AkpNy9KbFV9YrISdepqZA1ZfckZSA9u_vfPUAf5Z4H8,3773
89
- netbox_dns/models/view.py,sha256=1OGbol3Ekg1G7c6kPRLwTaLW_ugzNozewnaN2SpyOqc,4756
90
- netbox_dns/models/zone.py,sha256=gq2gnF_ykzZ9kLtiHSm1Lwku-uWU_iFv-0HPlH54JzI,32267
91
- netbox_dns/models/zone_template.py,sha256=kH16CdFk7OpjSiKfJb3bsBi--Shp2V1Fd7jVRJtbl_4,3945
84
+ netbox_dns/models/__init__.py,sha256=iTTJNgUfPAmU4n41usqDSGPvncd4Wpsb9f43ryVDDOs,209
85
+ netbox_dns/models/nameserver.py,sha256=8wUutl50DhYJusd55rIFSnZFaT_Yqj81KOeYqmrPR9I,3459
86
+ netbox_dns/models/record.py,sha256=Qp2N35JrUsSmmUyrTTKIE0CoYRakrGcte73-PB5ktpc,29467
87
+ netbox_dns/models/record_template.py,sha256=WcwcVnjy97dgMQVSLJaX_2YJXGKS-AlFb_4QYf6vwOg,5210
88
+ netbox_dns/models/registrar.py,sha256=L5tbO8rtOa0VCs_y90nHYLKSRKBnnUhh_6sxZ3Mm2kk,1942
89
+ netbox_dns/models/registration_contact.py,sha256=O7T1clUjuilZnDjvhJKaHZdmNEF4aLg2h8K5p4llWOs,3825
90
+ netbox_dns/models/view.py,sha256=gQvKNr_FmhG2EMz2T8kWbdK4b8CyqI-Qc67-Dgrx2SI,4808
91
+ netbox_dns/models/zone.py,sha256=PyfFc32E1McdJAt5IoEHis5gwmUvr5cWeCGMXpQacnY,32880
92
+ netbox_dns/models/zone_template.py,sha256=QjjOvSZktH_6l64bCZzVudnL1s9qU6_ZVDkhrhW1zqc,4970
92
93
  netbox_dns/signals/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
94
  netbox_dns/signals/ipam_dnssync.py,sha256=1zhlf4cMcJLlFosX7YzyqVYdFFHV4MFwTz5KCdL8xQc,7730
94
95
  netbox_dns/tables/__init__.py,sha256=axENVF9vX9BtDKCNxrapRjye1NnygUg9BS0BBj6a0io,209
@@ -100,15 +101,15 @@ netbox_dns/tables/registrar.py,sha256=XQtJj0c4O4gpCdUp903GSD0tIuARmJw13Nwosw9pTF
100
101
  netbox_dns/tables/registration_contact.py,sha256=n_FKE90j6KNgPKRVq1WXg4vnOzFE238oXsi_NYVAU9M,931
101
102
  netbox_dns/tables/view.py,sha256=gsuWQWAk3RstNIKzBDOHNHR2D3ykX6WigYLMj0VhQFs,1148
102
103
  netbox_dns/tables/zone.py,sha256=_WihxcaUoQ2pgNyufXau8-yDqgIUMU6HAmbK5jxfIFM,1965
103
- netbox_dns/tables/zone_template.py,sha256=l9MC03E0UE_cZoh7YI4DsiccvaUxZZZwf-AAZ7OhgC4,1504
104
- netbox_dns/templates/netbox_dns/nameserver.html,sha256=cQM8p3aHgnmxY2L1951_kDULg2DPl3kpncPQBu6NGAk,1639
105
- netbox_dns/templates/netbox_dns/record.html,sha256=y2QhJ4fL8Hjd2dKTtENktQ9ru0ZdS_l-7T9_IqJQSy4,6572
106
- netbox_dns/templates/netbox_dns/recordtemplate.html,sha256=jQB42mBNlSt-Tq_uQFIyylEPQYqWP9BVD_W5As-M2Qc,3708
104
+ netbox_dns/tables/zone_template.py,sha256=6Cls0YZ1sI1nyQn9Yu2EMW5pTwOAcHsqNxhc6WuqXac,1647
105
+ netbox_dns/templates/netbox_dns/nameserver.html,sha256=MawPiuAmjFrbv0zRi-7xkm8vr-dT1tlEno8EcoQ9peU,1714
106
+ netbox_dns/templates/netbox_dns/record.html,sha256=1KBT4xDooTX9kt1cUoPD2-6QnMizPmbItA0JAAgRzfw,6550
107
+ netbox_dns/templates/netbox_dns/recordtemplate.html,sha256=a29PAUl-KI_I1lxWpVdPp2loJtzgis9DG9erOWrOZM0,3708
107
108
  netbox_dns/templates/netbox_dns/registrar.html,sha256=4kJuj3biiDxQrIMQEQUEmF4iGRE4psr6Fh0CBP1evz8,2308
108
109
  netbox_dns/templates/netbox_dns/registrationcontact.html,sha256=sljVp_MrPSJRc2vJCPFXq9MiWOw4wjbr1kI_YStBntw,3094
109
- netbox_dns/templates/netbox_dns/view.html,sha256=TslfDC0ZzGU59iO_OcaX8jvt6fTjWot-wYRqRGRYvLE,3245
110
- netbox_dns/templates/netbox_dns/zone.html,sha256=BushtUbicj9zK6s50LKiaJhs25J0JY3-QHYCnUu8fKY,6991
111
- netbox_dns/templates/netbox_dns/zonetemplate.html,sha256=z_VJEkf_yNjL9xoVMHG4VHQvuXwBSarA_SoPbjutBgA,3667
110
+ netbox_dns/templates/netbox_dns/view.html,sha256=1MuzOYNQezRrryNjlklgxErjGTFoVnwqcxf4qceuglw,3320
111
+ netbox_dns/templates/netbox_dns/zone.html,sha256=Ci8MbZgd34vJh67F44_f7Tb4VvV4N14-H-Zh6-qDZsM,6894
112
+ netbox_dns/templates/netbox_dns/zonetemplate.html,sha256=iE2Dzl3v9AqjUmPuqA5jhPnO94RWxtJgwX1NAr-wimE,3898
112
113
  netbox_dns/templates/netbox_dns/record/managed.html,sha256=uwpxQTxyfAXkWqThLT-T2ZssKNUhXTDDMnLWJSVuDNU,119
113
114
  netbox_dns/templates/netbox_dns/record/related.html,sha256=R59aPhE4CyIZtTH0ncwDyS6_wAe_Y-oZjuN_j4qk8iA,1158
114
115
  netbox_dns/templates/netbox_dns/view/button.html,sha256=EMOB5x78XpyfN1qi-pY1CKKKLjyHo9rFUa4Uhq6rFMc,322
@@ -125,24 +126,24 @@ netbox_dns/templates/netbox_dns/zone/rfc2317_child_zone.html,sha256=rWlmb3zRQbLY
125
126
  netbox_dns/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
126
127
  netbox_dns/templatetags/netbox_dns.py,sha256=DND1DMPzv636Rak3M6Hor_Vw6pjqUfSTquofIw4dIsA,223
127
128
  netbox_dns/utilities/__init__.py,sha256=cSGf-nGaRWx9b-Xrh3dLMJYoWNsZ6FF-qdmV4F1uOgg,74
128
- netbox_dns/utilities/conversions.py,sha256=NS37SoMqXc13wNWRkKnLfyQbVi6QKD33fu5ovTKRo74,1979
129
+ netbox_dns/utilities/conversions.py,sha256=FXvYCo7WLtvcf63zoQBkb4rr_TxxzXz8imZAfKGpk7E,2596
129
130
  netbox_dns/utilities/dns.py,sha256=QKST49UkCw7n2GyrN3wU5ap6Cw98t1SZxFYJlyG2x70,315
130
131
  netbox_dns/utilities/ipam_dnssync.py,sha256=tFphPVluDUS3-4NsUW1_D1dDksA3AgIozf7JAoTIE_w,9533
131
132
  netbox_dns/validators/__init__.py,sha256=Mr8TvmcJTa8Pubj8TzbFBKfbHhEmGcr5JdQvczEJ39A,72
132
- netbox_dns/validators/dns_name.py,sha256=XC8yv_PeInKtya8FyPnzmazSe7BJcPw_2iK3A5A3ws0,3556
133
+ netbox_dns/validators/dns_name.py,sha256=Sil68Av49jfZPzgFMV_1qEcLnuuAWXmbxfAJPDXUsGg,3766
133
134
  netbox_dns/validators/dns_value.py,sha256=-mc62mth-hlbPUPe_RlCR7vo1KSD6_gQDXiE8rjB-Cc,5206
134
135
  netbox_dns/validators/rfc2317.py,sha256=uKkwxpakiFFKdYA0qy8WSlEnbFwJD4MDw6gGV4F6skg,706
135
136
  netbox_dns/views/__init__.py,sha256=axENVF9vX9BtDKCNxrapRjye1NnygUg9BS0BBj6a0io,209
136
- netbox_dns/views/nameserver.py,sha256=2PaOHtcjaZm0FQMYTmiys-uqQsCBP_RKamW2Jj3rJOY,3896
137
- netbox_dns/views/record.py,sha256=TJsebzLhp3Dnu6SmioJiSN-zRFVSh1RptqgGL5SRUTU,6464
138
- netbox_dns/views/record_template.py,sha256=ZX0tOkFnUsv7h8ImBxDt9aIaRtNjXoUxXopMc73W5-I,3094
139
- netbox_dns/views/registrar.py,sha256=vIdflAmaqvUykvJfBV2Y8nhsvUZrU43nENUBM6WzM2g,2773
140
- netbox_dns/views/registration_contact.py,sha256=u__0w4Nm1_5lnAeFXfTY-cD86facWxIolEfC-SrxXyk,3551
141
- netbox_dns/views/view.py,sha256=a6-wdMyTWoZekiR2VnM3VNSOjX-8L3Qjqqi973UobAA,3391
142
- netbox_dns/views/zone.py,sha256=H7UPN4T_sn_3ijvXi7t8iteJFs6qqEtVzhvchKOOzCM,7133
143
- netbox_dns/views/zone_template.py,sha256=vNXG96D6uZJo4KRdsgsTL3d9JzRtiDJg4_h4_3gjAfk,2667
144
- netbox_plugin_dns-1.2.2.dist-info/LICENSE,sha256=I3tDu11bZfhFm3EkV4zOD5TmWgLjnUNLEFwrdjniZYs,1112
145
- netbox_plugin_dns-1.2.2.dist-info/METADATA,sha256=Ef4CV4H2DEOAvT4lQAGFJ21ik_nfeRwEJmFFKXwwmMs,7605
146
- netbox_plugin_dns-1.2.2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
147
- netbox_plugin_dns-1.2.2.dist-info/top_level.txt,sha256=sA1Rwl1mRKvMC6XHe2ylZ1GF-Q1NGd08XedK9Y4xZc4,11
148
- netbox_plugin_dns-1.2.2.dist-info/RECORD,,
137
+ netbox_dns/views/nameserver.py,sha256=6lHg8fqBjc_SoITzFj1FiRARpPF7nSn9knAZxe9x5Rg,3932
138
+ netbox_dns/views/record.py,sha256=quFf9BIQJIb8uodD8-7HBmOHBH7xe-Vu8UMr3Q5jZNo,6496
139
+ netbox_dns/views/record_template.py,sha256=CbSyckBvyEvcZCeZgK3q0fJsa1_5HbwUflh_iM7JjH0,3134
140
+ netbox_dns/views/registrar.py,sha256=Um_2wnzmP2bqbdMUhBPhny2My0R8fMXScQ9GLiTCrvg,2808
141
+ netbox_dns/views/registration_contact.py,sha256=c9KrNkfFNsb55pL74A5rN1CNx32M82V6mdwBYduNxas,3596
142
+ netbox_dns/views/view.py,sha256=VfrKaLC9D_KNZNmRyFVohRlmMlMbtblAuPgNg0LNyf8,3421
143
+ netbox_dns/views/zone.py,sha256=W66Miyaf4RKW-8z5wMrerrtmHclhht3h-lPqTWFpiOw,7163
144
+ netbox_dns/views/zone_template.py,sha256=IIW1lr6RQmhShtqJu6A6LnHdxdBrkkZQHxIDSTqQeyc,2705
145
+ netbox_plugin_dns-1.2.4.dist-info/LICENSE,sha256=I3tDu11bZfhFm3EkV4zOD5TmWgLjnUNLEFwrdjniZYs,1112
146
+ netbox_plugin_dns-1.2.4.dist-info/METADATA,sha256=JB-PQlr1KnJ1dJV7qQzZIVjTLsZnHJFgJJtFoW2eXys,7636
147
+ netbox_plugin_dns-1.2.4.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
148
+ netbox_plugin_dns-1.2.4.dist-info/top_level.txt,sha256=sA1Rwl1mRKvMC6XHe2ylZ1GF-Q1NGd08XedK9Y4xZc4,11
149
+ netbox_plugin_dns-1.2.4.dist-info/RECORD,,