netbox-plugin-dns 1.0.7__py3-none-any.whl → 1.1.0b1__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 (69) hide show
  1. netbox_dns/__init__.py +21 -5
  2. netbox_dns/api/nested_serializers.py +16 -17
  3. netbox_dns/api/serializers.py +1 -0
  4. netbox_dns/api/serializers_/prefix.py +18 -0
  5. netbox_dns/api/serializers_/record.py +0 -1
  6. netbox_dns/api/serializers_/view.py +34 -2
  7. netbox_dns/api/urls.py +3 -0
  8. netbox_dns/api/views.py +36 -0
  9. netbox_dns/fields/__init__.py +1 -0
  10. netbox_dns/fields/ipam.py +18 -0
  11. netbox_dns/filtersets/record.py +1 -1
  12. netbox_dns/filtersets/view.py +16 -0
  13. netbox_dns/forms/view.py +116 -4
  14. netbox_dns/forms/zone.py +0 -8
  15. netbox_dns/graphql/schema.py +40 -16
  16. netbox_dns/graphql/types.py +1 -0
  17. netbox_dns/management/commands/setup_autodns.py +120 -0
  18. netbox_dns/migrations/0007_view_prefixes.py +18 -0
  19. netbox_dns/models/__init__.py +0 -2
  20. netbox_dns/models/contact.py +3 -9
  21. netbox_dns/models/nameserver.py +3 -8
  22. netbox_dns/models/record.py +71 -17
  23. netbox_dns/models/record_template.py +1 -4
  24. netbox_dns/models/registrar.py +1 -7
  25. netbox_dns/models/view.py +7 -9
  26. netbox_dns/models/zone.py +28 -29
  27. netbox_dns/models/zone_template.py +5 -8
  28. netbox_dns/signals/ipam_autodns.py +138 -0
  29. netbox_dns/tables/contact.py +1 -0
  30. netbox_dns/tables/nameserver.py +7 -1
  31. netbox_dns/tables/record.py +30 -10
  32. netbox_dns/tables/record_template.py +17 -0
  33. netbox_dns/tables/registrar.py +2 -0
  34. netbox_dns/tables/view.py +32 -3
  35. netbox_dns/tables/zone.py +15 -0
  36. netbox_dns/tables/zone_template.py +16 -2
  37. netbox_dns/template_content.py +28 -39
  38. netbox_dns/templates/netbox_dns/record.html +6 -6
  39. netbox_dns/templates/netbox_dns/view/related.html +17 -0
  40. netbox_dns/templates/netbox_dns/view.html +29 -0
  41. netbox_dns/urls/contact.py +32 -10
  42. netbox_dns/urls/nameserver.py +38 -14
  43. netbox_dns/urls/record.py +19 -7
  44. netbox_dns/urls/record_template.py +27 -18
  45. netbox_dns/urls/registrar.py +35 -11
  46. netbox_dns/urls/view.py +22 -8
  47. netbox_dns/urls/zone.py +46 -8
  48. netbox_dns/urls/zone_template.py +26 -16
  49. netbox_dns/utilities/__init__.py +2 -74
  50. netbox_dns/utilities/conversions.py +83 -0
  51. netbox_dns/utilities/ipam_autodns.py +205 -0
  52. netbox_dns/validators/dns_name.py +0 -9
  53. netbox_dns/views/contact.py +1 -0
  54. netbox_dns/views/nameserver.py +3 -7
  55. netbox_dns/views/record.py +2 -9
  56. netbox_dns/views/record_template.py +1 -1
  57. netbox_dns/views/registrar.py +1 -0
  58. netbox_dns/views/view.py +1 -6
  59. netbox_dns/views/zone.py +6 -7
  60. netbox_dns/views/zone_template.py +2 -2
  61. {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0b1.dist-info}/METADATA +2 -2
  62. {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0b1.dist-info}/RECORD +64 -61
  63. netbox_dns/management/commands/setup_coupling.py +0 -109
  64. netbox_dns/migrations/0007_alter_ordering_options.py +0 -25
  65. netbox_dns/signals/ipam_coupling.py +0 -168
  66. netbox_dns/templates/netbox_dns/related_dns_objects.html +0 -21
  67. netbox_dns/utilities/ipam_coupling.py +0 -112
  68. {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0b1.dist-info}/LICENSE +0 -0
  69. {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0b1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,120 @@
1
+ from django.core.management.base import BaseCommand
2
+
3
+ from core.models import ObjectType
4
+ from extras.models import CustomField
5
+ from extras.choices import CustomFieldTypeChoices
6
+ from ipam.models import IPAddress
7
+
8
+
9
+ class Command(BaseCommand):
10
+ help = "Setup IPAddress custom fields for IPAM AutoDNS"
11
+
12
+ def add_arguments(self, parser):
13
+ parser.add_argument(
14
+ "--remove", action="store_true", default=False, help="Remove custom fields"
15
+ )
16
+
17
+ def handle(self, *model_names, **options):
18
+ ipaddress_object_type = ObjectType.objects.get_for_model(IPAddress)
19
+
20
+ if options.get("remove"):
21
+ if options.get("verbosity"):
22
+ self.stdout.write(f"Trying to remove IPAM AutoDNS custom fields")
23
+ for cf in (
24
+ "ipaddress_dns_disabled",
25
+ "ipaddress_dns_record_ttl",
26
+ "ipaddress_dns_record_disable_ptr",
27
+ ):
28
+ try:
29
+ CustomField.objects.get(
30
+ name=cf, object_types=ipaddress_object_type
31
+ ).delete()
32
+ if options.get("verbosity") >= 2:
33
+ self.stdout.write(f"Custom field '{cf}' removed")
34
+ except CustomField.DoesNotExist:
35
+ pass
36
+ return
37
+
38
+ # +
39
+ # Remove pre-existing IPAM Coupling custom fields
40
+ # -
41
+ if options.get("verbosity"):
42
+ self.stdout.write(f"Trying to remove obsolete IPAM Coupling custom fields")
43
+ for cf in (
44
+ "ipaddress_dns_record_name",
45
+ "ipaddress_dns_zone_id",
46
+ ):
47
+ try:
48
+ CustomField.objects.get(
49
+ name=cf, object_types=ipaddress_object_type
50
+ ).delete()
51
+ if options.get("verbosity") >= 2:
52
+ self.stdout.write(f"Removed custom field '{cf}'")
53
+ except CustomField.DoesNotExist:
54
+ pass
55
+
56
+ if options.get("verbosity"):
57
+ self.stdout.write(f"Creating IPAM AutoDNS custom fields")
58
+
59
+ if not CustomField.objects.filter(
60
+ name="ipaddress_dns_disabled",
61
+ type=CustomFieldTypeChoices.TYPE_BOOLEAN,
62
+ object_types=ipaddress_object_type,
63
+ ).exists():
64
+ cf_disable_ptr = CustomField.objects.create(
65
+ name="ipaddress_dns_disabled",
66
+ label="Disable AutoDNS",
67
+ description="Disable DNS address and pointer record generation for this address",
68
+ type=CustomFieldTypeChoices.TYPE_BOOLEAN,
69
+ required=False,
70
+ default=False,
71
+ group_name="AutoDNS",
72
+ is_cloneable=True,
73
+ weight=100,
74
+ )
75
+ cf_disable_ptr.object_types.set([ipaddress_object_type])
76
+ if options.get("verbosity") >= 2:
77
+ self.stdout.write("Created custom field 'ipaddress_dns_disabled'")
78
+
79
+ if not CustomField.objects.filter(
80
+ name="ipaddress_dns_record_ttl",
81
+ type=CustomFieldTypeChoices.TYPE_INTEGER,
82
+ object_types=ipaddress_object_type,
83
+ ).exists():
84
+ cf_ttl = CustomField.objects.create(
85
+ name="ipaddress_dns_record_ttl",
86
+ description="TTL for DNS records created for this address",
87
+ label="TTL",
88
+ type=CustomFieldTypeChoices.TYPE_INTEGER,
89
+ validation_minimum=0,
90
+ validation_maximum=2147483647,
91
+ required=False,
92
+ group_name="AutoDNS",
93
+ is_cloneable=True,
94
+ weight=200,
95
+ )
96
+ cf_ttl.object_types.set([ipaddress_object_type])
97
+ if options.get("verbosity") >= 2:
98
+ self.stdout.write("Created custom field 'ipaddress_dns_record_ttl'")
99
+
100
+ if not CustomField.objects.filter(
101
+ name="ipaddress_dns_record_disable_ptr",
102
+ type=CustomFieldTypeChoices.TYPE_BOOLEAN,
103
+ object_types=ipaddress_object_type,
104
+ ).exists():
105
+ cf_disable_ptr = CustomField.objects.create(
106
+ name="ipaddress_dns_record_disable_ptr",
107
+ description="Disable DNS PTR record generation for this address",
108
+ label="Disable PTR",
109
+ type=CustomFieldTypeChoices.TYPE_BOOLEAN,
110
+ required=False,
111
+ default=False,
112
+ group_name="AutoDNS",
113
+ is_cloneable=True,
114
+ weight=300,
115
+ )
116
+ cf_disable_ptr.object_types.set([ipaddress_object_type])
117
+ if options.get("verbosity") >= 2:
118
+ self.stdout.write(
119
+ "Created custom field 'ipaddress_dns_record_disable_ptr'"
120
+ )
@@ -0,0 +1,18 @@
1
+ from django.db import migrations, models
2
+
3
+
4
+ class Migration(migrations.Migration):
5
+ dependencies = [
6
+ ("ipam", "0067_ipaddress_index_host"),
7
+ ("netbox_dns", "0006_templating"),
8
+ ]
9
+
10
+ operations = [
11
+ migrations.AddField(
12
+ model_name="view",
13
+ name="prefixes",
14
+ field=models.ManyToManyField(
15
+ blank=True, related_name="netbox_dns_views", to="ipam.prefix"
16
+ ),
17
+ ),
18
+ ]
@@ -11,5 +11,3 @@ from .record_template import *
11
11
  # Backwards compatibility fix, will be removed in version 1.1
12
12
  # -
13
13
  from netbox_dns.choices import *
14
-
15
- from netbox_dns.signals import ipam_coupling
@@ -87,7 +87,7 @@ class Contact(NetBoxModel):
87
87
  related_name="netbox_dns_contact_set",
88
88
  )
