netbox-plugin-dns 1.1.1__py3-none-any.whl → 1.1.2__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.

netbox_dns/__init__.py CHANGED
@@ -5,7 +5,7 @@ from ipam.choices import IPAddressStatusChoices
5
5
 
6
6
  from netbox_dns.choices import RecordTypeChoices
7
7
 
8
- __version__ = "1.1.1"
8
+ __version__ = "1.1.2"
9
9
 
10
10
 
11
11
  class DNSConfig(PluginConfig):
@@ -37,8 +37,9 @@ class DNSConfig(PluginConfig):
37
37
  "tolerate_underscores_in_labels": False,
38
38
  "tolerate_underscores_in_hostnames": False, # Deprecated, will be removed in 1.2.0
39
39
  "tolerate_leading_underscore_types": [
40
- RecordTypeChoices.TXT,
41
40
  RecordTypeChoices.SRV,
41
+ RecordTypeChoices.TLSA,
42
+ RecordTypeChoices.TXT,
42
43
  ],
43
44
  "tolerate_non_rfc1035_types": [],
44
45
  "enable_root_zones": False,
@@ -65,5 +65,6 @@ class ViewSerializer(NetBoxModelSerializer):
65
65
  "custom_fields",
66
66
  "tenant",
67
67
  "prefixes",
68
+ "ip_address_filter",
68
69
  )
69
70
  brief_fields = ("id", "url", "display", "name", "default_view", "description")
netbox_dns/forms/view.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from django import forms
2
2
  from django.conf import settings
3
- from django.core.exceptions import ValidationError
3
+ from django.core.exceptions import ValidationError, FieldError
4
4
  from django.db.models import Q, Count
5
5
 
6
6
  from netbox.forms import (
@@ -18,9 +18,10 @@ from utilities.forms.fields import (
18
18
  )
19
19
  from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES
20
20
  from utilities.forms.rendering import FieldSet
21
+ from utilities.forms.fields import JSONField
21
22
  from tenancy.models import Tenant
22
23
  from tenancy.forms import TenancyForm, TenancyFilterForm
23
- from ipam.models import Prefix
24
+ from ipam.models import Prefix, IPAddress
24
25
  from netbox.context import current_request
25
26
 
26
27
  from netbox_dns.models import View
@@ -29,6 +30,7 @@ from netbox_dns.utilities import (
29
30
  check_dns_records,
30
31
  get_ip_addresses_by_prefix,
31
32
  get_views_by_prefix,
33
+ get_query_from_filter,
32
34
  )
33
35
 
34
36
 
@@ -45,7 +47,7 @@ class ViewPrefixUpdateMixin:
45
47
  def clean(self, *args, **kwargs):
46
48
  super().clean(*args, **kwargs)
47
49
 
48
- if self.instance.pk is None or "prefixes" not in self.changed_data:
50
+ if self.instance._state.adding or "prefixes" not in self.changed_data:
49
51
  return
50
52
 
51
53
  prefixes = self.cleaned_data.get("prefixes")
@@ -97,6 +99,7 @@ class ViewForm(ViewPrefixUpdateMixin, TenancyForm, NetBoxModelForm):
97
99
 
98
100
  if settings.PLUGINS_CONFIG["netbox_dns"].get("dnssync_disabled"):
99
101
  del self.fields["prefixes"]
102
+ del self.fields["ip_address_filter"]
100
103
 
101
104
  if request := current_request.get():
102
105
  if not request.user.has_perm("ipam.view_prefix"):
@@ -107,12 +110,6 @@ class ViewForm(ViewPrefixUpdateMixin, TenancyForm, NetBoxModelForm):
107
110
  "placeholder"
108
111
  ] = "You do not have permission to modify assigned prefixes"
109
112
 
110
- def clean_prefixes(self):
111
- if hasattr(self, "_saved_prefixes"):
112
- return self._saved_prefixes
113
-
114
- return self.cleaned_data["prefixes"]
115
-
116
113
  prefixes = PrefixDynamicModelMultipleChoiceField(
117
114
  queryset=Prefix.objects.all(),
118
115
  required=False,
@@ -121,10 +118,15 @@ class ViewForm(ViewPrefixUpdateMixin, TenancyForm, NetBoxModelForm):
121
118
  "depth": None,
122
119
  },
123
120
  )
121
+ ip_address_filter = JSONField(
122
+ label="IP Address Filter",
123
+ required=False,
124
+ help_text="Specify criteria for address record creation in JSON form",
125
+ )
124
126
 
125
127
  fieldsets = (
126
128
  FieldSet("name", "default_view", "description", "tags", name="View"),
127
- FieldSet("prefixes"),
129
+ FieldSet("prefixes", "ip_address_filter"),
128
130
  FieldSet("tenant_group", "tenant", name="Tenancy"),
129
131
  )
130
132
 
@@ -137,8 +139,25 @@ class ViewForm(ViewPrefixUpdateMixin, TenancyForm, NetBoxModelForm):
137
139
  "tags",
138
140
  "tenant",
139
141
  "prefixes",
142
+ "ip_address_filter",
140
143
  )
141
144
 
145
+ def clean_prefixes(self):
146
+ if hasattr(self, "_saved_prefixes"):
147
+ return self._saved_prefixes
148
+
149
+ return self.cleaned_data["prefixes"]
150
+
151
+ def clean_ip_address_filter(self):
152
+ ip_address_filter = self.cleaned_data.get("ip_address_filter")
153
+
154
+ try:
155
+ IPAddress.objects.filter(get_query_from_filter(ip_address_filter)).exists()
156
+ except (FieldError, ValueError) as exc:
157
+ self.add_error("ip_address_filter", f"Invalid filter for IPAddress: {exc}")
158
+
159
+ return ip_address_filter
160
+
142
161
 
