netbox-plugin-dns 1.3b1__py3-none-any.whl → 1.3.1__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 (73) hide show
  1. netbox_dns/__init__.py +3 -2
  2. netbox_dns/api/nested_serializers.py +54 -49
  3. netbox_dns/api/serializers_/dnssec_key_template.py +22 -13
  4. netbox_dns/api/serializers_/dnssec_policy.py +89 -40
  5. netbox_dns/api/serializers_/nameserver.py +25 -13
  6. netbox_dns/api/serializers_/record.py +46 -38
  7. netbox_dns/api/serializers_/record_template.py +21 -12
  8. netbox_dns/api/serializers_/registrar.py +14 -5
  9. netbox_dns/api/serializers_/registration_contact.py +13 -5
  10. netbox_dns/api/serializers_/view.py +28 -19
  11. netbox_dns/api/serializers_/zone.py +87 -62
  12. netbox_dns/api/serializers_/zone_template.py +40 -34
  13. netbox_dns/choices/dnssec_key_template.py +4 -0
  14. netbox_dns/choices/record.py +4 -2
  15. netbox_dns/filtersets/dnssec_key_template.py +10 -4
  16. netbox_dns/filtersets/dnssec_policy.py +2 -0
  17. netbox_dns/filtersets/nameserver.py +9 -4
  18. netbox_dns/filtersets/record.py +16 -15
  19. netbox_dns/filtersets/record_template.py +13 -12
  20. netbox_dns/filtersets/registrar.py +1 -0
  21. netbox_dns/filtersets/registration_contact.py +1 -0
  22. netbox_dns/filtersets/view.py +10 -4
  23. netbox_dns/filtersets/zone.py +39 -20
  24. netbox_dns/filtersets/zone_template.py +10 -9
  25. netbox_dns/forms/dnssec_key_template.py +97 -46
  26. netbox_dns/forms/dnssec_policy.py +124 -105
  27. netbox_dns/forms/nameserver.py +71 -36
  28. netbox_dns/forms/record.py +96 -78
  29. netbox_dns/forms/record_template.py +87 -59
  30. netbox_dns/forms/registrar.py +55 -39
  31. netbox_dns/forms/registration_contact.py +64 -41
  32. netbox_dns/forms/view.py +98 -51
  33. netbox_dns/forms/zone.py +273 -243
  34. netbox_dns/forms/zone_template.py +151 -101
  35. netbox_dns/graphql/types.py +3 -3
  36. netbox_dns/locale/de/LC_MESSAGES/django.mo +0 -0
  37. netbox_dns/locale/fr/LC_MESSAGES/django.mo +0 -0
  38. netbox_dns/migrations/0019_dnssecpolicy_parental_agents.py +25 -0
  39. netbox_dns/migrations/0020_remove_dnssecpolicy_parental_agents_and_more.py +29 -0
  40. netbox_dns/migrations/0021_alter_record_ptr_record.py +25 -0
  41. netbox_dns/models/dnssec_key_template.py +24 -22
  42. netbox_dns/models/dnssec_policy.py +17 -16
  43. netbox_dns/models/nameserver.py +26 -25
  44. netbox_dns/models/record.py +117 -61
  45. netbox_dns/models/record_template.py +30 -29
  46. netbox_dns/models/registrar.py +13 -12
  47. netbox_dns/models/registration_contact.py +33 -32
  48. netbox_dns/models/view.py +16 -15
  49. netbox_dns/models/zone.py +88 -57
  50. netbox_dns/models/zone_template.py +35 -34
  51. netbox_dns/tables/dnssec_key_template.py +13 -12
  52. netbox_dns/tables/dnssec_policy.py +18 -15
  53. netbox_dns/tables/nameserver.py +10 -8
  54. netbox_dns/tables/record.py +55 -34
  55. netbox_dns/tables/record_template.py +21 -17
  56. netbox_dns/tables/registrar.py +15 -9
  57. netbox_dns/tables/registration_contact.py +15 -9
  58. netbox_dns/tables/view.py +25 -12
  59. netbox_dns/tables/zone.py +23 -21
  60. netbox_dns/tables/zone_template.py +17 -13
  61. netbox_dns/template_content.py +13 -2
  62. netbox_dns/templates/netbox_dns/record.html +13 -11
  63. netbox_dns/templates/netbox_dns/zone.html +10 -0
  64. netbox_dns/validators/dns_name.py +1 -1
  65. netbox_dns/views/dnssec_key_template.py +3 -3
  66. netbox_dns/views/record.py +19 -3
  67. netbox_dns/views/record_template.py +3 -1
  68. netbox_dns/views/zone_template.py +5 -3
  69. {netbox_plugin_dns-1.3b1.dist-info → netbox_plugin_dns-1.3.1.dist-info}/METADATA +3 -2
  70. {netbox_plugin_dns-1.3b1.dist-info → netbox_plugin_dns-1.3.1.dist-info}/RECORD +73 -70
  71. {netbox_plugin_dns-1.3b1.dist-info → netbox_plugin_dns-1.3.1.dist-info}/WHEEL +1 -1
  72. {netbox_plugin_dns-1.3b1.dist-info → netbox_plugin_dns-1.3.1.dist-info}/licenses/LICENSE +0 -0
  73. {netbox_plugin_dns-1.3b1.dist-info → netbox_plugin_dns-1.3.1.dist-info}/top_level.txt +0 -0
