netbox-plugin-dns 1.1.3__py3-none-any.whl → 1.1.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 (39) hide show
  1. netbox_dns/__init__.py +26 -5
  2. netbox_dns/api/views.py +1 -1
  3. netbox_dns/choices/zone.py +2 -0
  4. netbox_dns/fields/address.py +3 -21
  5. netbox_dns/filtersets/record.py +3 -0
  6. netbox_dns/filtersets/zone.py +1 -2
  7. netbox_dns/forms/record.py +29 -13
  8. netbox_dns/forms/view.py +2 -3
  9. netbox_dns/forms/zone.py +15 -10
  10. netbox_dns/forms/zone_template.py +5 -5
  11. netbox_dns/locale/de/LC_MESSAGES/django.mo +0 -0
  12. netbox_dns/models/nameserver.py +4 -8
  13. netbox_dns/models/record.py +26 -41
  14. netbox_dns/models/record_template.py +5 -5
  15. netbox_dns/models/view.py +2 -3
  16. netbox_dns/models/zone.py +96 -39
  17. netbox_dns/signals/ipam_dnssync.py +1 -1
  18. netbox_dns/tables/record.py +14 -2
  19. netbox_dns/tables/zone.py +1 -2
  20. netbox_dns/template_content.py +16 -0
  21. netbox_dns/templates/netbox_dns/record.html +12 -0
  22. netbox_dns/templates/netbox_dns/view.html +1 -1
  23. netbox_dns/templates/netbox_dns/zone/delegation_record.html +18 -0
  24. netbox_dns/templates/netbox_dns/zone.html +1 -1
  25. netbox_dns/utilities/__init__.py +1 -0
  26. netbox_dns/utilities/dns.py +12 -0
  27. netbox_dns/utilities/ipam_dnssync.py +10 -13
  28. netbox_dns/validators/dns_value.py +47 -6
  29. netbox_dns/views/nameserver.py +3 -3
  30. netbox_dns/views/record.py +44 -11
  31. netbox_dns/views/registrar.py +1 -1
  32. netbox_dns/views/registration_contact.py +1 -1
  33. netbox_dns/views/view.py +2 -2
  34. netbox_dns/views/zone.py +49 -20
  35. {netbox_plugin_dns-1.1.3.dist-info → netbox_plugin_dns-1.1.5.dist-info}/METADATA +3 -2
  36. {netbox_plugin_dns-1.1.3.dist-info → netbox_plugin_dns-1.1.5.dist-info}/RECORD +39 -37
  37. {netbox_plugin_dns-1.1.3.dist-info → netbox_plugin_dns-1.1.5.dist-info}/WHEEL +1 -1
  38. {netbox_plugin_dns-1.1.3.dist-info → netbox_plugin_dns-1.1.5.dist-info}/LICENSE +0 -0
  39. {netbox_plugin_dns-1.1.3.dist-info → netbox_plugin_dns-1.1.5.dist-info}/top_level.txt +0 -0
netbox_dns/__init__.py CHANGED
@@ -1,12 +1,18 @@
1
- from django.conf import settings
2
1
  from django.utils.translation import gettext_lazy as _
2
+ from django.core.exceptions import ImproperlyConfigured
3
3
 
4
4
  from netbox.plugins import PluginConfig
5
+ from netbox.plugins.utils import get_plugin_config
5
6
  from ipam.choices import IPAddressStatusChoices
6
7
 
7
- from netbox_dns.choices import RecordTypeChoices
8
+ from netbox_dns.choices import RecordTypeChoices, RecordStatusChoices, ZoneStatusChoices
8
9
 
9
- __version__ = "1.1.3"
10
+ __version__ = "1.1.5"
11
+
12
+
13
+ def _check_list(setting):
14
+ if not isinstance(get_plugin_config("netbox_dns", setting), list):
15
+ raise ImproperlyConfigured(f"{setting} must be a list")
10
16
 
11
17
 
12
18
  class DNSConfig(PluginConfig):
