netbox-plugin-dns 1.1.0b2__py3-none-any.whl → 1.1b3__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.0b2"
8
+ __version__ = "1.1b3"
9
9
 
10
10
 
11
11
  class DNSConfig(PluginConfig):
netbox_dns/forms/view.py CHANGED
@@ -21,6 +21,7 @@ from utilities.forms.rendering import FieldSet
21
21
  from tenancy.models import Tenant
22
22
  from tenancy.forms import TenancyForm, TenancyFilterForm
23
23
  from ipam.models import Prefix
24
+ from netbox.context import current_request
24
25
 
25
26
  from netbox_dns.models import View
26
27
  from netbox_dns.fields import PrefixDynamicModelMultipleChoiceField
@@ -37,6 +38,7 @@ __all__ = (
37
38
  "ViewFilterForm",
38
39
  "ViewImportForm",
39
40
  "ViewBulkEditForm",
41
+ "ViewPrefixEditForm",
40
42
  )
41
43
 
42
44
 
@@ -97,6 +99,21 @@ class ViewForm(ViewPrefixUpdateMixin, TenancyForm, NetBoxModelForm):
97
99
  if settings.PLUGINS_CONFIG["netbox_dns"].get("autodns_disabled"):
98
100
  del self.fields["prefixes"]
99
101
 
102
+ if request := current_request.get():
103
+ if not request.user.has_perm("ipam.view_prefix"):
104
+ self._saved_prefixes = self.initial["prefixes"]
105
+ self.initial["prefixes"] = []
106
+ self.fields["prefixes"].disabled = True
107
+ self.fields["prefixes"].widget.attrs[
108
+ "placeholder"
109
+ ] = "You do not have permission to modify assigned prefixes"
110
+
111
+ def clean_prefixes(self):
112
+ if hasattr(self, "_saved_prefixes"):
113
+ return self._saved_prefixes
114
+
115
+ return self.cleaned_data["prefixes"]
116
+
100
117
  prefixes = PrefixDynamicModelMultipleChoiceField(
101
118
  queryset=Prefix.objects.all(),
102
119
  required=False,
@@ -201,3 +218,74 @@ class ViewBulkEditForm(NetBoxModelBulkEditForm):
201
218
  )
202
219
 
203
220
  nullable_fields = ("description", "tenant")
221
+
222
+
223
+ class ViewPrefixEditForm(forms.ModelForm):
224
+ views = DynamicModelMultipleChoiceField(
225
+ queryset=View.objects.all(),
226
+ required=False,
227
+ label="Assigned DNS Views",
228
+ help_text="Explicitly assigning DNS views overrides all inherited views for this prefix",
229
+ )
230
+
231
+ class Meta:
232
+ model = Prefix
233
+ fields = ("views",)
234
+
235
+ def __init__(self, *args, **kwargs):
236
+ super().__init__(*args, **kwargs)
237
+
238
+ self.initial["views"] = self.instance.netbox_dns_views.all()
239
+ self._permission_denied = False
240
+
241
+ if request := current_request.get():
242
+ if not request.user.has_perm("netbox_dns.change_view"):
243
+ self._permission_denied = True
244
+ self.initial["views"] = []
245
+ self.fields["views"].disabled = True
246
+ self.fields["views"].widget.attrs[
247
+ "placeholder"
248
+ ] = "You do not have permission to modify assigned views"
249
+
250
+ def clean(self, *args, **kwargs):
251
+ if self._permission_denied:
252
+ return
253
+
254
+ prefix = self.instance
255
+
256
+ super().clean(*args, **kwargs)
257
+
258
+ views = self.cleaned_data.get("views")
259
+ old_views = prefix.netbox_dns_views.all()
260
+
261
+ check_views = View.objects.none()
262
+
263
+ if not views.exists():
264
+ if (parent := prefix.get_parents().last()) is not None:
265
+ check_views = parent.netbox_dns_views.all().difference(old_views)
266
+
267
+ else:
268
+ check_views = views.difference(old_views)
269
+
270
+ for view in check_views:
271
+ try:
272
+ for ip_address in get_ip_addresses_by_prefix(prefix, check_view=False):
273
+ check_dns_records(ip_address, view=view)
274
+ except ValidationError as exc:
275
+ self.add_error("views", exc.messages)
276
+
277
+ def save(self, *args, **kwargs):
278
+ prefix = self.instance
279
+
280
+ if self._permission_denied:
281
+ return prefix
282
+
283
+ old_views = prefix.netbox_dns_views.all()
284
+ views = self.cleaned_data.get("views")
285
+
286
+ for view in views.difference(old_views):
287
+ view.prefixes.add(prefix)
288
+ for view in old_views.difference(views):
289
+ view.prefixes.remove(prefix)
290
+
291
+ return prefix
netbox_dns/forms/zone.py CHANGED
@@ -81,6 +81,14 @@ class ZoneTemplateUpdateMixin:
81
81
  else:
82
82
  zone_data = self.cleaned_data.copy()
83
83
 
84
+ custom_fields = dict()
85
+ for key, value in zone_data.copy().items():
86
+ if key.startswith("cf_"):
87
+ custom_fields[key[3:]] = value
88
+ zone_data.pop(key)
89
+ if custom_fields:
90
+ zone_data["custom_field_data"] = custom_fields
91
+
84
92
  zone_data.pop("template", None)
85
93
  zone_data.pop("tenant_group", None)
86
94
  zone_data.pop("_init_time", None)
