netbox-plugin-dns 1.0.7__py3-none-any.whl → 1.1.0__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 +23 -4
- netbox_dns/api/serializers.py +2 -1
- netbox_dns/api/serializers_/prefix.py +18 -0
- netbox_dns/api/serializers_/{contact.py → registration_contact.py} +5 -5
- netbox_dns/api/serializers_/view.py +34 -2
- netbox_dns/api/serializers_/zone.py +5 -5
- netbox_dns/api/serializers_/zone_template.py +5 -5
- netbox_dns/api/urls.py +5 -2
- netbox_dns/api/views.py +17 -7
- netbox_dns/fields/__init__.py +1 -0
- netbox_dns/fields/ipam.py +15 -0
- netbox_dns/filtersets/__init__.py +1 -1
- netbox_dns/filtersets/{contact.py → registration_contact.py} +4 -4
- netbox_dns/filtersets/view.py +16 -0
- netbox_dns/filtersets/zone.py +15 -15
- netbox_dns/filtersets/zone_template.py +15 -15
- netbox_dns/forms/__init__.py +1 -1
- netbox_dns/forms/{contact.py → registration_contact.py} +16 -16
- netbox_dns/forms/view.py +204 -4
- netbox_dns/forms/zone.py +15 -18
- netbox_dns/forms/zone_template.py +13 -13
- netbox_dns/graphql/__init__.py +2 -2
- netbox_dns/graphql/filters.py +5 -5
- netbox_dns/graphql/schema.py +9 -5
- netbox_dns/graphql/types.py +41 -12
- netbox_dns/management/commands/rebuild_dnssync.py +18 -0
- netbox_dns/management/commands/setup_dnssync.py +140 -0
- netbox_dns/migrations/0008_view_prefixes.py +18 -0
- netbox_dns/migrations/0009_rename_contact_registrationcontact.py +27 -0
- netbox_dns/models/__init__.py +1 -3
- netbox_dns/models/record.py +139 -20
- netbox_dns/models/{contact.py → registration_contact.py} +8 -8
- netbox_dns/models/view.py +5 -0
- netbox_dns/models/zone.py +66 -30
- netbox_dns/models/zone_template.py +4 -4
- netbox_dns/navigation.py +7 -7
- netbox_dns/signals/ipam_dnssync.py +224 -0
- netbox_dns/tables/__init__.py +1 -1
- netbox_dns/tables/ipam_dnssync.py +11 -0
- netbox_dns/tables/record.py +33 -0
- netbox_dns/tables/{contact.py → registration_contact.py} +5 -5
- netbox_dns/tables/view.py +24 -2
- netbox_dns/template_content.py +41 -40
- netbox_dns/templates/netbox_dns/record.html +6 -6
- netbox_dns/templates/netbox_dns/{contact.html → registrationcontact.html} +1 -1
- netbox_dns/templates/netbox_dns/view/button.html +9 -0
- netbox_dns/templates/netbox_dns/view/prefix.html +41 -0
- netbox_dns/templates/netbox_dns/view/related.html +17 -0
- netbox_dns/templates/netbox_dns/view.html +25 -0
- netbox_dns/urls/__init__.py +2 -2
- netbox_dns/urls/registration_contact.py +60 -0
- netbox_dns/urls/view.py +6 -0
- netbox_dns/utilities/__init__.py +2 -74
- netbox_dns/utilities/conversions.py +83 -0
- netbox_dns/utilities/ipam_dnssync.py +295 -0
- netbox_dns/views/__init__.py +1 -1
- netbox_dns/views/record.py +3 -5
- netbox_dns/views/registration_contact.py +94 -0
- netbox_dns/views/view.py +26 -1
- {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0.dist-info}/METADATA +2 -1
- {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0.dist-info}/RECORD +63 -54
- netbox_dns/management/commands/setup_coupling.py +0 -109
- netbox_dns/signals/ipam_coupling.py +0 -168
- netbox_dns/templates/netbox_dns/related_dns_objects.html +0 -21
- netbox_dns/urls/contact.py +0 -29
- netbox_dns/utilities/ipam_coupling.py +0 -112
- netbox_dns/views/contact.py +0 -94
- {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0.dist-info}/LICENSE +0 -0
- {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
|
|
5
|
+
from dns import name as dns_name
|
|
6
|
+
|
|
7
|
+
from django.conf import settings
|
|
8
|
+
from django.db.models import Q
|
|
9
|
+
|
|
10
|
+
from netbox.context import current_request
|
|
11
|
+
from ipam.models import IPAddress, Prefix
|
|
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
|
+
from netbox_dns.choices import RecordStatusChoices
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
__all__ = (
|
|
20
|
+
"get_zones",
|
|
21
|
+
"check_dns_records",
|
|
22
|
+
"update_dns_records",
|
|
23
|
+
"delete_dns_records",
|
|
24
|
+
"get_views_by_prefix",
|
|
25
|
+
"get_ip_addresses_by_prefix",
|
|
26
|
+
"get_ip_addresses_by_view",
|
|
27
|
+
"get_ip_addresses_by_zone",
|
|
28
|
+
"check_record_permission",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _get_assigned_views(ip_address):
|
|
33
|
+
longest_prefix = Prefix.objects.filter(
|
|
34
|
+
vrf=ip_address.vrf,
|
|
35
|
+
prefix__net_contains_or_equals=str(ip_address.address.ip),
|
|
36
|
+
netbox_dns_views__isnull=False,
|
|
37
|
+
).last()
|
|
38
|
+
|
|
39
|
+
if longest_prefix is None:
|
|
40
|
+
return []
|
|
41
|
+
|
|
42
|
+
return longest_prefix.netbox_dns_views.all()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _get_record_status(ip_address):
|
|
46
|
+
return (
|
|
47
|
+
RecordStatusChoices.STATUS_ACTIVE
|
|
48
|
+
if ip_address.status
|
|
49
|
+
in settings.PLUGINS_CONFIG["netbox_dns"].get(
|
|
50
|
+
"dnssync_ipaddress_active_status", []
|
|
51
|
+
)
|
|
52
|
+
else RecordStatusChoices.STATUS_INACTIVE
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _valid_entry(ip_address, zone):
|
|
57
|
+
return zone.view in _get_assigned_views(ip_address) and dns_name.from_text(
|
|
58
|
+
ip_address.dns_name
|
|
59
|
+
).is_subdomain(dns_name.from_text(zone.name))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _match_data(ip_address, record):
|
|
63
|
+
cf_disable_ptr = ip_address.custom_field_data.get(
|
|
64
|
+
"ipaddress_dns_record_disable_ptr"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
record.fqdn.rstrip(".") == ip_address.dns_name.rstrip(".")
|
|
69
|
+
and record.value == str(ip_address.address.ip)
|
|
70
|
+
and record.status == _get_record_status(ip_address)
|
|
71
|
+
and record.ttl == ip_address.custom_field_data.get("ipaddress_dns_record_ttl")
|
|
72
|
+
and (cf_disable_ptr is None or record.disable_ptr == cf_disable_ptr)
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def get_zones(ip_address, view=None, old_zone=None):
|
|
77
|
+
if view is None:
|
|
78
|
+
views = _get_assigned_views(ip_address)
|
|
79
|
+
if not views:
|
|
80
|
+
return []
|
|
81
|
+
|
|
82
|
+
else:
|
|
83
|
+
views = [view]
|
|
84
|
+
|
|
85
|
+
min_labels = settings.PLUGINS_CONFIG["netbox_dns"].get(
|
|
86
|
+
"dnssync_minimum_zone_labels", 2
|
|
87
|
+
)
|
|
88
|
+
fqdn = dns_name.from_text(ip_address.dns_name)
|
|
89
|
+
zone_name_candidates = [
|
|
90
|
+
fqdn.split(i)[1].to_text().rstrip(".")
|
|
91
|
+
for i in range(min_labels + 1, len(fqdn.labels))
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
zones = _zone.Zone.objects.filter(
|
|
95
|
+
view__in=views,
|
|
96
|
+
name__in=zone_name_candidates,
|
|
97
|
+
active=True,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
zone_map = defaultdict(list)
|
|
101
|
+
|
|
102
|
+
if old_zone is not None:
|
|
103
|
+
zones = zones.exclude(pk=old_zone.pk)
|
|
104
|
+
if _valid_entry(ip_address, old_zone):
|
|
105
|
+
zone_map[old_zone.view].append(old_zone)
|
|
106
|
+
|
|
107
|
+
for zone in zones:
|
|
108
|
+
zone_map[zone.view].append(zone)
|
|
109
|
+
|
|
110
|
+
return [
|
|
111
|
+
sorted(zones_per_view, key=lambda x: len(x.name))[-1]
|
|
112
|
+
for zones_per_view in zone_map.values()
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def check_dns_records(ip_address, zone=None, view=None):
|
|
117
|
+
if ip_address.dns_name == "":
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
if zone is None:
|
|
121
|
+
zones = get_zones(ip_address, view=view)
|
|
122
|
+
|
|
123
|
+
if ip_address.pk is not None:
|
|
124
|
+
for record in ip_address.netbox_dns_records.filter(zone__in=zones):
|
|
125
|
+
if not _match_data(ip_address, record):
|
|
126
|
+
record.update_from_ip_address(ip_address)
|
|
127
|
+
|
|
128
|
+
if record is not None:
|
|
129
|
+
record.clean()
|
|
130
|
+
|
|
131
|
+
zones = _zone.Zone.objects.filter(
|
|
132
|
+
pk__in=[zone.pk for zone in zones]
|
|
133
|
+
).exclude(
|
|
134
|
+
pk__in=set(ip_address.netbox_dns_records.values_list("zone", flat=True))
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
for zone in zones:
|
|
138
|
+
record = _record.Record.create_from_ip_address(
|
|
139
|
+
ip_address,
|
|
140
|
+
zone,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
if record is not None:
|
|
144
|
+
record.clean()
|
|
145
|
+
|
|
146
|
+
if ip_address.pk is None:
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
try:
|
|
150
|
+
new_zone = get_zones(ip_address, old_zone=zone)[0]
|
|
151
|
+
except IndexError:
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
for record in ip_address.netbox_dns_records.filter(zone=zone):
|
|
155
|
+
record.update_from_ip_address(ip_address, new_zone)
|
|
156
|
+
|
|
157
|
+
if record is not None:
|
|
158
|
+
record.clean(new_zone=new_zone)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def update_dns_records(ip_address):
|
|
162
|
+
if ip_address.dns_name == "":
|
|
163
|
+
delete_dns_records(ip_address)
|
|
164
|
+
return
|
|
165
|
+
|
|
166
|
+
zones = get_zones(ip_address)
|
|
167
|
+
|
|
168
|
+
if ip_address.pk is not None:
|
|
169
|
+
for record in ip_address.netbox_dns_records.all():
|
|
170
|
+
if record.zone not in zones or ip_address.custom_field_data.get(
|
|
171
|
+
"ipaddress_dns_disabled"
|
|
172
|
+
):
|
|
173
|
+
record.delete()
|
|
174
|
+
continue
|
|
175
|
+
|
|
176
|
+
record.update_fqdn()
|
|
177
|
+
if not _match_data(ip_address, record):
|
|
178
|
+
record.update_from_ip_address(ip_address)
|
|
179
|
+
|
|
180
|
+
if record is not None:
|
|
181
|
+
record.save()
|
|
182
|
+
|
|
183
|
+
zones = _zone.Zone.objects.filter(pk__in=[zone.pk for zone in zones]).exclude(
|
|
184
|
+
pk__in=set(
|
|
185
|
+
ip_address.netbox_dns_records.all().values_list("zone", flat=True)
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
for zone in zones:
|
|
190
|
+
record = _record.Record.create_from_ip_address(
|
|
191
|
+
ip_address,
|
|
192
|
+
zone,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
if record is not None:
|
|
196
|
+
record.save()
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def delete_dns_records(ip_address):
|
|
200
|
+
for record in ip_address.netbox_dns_records.all():
|
|
201
|
+
record.delete()
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def get_views_by_prefix(prefix):
|
|
205
|
+
if (views := prefix.netbox_dns_views.all()).exists():
|
|
206
|
+
return views
|
|
207
|
+
|
|
208
|
+
if (parent := prefix.get_parents().filter(netbox_dns_views__isnull=False)).exists():
|
|
209
|
+
return parent.last().netbox_dns_views.all()
|
|
210
|
+
|
|
211
|
+
return _view.View.objects.none()
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def get_ip_addresses_by_prefix(prefix, check_view=True):
|
|
215
|
+
"""
|
|
216
|
+
Find all IPAddress objects that are in a given prefix, provided that prefix
|
|
217
|
+
is assigned to NetBox DNS view. IPAddress objects belonging to a sub-prefix
|
|
218
|
+
that is assigned to a NetBox DNS view itself are excluded, because the zones
|
|
219
|
+
that are relevant for them are depending on the view set of the sub-prefix.
|
|
220
|
+
|
|
221
|
+
If neither the prefix nor any parent prefix is assigned to a view, the list
|
|
222
|
+
of IPAddress objects returned is empty.
|
|
223
|
+
"""
|
|
224
|
+
if check_view and not get_views_by_prefix(prefix):
|
|
225
|
+
return IPAddress.objects.none()
|
|
226
|
+
|
|
227
|
+
queryset = IPAddress.objects.filter(
|
|
228
|
+
vrf=prefix.vrf, address__net_host_contained=prefix.prefix
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
for exclude_child in (
|
|
232
|
+
prefix.get_children().filter(netbox_dns_views__isnull=False).distinct()
|
|
233
|
+
):
|
|
234
|
+
queryset = queryset.exclude(
|
|
235
|
+
vrf=exclude_child.vrf,
|
|
236
|
+
address__net_host_contained=exclude_child.prefix,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
return queryset
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def get_ip_addresses_by_view(view):
|
|
243
|
+
"""
|
|
244
|
+
Find all IPAddress objects that are within prefixes that have a NetBox DNS
|
|
245
|
+
view assigned to them, or that inherit a view from their parent prefix.
|
|
246
|
+
|
|
247
|
+
Inheritance is defined recursively if the prefix is assigned to the view or
|
|
248
|
+
if it is a child prefix of the prefix that is not assigned to a view directly
|
|
249
|
+
or by inheritance.
|
|
250
|
+
"""
|
|
251
|
+
queryset = IPAddress.objects.none()
|
|
252
|
+
for prefix in Prefix.objects.filter(netbox_dns_views__in=[view]):
|
|
253
|
+
sub_queryset = IPAddress.objects.filter(
|
|
254
|
+
vrf=prefix.vrf, address__net_host_contained=prefix.prefix
|
|
255
|
+
)
|
|
256
|
+
for exclude_child in prefix.get_children().exclude(
|
|
257
|
+
Q(netbox_dns_views__isnull=True) | Q(netbox_dns_views__in=[view])
|
|
258
|
+
):
|
|
259
|
+
sub_queryset = sub_queryset.exclude(
|
|
260
|
+
vrf=exclude_child.vrf,
|
|
261
|
+
address__net_host_contained=exclude_child.prefix,
|
|
262
|
+
)
|
|
263
|
+
queryset |= sub_queryset
|
|
264
|
+
|
|
265
|
+
return queryset
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def get_ip_addresses_by_zone(zone):
|
|
269
|
+
"""
|
|
270
|
+
Find all IPAddress objects that are relevant for a NetBox DNS zone. These
|
|
271
|
+
are the IPAddress objects in prefixes assigned to the same view, if the
|
|
272
|
+
'dns_name' attribute of the IPAddress object ends in the zone's name.
|
|
273
|
+
"""
|
|
274
|
+
queryset = get_ip_addresses_by_view(zone.view).filter(
|
|
275
|
+
dns_name__regex=rf"\.{re.escape(zone.name)}\.?$"
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
return queryset
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def check_record_permission(add=True, change=True, delete=True):
|
|
282
|
+
checks = locals().copy()
|
|
283
|
+
|
|
284
|
+
request = current_request.get()
|
|
285
|
+
|
|
286
|
+
if request is None:
|
|
287
|
+
return True
|
|
288
|
+
|
|
289
|
+
return all(
|
|
290
|
+
(
|
|
291
|
+
request.user.has_perm(f"netbox_dns.{perm}_record")
|
|
292
|
+
for perm, check in checks.items()
|
|
293
|
+
if check
|
|
294
|
+
)
|
|
295
|
+
)
|
netbox_dns/views/__init__.py
CHANGED
netbox_dns/views/record.py
CHANGED
|
@@ -39,9 +39,7 @@ class RecordListView(generic.ObjectListView):
|
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
class ManagedRecordListView(generic.ObjectListView):
|
|
42
|
-
queryset = Record.objects.
|
|
43
|
-
"zone", "address_record"
|
|
44
|
-
)
|
|
42
|
+
queryset = Record.objects.prefetch_related("ipam_ip_address", "address_record")
|
|
45
43
|
filterset = RecordFilterSet
|
|
46
44
|
filterset_form = RecordFilterForm
|
|
47
45
|
table = ManagedRecordTable
|
|
@@ -92,11 +90,11 @@ class RecordView(generic.ObjectView):
|
|
|
92
90
|
zone=parent_zone,
|
|
93
91
|
)
|
|
94
92
|
cname_records = cname_records.union(
|
|
95
|
-
|
|
93
|
+
{
|
|
96
94
|
record
|
|
97
95
|
for record in parent_cname_records
|
|
98
96
|
if record.value_fqdn == instance.fqdn
|
|
99
|
-
|
|
97
|
+
}
|
|
100
98
|
)
|
|
101
99
|
|
|
102
100
|
if cname_records:
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from django.db.models import Q
|
|
2
|
+
|
|
3
|
+
from netbox.views import generic
|
|
4
|
+
|
|
5
|
+
from utilities.views import ViewTab, register_model_view
|
|
6
|
+
|
|
7
|
+
from netbox_dns.models import RegistrationContact, Zone
|
|
8
|
+
from netbox_dns.filtersets import RegistrationContactFilterSet, ZoneFilterSet
|
|
9
|
+
from netbox_dns.forms import (
|
|
10
|
+
RegistrationContactForm,
|
|
11
|
+
RegistrationContactFilterForm,
|
|
12
|
+
RegistrationContactImportForm,
|
|
13
|
+
RegistrationContactBulkEditForm,
|
|
14
|
+
)
|
|
15
|
+
from netbox_dns.tables import RegistrationContactTable, ZoneTable
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
__all__ = (
|
|
19
|
+
"RegistrationContactView",
|
|
20
|
+
"RegistrationContactEditView",
|
|
21
|
+
"RegistrationContactListView",
|
|
22
|
+
"RegistrationContactDeleteView",
|
|
23
|
+
"RegistrationContactBulkImportView",
|
|
24
|
+
"RegistrationContactBulkEditView",
|
|
25
|
+
"RegistrationContactBulkDeleteView",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class RegistrationContactView(generic.ObjectView):
|
|
30
|
+
queryset = RegistrationContact.objects.all()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class RegistrationContactListView(generic.ObjectListView):
|
|
34
|
+
queryset = RegistrationContact.objects.all()
|
|
35
|
+
table = RegistrationContactTable
|
|
36
|
+
filterset = RegistrationContactFilterSet
|
|
37
|
+
filterset_form = RegistrationContactFilterForm
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class RegistrationContactEditView(generic.ObjectEditView):
|
|
41
|
+
queryset = RegistrationContact.objects.all()
|
|
42
|
+
form = RegistrationContactForm
|
|
43
|
+
default_return_url = "plugins:netbox_dns:registrationcontact_list"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class RegistrationContactDeleteView(generic.ObjectDeleteView):
|
|
47
|
+
queryset = RegistrationContact.objects.all()
|
|
48
|
+
default_return_url = "plugins:netbox_dns:registrationcontact_list"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class RegistrationContactBulkImportView(generic.BulkImportView):
|
|
52
|
+
queryset = RegistrationContact.objects.all()
|
|
53
|
+
model_form = RegistrationContactImportForm
|
|
54
|
+
table = RegistrationContactTable
|
|
55
|
+
default_return_url = "plugins:netbox_dns:registrationcontact_list"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class RegistrationContactBulkEditView(generic.BulkEditView):
|
|
59
|
+
queryset = RegistrationContact.objects.all()
|
|
60
|
+
filterset = RegistrationContactFilterSet
|
|
61
|
+
table = RegistrationContactTable
|
|
62
|
+
form = RegistrationContactBulkEditForm
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class RegistrationContactBulkDeleteView(generic.BulkDeleteView):
|
|
66
|
+
queryset = RegistrationContact.objects.all()
|
|
67
|
+
table = RegistrationContactTable
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@register_model_view(RegistrationContact, "zones")
|
|
71
|
+
class RegistrationContactZoneListView(generic.ObjectChildrenView):
|
|
72
|
+
queryset = RegistrationContact.objects.all().prefetch_related(
|
|
73
|
+
"zone_set", "admin_c_zones", "tech_c_zones", "billing_c_zones"
|
|
74
|
+
)
|
|
75
|
+
child_model = Zone
|
|
76
|
+
table = ZoneTable
|
|
77
|
+
filterset = ZoneFilterSet
|
|
78
|
+
template_name = "netbox_dns/zone/child.html"
|
|
79
|
+
hide_if_empty = True
|
|
80
|
+
|
|
81
|
+
tab = ViewTab(
|
|
82
|
+
label="Zones",
|
|
83
|
+
permission="netbox_dns.view_zone",
|
|
84
|
+
badge=lambda obj: len(obj.zones),
|
|
85
|
+
hide_if_empty=True,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def get_children(self, request, parent):
|
|
89
|
+
return Zone.objects.filter(
|
|
90
|
+
Q(registrant=parent)
|
|
91
|
+
| Q(admin_c=parent)
|
|
92
|
+
| Q(tech_c=parent)
|
|
93
|
+
| Q(billing_c=parent)
|
|
94
|
+
)
|
netbox_dns/views/view.py
CHANGED
|
@@ -2,11 +2,19 @@ from utilities.views import ViewTab, register_model_view
|
|
|
2
2
|
|
|
3
3
|
from netbox.views import generic
|
|
4
4
|
from tenancy.views import ObjectContactsView
|
|
5
|
+
from ipam.models import Prefix
|
|
5
6
|
|
|
6
7
|
from netbox_dns.models import View, Zone
|
|
7
8
|
from netbox_dns.filtersets import ViewFilterSet, ZoneFilterSet
|
|
8
|
-
from netbox_dns.forms import
|
|
9
|
+
from netbox_dns.forms import (
|
|
10
|
+
ViewForm,
|
|
11
|
+
ViewFilterForm,
|
|
12
|
+
ViewImportForm,
|
|
13
|
+
ViewBulkEditForm,
|
|
14
|
+
ViewPrefixEditForm,
|
|
15
|
+
)
|
|
9
16
|
from netbox_dns.tables import ViewTable, ZoneTable
|
|
17
|
+
from netbox_dns.utilities import get_views_by_prefix
|
|
10
18
|
|
|
11
19
|
|
|
12
20
|
__all__ = (
|
|
@@ -17,6 +25,7 @@ __all__ = (
|
|
|
17
25
|
"ViewBulkImportView",
|
|
18
26
|
"ViewBulkEditView",
|
|
19
27
|
"ViewBulkDeleteView",
|
|
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.0
|
|
3
|
+
Version: 1.1.0
|
|
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
|
|
@@ -48,6 +48,7 @@ The main focus of the plugin is to ensure the quality of the data stored in it.
|
|
|
48
48
|
* Validation of record types such as CNAME and singletons, to ensure DNS zone validity
|
|
49
49
|
* Support for [RFC 2317](https://datatracker.ietf.org/doc/html/rfc2317) delegation of PTR zones for IPv4 subnets longer than 24 bits
|
|
50
50
|
* Templating for zones and records enables faster creations of zones with given boilerplate object relations, such as name servers, tags, tenants or registration information, or records like standard SPF or MX records that are the same for a subset of zones
|
|
51
|
+
* IPAM DNSsync can be used to automatically create address and pointer records for IP addresses by assigning prefixes to DNS views. When an IP address has a DNS name assigned and there are zones with matching names in the DNS views linked to the IP address' prefix, a matching DNS record will be created in these zones
|
|
51
52
|
|
|
52
53
|
Other main features include:
|
|
53
54
|
|