@@ -26,6 +32,13 @@ class DNSConfig(PluginConfig):
26
32
  "zone_soa_retry": 7200,
27
33
  "zone_soa_expire": 2419200,
28
34
  "zone_soa_minimum": 3600,
35
+ "zone_active_status": [
36
+ ZoneStatusChoices.STATUS_ACTIVE,
37
+ ZoneStatusChoices.STATUS_DYNAMIC,
38
+ ],
39
+ "record_active_status": [
40
+ RecordStatusChoices.STATUS_ACTIVE,
41
+ ],
29
42
  "dnssync_disabled": False,
30
43
  "dnssync_ipaddress_active_status": [
31
44
  IPAddressStatusChoices.STATUS_ACTIVE,
@@ -46,7 +59,7 @@ class DNSConfig(PluginConfig):
46
59
  "enable_root_zones": False,
47
60
  "enforce_unique_records": True,
48
61
  "enforce_unique_rrset_ttl": True,
49
- "menu_name": "NetBox DNS",
62
+ "menu_name": "DNS",
50
63
  "top_level_menu": True,
51
64
  }
52
65
  base_url = "netbox-dns"
@@ -54,10 +67,18 @@ class DNSConfig(PluginConfig):
54
67
  def ready(self):
55
68
  super().ready()
56
69
 
57
- if not settings.PLUGINS_CONFIG["netbox_dns"].get("dnssync_disabled"):
70
+ if not get_plugin_config("netbox_dns", "dnssync_disabled"):
58
71
  import netbox_dns.signals.ipam_dnssync # noqa: F401
59
72
  import netbox_dns.tables.ipam_dnssync # noqa: F401
60
73
 
74
+ for setting in (
75
+ "zone_active_status",
76
+ "record_active_status",
77
+ "dnssync_ipaddress_active_status",
78
+ "tolerate_leading_underscore_types",
79
+ ):
80
+ _check_list(setting)
81
+
61
82
 
62
83
  #
63
84
  # Initialize plugin config
netbox_dns/api/views.py CHANGED
@@ -71,7 +71,7 @@ class NameServerViewSet(NetBoxModelViewSet):
71
71
 
72
72
 
73
73
  class RecordViewSet(NetBoxModelViewSet):
74
- queryset = Record.objects.all().prefetch_related("zone", "zone__view", "tenant")
74
+ queryset = Record.objects.prefetch_related("zone", "zone__view", "tenant")
75
75
  serializer_class = RecordSerializer
76
76
  filterset_class = RecordFilterSet
77
77
 
@@ -13,10 +13,12 @@ class ZoneStatusChoices(ChoiceSet):
13
13
  STATUS_RESERVED = "reserved"
14
14
  STATUS_DEPRECATED = "deprecated"
15
15
  STATUS_PARKED = "parked"
16
+ STATUS_DYNAMIC = "dynamic"
16
17
 
17
18
  CHOICES = [
18
19
  (STATUS_ACTIVE, _("Active"), "blue"),
19
20
  (STATUS_RESERVED, _("Reserved"), "cyan"),
20
21
  (STATUS_DEPRECATED, _("Deprecated"), "red"),
21
22
  (STATUS_PARKED, _("Parked"), "gray"),
23
+ (STATUS_DYNAMIC, _("Dynamic"), "orange"),
22
24
  ]
@@ -1,31 +1,13 @@
1
- from django import forms
2
1
  from django.db import models
3
2
  from django.core.exceptions import ValidationError
4
3
  from django.utils.translation import gettext_lazy as _
5
4
 
6
5
  from netaddr import AddrFormatError, IPAddress
7
6
 
7
+ from ipam.formfields import IPAddressFormField
8
8
 
9
- __all__ = (
10
- "AddressFormField",
11
- "AddressField",
12
- )
13
9
 
14
-
15
- class AddressFormField(forms.Field):
16
- def to_python(self, value):
17
- if not value:
18
- return None
19
-
20
- if isinstance(value, IPAddress):
21
- return value
22
-
23
- try:
24
- ip_address = IPAddress(value)
25
- except AddrFormatError as exc:
26
- raise ValidationError(exc)
27
-
28
- return ip_address
10
+ __all__ = ("AddressField",)
29
11
 