@@ -30,7 +30,7 @@ class Command(BaseCommand):
30
30
  name=cf, object_types=ipaddress_object_type
31
31
  ).delete()
32
32
  if options.get("verbosity"):
33
- self.stdout.write(f"Custom field '{cf}' removed")
33
+ self.stdout.write(f"Removed custom field '{cf}'")
34
34
  except CustomField.DoesNotExist:
35
35
  pass
36
36
  return
@@ -84,6 +84,7 @@ class Command(BaseCommand):
84
84
  )
85
85
  if cf_ttl.group_name != "AutoDNS":
86
86
  cf_ttl.group_name = "AutoDNS"
87
+ cf_ttl.description = ("TTL for DNS records created for this address",)
87
88
  cf_ttl.save()
88
89
  if options.get("verbosity"):
89
90
  self.stdout.write("Updated custom field 'ipaddress_dns_record_ttl'")
@@ -112,6 +113,9 @@ class Command(BaseCommand):
112
113
  )
113
114
  if cf_disable_ptr.group_name != "AutoDNS":
114
115
  cf_disable_ptr.group_name = "AutoDNS"
116
+ cf_disable_ptr.description = (
117
+ "Disable DNS PTR record generation for this address",
118
+ )
115
119
  cf_disable_ptr.save()
116
120
  if options.get("verbosity"):
117
121
  self.stdout.write(
@@ -461,8 +461,11 @@ class Record(ObjectModificationMixin, NetBoxModel):
461
461
  self.rfc2317_cname_record.delete(save_zone_serial=save_zone_serial)
462
462
  self.rfc2317_cname_record = None
463
463
 
464
- def update_from_ip_address(self, ip_address):
465
- data = record_data_from_ip_address(ip_address, self.zone)
464
+ def update_from_ip_address(self, ip_address, zone=None):
465
+ if zone is None:
466
+ zone = self.zone
467
+
468
+ data = record_data_from_ip_address(ip_address, zone)
466
469
 
467
470
  if data is None:
468
471
  self.delete()
@@ -490,9 +493,12 @@ class Record(ObjectModificationMixin, NetBoxModel):
490
493
  **data,
491
494
  )
492
495
 
493
- def validate_name(self):
496
+ def validate_name(self, new_zone=None):
497
+ if new_zone is None:
498
+ new_zone = self.zone
499
+
494
500
  try:
495
- _zone = dns_name.from_text(self.zone.name, origin=dns_name.root)
501
+ _zone = dns_name.from_text(new_zone.name, origin=dns_name.root)
496
502
  name = dns_name.from_text(self.name, origin=None)
497
503
  fqdn = dns_name.from_text(self.name, origin=_zone)
498
504
 
@@ -512,7 +518,7 @@ class Record(ObjectModificationMixin, NetBoxModel):
512
518
  if not fqdn.is_subdomain(_zone):
513
519
  raise ValidationError(
514
520
  {
515
- "name": f"{self.name} is not a name in {self.zone.name}",
521
+ "name": f"{self.name} is not a name in {new_zone.name}",
516
522
  }
517
523
  )
518
524
 
@@ -544,15 +550,18 @@ class Record(ObjectModificationMixin, NetBoxModel):
544
550
  except ValidationError as exc:
545
551
  raise ValidationError({"value": exc}) from None
546
552
 
547
- def check_unique_record(self):
553
+ def check_unique_record(self, new_zone=None):
548
554
  if not get_plugin_config("netbox_dns", "enforce_unique_records", False):
549
555
  return
550
556
 
551
557
  if not self.is_active:
552
558
  return
553
559
 
560
+ if new_zone is None:
561
+ new_zone = self.zone
562
+
554
563
  records = Record.objects.filter(
555
- zone=self.zone,
564
+ zone=new_zone,
556
565
  name=self.name,
557
566
  type=self.type,
558
567
  value=self.value,
@@ -664,10 +673,10 @@ class Record(ObjectModificationMixin, NetBoxModel):
664
673
  self.type = self.type.upper()
665
674
  super().clean_fields(*args, **kwargs)
666
675
 
667
- def clean(self, *args, **kwargs):
668
- self.validate_name()
676
+ def clean(self, *args, new_zone=None, **kwargs):
677
+ self.validate_name(new_zone=new_zone)
669
678
  self.validate_value()
670
- self.check_unique_record()
679
+ self.check_unique_record(new_zone=new_zone)
671
680
  if self.pk is None:
672
681
  self.check_unique_rrset_ttl()
673
682
 
netbox_dns/models/zone.py CHANGED
@@ -654,13 +654,18 @@ class Zone(ObjectModificationMixin, NetBoxModel):
654
654
  )
655
655
 
656
656
  if old_zone.name != self.name or old_zone.view != self.view:
657
- update_ip_addresses = IPAddress.objects.filter(
658
- pk__in=self.record_set.filter(
657
+ for ip_address in get_ip_addresses_by_zone(self):
658
+ try:
659
+ check_dns_records(ip_address, zone=self)
660
+ except ValidationError as exc:
661
+ raise ValidationError(exc.messages)
662
+
663
+ ip_addresses = IPAddress.objects.filter(
664
+ netbox_dns_records__in=self.record_set.filter(
659
665
  ipam_ip_address__isnull=False
660
- ).values_list("ipam_ip_address", flat=True)
666
+ )
661
667
  )
662
- update_ip_addresses |= get_ip_addresses_by_zone(self)
663
- for ip_address in update_ip_addresses:
668
+ for ip_address in ip_addresses:
664
669
  try:
665
670
  check_dns_records(ip_address, zone=self)
666
671
  except ValidationError as exc:
@@ -1,5 +1,6 @@
1
1
  from netaddr import IPNetwork
2
2
 
3
+ from django.conf import settings
3
4
  from django.dispatch import receiver
4
5
  from django.db.models.signals import pre_delete, pre_save, post_save, m2m_changed
5
6
  from django.core.exceptions import ValidationError
@@ -26,12 +27,47 @@ AUTODNS_CUSTOM_FIELDS = {
26
27
  "ipaddress_dns_record_disable_ptr": False,
27
28
  }
28
29
 
30
+ IPADDRESS_ACTIVE_STATUS = settings.PLUGINS_CONFIG["netbox_dns"][
31
+ "autodns_ipaddress_active_status"
32
+ ]
33
+ ENFORCE_UNIQUE_RECORDS = settings.PLUGINS_CONFIG["netbox_dns"]["enforce_unique_records"]
34
+
29
35
 
30
36
  @receiver(post_clean, sender=IPAddress)
31
37
  def ipam_autodns_ipaddress_post_clean(instance, **kwargs):
32
38
  if not isinstance(instance.address, IPNetwork):
33
39
  return
34
40
 
41
+ if instance.custom_field_data.get("ipaddress_dns_disabled"):
42
+ return
43
+
44
+ # +
45
+ # Check for uniqueness of IP address and dns_name. If unique records are
46
+ # enforced, report an error when trying to create the same IP address with
47
+ # the same dns_name. Ignore existing IP addresses that have their CF
48
+ # "ipaddress_dns_disabled" set to "True".
49
+ # -
50
+ duplicate_addresses = IPAddress.objects.filter(
51
+ address=instance.address,
52
+ vrf=instance.vrf,
53
+ dns_name=instance.dns_name,
54
+ status__in=IPADDRESS_ACTIVE_STATUS,
55
+ )
56
+ if instance.pk is not None:
57
+ duplicate_addresses = duplicate_addresses.exclude(pk=instance.pk)
58
+
59
+ if ENFORCE_UNIQUE_RECORDS and instance.status in IPADDRESS_ACTIVE_STATUS:
60
+ for ip_address in duplicate_addresses.only("custom_field_data"):
61
+ if not ip_address.custom_field_data.get("ipaddress_dns_disabled"):
62
+ raise ValidationError(
63
+ {
64
+ "dns_name": "Unique DNS records are enforced and there is already "
65
+ f"an active IP address {instance.address} with DNS name {instance.dns_name}. "
66
+ "Plesase choose a different name or disable record creation for this "
67
+ "IP address."
68
+ }
69
+ )
70
+
35
71
  # +
36
72
  # Check NetBox DNS record permission for changes to IPAddress custom fields
37
73
  #
@@ -42,21 +78,23 @@ def ipam_autodns_ipaddress_post_clean(instance, **kwargs):
42
78
  instance.pk is not None
43
79
  and any(
44
80
  (
45
- cf_data.get(cf)
46
- != IPAddress.objects.get(pk=instance.pk).custom_field_data.get(cf)
47
- for cf in AUTODNS_CUSTOM_FIELDS.keys()
81
+ cf_data.get(cf, cf_default)
82
+ != IPAddress.objects.get(pk=instance.pk).custom_field_data.get(
83
+ cf, cf_default
84
+ )
85
+ for cf, cf_default in AUTODNS_CUSTOM_FIELDS.items()
48
86
  )
49
87
  )
50
- and not check_record_permission(request)
88
+ and not check_record_permission()
51
89
  ) or (
52
90
  instance.pk is None
53
91
  and any(
54
92
  (
55
- cf_data.get(cf) != cf_default
93
+ cf_data.get(cf, cf_default) != cf_default
56
94
  for cf, cf_default in AUTODNS_CUSTOM_FIELDS.items()
57
95
  )
58
96
  )
59
- and not check_record_permission(request, change=False, delete=False)
97
+ and not check_record_permission(change=False, delete=False)
60
98
  ):
61
99
  raise ValidationError(
62
100
  f"User '{request.user}' is not allowed to alter AutoDNS custom fields"
@@ -87,16 +125,29 @@ def ipam_autodns_ipaddress_post_save(instance, **kwargs):
87
125
  def ipam_autodns_prefix_pre_save(instance, **kwargs):
88
126
  """
89
127
  Changes that modify the prefix hierarchy cannot be validated properly before
90
- commiting them. So the solution in this case is to remove a prefix whose
91
- VRF or network has changed from all views it currently is assigned to.
128
+ commiting them. So the solution in this case is to ask the user to deassign
129
+ the prefix from any views it is assigned to and retry.
92
130
  """
131
+ request = current_request.get()
132
+
93
133
  if instance.pk is None or not instance.netbox_dns_views.exists():
94
134
  return
95
135
 
96
- saved_prefix = Prefix.objects.get(pk=instance.pk)
136
+ saved_prefix = Prefix.objects.prefetch_related("netbox_dns_views").get(
137
+ pk=instance.pk
138
+ )
97
139
  if saved_prefix.prefix != instance.prefix or saved_prefix.vrf != instance.vrf:
98
- for view in saved_prefix.netbox_dns_views.all():
99
- view.prefixes.remove(saved_prefix)
140
+ dns_views = ", ".join([view.name for view in instance.netbox_dns_views.all()])
141
+ if request is not None:
142
+ raise AbortRequest(
143
+ f"This prefix is currently assigned to the following DNS views: {dns_views}"
144
+ f"Please deassign it from these views before making changes to the prefix "
145
+ f"or VRF."
146
+ )
147
+
148
+ raise ValidationError(
149
+ f"Prefix is assigned to DNS views {dns_views}. Prefix and VRF must not be changed"
150
+ )
100
151
 
101
152
 
102
153
  @receiver(pre_delete, sender=Prefix)
@@ -1,4 +1,5 @@
1
1
  from django.conf import settings
2
+ from django.urls import reverse
2
3
 
3
4
  from netbox.plugins.utils import get_plugin_config
4
5
  from netbox.plugins import PluginTemplateExtension
@@ -63,6 +64,17 @@ class RelatedDNSViews(PluginTemplateExtension):
63
64
  extra_context=context,
64
65
  )