@@ -28,7 +28,6 @@ from netbox_dns.choices import (
28
28
  RecordClassChoices,
29
29
  )
30
30
 
31
-
32
31
  __all__ = (
33
32
  "Record",
34
33
  "RecordIndex",
@@ -115,6 +114,52 @@ class RecordManager(models.Manager.from_queryset(RestrictedQuerySet)):
115
114
 
116
115
 
117
116
  class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
117
+ class Meta:
118
+ verbose_name = _("Record")
119
+ verbose_name_plural = _("Records")
120
+
121
+ ordering = (
122
+ "fqdn",
123
+ "zone",
124
+ "name",
125
+ "type",
126
+ "value",
127
+ "status",
128
+ )
129
+
130
+ objects = RecordManager()
131
+ raw_objects = RestrictedQuerySet.as_manager()
132
+
133
+ clone_fields = (
134
+ "zone",
135
+ "type",
136
+ "name",
137
+ "value",
138
+ "status",
139
+ "ttl",
140
+ "disable_ptr",
141
+ "description",
142
+ "tenant",
143
+ )
144
+
145
+ def __init__(self, *args, **kwargs):
146
+ super().__init__(*args, **kwargs)
147
+
148
+ self._cleanup_ptr_record = None
149
+
150
+ def __str__(self):
151
+ try:
152
+ fqdn = dns_name.from_text(
153
+ self.name, origin=dns_name.from_text(self.zone.name)
154
+ ).relativize(dns_name.root)
155
+ name = fqdn.to_unicode()
156
+ except dns_name.IDNAException:
157
+ name = fqdn.to_text()
158
+ except dns_name.LabelTooLong:
159
+ name = f"{self.name[:59]}..."
160
+
161
+ return f"{name} [{self.type}]"
162
+
118
163
  unique_ptr_qs = Q(
119
164
  Q(disable_ptr=False),
120
165
  Q(Q(type=RecordTypeChoices.A) | Q(type=RecordTypeChoices.AAAA)),
@@ -165,11 +210,11 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
165
210
  null=False,
166
211
  default=False,
167
212
  )
168
- ptr_record = models.OneToOneField(
213
+ ptr_record = models.ForeignKey(
169
214
  verbose_name=_("PTR Record"),
170
215
  to="self",
171
216
  on_delete=models.SET_NULL,
172
- related_name="address_record",
217
+ related_name="address_records",
173
218
  null=True,
174
219
  blank=True,
175
220
  )
@@ -214,46 +259,13 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
214
259
  blank=True,
215
260
  )
216
261
 
217
- objects = RecordManager()
218
- raw_objects = RestrictedQuerySet.as_manager()
219
-
220
- clone_fields = (
221
- "zone",
222
- "type",
223
- "name",
224
- "value",
225
- "status",
226
- "ttl",
227
- "disable_ptr",
228
- "description",
229
- "tenant",
230
- )
231
-
232
- class Meta:
233
- verbose_name = _("Record")
234
- verbose_name_plural = _("Records")
235
-
236
- ordering = (
237
- "fqdn",
238
- "zone",
239
- "name",
240
- "type",
241
- "value",
242
- "status",
243
- )
244
-
245
- def __str__(self):
246
- try:
247
- fqdn = dns_name.from_text(
248
- self.name, origin=dns_name.from_text(self.zone.name)
249
- ).relativize(dns_name.root)
250
- name = fqdn.to_unicode()
251
- except dns_name.IDNAException:
252
- name = fqdn.to_text()
253
- except dns_name.LabelTooLong:
254
- name = f"{self.name[:59]}..."
262
+ @property
263
+ def cleanup_ptr_record(self):
264
+ return self._cleanup_ptr_record
255
265
 
256
- return f"{name} [{self.type}]"
266
+ @cleanup_ptr_record.setter
267
+ def cleanup_ptr_record(self, ptr_record):
268
+ self._cleanup_ptr_record = ptr_record
257
269
 
258
270
  @property
259
271
  def display_name(self):
@@ -348,6 +360,21 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
348
360
  def is_delegation_record(self):
349
361
  return self in self.zone.delegation_records
350
362
 
363
+ def refresh_ptr_record(
364
+ self, ptr_record=None, update_rfc2317_cname=True, save_zone_serial=True
365
+ ):
366
+ if ptr_record is None:
367
+ return
368
+
369
+ if not ptr_record.address_records.exists():
370
+ if ptr_record.rfc2317_cname_record is not None:
371
+ ptr_record.remove_from_rfc2317_cname_record()
372
+
373
+ ptr_record.delete(save_zone_serial=save_zone_serial)
374
+
375
+ elif update_rfc2317_cname:
376
+ ptr_record.update_rfc2317_cname_record(save_zone_serial=save_zone_serial)
377
+
351
378
  def update_ptr_record(self, update_rfc2317_cname=True, save_zone_serial=True):
352
379
  ptr_zone = self.ptr_zone
353
380
 
@@ -357,10 +384,8 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
357
384
  or not self.is_active
358
385
  or self.name.startswith("*")
359
386
  ):