30
12
 
31
13
  class AddressField(models.Field):
@@ -58,7 +40,7 @@ class AddressField(models.Field):
58
40
  return str(self.to_python(value))
59
41
 
60
42
  def form_class(self):
61
- return AddressFormField
43
+ return IPAddressFormField
62
44
 
63
45
  def formfield(self, **kwargs):
64
46
  defaults = {"form_class": self.form_class()}
@@ -76,6 +76,9 @@ class RecordFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
76
76
  method="filter_ip_address",
77
77
  label=_("IP Address"),
78
78
  )
79
+ active = django_filters.BooleanFilter(
80
+ label=_("Record is active"),
81
+ )
79
82
 
80
83
  managed = django_filters.BooleanFilter()
81
84
 
@@ -3,7 +3,6 @@ import netaddr
3
3
  import django_filters
4
4
  from django.db.models import Q
5
5
  from django.utils.translation import gettext as _
6
- from django.utils.translation import pgettext as _p
7
6
 
8
7
  from netbox.filtersets import NetBoxModelFilterSet
9
8
  from tenancy.filtersets import TenancyFilterSet
@@ -28,7 +27,7 @@ class ZoneFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
28
27
  queryset=View.objects.all(),
29
28
  field_name="view__name",
30
29
  to_field_name="name",
31
- label=_p("DNS", "View"),
30
+ label=_("View"),
32
31
  )
33
32
  # DEPRECATED: Remove in 1.1