143
162
  class ViewFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
144
163
  def __init__(self, *args, **kwargs):
@@ -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)
@@ -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}")
@@ -10,13 +10,14 @@ from django.urls import reverse
10
10
  from django.conf import settings
11
11
 
12
12
  from netbox.models import NetBoxModel
13
+ from ipam.models import IPAddress
13
14
  from netbox.models.features import ContactsMixin
14
15
  from netbox.search import SearchIndex, register_search
15
16
  from netbox.plugins.utils import get_plugin_config
16
17
  from utilities.querysets import RestrictedQuerySet
17
18
 
18
19
  from netbox_dns.fields import AddressField
19
- from netbox_dns.utilities import arpa_to_prefix, name_to_unicode
20
+ from netbox_dns.utilities import arpa_to_prefix, name_to_unicode, get_query_from_filter
20
21
  from netbox_dns.validators import validate_generic_name, validate_record_value
21
22
  from netbox_dns.mixins import ObjectModificationMixin
22
23
  from netbox_dns.choices import RecordTypeChoices, RecordStatusChoices
@@ -41,6 +42,20 @@ def record_data_from_ip_address(ip_address, zone):
41
42
  cf_data = ip_address.custom_field_data
42
43
 
43
44
  if cf_data.get("ipaddress_dns_disabled"):
45
+ # +
46
+ # DNS record creation disabled for this address
47
+ # -
48
+ return None
49
+
50
+ if (
51
+ zone.view.ip_address_filter is not None
52
+ and not IPAddress.objects.filter(
53
+ Q(pk=ip_address.pk), get_query_from_filter(zone.view.ip_address_filter)
54
+ ).exists()
55
+ ):
56
+ # +
57
+ # IP address does not match the filter
58
+ # -
44
59
  return None
45
60
 
46
61
  data = {
@@ -473,22 +488,29 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
473
488
  self.rfc2317_cname_record = None
474
489
 
475
490
  def update_from_ip_address(self, ip_address, zone=None):
491
+ """
492
+ Update an address record according to data from an IPAddress object.
493
+
494
+ Returns a tuple of two booleans: (update, delete).
495
+
496
+ update: The record was updated and needs to be cleaned and/or saved
497
+ delete: The record is no longer needed and needs to be deleted
498
+ """
476
499
  if zone is None:
477
500
  zone = self.zone
478
501
 
479
502
  data = record_data_from_ip_address(ip_address, zone)
480
503
 
481
504
  if data is None:
482
- self.delete()
483
- return
505
+ return False, True
484
506
 
485
507
  if all((getattr(self, attr) == data[attr] for attr in data.keys())):
486
- return
508
+ return False, False
487
509
 
488
510
  for attr, value in data.items():
489
511
  setattr(self, attr, value)
490
512
 
491
- return self
513
+ return True, False
492
514
 
493
515
  @classmethod
494
516
  def create_from_ip_address(cls, ip_address, zone):
@@ -585,7 +607,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
585
607
  status__in=Record.ACTIVE_STATUS_LIST,
586
608
  )
587
609
 
588
- if self.pk is not None:
610
+ if not self._state.adding:
589
611
  records = records.exclude(pk=self.pk)
590
612
 
591
613
  if records.exists():
@@ -624,7 +646,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
624
646
  record.save(update_fields=["status"])
625
647
 
626
648
  def check_unique_rrset_ttl(self):
627
- if self.pk is not None:
649
+ if not self._state.adding:
628
650
  return
629
651
 
630
652
  if not get_plugin_config("netbox_dns", "enforce_unique_rrset_ttl", False):
@@ -663,7 +685,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
663
685
  ) from None
664
686
 
665
687
  def update_rrset_ttl(self, ttl=None):
666
- if self.pk is None:
688
+ if self._state.adding:
667
689
  return
668
690
 
669
691
  if not get_plugin_config("netbox_dns", "enforce_unique_rrset_ttl", False):
@@ -699,7 +721,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
699
721
  self.validate_name(new_zone=new_zone)
700
722
  self.validate_value()
701
723
  self.check_unique_record(new_zone=new_zone)
702
- if self.pk is None:
724
+ if self._state.adding:
703
725
  self.check_unique_rrset_ttl()
704
726
 
705
727
  if not self.is_active:
@@ -787,7 +809,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
787
809
  ):
788
810
  self.full_clean()
789
811
 
790
- if self.pk is not None and update_rrset_ttl:
812
+ if not self._state.adding and update_rrset_ttl:
791
813
  self.update_rrset_ttl()
792
814
 
793
815
  if self.is_ptr_record:
netbox_dns/models/view.py CHANGED
@@ -9,6 +9,13 @@ from netbox.context import current_request
9
9
  from utilities.exceptions import AbortRequest
10
10
 
11
11
  from netbox_dns.mixins import ObjectModificationMixin
12
+ from netbox_dns.utilities import (
13
+ get_ip_addresses_by_view,
14
+ check_dns_records,
15
+ update_dns_records,
16
+ delete_dns_records,
17
+ get_query_from_filter,
18
+ )
12
19
 
13
20
 