360
- if self.ptr_record is not None:
361
- with transaction.atomic():
362
- self.ptr_record.delete()
363
- self.ptr_record = None
387
+ self.cleanup_ptr_record = self.ptr_record
388
+ self.ptr_record = None
364
389
  return
365
390
 
366
391
  if ptr_zone.is_rfc2317_zone:
@@ -373,7 +398,14 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
373
398
  )
374
399
 
375
400
  ptr_value = self.fqdn
376
- ptr_record = self.ptr_record
401
+ if ptr_record := self.ptr_record:
402
+ if (
403
+ ptr_record.zone == ptr_zone
404
+ and ptr_record.name == ptr_name
405
+ and ptr_record.value == ptr_value
406
+ and ptr_record.ttl == self.ttl
407
+ ):
408
+ return
377
409
 
378
410
  if ptr_record is not None:
379
411
  if (
@@ -384,24 +416,36 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
384
416
  save_zone_serial=save_zone_serial
385
417
  )
386
418
 
419
+ try:
420
+ ptr_record = Record.objects.get(
421
+ name=ptr_name,
422
+ zone=ptr_zone,
423
+ type=RecordTypeChoices.PTR,
424
+ value=ptr_value,
425
+ )
426
+ except Record.DoesNotExist:
427
+ pass
428
+
387
429
  with transaction.atomic():
388
430
  if ptr_record is not None:
389
431
  if ptr_record.zone.pk != ptr_zone.pk:
390
432
  if ptr_record.rfc2317_cname_record is not None:
391
433
  ptr_record.rfc2317_cname_record.delete()
392
- ptr_record.delete(save_zone_serial=save_zone_serial)
393
- ptr_record = None
434
+ ptr_record.rfc2317_cname_record = None
435
+
436
+ if ptr_record.address_records.count() == 1:
437
+ ptr_record.zone = ptr_zone
438
+ ptr_record.name = ptr_name
439
+ ptr_record.value = ptr_value
440
+ ptr_record.ttl = self.ttl
441
+ ptr_record.save(
442
+ update_rfc2317_cname=update_rfc2317_cname,
443
+ save_zone_serial=save_zone_serial,
444
+ )
394
445
 
395
446
  else:
396
- if (
397
- ptr_record.name != ptr_name
398
- or ptr_record.value != ptr_value
399
- or ptr_record.ttl != self.ttl
400
- ):
401
- ptr_record.name = ptr_name
402
- ptr_record.value = ptr_value
403
- ptr_record.ttl = self.ttl
404
- ptr_record.save(save_zone_serial=save_zone_serial)
447
+ self.ptr_record = ptr_record
448
+ return
405
449
 
406
450
  if ptr_record is None:
407
451
  ptr_record = Record(
@@ -905,13 +949,19 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
905
949
  save_zone_serial=save_zone_serial,
906
950
  )
907
951
  elif self.ptr_record is not None:
908
- self.ptr_record.delete()
952
+ self.cleanup_ptr_record = self.ptr_record
909
953
  self.ptr_record = None