34
33
  name_server_id = django_filters.ModelMultipleChoiceFilter(
@@ -1,6 +1,5 @@
1
1
  from django import forms
2
2
  from django.utils.translation import gettext_lazy as _
3
- from django.utils.translation import pgettext_lazy as _p
4
3
 
5
4
  from netbox.forms import (
6
5
  NetBoxModelBulkEditForm,
@@ -38,12 +37,6 @@ class RecordForm(TenancyForm, NetBoxModelForm):
38
37
  def __init__(self, *args, **kwargs):
39
38
  super().__init__(*args, **kwargs)
40
39
 
41
- initial_zone_id = self.initial.get("zone")
42
- if initial_zone_id is not None:
43
- self.initial["view"] = Zone.objects.get(pk=initial_zone_id).view
44
- else:
45
- self.initial["view"] = View.get_default_view()
46
-
47
40
  initial_name = self.initial.get("name")
48
41
  if initial_name:
49
42
  self.initial["name"] = name_to_unicode(initial_name)
@@ -51,7 +44,10 @@ class RecordForm(TenancyForm, NetBoxModelForm):
51
44
  view = DynamicModelChoiceField(
52
45
  queryset=View.objects.all(),
53
46
  required=False,
54
- label=_p("DNS", "View"),
47
+ initial_params={
48
+ "zone": "$zone",
49
+ },
50
+ label=_("View"),
55
51
  )
56
52
  zone = DynamicModelChoiceField(
57
53
  queryset=Zone.objects.all(),
@@ -111,6 +107,7 @@ class RecordFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
111
107
  fieldsets = (
112
108
  FieldSet("q", "filter_id", "tag"),
113
109
  FieldSet(
110
+ "view_id",
114
111
  "zone_id",
115
112
  "name",
116
113
  "fqdn",
@@ -118,6 +115,7 @@ class RecordFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
118
115
  "value",
119
116
  "disable_ptr",
120
117
  "status",
118
+ "active",
121
119
  "description",
122
120
  name=_("Attributes"),
123
121
  ),
@@ -151,10 +149,23 @@ class RecordFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
151
149
  required=False,
152
150
  label=_("Status"),
153
151
  )
152
+ view_id = DynamicModelMultipleChoiceField(
153
+ queryset=View.objects.all(),
154
+ required=False,
155
+ label=_("View"),
156
+ )
154
157
  zone_id = DynamicModelMultipleChoiceField(
155
158
  queryset=Zone.objects.all(),
156
159
  required=False,
157
160
  label=_("Zone"),
161
+ query_params={
162
+ "view_id": "$view_id",
163
+ },
164
+ )
165
+ active = forms.NullBooleanField(
166
+ required=False,
167
+ widget=forms.Select(choices=BOOLEAN_WITH_BLANK_CHOICES),
168
+ label=_("Active"),
158
169
  )
159
170
  description = forms.CharField(
160
171
  required=False,
@@ -175,23 +186,28 @@ class RecordImportForm(NetBoxModelImportForm):
175
186
  pass
176
187
 
177
188
  if view is not None:
178
- self.fields["zone"].queryset = Zone.objects.filter(view=view)
189
+ self.fields["zone"].queryset = view.zone_set
179
190
  else:
180
- self.fields["zone"].queryset = Zone.objects.filter(
181
- view=View.get_default_view()
182
- )
191
+ self.fields["zone"].queryset = View.get_default_view().zone_set
183
192
 
184
193
  zone = CSVModelChoiceField(
185
194
  queryset=Zone.objects.all(),
186
195
  to_field_name="name",
187
196
  required=True,
188
197
  label=_("Zone"),
198
+ error_messages={
199
+ "invalid_choice": _("Zone %(value)s not found"),
200
+ },
189
201
  )
190
202
  view = CSVModelChoiceField(
191
203
  queryset=View.objects.all(),
192
204
  to_field_name="name",
193
205
  required=False,
194
- label=_p("DNS", "View"),
206
+ label=_("View"),
207
+ error_messages={
208
+ "invalid_choice": _("View %(value)s not found"),
209
+ },
210
+ help_text=_("This field is required if the zone is not in the default view"),
195
211
  )
196
212
  type = CSVChoiceField(
197
213
  choices=RecordTypeChoices,
netbox_dns/forms/view.py CHANGED
@@ -3,7 +3,6 @@ from django.conf import settings
3
3
  from django.core.exceptions import ValidationError, FieldError
4
4
  from django.db.models import Q, Count
5
5
  from django.utils.translation import gettext_lazy as _
6
- from django.utils.translation import pgettext_lazy as _p
7
6
 
8
7
  from netbox.forms import (
9
8
  NetBoxModelBulkEditForm,
@@ -127,7 +126,7 @@ class ViewForm(ViewPrefixUpdateMixin, TenancyForm, NetBoxModelForm):
127
126
  )
128
127
 
129
128
  fieldsets = (
130
- FieldSet("name", "default_view", "description", name=_p("DNS", "View")),
129
+ FieldSet("name", "default_view", "description", name=_("View")),
131
130
  FieldSet("prefixes", "ip_address_filter"),
132
131
  FieldSet("tenant_group", "tenant", name=_("Tenancy")),
133
132
  FieldSet("tags", name=_("Tags")),
@@ -302,7 +301,7 @@ class ViewPrefixEditForm(forms.ModelForm):
302
301
 
303
302
  if not views.exists():
304
303
  if (parent := prefix.get_parents().last()) is not None:
305
- check_views = parent.netbox_dns_views.all().difference(old_views)
304
+ check_views = parent.netbox_dns_views.difference(old_views)
306
305
 
307
306
  else:
308
307
  check_views = views.difference(old_views)
netbox_dns/forms/zone.py CHANGED
@@ -4,7 +4,6 @@ from django.conf import settings
4
4
  from django.core.validators import MinValueValidator, MaxValueValidator
5
5
  from django.core.exceptions import ValidationError
6
6
  from django.utils.translation import gettext_lazy as _
7
- from django.utils.translation import pgettext_lazy as _p
8
7
 
9
8
  from netbox.forms import (
10
9
  NetBoxModelBulkEditForm,
@@ -354,6 +353,7 @@ class ZoneFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
354
353
  "status",
355
354
  "name",
356
355
  "nameserver_id",
356
+ "active",
357
357
  "description",
358
358
  name=_("Attributes"),
359
359
  ),
@@ -384,7 +384,7 @@ class ZoneFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
384
384
  view_id = DynamicModelMultipleChoiceField(
385
385
  queryset=View.objects.all(),
386
386
  required=False,
387
- label=_p("DNS", "View"),
387
+ label=_("View"),
388
388
  )
389
389
  status = forms.MultipleChoiceField(
390
390
  choices=ZoneStatusChoices,
@@ -400,6 +400,11 @@ class ZoneFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
400
400
  required=False,
401
401
  label=_("Nameservers"),
402
402
  )
403
+ active = forms.NullBooleanField(
404
+ required=False,
405
+ widget=forms.Select(choices=BOOLEAN_WITH_BLANK_CHOICES),
406
+ label=_("Active"),
407
+ )
403
408
  description = forms.CharField(
404
409
  required=False,
405
410
  label=_("Description"),
@@ -470,7 +475,7 @@ class ZoneImportForm(ZoneTemplateUpdateMixin, NetBoxModelImportForm):
470
475
  required=False,
471
476
  to_field_name="name",
472
477
  error_messages={
473
- "invalid_choice": _("View not found."),
478
+ "invalid_choice": _("View %(value)s not found"),
474
479
  },
475
480
  label=_("View"),
476
481
  )
@@ -499,7 +504,7 @@ class ZoneImportForm(ZoneTemplateUpdateMixin, NetBoxModelImportForm):
499
504
  required=False,
500
505
  to_field_name="name",
501
506
  error_messages={
502
- "invalid_choice": _("Nameserver not found."),
507
+ "invalid_choice": _("Nameserver %(value)s not found"),
503
508
  },
504
509
  help_text=_("Primary nameserver for the zone"),
505
510
  label=_("SOA MName"),
@@ -554,7 +559,7 @@ class ZoneImportForm(ZoneTemplateUpdateMixin, NetBoxModelImportForm):
554
559
  required=False,
555
560
  to_field_name="name",
556
561
  error_messages={
557
- "invalid_choice": _("Registrar not found."),
562
+ "invalid_choice": _("Registrar %(value)s not found"),
558
563
  },
559
564
  label=_("Registrar"),
560
565
  )
@@ -567,7 +572,7 @@ class ZoneImportForm(ZoneTemplateUpdateMixin, NetBoxModelImportForm):
567
572
  required=False,
568
573
  to_field_name="contact_id",
569
574
  error_messages={
570
- "invalid_choice": _("Registrant contact ID not found"),
575
+ "invalid_choice": _("Registrant contact ID %(value)s not found"),
571
576
  },
572
577
  label=_("Registrant"),
573
578
  )
@@ -576,7 +581,7 @@ class ZoneImportForm(ZoneTemplateUpdateMixin, NetBoxModelImportForm):
576
581
  required=False,
577
582
  to_field_name="contact_id",
578
583
  error_messages={
579
- "invalid_choice": _("Administrative contact ID not found"),
584
+ "invalid_choice": _("Administrative contact ID %(value)s not found"),
580
585
  },
581
586
  label=_("Administrative Contact"),
582
587
  )
@@ -585,7 +590,7 @@ class ZoneImportForm(ZoneTemplateUpdateMixin, NetBoxModelImportForm):
585
590
  required=False,
586
591
  to_field_name="contact_id",
587
592
  error_messages={
588
- "invalid_choice": _("Technical contact ID not found"),
593
+ "invalid_choice": _("Technical contact ID %(value)s not found"),
589
594
  },
590
595
  label=_("Technical Contact"),
591
596
  )
@@ -669,7 +674,7 @@ class ZoneBulkEditForm(NetBoxModelBulkEditForm):
669
674
  view = DynamicModelChoiceField(
670
675
  queryset=View.objects.all(),
671
676
  required=False,
672
- label=_p("DNS", "View"),
677
+ label=_("View"),
673
678
  )
674
679
  status = forms.ChoiceField(
675
680
  choices=add_blank_choice(ZoneStatusChoices),
@@ -781,7 +786,7 @@ class ZoneBulkEditForm(NetBoxModelBulkEditForm):
781
786
  tenant_group = DynamicModelChoiceField(
782
787
  queryset=TenantGroup.objects.all(),
783
788
  required=False,
784
- label=_("Tenant"),
789
+ label=_("Tenant Group"),
785
790
  )
786
791
  tenant = DynamicModelChoiceField(
787
792
  queryset=Tenant.objects.all(),
@@ -159,7 +159,7 @@ class ZoneTemplateImportForm(NetBoxModelImportForm):
159
159
  required=False,
160
160
  to_field_name="name",
161
161
  error_messages={
162
- "invalid_choice": _("Registrar not found."),
162
+ "invalid_choice": _("Registrar %(value)s not found"),
163
163
  },
164
164
  label=_("Registrar"),
165
165
  )
@@ -168,7 +168,7 @@ class ZoneTemplateImportForm(NetBoxModelImportForm):
168
168
  required=False,
169
169
  to_field_name="contact_id",
170
170
  error_messages={
171
- "invalid_choice": _("Registrant contact ID not found"),
171
+ "invalid_choice": _("Registrant contact ID %(value)s not found"),
172
172
  },
173
173
  label=_("Registrant"),
174
174
  )
@@ -177,7 +177,7 @@ class ZoneTemplateImportForm(NetBoxModelImportForm):
177
177
  required=False,
178
178
  to_field_name="contact_id",
179
179
  error_messages={
180
- "invalid_choice": _("Administrative contact ID not found"),
180
+ "invalid_choice": _("Administrative contact ID %(value)s not found"),
181
181
  },
182
182
  label=_("Administrative Contact"),
183
183
  )
@@ -186,7 +186,7 @@ class ZoneTemplateImportForm(NetBoxModelImportForm):
186
186
  required=False,
187
187
  to_field_name="contact_id",
188
188
  error_messages={
189
- "invalid_choice": _("Technical contact ID not found"),
189
+ "invalid_choice": _("Technical contact ID %(value)s not found"),
190
190
  },
191
191
  label=_("Technical Contact"),
192
192
  )