14
21
  __all__ = (
@@ -34,6 +41,11 @@ class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
34
41
  related_name="netbox_dns_views",
35
42
  blank=True,
36
43
  )
44
+ ip_address_filter = models.JSONField(
45
+ verbose_name="IP Address Filter",
46
+ blank=True,
47
+ null=True,
48
+ )
37
49
  tenant = models.ForeignKey(
38
50
  to="tenancy.Tenant",
39
51
  on_delete=models.PROTECT,
@@ -87,6 +99,21 @@ class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
87
99
  }
88
100
  )
89
101
 
102
+ if "ip_address_filter" in changed_fields and self.get_saved_value(
103
+ "ip_address_filter"
104
+ ):
105
+ try:
106
+ for ip_address in get_ip_addresses_by_view(self).filter(
107
+ get_query_from_filter(self.ip_address_filter)
108
+ ):
109
+ check_dns_records(ip_address, view=self)
110
+ except ValidationError as exc:
111
+ raise ValidationError(
112
+ {
113
+ "ip_address_filter": exc.messages,
114
+ }
115
+ )
116
+
90
117
  super().clean(*args, **kwargs)
91
118
 
92
119
  def save(self, *args, **kwargs):
@@ -106,6 +133,19 @@ class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
106
133
  view.default_view = False
107
134
  view.save()
108
135
 
136
+ if changed_fields is not None and "ip_address_filter" in changed_fields:
137
+ ip_addresses = get_ip_addresses_by_view(self)
138
+
139
+ for ip_address in ip_addresses.exclude(
140
+ get_query_from_filter(self.ip_address_filter)
141
+ ):
142
+ delete_dns_records(ip_address, view=self)
143
+
144
+ for ip_address in ip_addresses.filter(
145
+ get_query_from_filter(self.ip_address_filter)
146
+ ):
147
+ update_dns_records(ip_address, view=self)
148
+
109
149
 
110
150
  @register_search
111
151
  class ViewIndex(SearchIndex):
netbox_dns/models/zone.py CHANGED
@@ -649,14 +649,16 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
649
649
  }
650
650
  )
651
651
 
652
- if self.pk is not None:
653
- old_zone = Zone.objects.get(pk=self.pk)
652
+ if not self._state.adding:
653
+ old_soa_serial = self.get_saved_value("soa_serial")
654
+ old_soa_serial_auto = self.get_saved_value("soa_serial_auto")
655
+
654
656
  if not self.soa_serial_auto:
655
- self.check_soa_serial_increment(old_zone.soa_serial, self.soa_serial)
656
- elif not old_zone.soa_serial_auto:
657
+ self.check_soa_serial_increment(old_soa_serial, self.soa_serial)
658
+ elif not old_soa_serial_auto:
657
659
  try:
658
660
  self.check_soa_serial_increment(
659
- old_zone.soa_serial, self.get_auto_serial()
661
+ old_soa_serial, self.get_auto_serial()
660
662
  )
661
663
  except ValidationError:
662
664
  raise ValidationError(
@@ -665,10 +667,13 @@ class Zone(ObjectModificationMixin, ContactsMixin, NetBoxModel):
665
667
  }
666
668
  )
667
669
 
670
+ old_name = self.get_saved_value("name")
671
+ old_view_id = self.get_saved_value("view_id")
672
+
668
673
  if (
669
674
  not self.ip_addresses_checked
670
- and old_zone.name != self.name
671
- or old_zone.view != self.view
675
+ and old_name != self.name
676
+ or old_view_id != self.view_id
672
677
  ):
