netbox-plugin-dns 1.1.2__py3-none-any.whl → 1.1.4__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 +14 -6
- netbox_dns/api/nested_serializers.py +3 -2
- netbox_dns/api/serializers_/nameserver.py +2 -1
- netbox_dns/api/serializers_/record.py +5 -4
- netbox_dns/api/serializers_/record_template.py +2 -1
- netbox_dns/api/serializers_/view.py +2 -1
- netbox_dns/api/serializers_/zone.py +12 -11
- netbox_dns/api/serializers_/zone_template.py +8 -7
- netbox_dns/api/views.py +9 -4
- netbox_dns/choices/record.py +4 -2
- netbox_dns/choices/zone.py +8 -4
- netbox_dns/fields/address.py +5 -22
- netbox_dns/fields/network.py +2 -1
- netbox_dns/fields/rfc2317.py +7 -3
- netbox_dns/filtersets/nameserver.py +3 -2
- netbox_dns/filtersets/record.py +14 -9
- netbox_dns/filtersets/record_template.py +3 -2
- netbox_dns/filtersets/view.py +3 -2
- netbox_dns/filtersets/zone.py +24 -22
- netbox_dns/filtersets/zone_template.py +15 -14
- netbox_dns/forms/nameserver.py +41 -17
- netbox_dns/forms/record.py +61 -32
- netbox_dns/forms/record_template.py +49 -28
- netbox_dns/forms/registrar.py +21 -17
- netbox_dns/forms/registration_contact.py +37 -25
- netbox_dns/forms/view.py +49 -27
- netbox_dns/forms/zone.py +173 -120
- netbox_dns/forms/zone_template.py +53 -43
- netbox_dns/locale/de/LC_MESSAGES/django.mo +0 -0
- netbox_dns/locale/en/LC_MESSAGES/django.mo +0 -0
- netbox_dns/management/commands/rebuild_dnssync.py +14 -1
- netbox_dns/models/nameserver.py +6 -2
- netbox_dns/models/record.py +74 -40
- netbox_dns/models/record_template.py +17 -9
- netbox_dns/models/registrar.py +11 -7
- netbox_dns/models/registration_contact.py +23 -11
- netbox_dns/models/view.py +15 -6
- netbox_dns/models/zone.py +83 -50
- netbox_dns/models/zone_template.py +12 -10
- netbox_dns/navigation.py +30 -28
- netbox_dns/signals/ipam_dnssync.py +21 -14
- netbox_dns/tables/ipam_dnssync.py +2 -1
- netbox_dns/tables/nameserver.py +2 -0
- netbox_dns/tables/record.py +21 -11
- netbox_dns/tables/record_template.py +12 -5
- netbox_dns/tables/registrar.py +2 -0
- netbox_dns/tables/registration_contact.py +2 -0
- netbox_dns/tables/view.py +3 -1
- netbox_dns/tables/zone.py +15 -2
- netbox_dns/tables/zone_template.py +7 -0
- netbox_dns/templates/netbox_dns/nameserver.html +6 -5
- netbox_dns/templates/netbox_dns/record/managed.html +2 -1
- netbox_dns/templates/netbox_dns/record/related.html +26 -14
- netbox_dns/templates/netbox_dns/record.html +39 -20
- netbox_dns/templates/netbox_dns/recordtemplate.html +27 -15
- netbox_dns/templates/netbox_dns/registrar.html +11 -10
- netbox_dns/templates/netbox_dns/registrationcontact.html +16 -15
- netbox_dns/templates/netbox_dns/view/button.html +2 -1
- netbox_dns/templates/netbox_dns/view/prefix.html +7 -4
- netbox_dns/templates/netbox_dns/view/related.html +26 -10
- netbox_dns/templates/netbox_dns/view.html +11 -14
- netbox_dns/templates/netbox_dns/zone/base.html +2 -1
- netbox_dns/templates/netbox_dns/zone/child.html +3 -2
- netbox_dns/templates/netbox_dns/zone/record.html +3 -2
- netbox_dns/templates/netbox_dns/zone/registration.html +8 -7
- netbox_dns/templates/netbox_dns/zone.html +28 -30
- netbox_dns/templates/netbox_dns/zonetemplate.html +27 -17
- netbox_dns/utilities/ipam_dnssync.py +15 -4
- netbox_dns/validators/dns_name.py +11 -4
- netbox_dns/validators/dns_value.py +55 -9
- netbox_dns/validators/rfc2317.py +6 -3
- netbox_dns/views/nameserver.py +4 -2
- netbox_dns/views/record_template.py +4 -3
- netbox_dns/views/registrar.py +3 -1
- netbox_dns/views/registration_contact.py +2 -1
- netbox_dns/views/view.py +2 -1
- netbox_dns/views/zone.py +6 -4
- netbox_dns/views/zone_template.py +8 -7
- {netbox_plugin_dns-1.1.2.dist-info → netbox_plugin_dns-1.1.4.dist-info}/METADATA +2 -2
- netbox_plugin_dns-1.1.4.dist-info/RECORD +150 -0
- netbox_plugin_dns-1.1.2.dist-info/RECORD +0 -148
- {netbox_plugin_dns-1.1.2.dist-info → netbox_plugin_dns-1.1.4.dist-info}/LICENSE +0 -0
- {netbox_plugin_dns-1.1.2.dist-info → netbox_plugin_dns-1.1.4.dist-info}/WHEEL +0 -0
- {netbox_plugin_dns-1.1.2.dist-info → netbox_plugin_dns-1.1.4.dist-info}/top_level.txt +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
{% load helpers %}
|
|
3
3
|
{% load render_table from django_tables2 %}
|
|
4
4
|
{% load perms %}
|
|
5
|
+
{% load i18n %}
|
|
5
6
|
|
|
6
7
|
{% block content %}
|
|
7
8
|
{% include 'inc/table_controls_htmx.html' with table_modal="ZoneTable_config" %}
|
|
@@ -23,12 +24,12 @@
|
|
|
23
24
|
{% block bulk_buttons %}{% endblock %}
|
|
24
25
|
{% if bulk_edit_url and perms.netbox_dns.change_zone %}
|
|
25
26
|
<button type="submit" name="_edit" formaction="{% url bulk_edit_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-warning">
|
|
26
|
-
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit Selected
|
|
27
|
+
<i class="mdi mdi-pencil" aria-hidden="true"></i> {% trans "Edit Selected" %}
|
|
27
28
|
</button>
|
|
28
29
|
{% endif %}
|
|
29
30
|
{% if bulk_delete_url and perms.netbox_dns.delete_zone %}
|
|
30
31
|
<button type="submit" name="_delete" formaction="{% url bulk_delete_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-danger">
|
|
31
|
-
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete Selected
|
|
32
|
+
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> {% trans "Delete Selected" %}
|
|
32
33
|
</button>
|
|
33
34
|
{% endif %}
|
|
34
35
|
</div>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
{% load helpers %}
|
|
3
3
|
{% load render_table from django_tables2 %}
|
|
4
4
|
{% load perms %}
|
|
5
|
+
{% load i18n %}
|
|
5
6
|
|
|
6
7
|
{% block content %}
|
|
7
8
|
{% include 'inc/table_controls_htmx.html' with table_modal="RecordTable_config" %}
|
|
@@ -23,12 +24,12 @@
|
|
|
23
24
|
{% block bulk_buttons %}{% endblock %}
|
|
24
25
|
{% if bulk_edit_url and perms.netbox_dns.change_record %}
|
|
25
26
|
<button type="submit" name="_edit" formaction="{% url bulk_edit_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-warning">
|
|
26
|
-
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit Selected
|
|
27
|
+
<i class="mdi mdi-pencil" aria-hidden="true"></i> {% trans "Edit Selected" %}
|
|
27
28
|
</button>
|
|
28
29
|
{% endif %}
|
|
29
30
|
{% if bulk_delete_url and perms.netbox_dns.delete_record %}
|
|
30
31
|
<button type="submit" name="_delete" formaction="{% url bulk_delete_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-danger">
|
|
31
|
-
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete Selected
|
|
32
|
+
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> {% trans "Delete Selected" %}
|
|
32
33
|
</button>
|
|
33
34
|
{% endif %}
|
|
34
35
|
</div>
|
|
@@ -1,32 +1,33 @@
|
|
|
1
1
|
{% extends 'netbox_dns/zone/base.html' %}
|
|
2
2
|
{% load helpers %}
|
|
3
|
+
{% load i18n %}
|
|
3
4
|
|
|
4
5
|
{% block content %}
|
|
5
6
|
<div class="card">
|
|
6
|
-
<h5 class="card-header">Domain Registration</h5>
|
|
7
|
+
<h5 class="card-header">{% trans "Domain Registration" %}</h5>
|
|
7
8
|
<table class="table table-hover attr-table">
|
|
8
9
|
<tr>
|
|
9
|
-
<th scope="row">Registrar</th>
|
|
10
|
+
<th scope="row">{% trans "Registrar" %}</th>
|
|
10
11
|
<td>{{ object.registrar|linkify|placeholder }}</td>
|
|
11
12
|
</tr>
|
|
12
13
|
<tr>
|
|
13
|
-
<th scope="row">Registry Domain ID</th>
|
|
14
|
+
<th scope="row">{% trans "Registry Domain ID" %}</th>
|
|
14
15
|
<td>{{ object.registry_domain_id|placeholder }}</td>
|
|
15
16
|
</tr>
|
|
16
17
|
<tr>
|
|
17
|
-
<th scope="row">Registrant</th>
|
|
18
|
+
<th scope="row">{% trans "Registrant" %}</th>
|
|
18
19
|
<td>{{ object.registrant|linkify|placeholder }}</td>
|
|
19
20
|
</tr>
|
|
20
21
|
<tr>
|
|
21
|
-
<th scope="row">Administrative Contact</th>
|
|
22
|
+
<th scope="row">{% trans "Administrative Contact" %}</th>
|
|
22
23
|
<td>{{ object.admin_c|linkify|placeholder }}</td>
|
|
23
24
|
</tr>
|
|
24
25
|
<tr>
|
|
25
|
-
<th scope="row">Technical Contact</th>
|
|
26
|
+
<th scope="row">{% trans "Technical Contact" %}</th>
|
|
26
27
|
<td>{{ object.tech_c|linkify|placeholder }}</td>
|
|
27
28
|
</tr>
|
|
28
29
|
<tr>
|
|
29
|
-
<th scope="row">Billing Contact</th>
|
|
30
|
+
<th scope="row">{% trans "Billing Contact" %}</th>
|
|
30
31
|
<td>{{ object.billing_c|linkify|placeholder }}</td>
|
|
31
32
|
</tr>
|
|
32
33
|
</table>
|
|
@@ -1,43 +1,41 @@
|
|
|
1
1
|
{% extends 'netbox_dns/zone/base.html' %}
|
|
2
2
|
{% load helpers %}
|
|
3
|
-
{% load
|
|
3
|
+
{% load i18n %}
|
|
4
4
|
|
|
5
5
|
{% block content %}
|
|
6
6
|
<div class="row">
|
|
7
7
|
<div class="col col-md-6">
|
|
8
8
|
<div class="card">
|
|
9
|
-
<h5 class="card-header">
|
|
10
|
-
Zone
|
|
11
|
-
</h5>
|
|
9
|
+
<h5 class="card-header">{% trans "Zone" %}</h5>
|
|
12
10
|
<table class="table table-hover attr-table">
|
|
13
11
|
<tr>
|
|
14
|
-
<th scope="row">Name</th>
|
|
12
|
+
<th scope="row">{% trans "Name" %}</th>
|
|
15
13
|
<td>{{ object.name }}</td>
|
|
16
14
|
</tr>
|
|
17
15
|
{% if unicode_name %}
|
|
18
16
|
<tr>
|
|
19
|
-
<th scope="row">IDN</th>
|
|
17
|
+
<th scope="row">{% trans "IDN" %}</th>
|
|
20
18
|
<td>{{ unicode_name }}</td>
|
|
21
19
|
</tr>
|
|
22
20
|
{% endif %}
|
|
23
21
|
{% if parent_zone %}
|
|
24
22
|
<tr>
|
|
25
|
-
<th scope="row">Parent Zone</th>
|
|
23
|
+
<th scope="row">{% trans "Parent Zone" %}</th>
|
|
26
24
|
<td>{{ parent_zone|linkify }}</td>
|
|
27
25
|
</tr>
|
|
28
26
|
{% endif %}
|
|
29
27
|
<tr>
|
|
30
|
-
<th scope="row">View</th>
|
|
28
|
+
<th scope="row">{% trans "View" context "DNS" %}</th>
|
|
31
29
|
<td>{{ object.view|linkify }}</td>
|
|
32
30
|
</tr>
|
|
33
31
|
{% if object.description %}
|
|
34
32
|
<tr>
|
|
35
|
-
<th scope="row">Description</th>
|
|
33
|
+
<th scope="row">{% trans "Description" %}</th>
|
|
36
34
|
<td style="word-break:break-all;">{{ object.description }}</td>
|
|
37
35
|
</tr>
|
|
38
36
|
{% endif %}
|
|
39
37
|
<tr>
|
|
40
|
-
<th scope="row">Tenant</th>
|
|
38
|
+
<th scope="row">{% trans "Tenant" %}</th>
|
|
41
39
|
<td>
|
|
42
40
|
{% if object.tenant.group %}
|
|
43
41
|
{{ object.tenant.group|linkify }} /
|
|
@@ -46,11 +44,11 @@
|
|
|
46
44
|
</td>
|
|
47
45
|
</tr>
|
|
48
46
|
<tr>
|
|
49
|
-
<th scope="row">Status</th>
|
|
47
|
+
<th scope="row">{% trans "Status" %}</th>
|
|
50
48
|
<td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
|
|
51
49
|
</tr>
|
|
52
50
|
<tr>
|
|
53
|
-
<th scope="row">Nameservers</th>
|
|
51
|
+
<th scope="row">{% trans "Nameservers" %}</th>
|
|
54
52
|
<td>
|
|
55
53
|
<table>
|
|
56
54
|
{% for nameserver in object.nameservers.all %}
|
|
@@ -60,7 +58,7 @@
|
|
|
60
58
|
</td>
|
|
61
59
|
{% if nameserver_warnings %}
|
|
62
60
|
<tr>
|
|
63
|
-
<th class="text-warning" scope="row">Warnings</th>
|
|
61
|
+
<th class="text-warning" scope="row">{% trans "Warnings" %}</th>
|
|
64
62
|
<td>
|
|
65
63
|
<table>
|
|
66
64
|
{% for warning in nameserver_warnings %}
|
|
@@ -74,7 +72,7 @@
|
|
|
74
72
|
{% endif %}
|
|
75
73
|
{% if nameserver_errors %}
|
|
76
74
|
<tr>
|
|
77
|
-
<th class="text-danger" scope="row">Errors</th>
|
|
75
|
+
<th class="text-danger" scope="row">{% trans "Errors" %}</th>
|
|
78
76
|
<td>
|
|
79
77
|
<table>
|
|
80
78
|
{% for error in nameserver_errors %}
|
|
@@ -88,11 +86,11 @@
|
|
|
88
86
|
{% endif %}
|
|
89
87
|
</tr>
|
|
90
88
|
<tr>
|
|
91
|
-
<th scope="row">Default TTL</th>
|
|
89
|
+
<th scope="row">{% trans "Default TTL" %}</th>
|
|
92
90
|
<td>{{ object.default_ttl }}</td>
|
|
93
91
|
</tr>
|
|
94
92
|
<tr>
|
|
95
|
-
<th scope="row">Description</th>
|
|
93
|
+
<th scope="row">{% trans "Description" %}</th>
|
|
96
94
|
<td>{{ object.description }}</td>
|
|
97
95
|
</tr>
|
|
98
96
|
</table>
|
|
@@ -103,64 +101,64 @@
|
|
|
103
101
|
</div>
|
|
104
102
|
<div class="col col-md-6">
|
|
105
103
|
<div class="card">
|
|
106
|
-
<h5 class="card-header">Zone SOA</h5>
|
|
104
|
+
<h5 class="card-header">{% trans "Zone SOA" %}</h5>
|
|
107
105
|
<table class="table table-hover attr-table">
|
|
108
106
|
<tr>
|
|
109
|
-
<th scope="row">TTL</th>
|
|
107
|
+
<th scope="row">{% trans "TTL" %}</th>
|
|
110
108
|
<td>{{ object.soa_ttl }}</td>
|
|
111
109
|
</tr>
|
|
112
110
|
<tr>
|
|
113
|
-
<th scope="row">MName</th>
|
|
111
|
+
<th scope="row">{% trans "MName" %}</th>
|
|
114
112
|
<td>{{ object.soa_mname|linkify }}</td>
|
|
115
113
|
</tr>
|
|
116
114
|
<tr>
|
|
117
|
-
<th scope="row">RName</th>
|
|
115
|
+
<th scope="row">{% trans "RName" %}</th>
|
|
118
116
|
<td>{{ object.soa_rname }}</td>
|
|
119
117
|
</tr>
|
|
120
118
|
{% if object.soa_serial_auto %}
|
|
121
119
|
<tr>
|
|
122
|
-
<th scope="row">Serial (auto-generated)</th>
|
|
120
|
+
<th scope="row">{% trans "Serial (auto-generated)" %}</th>
|
|
123
121
|
<td>{{ object.soa_serial }}</td>
|
|
124
122
|
</tr>
|
|
125
123
|
{% else %}
|
|
126
124
|
<tr>
|
|
127
|
-
<th scope="row">Serial</th>
|
|
125
|
+
<th scope="row">{% trans "Serial" context "SOA" %}</th>
|
|
128
126
|
<td>{{ object.soa_serial }}</td>
|
|
129
127
|
</tr>
|
|
130
128
|
{% endif %}
|
|
131
129
|
<tr>
|
|
132
|
-
<th scope="row">Refresh</th>
|
|
130
|
+
<th scope="row">{% trans "Refresh" %}</th>
|
|
133
131
|
<td>{{ object.soa_refresh }}</td>
|
|
134
132
|
</tr>
|
|
135
133
|
<tr>
|
|
136
|
-
<th scope="row">Retry</th>
|
|
134
|
+
<th scope="row">{% trans "Retry" %}</th>
|
|
137
135
|
<td>{{ object.soa_retry }}</td>
|
|
138
136
|
</tr>
|
|
139
137
|
<tr>
|
|
140
|
-
<th scope="row">Expire</th>
|
|
138
|
+
<th scope="row">{% trans "Expire" %}</th>
|
|
141
139
|
<td>{{ object.soa_expire }}</td>
|
|
142
140
|
</tr>
|
|
143
141
|
<tr>
|
|
144
|
-
<th scope="row">Minimum TTL</th>
|
|
142
|
+
<th scope="row">{% trans "Minimum TTL" %}</th>
|
|
145
143
|
<td>{{ object.soa_minimum }}</td>
|
|
146
144
|
</tr>
|
|
147
145
|
</table>
|
|
148
146
|
</div>
|
|
149
147
|
{% if object.rfc2317_prefix %}
|
|
150
148
|
<div class="card">
|
|
151
|
-
<h5 class="card-header">RFC2317</h5>
|
|
149
|
+
<h5 class="card-header">{% trans "RFC2317" %}</h5>
|
|
152
150
|
<table class="table table-hover attr-table">
|
|
153
151
|
<tr>
|
|
154
|
-
<th scope="row">Prefix</th>
|
|
152
|
+
<th scope="row">{% trans "Prefix" %}</th>
|
|
155
153
|
<td>{{ object.rfc2317_prefix }}</td>
|
|
156
154
|
</tr>
|
|
157
155
|
<tr>
|
|
158
|
-
<th scope="row">Parent Managed</th>
|
|
156
|
+
<th scope="row">{% trans "Parent Managed" %}</th>
|
|
159
157
|
<td>{% checkmark object.rfc2317_parent_managed %}</td>
|
|
160
158
|
</tr>
|
|
161
159
|
{% if object.rfc2317_parent_managed %}
|
|
162
160
|
<tr>
|
|
163
|
-
<th scope="row">Parent Zone</th>
|
|
161
|
+
<th scope="row">{% trans "Parent Zone" %}</th>
|
|
164
162
|
<td>{{ object.rfc2317_parent_zone|linkify }}</td>
|
|
165
163
|
</tr>
|
|
166
164
|
{% endif %}
|
|
@@ -3,27 +3,26 @@
|
|
|
3
3
|
{% load plugins %}
|
|
4
4
|
{% load render_table from django_tables2 %}
|
|
5
5
|
{% load perms %}
|
|
6
|
+
{% load i18n %}
|
|
6
7
|
|
|
7
8
|
{% block content %}
|
|
8
9
|
<div class="row">
|
|
9
10
|
<div class="col col-md-6">
|
|
10
11
|
<div class="card">
|
|
11
|
-
<h5 class="card-header">
|
|
12
|
-
Zone Template
|
|
13
|
-
</h5>
|
|
12
|
+
<h5 class="card-header">{% trans "Zone Template" %}</h5>
|
|
14
13
|
<table class="table table-hover attr-table">
|
|
15
14
|
<tr>
|
|
16
|
-
<th scope="row">Name</th>
|
|
15
|
+
<th scope="row">{% trans "Name" %}</th>
|
|
17
16
|
<td>{{ object.name }}</td>
|
|
18
17
|
</tr>
|
|
19
18
|
{% if object.description %}
|
|
20
19
|
<tr>
|
|
21
|
-
<th scope="row">Description</th>
|
|
20
|
+
<th scope="row">{% trans "Description" %}</th>
|
|
22
21
|
<td style="word-break:break-all;">{{ object.description }}</td>
|
|
23
22
|
</tr>
|
|
24
23
|
{% endif %}
|
|
25
24
|
<tr>
|
|
26
|
-
<th scope="row">Tenant</th>
|
|
25
|
+
<th scope="row">{% trans "Tenant" %}</th>
|
|
27
26
|
<td>
|
|
28
27
|
{% if object.tenant.group %}
|
|
29
28
|
{{ object.tenant.group|linkify }} /
|
|
@@ -32,7 +31,7 @@
|
|
|
32
31
|
</td>
|
|
33
32
|
</tr>
|
|
34
33
|
<tr>
|
|
35
|
-
<th scope="row">Nameservers</th>
|
|
34
|
+
<th scope="row">{% trans "Nameservers" %}</th>
|
|
36
35
|
<td>
|
|
37
36
|
<table>
|
|
38
37
|
{% for nameserver in object.nameservers.all %}
|
|
@@ -42,7 +41,7 @@
|
|
|
42
41
|
</td>
|
|
43
42
|
</tr>
|
|
44
43
|
<tr>
|
|
45
|
-
<th scope="row">Description</th>
|
|
44
|
+
<th scope="row">{% trans "Description" %}</th>
|
|
46
45
|
<td>{{ object.description }}</td>
|
|
47
46
|
</tr>
|
|
48
47
|
</table>
|
|
@@ -53,34 +52,45 @@
|
|
|
53
52
|
</div>
|
|
54
53
|
<div class="col col-md-6">
|
|
55
54
|
<div class="card">
|
|
56
|
-
<h5 class="card-header">Domain Registration</h5>
|
|
55
|
+
<h5 class="card-header">{% trans "Domain Registration" %}</h5>
|
|
57
56
|
<table class="table table-hover attr-table">
|
|
58
57
|
<tr>
|
|
59
|
-
<th scope="row">Registrar</th>
|
|
58
|
+
<th scope="row">{% trans "Registrar" %}</th>
|
|
60
59
|
<td>{{ object.registrar|linkify|placeholder }}</td>
|
|
61
60
|
</tr>
|
|
62
61
|
<tr>
|
|
63
|
-
<th scope="row">Registrant</th>
|
|
62
|
+
<th scope="row">{% trans "Registrant" %}</th>
|
|
64
63
|
<td>{{ object.registrant|linkify|placeholder }}</td>
|
|
65
64
|
</tr>
|
|
66
65
|
<tr>
|
|
67
|
-
<th scope="row">Administrative Contact</th>
|
|
66
|
+
<th scope="row">{% trans "Administrative Contact" %}</th>
|
|
68
67
|
<td>{{ object.admin_c|linkify|placeholder }}</td>
|
|
69
68
|
</tr>
|
|
70
69
|
<tr>
|
|
71
|
-
<th scope="row">Technical Contact</th>
|
|
70
|
+
<th scope="row">{% trans "Technical Contact" %}</th>
|
|
72
71
|
<td>{{ object.tech_c|linkify|placeholder }}</td>
|
|
73
72
|
</tr>
|
|
74
73
|
<tr>
|
|
75
|
-
<th scope="row">Billing Contact</th>
|
|
74
|
+
<th scope="row">{% trans "Billing Contact" %}</th>
|
|
76
75
|
<td>{{ object.billing_c|linkify|placeholder }}</td>
|
|
77
76
|
</tr>
|
|
78
77
|
</table>
|
|
79
78
|
</div>
|
|
80
79
|
</div>
|
|
81
80
|
</div>
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
{% if record_template_table %}
|
|
82
|
+
<div class="col col-md-12">
|
|
83
|
+
<div class="card">
|
|
84
|
+
{% if record_template_table.rows|length == 1 %}
|
|
85
|
+
<h2 class="card-header">{% trans "Record Template" %}</h2>
|
|
86
|
+
{% else %}
|
|
87
|
+
<h2 class="card-header">{% trans "Record Templates" %}</h2>
|
|
88
|
+
{% endif %}
|
|
89
|
+
<div class="table-responsive">
|
|
90
|
+
{% render_table record_template_table 'inc/table.html' %}
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
{% endif %}
|
|
85
95
|
</div>
|
|
86
96
|
{% endblock %}
|
|
@@ -158,12 +158,13 @@ def check_dns_records(ip_address, zone=None, view=None):
|
|
|
158
158
|
record.clean(new_zone=new_zone)
|
|
159
159
|
|
|
160
160
|
|
|
161
|
-
def update_dns_records(ip_address, view=None):
|
|
161
|
+
def update_dns_records(ip_address, view=None, force=False):
|
|
162
162
|
from netbox_dns.models import Zone, Record
|
|
163
163
|
|
|
164
|
+
updated = False
|
|
165
|
+
|
|
164
166
|
if ip_address.dns_name == "":
|
|
165
|
-
delete_dns_records(ip_address)
|
|
166
|
-
return
|
|
167
|
+
return delete_dns_records(ip_address)
|
|
167
168
|
|
|
168
169
|
zones = get_zones(ip_address, view=view)
|
|
169
170
|
|
|
@@ -178,16 +179,19 @@ def update_dns_records(ip_address, view=None):
|
|
|
178
179
|
"ipaddress_dns_disabled"
|
|
179
180
|
):
|
|
180
181
|
record.delete()
|
|
182
|
+
updated = True
|
|
181
183
|
continue
|
|
182
184
|
|
|
183
185
|
record.update_fqdn()
|
|
184
|
-
if not _match_data(ip_address, record):
|
|
186
|
+
if not _match_data(ip_address, record) or force:
|
|
185
187
|
updated, deleted = record.update_from_ip_address(ip_address)
|
|
186
188
|
|
|
187
189
|
if deleted:
|
|
188
190
|
record.delete()
|
|
191
|
+
updated = True
|
|
189
192
|
elif updated:
|
|
190
193
|
record.save()
|
|
194
|
+
updated = True
|
|
191
195
|
|
|
192
196
|
zones = Zone.objects.filter(pk__in=[zone.pk for zone in zones]).exclude(
|
|
193
197
|
pk__in=set(
|
|
@@ -203,9 +207,13 @@ def update_dns_records(ip_address, view=None):
|
|
|
203
207
|
|
|
204
208
|
if record is not None:
|
|
205
209
|
record.save()
|
|
210
|
+
updated = True
|
|
211
|
+
|
|
212
|
+
return updated
|
|
206
213
|
|
|
207
214
|
|
|
208
215
|
def delete_dns_records(ip_address, view=None):
|
|
216
|
+
deleted = False
|
|
209
217
|
|
|
210
218
|
if view is None:
|
|
211
219
|
address_records = ip_address.netbox_dns_records.all()
|
|
@@ -214,6 +222,9 @@ def delete_dns_records(ip_address, view=None):
|
|
|
214
222
|
|
|
215
223
|
for record in address_records:
|
|
216
224
|
record.delete()
|
|
225
|
+
deleted = True
|
|
226
|
+
|
|
227
|
+
return deleted
|
|
217
228
|
|
|
218
229
|
|
|
219
230
|
def get_views_by_prefix(prefix):
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import re
|
|
2
2
|
|
|
3
3
|
from django.core.exceptions import ValidationError
|
|
4
|
+
from django.utils.translation import gettext as _
|
|
4
5
|
|
|
5
6
|
from netbox.plugins.utils import get_plugin_config
|
|
6
7
|
|
|
@@ -55,7 +56,9 @@ def validate_fqdn(name, always_tolerant=False):
|
|
|
55
56
|
regex = rf"^(\*|{label})(\.{zone_label})+\.?$"
|
|
56
57
|
|
|
57
58
|
if not re.match(regex, name, flags=re.IGNORECASE) or _has_invalid_double_dash(name):
|
|
58
|
-
raise ValidationError(
|
|
59
|
+
raise ValidationError(
|
|
60
|
+
_("{name} is not a valid fully qualified DNS host name").format(name=name)
|
|
61
|
+
)
|
|
59
62
|
|
|
60
63
|
|
|
61
64
|
def validate_rname(name, always_tolerant=False):
|
|
@@ -63,7 +66,7 @@ def validate_rname(name, always_tolerant=False):
|
|
|
63
66
|
regex = rf"^(\*|{label})(\\\.{label})*(\.{zone_label}){{2,}}\.?$"
|
|
64
67
|
|
|
65
68
|
if not re.match(regex, name, flags=re.IGNORECASE) or _has_invalid_double_dash(name):
|
|
66
|
-
raise ValidationError(
|
|
69
|
+
raise ValidationError(_("{name} is not a valid RName").format(name=name))
|
|
67
70
|
|
|
68
71
|
|
|
69
72
|
def validate_generic_name(
|
|
@@ -76,7 +79,9 @@ def validate_generic_name(
|
|
|
76
79
|
regex = rf"^([*@]|(\*\.)?{label}(\.{zone_label})*\.?)$"
|
|
77
80
|
|
|
78
81
|
if not re.match(regex, name, flags=re.IGNORECASE) or _has_invalid_double_dash(name):
|
|
79
|
-
raise ValidationError(
|
|
82
|
+
raise ValidationError(
|
|
83
|
+
_("{name} is not a valid DNS host name").format(name=name)
|
|
84
|
+
)
|
|
80
85
|
|
|
81
86
|
|
|
82
87
|
def validate_domain_name(
|
|
@@ -97,4 +102,6 @@ def validate_domain_name(
|
|
|
97
102
|
regex = rf"^{label}(\.{zone_label})*\.?$"
|
|
98
103
|
|
|
99
104
|
if not re.match(regex, name, flags=re.IGNORECASE) or _has_invalid_double_dash(name):
|
|
100
|
-
raise ValidationError(
|
|
105
|
+
raise ValidationError(
|
|
106
|
+
_("{name} is not a valid DNS domain name").format(name=name)
|
|
107
|
+
)
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import re
|
|
2
|
+
import textwrap
|
|
3
|
+
|
|
2
4
|
from dns import rdata, name as dns_name
|
|
5
|
+
from dns.exception import SyntaxError
|
|
3
6
|
|
|
4
7
|
from django.core.exceptions import ValidationError
|
|
8
|
+
from django.utils.translation import gettext as _
|
|
5
9
|
|
|
6
10
|
from netbox_dns.choices import RecordClassChoices, RecordTypeChoices
|
|
7
11
|
from netbox_dns.validators import (
|
|
@@ -10,27 +14,69 @@ from netbox_dns.validators import (
|
|
|
10
14
|
validate_generic_name,
|
|
11
15
|
)
|
|
12
16
|
|
|
17
|
+
MAX_TXT_LENGTH = 255
|
|
13
18
|
|
|
14
19
|
__all__ = ("validate_record_value",)
|
|
15
20
|
|
|
16
21
|
|
|
17
|
-
def validate_record_value(
|
|
22
|
+
def validate_record_value(record):
|
|
18
23
|
def _validate_idn(name):
|
|
19
24
|
try:
|
|
20
25
|
name.to_unicode()
|
|
21
26
|
except dns_name.IDNAException as exc:
|
|
22
27
|
raise ValidationError(
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
"{name} is not a valid IDN: {error}.".format(
|
|
29
|
+
name=name.to_text(), error=exc
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def _split_text_value(value):
|
|
34
|
+
# +
|
|
35
|
+
# Text values longer than 255 characters need to be broken up for TXT and
|
|
36
|
+
# SPF records.
|
|
37
|
+
# First, in case they had been split into separate strings, reassemble the
|
|
38
|
+
# original (long) value, then split it into chunks of a maximum length of
|
|
39
|
+
# 255 (preferably at word boundaries), and then build a sequence of partial
|
|
40
|
+
# strings enclosed in double quotes and separated by space.
|
|
41
|
+
#
|
|
42
|
+
# See https://datatracker.ietf.org/doc/html/rfc4408#section-3.1.3 for details.
|
|
43
|
+
# -
|
|
44
|
+
raw_value = "".join(re.findall(r'"([^"]+)"', value))
|
|
45
|
+
if not raw_value:
|
|
46
|
+
raw_value = value
|
|
47
|
+
|
|
48
|
+
return " ".join(
|
|
49
|
+
f'"{part}"'
|
|
50
|
+
for part in textwrap.wrap(raw_value, MAX_TXT_LENGTH, drop_whitespace=False)
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if record.type in (RecordTypeChoices.TXT, RecordTypeChoices.SPF):
|
|
54
|
+
if not (record.value.isascii() and record.value.isprintable()):
|
|
55
|
+
raise ValidationError(
|
|
56
|
+
_(
|
|
57
|
+
"Record value {value} for a type {type} record is not a printable ASCII string."
|
|
58
|
+
).format(value=record.value, type=record.type)
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
if len(record.value) <= MAX_TXT_LENGTH:
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
rr = rdata.from_text(RecordClassChoices.IN, record.type, record.value)
|
|
66
|
+
except SyntaxError as exc:
|
|
67
|
+
if str(exc) == "string too long":
|
|
68
|
+
record.value = _split_text_value(record.value)
|
|
25
69
|
|
|
26
70
|
try:
|
|
27
|
-
rr = rdata.from_text(RecordClassChoices.IN,
|
|
28
|
-
except
|
|
71
|
+
rr = rdata.from_text(RecordClassChoices.IN, record.type, record.value)
|
|
72
|
+
except SyntaxError as exc:
|
|
29
73
|
raise ValidationError(
|
|
30
|
-
|
|
31
|
-
|
|
74
|
+
_(
|
|
75
|
+
"Record value {value} is not a valid value for a {type} record: {error}."
|
|
76
|
+
).format(value=record.value, type=record.type, error=exc)
|
|
77
|
+
)
|
|
32
78
|
|
|
33
|
-
match
|
|
79
|
+
match record.type:
|
|
34
80
|
case RecordTypeChoices.CNAME:
|
|
35
81
|
_validate_idn(rr.target)
|
|
36
82
|
validate_domain_name(
|
netbox_dns/validators/rfc2317.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from django.core.exceptions import ValidationError
|
|
2
|
+
from django.utils.translation import gettext as _
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
__all__ = (
|
|
@@ -11,15 +12,17 @@ __all__ = (
|
|
|
11
12
|
def validate_prefix(prefix):
|
|
12
13
|
if prefix.ip != prefix.cidr.ip:
|
|
13
14
|
raise ValidationError(
|
|
14
|
-
|
|
15
|
+
_("{prefix} is not a valid prefix. Did you mean {cidr}?").format(
|
|
16
|
+
prefix=prefix, cidr=prefix.cidr
|
|
17
|
+
)
|
|
15
18
|
)
|
|
16
19
|
|
|
17
20
|
|
|
18
21
|
def validate_ipv4(prefix):
|
|
19
22
|
if prefix.version != 4:
|
|
20
|
-
raise ValidationError("RFC2317 requires an IPv4 prefix.")
|
|
23
|
+
raise ValidationError(_("RFC2317 requires an IPv4 prefix."))
|
|
21
24
|
|
|
22
25
|
|
|
23
26
|
def validate_rfc2317(prefix):
|
|
24
27
|
if prefix.prefixlen <= 24:
|
|
25
|
-
raise ValidationError("RFC2317 requires at least 25 bit prefix length.")
|
|
28
|
+
raise ValidationError(_("RFC2317 requires at least 25 bit prefix length."))
|
netbox_dns/views/nameserver.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from dns import name as dns_name
|
|
2
2
|
|
|
3
|
+
from django.utils.translation import gettext_lazy as _
|
|
4
|
+
|
|
3
5
|
from netbox.views import generic
|
|
4
6
|
from utilities.views import ViewTab, register_model_view
|
|
5
7
|
from tenancy.views import ObjectContactsView
|
|
@@ -91,7 +93,7 @@ class NameServerZoneListView(generic.ObjectChildrenView):
|
|
|
91
93
|
hide_if_empty = True
|
|
92
94
|
|
|
93
95
|
tab = ViewTab(
|
|
94
|
-
label="Zones",
|
|
96
|
+
label=_("Zones"),
|
|
95
97
|
permission="netbox_dns.view_zone",
|
|
96
98
|
badge=lambda obj: obj.zones.count(),
|
|
97
99
|
hide_if_empty=True,
|
|
@@ -111,7 +113,7 @@ class NameServerSOAZoneListView(generic.ObjectChildrenView):
|
|
|
111
113
|
hide_if_empty = True
|
|
112
114
|
|
|
113
115
|
tab = ViewTab(
|
|
114
|
-
label="SOA Zones",
|
|
116
|
+
label=_("SOA Zones"),
|
|
115
117
|
permission="netbox_dns.view_zone",
|
|
116
118
|
badge=lambda obj: obj.zones_soa.count(),
|
|
117
119
|
hide_if_empty=True,
|
|
@@ -46,9 +46,10 @@ class RecordTemplateView(generic.ObjectView):
|
|
|
46
46
|
if instance.value != unicode_value:
|
|
47
47
|
context["unicode_value"] = unicode_value
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
if instance.zone_templates.exists():
|
|
50
|
+
context["zone_template_table"] = ZoneTemplateDisplayTable(
|
|
51
|
+
data=instance.zone_templates.all()
|
|
52
|
+
)
|
|
52
53
|
|
|
53
54
|
return context
|
|
54
55
|
|
netbox_dns/views/registrar.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from django.utils.translation import gettext as _
|
|
2
|
+
|
|
1
3
|
from netbox.views import generic
|
|
2
4
|
|
|
3
5
|
from utilities.views import ViewTab, register_model_view
|
|
@@ -75,7 +77,7 @@ class RegistrarZoneListView(generic.ObjectChildrenView):
|
|
|
75
77
|
hide_if_empty = True
|
|
76
78
|
|
|
77
79
|
tab = ViewTab(
|
|
78
|
-
label="Zones",
|
|
80
|
+
label=_("Zones"),
|
|
79
81
|
permission="netbox_dns.view_zone",
|
|
80
82
|
badge=lambda obj: obj.zone_set.count(),
|
|
81
83
|
hide_if_empty=True,
|