@@ -195,7 +195,7 @@ class ZoneTemplateImportForm(NetBoxModelImportForm):
195
195
  required=False,
196
196
  to_field_name="contact_id",
197
197
  error_messages={
198
- "invalid_choice": _("Billing contact ID not found"),
198
+ "invalid_choice": _("Billing contact ID %(value)s not found"),
199
199
  },
200
200
  label=_("Billing Contact"),
201
201
  )
Binary file
@@ -19,8 +19,6 @@ from netbox_dns.choices import RecordTypeChoices
19
19
  from netbox_dns.validators import validate_fqdn
20
20
  from netbox_dns.mixins import ObjectModificationMixin
21
21
 
22
- from .record import Record
23
-
24
22
 
25
23
  __all__ = (
26
24
  "NameServer",
@@ -80,7 +78,7 @@ class NameServer(ObjectModificationMixin, ContactsMixin, NetBoxModel):
80
78
  {
81
79
  "name": str(exc),
82
80
  }
83
- ) from None
81
+ )
84
82
 
85
83
  try:
86
84
  validate_fqdn(self.name)
@@ -89,7 +87,7 @@ class NameServer(ObjectModificationMixin, ContactsMixin, NetBoxModel):
89
87
  {
90
88
  "name": exc,
91
89
  }