673
678
  ip_addresses = IPAddress.objects.filter(
674
679
  netbox_dns_records__in=self.record_set.filter(
@@ -52,7 +52,7 @@ def ipam_dnssync_ipaddress_post_clean(instance, **kwargs):
52
52
  dns_name=instance.dns_name,
53
53
  status__in=IPADDRESS_ACTIVE_STATUS,
54
54
  )
55
- if instance.pk is not None:
55
+ if not instance._state.adding:
56
56
  duplicate_addresses = duplicate_addresses.exclude(pk=instance.pk)
57
57
 
58
58
  if ENFORCE_UNIQUE_RECORDS and instance.status in IPADDRESS_ACTIVE_STATUS:
@@ -74,7 +74,7 @@ def ipam_dnssync_ipaddress_post_clean(instance, **kwargs):
74
74
  if (request := current_request.get()) is not None:
75
75
  cf_data = instance.custom_field_data
76
76
  if (
77
- instance.pk is not None
77
+ not instance._state.adding
78
78
  and any(
79
79
  (
80
80
  cf_data.get(cf, cf_default)
@@ -86,7 +86,7 @@ def ipam_dnssync_ipaddress_post_clean(instance, **kwargs):
86
86
  )
87
87
  and not check_record_permission()
88
88
  ) or (
89
- instance.pk is None
89
+ instance._state.adding
90
90
  and any(
91
91
  (
92
92
  cf_data.get(cf, cf_default) != cf_default
@@ -129,7 +129,7 @@ def ipam_dnssync_prefix_pre_save(instance, **kwargs):
129
129
  """
130
130
  request = current_request.get()
131
131
 
132
- if instance.pk is None or not instance.netbox_dns_views.exists():
132
+ if instance._state.adding or not instance.netbox_dns_views.exists():
133
133
  return
134
134
 
135
135
  saved_prefix = Prefix.objects.prefetch_related("netbox_dns_views").get(
netbox_dns/tables/view.py CHANGED
@@ -23,7 +23,7 @@ class ViewTable(TenancyColumnsMixin, NetBoxTable):
23
23
 
24
24
  class Meta(NetBoxTable.Meta):
25
25
  model = View
26
- fields = ("description",)
26
+ fields = ("description", "ip_address_filter")
27
27
  default_columns = ("name", "default_view")
28
28
 
29
29
 
@@ -42,6 +42,7 @@
42
42
  </h5>
43
43
  <div class="card-body">
44
44
  <table class="table table-hover attr-table">
45
+ {% if object.prefixes.exists %}
45
46
  {% for prefix in object.prefixes.all %}
46
47
  <tr>
47
48
  <td>
@@ -56,9 +57,24 @@
56
57
  {% endif %}
57
58
  </tr>
58
59
  {% endfor %}
60
+ {% else %}
61
+ <span class="text-muted">No prefixes assigned</span>
62
+ {% endif %}
59
63
  </table>
60
64
  </div>
61
65
  </div>
66
+ <div class="card">
67
+ <h5 class="card-header">
68
+ IP Address Filters
69
+ </h5>
70
+ <div class="card-body">
71
+ {% if object.ip_address_filter %}
72
+ <pre>{{ object.ip_address_filter|json }}</pre>
73
+ {% else %}
74
+ <span class="text-muted">No filters defined</span>
75
+ {% endif %}
76
+ </div>
77
+ </div>
62
78
  {% endif %}
63
79
  </div>
64
80
  </div>
@@ -10,9 +10,6 @@ from django.db.models import Q
10
10
  from netbox.context import current_request
11
11
  from ipam.models import IPAddress, Prefix
12
12
 
13
- from netbox_dns.models import zone as _zone
14
- from netbox_dns.models import record as _record
15
- from netbox_dns.models import view as _view
16
13
  from netbox_dns.choices import RecordStatusChoices
17
14
 
18
15
 
@@ -26,6 +23,7 @@ __all__ = (
26
23
  "get_ip_addresses_by_view",
27
24
  "get_ip_addresses_by_zone",
28
25
  "check_record_permission",
26
+ "get_query_from_filter",
29
27
  )
30
28
 
31
29
 
@@ -74,6 +72,8 @@ def _match_data(ip_address, record):
74
72
 
75
73
 
76
74
  def get_zones(ip_address, view=None, old_zone=None):
75
+ from netbox_dns.models import Zone
76
+
77
77
  if view is None:
78
78
  views = _get_assigned_views(ip_address)
79
79
  if not views:
@@ -91,7 +91,7 @@ def get_zones(ip_address, view=None, old_zone=None):
91
91
  for i in range(min_labels + 1, len(fqdn.labels) + 1)
92
92
  ]
93
93
 
94
- zones = _zone.Zone.objects.filter(
94
+ zones = Zone.objects.filter(
95
95
  view__in=views,
96
96
  name__in=zone_name_candidates,
97
97
  active=True,
@@ -114,28 +114,28 @@ def get_zones(ip_address, view=None, old_zone=None):
114
114
 
115
115
 
116
116
  def check_dns_records(ip_address, zone=None, view=None):
117
+ from netbox_dns.models import Zone, Record
118
+
117
119
  if ip_address.dns_name == "":
118
120
  return
119
121
 
120
122
  if zone is None:
121
123
  zones = get_zones(ip_address, view=view)
122
124
 
123
- if ip_address.pk is not None:
125
+ if not ip_address._state.adding:
124
126
  for record in ip_address.netbox_dns_records.filter(zone__in=zones):
125
127
  if not _match_data(ip_address, record):
126
- record.update_from_ip_address(ip_address)
128
+ updated = record.update_from_ip_address(ip_address)
127
129
 
128
- if record is not None:
130
+ if updated:
129
131
  record.clean()
130
132
 
131
- zones = _zone.Zone.objects.filter(
132
- pk__in=[zone.pk for zone in zones]
133
- ).exclude(
133
+ zones = Zone.objects.filter(pk__in=[zone.pk for zone in zones]).exclude(
134
134
  pk__in=set(ip_address.netbox_dns_records.values_list("zone", flat=True))
135
135
  )
136
136
 
137
137
  for zone in zones:
138
- record = _record.Record.create_from_ip_address(
138
+ record = Record.create_from_ip_address(
139
139
  ip_address,
140
140
  zone,
141
141
  )
@@ -143,7 +143,7 @@ def check_dns_records(ip_address, zone=None, view=None):
143
143
  if record is not None:
144
144
  record.clean()
145
145
 
146
- if ip_address.pk is None:
146
+ if ip_address._state.adding:
147
147
  return
148
148
 
149
149
  try:
@@ -152,21 +152,28 @@ def check_dns_records(ip_address, zone=None, view=None):
152
152
  return
153
153
 
154
154
  for record in ip_address.netbox_dns_records.filter(zone=zone):
155
- record.update_from_ip_address(ip_address, new_zone)
155
+ updated = record.update_from_ip_address(ip_address, new_zone)
156
156
 
157
- if record is not None:
157
+ if updated:
158
158
  record.clean(new_zone=new_zone)
159
159
 
160
160
 
161
- def update_dns_records(ip_address):
161
+ def update_dns_records(ip_address, view=None):
162
+ from netbox_dns.models import Zone, Record
163
+
162
164
  if ip_address.dns_name == "":
163
165
  delete_dns_records(ip_address)
164
166
  return
165
167
 
166
- zones = get_zones(ip_address)
168
+ zones = get_zones(ip_address, view=view)
167
169
 
168
- if ip_address.pk is not None:
169
- for record in ip_address.netbox_dns_records.all():
170
+ if not ip_address._state.adding:
171
+ if view is None:
172
+ address_records = ip_address.netbox_dns_records.all()
173
+ else:
174
+ address_records = ip_address.netbox_dns_records.filter(zone__view=view)
175
+
176
+ for record in address_records:
170
177
  if record.zone not in zones or ip_address.custom_field_data.get(
171
178
  "ipaddress_dns_disabled"
172
179
  ):
@@ -175,19 +182,21 @@ def update_dns_records(ip_address):
175
182
 
176
183
  record.update_fqdn()
177
184
  if not _match_data(ip_address, record):
178
- record.update_from_ip_address(ip_address)
185
+ updated, deleted = record.update_from_ip_address(ip_address)
179
186
 
180
- if record is not None:
187
+ if deleted:
188
+ record.delete()
189
+ elif updated:
181
190
  record.save()
182
191
 
183
- zones = _zone.Zone.objects.filter(pk__in=[zone.pk for zone in zones]).exclude(
192
+ zones = Zone.objects.filter(pk__in=[zone.pk for zone in zones]).exclude(
184
193
  pk__in=set(
185
194
  ip_address.netbox_dns_records.all().values_list("zone", flat=True)
186
195
  )
187
196
  )
188
197
 
189
198
  for zone in zones:
190
- record = _record.Record.create_from_ip_address(
199
+ record = Record.create_from_ip_address(
191
200
  ip_address,
192
201
  zone,
193
202
  )
@@ -196,19 +205,27 @@ def update_dns_records(ip_address):
196
205
  record.save()
197
206
 
198
207
 
199
- def delete_dns_records(ip_address):
200
- for record in ip_address.netbox_dns_records.all():
208
+ def delete_dns_records(ip_address, view=None):
209
+
210
+ if view is None:
211
+ address_records = ip_address.netbox_dns_records.all()
212
+ else:
213
+ address_records = ip_address.netbox_dns_records.filter(zone__view=view)
214
+
215
+ for record in address_records:
201
216
  record.delete()
202
217
 
203
218
 
204
219
  def get_views_by_prefix(prefix):
220
+ from netbox_dns.models import View
221
+
205
222
  if (views := prefix.netbox_dns_views.all()).exists():
206
223
  return views
207
224
 
208
225
  if (parent := prefix.get_parents().filter(netbox_dns_views__isnull=False)).exists():
209
226
  return parent.last().netbox_dns_views.all()
210
227
 
211
- return _view.View.objects.none()
228
+ return View.objects.none()
212
229
 
213
230
 
214
231
  def get_ip_addresses_by_prefix(prefix, check_view=True):
@@ -293,3 +310,18 @@ def check_record_permission(add=True, change=True, delete=True):
293
310
  if check
294
311
  )
295
312
  )
313
+
314
+
315
+ def get_query_from_filter(ip_address_filter):
316
+ query = Q()
317
+
318
+ if not isinstance(ip_address_filter, list):
319
+ ip_address_filter = [ip_address_filter]
320
+
321
+ for condition in ip_address_filter:
322
+ if condition:
323
+ query |= Q(**condition)
324
+ else:
325
+ return Q()
326
+
327
+ return query
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: netbox-plugin-dns
3
- Version: 1.1.1
3
+ Version: 1.1.2
4
4
  Summary: NetBox DNS is a NetBox plugin for managing DNS data.
5
5
  Author-email: Peter Eckel <pete@netbox-dns.org>
6
6
  Project-URL: Homepage, https://github.com/peteeckel/netbox-plugin-dns
@@ -1,4 +1,4 @@
1
- netbox_dns/__init__.py,sha256=NoPPcY-YX4BZdly6P1seECNpt9WQpptx5W9ddkGjXwg,1920
1
+ netbox_dns/__init__.py,sha256=6S7LL8UGPVpKSir7BCdth9yKtWtyRVKikVjQY58W62E,1956
2
2
  netbox_dns/apps.py,sha256=JCW5eS-AQBUubDJve1DjP-IRFKTFGQh1NLGWzJpC5MI,151
3
3
  netbox_dns/navigation.py,sha256=YAEfmqoNRSXLSf-n3HLImDXbAfzcRdCh57BVnofmWPQ,6046
4
4
  netbox_dns/template_content.py,sha256=YzE-ZJlERhFybrUKJrmNwHk8_8RPNexkV66x62Sbzic,3718
@@ -13,7 +13,7 @@ netbox_dns/api/serializers_/record.py,sha256=62924HonaKflFXkTgFft_624BKEikAMkak1
13
13
  netbox_dns/api/serializers_/record_template.py,sha256=qh-g-_f6M-eU4GzN-l0nPxiNBpZyYBgib26HwfOMtUc,1466
14
14
  netbox_dns/api/serializers_/registrar.py,sha256=xLIaeBJ5ckV1Jf-uyCTFcvsLlsRMlpDtIg6q79vXZic,842
15
15
  netbox_dns/api/serializers_/registration_contact.py,sha256=3IGWW5xB9XEBGApCGZCZIxpCmy1Y5jQUbA4GzmtaCik,1024
16
- netbox_dns/api/serializers_/view.py,sha256=u5HqZtPzGCBTFCsH5GkPGsyG-oR4WIJ8XOqdWe3oUow,1723
16
+ netbox_dns/api/serializers_/view.py,sha256=_0fqdhvHU0scqc8ZNDSCcxaQDY-Auxu0Q9LLzlnrGqc,1756
17
17
  netbox_dns/api/serializers_/zone.py,sha256=vQMKyC1Pcnvp7OjRnSZt-cMP5iae9HLuMts6_JyxrG4,4935
18
18
  netbox_dns/api/serializers_/zone_template.py,sha256=itwcdMe8NxMbH95yXBJTQkzzMOWzOvzTOOHJWKfEFgg,3782
19
19
  netbox_dns/choices/__init__.py,sha256=jOVs2VGV5SVADRlqVnrFeAy26i8BIeEAbGpiX7K8bL8,42
@@ -39,13 +39,13 @@ netbox_dns/forms/record.py,sha256=svBVAFy-egDEPLcRWkxNi_1bkabKmWgJ87pmdNt6dh4,71
39
39
  netbox_dns/forms/record_template.py,sha256=Q77p9sExJ8Xbl-Co2Px2R0At5O3naQJwx4pnino6i2o,5573
40
40
  netbox_dns/forms/registrar.py,sha256=FMnvrcq62R3wNp_2ZUEk3v_PIav0KrWPATaJ7_9KFAo,3758
41
41
  netbox_dns/forms/registration_contact.py,sha256=WHpIRnwPD7nIDKi0-zRdv7tLUN2t_n3rmt_mZrqjfEk,5524
42
- netbox_dns/forms/view.py,sha256=DkfZOLkCClTF-7pLMiZE8J1Z9_oxmeBiEWZ7-_yTZro,9045
42
+ netbox_dns/forms/view.py,sha256=x0Q22BlkQL60XEyRcrdgl6cTI3plzB4u8pqmcwZiJ2c,9808
43
43
  netbox_dns/forms/zone.py,sha256=6NI3uiI2bXIhzPM7CTxR4pbv198xmU-QHEizy16Qb-E,23601
44
44
  netbox_dns/forms/zone_template.py,sha256=D0JgQE03uQ_vR6zHkS78a5PzUziNSu1ho_mk61JTRo4,8317
45
45
  netbox_dns/graphql/__init__.py,sha256=jghYD6uOSAis6YyLbtI3YJGZfwPw1uL2FBRsHs1EhNk,514
46
46
  netbox_dns/graphql/filters.py,sha256=fHCjFIwbPBJJMk2W7HI8LhrfFhCtQtCM9IE8ZMgVafc,1766
47
- netbox_dns/graphql/schema.py,sha256=z_56Co3d-cmswmnY8MeOryLOGZEGK4WwYW9eekNuYRw,2430
48
- netbox_dns/graphql/types.py,sha256=W8uKiJrPCu1vnRbG4dj4pAaNiE5Nvnt0mmWCrSoXm68,6652
47
+ netbox_dns/graphql/schema.py,sha256=q9DQ_hfRB0e6Znq4-IS6UEeTOfMkZmrWkwxcAql1uOA,2270
48
+ netbox_dns/graphql/types.py,sha256=24m-qblJoauYzu5n1UDsw_IUvr_r3PRGO-VDt9aKQ3o,6686
49
49
  netbox_dns/management/commands/cleanup_database.py,sha256=kfnyybudwKGigjJmrOwafPWSUasZr9jQsxN4eWAgMvY,5969
50
50
  netbox_dns/management/commands/cleanup_rrset_ttl.py,sha256=UFRURLBcFeGHUS2lrYFv7UWIebjI72aG1EUQJt0XsXw,2046
51
51
  netbox_dns/management/commands/rebuild_dnssync.py,sha256=56YGEsAO_NYCXRmuL9Ju_Ni3FLtMD0MO4ObZAAPefLk,617
@@ -61,6 +61,7 @@ netbox_dns/migrations/0006_templating.py,sha256=7MOZ2bLwkjK1BQCBPBaFWJ-mS_JNrok1
61
61
  netbox_dns/migrations/0007_alter_ordering_options.py,sha256=IDGgxEWOaSs9_WKJK1C_5_6M1uJtYscn3Vco0UHAol8,639
62
62
  netbox_dns/migrations/0008_view_prefixes.py,sha256=LInzrOXTflGd2WUmyXZtjEegg7S_KBNvrbdo3jE28fE,472
63
63
  netbox_dns/migrations/0009_rename_contact_registrationcontact.py,sha256=-8IknnaIZxoBaf5uSQj8rVe0SRvYsuhcJ0_jzRh4EUk,1314
64
+ netbox_dns/migrations/0010_view_ip_address_filter.py,sha256=uARQADJB7u1vpx0TBlOfGTkqMCF4xZclMhITESHm-ok,420
64
65
  netbox_dns/migrations/0020_netbox_3_4.py,sha256=UMcHdn8ZAuQjUaM_3rEGpktYrM0TuvhccD7Jt7WQnPs,1271
65
66
  netbox_dns/migrations/0021_record_ip_address.py,sha256=EqdhWXmq7aiK4X79xTRUZng3zFncCl-8JoO65HqlJKw,3244
66
67
  netbox_dns/migrations/0022_search.py,sha256=KW1ffEZ4-0dppGQ_KD1EN7iw8eQJOnDco-xfJFRZqKQ,172
@@ -73,18 +74,18 @@ netbox_dns/migrations/0028_rfc2317_fields.py,sha256=D8r43xxBjYXiL6ycmX8RY5_WG7tR
73
74
  netbox_dns/migrations/0029_record_fqdn.py,sha256=UAAU38ekKQyiYDOJlcrz6Qbk4bqZfSHZyAHUZFFQrOw,808
74
75
  netbox_dns/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
76
  netbox_dns/mixins/__init__.py,sha256=LxTEfpod_RHCyMtnzDljv0_dwqp2z3Q6tqbXW8LTGD8,35
76
- netbox_dns/mixins/object_modification.py,sha256=JbGi8a52wkZ3fFBlfat590CfqRJcEWxBsxSeTIx3Qtg,833
77
+ netbox_dns/mixins/object_modification.py,sha256=biLbHOkayEdKPu-wxuYu7ZIXhB3cfv9k07asrQCtFb0,1658
77
78
  netbox_dns/models/__init__.py,sha256=5Ns9RaemTe5L0L3c6a38RxembWhV-sX9cqfjl05aPQw,313
78
79
  netbox_dns/models/nameserver.py,sha256=gvQRcTOVHRiX_rcBZfjNV6rTRKNJtA5HxgggYEgITPA,3296
79
- netbox_dns/models/record.py,sha256=O8w6PtuUIaFyrZg1ghIe-ZSrdQ9snBEmOeiSlR5mBPQ,26454
80
+ netbox_dns/models/record.py,sha256=RDdGAZVKR--ix-r04bV32nMhKJnG_aUaFgMyt-SbRHo,27222
80
81
  netbox_dns/models/record_template.py,sha256=pyzrtk-VFEHf9hrWTW6pwPKo2AH5cEKR0XI4ZG9nVHk,4753
81
82
  netbox_dns/models/registrar.py,sha256=zCSVa6NSN9sRJzvvuSUNK4LcHBbe0OvEiTbIcrrdk9k,1671
82
83
  netbox_dns/models/registration_contact.py,sha256=v5BxTtGPrR22TyqIQkgaSH26ozhzIVrkU-PuezHki-g,3217
83
- netbox_dns/models/view.py,sha256=hSvbb-xc7r3AyTvuxvCnWGqG7gpTkeO_FsZoHXCVMzY,3029
84
- netbox_dns/models/zone.py,sha256=Xvg4gkskh5s6FlrA4AoAPB8aQ_Z5fBaXd8ACtgaFuEI,29598
84
+ netbox_dns/models/view.py,sha256=skVcXB1krvSn1-gmvnNnj5o76AsTYPinaIkSXNbBOAg,4396
85
+ netbox_dns/models/zone.py,sha256=Mo63r1OLbbya4LGZbomIqjvibwojEwqEdV0I7umnK48,29780
85
86
  netbox_dns/models/zone_template.py,sha256=LvNqUi_Ucj3zHb9b778kFM2EZ1s9aXp980Et5g7lvTY,3889
86
87
  netbox_dns/signals/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
87
- netbox_dns/signals/ipam_dnssync.py,sha256=ToGg2Z_L3wvM6gNHX-oU8QTEkbrsXuVt7J6IcQbGb9A,7964
88
+ netbox_dns/signals/ipam_dnssync.py,sha256=GXsKk2c9KlNb8b6Sb58sPZ-nDFKLkL0cN7TEgc08GGU,7976
88
89
  netbox_dns/tables/__init__.py,sha256=axENVF9vX9BtDKCNxrapRjye1NnygUg9BS0BBj6a0io,209
89
90
  netbox_dns/tables/ipam_dnssync.py,sha256=7gxf6nXn2zgpnpCr5E5qebXv7QCzrgIqfSzyka53USU,272
90
91
  netbox_dns/tables/nameserver.py,sha256=GGi8x8QXbrFoFjxueL1zd5L-hDZwXGs1PcLZ2DyI4JI,652
@@ -92,7 +93,7 @@ netbox_dns/tables/record.py,sha256=QTK-AQMwEG7pjR3fkFo6E45FzGsdCKLpDZLzudRiJRQ,3
92
93
  netbox_dns/tables/record_template.py,sha256=CcXvyGycwp16ZwQFdmdt5W5Tlt__i1sBi3mixVvht3E,1763
93
94
  netbox_dns/tables/registrar.py,sha256=Vtl3RKjhWzUDv4wAfjbNV1prZHf6SyGYmSS0XKccI9g,640
94
95
  netbox_dns/tables/registration_contact.py,sha256=xUszp7_vbZ79KHewBZkSyxNcWE3W3o8O6Pp0N-8VDXg,838
95
- netbox_dns/tables/view.py,sha256=w6tq_zDXGf1-MVmoCoZEuPajiuH_rccRH5eU5eurQ_s,1038
96
+ netbox_dns/tables/view.py,sha256=u2XP66nrv1cmeBxQL9VM2c1wtoM_quZFB0B8O5-3IEE,1058
96
97
  netbox_dns/tables/zone.py,sha256=bVi4sLFdw61kfIiRJnoJOtgkVGGlTZcogw-cAzFtAEQ,1507
97
98
  netbox_dns/tables/zone_template.py,sha256=gezSSpbNkiHnstNq470OsTmkl13foFXTGx-NH8W87Hw,1204
98
99
  netbox_dns/templates/netbox_dns/nameserver.html,sha256=DpTdetQVV_jKThDbi62LvbhiCay-1QxR-yiJEiPFm4w,1554
@@ -100,7 +101,7 @@ netbox_dns/templates/netbox_dns/record.html,sha256=o3z_D6Fqqn7nx1IwPXKQ75ZaPhU6k
100
101
  netbox_dns/templates/netbox_dns/recordtemplate.html,sha256=9tkXtKqa5p3LdOU9REm99WSFwGJaH8OczpIqXZuXMcg,3099
101
102
  netbox_dns/templates/netbox_dns/registrar.html,sha256=O5veGmW59Pf5yN25ihPLvRIkA2P7xmSGv0G3NrRG8vI,2152
102
103
  netbox_dns/templates/netbox_dns/registrationcontact.html,sha256=4I3I7G38bIOVifoqgGMyPTAhD0UaD8ZCnXK7Fu9K9VQ,2868
103
- netbox_dns/templates/netbox_dns/view.html,sha256=zqf42FGdNudoIIWCaCe9bmP_VOZDbv3GjS-qJNMKPVY,2479
104
+ netbox_dns/templates/netbox_dns/view.html,sha256=UmUTudA8CsTIInEXaM4ocRDRPlcZ4pTQfcolfPe_jIQ,3165
104
105
  netbox_dns/templates/netbox_dns/zone.html,sha256=zPi1sLFnya-ytx8-Pcf_ujc1sKllCRuz48x2E-S1HSo,6285
105
106
  netbox_dns/templates/netbox_dns/zonetemplate.html,sha256=qjUWqrg4uDaIFt9-g0OBxTi86i_ctf2Ps4qHh11djQE,3081
106
107
  netbox_dns/templates/netbox_dns/record/managed.html,sha256=G6LPG1koUGuzUiwYdv1okdVa4sKaofiQegDBnsFL0kA,89
@@ -126,7 +127,7 @@ netbox_dns/urls/zone.py,sha256=EzZ_U5v9NfWB5TVAc0i35EI-SVyXl6KrI844sMT0x5Q,937
126
127
  netbox_dns/urls/zone_template.py,sha256=nGrIaincQxCabUsLJL9JODoeTToMRSPllm7kuiPzeII,1378
127
128
  netbox_dns/utilities/__init__.py,sha256=mmR0JdH1DJVhUKesnO3CZFj0Rw_wrsMPoYTpOOKHl9I,55
128
129
  netbox_dns/utilities/conversions.py,sha256=NS37SoMqXc13wNWRkKnLfyQbVi6QKD33fu5ovTKRo74,1979
129
- netbox_dns/utilities/ipam_dnssync.py,sha256=YpB6ocdsTFU5KYx6k4MIekH5PMkbYjQpETy-YdrGnxs,8626
130
+ netbox_dns/utilities/ipam_dnssync.py,sha256=b4up7GXuAc4ba-cHmS56vWFPEmYVSsQG_Dd-3pHndrI,9379
130
131
  netbox_dns/validators/__init__.py,sha256=Mr8TvmcJTa8Pubj8TzbFBKfbHhEmGcr5JdQvczEJ39A,72
131
132
  netbox_dns/validators/dns_name.py,sha256=4JojiP6pb1Z1m_PmDv4g65Ckhg5rQkVqm8JAwHW28nA,3432
132
133
  netbox_dns/validators/dns_value.py,sha256=y2Zga4hmywqDrTBXcMC-sWaFbw4eoY8pySq7cWnMP8Y,2822
@@ -140,8 +141,8 @@ netbox_dns/views/registration_contact.py,sha256=Yg4gTldQQPfAFgjD7vultw3OmcovWZti
140
141
  netbox_dns/views/view.py,sha256=-qdpEVrAI_fquZa-_jW2FvNXmCt3yRHIrkfgKKi01hc,2899
141
142
  netbox_dns/views/zone.py,sha256=Na286tp_XcnT-4FIgXsvh1wPByir0RtBil0B2JhfuZE,5495
142
143
  netbox_dns/views/zone_template.py,sha256=UaPEReBFoJP1k7MC8jS-iT3KQtJqSFu67jjCRiTCH4c,2118
143
- netbox_plugin_dns-1.1.1.dist-info/LICENSE,sha256=I3tDu11bZfhFm3EkV4zOD5TmWgLjnUNLEFwrdjniZYs,1112
144
- netbox_plugin_dns-1.1.1.dist-info/METADATA,sha256=UDTGeYxoZOQyBwuA0uXKXNfZS4j7MRHeG1aGueuLPT0,7129
145
- netbox_plugin_dns-1.1.1.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
146
- netbox_plugin_dns-1.1.1.dist-info/top_level.txt,sha256=sA1Rwl1mRKvMC6XHe2ylZ1GF-Q1NGd08XedK9Y4xZc4,11
147
- netbox_plugin_dns-1.1.1.dist-info/RECORD,,
144
+ netbox_plugin_dns-1.1.2.dist-info/LICENSE,sha256=I3tDu11bZfhFm3EkV4zOD5TmWgLjnUNLEFwrdjniZYs,1112
145
+ netbox_plugin_dns-1.1.2.dist-info/METADATA,sha256=cSzLVUftq3LnCskiVNmYcpuHYqsNUrpzUNp0lIOQ50k,7129
146
+ netbox_plugin_dns-1.1.2.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
147
+ netbox_plugin_dns-1.1.2.dist-info/top_level.txt,sha256=sA1Rwl1mRKvMC6XHe2ylZ1GF-Q1NGd08XedK9Y4xZc4,11
148
+ netbox_plugin_dns-1.1.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (74.1.2)
2
+ Generator: setuptools (75.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5