netbox-plugin-dns 1.2.7b2__py3-none-any.whl → 1.3b1__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 +55 -29
  2. netbox_dns/api/field_serializers.py +25 -0
  3. netbox_dns/api/nested_serializers.py +19 -1
  4. netbox_dns/api/serializers_/dnssec_key_template.py +13 -0
  5. netbox_dns/api/serializers_/dnssec_policy.py +30 -0
  6. netbox_dns/api/serializers_/record.py +2 -0
  7. netbox_dns/api/serializers_/record_template.py +2 -0
  8. netbox_dns/api/serializers_/zone.py +10 -1
  9. netbox_dns/choices/dnssec_key_template.py +4 -4
  10. netbox_dns/choices/dnssec_policy.py +2 -2
  11. netbox_dns/choices/record.py +64 -19
  12. netbox_dns/choices/utilities.py +4 -26
  13. netbox_dns/choices/zone.py +96 -1
  14. netbox_dns/fields/choice_array.py +13 -0
  15. netbox_dns/fields/timeperiod.py +15 -13
  16. netbox_dns/filtersets/dnssec_policy.py +25 -1
  17. netbox_dns/filtersets/zone.py +7 -2
  18. netbox_dns/filtersets/zone_template.py +2 -2
  19. netbox_dns/forms/dnssec_key_template.py +2 -1
  20. netbox_dns/forms/dnssec_policy.py +32 -2
  21. netbox_dns/forms/nameserver.py +2 -0
  22. netbox_dns/forms/record_template.py +1 -0
  23. netbox_dns/forms/zone.py +78 -15
  24. netbox_dns/forms/zone_template.py +9 -0
  25. netbox_dns/graphql/enums.py +41 -0
  26. netbox_dns/graphql/filter_lookups.py +13 -0
  27. netbox_dns/graphql/filters/__init__.py +12 -0
  28. netbox_dns/graphql/filters/dnssec_key_template.py +63 -0
  29. netbox_dns/graphql/filters/dnssec_policy.py +123 -0
  30. netbox_dns/graphql/filters/nameserver.py +32 -0
  31. netbox_dns/graphql/filters/record.py +89 -0
  32. netbox_dns/graphql/filters/record_template.py +55 -0
  33. netbox_dns/graphql/filters/registrar.py +30 -0
  34. netbox_dns/graphql/filters/registration_contact.py +27 -0
  35. netbox_dns/graphql/filters/view.py +28 -0
  36. netbox_dns/graphql/filters/zone.py +146 -0
  37. netbox_dns/graphql/filters/zone_template.py +97 -0
  38. netbox_dns/locale/de/LC_MESSAGES/django.mo +0 -0
  39. netbox_dns/locale/fr/LC_MESSAGES/django.mo +0 -0
  40. netbox_dns/migrations/0018_zone_domain_status_zone_expiration_date.py +23 -0
  41. netbox_dns/models/dnssec_key_template.py +0 -5
  42. netbox_dns/models/dnssec_policy.py +5 -8
  43. netbox_dns/models/nameserver.py +0 -5
  44. netbox_dns/models/record.py +4 -6
  45. netbox_dns/models/record_template.py +0 -5
  46. netbox_dns/models/registrar.py +0 -5
  47. netbox_dns/models/registration_contact.py +0 -5
  48. netbox_dns/models/view.py +0 -5
  49. netbox_dns/models/zone.py +44 -7
  50. netbox_dns/models/zone_template.py +1 -6
  51. netbox_dns/tables/zone.py +6 -1
  52. netbox_dns/template_content.py +2 -1
  53. netbox_dns/templates/netbox_dns/zone/registration.html +19 -0
  54. netbox_dns/urls.py +7 -0
  55. netbox_dns/utilities/conversions.py +13 -0
  56. netbox_dns/validators/dns_value.py +3 -0
  57. netbox_dns/validators/dnssec.py +10 -8
  58. netbox_dns/views/dnssec_key_template.py +0 -9
  59. netbox_dns/views/dnssec_policy.py +3 -10
  60. netbox_dns/views/nameserver.py +0 -9
  61. netbox_dns/views/record.py +0 -9
  62. netbox_dns/views/record_template.py +0 -3
  63. netbox_dns/views/registrar.py +0 -3
  64. netbox_dns/views/registration_contact.py +0 -3
  65. netbox_dns/views/view.py +0 -9
  66. netbox_dns/views/zone.py +11 -11
  67. netbox_dns/views/zone_template.py +0 -4
  68. {netbox_plugin_dns-1.2.7b2.dist-info → netbox_plugin_dns-1.3b1.dist-info}/METADATA +5 -3
  69. {netbox_plugin_dns-1.2.7b2.dist-info → netbox_plugin_dns-1.3b1.dist-info}/RECORD +72 -58
  70. {netbox_plugin_dns-1.2.7b2.dist-info → netbox_plugin_dns-1.3b1.dist-info}/WHEEL +1 -1
  71. netbox_dns/graphql/filters.py +0 -88
  72. {netbox_plugin_dns-1.2.7b2.dist-info → netbox_plugin_dns-1.3b1.dist-info/licenses}/LICENSE +0 -0
  73. {netbox_plugin_dns-1.2.7b2.dist-info → netbox_plugin_dns-1.3b1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,97 @@
1
+ from typing import Annotated, TYPE_CHECKING
2
+
3
+ import strawberry
4
+ import strawberry_django
5
+ from strawberry.scalars import ID
6
+ from strawberry_django import FilterLookup
7
+
8
+ from netbox.graphql.filter_mixins import NetBoxModelFilterMixin
9
+ from tenancy.graphql.filter_mixins import ContactFilterMixin, TenancyFilterMixin
10
+
11
+ if TYPE_CHECKING:
12
+ from .enums import NetBoxDNSZoneStatusEnum
13
+ from .nameserver import NetBoxDNSNameServerFilter
14
+ from .record_template import NetBoxDNSRecordTemplateFilter
15
+ from .registrar import NetBoxDNSRegistrarFilter
16
+ from .registration_contact import NetBoxDNSRegistrationContactFilter
17
+
18
+ from netbox_dns.models import ZoneTemplate
19
+
20
+
21
+ __all__ = ("NetBoxDNSZoneTemplateFilter",)
22
+
23
+
24
+ @strawberry_django.filter(ZoneTemplate, lookups=True)
25
+ class NetBoxDNSZoneTemplateFilter(
26
+ ContactFilterMixin, TenancyFilterMixin, NetBoxModelFilterMixin
27
+ ):
28
+ name: FilterLookup[str] | None = strawberry_django.filter_field()
29
+ description: FilterLookup[str] | None = strawberry_django.filter_field()
30
+ nameservers: (
31
+ Annotated[
32
+ "NetBoxDNSNameServerFilter", strawberry.lazy("netbox_dns.graphql.filters")
33
+ ]
34
+ | None
35
+ ) = strawberry_django.filter_field()
36
+ status: (
37
+ Annotated[
38
+ "NetBoxDNSZoneStatusEnum", strawberry.lazy("netbox_dns.graphql.enums")
39
+ ]
40
+ | None
41
+ ) = strawberry_django.filter_field()
42
+
43
+ soa_mname: (
44
+ Annotated[
45
+ "NetBoxDNSNameServerFilter", strawberry.lazy("netbox_dns.graphql.filters")
46
+ ]
47
+ | None
48
+ ) = strawberry_django.filter_field()
49
+ soa_mname_id: ID | None = strawberry_django.filter_field()
50
+ soa_rname: FilterLookup[str] | None = strawberry_django.filter_field()
51
+
52
+ registrar: (
53
+ Annotated[
54
+ "NetBoxDNSRegistrarFilter", strawberry.lazy("netbox_dns.graphql.filters")
55
+ ]
56
+ | None
57
+ )
58
+ registrar_id: ID | None = strawberry_django.filter_field()
59
+ registrant: (
60
+ Annotated[
61
+ "NetBoxDNSRegistrationContactFilter",
62
+ strawberry.lazy("netbox_dns.graphql.filters"),
63
+ ]
64
+ | None
65
+ )
66
+ registrant_id: ID | None = strawberry_django.filter_field()
67
+ admin_c: (
68
+ Annotated[
69
+ "NetBoxDNSRegistrationContactFilter",
70
+ strawberry.lazy("netbox_dns.graphql.filters"),
71
+ ]
72
+ | None
73
+ )
74
+ admin_c_id: ID | None = strawberry_django.filter_field()
75
+ tech_c: (
76
+ Annotated[
77
+ "NetBoxDNSRegistrationContactFilter",
78
+ strawberry.lazy("netbox_dns.graphql.filters"),
79
+ ]
80
+ | None
81
+ )
82
+ tech_c_id: ID | None = strawberry_django.filter_field()
83
+ billing_c: (
84
+ Annotated[
85
+ "NetBoxDNSRegistrationContactFilter",
86
+ strawberry.lazy("netbox_dns.graphql.filters"),
87
+ ]
88
+ | None
89
+ )
90
+ billing_c_id: ID | None = strawberry_django.filter_field()
91
+ record_templates: (
92
+ Annotated[
93
+ "NetBoxDNSRecordTemplateFilter",
94
+ strawberry.lazy("netbox_dns.graphql.filters"),
95
+ ]
96
+ | None
97
+ ) = strawberry_django.filter_field()
Binary file
Binary file
@@ -0,0 +1,23 @@
1
+ # Generated by Django 5.1.7 on 2025-04-03 13:43
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("netbox_dns", "0017_dnssec_policy_zone_zone_template"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name="zone",
15
+ name="domain_status",
16
+ field=models.CharField(blank=True, max_length=50, null=True),
17
+ ),
18
+ migrations.AddField(
19
+ model_name="zone",
20
+ name="expiration_date",
21
+ field=models.DateField(blank=True, null=True),
22
+ ),
23
+ ]
@@ -1,5 +1,4 @@
1
1
  from django.db import models