910
954
 
911
955
  changed_fields = self.changed_fields
912
956
  if changed_fields is None or changed_fields:
913
957
  super().save(*args, **kwargs)
914
958
 
959
+ self.refresh_ptr_record(
960
+ self.cleanup_ptr_record,
961
+ update_rfc2317_cname=update_rfc2317_cname,
962
+ save_zone_serial=save_zone_serial,
963
+ )
964
+
915
965
  if self.type != RecordTypeChoices.SOA and self.zone.soa_serial_auto:
916
966
  self.zone.update_serial(save_zone_serial=save_zone_serial)
917
967
 
@@ -919,11 +969,16 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
919
969
  if self.rfc2317_cname_record:
920
970
  self.remove_from_rfc2317_cname_record(save_zone_serial=save_zone_serial)
921
971
 
922
- if self.ptr_record:
923
- self.ptr_record.delete()
972
+ ptr_record = self.ptr_record
924
973
 
925
974
  super().delete(*args, **kwargs)
926
975
 
976
+ self.refresh_ptr_record(
977
+ ptr_record,
978
+ update_rfc2317_cname=True,
979
+ save_zone_serial=save_zone_serial,
980
+ )
981
+
927
982
  _zone = self.zone
928
983
  if _zone.soa_serial_auto:
929
984
  _zone.update_serial(save_zone_serial=save_zone_serial)
@@ -932,6 +987,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
932
987
  @register_search
933
988
  class RecordIndex(SearchIndex):
934
989
  model = Record