89
89
 
90
- clone_fields = (
90
+ clone_fields = [
91
91
  "name",
92
92
  "description",
93
93
  "organization",
@@ -102,7 +102,7 @@ class Contact(NetBoxModel):
102
102
  "fax_ext",
103
103
  "email",
104
104
  "tags",
105
- )
105
+ ]
106
106
 
107
107
  def get_absolute_url(self):
108
108
  return reverse("plugins:netbox_dns:contact", kwargs={"pk": self.pk})
@@ -114,13 +114,7 @@ class Contact(NetBoxModel):
114
114
  return self.contact_id
115
115
 
116
116
  class Meta:
117
- verbose_name = "Contact"
118
- verbose_name_plural = "Contacts"
119
-
120
- ordering = (
121
- "name",
122
- "contact_id",
123
- )
117
+ ordering = ("name", "contact_id")
124
118
 
125
119
  @property
126
120
  def zones(self):
@@ -7,7 +7,6 @@ from django.urls import reverse
7
7
 
8
8
  from netbox.models import NetBoxModel
9
9
  from netbox.search import SearchIndex, register_search
10
- from netbox.models.features import ContactsMixin
11
10
 
12
11
  from netbox_dns.utilities import (
13
12
  name_to_unicode,
@@ -27,7 +26,7 @@ __all__ = (
27
26
  )
28
27
 
29
28
 
30
- class NameServer(ObjectModificationMixin, ContactsMixin, NetBoxModel):
29
+ class NameServer(ObjectModificationMixin, NetBoxModel):
31
30
  name = models.CharField(
32
31
  unique=True,
33
32
  max_length=255,
@@ -44,17 +43,13 @@ class NameServer(ObjectModificationMixin, ContactsMixin, NetBoxModel):
44
43
  null=True,
45
44
  )
46
45
 
47
- clone_fields = (
48
- "name",
49
- "description",
50
- )
46
+ clone_fields = ["name", "description"]
51
47
 
52
48
  class Meta:
49
+ ordering = ("name",)
53
50
  verbose_name = "Nameserver"
54
51
  verbose_name_plural = "Nameservers"
55
52
 
56
- ordering = ("name",)
57
-
58
53
  def __str__(self):
59
54
  try:
60
55
  return dns_name.from_text(self.name, origin=None).to_unicode()
@@ -7,9 +7,9 @@ from django.core.exceptions import ValidationError
7
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
+ from django.conf import settings
10
11
 
11
12
  from netbox.models import NetBoxModel
12
- from netbox.models.features import ContactsMixin
13
13
  from netbox.search import SearchIndex, register_search
14
14
  from netbox.plugins.utils import get_plugin_config
15
15
  from utilities.querysets import RestrictedQuerySet
@@ -36,6 +36,43 @@ def min_ttl(*ttl_list):
36
36
  return min((ttl for ttl in ttl_list if ttl is not None), default=None)
37
37
 
38
38
 
39
+ def record_data_from_ip_address(ip_address, zone):
40
+ cf_data = ip_address.custom_field_data
41
+
42
+ if cf_data.get("ipaddress_dns_disabled"):
43
+ return None
44
+
45
+ data = {
46
+ "name": (
47
+ dns_name.from_text(ip_address.dns_name)
48
+ .relativize(dns_name.from_text(zone.name))
49
+ .to_text()
50
+ ),
51
+ "type": (
52
+ RecordTypeChoices.A
53
+ if ip_address.address.version == 4
54
+ else RecordTypeChoices.AAAA
55
+ ),
56
+ "value": str(ip_address.address.ip),
57
+ "status": (
58
+ RecordStatusChoices.STATUS_ACTIVE
59
+ if ip_address.status
60
+ in settings.PLUGINS_CONFIG["netbox_dns"].get(
61
+ "autodns_ipaddress_active_status", []
62
+ )
63
+ else RecordStatusChoices.STATUS_INACTIVE
64
+ ),
65
+ }
66
+
67
+ if "ipaddress_dns_record_ttl" in cf_data:
68
+ data["ttl"] = cf_data.get("ipaddress_dns_record_ttl")
69
+
70
+ if (disable_ptr := cf_data.get("ipaddress_dns_record_disable_ptr")) is not None:
71
+ data["disable_ptr"] = disable_ptr
72
+
73
+ return data
74
+
75
+
39
76
  class RecordManager(models.Manager.from_queryset(RestrictedQuerySet)):
40
77
  """
41
78
  Custom manager for records providing the activity status annotation
@@ -63,7 +100,7 @@ class RecordManager(models.Manager.from_queryset(RestrictedQuerySet)):
63
100
  )
64
101
 
65
102
 
66
- class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
103
+ class Record(ObjectModificationMixin, NetBoxModel):
67
104
  ACTIVE_STATUS_LIST = (RecordStatusChoices.STATUS_ACTIVE,)
68
105
 
69
106
  unique_ptr_qs = Q(
@@ -156,7 +193,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
156
193
  objects = RecordManager()
157
194
  raw_objects = RestrictedQuerySet.as_manager()
158
195
 
159
- clone_fields = (
196
+ clone_fields = [
160
197
  "zone",
161
198
  "type",
162
199
  "name",
@@ -165,20 +202,10 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
165
202
  "ttl",
166
203
  "disable_ptr",
167
204
  "description",
168
- )
205
+ ]
169
206
 
170
207
  class Meta:
171
- verbose_name = "Record"
172
- verbose_name_plural = "Records"
173
-
174
- ordering = (
175
- "fqdn",
176
- "zone",
177
- "name",
178
- "type",
179
- "value",
180
- "status",
181
- )
208
+ ordering = ("zone", "name", "type", "value", "status")
182
209
 
183
210
  def __str__(self):
184
211
  try:
@@ -434,6 +461,35 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
434
461
  self.rfc2317_cname_record.delete(save_zone_serial=save_zone_serial)
435
462
  self.rfc2317_cname_record = None
436
463
 
464
+ def update_from_ip_address(self, ip_address):
465
+ data = record_data_from_ip_address(ip_address, self.zone)
466
+
467
+ if data is None:
468
+ self.delete()
469
+ return
470
+
471
+ if all((getattr(self, attr) == data[attr] for attr in data.keys())):
472
+ return
473
+
474
+ for attr, value in data.items():
475
+ setattr(self, attr, value)
476
+
477
+ return self
478
+
479
+ @classmethod
480
+ def create_from_ip_address(cls, ip_address, zone):
481
+ data = record_data_from_ip_address(ip_address, zone)
482
+
483
+ if data is None:
484
+ return
485
+
486
+ return Record(
487
+ zone=zone,
488
+ managed=True,
489
+ ipam_ip_address=ip_address,
490
+ **data,
491
+ )
492
+
437
493
  def validate_name(self):
438
494
  try:
439
495
  _zone = dns_name.from_text(self.zone.name, origin=dns_name.root)
@@ -531,7 +587,6 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
531
587
  )
532
588
  .exclude(ttl=self.ttl)
533
589
  .exclude(type=RecordTypeChoices.PTR, managed=True)
534
- .exclude(status=RecordStatusChoices.STATUS_INACTIVE)
535
590
  )
536
591
 
537
592
  if not records.exists():
@@ -566,7 +621,6 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
566
621
  .exclude(pk=self.pk)
567
622
  .exclude(ttl=ttl)
568
623
  .exclude(type=RecordTypeChoices.PTR, managed=True)
569
- .exclude(status=RecordStatusChoices.STATUS_INACTIVE)
570
624
  )
571
625
 
572
626
  for record in records:
@@ -85,10 +85,7 @@ class RecordTemplate(NetBoxModel):
85
85
  )
86
86
 
87
87
  class Meta:
88
- verbose_name = "Record Template"
89
- verbose_name_plural = "Record Templates"
90
-
91
- ordering = ("name",)
88
+ ordering = ["name"]
92
89
 
93
90
  def __str__(self):
94
91
  return str(self.name)
@@ -59,13 +59,7 @@ class Registrar(NetBoxModel):
59
59
  return str(self.name)
60
60
 
61
61
  class Meta:
62
- verbose_name = "Registrar"
63
- verbose_name_plural = "Registrars"
64
-
65
- ordering = (
66
- "name",
67
- "iana_id",
68
- )
62
+ ordering = ("name", "iana_id")
69
63
 
70
64
 
71
65
  @register_search
netbox_dns/models/view.py CHANGED
@@ -3,7 +3,6 @@ from django.urls import reverse
3
3
  from django.core.exceptions import ValidationError
4
4
 
5
5
  from netbox.models import NetBoxModel
6
- from netbox.models.features import ContactsMixin
7
6
  from netbox.search import SearchIndex, register_search
8
7
  from netbox.context import current_request
9
8
  from utilities.exceptions import AbortRequest
@@ -17,7 +16,7 @@ __all__ = (
17
16
  )
18
17
 
19
18
 
20
- class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
19
+ class View(ObjectModificationMixin, NetBoxModel):
21
20
  name = models.CharField(
22
21
  unique=True,
23
22
  max_length=255,
@@ -29,6 +28,11 @@ class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
29
28
  default_view = models.BooleanField(
30
29
  default=False,
31
30
  )
31
+ prefixes = models.ManyToManyField(
32
+ to="ipam.Prefix",
33
+ related_name="netbox_dns_views",
34
+ blank=True,
35
+ )
32
36
  tenant = models.ForeignKey(
33
37
  to="tenancy.Tenant",
34
38
  on_delete=models.PROTECT,
@@ -37,10 +41,7 @@ class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
37
41
  null=True,
38
42
  )
39
43
 
40
- clone_fields = (
41
- "name",
42
- "description",
43
- )
44
+ clone_fields = ["name", "description"]
44
45
 
45
46
  @classmethod
46
47
  def get_default_view(cls):
@@ -53,9 +54,6 @@ class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
53
54
  return str(self.name)
54
55
 
55
56
  class Meta:
56
- verbose_name = "View"
57
- verbose_name_plural = "Views"
58
-
59
57
  ordering = ("name",)
60
58
 
61
59
  def delete(self, *args, **kwargs):
netbox_dns/models/zone.py CHANGED
@@ -19,7 +19,6 @@ from django.dispatch import receiver
19
19
  from django.conf import settings
20
20
 
21
21
  from netbox.models import NetBoxModel
22
- from netbox.models.features import ContactsMixin
23
22
  from netbox.search import SearchIndex, register_search
24
23
  from netbox.plugins.utils import get_plugin_config
25
24
  from utilities.querysets import RestrictedQuerySet
@@ -28,6 +27,8 @@ from ipam.models import IPAddress
28
27
  from netbox_dns.choices import RecordClassChoices, RecordTypeChoices, ZoneStatusChoices
29
28
  from netbox_dns.fields import NetworkField, RFC2317NetworkField
30
29
  from netbox_dns.utilities import (
30
+ update_dns_records,
31
+ get_ip_addresses_by_zone,
31
32
  arpa_to_prefix,
32
33
  name_to_unicode,
33
34
  normalize_name,
@@ -35,7 +36,6 @@ from netbox_dns.utilities import (
35
36
  )
36
37
  from netbox_dns.validators import (
37
38
  validate_fqdn,
38
- validate_rname,
39
39
  validate_domain_name,
40
40
  )
41
41
  from netbox_dns.mixins import ObjectModificationMixin
@@ -68,7 +68,7 @@ class ZoneManager(models.Manager.from_queryset(RestrictedQuerySet)):
68
68
  )
69
69
 
70
70
 
71
- class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
71
+ class Zone(ObjectModificationMixin, NetBoxModel):
72
72
  ACTIVE_STATUS_LIST = (ZoneStatusChoices.STATUS_ACTIVE,)
73
73
 
74
74
  def __init__(self, *args, **kwargs):
@@ -247,7 +247,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
247
247
 
248
248
  objects = ZoneManager()
249
249
 
250
- clone_fields = (
250
+ clone_fields = [
251
251
  "view",
252
252
  "name",
253
253
  "status",
@@ -261,12 +261,9 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
261
261
  "soa_expire",
262
262
  "soa_minimum",
263
263
  "description",
264
- )
264
+ ]
265
265
 
266
266
  class Meta:
267
- verbose_name = "Zone"
268
- verbose_name_plural = "Zones"
269
-
270
267
  ordering = (
271
268
  "view",
272
269
  "name",
@@ -623,7 +620,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
623
620
  raise ValidationError("soa_rname not set and no default value defined")
624
621
  try:
625
622
  dns_name.from_text(self.soa_rname, origin=dns_name.root)
626
- validate_rname(self.soa_rname)
623
+ validate_fqdn(self.soa_rname)
627
624
  except (DNSException, ValidationError) as exc:
628
625
  raise ValidationError(
629
626
  {
@@ -643,7 +640,7 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
643
640
  old_zone = Zone.objects.get(pk=self.pk)
644
641
  if not self.soa_serial_auto:
645
642
  self.check_soa_serial_increment(old_zone.soa_serial, self.soa_serial)
646
- elif not old_zone.soa_serial_auto:
643
+ else:
647
644
  try:
648
645
  self.check_soa_serial_increment(
649
646
  old_zone.soa_serial, self.get_auto_serial()
@@ -773,17 +770,6 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
773
770
  ):
774
771
  address_record.save(update_fields=["ptr_record"])
775
772
 
776
- # Fix name in IP Address when zone name is changed
777
- if (
778
- get_plugin_config("netbox_dns", "feature_ipam_coupling")
779
- and "name" in changed_fields
780
- ):
781
- for ip in IPAddress.objects.filter(
782
- custom_field_data__ipaddress_dns_zone_id=self.pk
783
- ):
784
- ip.dns_name = f'{ip.custom_field_data["ipaddress_dns_record_name"]}.{self.name}'
785
- ip.save(update_fields=["dns_name"])
786
-
787
773
  if changed_fields is not None and "name" in changed_fields:
788
774
  for _record in self.record_set.all():
789
775
  _record.save(
@@ -793,6 +779,16 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
793
779
  update_rfc2317_cname=False,
794
780
  )
795
781
 
782
+ if changed_fields is None or {"name", "view"} & changed_fields:
783
+ update_ip_addresses = IPAddress.objects.filter(
784
+ pk__in=self.record_set.filter(
785
+ ipam_ip_address__isnull=False
786
+ ).values_list("ipam_ip_address", flat=True)
787
+ )
788
+ update_ip_addresses |= get_ip_addresses_by_zone(self)
789
+ for ip_address in update_ip_addresses:
790
+ update_dns_records(ip_address)
791
+
796
792
  self.save_soa_serial()
797
793
  self.update_soa_record()
798
794
 
@@ -828,14 +824,13 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
828
824
  self.rfc2317_child_zones.all().values_list("pk", flat=True)
829
825
  )
830
826
 
831
- if get_plugin_config("netbox_dns", "feature_ipam_coupling"):
832
- for ip in IPAddress.objects.filter(
833
- custom_field_data__ipaddress_dns_zone_id=self.pk
834
- ):
835
- ip.dns_name = ""
836
- ip.custom_field_data["ipaddress_dns_record_name"] = None
837
- ip.custom_field_data["ipaddress_dns_zone_id"] = None
838
- ip.save(update_fields=["dns_name", "custom_field_data"])
827
+ ipam_ip_addresses = list(
828
+ IPAddress.objects.filter(
829
+ netbox_dns_records__in=self.record_set.filter(
830
+ ipam_ip_address__isnull=False
831
+ )
832
+ ).values_list("pk", flat=True)
833
+ )
839
834
 
840
835
  super().delete(*args, **kwargs)
841
836
 
@@ -849,6 +844,10 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
849
844
  address_zone.save_soa_serial()
850
845
  address_zone.update_soa_record()
851
846
 
847
+ ip_addresses = IPAddress.objects.filter(pk__in=ipam_ip_addresses)
848
+ for ip_address in ip_addresses:
849
+ update_dns_records(ip_address)
850
+
852
851
  rfc2317_child_zones = Zone.objects.filter(pk__in=rfc2317_child_zones)
853
852
  if rfc2317_child_zones:
854
853
  for child_zone in rfc2317_child_zones:
@@ -82,7 +82,7 @@ class ZoneTemplate(NetBoxModel):
82
82
  null=True,
83
83
  )
84
84
 
85
- clone_fields = (
85
+ clone_fields = [
86
86
  "description",
87
87
  "nameservers",
88
88
  "record_templates",
@@ -92,22 +92,19 @@ class ZoneTemplate(NetBoxModel):
92
92
  "admin_c",
93
93
  "tech_c",
94
94
  "billing_c",
95
- )
95
+ ]
96
96
 
97
- template_fields = (
97
+ template_fields = [
98
98
  "tenant",
99
99
  "registrar",
100
100
  "registrant",
101
101
  "admin_c",
102
102
  "tech_c",
103
103
  "billing_c",
104
- )
104
+ ]
105
105
 
106
106
  class Meta:
107
- verbose_name = "Zone Template"
108
- verbose_name_plural = "Zone Templates"
109
-
110
- ordering = ("name",)
107
+ ordering = ["name"]
111
108
 
112
109
  def __str__(self):
113
110
  return str(self.name)