netbox-plugin-dns 1.1.1__py3-none-any.whl → 1.1.3__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 (88) hide show
  1. netbox_dns/__init__.py +8 -6
  2. netbox_dns/api/nested_serializers.py +3 -2
  3. netbox_dns/api/serializers_/nameserver.py +2 -1
  4. netbox_dns/api/serializers_/record.py +5 -4
  5. netbox_dns/api/serializers_/record_template.py +2 -1
  6. netbox_dns/api/serializers_/view.py +3 -1
  7. netbox_dns/api/serializers_/zone.py +12 -11
  8. netbox_dns/api/serializers_/zone_template.py +8 -7
  9. netbox_dns/api/views.py +9 -4
  10. netbox_dns/choices/record.py +4 -2
  11. netbox_dns/choices/zone.py +6 -4
  12. netbox_dns/fields/address.py +2 -1
  13. netbox_dns/fields/network.py +2 -1
  14. netbox_dns/fields/rfc2317.py +7 -3
  15. netbox_dns/filtersets/nameserver.py +3 -2
  16. netbox_dns/filtersets/record.py +10 -9
  17. netbox_dns/filtersets/record_template.py +3 -2
  18. netbox_dns/filtersets/view.py +3 -2
  19. netbox_dns/filtersets/zone.py +24 -22
  20. netbox_dns/filtersets/zone_template.py +15 -14
  21. netbox_dns/forms/nameserver.py +41 -17
  22. netbox_dns/forms/record.py +43 -26
  23. netbox_dns/forms/record_template.py +49 -28
  24. netbox_dns/forms/registrar.py +21 -17
  25. netbox_dns/forms/registration_contact.py +37 -25
  26. netbox_dns/forms/view.py +75 -34
  27. netbox_dns/forms/zone.py +167 -120
  28. netbox_dns/forms/zone_template.py +53 -43
  29. netbox_dns/graphql/schema.py +0 -10
  30. netbox_dns/graphql/types.py +1 -0
  31. netbox_dns/locale/de/LC_MESSAGES/django.mo +0 -0
  32. netbox_dns/locale/en/LC_MESSAGES/django.mo +0 -0
  33. netbox_dns/management/commands/rebuild_dnssync.py +14 -1
  34. netbox_dns/migrations/0010_view_ip_address_filter.py +18 -0
  35. netbox_dns/mixins/object_modification.py +30 -8
  36. netbox_dns/models/nameserver.py +6 -2
  37. netbox_dns/models/record.py +95 -40
  38. netbox_dns/models/record_template.py +16 -8
  39. netbox_dns/models/registrar.py +11 -7
  40. netbox_dns/models/registration_contact.py +23 -11
  41. netbox_dns/models/view.py +54 -5
  42. netbox_dns/models/zone.py +77 -50
  43. netbox_dns/models/zone_template.py +12 -10
  44. netbox_dns/navigation.py +30 -28
  45. netbox_dns/signals/ipam_dnssync.py +25 -18
  46. netbox_dns/tables/ipam_dnssync.py +2 -1
  47. netbox_dns/tables/nameserver.py +2 -0
  48. netbox_dns/tables/record.py +21 -11
  49. netbox_dns/tables/record_template.py +12 -5
  50. netbox_dns/tables/registrar.py +2 -0
  51. netbox_dns/tables/registration_contact.py +2 -0
  52. netbox_dns/tables/view.py +4 -2
  53. netbox_dns/tables/zone.py +15 -2
  54. netbox_dns/tables/zone_template.py +7 -0
  55. netbox_dns/templates/netbox_dns/nameserver.html +6 -5
  56. netbox_dns/templates/netbox_dns/record/managed.html +2 -1
  57. netbox_dns/templates/netbox_dns/record/related.html +26 -14
  58. netbox_dns/templates/netbox_dns/record.html +39 -20
  59. netbox_dns/templates/netbox_dns/recordtemplate.html +27 -15
  60. netbox_dns/templates/netbox_dns/registrar.html +11 -10
  61. netbox_dns/templates/netbox_dns/registrationcontact.html +16 -15
  62. netbox_dns/templates/netbox_dns/view/button.html +2 -1
  63. netbox_dns/templates/netbox_dns/view/prefix.html +7 -4
  64. netbox_dns/templates/netbox_dns/view/related.html +26 -10
  65. netbox_dns/templates/netbox_dns/view.html +22 -9
  66. netbox_dns/templates/netbox_dns/zone/base.html +2 -1
  67. netbox_dns/templates/netbox_dns/zone/child.html +3 -2
  68. netbox_dns/templates/netbox_dns/zone/record.html +3 -2
  69. netbox_dns/templates/netbox_dns/zone/registration.html +8 -7
  70. netbox_dns/templates/netbox_dns/zone.html +28 -30
  71. netbox_dns/templates/netbox_dns/zonetemplate.html +27 -17
  72. netbox_dns/utilities/ipam_dnssync.py +71 -28
  73. netbox_dns/validators/dns_name.py +11 -4
  74. netbox_dns/validators/dns_value.py +9 -4
  75. netbox_dns/validators/rfc2317.py +6 -3
  76. netbox_dns/views/nameserver.py +4 -2
  77. netbox_dns/views/record_template.py +4 -3
  78. netbox_dns/views/registrar.py +3 -1
  79. netbox_dns/views/registration_contact.py +2 -1
  80. netbox_dns/views/view.py +2 -1
  81. netbox_dns/views/zone.py +6 -4
  82. netbox_dns/views/zone_template.py +8 -7
  83. {netbox_plugin_dns-1.1.1.dist-info → netbox_plugin_dns-1.1.3.dist-info}/METADATA +1 -1
  84. netbox_plugin_dns-1.1.3.dist-info/RECORD +150 -0
  85. {netbox_plugin_dns-1.1.1.dist-info → netbox_plugin_dns-1.1.3.dist-info}/WHEEL +1 -1
  86. netbox_plugin_dns-1.1.1.dist-info/RECORD +0 -147
  87. {netbox_plugin_dns-1.1.1.dist-info → netbox_plugin_dns-1.1.3.dist-info}/LICENSE +0 -0
  88. {netbox_plugin_dns-1.1.1.dist-info → netbox_plugin_dns-1.1.3.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  from django import forms
2
+ from django.utils.translation import gettext_lazy as _
2
3
 
3
4
  from netbox.forms import (
4
5
  NetBoxModelBulkEditForm,
@@ -14,7 +15,7 @@ from utilities.forms.fields import (
14
15
  DynamicModelChoiceField,
15
16
  )
16
17
  from utilities.forms.rendering import FieldSet
17
- from tenancy.models import Tenant
18
+ from tenancy.models import Tenant, TenantGroup
18
19
  from tenancy.forms import TenancyForm, TenancyFilterForm
19
20
 
20
21
  from netbox_dns.models import (
@@ -45,18 +46,18 @@ class ZoneTemplateForm(TenancyForm, NetBoxModelForm):
45
46
  )
46
47
 
47
48
  fieldsets = (
48
- FieldSet("name", "description", "nameservers", name="Zone Template"),
49
- FieldSet("record_templates", name="Record Templates"),
49
+ FieldSet("name", "description", "nameservers", name=_("Zone Template")),
50
+ FieldSet("record_templates", name=_("Record Templates")),
50
51
  FieldSet(
51
52
  "registrar",
52
53
  "registrant",
53
54
  "admin_c",
54
55
  "tech_c",
55
56
  "billing_c",
56
- name="Domain Registration",
57
+ name=_("Domain Registration"),
57
58
  ),
58
- FieldSet("tags", name="Tags"),
59
- FieldSet("tenant_group", "tenant", name="Tenancy"),
59
+ FieldSet("tenant_group", "tenant", name=_("Tenancy")),
60
+ FieldSet("tags", name=_("Tags")),
60
61
  )
61
62
 
62
63
  class Meta:
@@ -67,13 +68,14 @@ class ZoneTemplateForm(TenancyForm, NetBoxModelForm):
67
68
  "nameservers",
68
69
  "record_templates",
69
70
  "description",
70
- "tags",
71
71
  "registrar",
72
72
  "registrant",
73
73
  "admin_c",
74
74
  "tech_c",
75
75
  "billing_c",
76
+ "tenant_group",
76
77
  "tenant",
78
+ "tags",
77
79
  )
78
80
 
79
81
 
@@ -81,32 +83,32 @@ class ZoneTemplateFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
81
83
  model = ZoneTemplate
82
84
  fieldsets = (
83
85
  FieldSet("q", "filter_id", "tag"),
84
- FieldSet("name", "nameserver_id", "description", name="Attributes"),
85
- FieldSet("record_template_id", name="Record Templates"),
86
+ FieldSet("name", "nameserver_id", "description", name=_("Attributes")),
87
+ FieldSet("record_template_id", name=_("Record Templates")),
86
88
  FieldSet(
87
89
  "registrar_id",
88
90
  "registrant_id",
89
91
  "admin_c_id",
90
92
  "tech_c_id",
91
93
  "billing_c_id",
92
- name="Registration",
94
+ name=_("Registration"),
93
95
  ),
94
- FieldSet("tenant_group_id", "tenant_id", name="Tenancy"),
96
+ FieldSet("tenant_group_id", "tenant_id", name=_("Tenancy")),
95
97
  )
96
98
 
97
99
  name = forms.CharField(
98
100
  required=False,
99
- label="Template name",
101
+ label=_("Template Name"),
100
102
  )
101
103
  nameserver_id = DynamicModelMultipleChoiceField(
102
104
  queryset=NameServer.objects.all(),
103
105
  required=False,
104
- label="Nameservers",
106
+ label=_("Nameservers"),
105
107
  )
106
108
  record_template_id = DynamicModelMultipleChoiceField(
107
109
  queryset=RecordTemplate.objects.all(),
108
110
  required=False,
109
- label="Record templates",
111
+ label=_("Record Templates"),
110
112
  )
111
113
  description = forms.CharField(
112
114
  required=False,
@@ -114,27 +116,27 @@ class ZoneTemplateFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
114
116
  registrar_id = DynamicModelMultipleChoiceField(
115
117
  queryset=Registrar.objects.all(),
116
118
  required=False,
117
- label="Registrar",
119
+ label=_("Registrar"),
118
120
  )
119
121
  registrant_id = DynamicModelMultipleChoiceField(
120
122
  queryset=RegistrationContact.objects.all(),
121
123
  required=False,
122
- label="Registrant",
124
+ label=_("Registrant"),
123
125
  )
124
126
  admin_c_id = DynamicModelMultipleChoiceField(
125
127
  queryset=RegistrationContact.objects.all(),
126
128
  required=False,
127
- label="Admin-C",
129
+ label=_("Administrative Contact"),
128
130
  )
129
131
  tech_c_id = DynamicModelMultipleChoiceField(
130
132
  queryset=RegistrationContact.objects.all(),
131
133
  required=False,
132
- label="Tech-C",
134
+ label=_("Technical Contact"),
133
135
  )
134
136
  billing_c_id = DynamicModelMultipleChoiceField(
135
137
  queryset=RegistrationContact.objects.all(),
136
138
  required=False,
137
- label="Billing-C",
139
+ label=_("Billing Contact"),
138
140
  )
139
141
  tag = TagFilterField(ZoneTemplate)
140
142
 
@@ -144,64 +146,64 @@ class ZoneTemplateImportForm(NetBoxModelImportForm):
144
146
  queryset=NameServer.objects.all(),
145
147
  to_field_name="name",
146
148
  required=False,
147
- help_text="Name servers for the zone template",
149
+ label=_("Nameservers"),
148
150
  )
149
151
  record_templates = CSVModelMultipleChoiceField(
150
152
  queryset=RecordTemplate.objects.all(),
151
153
  to_field_name="name",
152
154
  required=False,
153
- help_text="Record templates used by this zone template",
155
+ label=_("Record Remplates"),
154
156
  )
155
157
  registrar = CSVModelChoiceField(
156
158
  queryset=Registrar.objects.all(),
157
159
  required=False,
158
160
  to_field_name="name",
159
- help_text="Registrar the domain is registered with",
160
161
  error_messages={
161
- "invalid_choice": "Registrar not found.",
162
+ "invalid_choice": _("Registrar not found."),
162
163
  },
164
+ label=_("Registrar"),
163
165
  )
164
166
  registrant = CSVModelChoiceField(
165
167
  queryset=RegistrationContact.objects.all(),
166
168
  required=False,
167
169
  to_field_name="contact_id",
168
- help_text="Owner of the domain",
169
170
  error_messages={
170
- "invalid_choice": "Registrant contact ID not found",
171
+ "invalid_choice": _("Registrant contact ID not found"),
171
172
  },
173
+ label=_("Registrant"),
172
174
  )
173
175
  admin_c = CSVModelChoiceField(
174
176
  queryset=RegistrationContact.objects.all(),
175
177
  required=False,
176
178
  to_field_name="contact_id",
177
- help_text="Administrative contact for the domain",
178
179
  error_messages={
179
- "invalid_choice": "Administrative contact ID not found",
180
+ "invalid_choice": _("Administrative contact ID not found"),
180
181
  },
182
+ label=_("Administrative Contact"),
181
183
  )
182
184
  tech_c = CSVModelChoiceField(
183
185
  queryset=RegistrationContact.objects.all(),
184
186
  required=False,
185
187
  to_field_name="contact_id",
186
- help_text="Technical contact for the domain",
187
188
  error_messages={
188
- "invalid_choice": "Technical contact ID not found",
189
+ "invalid_choice": _("Technical contact ID not found"),
189
190
  },
191
+ label=_("Technical Contact"),
190
192
  )
191
193
  billing_c = CSVModelChoiceField(
192
194
  queryset=RegistrationContact.objects.all(),
193
195
  required=False,
194
196
  to_field_name="contact_id",
195
- help_text="Billing contact for the domain",
196
197
  error_messages={
197
- "invalid_choice": "Billing contact ID not found",
198
+ "invalid_choice": _("Billing contact ID not found"),
198
199
  },
200
+ label=_("Billing Contact"),
199
201
  )
200
202
  tenant = CSVModelChoiceField(
201
203
  queryset=Tenant.objects.all(),
202
204
  required=False,
203
205
  to_field_name="name",
204
- help_text="Assigned tenant",
206
+ label=_("Tenant"),
205
207
  )
206
208
 
207
209
  class Meta:
@@ -226,42 +228,49 @@ class ZoneTemplateBulkEditForm(NetBoxModelBulkEditForm):
226
228
  nameservers = DynamicModelMultipleChoiceField(
227
229
  queryset=NameServer.objects.all(),
228
230
  required=False,
231
+ label=_("Nameservers"),
229
232
  )
230
233
  record_templates = DynamicModelMultipleChoiceField(
231
234
  queryset=RecordTemplate.objects.all(),
232
235
  required=False,
236
+ label=_("Record Templates"),
233
237
  )
234
238
  description = forms.CharField(max_length=200, required=False)
235
239
  registrar = DynamicModelChoiceField(
236
240
  queryset=Registrar.objects.all(),
237
241
  required=False,
242
+ label=_("Registrar"),
238
243
  )
239
244
  registrant = DynamicModelChoiceField(
240
245
  queryset=RegistrationContact.objects.all(),
241
246
  required=False,
247
+ label=_("Registrant"),
242
248
  )
243
249
  admin_c = DynamicModelChoiceField(
244
250
  queryset=RegistrationContact.objects.all(),
245
251
  required=False,
246
- label="Administrative Contact",
252
+ label=_("Administrative Contact"),
247
253
  )
248
254
  tech_c = DynamicModelChoiceField(
249
255
  queryset=RegistrationContact.objects.all(),
250
256
  required=False,
251
- label="Technical Contact",
257
+ label=_("Technical Contact"),
252
258
  )
253
259
  billing_c = DynamicModelChoiceField(
254
260
  queryset=RegistrationContact.objects.all(),
255
261
  required=False,
256
- label="Billing Contact",
262
+ label=_("Billing Contact"),
257
263
  )
258
- tenant = CSVModelChoiceField(
264
+ tenant_group = DynamicModelChoiceField(
265
+ queryset=TenantGroup.objects.all(),
266
+ required=False,
267
+ label=_("Tenant Group"),
268
+ )
269
+ tenant = DynamicModelChoiceField(
259
270
  queryset=Tenant.objects.all(),
260
271
  required=False,
261
- to_field_name="name",
262
- help_text="Assigned tenant",
272
+ label=_("Tenant"),
263
273
  )
264
- tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False)
265
274
 
266
275
  model = ZoneTemplate
267
276
 
@@ -269,11 +278,11 @@ class ZoneTemplateBulkEditForm(NetBoxModelBulkEditForm):
269
278
  FieldSet(
270
279
  "nameservers",
271
280
  "description",
272
- name="Attributes",
281
+ name=_("Attributes"),
273
282
  ),
274
283
  FieldSet(
275
284
  "record_templates",
276
- name="Record Templates",
285
+ name=_("Record Templates"),
277
286
  ),
278
287
  FieldSet(
279
288
  "registrar",
@@ -281,9 +290,9 @@ class ZoneTemplateBulkEditForm(NetBoxModelBulkEditForm):
281
290
  "admin_c",
282
291
  "tech_c",
283
292
  "billing_c",
284
- name="Domain Registration",
293
+ name=_("Domain Registration"),
285
294
  ),
286
- FieldSet("tenant_group", "tenant", name="Tenancy"),
295
+ FieldSet("tenant_group", "tenant", name=_("Tenancy")),
287
296
  )
