netbox-plugin-dns 0.21.4__py3-none-any.whl → 1.4.7__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.
- netbox_dns/__init__.py +106 -41
- netbox_dns/api/field_serializers.py +25 -0
- netbox_dns/api/nested_serializers.py +95 -52
- netbox_dns/api/serializers.py +14 -296
- netbox_dns/api/serializers_/__init__.py +0 -0
- netbox_dns/api/serializers_/dnssec_key_template.py +69 -0
- netbox_dns/api/serializers_/dnssec_policy.py +165 -0
- netbox_dns/api/serializers_/nameserver.py +56 -0
- netbox_dns/api/serializers_/prefix.py +18 -0
- netbox_dns/api/serializers_/record.py +105 -0
- netbox_dns/api/serializers_/record_template.py +71 -0
- netbox_dns/api/serializers_/registrar.py +45 -0
- netbox_dns/api/serializers_/registration_contact.py +50 -0
- netbox_dns/api/serializers_/view.py +81 -0
- netbox_dns/api/serializers_/zone.py +247 -0
- netbox_dns/api/serializers_/zone_template.py +157 -0
- netbox_dns/api/urls.py +13 -2
- netbox_dns/api/views.py +96 -58
- netbox_dns/choices/__init__.py +4 -0
- netbox_dns/choices/dnssec_key_template.py +67 -0
- netbox_dns/choices/dnssec_policy.py +40 -0
- netbox_dns/choices/record.py +104 -0
- netbox_dns/choices/utilities.py +4 -0
- netbox_dns/choices/zone.py +119 -0
- netbox_dns/fields/__init__.py +4 -0
- netbox_dns/fields/address.py +22 -16
- netbox_dns/fields/choice_array.py +33 -0
- netbox_dns/fields/ipam.py +15 -0
- netbox_dns/fields/network.py +42 -18
- netbox_dns/fields/rfc2317.py +97 -0
- netbox_dns/fields/timeperiod.py +33 -0
- netbox_dns/filters.py +7 -0
- netbox_dns/filtersets/__init__.py +12 -0
- netbox_dns/filtersets/dnssec_key_template.py +57 -0
- netbox_dns/filtersets/dnssec_policy.py +101 -0
- netbox_dns/filtersets/nameserver.py +46 -0
- netbox_dns/filtersets/record.py +135 -0
- netbox_dns/filtersets/record_template.py +59 -0
- netbox_dns/{filters → filtersets}/registrar.py +8 -1
- netbox_dns/{filters/contact.py → filtersets/registration_contact.py} +9 -3
- netbox_dns/filtersets/view.py +45 -0
- netbox_dns/filtersets/zone.py +254 -0
- netbox_dns/filtersets/zone_template.py +165 -0
- netbox_dns/forms/__init__.py +5 -1
- netbox_dns/forms/dnssec_key_template.py +250 -0
- netbox_dns/forms/dnssec_policy.py +654 -0
- netbox_dns/forms/nameserver.py +121 -27
- netbox_dns/forms/record.py +215 -104
- netbox_dns/forms/record_template.py +285 -0
- netbox_dns/forms/registrar.py +108 -31
- netbox_dns/forms/registration_contact.py +282 -0
- netbox_dns/forms/view.py +331 -20
- netbox_dns/forms/zone.py +769 -373
- netbox_dns/forms/zone_template.py +463 -0
- netbox_dns/graphql/__init__.py +25 -22
- netbox_dns/graphql/enums.py +41 -0
- netbox_dns/graphql/filter_lookups.py +13 -0
- netbox_dns/graphql/filters/__init__.py +12 -0
- netbox_dns/graphql/filters/dnssec_key_template.py +63 -0
- netbox_dns/graphql/filters/dnssec_policy.py +124 -0
- netbox_dns/graphql/filters/nameserver.py +32 -0
- netbox_dns/graphql/filters/record.py +89 -0
- netbox_dns/graphql/filters/record_template.py +55 -0
- netbox_dns/graphql/filters/registrar.py +30 -0
- netbox_dns/graphql/filters/registration_contact.py +27 -0
- netbox_dns/graphql/filters/view.py +28 -0
- netbox_dns/graphql/filters/zone.py +147 -0
- netbox_dns/graphql/filters/zone_template.py +97 -0
- netbox_dns/graphql/schema.py +89 -7
- netbox_dns/graphql/types.py +355 -0
- netbox_dns/locale/de/LC_MESSAGES/django.mo +0 -0
- netbox_dns/locale/en/LC_MESSAGES/django.mo +0 -0
- netbox_dns/locale/fr/LC_MESSAGES/django.mo +0 -0
- netbox_dns/management/commands/cleanup_database.py +175 -156
- netbox_dns/management/commands/cleanup_rrset_ttl.py +64 -0
- netbox_dns/management/commands/rebuild_dnssync.py +23 -0
- netbox_dns/management/commands/setup_dnssync.py +140 -0
- netbox_dns/migrations/0001_squashed_netbox_dns_0_15.py +0 -27
- netbox_dns/migrations/0001_squashed_netbox_dns_0_22.py +557 -0
- netbox_dns/migrations/{0013_add_nameserver_zone_record_description.py → 0002_contact_description_registrar_description.py} +4 -9
- netbox_dns/migrations/0003_default_view.py +15 -0
- netbox_dns/migrations/0004_create_and_assign_default_view.py +26 -0
- netbox_dns/migrations/0005_alter_zone_view_not_null.py +18 -0
- netbox_dns/migrations/0006_templating.py +172 -0
- netbox_dns/migrations/0007_alter_ordering_options.py +25 -0
- netbox_dns/migrations/0008_view_prefixes.py +18 -0
- netbox_dns/migrations/0009_rename_contact_registrationcontact.py +36 -0
- netbox_dns/migrations/0010_view_ip_address_filter.py +18 -0
- netbox_dns/migrations/0011_rename_related_fields.py +63 -0
- netbox_dns/migrations/0012_natural_ordering.py +88 -0
- netbox_dns/migrations/0013_zonetemplate_soa_mname_zonetemplate_soa_rname.py +30 -0
- netbox_dns/migrations/0014_alter_unique_constraints_lowercase.py +42 -0
- netbox_dns/migrations/0015_dnssec.py +168 -0
- netbox_dns/migrations/{0015_add_record_status.py → 0016_dnssec_policy_status.py} +5 -4
- netbox_dns/migrations/0017_dnssec_policy_zone_zone_template.py +41 -0
- netbox_dns/migrations/0018_zone_domain_status_zone_expiration_date.py +23 -0
- netbox_dns/migrations/0019_dnssecpolicy_parental_agents.py +25 -0
- netbox_dns/migrations/0020_netbox_3_4.py +1 -1
- netbox_dns/migrations/0020_remove_dnssecpolicy_parental_agents_and_more.py +29 -0
- netbox_dns/migrations/0021_alter_record_ptr_record.py +25 -0
- netbox_dns/migrations/0021_record_ip_address.py +1 -1
- netbox_dns/migrations/0022_alter_record_ipam_ip_address.py +26 -0
- netbox_dns/migrations/0023_disable_ptr_false.py +27 -0
- netbox_dns/migrations/0024_zonetemplate_parental_agents.py +25 -0
- netbox_dns/migrations/0025_remove_zone_inline_signing_and_more.py +22 -0
- netbox_dns/migrations/0026_alter_dnssecpolicy_nsec3_opt_out.py +18 -0
- netbox_dns/migrations/0026_domain_registration.py +1 -1
- netbox_dns/migrations/0027_zone_comments.py +18 -0
- netbox_dns/migrations/0028_alter_zone_default_ttl_alter_zone_soa_minimum_and_more.py +54 -0
- netbox_dns/migrations/0028_rfc2317_fields.py +44 -0
- netbox_dns/migrations/0029_alter_registrationcontact_street.py +18 -0
- netbox_dns/migrations/0029_record_fqdn.py +30 -0
- netbox_dns/mixins/__init__.py +1 -0
- netbox_dns/mixins/object_modification.py +57 -0
- netbox_dns/models/__init__.py +5 -1
- netbox_dns/models/dnssec_key_template.py +114 -0
- netbox_dns/models/dnssec_policy.py +203 -0
- netbox_dns/models/nameserver.py +61 -30
- netbox_dns/models/record.py +781 -234
- netbox_dns/models/record_template.py +198 -0
- netbox_dns/models/registrar.py +34 -15
- netbox_dns/models/{contact.py → registration_contact.py} +72 -43
- netbox_dns/models/view.py +129 -9
- netbox_dns/models/zone.py +806 -242
- netbox_dns/models/zone_template.py +209 -0
- netbox_dns/navigation.py +176 -76
- netbox_dns/signals/__init__.py +0 -0
- netbox_dns/signals/dnssec.py +32 -0
- netbox_dns/signals/ipam_dnssync.py +216 -0
- netbox_dns/tables/__init__.py +5 -1
- netbox_dns/tables/dnssec_key_template.py +49 -0
- netbox_dns/tables/dnssec_policy.py +140 -0
- netbox_dns/tables/ipam_dnssync.py +12 -0
- netbox_dns/tables/nameserver.py +14 -17
- netbox_dns/tables/record.py +117 -59
- netbox_dns/tables/record_template.py +91 -0
- netbox_dns/tables/registrar.py +20 -10
- netbox_dns/tables/{contact.py → registration_contact.py} +22 -11
- netbox_dns/tables/view.py +47 -3
- netbox_dns/tables/zone.py +62 -31
- netbox_dns/tables/zone_template.py +78 -0
- netbox_dns/template_content.py +124 -38
- netbox_dns/templates/netbox_dns/dnsseckeytemplate.html +70 -0
- netbox_dns/templates/netbox_dns/dnssecpolicy.html +163 -0
- netbox_dns/templates/netbox_dns/nameserver.html +31 -28
- netbox_dns/templates/netbox_dns/record/managed.html +2 -1
- netbox_dns/templates/netbox_dns/record/related.html +17 -6
- netbox_dns/templates/netbox_dns/record.html +140 -93
- netbox_dns/templates/netbox_dns/recordtemplate.html +96 -0
- netbox_dns/templates/netbox_dns/registrar.html +41 -34
- netbox_dns/templates/netbox_dns/registrationcontact.html +76 -0
- netbox_dns/templates/netbox_dns/view/button.html +10 -0
- netbox_dns/templates/netbox_dns/view/prefix.html +44 -0
- netbox_dns/templates/netbox_dns/view/related.html +33 -0
- netbox_dns/templates/netbox_dns/view.html +62 -18
- netbox_dns/templates/netbox_dns/zone/base.html +6 -3
- netbox_dns/templates/netbox_dns/zone/child.html +6 -5
- netbox_dns/templates/netbox_dns/zone/child_zone.html +18 -0
- netbox_dns/templates/netbox_dns/zone/delegation_record.html +18 -0
- netbox_dns/templates/netbox_dns/zone/managed_record.html +1 -1
- netbox_dns/templates/netbox_dns/zone/record.html +6 -5
- netbox_dns/templates/netbox_dns/zone/registration.html +43 -24
- netbox_dns/templates/netbox_dns/zone/rfc2317_child_zone.html +18 -0
- netbox_dns/templates/netbox_dns/zone.html +178 -119
- netbox_dns/templates/netbox_dns/zonetemplate/child.html +46 -0
- netbox_dns/templates/netbox_dns/zonetemplate.html +124 -0
- netbox_dns/templatetags/netbox_dns.py +10 -0
- netbox_dns/urls.py +50 -210
- netbox_dns/utilities/__init__.py +3 -0
- netbox_dns/{utilities.py → utilities/conversions.py} +55 -7
- netbox_dns/utilities/dns.py +11 -0
- netbox_dns/utilities/ipam_dnssync.py +370 -0
- netbox_dns/validators/__init__.py +4 -0
- netbox_dns/validators/dns_name.py +116 -0
- netbox_dns/validators/dns_value.py +147 -0
- netbox_dns/validators/dnssec.py +148 -0
- netbox_dns/validators/rfc2317.py +28 -0
- netbox_dns/views/__init__.py +5 -1
- netbox_dns/views/dnssec_key_template.py +78 -0
- netbox_dns/views/dnssec_policy.py +146 -0
- netbox_dns/views/nameserver.py +34 -15
- netbox_dns/views/record.py +156 -15
- netbox_dns/views/record_template.py +93 -0
- netbox_dns/views/registrar.py +32 -13
- netbox_dns/views/registration_contact.py +101 -0
- netbox_dns/views/view.py +58 -14
- netbox_dns/views/zone.py +130 -33
- netbox_dns/views/zone_template.py +82 -0
- netbox_plugin_dns-1.4.7.dist-info/METADATA +132 -0
- netbox_plugin_dns-1.4.7.dist-info/RECORD +201 -0
- {netbox_plugin_dns-0.21.4.dist-info → netbox_plugin_dns-1.4.7.dist-info}/WHEEL +2 -1
- {netbox_plugin_dns-0.21.4.dist-info → netbox_plugin_dns-1.4.7.dist-info/licenses}/LICENSE +2 -1
- netbox_plugin_dns-1.4.7.dist-info/top_level.txt +1 -0
- netbox_dns/filters/__init__.py +0 -6
- netbox_dns/filters/nameserver.py +0 -18
- netbox_dns/filters/record.py +0 -53
- netbox_dns/filters/view.py +0 -18
- netbox_dns/filters/zone.py +0 -112
- netbox_dns/forms/contact.py +0 -211
- netbox_dns/graphql/contact.py +0 -19
- netbox_dns/graphql/nameserver.py +0 -19
- netbox_dns/graphql/record.py +0 -19
- netbox_dns/graphql/registrar.py +0 -19
- netbox_dns/graphql/view.py +0 -19
- netbox_dns/graphql/zone.py +0 -19
- netbox_dns/management/commands/setup_coupling.py +0 -75
- netbox_dns/management/commands/update_soa.py +0 -22
- netbox_dns/middleware.py +0 -226
- netbox_dns/migrations/0001_initial.py +0 -115
- netbox_dns/migrations/0002_zone_default_ttl.py +0 -18
- netbox_dns/migrations/0003_soa_managed_records.py +0 -112
- netbox_dns/migrations/0004_create_ptr_for_a_aaaa_records.py +0 -80
- netbox_dns/migrations/0005_update_ns_records.py +0 -41
- netbox_dns/migrations/0006_zone_soa_serial_auto.py +0 -29
- netbox_dns/migrations/0007_alter_zone_soa_serial_auto.py +0 -17
- netbox_dns/migrations/0008_zone_status_names.py +0 -21
- netbox_dns/migrations/0009_netbox32.py +0 -71
- netbox_dns/migrations/0010_update_soa_records.py +0 -58
- netbox_dns/migrations/0011_add_view_model.py +0 -70
- netbox_dns/migrations/0012_adjust_zone_and_record.py +0 -17
- netbox_dns/migrations/0014_add_view_description.py +0 -16
- netbox_dns/migrations/0016_cleanup_ptr_records.py +0 -38
- netbox_dns/migrations/0017_alter_record_ttl.py +0 -17
- netbox_dns/migrations/0018_zone_arpa_network.py +0 -51
- netbox_dns/migrations/0019_update_ns_ttl.py +0 -19
- netbox_dns/templates/netbox_dns/contact.html +0 -71
- netbox_dns/templates/netbox_dns/related_dns_objects.html +0 -21
- netbox_dns/templatetags/view_helpers.py +0 -15
- netbox_dns/validators.py +0 -57
- netbox_dns/views/contact.py +0 -83
- netbox_plugin_dns-0.21.4.dist-info/METADATA +0 -101
- netbox_plugin_dns-0.21.4.dist-info/RECORD +0 -110
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import dns
|
|
2
|
+
from dns import name as dns_name
|
|
3
|
+
|
|
4
|
+
from django.core.exceptions import ValidationError
|
|
5
|
+
from django.db import models
|
|
6
|
+
from django.utils.translation import gettext_lazy as _
|
|
7
|
+
from django.core.validators import MaxValueValidator
|
|
8
|
+
|
|
9
|
+
from netbox.models import NetBoxModel
|
|
10
|
+
from netbox.search import SearchIndex, register_search
|
|
11
|
+
from netbox.plugins.utils import get_plugin_config
|
|
12
|
+
|
|
13
|
+
from netbox_dns.choices import RecordTypeChoices, RecordStatusChoices
|
|
14
|
+
from netbox_dns.validators import validate_generic_name, validate_record_value
|
|
15
|
+
|
|
16
|
+
from .record import Record
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
__all__ = (
|
|
20
|
+
"RecordTemplate",
|
|
21
|
+
"RecordTemplateIndex",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class RecordTemplate(NetBoxModel):
|
|
26
|
+
class Meta:
|
|
27
|
+
verbose_name = _("Record Template")
|
|
28
|
+
verbose_name_plural = _("Record Templates")
|
|
29
|
+
|
|
30
|
+
ordering = ("name",)
|
|
31
|
+
|
|
32
|
+
clone_fields = (
|
|
33
|
+
"record_name",
|
|
34
|
+
"description",
|
|
35
|
+
"type",
|
|
36
|
+
"value",
|
|
37
|
+
"status",
|
|
38
|
+
"ttl",
|
|
39
|
+
"disable_ptr",
|
|
40
|
+
"tenant",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
template_fields = (
|
|
44
|
+
"type",
|
|
45
|
+
"value",
|
|
46
|
+
"status",
|
|
47
|
+
"ttl",
|
|
48
|
+
"disable_ptr",
|
|
49
|
+
"tenant",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def __str__(self):
|
|
53
|
+
return str(self.name)
|
|
54
|
+
|
|
55
|
+
name = models.CharField(
|
|
56
|
+
verbose_name=_("Template Name"),
|
|
57
|
+
unique=True,
|
|
58
|
+
max_length=200,
|
|
59
|
+
db_collation="natural_sort",
|
|
60
|
+
)
|
|
61
|
+
record_name = models.CharField(
|
|
62
|
+
verbose_name=_("Name"),
|
|
63
|
+
max_length=255,
|
|
64
|
+
db_collation="natural_sort",
|
|
65
|
+
)
|
|
66
|
+
description = models.CharField(
|
|
67
|
+
verbose_name=_("Description"),
|
|
68
|
+
max_length=200,
|
|
69
|
+
blank=True,
|
|
70
|
+
)
|
|
71
|
+
type = models.CharField(
|
|
72
|
+
verbose_name=_("Type"),
|
|
73
|
+
choices=RecordTypeChoices,
|
|
74
|
+
)
|
|
75
|
+
value = models.CharField(
|
|
76
|
+
verbose_name=_("Value"),
|
|
77
|
+
max_length=65535,
|
|
78
|
+
)
|
|
79
|
+
status = models.CharField(
|
|
80
|
+
verbose_name=_("Status"),
|
|
81
|
+
choices=RecordStatusChoices,
|
|
82
|
+
default=RecordStatusChoices.STATUS_ACTIVE,
|
|
83
|
+
blank=False,
|
|
84
|
+
)
|
|
85
|
+
ttl = models.PositiveIntegerField(
|
|
86
|
+
verbose_name=_("TTL"),
|
|
87
|
+
null=True,
|
|
88
|
+
blank=True,
|
|
89
|
+
validators=[MaxValueValidator(2147483647)],
|
|
90
|
+
)
|
|
91
|
+
disable_ptr = models.BooleanField(
|
|
92
|
+
verbose_name=_("Disable PTR"),
|
|
93
|
+
help_text=_("Disable PTR record creation"),
|
|
94
|
+
default=False,
|
|
95
|
+
)
|
|
96
|
+
tenant = models.ForeignKey(
|
|
97
|
+
verbose_name=_("Tenant"),
|
|
98
|
+
to="tenancy.Tenant",
|
|
99
|
+
on_delete=models.PROTECT,
|
|
100
|
+
related_name="+",
|
|
101
|
+
blank=True,
|
|
102
|
+
null=True,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def get_status_color(self):
|
|
106
|
+
return RecordStatusChoices.colors.get(self.status)
|
|
107
|
+
|
|
108
|
+
def validate_name(self):
|
|
109
|
+
try:
|
|
110
|
+
name = dns_name.from_text(self.record_name, origin=None)
|
|
111
|
+
name.to_unicode()
|
|
112
|
+
|
|
113
|
+
except dns.exception.DNSException as exc:
|
|
114
|
+
raise ValidationError({"record_name": str(exc)})
|
|
115
|
+
|
|
116
|
+
if self.type not in get_plugin_config(
|
|
117
|
+
"netbox_dns", "tolerate_non_rfc1035_types", default=[]
|
|
118
|
+
):
|
|
119
|
+
try:
|
|
120
|
+
validate_generic_name(
|
|
121
|
+
self.record_name,
|
|
122
|
+
(
|
|
123
|
+
self.type
|
|
124
|
+
in get_plugin_config(
|
|
125
|
+
"netbox_dns",
|
|
126
|
+
"tolerate_leading_underscore_types",
|
|
127
|
+
default=[],
|
|
128
|
+
)
|
|
129
|
+
),
|
|
130
|
+
)
|
|
131
|
+
except ValidationError as exc:
|
|
132
|
+
raise ValidationError(
|
|
133
|
+
{
|
|
134
|
+
"record_name": exc,
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
def validate_value(self):
|
|
139
|
+
try:
|
|
140
|
+
validate_record_value(self)
|
|
141
|
+
except ValidationError as exc:
|
|
142
|
+
raise ValidationError({"value": exc})
|
|
143
|
+
|
|
144
|
+
def matching_records(self, zone):
|
|
145
|
+
return zone.records.filter(
|
|
146
|
+
name=self.record_name, type=self.type, value=self.value
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
def create_record(self, zone):
|
|
150
|
+
if self.matching_records(zone).exists():
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
record_data = {
|
|
154
|
+
"zone": zone,
|
|
155
|
+
"name": self.record_name,
|
|
156
|
+
}
|
|
157
|
+
for field in self.template_fields:
|
|
158
|
+
record_data[field] = getattr(self, field)
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
record = Record.objects.create(**record_data)
|
|
162
|
+
except ValidationError as exc:
|
|
163
|
+
raise ValidationError(
|
|
164
|
+
{
|
|
165
|
+
None: _(
|
|
166
|
+
"Error while processing record template {template}: {error}"
|
|
167
|
+
).format(template=self, error=exc.messages[0])
|
|
168
|
+
}
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
if tags := self.tags.all():
|
|
172
|
+
record.tags.set(tags)
|
|
173
|
+
|
|
174
|
+
def clean_fields(self, exclude=None):
|
|
175
|
+
self.type = self.type.upper()
|
|
176
|
+
if get_plugin_config("netbox_dns", "convert_names_to_lowercase", False):
|
|
177
|
+
self.record_name = self.record_name.lower()
|
|
178
|
+
|
|
179
|
+
super().clean_fields(exclude=exclude)
|
|
180
|
+
|
|
181
|
+
def clean(self, *args, **kwargs):
|
|
182
|
+
self.validate_name()
|
|
183
|
+
self.validate_value()
|
|
184
|
+
|
|
185
|
+
super().clean(*args, **kwargs)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@register_search
|
|
189
|
+
class RecordTemplateIndex(SearchIndex):
|
|
190
|
+
model = RecordTemplate
|
|
191
|
+
|
|
192
|
+
fields = (
|
|
193
|
+
("name", 100),
|
|
194
|
+
("record_name", 120),
|
|
195
|
+
("value", 150),
|
|
196
|
+
("type", 200),
|
|
197
|
+
("description", 500),
|
|
198
|
+
)
|
netbox_dns/models/registrar.py
CHANGED
|
@@ -1,61 +1,80 @@
|
|
|
1
1
|
from django.db import models
|
|
2
|
-
from django.
|
|
2
|
+
from django.utils.translation import gettext_lazy as _
|
|
3
3
|
|
|
4
4
|
from netbox.models import NetBoxModel
|
|
5
5
|
from netbox.search import SearchIndex, register_search
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
__all__ = (
|
|
9
|
+
"Registrar",
|
|
10
|
+
"RegistrarIndex",
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
8
14
|
class Registrar(NetBoxModel):
|
|
15
|
+
class Meta:
|
|
16
|
+
verbose_name = _("Registrar")
|
|
17
|
+
verbose_name_plural = _("Registrars")
|
|
18
|
+
|
|
19
|
+
ordering = (
|
|
20
|
+
"name",
|
|
21
|
+
"iana_id",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
def __str__(self):
|
|
25
|
+
return str(self.name)
|
|
26
|
+
|
|
9
27
|
# +
|
|
10
28
|
# Data fields according to https://www.icann.org/resources/pages/rdds-labeling-policy-2017-02-01-en
|
|
11
29
|
# -
|
|
12
30
|
name = models.CharField(
|
|
31
|
+
verbose_name=_("Name"),
|
|
13
32
|
unique=True,
|
|
14
33
|
max_length=255,
|
|
34
|
+
db_collation="natural_sort",
|
|
35
|
+
)
|
|
36
|
+
description = models.CharField(
|
|
37
|
+
verbose_name=_("Description"),
|
|
38
|
+
blank=True,
|
|
39
|
+
max_length=200,
|
|
15
40
|
)
|
|
16
41
|
iana_id = models.IntegerField(
|
|
17
|
-
verbose_name="IANA ID",
|
|
42
|
+
verbose_name=_("IANA ID"),
|
|
18
43
|
null=True,
|
|
19
44
|
blank=True,
|
|
20
45
|
)
|
|
21
46
|
referral_url = models.URLField(
|
|
22
|
-
verbose_name="Referral URL",
|
|
47
|
+
verbose_name=_("Referral URL"),
|
|
23
48
|
max_length=255,
|
|
24
49
|
blank=True,
|
|
25
50
|
)
|
|
26
51
|
whois_server = models.CharField(
|
|
27
|
-
verbose_name="WHOIS Server",
|
|
52
|
+
verbose_name=_("WHOIS Server"),
|
|
28
53
|
max_length=255,
|
|
29
54
|
blank=True,
|
|
30
55
|
)
|
|
31
56
|
address = models.CharField(
|
|
57
|
+
verbose_name=_("Address"),
|
|
32
58
|
max_length=200,
|
|
33
59
|
blank=True,
|
|
34
60
|
)
|
|
35
61
|
abuse_email = models.EmailField(
|
|
36
|
-
verbose_name="Abuse Email",
|
|
62
|
+
verbose_name=_("Abuse Email"),
|
|
37
63
|
blank=True,
|
|
38
64
|
)
|
|
39
65
|
abuse_phone = models.CharField(
|
|
40
|
-
verbose_name="Abuse Phone",
|
|
66
|
+
verbose_name=_("Abuse Phone"),
|
|
41
67
|
max_length=50,
|
|
42
68
|
blank=True,
|
|
43
69
|
)
|
|
44
70
|
|
|
45
|
-
def get_absolute_url(self):
|
|
46
|
-
return reverse("plugins:netbox_dns:registrar", kwargs={"pk": self.id})
|
|
47
|
-
|
|
48
|
-
def __str__(self):
|
|
49
|
-
return str(self.name)
|
|
50
|
-
|
|
51
|
-
class Meta:
|
|
52
|
-
ordering = ("name", "iana_id")
|
|
53
|
-
|
|
54
71
|
|
|
55
72
|
@register_search
|
|
56
73
|
class RegistrarIndex(SearchIndex):
|
|
57
74
|
model = Registrar
|
|
75
|
+
|
|
58
76
|
fields = (
|
|
59
77
|
("name", 100),
|
|
60
78
|
("iana_id", 100),
|
|
79
|
+
("description", 500),
|
|
61
80
|
)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from django.db import models
|
|
2
|
-
from django.
|
|
2
|
+
from django.utils.translation import gettext_lazy as _
|
|
3
3
|
|
|
4
4
|
from netbox.models import NetBoxModel
|
|
5
5
|
from netbox.search import SearchIndex, register_search
|
|
@@ -7,108 +7,135 @@ from netbox.search import SearchIndex, register_search
|
|
|
7
7
|
from taggit.managers import TaggableManager
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
__all__ = (
|
|
11
|
+
"RegistrationContact",
|
|
12
|
+
"RegistrationContactIndex",
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class RegistrationContact(NetBoxModel):
|
|
17
|
+
class Meta:
|
|
18
|
+
verbose_name = _("Registration Contact")
|
|
19
|
+
verbose_name_plural = _("Registration Contacts")
|
|
20
|
+
|
|
21
|
+
ordering = (
|
|
22
|
+
"name",
|
|
23
|
+
"contact_id",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
clone_fields = (
|
|
27
|
+
"name",
|
|
28
|
+
"description",
|
|
29
|
+
"organization",
|
|
30
|
+
"street",
|
|
31
|
+
"city",
|
|
32
|
+
"state_province",
|
|
33
|
+
"postal_code",
|
|
34
|
+
"country",
|
|
35
|
+
"phone",
|
|
36
|
+
"phone_ext",
|
|
37
|
+
"fax",
|
|
38
|
+
"fax_ext",
|
|
39
|
+
"email",
|
|
40
|
+
"tags",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def __str__(self):
|
|
44
|
+
if self.name is not None:
|
|
45
|
+
return f"{self.contact_id} ({self.name})"
|
|
46
|
+
|
|
47
|
+
return self.contact_id
|
|
48
|
+
|
|
11
49
|
# +
|
|
12
50
|
# Data fields according to https://www.icann.org/resources/pages/rdds-labeling-policy-2017-02-01-en
|
|
13
51
|
# -
|
|
14
52
|
contact_id = models.CharField(
|
|
15
|
-
verbose_name="Contact ID",
|
|
53
|
+
verbose_name=_("Contact ID"),
|
|
16
54
|
max_length=50,
|
|
17
55
|
unique=True,
|
|
56
|
+
db_collation="natural_sort",
|
|
18
57
|
)
|
|
19
58
|
name = models.CharField(
|
|
59
|
+
verbose_name=_("Name"),
|
|
20
60
|
blank=True,
|
|
21
61
|
max_length=100,
|
|
62
|
+
db_collation="natural_sort",
|
|
63
|
+
)
|
|
64
|
+
description = models.CharField(
|
|
65
|
+
verbose_name=_("Description"),
|
|
66
|
+
blank=True,
|
|
67
|
+
max_length=200,
|
|
22
68
|
)
|
|
23
69
|
organization = models.CharField(
|
|
70
|
+
verbose_name=_("Organization"),
|
|
24
71
|
blank=True,
|
|
25
72
|
max_length=200,
|
|
26
73
|
)
|
|
27
74
|
street = models.CharField(
|
|
75
|
+
verbose_name=_("Street"),
|
|
28
76
|
blank=True,
|
|
29
|
-
max_length=
|
|
77
|
+
max_length=255,
|
|
30
78
|
)
|
|
31
79
|
city = models.CharField(
|
|
80
|
+
verbose_name=_("City"),
|
|
32
81
|
blank=True,
|
|
33
82
|
max_length=50,
|
|
34
83
|
)
|
|
35
84
|
state_province = models.CharField(
|
|
36
|
-
verbose_name="State/Province",
|
|
85
|
+
verbose_name=_("State/Province"),
|
|
37
86
|
blank=True,
|
|
38
87
|
max_length=255,
|
|
39
88
|
)
|
|
40
89
|
postal_code = models.CharField(
|
|
41
|
-
verbose_name="Postal Code",
|
|
90
|
+
verbose_name=_("Postal Code"),
|
|
42
91
|
blank=True,
|
|
43
92
|
max_length=20,
|
|
44
93
|
)
|
|
45
94
|
country = models.CharField(
|
|
46
|
-
verbose_name="Country (ISO 3166)",
|
|
95
|
+
verbose_name=_("Country (ISO 3166)"),
|
|
47
96
|
blank=True,
|
|
48
97
|
max_length=2,
|
|
49
98
|
)
|
|
50
99
|
phone = models.CharField(
|
|
51
|
-
verbose_name="Phone",
|
|
100
|
+
verbose_name=_("Phone"),
|
|
52
101
|
blank=True,
|
|
53
102
|
max_length=50,
|
|
54
103
|
)
|
|
55
104
|
phone_ext = models.CharField(
|
|
56
|
-
verbose_name="Phone Extension",
|
|
105
|
+
verbose_name=_("Phone Extension"),
|
|
57
106
|
blank=True,
|
|
58
107
|
max_length=50,
|
|
59
108
|
)
|
|
60
109
|
fax = models.CharField(
|
|
61
|
-
verbose_name="Fax",
|
|
110
|
+
verbose_name=_("Fax"),
|
|
62
111
|
blank=True,
|
|
63
112
|
max_length=50,
|
|
64
113
|
)
|
|
65
114
|
fax_ext = models.CharField(
|
|
66
|
-
verbose_name="Fax Extension",
|
|
115
|
+
verbose_name=_("Fax Extension"),
|
|
67
116
|
blank=True,
|
|
68
117
|
max_length=50,
|
|
69
118
|
)
|
|
70
119
|
email = models.EmailField(
|
|
71
|
-
verbose_name="Email",
|
|
120
|
+
verbose_name=_("Email"),
|
|
72
121
|
blank=True,
|
|
73
122
|
)
|
|
74
123
|
|
|
124
|
+
# +
|
|
125
|
+
# TODO: Retained for backwards compatibility with older versions where
|
|
126
|
+
# 'RegistrationContact' was still ambiguously named 'Contact'.
|
|
127
|
+
#
|
|
128
|
+
# Removing it requires a data migration.
|
|
129
|
+
# -
|
|
75
130
|
tags = TaggableManager(
|
|
76
131
|
through="extras.TaggedItem",
|
|
77
132
|
related_name="netbox_dns_contact_set",
|
|
78
133
|
)
|
|
79
134
|
|
|
80
|
-
clone_fields = [
|
|
81
|
-
"name",
|
|
82
|
-
"organization",
|
|
83
|
-
"street",
|
|
84
|
-
"city",
|
|
85
|
-
"state_province",
|
|
86
|
-
"postal_code",
|
|
87
|
-
"country",
|
|
88
|
-
"phone",
|
|
89
|
-
"phone_ext",
|
|
90
|
-
"fax",
|
|
91
|
-
"fax_ext",
|
|
92
|
-
"email",
|
|
93
|
-
"tags",
|
|
94
|
-
]
|
|
95
|
-
|
|
96
|
-
def get_absolute_url(self):
|
|
97
|
-
return reverse("plugins:netbox_dns:contact", kwargs={"pk": self.id})
|
|
98
|
-
|
|
99
|
-
def __str__(self):
|
|
100
|
-
if self.name is not None:
|
|
101
|
-
return f"{self.contact_id} ({self.name})"
|
|
102
|
-
|
|
103
|
-
return self.contact_id
|
|
104
|
-
|
|
105
|
-
class Meta:
|
|
106
|
-
ordering = ("name", "contact_id")
|
|
107
|
-
|
|
108
135
|
@property
|
|
109
136
|
def zones(self):
|
|
110
137
|
return (
|
|
111
|
-
set(self.
|
|
138
|
+
set(self.registrant_zones.all())
|
|
112
139
|
| set(self.admin_c_zones.all())
|
|
113
140
|
| set(self.tech_c_zones.all())
|
|
114
141
|
| set(self.billing_c_zones.all())
|
|
@@ -116,11 +143,13 @@ class Contact(NetBoxModel):
|
|
|
116
143
|
|
|
117
144
|
|
|
118
145
|
@register_search
|
|
119
|
-
class
|
|
120
|
-
model =
|
|
146
|
+
class RegistrationContactIndex(SearchIndex):
|
|
147
|
+
model = RegistrationContact
|
|
148
|
+
|
|
121
149
|
fields = (
|
|
122
150
|
("name", 100),
|
|
123
151
|
("contact_id", 100),
|
|
124
152
|
("email", 200),
|
|
125
153
|
("organization", 500),
|
|
154
|
+
("description", 500),
|
|
126
155
|
)
|
netbox_dns/models/view.py
CHANGED
|
@@ -1,20 +1,73 @@
|
|
|
1
1
|
from django.db import models
|
|
2
|
-
from django.
|
|
2
|
+
from django.core.exceptions import ValidationError
|
|
3
|
+
from django.utils.translation import gettext_lazy as _
|
|
3
4
|
|
|
4
5
|
from netbox.models import NetBoxModel
|
|
6
|
+
from netbox.models.features import ContactsMixin
|
|
5
7
|
from netbox.search import SearchIndex, register_search
|
|
8
|
+
from netbox.context import current_request
|
|
9
|
+
from utilities.exceptions import AbortRequest
|
|
6
10
|
|
|
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
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
__all__ = (
|
|
22
|
+
"View",
|
|
23
|
+
"ViewIndex",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
|
|
28
|
+
class Meta:
|
|
29
|
+
verbose_name = _("View")
|
|
30
|
+
verbose_name_plural = _("Views")
|
|
31
|
+
|
|
32
|
+
ordering = ("name",)
|
|
33
|
+
|
|
34
|
+
clone_fields = (
|
|
35
|
+
"name",
|
|
36
|
+
"description",
|
|
37
|
+
"tenant",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def __str__(self):
|
|
41
|
+
return str(self.name)
|
|
7
42
|
|
|
8
|
-
class View(NetBoxModel):
|
|
9
43
|
name = models.CharField(
|
|
44
|
+
verbose_name=_("Name"),
|
|
10
45
|
unique=True,
|
|
11
46
|
max_length=255,
|
|
47
|
+
db_collation="natural_sort",
|
|
12
48
|
)
|
|
13
49
|
description = models.CharField(
|
|
50
|
+
verbose_name=_("Description"),
|
|
14
51
|
max_length=200,
|
|
15
52
|
blank=True,
|
|
16
53
|
)
|
|
54
|
+
default_view = models.BooleanField(
|
|
55
|
+
verbose_name=_("Default View"),
|
|
56
|
+
default=False,
|
|
57
|
+
)
|
|
58
|
+
prefixes = models.ManyToManyField(
|
|
59
|
+
verbose_name=_("IPAM Prefixes"),
|
|
60
|
+
to="ipam.Prefix",
|
|
61
|
+
related_name="netbox_dns_views",
|
|
62
|
+
blank=True,
|
|
63
|
+
)
|
|
64
|
+
ip_address_filter = models.JSONField(
|
|
65
|
+
verbose_name=_("IP Address Filter"),
|
|
66
|
+
blank=True,
|
|
67
|
+
null=True,
|
|
68
|
+
)
|
|
17
69
|
tenant = models.ForeignKey(
|
|
70
|
+
verbose_name=_("Tenant"),
|
|
18
71
|
to="tenancy.Tenant",
|
|
19
72
|
on_delete=models.PROTECT,
|
|
20
73
|
related_name="netbox_dns_views",
|
|
@@ -22,21 +75,88 @@ class View(NetBoxModel):
|
|
|
22
75
|
null=True,
|
|
23
76
|
)
|
|
24
77
|
|
|
25
|
-
|
|
78
|
+
@classmethod
|
|
79
|
+
def get_default_view(cls):
|
|
80
|
+
return cls.objects.get(default_view=True)
|
|
26
81
|
|
|
27
|
-
def
|
|
28
|
-
|
|
82
|
+
def delete(self, *args, **kwargs):
|
|
83
|
+
if self.default_view:
|
|
84
|
+
if current_request.get() is not None:
|
|
85
|
+
raise AbortRequest(_("The default view cannot be deleted"))
|
|
29
86
|
|
|
30
|
-
|
|
31
|
-
return str(self.name)
|
|
87
|
+
raise ValidationError(_("The default view cannot be deleted"))
|
|
32
88
|
|
|
33
|
-
|
|
34
|
-
|
|
89
|
+
super().delete(*args, **kwargs)
|
|
90
|
+
|
|
91
|
+
def clean(self, *args, **kwargs):
|
|
92
|
+
if (changed_fields := self.changed_fields) is None:
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
if (
|
|
96
|
+
"default_view" in changed_fields
|
|
97
|
+
and not self.default_view
|
|
98
|
+
and not View.objects.filter(default_view=True).exclude(pk=self.pk).exists()
|
|
99
|
+
):
|
|
100
|
+
raise ValidationError(
|
|
101
|
+
{
|
|
102
|
+
"default_view": _(
|
|
103
|
+
"Please select a different view as default view to change this setting!"
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
if "ip_address_filter" in changed_fields and self.get_saved_value(
|
|
109
|
+
"ip_address_filter"
|
|
110
|
+
):
|
|
111
|
+
try:
|
|
112
|
+
for ip_address in get_ip_addresses_by_view(self).filter(
|
|
113
|
+
get_query_from_filter(self.ip_address_filter)
|
|
114
|
+
):
|
|
115
|
+
check_dns_records(ip_address, view=self)
|
|
116
|
+
except ValidationError as exc:
|
|
117
|
+
raise ValidationError(
|
|
118
|
+
{
|
|
119
|
+
"ip_address_filter": exc.messages,
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
super().clean(*args, **kwargs)
|
|
124
|
+
|
|
125
|
+
def save(self, *args, **kwargs):
|
|
126
|
+
self.clean()
|
|
127
|
+
|
|
128
|
+
changed_fields = self.changed_fields
|
|
129
|
+
|
|
130
|
+
super().save(*args, **kwargs)
|
|
131
|
+
|
|
132
|
+
if (changed_fields is None and self.default_view) or (
|
|
133
|
+
changed_fields is not None
|
|
134
|
+
and self.default_view
|
|
135
|
+
and "default_view" in changed_fields
|
|
136
|
+
):
|
|
137
|
+
other_views = View.objects.filter(default_view=True).exclude(pk=self.pk)
|
|
138
|
+
for view in other_views:
|
|
139
|
+
view.default_view = False
|
|
140
|
+
view.save()
|
|
141
|
+
|
|
142
|
+
if changed_fields is not None and "ip_address_filter" in changed_fields:
|
|
143
|
+
ip_addresses = get_ip_addresses_by_view(self)
|
|
144
|
+
|
|
145
|
+
for ip_address in ip_addresses.exclude(
|
|
146
|
+
get_query_from_filter(self.ip_address_filter)
|
|
147
|
+
):
|
|
148
|
+
delete_dns_records(ip_address, view=self)
|
|
149
|
+
|
|
150
|
+
for ip_address in ip_addresses.filter(
|
|
151
|
+
get_query_from_filter(self.ip_address_filter)
|
|
152
|
+
):
|
|
153
|
+
update_dns_records(ip_address, view=self)
|
|
35
154
|
|
|
36
155
|
|
|
37
156
|
@register_search
|
|
38
157
|
class ViewIndex(SearchIndex):
|
|
39
158
|
model = View
|
|
159
|
+
|
|
40
160
|
fields = (
|
|
41
161
|
("name", 100),
|
|
42
162
|
("description", 500),
|