netbox-plugin-dns 1.1.2__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 (84) hide show
  1. netbox_dns/__init__.py +6 -5
  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 +2 -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 +49 -27
  27. netbox_dns/forms/zone.py +167 -120
  28. netbox_dns/forms/zone_template.py +53 -43
  29. netbox_dns/locale/de/LC_MESSAGES/django.mo +0 -0
  30. netbox_dns/locale/en/LC_MESSAGES/django.mo +0 -0
  31. netbox_dns/management/commands/rebuild_dnssync.py +14 -1
  32. netbox_dns/models/nameserver.py +6 -2
  33. netbox_dns/models/record.py +63 -30
  34. netbox_dns/models/record_template.py +16 -8
  35. netbox_dns/models/registrar.py +11 -7
  36. netbox_dns/models/registration_contact.py +23 -11
  37. netbox_dns/models/view.py +15 -6
  38. netbox_dns/models/zone.py +65 -43
  39. netbox_dns/models/zone_template.py +12 -10
  40. netbox_dns/navigation.py +30 -28
  41. netbox_dns/signals/ipam_dnssync.py +21 -14
  42. netbox_dns/tables/ipam_dnssync.py +2 -1
  43. netbox_dns/tables/nameserver.py +2 -0
  44. netbox_dns/tables/record.py +21 -11
  45. netbox_dns/tables/record_template.py +12 -5
  46. netbox_dns/tables/registrar.py +2 -0
  47. netbox_dns/tables/registration_contact.py +2 -0
  48. netbox_dns/tables/view.py +3 -1
  49. netbox_dns/tables/zone.py +15 -2
  50. netbox_dns/tables/zone_template.py +7 -0
  51. netbox_dns/templates/netbox_dns/nameserver.html +6 -5
  52. netbox_dns/templates/netbox_dns/record/managed.html +2 -1
  53. netbox_dns/templates/netbox_dns/record/related.html +26 -14
  54. netbox_dns/templates/netbox_dns/record.html +39 -20
  55. netbox_dns/templates/netbox_dns/recordtemplate.html +27 -15
  56. netbox_dns/templates/netbox_dns/registrar.html +11 -10
  57. netbox_dns/templates/netbox_dns/registrationcontact.html +16 -15
  58. netbox_dns/templates/netbox_dns/view/button.html +2 -1
  59. netbox_dns/templates/netbox_dns/view/prefix.html +7 -4
  60. netbox_dns/templates/netbox_dns/view/related.html +26 -10
  61. netbox_dns/templates/netbox_dns/view.html +11 -14
  62. netbox_dns/templates/netbox_dns/zone/base.html +2 -1
  63. netbox_dns/templates/netbox_dns/zone/child.html +3 -2
  64. netbox_dns/templates/netbox_dns/zone/record.html +3 -2
  65. netbox_dns/templates/netbox_dns/zone/registration.html +8 -7
  66. netbox_dns/templates/netbox_dns/zone.html +28 -30
  67. netbox_dns/templates/netbox_dns/zonetemplate.html +27 -17
  68. netbox_dns/utilities/ipam_dnssync.py +15 -4
  69. netbox_dns/validators/dns_name.py +11 -4
  70. netbox_dns/validators/dns_value.py +9 -4
  71. netbox_dns/validators/rfc2317.py +6 -3
  72. netbox_dns/views/nameserver.py +4 -2
  73. netbox_dns/views/record_template.py +4 -3
  74. netbox_dns/views/registrar.py +3 -1
  75. netbox_dns/views/registration_contact.py +2 -1
  76. netbox_dns/views/view.py +2 -1
  77. netbox_dns/views/zone.py +6 -4
  78. netbox_dns/views/zone_template.py +8 -7
  79. {netbox_plugin_dns-1.1.2.dist-info → netbox_plugin_dns-1.1.3.dist-info}/METADATA +1 -1
  80. netbox_plugin_dns-1.1.3.dist-info/RECORD +150 -0
  81. netbox_plugin_dns-1.1.2.dist-info/RECORD +0 -148
  82. {netbox_plugin_dns-1.1.2.dist-info → netbox_plugin_dns-1.1.3.dist-info}/LICENSE +0 -0
  83. {netbox_plugin_dns-1.1.2.dist-info → netbox_plugin_dns-1.1.3.dist-info}/WHEEL +0 -0
  84. {netbox_plugin_dns-1.1.2.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
  )
@@ -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
+ )
@@ -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
 
@@ -8,6 +8,7 @@ from django.db import transaction, models
8
8
  from django.db.models import Q, ExpressionWrapper, BooleanField, Min
9
9
  from django.urls import reverse
10
10
  from django.conf import settings