65
66
 
67
+ def buttons(self):
68
+ return self.render(
69
+ "netbox_dns/view/button.html",
70
+ extra_context={
71
+ "url": reverse(
72
+ "plugins:netbox_dns:prefix_views",
73
+ kwargs={"pk": self.context.get("object").pk},
74
+ ),
75
+ },
76
+ )
77
+
66
78
 
67
79
  class IPRelatedDNSRecords(PluginTemplateExtension):
68
80
  model = "ipam.ipaddress"
@@ -0,0 +1,9 @@
1
+ {% load perms %}
2
+
3
+ {% if perms.netbox_dns.change_view %}
4
+ <a href="{{ url }}?return_url={{ object.get_absolute_url }}">
5
+ <button type="submit" class="btn btn-primary" name="assign-view">
6
+ <i class="mdi mdi-eye-outline" aria-hidden="true"></i> DNS Views
7
+ </button>
8
+ </a>
9
+ {% endif %}
@@ -0,0 +1,41 @@
1
+ {% extends 'generic/_base.html' %}
2
+
3
+ {% block title %}
4
+ Configure DNS views for {{ object|meta:"verbose_name" }} {{ object }} {% if object.vrf %}[{{ object.vrf }}]{% endif %}
5
+ {% endblock title %}
6
+
7
+ {% block content %}
8
+ <div class="tab-pane show active" id="edit-form" role="tabpanel" aria-labelledby="object-list-tab">
9
+
10
+ <form action="" method="post" enctype="multipart/form-data" class="object-edit mt-5">
11
+ {% csrf_token %}
12
+
13
+ <div id="form_fields" hx-disinherit="hx-select hx-swap">
14
+ {% if inherited_from %}
15
+ <div class="card">
16
+ <table class="table table-hover attr-table">
17
+ <th>Views inherited from prefix {{ inherited_from }} {% if inherited_from.vrf %}[{{ inherited_from.vrf }}] {% endif %}</th>
18
+ {% for view in inherited_views %}
19
+ <tr><td>{{ view|linkify }}</td><td>{{ view.description }}</td></tr>
20
+ {% endfor %}
21
+ </table>
22
+ </div>
23
+ {% endif %}
24
+ {% block form %}
25
+ {% include 'htmx/form.html' %}
26
+ {% endblock form %}
27
+ </div>
28
+
29
+ <div class="text-end my-3">
30
+ {% block buttons %}
31
+ <a href="{{ return_url }}" class="btn btn-outline-secondary">Cancel</a>
32
+ <button type="submit" name="_update" class="btn btn-primary">Save</button>
33
+ {% endblock buttons %}
34
+ </div>
35
+ </form>
36
+ </div>
37
+ {% endblock content %}
38
+
39
+ {% block modals %}
40
+ {% include 'inc/htmx_modal.html' with size='lg' %}
41
+ {% endblock %}
netbox_dns/urls/view.py CHANGED
@@ -12,6 +12,7 @@ from netbox_dns.views import (
12
12
  ViewBulkEditView,
13
13
  ViewBulkDeleteView,
14
14
  ViewZoneListView,
15
+ ViewPrefixEditView,
15
16
  )
16
17
 
17
18
  view_urlpatterns = [
@@ -36,4 +37,9 @@ view_urlpatterns = [
36
37
  name="view_changelog",
37
38
  kwargs={"model": View},
38
39
  ),
40
+ path(
41
+ "prefixes/<int:pk>/assign-views/",
42
+ ViewPrefixEditView.as_view(),
43
+ name="prefix_views",
44
+ ),
39
45
  ]
@@ -52,7 +52,13 @@ def _get_record_status(ip_address):
52
52
  )
53
53
 
54
54
 
55
- def get_zones(ip_address, view=None):
55
+ def _valid_entry(ip_address, zone):
56
+ return zone.view in _get_assigned_views(ip_address) and dns_name.from_text(
57
+ ip_address.dns_name
58
+ ).is_subdomain(dns_name.from_text(zone.name))
59
+
60
+
61
+ def get_zones(ip_address, view=None, old_zone=None):
56
62
  if view is None:
57
63
  views = _get_assigned_views(ip_address)
58
64
  if not views:
@@ -72,10 +78,13 @@ def get_zones(ip_address, view=None):
72
78
  active=True,
73
79
  )
74
80
 
75
- if not zones:
76
- return []
77
-
78
81
  zone_map = defaultdict(list)
82
+
83
+ if old_zone is not None:
84
+ zones = zones.exclude(pk=old_zone.pk)
85
+ if _valid_entry(ip_address, old_zone):
86
+ zone_map[old_zone.view].append(old_zone)
87
+
79
88
  for zone in zones:
80
89
  zone_map[zone.view].append(zone)
81
90
 
@@ -91,35 +100,49 @@ def check_dns_records(ip_address, zone=None, view=None):
91
100
 
92
101
  if zone is None:
93
102
  zones = get_zones(ip_address, view=view)
94
- else:
95
- zones = [zone]
96
-
97
- if ip_address.pk is not None:
98
- for record in ip_address.netbox_dns_records.filter(zone__in=zones):
99
- if (
100
- record.fqdn != ip_address.dns_name
101
- or record.value != ip_address.address.ip
102
- or record.status != _get_record_status(ip_address)
103
- ):
104
- record.update_from_ip_address(ip_address)
105
103
 
106
- if record is not None:
107
- record.clean()
104
+ if ip_address.pk is not None:
105
+ for record in ip_address.netbox_dns_records.filter(zone__in=zones):
106
+ if (
107
+ record.fqdn != ip_address.dns_name
108
+ or record.value != ip_address.address.ip
109
+ or record.status != _get_record_status(ip_address)
110
+ ):
111
+ record.update_from_ip_address(ip_address)
112
+
113
+ if record is not None:
114
+ record.clean()
115
+
116
+ zones = _zone.Zone.objects.filter(
117
+ pk__in=[zone.pk for zone in zones]
118
+ ).exclude(
119
+ pk__in=set(
120
+ ip_address.netbox_dns_records.all().values_list("zone", flat=True)
121
+ )
122
+ )
108
123
 