92
- ) from None
90
+ )
93
91
 
94
92
  super().clean(*args, **kwargs)
95
93
 
@@ -112,10 +110,8 @@ class NameServer(ObjectModificationMixin, ContactsMixin, NetBoxModel):
112
110
 
113
111
  def delete(self, *args, **kwargs):
114
112
  with transaction.atomic():
115
- zones = self.zones.all()
116
- for zone in zones:
117
- Record.objects.filter(
118
- Q(zone=zone),
113
+ for zone in self.zones.all():
114
+ zone.record_set.filter(
119
115
  Q(managed=True),
120
116
  Q(value=f"{self.name}."),
121
117
  Q(type=RecordTypeChoices.NS),
@@ -23,17 +23,15 @@ from netbox_dns.validators import validate_generic_name, validate_record_value
23
23
  from netbox_dns.mixins import ObjectModificationMixin
24
24
  from netbox_dns.choices import RecordTypeChoices, RecordStatusChoices
25
25
 
26
- # +
27
- # This is a hack designed to break cyclic imports between Record and Zone
28
- # -
29
- from netbox_dns.models import zone
30
-
31
26
 
32
27
  __all__ = (
33
28
  "Record",
34
29
  "RecordIndex",
35
30
  )
36
31
 
32
+ ZONE_ACTIVE_STATUS_LIST = get_plugin_config("netbox_dns", "zone_active_status")
33
+ RECORD_ACTIVE_STATUS_LIST = get_plugin_config("netbox_dns", "record_active_status")
34
+
37
35
 
38
36
  def min_ttl(*ttl_list):
39
37
  return min((ttl for ttl in ttl_list if ttl is not None), default=None)
@@ -102,14 +100,8 @@ class RecordManager(models.Manager.from_queryset(RestrictedQuerySet)):
102
100
  .annotate(
103
101
  active=ExpressionWrapper(
104
102
  Q(
105
- Q(zone__status__in=zone.Zone.ACTIVE_STATUS_LIST)
106
- & Q(
107
- Q(address_record__isnull=True)
108
- | Q(
109
- address_record__zone__status__in=zone.Zone.ACTIVE_STATUS_LIST
110
- )
111
- )
112
- & Q(status__in=Record.ACTIVE_STATUS_LIST)
103
+ zone__status__in=ZONE_ACTIVE_STATUS_LIST,
104
+ status__in=RECORD_ACTIVE_STATUS_LIST,
113
105
  ),
114
106
  output_field=BooleanField(),
115
107
  )
@@ -118,8 +110,6 @@ class RecordManager(models.Manager.from_queryset(RestrictedQuerySet)):
118
110
 
119
111
 
120
112
  class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
121
- ACTIVE_STATUS_LIST = (RecordStatusChoices.STATUS_ACTIVE,)
122
-
123
113
  unique_ptr_qs = Q(
124
114
  Q(disable_ptr=False),
125
115
  Q(Q(type=RecordTypeChoices.A) | Q(type=RecordTypeChoices.AAAA)),
@@ -268,7 +258,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
268
258
 
269
259
  @property
270
260
  def value_fqdn(self):
271
- if self.type != RecordTypeChoices.CNAME:
261
+ if self.type not in (RecordTypeChoices.CNAME, RecordTypeChoices.NS):
272
262
  return None
273
263
 
274
264
  _zone = dns_name.from_text(self.zone.name)
@@ -295,8 +285,8 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
295
285
  @property
296
286
  def is_active(self):
297
287
  return (
298
- self.status in Record.ACTIVE_STATUS_LIST
299
- and self.zone.status in zone.Zone.ACTIVE_STATUS_LIST
288
+ self.status in RECORD_ACTIVE_STATUS_LIST
289
+ and self.zone.status in ZONE_ACTIVE_STATUS_LIST
300
290
  )
301
291
 
302
292
  @property
@@ -330,8 +320,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
330
320
  def ptr_zone(self):
331
321
  if self.type == RecordTypeChoices.A:
332
322
  ptr_zone = (
333
- zone.Zone.objects.filter(
334
- view=self.zone.view,
323
+ self.zone.view.zone_set.filter(
335
324
  rfc2317_prefix__net_contains=self.value,
336
325
  )
337
326
  .order_by("rfc2317_prefix__net_mask_length")
@@ -342,15 +331,17 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
342
331
  return ptr_zone
343
332
 
344
333
  ptr_zone = (
345
- zone.Zone.objects.filter(
346
- view=self.zone.view, arpa_network__net_contains=self.value
347
- )
334
+ self.zone.view.zone_set.filter(arpa_network__net_contains=self.value)
348
335
  .order_by("arpa_network__net_mask_length")
349
336
  .last()
350
337
  )
351
338
 
352
339
  return ptr_zone
353
340
 
341
+ @property
342
+ def is_delegation_record(self):
343
+ return self in self.zone.delegation_records
344
+
354
345
  def update_ptr_record(self, update_rfc2317_cname=True, save_zone_serial=True):
355
346
  ptr_zone = self.ptr_zone
356
347
 
@@ -460,10 +451,9 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
460
451
 
461
452
  self.remove_from_rfc2317_cname_record(save_zone_serial=save_zone_serial)
462
453
 
463
- rfc2317_cname_record = Record.objects.filter(
454
+ rfc2317_cname_record = self.zone.rfc2317_parent_zone.record_set.filter(
464
455
  name=cname_name,
465
456
  type=RecordTypeChoices.CNAME,
466
- zone=self.zone.rfc2317_parent_zone,
467
457
  managed=True,
468
458
  value=self.fqdn,
469
459
  ).first()
@@ -597,7 +587,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
597
587
 
598
588
  def validate_value(self):
599
589
  try:
600
- validate_record_value(self.type, self.value)
590
+ validate_record_value(self)
601
591
  except ValidationError as exc:
602
592
  raise ValidationError({"value": exc})
603
593
 
@@ -611,12 +601,11 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
611
601
  if new_zone is None:
612
602
  new_zone = self.zone
613
603
 
614
- records = Record.objects.filter(
615
- zone=new_zone,
604
+ records = new_zone.record_set.filter(
616
605
  name=self.name,
617
606
  type=self.type,
618
607
  value=self.value,
619
- status__in=Record.ACTIVE_STATUS_LIST,
608
+ status__in=RECORD_ACTIVE_STATUS_LIST,
620
609
  )
621
610
 
622
611
  if not self._state.adding:
@@ -648,12 +637,11 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
648
637
  if not get_plugin_config("netbox_dns", "dnssync_conflict_deactivate", False):
649
638
  return
650
639
 
651
- records = Record.objects.filter(
652
- zone=self.zone,
640
+ records = self.zone.record_set.filter(
653
641
  name=self.name,
654
642
  type=self.type,
655
643
  value=self.value,
656
- status__in=Record.ACTIVE_STATUS_LIST,
644
+ status__in=RECORD_ACTIVE_STATUS_LIST,
657
645
  ipam_ip_address__isnull=True,
658
646
  )
659
647
 
@@ -677,8 +665,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
677
665
  return
678
666
 
679
667
  records = (
680
- Record.objects.filter(
681
- zone=self.zone,
668
+ self.zone.record_set.filter(
682
669
  name=self.name,
683
670
  type=self.type,
684
671
  )
@@ -721,8 +708,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
721
708
  ttl = self.ttl
722
709
 
723
710
  records = (
724
- Record.objects.filter(
725
- zone=self.zone,
711
+ self.zone.record_set.filter(
726
712
  name=self.name,
727
713
  type=self.type,
728
714
  )
@@ -750,9 +736,9 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
750
736
  if not self.is_active:
751
737
  return
752
738
 
753
- records = Record.objects.filter(
754
- name=self.name, zone=self.zone, active=True
755
- ).exclude(pk=self.pk)
739
+ records = self.zone.record_set.filter(name=self.name, active=True).exclude(
740
+ pk=self.pk
741
+ )
756
742
 
757
743
  if self.type == RecordTypeChoices.A and not self.disable_ptr:
758
744
  ptr_zone = self.ptr_zone
@@ -769,8 +755,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
769
755
  )
770
756
 
771
757
  if (
772
- Record.objects.filter(
773
- zone=ptr_cname_zone,
758
+ ptr_cname_zone.record_set.filter(
774
759
  name=ptr_cname_name,
775
760
  active=True,
776
761
  )