11
+ from django.utils.translation import gettext_lazy as _
11
12
 
12
13
  from netbox.models import NetBoxModel
13
14
  from ipam.models import IPAddress
@@ -125,58 +126,67 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
125
126
  )
126
127
 
127
128
  name = models.CharField(
129
+ verbose_name=_("Name"),
128
130
  max_length=255,
129
131
  )
130
132
  zone = models.ForeignKey(
131
- "Zone",
133
+ verbose_name=_("Zone"),
134
+ to="Zone",
132
135
  on_delete=models.CASCADE,
133
136
  )
134
137
  fqdn = models.CharField(
138
+ verbose_name=_("FQDN"),
135
139
  max_length=255,
136
140
  null=True,
137
141
  blank=True,
138
142
  default=None,
139
143
  )
140
144
  type = models.CharField(
145
+ verbose_name=_("Type"),
141
146
  choices=RecordTypeChoices,
142
147
  max_length=10,
143
148
  )
144
149
  value = models.CharField(
150
+ verbose_name=_("Value"),
145
151
  max_length=65535,
146
152
  )
147
153
  status = models.CharField(
154
+ verbose_name=_("Status"),
148
155
  max_length=50,
149
156
  choices=RecordStatusChoices,
150
157
  default=RecordStatusChoices.STATUS_ACTIVE,
151
158
  blank=False,
152
159
  )
153
160
  ttl = models.PositiveIntegerField(
154
- verbose_name="TTL",
161
+ verbose_name=_("TTL"),
155
162
  null=True,
156
163
  blank=True,
157
164
  )
158
165
  managed = models.BooleanField(
166
+ verbose_name=_("Managed"),
159
167
  null=False,
160
168
  default=False,
161
169
  )
162
170
  ptr_record = models.OneToOneField(
163
- "self",
171
+ verbose_name="PTR Record",
172
+ to="self",
164
173
  on_delete=models.SET_NULL,
165
174
  related_name="address_record",
166
- verbose_name="PTR record",
167
175
  null=True,
168
176
  blank=True,
169
177
  )
170
178
  disable_ptr = models.BooleanField(
171
- verbose_name="Disable PTR",
172
- help_text="Disable PTR record creation",
179
+ verbose_name=_("Disable PTR"),
180
+ help_text=_("Disable PTR record creation"),
173
181
  default=False,
174
182
  )
175
183
  description = models.CharField(
184
+ verbose_name=_("Description"),
176
185
  max_length=200,
177
186
  blank=True,
178
187
  )
179
188
  tenant = models.ForeignKey(
189
+ verbose_name=_("Tenant"),
180
190
  to="tenancy.Tenant",
181
191
  on_delete=models.PROTECT,
182
192
  related_name="netbox_dns_records",
@@ -184,13 +194,13 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
184
194
  null=True,
185
195
  )
186
196
  ip_address = AddressField(
187
- verbose_name="Related IP Address",
188
- help_text="IP address related to an address (A/AAAA) or PTR record",
197
+ verbose_name=_("Related IP Address"),
198
+ help_text=_("IP address related to an address (A/AAAA) or PTR record"),
189
199
  blank=True,
190
200
  null=True,
191
201
  )
192
202
  ipam_ip_address = models.ForeignKey(
193
- verbose_name="IPAM IP Address",
203
+ verbose_name=_("IPAM IP Address"),
194
204
  to="ipam.IPAddress",
195
205
  on_delete=models.CASCADE,
196
206
  related_name="netbox_dns_records",
@@ -198,10 +208,10 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
198
208
  null=True,
199
209
  )
200
210
  rfc2317_cname_record = models.ForeignKey(
201
- "self",
211
+ verbose_name=_("RFC2317 CNAME Record"),
212
+ to="self",
202
213
  on_delete=models.SET_NULL,
203
214
  related_name="rfc2317_ptr_records",
204
- verbose_name="RFC2317 CNAME record",
205
215
  null=True,
206
216
  blank=True,
207
217
  )
@@ -221,8 +231,8 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
221
231
  )
222
232
 
223
233
  class Meta:
224
- verbose_name = "Record"
225
- verbose_name_plural = "Records"
234
+ verbose_name = _("Record")
235
+ verbose_name_plural = _("Records")
226
236
 
227
237
  ordering = (
228
238
  "fqdn",
@@ -537,7 +547,9 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
537
547
  if not fqdn.is_subdomain(_zone):
538
548
  raise ValidationError(
539
549
  {
540
- "name": f"{self.name} is not a name in {zone.name}",
550
+ "name": _("{name} is not a name in {zone}").format(
551
+ name=self.name, zone=zone.name
552
+ ),
541
553
  }
542
554
  )
543
555
 
@@ -581,13 +593,13 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
581
593
  {
582
594
  "name": exc,
583
595
  }
584
- ) from None
596
+ )
585
597
 