2
- from django.urls import reverse
3
2
  from django.utils.translation import gettext_lazy as _
4
3
 
5
4
  from netbox.models import NetBoxModel
@@ -87,10 +86,6 @@ class DNSSECKeyTemplate(ContactsMixin, NetBoxModel):
87
86
  def __str__(self):
88
87
  return f"{str(self.name)} [{self.type}]"
89
88
 
90
- # TODO: Remove in version 1.3.0 (NetBox #18555)
91
- def get_absolute_url(self):
92
- return reverse("plugins:netbox_dns:dnsseckeytemplate", kwargs={"pk": self.pk})
93
-
94
89
  def get_type_color(self):
95
90
  return DNSSECKeyTemplateTypeChoices.colors.get(self.type)
96
91
 
@@ -1,5 +1,4 @@
1
1
  from django.db import models
2
- from django.urls import reverse
3
2
  from django.utils.translation import gettext_lazy as _
4
3
 
5
4
  from netbox.models import NetBoxModel
@@ -168,10 +167,6 @@ class DNSSECPolicy(ContactsMixin, NetBoxModel):
168
167
  def get_status_color(self):
169
168
  return DNSSECPolicyStatusChoices.colors.get(self.status)
170
169
 
171
- # TODO: Remove in version 1.3.0 (NetBox #18555)
172
- def get_absolute_url(self):
173
- return reverse("plugins:netbox_dns:dnssecpolicy", kwargs={"pk": self.pk})
174
-
175
170
  @property