288
297
 
289
298
  nullable_fields = (
@@ -295,4 +304,5 @@ class ZoneTemplateBulkEditForm(NetBoxModelBulkEditForm):
295
304
  "admin_c",
296
305
  "tech_c",
297
306
  "billing_c",
307
+ "tenant",
298
308
  )
@@ -3,16 +3,6 @@ from typing import List
3
3
  import strawberry
4
4
  import strawberry_django
5
5
 
6
- from netbox_dns.models import (
7
- NameServer,
8
- View,
9
- Zone,
10
- Record,
11
- RegistrationContact,
12
- Registrar,
13
- ZoneTemplate,
14
- RecordTemplate,
15
- )
16
6
  from .types import (
17
7
  NetBoxDNSNameServerType,
18
8
  NetBoxDNSViewType,
@@ -43,6 +43,7 @@ class NetBoxDNSViewType(NetBoxObjectType):
43
43
  description: str
44
44
  tenant: Annotated["TenantType", strawberry.lazy("tenancy.graphql.types")] | None
45
45
  prefixes: List[Annotated["PrefixType", strawberry.lazy("ipam.graphql.types")]]
46
+ ip_address_filter: str | None
46
47
 
47
48
 
48
49
  @strawberry_django.type(Zone, fields="__all__", filters=NetBoxDNSZoneFilter)
@@ -8,6 +8,13 @@ from netbox_dns.utilities import update_dns_records
8
8
  class Command(BaseCommand):
9
9
  help = "Rebuild DNSsync relationships between IP addresses and records"
10
10
 
11
+ def add_arguments(self, parser):
12
+ parser.add_argument(
13
+ "--force",
14
+ action="store_true",
15
+ help="Update records even if DNS name was not changed (required for rebuilding filtered views",
16
+ )
17
+
11
18
  def handle(self, *model_names, **options):
12
19
  ip_addresses = IPAddress.objects.all()
13
20
  for ip_address in ip_addresses:
@@ -15,4 +22,10 @@ class Command(BaseCommand):
15
22
  self.stdout.write(
16
23
  f"Updating DNS records for IP Address {ip_address}, VRF {ip_address.vrf}"
17
24
  )
18
- update_dns_records(ip_address)
25
+ if (
26
+ update_dns_records(ip_address, force=options.get("force"))
27
+ and options.get("verbosity") >= 1
28
+ ):
29
+ self.stdout.write(
30
+ f"Updated DNS records for IP Address {ip_address}, VRF {ip_address.vrf}"
31
+ )
@@ -0,0 +1,18 @@
1
+ # Generated by Django 5.0.9 on 2024-09-18 13:12
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("netbox_dns", "0009_rename_contact_registrationcontact"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name="view",
15
+ name="ip_address_filter",
16
+ field=models.JSONField(blank=True, null=True),
17
+ ),
18
+ ]
@@ -17,15 +17,37 @@ class ObjectModificationMixin:
17
17
 