586
598
  def validate_value(self):
587
599
  try:
588
600
  validate_record_value(self.type, self.value)
589
601
  except ValidationError as exc:
590
- raise ValidationError({"value": exc}) from None
602
+ raise ValidationError({"value": exc})
591
603
 
592
604
  def check_unique_record(self, new_zone=None):
593
605
  if not get_plugin_config("netbox_dns", "enforce_unique_records", False):
@@ -621,9 +633,13 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
621
633
 
622
634
  raise ValidationError(
623
635
  {
624
- "value": f"There is already an active {self.type} record for name {self.name} in zone {self.zone} with value {self.value}."
636
+ "value": _(
637
+ "There is already an active {type} record for name {name} in zone {zone} with value {value}."
638
+ ).format(
639
+ type=self.type, name=self.name, zone=self.zone, value=self.value
640
+ )
625
641
  }
626
- ) from None
642
+ )
627
643
 
628
644
  def handle_conflicting_address_records(self):
629
645
  if self.ipam_ip_address is None or not self.is_active:
@@ -680,9 +696,16 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
680
696
  conflicting_ttls = ", ".join({str(record.ttl) for record in records})
681
697
  raise ValidationError(
682
698
  {
683
- "ttl": f"There is at least one active {self.type} record for name {self.name} in zone {self.zone} and TTL is different ({conflicting_ttls})."
699
+ "ttl": _(
700
+ "There is at least one active {type} record for name {name} in zone {zone} and TTL is different ({ttls})."
701
+ ).format(
702
+ type=self.type,
703
+ name=self.name,
704
+ zone=self.zone,
705
+ ttls=conflicting_ttls,
706
+ )
684
707
  }
685
- ) from None
708
+ )
686
709
 
687
710
  def update_rrset_ttl(self, ttl=None):
688
711
  if self._state.adding:
@@ -760,24 +783,30 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
760
783
  ):
761
784
  raise ValidationError(
762
785
  {
763
- "value": f"There is already an active record for name {ptr_cname_name} in zone {ptr_cname_zone}, RFC2317 CNAME is not allowed."
786
+ "value": _(
787
+ "There is already an active record for name {name} in zone {zone}, RFC2317 CNAME is not allowed."
788
+ ).format(name=ptr_cname_name, zone=ptr_cname_zone)
764
789
  }
765
- ) from None
790
+ )
766
791
 
767
792
  if self.type == RecordTypeChoices.SOA and self.name != "@":
768
793
  raise ValidationError(
769
794
  {
770
- "name": "SOA records are only allowed with name @ and are created automatically by NetBox DNS"
795
+ "name": _(
796
+ "SOA records are only allowed with name @ and are created automatically by NetBox DNS"
797
+ )
771
798
  }
772
- ) from None
799
+ )
773
800
 
774
801
  if self.type == RecordTypeChoices.CNAME:
775
802
  if records.exclude(type=RecordTypeChoices.NSEC).exists():
776
803
  raise ValidationError(
777
804
  {
778
- "type": f"There is already an active record for name {self.name} in zone {self.zone}, CNAME is not allowed."
805
+ "type": _(
806
+ "There is already an active record for name {name} in zone {zone}, CNAME is not allowed."
807
+ ).format(name=self.name, zone=self.zone)
779
808
  }
780
- ) from None
809
+ )
781
810
 
782
811
  elif (
783
812
  records.filter(type=RecordTypeChoices.CNAME).exists()
@@ -785,17 +814,21 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
785
814
  ):
786
815
  raise ValidationError(
787
816
  {
788
- "type": f"There is already an active CNAME record for name {self.name} in zone {self.zone}, no other record allowed."
817
+ "type": _(
818
+ "There is already an active CNAME record for name {name} in zone {zone}, no other record allowed."
819
+ ).format(name=self.name, zone=self.zone)
789
820
  }
790
- ) from None
821
+ )
791
822
 
792
823
  elif self.type in RecordTypeChoices.SINGLETONS:
793
824
  if records.filter(type=self.type).exists():
794
825
  raise ValidationError(
795
826
  {
796
- "type": f"There is already an active {self.type} record for name {self.name} in zone {self.zone}, more than one are not allowed."
827
+ "type": _(
828
+ "There is already an active {type} record for name {name} in zone {zone}, more than one are not allowed."
829
+ ).format(type=self.type, name=self.name, zone=self.zone)
797
830
  }
798
- ) from None
831
+ )
799
832
 
800
833
  super().clean(*args, **kwargs)
801
834