176
171
  def purge_keys_value(self):
177
172
  return self.purge_keys if self.purge_keys is not None else 776000
@@ -181,16 +176,18 @@ class DNSSECPolicy(ContactsMixin, NetBoxModel):
181
176
  return self.publish_safety if self.publish_safety is not None else 3600
182
177
 
183
178
  def get_effective_value(self, attribute):
184
- default_value = get_plugin_config("netbox_dns", f"dnssec_{attribute}", None)
185
-
186
179
  if not hasattr(self, attribute):
187
180
  raise AttributeError(f"DNSSECPolicy does not have attribute {attribute}")
188
181
 
189
182
  if (value := getattr(self, attribute)) is None:
190
- return default_value
183
+ return self.get_fallback_setting(attribute)
191
184
 
192
185
  return value
193
186
 
187
+ @classmethod
188
+ def get_fallback_setting(cls, attribute):
189
+ return get_plugin_config("netbox_dns", f"dnssec_{attribute}")
190
+
194
191
 
195
192
  @register_search
196
193
  class DNSSECPolicyIndex(SearchIndex):
@@ -4,7 +4,6 @@ from django.core.exceptions import ValidationError
4
4
  from django.db import models, transaction
5
5
  from django.db.models import Q, UniqueConstraint
6
6
  from django.db.models.functions import Lower