18
18
  self.__class__.check_fields.add("custom_field_data")
19
19
 
20
+ self._save_field_values()
21
+
22
+ def _save_field_values(self):
23
+ for field in self.check_fields:
24
+ if f"{field}_id" in self.__dict__:
25
+ setattr(self, f"_saved_{field}_id", self.__dict__.get(f"{field}_id"))
26
+ else:
27
+ setattr(self, f"_saved_{field}", self.__dict__.get(field))
28
+
29
+ def save(self, *args, **kwargs):
30
+ super().save(*args, **kwargs)
31
+
32
+ self._save_field_values()
33
+
20
34
  @property
21
35
  def changed_fields(self):
22
- if self.pk is None:
36
+ if self._state.adding:
23
37
  return None
24
38
 
25
- saved = self.__class__.objects.get(pk=self.pk)
26
-
27
- return {
28
- field
29
- for field in self.check_fields
30
- if getattr(self, field) != getattr(saved, field)
31
- }
39
+ _changed_fields = set()
40
+ for field in self.check_fields:
41
+ if f"_saved_{field}_id" in self.__dict__:
42
+ if self.__dict__.get(f"_saved_{field}_id") != self.__dict__.get(
43
+ f"{field}_id"
44
+ ):
45
+ _changed_fields.add(field)
46
+ else:
47
+ if self.__dict__.get(f"_saved_{field}") != self.__dict__.get(field):
48
+ _changed_fields.add(field)
49
+
50
+ return _changed_fields
51
+
52
+ def get_saved_value(self, field):
53
+ return self.__dict__.get(f"_saved_{field}")
@@ -4,6 +4,7 @@ from django.core.exceptions import ValidationError
4
4
  from django.db import models, transaction