109
- zones = _zone.Zone.objects.filter(pk__in=[zone.pk for zone in zones]).exclude(
110
- pk__in=set(
111
- ip_address.netbox_dns_records.all().values_list("zone", flat=True)
124
+ for zone in zones:
125
+ record = _record.Record.create_from_ip_address(
126
+ ip_address,
127
+ zone,
112
128
  )
113
- )
114
129
 
115
- for zone in zones:
116
- record = _record.Record.create_from_ip_address(
117
- ip_address,
118
- zone,
119
- )
130
+ if record is not None:
131
+ record.clean()
132
+
133
+ if ip_address.pk is None:
134
+ return
135
+
136
+ try:
137
+ new_zone = get_zones(ip_address, old_zone=zone)[0]
138
+ except IndexError:
139
+ return
140
+
141
+ for record in ip_address.netbox_dns_records.filter(zone=zone):
142
+ record.update_from_ip_address(ip_address, new_zone)
120
143
 
121
144
  if record is not None:
122
- record.clean()
145
+ record.clean(new_zone=new_zone)
123
146
 
124
147
 
125
148
  def update_dns_records(ip_address):
@@ -240,7 +263,7 @@ def get_ip_addresses_by_zone(zone):
240
263
 
241
264
 
242
265
  def check_record_permission(add=True, change=True, delete=True):
243
- checks = locals()
266
+ checks = locals().copy()
244
267
 
245
268
  request = current_request.get()
246
269
 
@@ -249,8 +272,8 @@ def check_record_permission(add=True, change=True, delete=True):
249
272
 
250
273
  return all(
251
274
  (
252
- request.user.has_perm(f"nebox_dns.{perm}_record")
253
- for perm, check in locals().items()
275
+ request.user.has_perm(f"netbox_dns.{perm}_record")
276
+ for perm, check in checks.items()
254
277
  if check
255
278
  )
256
279
  )
netbox_dns/views/view.py CHANGED
@@ -1,11 +1,19 @@
1
1
  from utilities.views import ViewTab, register_model_view
2
2
 
3
3
  from netbox.views import generic
4
+ from ipam.models import Prefix
4
5
 
5
6
  from netbox_dns.models import View, Zone
6
7
  from netbox_dns.filtersets import ViewFilterSet, ZoneFilterSet
7
- from netbox_dns.forms import ViewForm, ViewFilterForm, ViewImportForm, ViewBulkEditForm
8
+ from netbox_dns.forms import (
9
+ ViewForm,
10
+ ViewFilterForm,
11
+ ViewImportForm,
12
+ ViewBulkEditForm,
13
+ ViewPrefixEditForm,
14
+ )
8
15
  from netbox_dns.tables import ViewTable, ZoneTable
16
+ from netbox_dns.utilities import get_views_by_prefix
9
17
 
10
18
 
11
19
  __all__ = (
@@ -17,6 +25,7 @@ __all__ = (
17
25
  "ViewBulkEditView",
18
26
  "ViewBulkDeleteView",
19
27
  "ViewZoneListView",
28
+ "ViewPrefixEditView",
20
29
  )
21
30
 
22
31
 
@@ -61,6 +70,22 @@ class ViewBulkDeleteView(generic.BulkDeleteView):
61
70
  table = ViewTable
62
71
 
63
72
 
73
+ class ViewPrefixEditView(generic.ObjectEditView):
74
+ queryset = Prefix.objects.all()
75
+ form = ViewPrefixEditForm
76
+ template_name = "netbox_dns/view/prefix.html"
77
+
78
+ def get_extra_context(self, request, instance):
79
+ parents = instance.get_parents()
80
+ if parents:
81
+ return {
82
+ "inherited_views": get_views_by_prefix(parents.last()),
83
+ "inherited_from": parents.filter(netbox_dns_views__isnull=False).last(),
84
+ }
85
+
86
+ return {}
87
+
88
+
64
89
  @register_model_view(View, "zones")
65
90
  class ViewZoneListView(generic.ObjectChildrenView):
66
91
  queryset = View.objects.all().prefetch_related("zone_set")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: netbox-plugin-dns
3
- Version: 1.1.0b2
3
+ Version: 1.1b3
4
4
  Summary: NetBox DNS is a NetBox plugin for managing DNS data.
5
5
  Home-page: https://github.com/peteeckel/netbox-plugin-dns
6
6
  License: MIT
@@ -1,4 +1,4 @@
1
- netbox_dns/__init__.py,sha256=Jdvrhj7X8a6ZTLJf0EYzWJyhawP_uJc3GSdg5USr3nw,1835
1
+ netbox_dns/__init__.py,sha256=Ck45UmyPbGnlkricEwMjOpBDT-BBjVBZfmKi3AbwsxM,1833
2
2
  netbox_dns/api/nested_serializers.py,sha256=-ZhAiyf-8UHlkcBomBp1J7ci1dSwrxWRbbfskD-D_yQ,3172
3
3
  netbox_dns/api/serializers.py,sha256=u-kQurUftGkUGAMh-VkMgXPebLYeZq9WDz9uKzkk2No,370
4
4
  netbox_dns/api/serializers_/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -37,8 +37,8 @@ netbox_dns/forms/nameserver.py,sha256=LHomCHmFcASobaD3Z7yhAyA24h-LrYImVMz-EUXbwK
37
37
  netbox_dns/forms/record.py,sha256=svBVAFy-egDEPLcRWkxNi_1bkabKmWgJ87pmdNt6dh4,7155
38
38
  netbox_dns/forms/record_template.py,sha256=Q77p9sExJ8Xbl-Co2Px2R0At5O3naQJwx4pnino6i2o,5573
39
39
  netbox_dns/forms/registrar.py,sha256=FMnvrcq62R3wNp_2ZUEk3v_PIav0KrWPATaJ7_9KFAo,3758
40
- netbox_dns/forms/view.py,sha256=CSMpWmjKQdfBMILdLLyOjY6o27W3mZgAVIBGjCiPS78,6144
41
- netbox_dns/forms/zone.py,sha256=ZbsYcWX-t1luqBsLj4vec0IZG0lmCwGW5nzVh77qJrw,23164
40
+ netbox_dns/forms/view.py,sha256=AMTBIw8uNTjDdq8QZTxyw-e8KfQ43SVkO2tc3og4vt8,9069
41
+ netbox_dns/forms/zone.py,sha256=17Ii2csnYquuz7HGgaK36ZgOzWnFXFeh1IQYvWnBKC0,23537
42
42
  netbox_dns/forms/zone_template.py,sha256=UNykid5pRB_ydy40j2DzRlBXp3_QAOqdqxdUojKYTd4,8161
43
43
  netbox_dns/graphql/__init__.py,sha256=ZZSsx-VM108tB_FrcVy3uGGhtmePpkXnY5U1ytnoTvE,490
44
44
  netbox_dns/graphql/filters.py,sha256=6Ot_d1e7h5lVXVVBB3hyWUql94K3zsK9Tjb3RVJqluw,1706
@@ -46,7 +46,7 @@ netbox_dns/graphql/schema.py,sha256=P-oQ8ei3sC6XLhgCa_riRbRTrMkPCVTJXkGv0U2rPYw,
46
46
  netbox_dns/graphql/types.py,sha256=4ewWOqEbWtCBiU9bdIm_6CIm6MKAM6szCAXSvokpqWg,6108
47
47
  netbox_dns/management/commands/cleanup_database.py,sha256=kfnyybudwKGigjJmrOwafPWSUasZr9jQsxN4eWAgMvY,5969
48
48
  netbox_dns/management/commands/cleanup_rrset_ttl.py,sha256=UFRURLBcFeGHUS2lrYFv7UWIebjI72aG1EUQJt0XsXw,2046
49
- netbox_dns/management/commands/setup_autodns.py,sha256=kX1T7VjMZ0BPuT_zte6VO6xqOSpYJXaYNfu5dxlrZLc,5507
49
+ netbox_dns/management/commands/setup_autodns.py,sha256=8ipEDyvZ0MIzkcj9gASModRbPHKyKCnyfke2SYOl61g,5733
50
50
  netbox_dns/management/commands/update_soa.py,sha256=Rj_Xk-qpwkAVRubVnM5OqSTwgzi93E0PqjwGb3rYjf0,660
51
51
  netbox_dns/migrations/0001_squashed_netbox_dns_0_15.py,sha256=3U0810NWSHPu2dTSHpfzlleDgwMS04FhJ_CkO76SDaw,10283
52
52
  netbox_dns/migrations/0001_squashed_netbox_dns_0_22.py,sha256=ML6Hp17lrXiaG0eUlBjKMm6HUNhw0AHPnKrb9AN-F6E,20279
@@ -72,15 +72,15 @@ netbox_dns/mixins/object_modification.py,sha256=JbGi8a52wkZ3fFBlfat590CfqRJcEWxB
72
72
  netbox_dns/models/__init__.py,sha256=wjwNsRttUVYQHZODZi806a_iUDoq_o7mdKObqh1N7N4,300
73
73
  netbox_dns/models/contact.py,sha256=oNLyD_6TOTNQQTcCvv6TAC7OkzPTMIRy2NP5nwNKaNg,3009
74
74
  netbox_dns/models/nameserver.py,sha256=yKo4Fwqnv5VtTndU2px7tRS3voF3Cal7OWQ6AImLwl0,3208
75
- netbox_dns/models/record.py,sha256=99Ys3daxvTJuUP6NeTJrEx8Du5C8c70zk8R7S_8YWAs,25703
75
+ netbox_dns/models/record.py,sha256=ArQp7gB94FZH9MeihfIbx9pN3Y90gSj4VsoSF6y1348,25966
76
76
  netbox_dns/models/record_template.py,sha256=3t9VceviX3kNIo5o0VPVFupLFDqPxpHIVLp5U3pBKB4,4661
77
77
  netbox_dns/models/registrar.py,sha256=T_oMUlTWTDixOVlIbEZGvOBdvUrKxRkkS41xgM2Oee8,1557
78
78
  netbox_dns/models/view.py,sha256=SYmhNYyRCv0rSCK5jrHtug4QgfWCBbjsAjZEEHk02QU,2873
79
- netbox_dns/models/zone.py,sha256=clKeoQ7ECYMKwIbqXnszOkuCRGC1_Q6N3q5xTUg8yoY,28905
79
+ netbox_dns/models/zone.py,sha256=C1f6uGKGeD_FKtFhWXiUO7gKF19pzu-9-pj0txP8R1E,29063
80
80
  netbox_dns/models/zone_template.py,sha256=lkiSIfx8KM0Cs3Mb3dLBxKbSpcssVUzQiSmD5W46was,3753
81
81
  netbox_dns/navigation.py,sha256=EITDZkbpu4KCC9u4Noj7OORWnkL3EYT2RIRvYlTw34Q,5961
82
82
  netbox_dns/signals/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
- netbox_dns/signals/ipam_autodns.py,sha256=HJkDU7BZHGFb02YlZHtCTRyKQiR3ZxSwempo-NdQ3o4,5779
83
+ netbox_dns/signals/ipam_autodns.py,sha256=XkngO_hbfi_kzb7pnplQbOYp8TcUvpP1j3R49rWy7Go,7908
84
84
  netbox_dns/tables/__init__.py,sha256=s41w4o77tIwmhnLjsOsg08R9m3wrlomkkfCLTVQuPzc,196
85
85
  netbox_dns/tables/contact.py,sha256=sPs7d1ZhVC5dOS37dPYFqebNd7WGvsV_eYzX_TMcbzY,804
86
86
  netbox_dns/tables/nameserver.py,sha256=fFiE-yH-_GyRDaV4SVw094r6uH58Kx56NSWDGaMR58g,764
@@ -90,7 +90,7 @@ netbox_dns/tables/registrar.py,sha256=M-ckyQUs6dqjTCPf7bAr6UuLEA-q9f9CxKW7yp3rGo
90
90
  netbox_dns/tables/view.py,sha256=jf2S4TiOdMq6-wWk0ndR1uBJpkOx_f3pqAuM1nSXTBo,1178
91
91
  netbox_dns/tables/zone.py,sha256=IeCiflrQBn1INV_PxoTySWQrDalykY4mDSG76VXC5WM,1877
92
92
  netbox_dns/tables/zone_template.py,sha256=70hvS-xpeaLkcM6y0R9xsUMQVKgTgZJaWWNd99BfmzI,1479
93
- netbox_dns/template_content.py,sha256=Lhrse5_Aw6L5xaemTD8-ZlTyFqJ-fVqp97TSRT6E7ek,3390
93
+ netbox_dns/template_content.py,sha256=a2TAwrw0TeDICv8vSiAuzeeuBEqPbahm_OfFkYGrG-s,3742
94
94
  netbox_dns/templates/netbox_dns/contact.html,sha256=fMHAQyLXIxohKoCTxFEnKetl9UVXeQgjasfpv_JONaw,2855
95
95
  netbox_dns/templates/netbox_dns/nameserver.html,sha256=DpTdetQVV_jKThDbi62LvbhiCay-1QxR-yiJEiPFm4w,1554
96
96
  netbox_dns/templates/netbox_dns/record/managed.html,sha256=G6LPG1koUGuzUiwYdv1okdVa4sKaofiQegDBnsFL0kA,89
@@ -98,6 +98,8 @@ netbox_dns/templates/netbox_dns/record/related.html,sha256=Aqor8uGcuHQTHjlX-Xmni
98
98
  netbox_dns/templates/netbox_dns/record.html,sha256=o3z_D6Fqqn7nx1IwPXKQ75ZaPhU6kae0WpaWa3UMcxQ,5211
99
99
  netbox_dns/templates/netbox_dns/recordtemplate.html,sha256=9tkXtKqa5p3LdOU9REm99WSFwGJaH8OczpIqXZuXMcg,3099
100
100
  netbox_dns/templates/netbox_dns/registrar.html,sha256=O5veGmW59Pf5yN25ihPLvRIkA2P7xmSGv0G3NrRG8vI,2152
101
+ netbox_dns/templates/netbox_dns/view/button.html,sha256=oXKNyPtY8XIu2sxtZWpFRXKXv862407ESyUQ4YsWCGE,292
102
+ netbox_dns/templates/netbox_dns/view/prefix.html,sha256=HD8f4mnbzFOXDj3Y_yq8yEeDpz_yFud8ZMpqbxzCEnA,1445
101
103
  netbox_dns/templates/netbox_dns/view/related.html,sha256=W9Ie2aOsFkWyYtBnZn38seQDBmyJkV9dqFDG-Dq3yMk,736
102
104
  netbox_dns/templates/netbox_dns/view.html,sha256=NSEfPSHPLw5yjUSat9N_KYKF5FezmTlCXqPC6FYiK9E,2479
103
105
  netbox_dns/templates/netbox_dns/zone/base.html,sha256=n_E4aVYdGeZZl-ARE8sb4DgAAgPs92X1UEFepX3xIlM,495
@@ -115,12 +117,12 @@ netbox_dns/urls/nameserver.py,sha256=BBbY-wqPqCquvLLv1_JhqToj7oDHhPNGCWHt0IfjBNM
115
117
  netbox_dns/urls/record.py,sha256=bDprohTso1N0GtPXH4X3TNHnkxopiOSQFXWItifEZ_k,1432
116
118
  netbox_dns/urls/record_template.py,sha256=Z-7aA-rPIxRBCmXNUiQcHIgjYfai28Tf_sLtkl2ihDk,1827
117
119
  netbox_dns/urls/registrar.py,sha256=u6B0zGGYNUJIKTo9uGiUeZLPD0QMGaQOAPShGEy4NaA,1728
118
- netbox_dns/urls/view.py,sha256=8AeBnOHWusXXQs4JXpNfMSHqszXAY1GDXGWmNsMulQ8,1327
120
+ netbox_dns/urls/view.py,sha256=jz5ANOOLCMAcWermTZYGq9BvnP02jpKGL6hCm33C47Q,1478
119
121
  netbox_dns/urls/zone.py,sha256=rmB1BkzmWNG06ILUf-39Aj6-SBFkwQouyixMQiamqPc,2005
120
122
  netbox_dns/urls/zone_template.py,sha256=w3Gu8qfLCWyHofeLkGZd1HpYSlcslomVlBQJZyqh8kk,1690
121
123
  netbox_dns/utilities/__init__.py,sha256=M9T8PUFlGddtENzEznHAPbEsz1VFrPcmbD-BGLCsvB4,55
122
124
  netbox_dns/utilities/conversions.py,sha256=NS37SoMqXc13wNWRkKnLfyQbVi6QKD33fu5ovTKRo74,1979
123
- netbox_dns/utilities/ipam_autodns.py,sha256=Wit1MoJbPlYIUMfft13u7LY_Lr_zOb13DGkxIS-n-98,7338
125
+ netbox_dns/utilities/ipam_autodns.py,sha256=QMCMZkY8YxZL3VeH5xHCQWWd3Cji9Nx0GTUcsjtXsQY,8137
124
126
  netbox_dns/validators/__init__.py,sha256=Mr8TvmcJTa8Pubj8TzbFBKfbHhEmGcr5JdQvczEJ39A,72
125
127
  netbox_dns/validators/dns_name.py,sha256=B4A0BOW5pKDjjukvksriRtnLzkYTx_pFjh7eqKo6PBE,3069
126
128
  netbox_dns/validators/dns_value.py,sha256=y2Zga4hmywqDrTBXcMC-sWaFbw4eoY8pySq7cWnMP8Y,2822
@@ -131,10 +133,10 @@ netbox_dns/views/nameserver.py,sha256=DFr0eybMshc1FW06g4cy9Nk4VRMxRqakI5KtHFiAVR
131
133
  netbox_dns/views/record.py,sha256=fHMafCC14C7d6oXbXc2vN-T70OAOaTY77_m3Dct-oiQ,4590
132
134
  netbox_dns/views/record_template.py,sha256=BkemTBEramLhYqB6HrA80sNgtduW1ZOJwbYs3i7srik,2510
133
135
  netbox_dns/views/registrar.py,sha256=yRQgFm3vgBD21ZQex9asjs0QWegvSHlcyHXLnjvc5xs,2324
134
- netbox_dns/views/view.py,sha256=I_hVZYFJF8GTnlUKPrTgBk_x9UDCbZXM8R7U5Bhizjs,2107
136
+ netbox_dns/views/view.py,sha256=iXBJTc3JD5cD5z0RTcHVTtYV-KNIJGneeoxymXChdUE,2759
135
137
  netbox_dns/views/zone.py,sha256=SKhf_WHcFVpKqFTuUMf-Dmxu1AwFHBeo_DtD8UGFrJ8,5483
136
138
  netbox_dns/views/zone_template.py,sha256=qvXl-bpc1fMc1WFngynj4-Q3-JJDgKdT-r54s4M1D0s,2118
137
- netbox_plugin_dns-1.1.0b2.dist-info/LICENSE,sha256=I3tDu11bZfhFm3EkV4zOD5TmWgLjnUNLEFwrdjniZYs,1112
138
- netbox_plugin_dns-1.1.0b2.dist-info/METADATA,sha256=V0TQOjdcjkw1vAf_Ih85cV2vr0xgG_10VEJkn8a2BBw,6406
139
- netbox_plugin_dns-1.1.0b2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
140
- netbox_plugin_dns-1.1.0b2.dist-info/RECORD,,
139
+ netbox_plugin_dns-1.1b3.dist-info/LICENSE,sha256=I3tDu11bZfhFm3EkV4zOD5TmWgLjnUNLEFwrdjniZYs,1112
140
+ netbox_plugin_dns-1.1b3.dist-info/METADATA,sha256=TZ6wKgEaZXVAH0j4IZsRrlfmQZ_kQjhy72ATDEvQmag,6404
141
+ netbox_plugin_dns-1.1b3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
142
+ netbox_plugin_dns-1.1b3.dist-info/RECORD,,