7
- from django.urls import reverse
8
7
  from django.utils.translation import gettext_lazy as _
9
8
 
10
9
  from netbox.models import NetBoxModel
@@ -80,10 +79,6 @@ class NameServer(ObjectModificationMixin, ContactsMixin, NetBoxModel):
80
79
  def display_name(self):
81
80
  return name_to_unicode(self.name)
82
81
 
83
- # TODO: Remove in version 1.3.0 (NetBox #18555)
84
- def get_absolute_url(self):
85
- return reverse("plugins:netbox_dns:nameserver", kwargs={"pk": self.pk})
86
-
87
82
  def clean_fields(self, exclude=None):
88
83
  if get_plugin_config("netbox_dns", "convert_names_to_lowercase", False):
89
84
  self.name = self.name.lower()
@@ -8,7 +8,6 @@ from dns import rdata
8
8
  from django.core.exceptions import ValidationError
9
9
  from django.db import transaction, models
10
10
  from django.db.models import Q, ExpressionWrapper, BooleanField, Min
11
- from django.urls import reverse
12
11
  from django.conf import settings
13
12
  from django.utils.translation import gettext_lazy as _
14
13
 
@@ -167,7 +166,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
167
166
  default=False,
168
167
  )
169
168
  ptr_record = models.OneToOneField(
170
- verbose_name="PTR Record",
169
+ verbose_name=_("PTR Record"),
171
170
  to="self",
172
171
  on_delete=models.SET_NULL,
173
172
  related_name="address_record",
@@ -263,10 +262,6 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
263
262
  def get_status_color(self):
264
263
  return RecordStatusChoices.colors.get(self.status)
265
264
 
266
- # TODO: Remove in version 1.3.0 (NetBox #18555)
267
- def get_absolute_url(self):
268
- return reverse("plugins:netbox_dns:record", kwargs={"pk": self.pk})
269
-
270
265
  @property
271
266
  def value_fqdn(self):
272
267
  if self.type not in (RecordTypeChoices.CNAME, RecordTypeChoices.NS):
@@ -649,6 +644,9 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
649
644
 
650
645
  @property
651
646
  def absolute_value(self):
647
+ if self.type in RecordTypeChoices.CUSTOM_TYPES:
648
+ return self.value
649
+
652
650
  zone = dns_name.from_text(self.zone.name)
653
651
  rr = rdata.from_text(RecordClassChoices.IN, self.type, self.value)
654
652
 
@@ -3,7 +3,6 @@ from dns import name as dns_name
3
3
 
4
4
  from django.core.exceptions import ValidationError
5
5
  from django.db import models
6
- from django.urls import reverse
7
6
  from django.utils.translation import gettext_lazy as _
8
7
 
9
8
  from netbox.models import NetBoxModel
@@ -104,10 +103,6 @@ class RecordTemplate(NetBoxModel):
104
103
  def get_status_color(self):
105
104
  return RecordStatusChoices.colors.get(self.status)
106
105
 
107
- # TODO: Remove in version 1.3.0 (NetBox #18555)
108
- def get_absolute_url(self):
109
- return reverse("plugins:netbox_dns:recordtemplate", kwargs={"pk": self.pk})
110
-
111
106
  def validate_name(self):
112
107
  try:
113
108
  name = dns_name.from_text(self.record_name, origin=None)
@@ -1,5 +1,4 @@
1
1
  from django.db import models
2
- from django.urls import reverse
3
2
  from django.utils.translation import gettext_lazy as _
4
3
 
5
4
  from netbox.models import NetBoxModel
@@ -57,10 +56,6 @@ class Registrar(NetBoxModel):
57
56
  blank=True,
58
57
  )
59
58
 
60
- # TODO: Remove in version 1.3.0 (NetBox #18555)
61
- def get_absolute_url(self):
62
- return reverse("plugins:netbox_dns:registrar", kwargs={"pk": self.pk})
63
-
64
59
  def __str__(self):
65
60
  return str(self.name)
66
61
 
@@ -1,5 +1,4 @@
1
1
  from django.db import models
