netbox-plugin-dns 1.2.7b2__py3-none-any.whl → 1.2.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of netbox-plugin-dns might be problematic. Click here for more details.
- netbox_dns/__init__.py +56 -29
- netbox_dns/api/field_serializers.py +25 -0
- netbox_dns/api/nested_serializers.py +19 -1
- netbox_dns/api/serializers_/dnssec_key_template.py +13 -0
- netbox_dns/api/serializers_/dnssec_policy.py +31 -0
- netbox_dns/api/serializers_/record.py +2 -0
- netbox_dns/api/serializers_/record_template.py +2 -0
- netbox_dns/api/serializers_/zone.py +10 -1
- netbox_dns/choices/dnssec_key_template.py +4 -4
- netbox_dns/choices/dnssec_policy.py +2 -2
- netbox_dns/choices/record.py +66 -19
- netbox_dns/choices/utilities.py +4 -26
- netbox_dns/choices/zone.py +96 -1
- netbox_dns/fields/choice_array.py +13 -0
- netbox_dns/fields/timeperiod.py +15 -13
- netbox_dns/filtersets/dnssec_policy.py +47 -1
- netbox_dns/filtersets/zone.py +7 -2
- netbox_dns/filtersets/zone_template.py +2 -2
- netbox_dns/forms/dnssec_key_template.py +2 -1
- netbox_dns/forms/dnssec_policy.py +82 -33
- netbox_dns/forms/nameserver.py +2 -0
- netbox_dns/forms/record_template.py +1 -0
- netbox_dns/forms/zone.py +78 -15
- netbox_dns/forms/zone_template.py +9 -0
- netbox_dns/graphql/types.py +1 -0
- netbox_dns/locale/de/LC_MESSAGES/django.mo +0 -0
- netbox_dns/locale/fr/LC_MESSAGES/django.mo +0 -0
- netbox_dns/migrations/0018_zone_domain_status_zone_expiration_date.py +23 -0
- netbox_dns/migrations/0019_dnssecpolicy_parental_agents.py +25 -0
- netbox_dns/models/dnssec_policy.py +14 -3
- netbox_dns/models/record.py +4 -1
- netbox_dns/models/zone.py +65 -4
- netbox_dns/models/zone_template.py +1 -1
- netbox_dns/tables/zone.py +6 -1
- netbox_dns/template_content.py +2 -1
- netbox_dns/templates/netbox_dns/dnssecpolicy.html +10 -0
- netbox_dns/templates/netbox_dns/zone/registration.html +19 -0
- netbox_dns/urls.py +7 -0
- netbox_dns/utilities/conversions.py +13 -0
- netbox_dns/validators/dns_value.py +3 -0
- netbox_dns/validators/dnssec.py +10 -8
- netbox_dns/views/dnssec_policy.py +3 -1
- netbox_dns/views/zone.py +11 -1
- {netbox_plugin_dns-1.2.7b2.dist-info → netbox_plugin_dns-1.2.8.dist-info}/METADATA +4 -3
- {netbox_plugin_dns-1.2.7b2.dist-info → netbox_plugin_dns-1.2.8.dist-info}/RECORD +48 -45
- {netbox_plugin_dns-1.2.7b2.dist-info → netbox_plugin_dns-1.2.8.dist-info}/WHEEL +1 -1
- {netbox_plugin_dns-1.2.7b2.dist-info → netbox_plugin_dns-1.2.8.dist-info/licenses}/LICENSE +0 -0
- {netbox_plugin_dns-1.2.7b2.dist-info → netbox_plugin_dns-1.2.8.dist-info}/top_level.txt +0 -0
netbox_dns/forms/zone.py
CHANGED
|
@@ -23,7 +23,7 @@ from utilities.forms.fields import (
|
|
|
23
23
|
DynamicModelChoiceField,
|
|
24
24
|
)
|
|
25
25
|
from utilities.release import load_release_data
|
|
26
|
-
from utilities.forms.widgets import BulkEditNullBooleanSelect
|
|
26
|
+
from utilities.forms.widgets import BulkEditNullBooleanSelect, DatePicker
|
|
27
27
|
from utilities.forms.rendering import FieldSet
|
|
28
28
|
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, add_blank_choice
|
|
29
29
|
from tenancy.models import Tenant, TenantGroup
|
|
@@ -38,7 +38,7 @@ from netbox_dns.models import (
|
|
|
38
38
|
ZoneTemplate,
|
|
39
39
|
DNSSECPolicy,
|
|
40
40
|
)
|
|
41
|
-
from netbox_dns.choices import ZoneStatusChoices
|
|
41
|
+
from netbox_dns.choices import ZoneStatusChoices, ZoneEPPStatusChoices
|
|
42
42
|
from netbox_dns.utilities import name_to_unicode, network_to_reverse
|
|
43
43
|
from netbox_dns.fields import RFC2317NetworkFormField, TimePeriodField
|
|
44
44
|
from netbox_dns.validators import validate_ipv4, validate_prefix, validate_rfc2317
|
|
@@ -59,10 +59,21 @@ class RollbackTransaction(Exception):
|
|
|
59
59
|
|
|
60
60
|
|
|
61
61
|
class ZoneTemplateUpdateMixin:
|
|
62
|
+
def _check_soa_mname(self):
|
|
63
|
+
if (
|
|
64
|
+
self.cleaned_data.get("soa_mname") is None
|
|
65
|
+
and "soa_mname" in self.fields.keys()
|
|
66
|
+
):
|
|
67
|
+
self.add_error(
|
|
68
|
+
"soa_mname",
|
|
69
|
+
_("soa_mname not set and no template or default value defined"),
|
|
70
|
+
)
|
|
71
|
+
|
|
62
72
|
def clean(self, *args, **kwargs):
|
|
63
73
|
super().clean(*args, **kwargs)
|
|
64
74
|
|
|
65
75
|
if (template := self.cleaned_data.get("template")) is None:
|
|
76
|
+
self._check_soa_mname()
|
|
66
77
|
return
|
|
67
78
|
|
|
68
79
|
if not self.cleaned_data.get("nameservers") and template.nameservers.all():
|
|
@@ -77,11 +88,7 @@ class ZoneTemplateUpdateMixin:
|
|
|
77
88
|
) not in (None, ""):
|
|
78
89
|
self.cleaned_data[field] = getattr(template, field)
|
|
79
90
|
|
|
80
|
-
|
|
81
|
-
self.add_error(
|
|
82
|
-
"soa_mname",
|
|
83
|
-
_("soa_mname not set and no template or default value defined"),
|
|
84
|
-
)
|
|
91
|
+
self._check_soa_mname()
|
|
85
92
|
|
|
86
93
|
if self.errors:
|
|
87
94
|
return
|
|
@@ -285,6 +292,8 @@ class ZoneForm(ZoneTemplateUpdateMixin, TenancyForm, NetBoxModelForm):
|
|
|
285
292
|
FieldSet(
|
|
286
293
|
"registrar",
|
|
287
294
|
"registry_domain_id",
|
|
295
|
+
"expiration_date",
|
|
296
|
+
"domain_status",
|
|
288
297
|
"registrant",
|
|
289
298
|
"admin_c",
|
|
290
299
|
"tech_c",
|
|
@@ -379,6 +388,8 @@ class ZoneForm(ZoneTemplateUpdateMixin, TenancyForm, NetBoxModelForm):
|
|
|
379
388
|
"inline_signing",
|
|
380
389
|
"registrar",
|
|
381
390
|
"registry_domain_id",
|
|
391
|
+
"expiration_date",
|
|
392
|
+
"domain_status",
|
|
382
393
|
"registrant",
|
|
383
394
|
"admin_c",
|
|
384
395
|
"tech_c",
|
|
@@ -387,8 +398,8 @@ class ZoneForm(ZoneTemplateUpdateMixin, TenancyForm, NetBoxModelForm):
|
|
|
387
398
|
"tenant",
|
|
388
399
|
"tags",
|
|
389
400
|
)
|
|
390
|
-
|
|
391
|
-
"
|
|
401
|
+
widgets = {
|
|
402
|
+
"expiration_date": DatePicker,
|
|
392
403
|
}
|
|
393
404
|
|
|
394
405
|
|
|
@@ -412,7 +423,7 @@ class ZoneFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|
|
412
423
|
name=_("SOA"),
|
|
413
424
|
),
|
|
414
425
|
FieldSet(
|
|
415
|
-
"
|
|
426
|
+
"dnssec_policy_id",
|
|
416
427
|
"inline_signing",
|
|
417
428
|
name=_("DNSSEC"),
|
|
418
429
|
),
|
|
@@ -425,6 +436,9 @@ class ZoneFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|
|
425
436
|
FieldSet(
|
|
426
437
|
"registrar_id",
|
|
427
438
|
"registry_domain_id",
|
|
439
|
+
"expiration_date_before",
|
|
440
|
+
"expiration_date_after",
|
|
441
|
+
"domain_status",
|
|
428
442
|
"registrant_id",
|
|
429
443
|
"admin_c_id",
|
|
430
444
|
"tech_c_id",
|
|
@@ -451,6 +465,7 @@ class ZoneFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|
|
451
465
|
nameserver_id = DynamicModelMultipleChoiceField(
|
|
452
466
|
queryset=NameServer.objects.all(),
|
|
453
467
|
required=False,
|
|
468
|
+
null_option=_("None"),
|
|
454
469
|
label=_("Nameservers"),
|
|
455
470
|
)
|
|
456
471
|
active = forms.NullBooleanField(
|
|
@@ -488,40 +503,67 @@ class ZoneFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|
|
488
503
|
rfc2317_parent_zone_id = DynamicModelMultipleChoiceField(
|
|
489
504
|
queryset=Zone.objects.all(),
|
|
490
505
|
required=False,
|
|
506
|
+
null_option=_("None"),
|
|
491
507
|
label=_("Parent Zone"),
|
|
492
508
|
)
|
|
493
|
-
registrar_id = DynamicModelMultipleChoiceField(
|
|
494
|
-
queryset=Registrar.objects.all(),
|
|
495
|
-
required=False,
|
|
496
|
-
label=_("Registrar"),
|
|
497
|
-
)
|
|
498
509
|
dnssec_policy_id = DynamicModelMultipleChoiceField(
|
|
499
510
|
queryset=DNSSECPolicy.objects.all(),
|
|
500
511
|
required=False,
|
|
512
|
+
null_option=_("None"),
|
|
501
513
|
label=_("DNSSEC Policy"),
|
|
502
514
|
)
|
|
515
|
+
inline_signing = forms.NullBooleanField(
|
|
516
|
+
required=False,
|
|
517
|
+
widget=forms.Select(choices=BOOLEAN_WITH_BLANK_CHOICES),
|
|
518
|
+
label=_("Use Inline Signing"),
|
|
519
|
+
)
|
|
520
|
+
registrar_id = DynamicModelMultipleChoiceField(
|
|
521
|
+
queryset=Registrar.objects.all(),
|
|
522
|
+
required=False,
|
|
523
|
+
null_option=_("None"),
|
|
524
|
+
label=_("Registrar"),
|
|
525
|
+
)
|
|
503
526
|
registry_domain_id = forms.CharField(
|
|
504
527
|
required=False,
|
|
505
528
|
label=_("Registry Domain ID"),
|
|
506
529
|
)
|
|
530
|
+
expiration_date_after = forms.DateField(
|
|
531
|
+
required=False,
|
|
532
|
+
label=_("Expiration Date after"),
|
|
533
|
+
widget=DatePicker,
|
|
534
|
+
)
|
|
535
|
+
expiration_date_before = forms.DateField(
|
|
536
|
+
required=False,
|
|
537
|
+
label=_("Expiration Date before"),
|
|
538
|
+
widget=DatePicker,
|
|
539
|
+
)
|
|
540
|
+
domain_status = forms.MultipleChoiceField(
|
|
541
|
+
choices=ZoneEPPStatusChoices,
|
|
542
|
+
required=False,
|
|
543
|
+
label=_("Domain Status"),
|
|
544
|
+
)
|
|
507
545
|
registrant_id = DynamicModelMultipleChoiceField(
|
|
508
546
|
queryset=RegistrationContact.objects.all(),
|
|
509
547
|
required=False,
|
|
548
|
+
null_option=_("None"),
|
|
510
549
|
label=_("Registrant"),
|
|
511
550
|
)
|
|
512
551
|
admin_c_id = DynamicModelMultipleChoiceField(
|
|
513
552
|
queryset=RegistrationContact.objects.all(),
|
|
514
553
|
required=False,
|
|
554
|
+
null_option=_("None"),
|
|
515
555
|
label=_("Administrative Contact"),
|
|
516
556
|
)
|
|
517
557
|
tech_c_id = DynamicModelMultipleChoiceField(
|
|
518
558
|
queryset=RegistrationContact.objects.all(),
|
|
519
559
|
required=False,
|
|
560
|
+
null_option=_("None"),
|
|
520
561
|
label=_("Technical Contact"),
|
|
521
562
|
)
|
|
522
563
|
billing_c_id = DynamicModelMultipleChoiceField(
|
|
523
564
|
queryset=RegistrationContact.objects.all(),
|
|
524
565
|
required=False,
|
|
566
|
+
null_option=_("None"),
|
|
525
567
|
label=_("Billing Contact"),
|
|
526
568
|
)
|
|
527
569
|
tag = TagFilterField(Zone)
|
|
@@ -638,6 +680,11 @@ class ZoneImportForm(ZoneTemplateUpdateMixin, NetBoxModelImportForm):
|
|
|
638
680
|
required=False,
|
|
639
681
|
label=_("Registry Domain ID"),
|
|
640
682
|
)
|
|
683
|
+
domain_status = CSVChoiceField(
|
|
684
|
+
choices=ZoneEPPStatusChoices,
|
|
685
|
+
required=False,
|
|
686
|
+
label=_("Domain Status"),
|
|
687
|
+
)
|
|
641
688
|
registrant = CSVModelChoiceField(
|
|
642
689
|
queryset=RegistrationContact.objects.all(),
|
|
643
690
|
required=False,
|
|
@@ -713,6 +760,8 @@ class ZoneImportForm(ZoneTemplateUpdateMixin, NetBoxModelImportForm):
|
|
|
713
760
|
"rfc2317_parent_managed",
|
|
714
761
|
"registrar",
|
|
715
762
|
"registry_domain_id",
|
|
763
|
+
"expiration_date",
|
|
764
|
+
"domain_status",
|
|
716
765
|
"registrant",
|
|
717
766
|
"admin_c",
|
|
718
767
|
"tech_c",
|
|
@@ -846,6 +895,16 @@ class ZoneBulkEditForm(NetBoxModelBulkEditForm):
|
|
|
846
895
|
required=False,
|
|
847
896
|
label=_("Registry Domain ID"),
|
|
848
897
|
)
|
|
898
|
+
expiration_date = forms.DateField(
|
|
899
|
+
required=False,
|
|
900
|
+
label=_("Expiration Date"),
|
|
901
|
+
widget=DatePicker,
|
|
902
|
+
)
|
|
903
|
+
domain_status = forms.ChoiceField(
|
|
904
|
+
choices=add_blank_choice(ZoneEPPStatusChoices),
|
|
905
|
+
required=False,
|
|
906
|
+
label=_("Domain Status"),
|
|
907
|
+
)
|
|
849
908
|
registrant = DynamicModelChoiceField(
|
|
850
909
|
queryset=RegistrationContact.objects.all(),
|
|
851
910
|
required=False,
|
|
@@ -913,6 +972,8 @@ class ZoneBulkEditForm(NetBoxModelBulkEditForm):
|
|
|
913
972
|
FieldSet(
|
|
914
973
|
"registrar",
|
|
915
974
|
"registry_domain_id",
|
|
975
|
+
"expiration_date",
|
|
976
|
+
"domain_status",
|
|
916
977
|
"registrant",
|
|
917
978
|
"admin_c",
|
|
918
979
|
"tech_c",
|
|
@@ -927,6 +988,8 @@ class ZoneBulkEditForm(NetBoxModelBulkEditForm):
|
|
|
927
988
|
"nameservers",
|
|
928
989
|
"rfc2317_prefix",
|
|
929
990
|
"registrar",
|
|
991
|
+
"expiration_date",
|
|
992
|
+
"domain_status",
|
|
930
993
|
"registry_domain_id",
|
|
931
994
|
"registrant",
|
|
932
995
|
"admin_c",
|
|
@@ -127,11 +127,13 @@ class ZoneTemplateFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|
|
127
127
|
nameserver_id = DynamicModelMultipleChoiceField(
|
|
128
128
|
queryset=NameServer.objects.all(),
|
|
129
129
|
required=False,
|
|
130
|
+
null_option=_("None"),
|
|
130
131
|
label=_("Nameservers"),
|
|
131
132
|
)
|
|
132
133
|
soa_mname_id = DynamicModelMultipleChoiceField(
|
|
133
134
|
queryset=NameServer.objects.all(),
|
|
134
135
|
required=False,
|
|
136
|
+
null_option=_("None"),
|
|
135
137
|
label=_("MName"),
|
|
136
138
|
)
|
|
137
139
|
soa_rname = forms.CharField(
|
|
@@ -141,6 +143,7 @@ class ZoneTemplateFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|
|
141
143
|
record_template_id = DynamicModelMultipleChoiceField(
|
|
142
144
|
queryset=RecordTemplate.objects.all(),
|
|
143
145
|
required=False,
|
|
146
|
+
null_option=_("None"),
|
|
144
147
|
label=_("Record Templates"),
|
|
145
148
|
)
|
|
146
149
|
description = forms.CharField(
|
|
@@ -149,31 +152,37 @@ class ZoneTemplateFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|
|
149
152
|
dnssec_policy_id = DynamicModelMultipleChoiceField(
|
|
150
153
|
queryset=DNSSECPolicy.objects.all(),
|
|
151
154
|
required=False,
|
|
155
|
+
null_option=_("None"),
|
|
152
156
|
label=_("DNSSEC Policy ID"),
|
|
153
157
|
)
|
|
154
158
|
registrar_id = DynamicModelMultipleChoiceField(
|
|
155
159
|
queryset=Registrar.objects.all(),
|
|
156
160
|
required=False,
|
|
161
|
+
null_option=_("None"),
|
|
157
162
|
label=_("Registrar"),
|
|
158
163
|
)
|
|
159
164
|
registrant_id = DynamicModelMultipleChoiceField(
|
|
160
165
|
queryset=RegistrationContact.objects.all(),
|
|
161
166
|
required=False,
|
|
167
|
+
null_option=_("None"),
|
|
162
168
|
label=_("Registrant"),
|
|
163
169
|
)
|
|
164
170
|
admin_c_id = DynamicModelMultipleChoiceField(
|
|
165
171
|
queryset=RegistrationContact.objects.all(),
|
|
166
172
|
required=False,
|
|
173
|
+
null_option=_("None"),
|
|
167
174
|
label=_("Administrative Contact"),
|
|
168
175
|
)
|
|
169
176
|
tech_c_id = DynamicModelMultipleChoiceField(
|
|
170
177
|
queryset=RegistrationContact.objects.all(),
|
|
171
178
|
required=False,
|
|
179
|
+
null_option=_("None"),
|
|
172
180
|
label=_("Technical Contact"),
|
|
173
181
|
)
|
|
174
182
|
billing_c_id = DynamicModelMultipleChoiceField(
|
|
175
183
|
queryset=RegistrationContact.objects.all(),
|
|
176
184
|
required=False,
|
|
185
|
+
null_option=_("None"),
|
|
177
186
|
label=_("Billing Contact"),
|
|
178
187
|
)
|
|
179
188
|
tag = TagFilterField(ZoneTemplate)
|
netbox_dns/graphql/types.py
CHANGED
|
@@ -214,6 +214,7 @@ class NetBoxDNSDNSSECPolicyType(NetBoxObjectType):
|
|
|
214
214
|
cds_digest_types: List[str]
|
|
215
215
|
parent_ds_ttl: BigInt | None
|
|
216
216
|
parent_propagation_delay: BigInt | None
|
|
217
|
+
parental_agents: List[str]
|
|
217
218
|
use_nsec3: bool
|
|
218
219
|
nsec3_iterations: BigInt | None
|
|
219
220
|
nsec3_opt_out: bool
|
|
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
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Generated by Django 5.2 on 2025-04-22 18:05
|
|
2
|
+
|
|
3
|
+
import django.contrib.postgres.fields
|
|
4
|
+
from django.db import migrations, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
("netbox_dns", "0018_zone_domain_status_zone_expiration_date"),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.AddField(
|
|
15
|
+
model_name="dnssecpolicy",
|
|
16
|
+
name="parental_agents",
|
|
17
|
+
field=django.contrib.postgres.fields.ArrayField(
|
|
18
|
+
base_field=models.GenericIPAddressField(),
|
|
19
|
+
blank=True,
|
|
20
|
+
default=list,
|
|
21
|
+
null=True,
|
|
22
|
+
size=None,
|
|
23
|
+
),
|
|
24
|
+
),
|
|
25
|
+
]
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from django.db import models
|
|
2
2
|
from django.urls import reverse
|
|
3
|
+
from django.contrib.postgres.fields import ArrayField
|
|
3
4
|
from django.utils.translation import gettext_lazy as _
|
|
4
5
|
|
|
5
6
|
from netbox.models import NetBoxModel
|
|
@@ -117,6 +118,14 @@ class DNSSECPolicy(ContactsMixin, NetBoxModel):
|
|
|
117
118
|
blank=True,
|
|
118
119
|
null=True,
|
|
119
120
|
)
|
|
121
|
+
parental_agents = ArrayField(
|
|
122
|
+
base_field=models.GenericIPAddressField(
|
|
123
|
+
protocol="both",
|
|
124
|
+
),
|
|
125
|
+
blank=True,
|
|
126
|
+
null=True,
|
|
127
|
+
default=list,
|
|
128
|
+
)
|
|
120
129
|
|
|
121
130
|
use_nsec3 = models.BooleanField(
|
|
122
131
|
verbose_name=_("Use NSEC3"),
|
|
@@ -181,16 +190,18 @@ class DNSSECPolicy(ContactsMixin, NetBoxModel):
|
|
|
181
190
|
return self.publish_safety if self.publish_safety is not None else 3600
|
|
182
191
|
|
|
183
192
|
def get_effective_value(self, attribute):
|
|
184
|
-
default_value = get_plugin_config("netbox_dns", f"dnssec_{attribute}", None)
|
|
185
|
-
|
|
186
193
|
if not hasattr(self, attribute):
|
|
187
194
|
raise AttributeError(f"DNSSECPolicy does not have attribute {attribute}")
|
|
188
195
|
|
|
189
196
|
if (value := getattr(self, attribute)) is None:
|
|
190
|
-
return
|
|
197
|
+
return self.get_fallback_setting(attribute)
|
|
191
198
|
|
|
192
199
|
return value
|
|
193
200
|
|
|
201
|
+
@classmethod
|
|
202
|
+
def get_fallback_setting(cls, attribute):
|
|
203
|
+
return get_plugin_config("netbox_dns", f"dnssec_{attribute}")
|
|
204
|
+
|
|
194
205
|
|
|
195
206
|
@register_search
|
|
196
207
|
class DNSSECPolicyIndex(SearchIndex):
|
netbox_dns/models/record.py
CHANGED
|
@@ -167,7 +167,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
167
167
|
default=False,
|
|
168
168
|
)
|
|
169
169
|
ptr_record = models.OneToOneField(
|
|
170
|
-
verbose_name="PTR Record",
|
|
170
|
+
verbose_name=_("PTR Record"),
|
|
171
171
|
to="self",
|
|
172
172
|
on_delete=models.SET_NULL,
|
|
173
173
|
related_name="address_record",
|
|
@@ -649,6 +649,9 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
649
649
|
|
|
650
650
|
@property
|
|
651
651
|
def absolute_value(self):
|
|
652
|
+
if self.type in RecordTypeChoices.CUSTOM_TYPES:
|
|
653
|
+
return self.value
|
|
654
|
+
|
|
652
655
|
zone = dns_name.from_text(self.zone.name)
|
|
653
656
|
rr = rdata.from_text(RecordClassChoices.IN, self.type, self.value)
|
|
654
657
|
|
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
|
|
@@ -26,7 +26,12 @@ from netbox.plugins.utils import get_plugin_config
|
|
|
26
26
|
from utilities.querysets import RestrictedQuerySet
|
|
27
27
|
from ipam.models import IPAddress
|
|
28
28
|
|
|
29
|
-
from netbox_dns.choices import
|
|
29
|
+
from netbox_dns.choices import (
|
|
30
|
+
RecordClassChoices,
|
|
31
|
+
RecordTypeChoices,
|
|
32
|
+
ZoneStatusChoices,
|
|
33
|
+
ZoneEPPStatusChoices,
|
|
34
|
+
)
|
|
30
35
|
from netbox_dns.fields import NetworkField, RFC2317NetworkField
|
|
31
36
|
from netbox_dns.utilities import (
|
|
32
37
|
update_dns_records,
|
|
@@ -202,6 +207,18 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
202
207
|
blank=True,
|
|
203
208
|
null=True,
|
|
204
209
|
)
|
|
210
|
+
expiration_date = models.DateField(
|
|
211
|
+
verbose_name=_("Expiration Date"),
|
|
212
|
+
blank=True,
|
|
213
|
+
null=True,
|
|
214
|
+
)
|
|
215
|
+
domain_status = models.CharField(
|
|
216
|
+
verbose_name=_("Domain Status"),
|
|
217
|
+
max_length=50,
|
|
218
|
+
choices=ZoneEPPStatusChoices,
|
|
219
|
+
blank=True,
|
|
220
|
+
null=True,
|
|
221
|
+
)
|
|
205
222
|
registrant = models.ForeignKey(
|
|
206
223
|
verbose_name=_("Registrant"),
|
|
207
224
|
to="RegistrationContact",
|
|
@@ -211,7 +228,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
211
228
|
null=True,
|
|
212
229
|
)
|
|
213
230
|
admin_c = models.ForeignKey(
|
|
214
|
-
verbose_name="Administrative Contact",
|
|
231
|
+
verbose_name=_("Administrative Contact"),
|
|
215
232
|
to="RegistrationContact",
|
|
216
233
|
on_delete=models.SET_NULL,
|
|
217
234
|
related_name="admin_c_zones",
|
|
@@ -288,6 +305,23 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
288
305
|
"tenant",
|
|
289
306
|
)
|
|
290
307
|
|
|
308
|
+
soa_clean_fields = {
|
|
309
|
+
"description",
|
|
310
|
+
"status",
|
|
311
|
+
"dnssec_policy",
|
|
312
|
+
"inline_signing",
|
|
313
|
+
"registrar",
|
|
314
|
+
"registry_domain_id",
|
|
315
|
+
"expiration_date",
|
|
316
|
+
"domain_status",
|
|
317
|
+
"registrant",
|
|
318
|
+
"admin_c",
|
|
319
|
+
"tech_c",
|
|
320
|
+
"billing_c",
|
|
321
|
+
"rfc2317_parent_managed",
|
|
322
|
+
"tenant",
|
|
323
|
+
}
|
|
324
|
+
|
|
291
325
|
class Meta:
|
|
292
326
|
verbose_name = _("Zone")
|
|
293
327
|
verbose_name_plural = _("Zones")
|
|
@@ -370,6 +404,9 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
370
404
|
def get_status_color(self):
|
|
371
405
|
return ZoneStatusChoices.colors.get(self.status)
|
|
372
406
|
|
|
407
|
+
def get_domain_status_color(self):
|
|
408
|
+
return ZoneEPPStatusChoices.colors.get(self.domain_status)
|
|
409
|
+
|
|
373
410
|
# TODO: Remove in version 1.3.0 (NetBox #18555)
|
|
374
411
|
def get_absolute_url(self):
|
|
375
412
|
return reverse("plugins:netbox_dns:zone", kwargs={"pk": self.pk})
|
|
@@ -410,6 +447,8 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
410
447
|
self.admin_c,
|
|
411
448
|
self.tech_c,
|
|
412
449
|
self.billing_c,
|
|
450
|
+
self.expiration_date,
|
|
451
|
+
self.domain_status,
|
|
413
452
|
)
|
|
414
453
|
)
|
|
415
454
|
|
|
@@ -579,6 +618,26 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
579
618
|
|
|
580
619
|
return ns_warnings, ns_errors
|
|
581
620
|
|
|
621
|
+
def check_expiration(self):
|
|
622
|
+
if self.expiration_date is None:
|
|
623
|
+
return None, None
|
|
624
|
+
|
|
625
|
+
expiration_warning = None
|
|
626
|
+
expiration_error = None
|
|
627
|
+
|
|
628
|
+
expiration_warning_days = get_plugin_config(
|
|
629
|
+
"netbox_dns", "zone_expiration_warning_days"
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
if self.expiration_date < date.today():
|
|
633
|
+
expiration_error = _("The registration for this domain has expired.")
|
|
634
|
+
elif (self.expiration_date - date.today()).days < expiration_warning_days:
|
|
635
|
+
expiration_warning = _(
|
|
636
|
+
f"The registration for his domain will expire less than {expiration_warning_days} days."
|
|
637
|
+
)
|
|
638
|
+
|
|
639
|
+
return expiration_warning, expiration_error
|
|
640
|
+
|
|
582
641
|
def check_soa_serial_increment(self, old_serial, new_serial):
|
|
583
642
|
MAX_SOA_SERIAL_INCREMENT = 2**31 - 1
|
|
584
643
|
SOA_SERIAL_WRAP = 2**32
|
|
@@ -870,7 +929,9 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
|
870
929
|
|
|
871
930
|
changed_fields = self.changed_fields
|
|
872
931
|
|
|
873
|
-
if self.soa_serial_auto
|
|
932
|
+
if self.soa_serial_auto and (
|
|
933
|
+
changed_fields is None or changed_fields - self.soa_clean_fields
|
|
934
|
+
):
|
|
874
935
|
self.soa_serial = self.get_auto_serial()
|
|
875
936
|
|
|
876
937
|
super().save(*args, **kwargs)
|
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",
|
netbox_dns/template_content.py
CHANGED
|
@@ -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: (
|
|
@@ -118,6 +118,16 @@
|
|
|
118
118
|
<th scope="row">{% trans "Parent Propagation Delay" %}</th>
|
|
119
119
|
<td>{{ object.parent_propagation_delay|placeholder }}</td>
|
|
120
120
|
</tr>
|
|
121
|
+
<tr>
|
|
122
|
+
<th scope="row">{% trans "Parental Agents" %}</th>
|
|
123
|
+
<td>
|
|
124
|
+
<table>
|
|
125
|
+
{% for parental_agent in object.parental_agents %}
|
|
126
|
+
<tr><td>{{ parental_agent }}</td></tr>
|
|
127
|
+
{% endfor %}
|
|
128
|
+
</table>
|
|
129
|
+
</td>
|
|
130
|
+
</tr>
|
|
121
131
|
</table>
|
|
122
132
|
</div>
|
|
123
133
|
<div class="card">
|
|
@@ -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 = (
|