netbox-plugin-dns 1.0.3__py3-none-any.whl → 1.0.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of netbox-plugin-dns might be problematic. Click here for more details.

Files changed (91) hide show
  1. netbox_dns/__init__.py +1 -1
  2. netbox_dns/api/nested_serializers.py +46 -1
  3. netbox_dns/api/serializers.py +2 -0
  4. netbox_dns/api/serializers_/contact.py +3 -0
  5. netbox_dns/api/serializers_/nameserver.py +4 -1
  6. netbox_dns/api/serializers_/record.py +5 -4
  7. netbox_dns/api/serializers_/record_template.py +57 -0
  8. netbox_dns/api/serializers_/registrar.py +3 -0
  9. netbox_dns/api/serializers_/view.py +3 -0
  10. netbox_dns/api/serializers_/zone.py +30 -6
  11. netbox_dns/api/serializers_/zone_template.py +129 -0
  12. netbox_dns/api/urls.py +4 -0
  13. netbox_dns/api/views.py +41 -1
  14. netbox_dns/choices/__init__.py +2 -0
  15. netbox_dns/choices/record.py +49 -0
  16. netbox_dns/choices/zone.py +20 -0
  17. netbox_dns/fields/address.py +6 -0
  18. netbox_dns/fields/network.py +3 -0
  19. netbox_dns/fields/rfc2317.py +3 -0
  20. netbox_dns/filtersets/__init__.py +3 -0
  21. netbox_dns/filtersets/contact.py +3 -0
  22. netbox_dns/filtersets/nameserver.py +3 -0
  23. netbox_dns/filtersets/record.py +5 -1
  24. netbox_dns/filtersets/record_template.py +54 -0
  25. netbox_dns/filtersets/registrar.py +3 -0
  26. netbox_dns/filtersets/view.py +3 -0
  27. netbox_dns/filtersets/zone.py +5 -8
  28. netbox_dns/filtersets/zone_template.py +116 -0
  29. netbox_dns/forms/__init__.py +2 -0
  30. netbox_dns/forms/contact.py +8 -0
  31. netbox_dns/forms/nameserver.py +8 -0
  32. netbox_dns/forms/record.py +25 -11
  33. netbox_dns/forms/record_template.py +220 -0
  34. netbox_dns/forms/registrar.py +8 -0
  35. netbox_dns/forms/view.py +10 -0
  36. netbox_dns/forms/zone.py +109 -36
  37. netbox_dns/forms/zone_template.py +298 -0
  38. netbox_dns/graphql/__init__.py +4 -0
  39. netbox_dns/graphql/filters.py +24 -1
  40. netbox_dns/graphql/schema.py +34 -1
  41. netbox_dns/graphql/types.py +73 -5
  42. netbox_dns/management/commands/cleanup_database.py +2 -6
  43. netbox_dns/management/commands/cleanup_rrset_ttl.py +2 -4
  44. netbox_dns/migrations/0001_squashed_netbox_dns_0_22.py +1 -2
  45. netbox_dns/migrations/0006_templating.py +172 -0
  46. netbox_dns/migrations/0021_record_ip_address.py +1 -1
  47. netbox_dns/mixins/object_modification.py +3 -0
  48. netbox_dns/models/__init__.py +7 -0
  49. netbox_dns/models/contact.py +6 -0
  50. netbox_dns/models/nameserver.py +8 -1
  51. netbox_dns/models/record.py +10 -122
  52. netbox_dns/models/record_template.py +180 -0
  53. netbox_dns/models/registrar.py +6 -0
  54. netbox_dns/models/view.py +7 -1
  55. netbox_dns/models/zone.py +57 -66
  56. netbox_dns/models/zone_template.py +149 -0
  57. netbox_dns/navigation.py +47 -0
  58. netbox_dns/tables/__init__.py +2 -0
  59. netbox_dns/tables/contact.py +3 -0
  60. netbox_dns/tables/nameserver.py +3 -2
  61. netbox_dns/tables/record.py +7 -3
  62. netbox_dns/tables/record_template.py +91 -0
  63. netbox_dns/tables/registrar.py +3 -0
  64. netbox_dns/tables/view.py +3 -0
  65. netbox_dns/tables/zone.py +3 -2
  66. netbox_dns/tables/zone_template.py +70 -0
  67. netbox_dns/template_content.py +2 -8
  68. netbox_dns/templates/netbox_dns/recordtemplate.html +84 -0
  69. netbox_dns/templates/netbox_dns/zonetemplate.html +86 -0
  70. netbox_dns/urls/__init__.py +4 -0
  71. netbox_dns/urls/record_template.py +65 -0
  72. netbox_dns/urls/zone_template.py +57 -0
  73. netbox_dns/utilities/ipam_coupling.py +2 -1
  74. netbox_dns/validators/__init__.py +1 -0
  75. netbox_dns/validators/dns_name.py +14 -9
  76. netbox_dns/validators/dns_value.py +83 -0
  77. netbox_dns/validators/rfc2317.py +7 -0
  78. netbox_dns/views/__init__.py +2 -0
  79. netbox_dns/views/contact.py +11 -0
  80. netbox_dns/views/nameserver.py +12 -0
  81. netbox_dns/views/record.py +14 -1
  82. netbox_dns/views/record_template.py +83 -0
  83. netbox_dns/views/registrar.py +12 -0
  84. netbox_dns/views/view.py +12 -0
  85. netbox_dns/views/zone.py +16 -0
  86. netbox_dns/views/zone_template.py +73 -0
  87. {netbox_plugin_dns-1.0.3.dist-info → netbox_plugin_dns-1.0.5.dist-info}/METADATA +2 -1
  88. netbox_plugin_dns-1.0.5.dist-info/RECORD +136 -0
  89. netbox_plugin_dns-1.0.3.dist-info/RECORD +0 -115
  90. {netbox_plugin_dns-1.0.3.dist-info → netbox_plugin_dns-1.0.5.dist-info}/LICENSE +0 -0
  91. {netbox_plugin_dns-1.0.3.dist-info → netbox_plugin_dns-1.0.5.dist-info}/WHEEL +0 -0