2
- from django.urls import reverse
3
2
  from django.utils.translation import gettext_lazy as _
4
3
 
5
4
  from netbox.models import NetBoxModel
@@ -118,10 +117,6 @@ class RegistrationContact(NetBoxModel):
118
117
  "tags",
119
118
  )
120
119
 
121
- # TODO: Remove in version 1.3.0 (NetBox #18555)
122
- def get_absolute_url(self):
123
- return reverse("plugins:netbox_dns:registrationcontact", kwargs={"pk": self.pk})
124
-
125
120
  def __str__(self):
126
121
  if self.name is not None:
127
122
  return f"{self.contact_id} ({self.name})"
netbox_dns/models/view.py CHANGED
@@ -1,5 +1,4 @@
1
1
  from django.db import models
2
- from django.urls import reverse
3
2
  from django.core.exceptions import ValidationError
4
3
  from django.utils.translation import gettext_lazy as _
5
4
 
@@ -71,10 +70,6 @@ class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
71
70
  def get_default_view(cls):
72
71
  return cls.objects.get(default_view=True)
73
72
 
74
- # TODO: Remove in version 1.3.0 (NetBox #18555)
75
- def get_absolute_url(self):
76
- return reverse("plugins:netbox_dns:view", kwargs={"pk": self.pk})
77
-
78
73
  def __str__(self):
79
74
  return str(self.name)
80
75
 
netbox_dns/models/zone.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import re
2
2
  from math import ceil
3
- from datetime import datetime
3
+ from datetime import datetime, date
4
4
 
5
5
  from dns import name as dns_name
6
6
  from dns.exception import DNSException
@@ -14,7 +14,6 @@ from django.db import models, transaction
14
14
  from django.db.models import Q, Max, ExpressionWrapper, BooleanField, UniqueConstraint
15
15
  from django.db.models.functions import Length, Lower
16
16
  from django.db.models.signals import m2m_changed
17
- from django.urls import reverse
18
17
  from django.dispatch import receiver
19
18
  from django.conf import settings
20
19
  from django.utils.translation import gettext_lazy as _
@@ -26,7 +25,12 @@ from netbox.plugins.utils import get_plugin_config
26
25
  from utilities.querysets import RestrictedQuerySet
27
26
  from ipam.models import IPAddress
28
27
 
29
- from netbox_dns.choices import RecordClassChoices, RecordTypeChoices, ZoneStatusChoices
28
+ from netbox_dns.choices import (
29
+ RecordClassChoices,
30
+ RecordTypeChoices,
31
+ ZoneStatusChoices,
32
+ ZoneEPPStatusChoices,
33
+ )
30
34
  from netbox_dns.fields import NetworkField, RFC2317NetworkField
31
35
  from netbox_dns.utilities import (
32
36
  update_dns_records,
@@ -202,6 +206,18 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
202
206
  blank=True,
203
207
  null=True,
204
208
  )
209
+ expiration_date = models.DateField(
210
+ verbose_name=_("Expiration Date"),
211
+ blank=True,
212
+ null=True,
213
+ )
214
+ domain_status = models.CharField(
215
+ verbose_name=_("Domain Status"),
216
+ max_length=50,
217
+ choices=ZoneEPPStatusChoices,
218
+ blank=True,
219
+ null=True,
220
+ )
205
221
  registrant = models.ForeignKey(
206
222
  verbose_name=_("Registrant"),
207
223
  to="RegistrationContact",
@@ -211,7 +227,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
211
227
  null=True,
212
228
  )
213
229
  admin_c = models.ForeignKey(
214
- verbose_name="Administrative Contact",
230
+ verbose_name=_("Administrative Contact"),
215
231
  to="RegistrationContact",
216
232
  on_delete=models.SET_NULL,
217
233
  related_name="admin_c_zones",
@@ -370,9 +386,8 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
370
386
  def get_status_color(self):
371
387
  return ZoneStatusChoices.colors.get(self.status)
372
388
 
373
- # TODO: Remove in version 1.3.0 (NetBox #18555)
374
- def get_absolute_url(self):
375
- return reverse("plugins:netbox_dns:zone", kwargs={"pk": self.pk})
389
+ def get_domain_status_color(self):
390
+ return ZoneEPPStatusChoices.colors.get(self.domain_status)
376
391
 
377
392
  def get_status_class(self):
378
393
  return self.CSS_CLASSES.get(self.status)
@@ -410,6 +425,8 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
410
425
  self.admin_c,
411
426
  self.tech_c,
412
427
  self.billing_c,
428
+ self.expiration_date,
429
+ self.domain_status,
413
430
  )