990
+
935
991
  fields = (
936
992
  ("fqdn", 100),
937
993
  ("name", 120),
@@ -22,6 +22,35 @@ __all__ = (
22
22
 
23
23
 
24
24
  class RecordTemplate(NetBoxModel):
25
+ class Meta:
26
+ verbose_name = _("Record Template")
27
+ verbose_name_plural = _("Record Templates")
28
+
29
+ ordering = ("name",)
30
+
31
+ clone_fields = (
32
+ "record_name",
33
+ "description",
34
+ "type",
35
+ "value",
36
+ "status",
37
+ "ttl",
38
+ "disable_ptr",
39
+ "tenant",
40
+ )
41
+
42
+ template_fields = (
43
+ "type",
44
+ "value",
45
+ "status",
46
+ "ttl",
47
+ "disable_ptr",
48
+ "tenant",
49
+ )
50
+
51
+ def __str__(self):
52
+ return str(self.name)
53
+
25
54
  name = models.CharField(
26
55
  verbose_name=_("Template Name"),
27
56
  unique=True,
@@ -71,35 +100,6 @@ class RecordTemplate(NetBoxModel):
71
100
  null=True,
72
101
  )
73
102
 
74
- clone_fields = (
75
- "record_name",
76
- "description",
77
- "type",
78
- "value",
79
- "status",
80
- "ttl",
81
- "disable_ptr",
82
- "tenant",
83
- )
84
-
85
- template_fields = (
86
- "type",
87
- "value",
88
- "status",
89
- "ttl",
90
- "disable_ptr",
91
- "tenant",
92
- )
93
-
94
- class Meta:
95
- verbose_name = _("Record Template")
96
- verbose_name_plural = _("Record Templates")
97
-
98
- ordering = ("name",)
99
-
100
- def __str__(self):
101
- return str(self.name)
102
-
103
103
  def get_status_color(self):
104
104
  return RecordStatusChoices.colors.get(self.status)
105
105
 
@@ -186,6 +186,7 @@ class RecordTemplate(NetBoxModel):
186
186
  @register_search
187
187
  class RecordTemplateIndex(SearchIndex):
188
188
  model = RecordTemplate
189
+
189
190
  fields = (
190
191
  ("name", 100),
191
192
  ("record_name", 120),
@@ -12,6 +12,18 @@ __all__ = (
12
12
 
13
13
 
14
14
  class Registrar(NetBoxModel):
15
+ class Meta:
16
+ verbose_name = _("Registrar")
17
+ verbose_name_plural = _("Registrars")
18
+
19
+ ordering = (
20
+ "name",
21
+ "iana_id",
22
+ )
23
+
24
+ def __str__(self):
25
+ return str(self.name)
26
+
15
27
  # +
16
28
  # Data fields according to https://www.icann.org/resources/pages/rdds-labeling-policy-2017-02-01-en
17
29
  # -
@@ -56,22 +68,11 @@ class Registrar(NetBoxModel):
56
68
  blank=True,
57
69
  )
58
70
 
59
- def __str__(self):
60
- return str(self.name)
61
-
62
- class Meta:
63
- verbose_name = _("Registrar")
64
- verbose_name_plural = _("Registrars")
65
-
66
- ordering = (
67
- "name",
68
- "iana_id",
69
- )
70
-
71
71
 
72
72
  @register_search
73
73
  class RegistrarIndex(SearchIndex):
74
74
  model = Registrar
75
+
75
76
  fields = (
76
77
  ("name", 100),
77
78
  ("iana_id", 100),
@@ -14,6 +14,38 @@ __all__ = (
14
14
 
15
15
 
16
16
  class RegistrationContact(NetBoxModel):
17
+ class Meta:
18
+ verbose_name = _("Registration Contact")
19
+ verbose_name_plural = _("Registration Contacts")
20
+
21
+ ordering = (
22
+ "name",
23
+ "contact_id",
24
+ )
25
+
26
+ clone_fields = (
27
+ "name",
28
+ "description",
29
+ "organization",
30
+ "street",
31
+ "city",
32
+ "state_province",
33
+ "postal_code",
34
+ "country",
35
+ "phone",
36
+ "phone_ext",
37
+ "fax",
38
+ "fax_ext",
39
+ "email",
40
+ "tags",
41
+ )
42
+
43
+ def __str__(self):
44
+ if self.name is not None:
45
+ return f"{self.contact_id} ({self.name})"
46
+
47
+ return self.contact_id
48
+
17
49
  # +
18
50
  # Data fields according to https://www.icann.org/resources/pages/rdds-labeling-policy-2017-02-01-en
19
51
  # -
@@ -100,38 +132,6 @@ class RegistrationContact(NetBoxModel):
100
132
  related_name="netbox_dns_contact_set",
101
133
  )
102
134
 
103
- clone_fields = (
104
- "name",
105
- "description",
106
- "organization",
107
- "street",
108
- "city",
109
- "state_province",
110
- "postal_code",
111
- "country",
112
- "phone",
113
- "phone_ext",
114
- "fax",
115
- "fax_ext",
116
- "email",
117
- "tags",
118
- )
119
-
120
- def __str__(self):
121
- if self.name is not None:
122
- return f"{self.contact_id} ({self.name})"
123
-
124
- return self.contact_id
125
-
126
- class Meta:
127
- verbose_name = _("Registration Contact")
128
- verbose_name_plural = _("Registration Contacts")
129
-
130
- ordering = (
131
- "name",
132
- "contact_id",
133
- )
134
-
135
135
  @property
136
136
  def zones(self):
137
137
  return (
@@ -145,6 +145,7 @@ class RegistrationContact(NetBoxModel):
145
145
  @register_search
146
146
  class RegistrationContactIndex(SearchIndex):
147
147
  model = RegistrationContact
148
+
148
149
  fields = (
149
150
  ("name", 100),
150
151
  ("contact_id", 100),
netbox_dns/models/view.py CHANGED
@@ -25,6 +25,21 @@ __all__ = (
25
25
 
26
26
 
27
27
  class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
28
+ class Meta:
29
+ verbose_name = _("View")
30
+ verbose_name_plural = _("Views")
31
+
32
+ ordering = ("name",)
33
+
34
+ clone_fields = (
35
+ "name",
36
+ "description",
37
+ "tenant",
38
+ )
39
+
40
+ def __str__(self):
41
+ return str(self.name)
42
+
28
43
  name = models.CharField(
29
44
  verbose_name=_("Name"),
30
45
  unique=True,
@@ -60,25 +75,10 @@ class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
60
75
  null=True,
61
76
  )
62
77
 
63
- clone_fields = (
64
- "name",
65
- "description",
66
- "tenant",
67
- )
68
-
69
78
  @classmethod
70
79
  def get_default_view(cls):
71
80
  return cls.objects.get(default_view=True)
72
81
 
73
- def __str__(self):
74
- return str(self.name)
75
-
76
- class Meta:
77
- verbose_name = _("View")
78
- verbose_name_plural = _("Views")
79
-
80
- ordering = ("name",)
81
-
82
82
  def delete(self, *args, **kwargs):
83
83
  if self.default_view:
84
84
  if current_request.get() is not None:
@@ -156,6 +156,7 @@ class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
156
156
  @register_search
157
157
  class ViewIndex(SearchIndex):
158
158
  model = View
159
+
159
160
  fields = (
160
161
  ("name", 100),
161
162
  ("description", 500),