netbox_dns/forms/zone.py CHANGED
@@ -1,7 +1,8 @@
1
1
  from django import forms
2
+ from django.db import transaction
2
3
  from django.conf import settings
3
4
  from django.core.validators import MinValueValidator, MaxValueValidator
4
- from django.urls import reverse_lazy
5
+ from django.core.exceptions import ValidationError
5
6
 
6
7
  from netbox.forms import (
7
8
  NetBoxModelBulkEditForm,
@@ -9,6 +10,7 @@ from netbox.forms import (
9
10
  NetBoxModelImportForm,
10
11
  NetBoxModelForm,
11
12
  )
13
+ from netbox.context import events_queue
12
14
  from utilities.forms.fields import (
13
15
  DynamicModelMultipleChoiceField,
14
16
  TagFilterField,
@@ -17,26 +19,107 @@ from utilities.forms.fields import (
17
19
  CSVModelMultipleChoiceField,
18
20
  DynamicModelChoiceField,
19
21
  )
20
- from utilities.forms.widgets import BulkEditNullBooleanSelect, APISelect
22
+ from utilities.forms.widgets import BulkEditNullBooleanSelect
21
23
  from utilities.forms.rendering import FieldSet
22
- from utilities.forms import add_blank_choice
24
+ from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, add_blank_choice
23
25
  from tenancy.models import Tenant
24
26
  from tenancy.forms import TenancyForm, TenancyFilterForm
25
27
 
26
28
  from netbox_dns.models import (
27
29
  View,
28
30
  Zone,
29
- ZoneStatusChoices,
30
31
  NameServer,
31
32
  Registrar,
32
33
  Contact,
34
+ ZoneTemplate,
33
35
  )
36
+ from netbox_dns.choices import ZoneStatusChoices
34
37
  from netbox_dns.utilities import name_to_unicode
35
38
  from netbox_dns.fields import RFC2317NetworkFormField
36
39
  from netbox_dns.validators import validate_ipv4, validate_prefix, validate_rfc2317
37
40
 
38
41
 
39
- class ZoneForm(TenancyForm, NetBoxModelForm):
42
+ __ALL__ = (
43
+ "ZoneForm",
44
+ "ZoneFilterForm",
45
+ "ZoneImportForm",
46
+ "ZoneBulkEditForm",
47
+ )
48
+
49
+
50
+ class RollbackTransaction(Exception):
51
+ pass
52
+
53
+
54
+ class ZoneTemplateUpdateMixin:
55
+ def clean(self, *args, **kwargs):
56
+ super().clean(*args, **kwargs)
57
+
58
+ if (template := self.cleaned_data.get("template")) is None:
59
+ return
60
+
61
+ if not self.cleaned_data.get("nameservers") and template.nameservers.all():
62
+ self.cleaned_data["nameservers"] = template.nameservers.all()
63
+
64
+ if not self.cleaned_data.get("tags") and template.tags.all():
65
+ self.cleaned_data["tags"] = template.tags.all()
66
+
67
+ for field in template.template_fields:
68
+ if (
69
+ self.cleaned_data.get(field) is None
70
+ and getattr(template, field) is not None
71
+ ):
72
+ self.cleaned_data[field] = getattr(template, field)
73
+
74
+ template_error = None
75
+ saved_events_queue = events_queue.get()
76
+
77
+ try:
78
+ with transaction.atomic():
79
+ if self.instance.id is not None:
80
+ zone = super().save(*args, **kwargs)
81
+ else:
82
+ zone_data = self.cleaned_data.copy()
83
+
84
+ zone_data.pop("template", None)
85
+ zone_data.pop("tenant_group", None)
86
+ zone_data.pop("_init_time", None)
87
+
88
+ nameservers = zone_data.pop("nameservers")
89
+ tags = zone_data.pop("tags")
90
+
91
+ zone = Zone.objects.create(**zone_data)
92
+
93
+ zone.nameservers.set(nameservers)
94
+ zone.tags.set(tags)
95
+
96
+ template.create_records(zone)
97
+ raise RollbackTransaction
98
+
99
+ except ValidationError as exc:
100
+ if isinstance(exc, dict):
101
+ template_error = item.value()
102
+ else:
103
+ template_error = [exc]
104
+ except RollbackTransaction:
105
+ pass
106
+
107
+ events_queue.set(saved_events_queue)
108
+ if template_error is not None:
109
+ raise ValidationError({"template": template_error})
110
+
111
+ return self.cleaned_data
112
+
113
+ def save(self, *args, **kwargs):
114
+ zone = super().save(*args, **kwargs)
115
+
116
+ if (template := self.cleaned_data.get("template")) is not None:
117
+ template.create_records(zone)
118
+
119
+ return zone
120
+
121
+
122
+ class ZoneForm(ZoneTemplateUpdateMixin, TenancyForm, NetBoxModelForm):
40
123
  nameservers = DynamicModelMultipleChoiceField(
41
124
  queryset=NameServer.objects.all(),
42
125
  required=False,
@@ -104,11 +187,17 @@ class ZoneForm(TenancyForm, NetBoxModelForm):
104
187
  help_text="IPv4 reverse zone for deletgating the RFC2317 PTR records is managed in NetBox DNS",
105
188
  required=False,
106
189
  )
190
+ template = DynamicModelChoiceField(
191
+ queryset=ZoneTemplate.objects.all(),
192
+ required=False,
193
+ label="Template",
194
+ )
107
195
 
108
196
  fieldsets = (
109
197
  FieldSet(
110
198
  "view",
111
199
  "name",
200
+ "template",
112
201
  "status",
113
202
  "nameservers",
114
203
  "default_ttl",
@@ -206,6 +295,7 @@ class ZoneForm(TenancyForm, NetBoxModelForm):
206
295
  "name",
207
296
  "view",
208
297
  "status",
298
+ "template",
209
299
  "nameservers",
210
300
  "default_ttl",
211
301
  "description",
@@ -297,6 +387,7 @@ class ZoneFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
297
387
  soa_serial_auto = forms.NullBooleanField(
298
388
  required=False,
299
389
  label="Generate Serial",
390
+ widget=forms.Select(choices=BOOLEAN_WITH_BLANK_CHOICES),
300
391
  )
301
392
  rfc2317_prefix = RFC2317NetworkFormField(
302
393
  required=False,
@@ -305,6 +396,7 @@ class ZoneFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
305
396
  rfc2317_parent_managed = forms.NullBooleanField(
306
397
  required=False,
307
398
  label="Parent Managed",
399
+ widget=forms.Select(choices=BOOLEAN_WITH_BLANK_CHOICES),
308
400
  )
309
401
  rfc2317_parent_zone_id = DynamicModelMultipleChoiceField(
310
402
  queryset=Zone.objects.all(),
@@ -343,7 +435,7 @@ class ZoneFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
343
435
  tag = TagFilterField(Zone)
344
436
 
345
437
 
346
- class ZoneImportForm(NetBoxModelImportForm):
438
+ class ZoneImportForm(ZoneTemplateUpdateMixin, NetBoxModelImportForm):
347
439
  view = CSVModelChoiceField(
348
440
  queryset=View.objects.all(),
349
441
  required=False,
@@ -385,7 +477,7 @@ class ZoneImportForm(NetBoxModelImportForm):
385
477
  required=False,
386
478
  help_text="Mailbox of the zone's administrator",
387
479
  )
388
- soa_serial_auto = forms.NullBooleanField(
480
+ soa_serial_auto = forms.BooleanField(
389
481
  required=False,
390
482
  help_text="Generate the SOA serial",
391
483
  )
@@ -473,6 +565,12 @@ class ZoneImportForm(NetBoxModelImportForm):
473
565
  to_field_name="name",
474
566
  help_text="Assigned tenant",
475
567
  )
568
+ template = CSVModelChoiceField(
569
+ queryset=ZoneTemplate.objects.all(),
570
+ required=False,
571
+ to_field_name="name",
572
+ label="Template",
573
+ )
476
574
 
477
575
  class Meta:
478
576
  model = Zone
@@ -481,6 +579,7 @@ class ZoneImportForm(NetBoxModelImportForm):
481
579
  "view",
482
580
  "name",
483
581
  "status",
582
+ "template",
484
583
  "nameservers",
485
584
  "default_ttl",
486
585
  "description",
@@ -532,9 +631,6 @@ class ZoneBulkEditForm(NetBoxModelBulkEditForm):
532
631
  queryset=View.objects.all(),
533
632
  required=False,
534
633
  label="View",
535
- widget=APISelect(
536
- attrs={"data-url": reverse_lazy("plugins-api:netbox_dns-api:view-list")}
537
- ),
538
634
  )
539
635
  status = forms.ChoiceField(
540
636
  choices=add_blank_choice(ZoneStatusChoices),
@@ -559,11 +655,6 @@ class ZoneBulkEditForm(NetBoxModelBulkEditForm):
559
655
  queryset=NameServer.objects.all(),
560
656
  required=False,
561
657
  label="SOA Primary Nameserver",
562
- widget=APISelect(
563
- attrs={
564
- "data-url": reverse_lazy("plugins-api:netbox_dns-api:nameserver-list")
565
- }
566
- ),
567
658
  )
568
659
  soa_rname = forms.CharField(
569
660
  required=False,
@@ -614,11 +705,6 @@ class ZoneBulkEditForm(NetBoxModelBulkEditForm):
614
705
  registrar = DynamicModelChoiceField(
615
706
  queryset=Registrar.objects.all(),
616
707
  required=False,
617
- widget=APISelect(
618
- attrs={
619
- "data-url": reverse_lazy("plugins-api:netbox_dns-api:registrar-list")
620
- }
621
- ),
622
708
  )
623
709
  registry_domain_id = forms.CharField(
624
710
  required=False,
@@ -627,41 +713,26 @@ class ZoneBulkEditForm(NetBoxModelBulkEditForm):
627
713
  registrant = DynamicModelChoiceField(
628
714
  queryset=Contact.objects.all(),
629
715
  required=False,
630
- widget=APISelect(
631
- attrs={"data-url": reverse_lazy("plugins-api:netbox_dns-api:contact-list")}
632
- ),
633
716
  )
634
717
  admin_c = DynamicModelChoiceField(
635
718
  queryset=Contact.objects.all(),
636
719
  required=False,
637
720
  label="Administrative Contact",
638
- widget=APISelect(
639
- attrs={"data-url": reverse_lazy("plugins-api:netbox_dns-api:contact-list")}
640
- ),
641
721
  )
642
722
  tech_c = DynamicModelChoiceField(
643
723
  queryset=Contact.objects.all(),
644
724
  required=False,
645
725
  label="Technical Contact",
646
- widget=APISelect(
647
- attrs={"data-url": reverse_lazy("plugins-api:netbox_dns-api:contact-list")}
648
- ),
649
726
  )
650
727
  billing_c = DynamicModelChoiceField(
651
728
  queryset=Contact.objects.all(),
652
729
  required=False,
653
730
  label="Billing Contact",
654
- widget=APISelect(
655
- attrs={"data-url": reverse_lazy("plugins-api:netbox_dns-api:contact-list")}
656
- ),
657
731
  )
658
- tenant = CSVModelChoiceField(
732
+ tenant = DynamicModelChoiceField(
659
733
  queryset=Tenant.objects.all(),
660
734
  required=False,
661
- to_field_name="name",
662
- help_text="Assigned tenant",
663
735
  )
664
- tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False)
665
736
 
666
737
  model = Zone
667
738
 
@@ -705,6 +776,7 @@ class ZoneBulkEditForm(NetBoxModelBulkEditForm):
705
776
 
706
777
  nullable_fields = (
707
778
  "description",
779
+ "nameservers",
708
780
  "rfc2317_prefix",
709
781
  "registrar",
710
782
  "registry_domain_id",
@@ -712,4 +784,5 @@ class ZoneBulkEditForm(NetBoxModelBulkEditForm):
712
784
  "admin_c",
713
785
  "tech_c",
714
786
  "billing_c",
787
+ "tenant",
715
788
  )
@@ -0,0 +1,298 @@
1
+ from django import forms
2
+
3
+ from netbox.forms import (
4
+ NetBoxModelBulkEditForm,
5
+ NetBoxModelFilterSetForm,
6
+ NetBoxModelImportForm,
7
+ NetBoxModelForm,
8
+ )
9
+ from utilities.forms.fields import (
10
+ DynamicModelMultipleChoiceField,
11
+ TagFilterField,
12
+ CSVModelChoiceField,
13
+ CSVModelMultipleChoiceField,
14
+ DynamicModelChoiceField,
15
+ )
16
+ from utilities.forms.rendering import FieldSet
17
+ from tenancy.models import Tenant
18
+ from tenancy.forms import TenancyForm, TenancyFilterForm
19
+
20
+ from netbox_dns.models import (
21
+ ZoneTemplate,
22
+ RecordTemplate,
23
+ NameServer,
24
+ Registrar,
25
+ Contact,
26
+ )
27
+
28
+
29
+ __ALL__ = (
30
+ "ZoneTemplateForm",
31
+ "ZoneTemplateFilterForm",
32
+ "ZoneTemplateImportForm",
33
+ "ZoneTemplateBulkEditForm",
34
+ )
35
+
36
+
37
+ class ZoneTemplateForm(TenancyForm, NetBoxModelForm):
38
+ nameservers = DynamicModelMultipleChoiceField(
39
+ queryset=NameServer.objects.all(),
40
+ required=False,
41
+ )
42
+ record_templates = DynamicModelMultipleChoiceField(
43
+ queryset=RecordTemplate.objects.all(),
44
+ required=False,
45
+ )
46
+
47
+ fieldsets = (
48
+ FieldSet("name", "description", "nameservers", name="Zone Template"),
49
+ FieldSet("record_templates", name="Record Templates"),
50
+ FieldSet(
51
+ "registrar",
52
+ "registrant",
53
+ "admin_c",
54
+ "tech_c",
55
+ "billing_c",
56
+ name="Domain Registration",
57
+ ),
58
+ FieldSet("tags", name="Tags"),
59
+ FieldSet("tenant_group", "tenant", name="Tenancy"),
60
+ )
61
+
62
+ class Meta:
63
+ model = ZoneTemplate
64
+
65
+ fields = (
66
+ "name",
67
+ "nameservers",
68
+ "record_templates",
69
+ "description",
70
+ "tags",
71
+ "registrar",
72
+ "registrant",
73
+ "admin_c",
74
+ "tech_c",
75
+ "billing_c",
76
+ "tenant",
77
+ )
78
+
79
+
80
+ class ZoneTemplateFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
81
+ model = ZoneTemplate
82
+ fieldsets = (
83
+ FieldSet("q", "filter_id", "tag"),
84
+ FieldSet("name", "nameserver_id", "description", name="Attributes"),
85
+ FieldSet("record_template_id", name="Record Templates"),
86
+ FieldSet(
87
+ "registrar_id",
88
+ "registrant_id",
89
+ "admin_c_id",
90
+ "tech_c_id",
91
+ "billing_c_id",
92
+ name="Registration",
93
+ ),
94
+ FieldSet("tenant_group_id", "tenant_id", name="Tenancy"),
95
+ )
96
+
97
+ name = forms.CharField(
98
+ required=False,
99
+ label="Template name",
100
+ )
101
+ nameserver_id = DynamicModelMultipleChoiceField(
102
+ queryset=NameServer.objects.all(),
103
+ required=False,
104
+ label="Nameservers",
105
+ )
106
+ record_template_id = DynamicModelMultipleChoiceField(
107
+ queryset=RecordTemplate.objects.all(),
108
+ required=False,
109
+ label="Record templates",
110
+ )
111
+ description = forms.CharField(
112
+ required=False,
113
+ )
114
+ registrar_id = DynamicModelMultipleChoiceField(
115
+ queryset=Registrar.objects.all(),
116
+ required=False,
117
+ label="Registrar",
118
+ )
119
+ registrant_id = DynamicModelMultipleChoiceField(
120
+ queryset=Contact.objects.all(),
121
+ required=False,
122
+ label="Registrant",
123
+ )
124
+ admin_c_id = DynamicModelMultipleChoiceField(
125
+ queryset=Contact.objects.all(),
126
+ required=False,
127
+ label="Admin-C",
128
+ )
129
+ tech_c_id = DynamicModelMultipleChoiceField(
130
+ queryset=Contact.objects.all(),
131
+ required=False,
132
+ label="Tech-C",
133
+ )
134
+ billing_c_id = DynamicModelMultipleChoiceField(
135
+ queryset=Contact.objects.all(),
136
+ required=False,
137
+ label="Billing-C",
138
+ )
139
+ tag = TagFilterField(ZoneTemplate)
140
+
141
+
142
+ class ZoneTemplateImportForm(NetBoxModelImportForm):
143
+ nameservers = CSVModelMultipleChoiceField(
144
+ queryset=NameServer.objects.all(),
145
+ to_field_name="name",
146
+ required=False,
147
+ help_text="Name servers for the zone template",
148
+ )
149
+ record_templates = CSVModelMultipleChoiceField(
150
+ queryset=RecordTemplate.objects.all(),
151
+ to_field_name="name",
152
+ required=False,
153
+ help_text="Record templates used by this zone template",
154
+ )
155
+ registrar = CSVModelChoiceField(
156
+ queryset=Registrar.objects.all(),
157
+ required=False,
158
+ to_field_name="name",
159
+ help_text="Registrar the domain is registered with",
160
+ error_messages={
161
+ "invalid_choice": "Registrar not found.",
162
+ },
163
+ )
164
+ registrant = CSVModelChoiceField(
165
+ queryset=Contact.objects.all(),
166
+ required=False,
167
+ to_field_name="contact_id",
168
+ help_text="Owner of the domain",
169
+ error_messages={
170
+ "invalid_choice": "Registrant contact ID not found",
171
+ },
172
+ )
173
+ admin_c = CSVModelChoiceField(
174
+ queryset=Contact.objects.all(),
175
+ required=False,
176
+ to_field_name="contact_id",
177
+ help_text="Administrative contact for the domain",
178
+ error_messages={
179
+ "invalid_choice": "Administrative contact ID not found",
180
+ },
181
+ )
182
+ tech_c = CSVModelChoiceField(
183
+ queryset=Contact.objects.all(),
184
+ required=False,
185
+ to_field_name="contact_id",
186
+ help_text="Technical contact for the domain",
187
+ error_messages={
188
+ "invalid_choice": "Technical contact ID not found",
189
+ },
190
+ )
191
+ billing_c = CSVModelChoiceField(
192
+ queryset=Contact.objects.all(),
193
+ required=False,
194
+ to_field_name="contact_id",
195
+ help_text="Billing contact for the domain",
196
+ error_messages={
197
+ "invalid_choice": "Billing contact ID not found",
198
+ },
199
+ )
200
+ tenant = CSVModelChoiceField(
201
+ queryset=Tenant.objects.all(),
202
+ required=False,
203
+ to_field_name="name",
204
+ help_text="Assigned tenant",
205
+ )
206
+
207
+ class Meta:
208
+ model = ZoneTemplate
209
+
210
+ fields = (
211
+ "name",
212
+ "nameservers",
213
+ "record_templates",
214
+ "description",
215
+ "registrar",
216
+ "registrant",
217
+ "admin_c",
218
+ "tech_c",
219
+ "billing_c",
220
+ "tenant",
221
+ "tags",
222
+ )
223
+
224
+
225
+ class ZoneTemplateBulkEditForm(NetBoxModelBulkEditForm):
226
+ nameservers = DynamicModelMultipleChoiceField(
227
+ queryset=NameServer.objects.all(),
228
+ required=False,
229
+ )
230
+ record_templates = DynamicModelMultipleChoiceField(
231
+ queryset=RecordTemplate.objects.all(),
232
+ required=False,
233
+ )
234
+ description = forms.CharField(max_length=200, required=False)
235
+ registrar = DynamicModelChoiceField(
236
+ queryset=Registrar.objects.all(),
237
+ required=False,
238
+ )
239
+ registrant = DynamicModelChoiceField(
240
+ queryset=Contact.objects.all(),
241
+ required=False,
242
+ )
243
+ admin_c = DynamicModelChoiceField(
244
+ queryset=Contact.objects.all(),
245
+ required=False,
246
+ label="Administrative Contact",
247
+ )
248
+ tech_c = DynamicModelChoiceField(
249
+ queryset=Contact.objects.all(),
250
+ required=False,
251
+ label="Technical Contact",
252
+ )
253
+ billing_c = DynamicModelChoiceField(
254
+ queryset=Contact.objects.all(),
255
+ required=False,
256
+ label="Billing Contact",
257
+ )
258
+ tenant = CSVModelChoiceField(
259
+ queryset=Tenant.objects.all(),
260
+ required=False,
261
+ to_field_name="name",
262
+ help_text="Assigned tenant",
263
+ )
264
+ tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False)
265
+
266
+ model = ZoneTemplate
267
+
268
+ fieldsets = (
269
+ FieldSet(
270
+ "nameservers",
271
+ "description",
272
+ name="Attributes",
273
+ ),
274
+ FieldSet(
275
+ "record_templates",
276
+ name="Record Templates",
277
+ ),
278
+ FieldSet(
279
+ "registrar",
280
+ "registrant",
281
+ "admin_c",
282
+ "tech_c",
283
+ "billing_c",
284
+ name="Domain Registration",
285
+ ),
286
+ FieldSet("tenant_group", "tenant", name="Tenancy"),
287
+ )
288
+
289
+ nullable_fields = (
290
+ "description",
291
+ "nameservers",
292
+ "record_templates",
293
+ "registrar",
294
+ "registrant",
295
+ "admin_c",
296
+ "tech_c",
297
+ "billing_c",
298
+ )
@@ -5,6 +5,8 @@ from .schema import (
5
5
  NetBoxDNSRegistrarQuery,
6
6
  NetBoxDNSZoneQuery,
7
7
  NetBoxDNSRecordQuery,
8
+ NetBoxDNSZoneTemplateQuery,
9
+ NetBoxDNSRecordTemplateQuery,
8
10
  )
9
11
 
10
12
  schema = [
@@ -14,4 +16,6 @@ schema = [
14
16
  NetBoxDNSRecordQuery,
15
17
  NetBoxDNSContactQuery,
16
18
  NetBoxDNSRegistrarQuery,
19
+ NetBoxDNSZoneTemplateQuery,
20
+ NetBoxDNSRecordTemplateQuery,
17
21
  ]
@@ -2,7 +2,16 @@ import strawberry_django
2
2
 
3
3
  from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
4
4
 
5
- from netbox_dns.models import NameServer, View, Zone, Record, Contact, Registrar
5
+ from netbox_dns.models import (
6
+ NameServer,
7
+ View,
8
+ Zone,
9
+ Record,
10
+ Contact,
11
+ Registrar,
12
+ ZoneTemplate,
13
+ RecordTemplate,
14
+ )
6
15
  from netbox_dns.filtersets import (
7
16
  NameServerFilterSet,
8
17
  ViewFilterSet,
@@ -10,6 +19,8 @@ from netbox_dns.filtersets import (
10
19
  RecordFilterSet,
11
20
  ContactFilterSet,
12
21
  RegistrarFilterSet,
22
+ ZoneTemplateFilterSet,
23
+ RecordTemplateFilterSet,
13
24
  )
14
25
 
15
26
 
@@ -37,6 +48,18 @@ class NetBoxDNSRecordFilter(BaseFilterMixin):
37
48
  ip_address: str | None
38
49
 
39
50
 
51
+ @strawberry_django.filter(ZoneTemplate, lookups=True)
52
+ @autotype_decorator(ZoneTemplateFilterSet)
53
+ class NetBoxDNSZoneTemplateFilter(BaseFilterMixin):
54
+ pass
55
+
56
+
57
+ @strawberry_django.filter(RecordTemplate, lookups=True)
58
+ @autotype_decorator(RecordTemplateFilterSet)
59
+ class NetBoxDNSRecordTemplateFilter(BaseFilterMixin):
60
+ pass
61
+
62
+
40
63
  @strawberry_django.filter(Contact, lookups=True)
41
64
  @autotype_decorator(ContactFilterSet)
42
65
  class NetBoxDNSContactFilter(BaseFilterMixin):
@@ -3,7 +3,16 @@ from typing import List
3
3
  import strawberry
4
4
  import strawberry_django
5
5
 
6
- from netbox_dns.models import NameServer, View, Zone, Record, Contact, Registrar
6
+ from netbox_dns.models import (
7
+ NameServer,
8
+ View,
9
+ Zone,
10
+ Record,
11
+ Contact,
12
+ Registrar,
13
+ ZoneTemplate,
14
+ RecordTemplate,
15
+ )
7
16
  from .types import (
8
17
  NetBoxDNSNameServerType,
9
18
  NetBoxDNSViewType,
@@ -11,6 +20,8 @@ from .types import (
11
20
  NetBoxDNSRecordType,
12
21
  NetBoxDNSContactType,
13
22
  NetBoxDNSRegistrarType,
23
+ NetBoxDNSZoneTemplateType,
24
+ NetBoxDNSRecordTemplateType,
14
25
  )
15
26
 
16
27
 
@@ -68,3 +79,25 @@ class NetBoxDNSRegistrarQuery:
68
79
  return Registrar.objects.get(pk=id)
69
80
 
70
81
  netbox_dns_registrar_list: List[NetBoxDNSRegistrarType] = strawberry_django.field()
82
+
83
+
84
+ @strawberry.type
85
+ class NetBoxDNSZoneTemplateQuery:
86
+ @strawberry.field
87
+ def netbox_dns_zone_template(self, id: int) -> NetBoxDNSZoneTemplateType:
88
+ return ZoneTemplate.objects.get(pk=id)
89
+
90
+ netbox_dns_zone_template_list: List[NetBoxDNSZoneTemplateType] = (
91
+ strawberry_django.field()
92
+ )
93
+
94
+
95
+ @strawberry.type
96
+ class NetBoxDNSRecordTemplateQuery:
97
+ @strawberry.field
98
+ def netbox_dns_record_template(self, id: int) -> NetBoxDNSRecordTemplateType:
99
+ return RecordTemplate.objects.get(pk=id)
100
+
101
+ netbox_dns_record_template_list: List[NetBoxDNSRecordTemplateType] = (
102
+ strawberry_django.field()
103
+ )