414
431
  )
415
432
 
@@ -579,6 +596,26 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
579
596
 
580
597
  return ns_warnings, ns_errors
581
598
 
599
+ def check_expiration(self):
600
+ if self.expiration_date is None:
601
+ return None, None
602
+
603
+ expiration_warning = None
604
+ expiration_error = None
605
+
606
+ expiration_warning_days = get_plugin_config(
607
+ "netbox_dns", "zone_expiration_warning_days"
608
+ )
609
+
610
+ if self.expiration_date < date.today():
611
+ expiration_error = _("The registration for this domain has expired.")
612
+ elif (self.expiration_date - date.today()).days < expiration_warning_days:
613
+ expiration_warning = _(
614
+ f"The registration for his domain will expire less than {expiration_warning_days} days."
615
+ )
616
+
617
+ return expiration_warning, expiration_error
618
+
582
619
  def check_soa_serial_increment(self, old_serial, new_serial):
583
620
  MAX_SOA_SERIAL_INCREMENT = 2**31 - 1
584
621
  SOA_SERIAL_WRAP = 2**32
@@ -2,7 +2,6 @@ from dns import name as dns_name
2
2
  from dns.exception import DNSException
3
3
 
4
4
  from django.db import models
5
- from django.urls import reverse
6
5
  from django.utils.translation import gettext_lazy as _
7
6
  from django.core.exceptions import ValidationError
8
7
 
@@ -139,17 +138,13 @@ class ZoneTemplate(NetBoxModel):
139
138
 
140
139
  class Meta:
141
140
  verbose_name = _("Zone Template")
142
- verbose_name_plural = "Zone Templates"
141
+ verbose_name_plural = _("Zone Templates")
143
142
 
144
143
  ordering = ("name",)
145
144
 
146
145
  def __str__(self):
147
146
  return str(self.name)
148
147
 
149
- # TODO: Remove in version 1.3.0 (NetBox #18555)
150
- def get_absolute_url(self):
151
- return reverse("plugins:netbox_dns:zonetemplate", kwargs={"pk": self.pk})
152
-
153
148
  def apply_to_zone_data(self, data):
154
149
  fields_changed = False
155
150
  for field in self.template_fields:
netbox_dns/tables/zone.py CHANGED
@@ -38,7 +38,7 @@ class ZoneTable(TenancyColumnsMixin, NetBoxTable):
38
38
  url_name="plugins:netbox_dns:zone_list",
39
39
  )
40
40
  default_ttl = tables.Column(
41
- verbose_name="Default TTL",
41
+ verbose_name=_("Default TTL"),
42
42
  )
43
43
  dnssec_policy = tables.Column(
44
44
  verbose_name=_("DNSSEC Policy"),
@@ -55,6 +55,9 @@ class ZoneTable(TenancyColumnsMixin, NetBoxTable):
55
55
  verbose_name=_("Registrar"),
56
56
  linkify=True,
57
57
  )
58
+ domain_status = ChoiceFieldColumn(
59
+ verbose_name=_("Domain Status"),
60
+ )
58
61
  registrant = tables.Column(
59
62
  verbose_name=_("Registrant"),
60
63
  linkify=True,
@@ -84,6 +87,8 @@ class ZoneTable(TenancyColumnsMixin, NetBoxTable):
84
87
  "inline_signing",
85
88
  "rfc2317_parent_managed",
86
89
  "registry_domain_id",
90
+ "expiration_date",
91
+ "domain_status",
87
92
  )