5
5
  from django.db.models import Q
6
6
  from django.urls import reverse
7
+ from django.utils.translation import gettext_lazy as _
7
8
 
8
9
  from netbox.models import NetBoxModel
9
10
  from netbox.search import SearchIndex, register_search
@@ -29,14 +30,17 @@ __all__ = (
29
30
 
30
31
  class NameServer(ObjectModificationMixin, ContactsMixin, NetBoxModel):
31
32
  name = models.CharField(
33
+ verbose_name=_("Name"),
32
34
  unique=True,
33
35
  max_length=255,
34
36
  )
35
37
  description = models.CharField(
38
+ verbose_name=_("Description"),
36
39
  max_length=200,
37
40
  blank=True,
38
41
  )
39
42
  tenant = models.ForeignKey(
43
+ verbose_name=_("Tenant"),
40
44
  to="tenancy.Tenant",
41
45
  on_delete=models.PROTECT,
42
46
  related_name="netbox_dns_nameservers",
@@ -50,8 +54,8 @@ class NameServer(ObjectModificationMixin, ContactsMixin, NetBoxModel):
50
54
  )
51
55
 
52
56
  class Meta:
53
- verbose_name = "Nameserver"
54
- verbose_name_plural = "Nameservers"
57
+ verbose_name = _("Nameserver")
58
+ verbose_name_plural = _("Nameservers")
55
59
 
56
60
  ordering = ("name",)
57
61