88
93
  default_columns = (
89
94
  "name",
@@ -2,6 +2,7 @@ import django_tables2 as tables
2
2
 
3
3
  from django.conf import settings
4
4
  from django.urls import reverse
5
+ from django.utils.translation import gettext_lazy as _
5
6
 
6
7
  from netbox.plugins.utils import get_plugin_config
7
8
  from netbox.plugins import PluginTemplateExtension
@@ -119,7 +120,7 @@ class IPRelatedDNSRecords(PluginTemplateExtension):
119
120
 
120
121
 
121
122
  address_records = tables.ManyToManyColumn(
122
- verbose_name="DNS Address Records",
123
+ verbose_name=_("DNS Address Records"),
123
124
  accessor="netbox_dns_records",
124
125
  linkify_item=True,
125
126
  transform=lambda obj: (
@@ -14,6 +14,25 @@
14
14
  <th scope="row">{% trans "Registry Domain ID" %}</th>
15
15
  <td>{{ object.registry_domain_id|placeholder }}</td>
16
16
  </tr>
17
+ <tr>
18
+ <th scope="row">{% trans "Expiration Date" %}</th>
19
+ <td>{{ object.expiration_date|placeholder }}</td>
20
+ </tr>
21
+ {% if expiration_warning %}
22
+ <tr>
23
+ <th class="text-warning" scope="row">{% trans "Warning" %}</th>
24
+ <td class="text-warning">{{ expiration_warning }}</td>
25
+ </tr>
26
+ {% elif expiration_error %}
27
+ <tr>
28
+ <th class="text-danger" scope="row">{% trans "Error" %}</th>
29
+ <td class="text-danger">{{ expiration_error }}</td>
30
+ </tr>
31
+ {% endif %}
32
+ <tr>
33
+ <th scope="row">{% trans "Domain Status" %}</th>
34
+ <td>{% badge object.get_domain_status_display bg_color=object.get_domain_status_color %}</td>
35
+ </tr>
17
36
  <tr>
18
37
  <th scope="row">{% trans "Registrant" %}</th>
19
38
  <td>{{ object.registrant|linkify|placeholder }}</td>
netbox_dns/urls.py CHANGED
@@ -2,6 +2,13 @@ from django.urls import include, path
2
2
 
3
3
  from utilities.urls import get_model_urls
4
4
 
5
+ # +
6
+ # Import views so the register_model_view is run. This is required for the
7
+ # URLs to be set up properly with get_model_urls().
8
+ # -
9
+ from .views import * # noqa: F401
10
+
11
+
5
12
  app_name = "netbox_dns"
6
13
 
7
14
  urlpatterns = (
@@ -4,6 +4,8 @@ from dns import name as dns_name
4
4
  from dns.exception import DNSException
5
5
  from netaddr import IPNetwork, AddrFormatError
6
6
 
7
+ from django.utils.dateparse import parse_duration
8
+
7
9
  from netbox.plugins.utils import get_plugin_config
8
10
 
9
11
 
@@ -15,6 +17,7 @@ __all__ = (
15
17
  "normalize_name",
16
18
  "network_to_reverse",
17
19
  "regex_from_list",
20
+ "iso8601_to_int",
18
21
  )
19
22
 
20
23
 
@@ -111,3 +114,13 @@ def network_to_reverse(network):
111
114
 
112
115
  def regex_from_list(names):
113
116
  return f"^({'|'.join(re.escape(name) for name in names)})$"
117
+
118
+
119
+ def iso8601_to_int(value):
120
+ try:
121
+ return int(value)
122
+ except ValueError:
123
+ duration = parse_duration(value)
124
+ if duration is None:
125
+ raise TypeError
126
+ return int(duration.total_seconds())
@@ -52,6 +52,9 @@ def validate_record_value(record):
52
52
  for part in textwrap.wrap(raw_value, MAX_TXT_LENGTH, drop_whitespace=False)
53
53
  )
54
54
 
55
+ if record.type in (RecordTypeChoices.CUSTOM_TYPES):
56
+ return
57
+
55
58
  if record.type in (RecordTypeChoices.TXT, RecordTypeChoices.SPF):
56
59
  if not (record.value.isascii() and record.value.isprintable()):
57
60
  raise ValidationError(
@@ -97,8 +97,8 @@ def validate_key_template_lifetime(key_template, policy, raise_exception=True):
97
97
  ):
98
98
  validation_errors.append(
99
99
  _(
100
- "Key Lifetime is less than DNSKEY TTL + Publish Safety + Zone Propagation Delay."
101
- )
100
+ "Key Lifetime {lifetime} is less than DNSKEY TTL + Publish Safety + Zone Propagation Delay."
101
+ ).format(lifetime=key_lifetime)
102
102
  )
103
103
 
104
104
  if (
@@ -109,8 +109,8 @@ def validate_key_template_lifetime(key_template, policy, raise_exception=True):
109
109
  ):
110
110
  validation_errors.append(
111
111
  _(
112
- "Key Lifetime is less than Max Zone TTL + Retire Safety + Zone Propagation Delay."
113
- )
112
+ "Key Lifetime {lifetime} is less than Max Zone TTL + Retire Safety + Zone Propagation Delay."
113
+ ).format(lifetime=key_lifetime)
114
114
  )
115
115
 
116
116
  if key_template.type == DNSSECKeyTemplateTypeChoices.TYPE_ZSK:
@@ -123,8 +123,10 @@ def validate_key_template_lifetime(key_template, policy, raise_exception=True):
123
123
  and key_lifetime < signatures_validity - signatures_refresh
124
124
  ):
125
125
  validation_errors.append(
126
- _("Key Lifetime is less than Signatures Validity - Signatures Refresh.")
127
- )
126
+ _(
127
+ "Key Lifetime {lifetime} is less than Signatures Validity - Signatures Refresh."
128
+ )
129
+ ).format(lifetime=key_lifetime)
128
130
  else:
129
131
  parent_ds_ttl = policy.get_effective_value("parent_ds_ttl")
130
132
  parent_propagation_delay = policy.get_effective_value(
@@ -139,8 +141,8 @@ def validate_key_template_lifetime(key_template, policy, raise_exception=True):
139
141
  ):
140
142
  validation_errors.append(
141
143
  _(
142
- "Key Lifetime is less than Parent DS TTL + Retire Safety + Parent Propagation Delay."
143
- )
144
+ "Key Lifetime {lifetime} is less than Parent DS TTL + Retire Safety + Parent Propagation Delay."
145
+ ).format(lifetime=key_lifetime)
144
146
  )
145
147
 
146
148
  return validation_errors
@@ -1,6 +1,5 @@
1
1
  from netbox.views import generic
2
2
  from utilities.views import register_model_view
3
- from tenancy.views import ObjectContactsView
4
3
 
5
4
  from netbox_dns.filtersets import DNSSECKeyTemplateFilterSet
6
5
  from netbox_dns.forms import (
@@ -50,13 +49,11 @@ class DNSSECKeyTemplateView(generic.ObjectView):
50
49
  class DNSSECKeyTemplateEditView(generic.ObjectEditView):
51
50
  queryset = DNSSECKeyTemplate.objects.all()
52
51
  form = DNSSECKeyTemplateForm
53
- default_return_url = "plugins:netbox_dns:dnsseckeytemplate_list"
54
52
 
55
53
 
56
54
  @register_model_view(DNSSECKeyTemplate, "delete")
57
55
  class DNSSECKeyTemplateDeleteView(generic.ObjectDeleteView):
58
56
  queryset = DNSSECKeyTemplate.objects.all()
59
- default_return_url = "plugins:netbox_dns:dnsseckeytemplate_list"
60
57
 
61
58
 
62
59
  @register_model_view(DNSSECKeyTemplate, "bulk_import", detail=False)
@@ -64,7 +61,6 @@ class DNSSECKeyTemplateBulkImportView(generic.BulkImportView):
64
61
  queryset = DNSSECKeyTemplate.objects.all()
65
62
  model_form = DNSSECKeyTemplateImportForm
66
63
  table = DNSSECKeyTemplateTable
67
- default_return_url = "plugins:netbox_dns:dnsseckeytemplate_list"
68
64
 
69
65
 
70
66
  @register_model_view(DNSSECKeyTemplate, "bulk_edit", path="edit", detail=False)
@@ -80,8 +76,3 @@ class DNSSECKeyTemplateBulkDeleteView(generic.BulkDeleteView):
80
76
  queryset = DNSSECKeyTemplate.objects.all()
81
77
  filterset = DNSSECKeyTemplateFilterSet
82
78
  table = DNSSECKeyTemplateTable
83
-
84
-
85
- @register_model_view(DNSSECKeyTemplate, "contacts")
86
- class DNSSECKeyTemplateContactsView(ObjectContactsView):
87
- queryset = DNSSECKeyTemplate.objects.all()