netbox-plugin-dns 1.1.0b1__tar.gz → 1.1b3__tar.gz
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_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/PKG-INFO +2 -2
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/__init__.py +3 -2
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/forms/view.py +93 -4
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/forms/zone.py +8 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/management/commands/setup_autodns.py +40 -20
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/models/record.py +54 -11
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/models/zone.py +19 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/signals/ipam_autodns.py +99 -16
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/template_content.py +12 -0
- netbox_plugin_dns-1.1b3/netbox_dns/templates/netbox_dns/view/button.html +9 -0
- netbox_plugin_dns-1.1b3/netbox_dns/templates/netbox_dns/view/prefix.html +41 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/templates/netbox_dns/view.html +2 -6
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/urls/view.py +6 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/utilities/ipam_autodns.py +94 -20
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/views/view.py +26 -1
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/pyproject.toml +2 -2
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/LICENSE +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/README.md +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/api/nested_serializers.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/api/serializers.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/api/serializers_/__init__.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/api/serializers_/contact.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/api/serializers_/nameserver.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/api/serializers_/prefix.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/api/serializers_/record.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/api/serializers_/record_template.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/api/serializers_/registrar.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/api/serializers_/view.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/api/serializers_/zone.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/api/serializers_/zone_template.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/api/urls.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/api/views.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/apps.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/choices/__init__.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/choices/record.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/choices/zone.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/fields/__init__.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/fields/address.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/fields/ipam.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/fields/network.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/fields/rfc2317.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/filtersets/__init__.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/filtersets/contact.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/filtersets/nameserver.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/filtersets/record.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/filtersets/record_template.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/filtersets/registrar.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/filtersets/view.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/filtersets/zone.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/filtersets/zone_template.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/forms/__init__.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/forms/contact.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/forms/nameserver.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/forms/record.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/forms/record_template.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/forms/registrar.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/forms/zone_template.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/graphql/__init__.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/graphql/filters.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/graphql/schema.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/graphql/types.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/management/commands/cleanup_database.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/management/commands/cleanup_rrset_ttl.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/management/commands/update_soa.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/0001_squashed_netbox_dns_0_15.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/0001_squashed_netbox_dns_0_22.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/0002_contact_description_registrar_description.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/0003_default_view.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/0004_create_and_assign_default_view.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/0005_alter_zone_view_not_null.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/0006_templating.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/0007_view_prefixes.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/0020_netbox_3_4.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/0021_record_ip_address.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/0022_search.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/0023_alter_record_value.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/0024_tenancy.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/0025_ipam_coupling_cf.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/0026_domain_registration.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/0027_alter_registrar_iana_id.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/0028_rfc2317_fields.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/0029_record_fqdn.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/migrations/__init__.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/mixins/__init__.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/mixins/object_modification.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/models/__init__.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/models/contact.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/models/nameserver.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/models/record_template.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/models/registrar.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/models/view.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/models/zone_template.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/navigation.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/signals/__init__.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/tables/__init__.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/tables/contact.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/tables/nameserver.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/tables/record.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/tables/record_template.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/tables/registrar.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/tables/view.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/tables/zone.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/tables/zone_template.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/templates/netbox_dns/contact.html +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/templates/netbox_dns/nameserver.html +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/templates/netbox_dns/record/managed.html +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/templates/netbox_dns/record/related.html +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/templates/netbox_dns/record.html +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/templates/netbox_dns/recordtemplate.html +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/templates/netbox_dns/registrar.html +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/templates/netbox_dns/view/related.html +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/templates/netbox_dns/zone/base.html +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/templates/netbox_dns/zone/child.html +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/templates/netbox_dns/zone/child_zone.html +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/templates/netbox_dns/zone/managed_record.html +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/templates/netbox_dns/zone/record.html +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/templates/netbox_dns/zone/registration.html +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/templates/netbox_dns/zone/rfc2317_child_zone.html +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/templates/netbox_dns/zone.html +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/templates/netbox_dns/zonetemplate.html +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/urls/__init__.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/urls/contact.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/urls/nameserver.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/urls/record.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/urls/record_template.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/urls/registrar.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/urls/zone.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/urls/zone_template.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/utilities/__init__.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/utilities/conversions.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/validators/__init__.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/validators/dns_name.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/validators/dns_value.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/validators/rfc2317.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/views/__init__.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/views/contact.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/views/nameserver.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/views/record.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/views/record_template.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/views/registrar.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/views/zone.py +0 -0
- {netbox_plugin_dns-1.1.0b1 → netbox_plugin_dns-1.1b3}/netbox_dns/views/zone_template.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: netbox-plugin-dns
|
|
3
|
-
Version: 1.
|
|
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
|
|
@@ -8,7 +8,7 @@ Keywords: netbox,netbox-plugin,dns
|
|
|
8
8
|
Author: Peter Eckel
|
|
9
9
|
Author-email: pete@netbox-dns.org
|
|
10
10
|
Requires-Python: >=3.10,<4.0
|
|
11
|
-
Classifier: Development Status ::
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
12
|
Classifier: License :: OSI Approved :: MIT License
|
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -5,14 +5,14 @@ from ipam.choices import IPAddressStatusChoices
|
|
|
5
5
|
|
|
6
6
|
from netbox_dns.choices import RecordTypeChoices
|
|
7
7
|
|
|
8
|
-
__version__ = "1.
|
|
8
|
+
__version__ = "1.1b3"
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class DNSConfig(PluginConfig):
|
|
12
12
|
name = "netbox_dns"
|
|
13
13
|
verbose_name = "NetBox DNS"
|
|
14
14
|
description = "NetBox plugin for DNS data"
|
|
15
|
-
min_version = "4.
|
|
15
|
+
min_version = "4.0.0"
|
|
16
16
|
version = __version__
|
|
17
17
|
author = "Peter Eckel"
|
|
18
18
|
author_email = "pete@netbox-dns.org"
|
|
@@ -31,6 +31,7 @@ class DNSConfig(PluginConfig):
|
|
|
31
31
|
IPAddressStatusChoices.STATUS_DHCP,
|
|
32
32
|
IPAddressStatusChoices.STATUS_SLAAC,
|
|
33
33
|
],
|
|
34
|
+
"autodns_conflict_deactivate": False,
|
|
34
35
|
"tolerate_characters_in_zone_labels": "",
|
|
35
36
|
"tolerate_underscores_in_labels": False,
|
|
36
37
|
"tolerate_underscores_in_hostnames": False, # Deprecated, will be removed in 1.2.0
|
|
@@ -21,12 +21,14 @@ 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
|
|
27
28
|
from netbox_dns.utilities import (
|
|
28
|
-
|
|
29
|
+
check_dns_records,
|
|
29
30
|
update_dns_records,
|
|
31
|
+
get_ip_addresses_by_prefix,
|
|
30
32
|
get_views_by_prefix,
|
|
31
33
|
)
|
|
32
34
|
|
|
@@ -36,6 +38,7 @@ __all__ = (
|
|
|
36
38
|
"ViewFilterForm",
|
|
37
39
|
"ViewImportForm",
|
|
38
40
|
"ViewBulkEditForm",
|
|
41
|
+
"ViewPrefixEditForm",
|
|
39
42
|
)
|
|
40
43
|
|
|
41
44
|
|
|
@@ -52,7 +55,7 @@ class ViewPrefixUpdateMixin:
|
|
|
52
55
|
for prefix in prefixes.difference(old_prefixes):
|
|
53
56
|
for ip_address in get_ip_addresses_by_prefix(prefix, check_view=False):
|
|
54
57
|
try:
|
|
55
|
-
|
|
58
|
+
check_dns_records(ip_address, view=self.instance)
|
|
56
59
|
except ValidationError as exc:
|
|
57
60
|
self.add_error("prefixes", exc.messages)
|
|
58
61
|
|
|
@@ -74,7 +77,7 @@ class ViewPrefixUpdateMixin:
|
|
|
74
77
|
# parent. If that's the case, the IP addresses need to be checked.
|
|
75
78
|
# -
|
|
76
79
|
if (parent := check_prefix.get_parents().last()) is None:
|
|
77
|
-
|
|
80
|
+
continue
|
|
78
81
|
|
|
79
82
|
for view in get_views_by_prefix(parent):
|
|
80
83
|
if view == self.instance:
|
|
@@ -84,7 +87,7 @@ class ViewPrefixUpdateMixin:
|
|
|
84
87
|
check_prefix, check_view=False
|
|
85
88
|
):
|
|
86
89
|
try:
|
|
87
|
-
|
|
90
|
+
check_dns_records(ip_address, view=view)
|
|
88
91
|
except ValidationError as exc:
|
|
89
92
|
self.add_error("prefixes", exc.messages)
|
|
90
93
|
|
|
@@ -96,6 +99,21 @@ class ViewForm(ViewPrefixUpdateMixin, TenancyForm, NetBoxModelForm):
|
|
|
96
99
|
if settings.PLUGINS_CONFIG["netbox_dns"].get("autodns_disabled"):
|
|
97
100
|
del self.fields["prefixes"]
|
|
98
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
|
+
|
|
99
117
|
prefixes = PrefixDynamicModelMultipleChoiceField(
|
|
100
118
|
queryset=Prefix.objects.all(),
|
|
101
119
|
required=False,
|
|
@@ -200,3 +218,74 @@ class ViewBulkEditForm(NetBoxModelBulkEditForm):
|
|
|
200
218
|
)
|
|
201
219
|
|
|
202
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
|
|
@@ -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)
|
|
@@ -29,8 +29,8 @@ class Command(BaseCommand):
|
|
|
29
29
|
CustomField.objects.get(
|
|
30
30
|
name=cf, object_types=ipaddress_object_type
|
|
31
31
|
).delete()
|
|
32
|
-
if options.get("verbosity")
|
|
33
|
-
self.stdout.write(f"
|
|
32
|
+
if options.get("verbosity"):
|
|
33
|
+
self.stdout.write(f"Removed custom field '{cf}'")
|
|
34
34
|
except CustomField.DoesNotExist:
|
|
35
35
|
pass
|
|
36
36
|
return
|
|
@@ -38,7 +38,7 @@ class Command(BaseCommand):
|
|
|
38
38
|
# +
|
|
39
39
|
# Remove pre-existing IPAM Coupling custom fields
|
|
40
40
|
# -
|
|
41
|
-
if options.get("verbosity"):
|
|
41
|
+
if options.get("verbosity") >= 2:
|
|
42
42
|
self.stdout.write(f"Trying to remove obsolete IPAM Coupling custom fields")
|
|
43
43
|
for cf in (
|
|
44
44
|
"ipaddress_dns_record_name",
|
|
@@ -48,12 +48,12 @@ class Command(BaseCommand):
|
|
|
48
48
|
CustomField.objects.get(
|
|
49
49
|
name=cf, object_types=ipaddress_object_type
|
|
50
50
|
).delete()
|
|
51
|
-
if options.get("verbosity")
|
|
51
|
+
if options.get("verbosity"):
|
|
52
52
|
self.stdout.write(f"Removed custom field '{cf}'")
|
|
53
53
|
except CustomField.DoesNotExist:
|
|
54
54
|
pass
|
|
55
55
|
|
|
56
|
-
if options.get("verbosity"):
|
|
56
|
+
if options.get("verbosity") >= 2:
|
|
57
57
|
self.stdout.write(f"Creating IPAM AutoDNS custom fields")
|
|
58
58
|
|
|
59
59
|
if not CustomField.objects.filter(
|
|
@@ -61,7 +61,7 @@ class Command(BaseCommand):
|
|
|
61
61
|
type=CustomFieldTypeChoices.TYPE_BOOLEAN,
|
|
62
62
|
object_types=ipaddress_object_type,
|
|
63
63
|
).exists():
|
|
64
|
-
|
|
64
|
+
cf_autodns_disabled = CustomField.objects.create(
|
|
65
65
|
name="ipaddress_dns_disabled",
|
|
66
66
|
label="Disable AutoDNS",
|
|
67
67
|
description="Disable DNS address and pointer record generation for this address",
|
|
@@ -72,15 +72,23 @@ class Command(BaseCommand):
|
|
|
72
72
|
is_cloneable=True,
|
|
73
73
|
weight=100,
|
|
74
74
|
)
|
|
75
|
-
|
|
76
|
-
if options.get("verbosity")
|
|
75
|
+
cf_autodns_disabled.object_types.set([ipaddress_object_type])
|
|
76
|
+
if options.get("verbosity"):
|
|
77
77
|
self.stdout.write("Created custom field 'ipaddress_dns_disabled'")
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
try:
|
|
80
|
+
cf_ttl = CustomField.objects.get(
|
|
81
|
+
name="ipaddress_dns_record_ttl",
|
|
82
|
+
type=CustomFieldTypeChoices.TYPE_INTEGER,
|
|
83
|
+
object_types=ipaddress_object_type,
|
|
84
|
+
)
|
|
85
|
+
if cf_ttl.group_name != "AutoDNS":
|
|
86
|
+
cf_ttl.group_name = "AutoDNS"
|
|
87
|
+
cf_ttl.description = ("TTL for DNS records created for this address",)
|
|
88
|
+
cf_ttl.save()
|
|
89
|
+
if options.get("verbosity"):
|
|
90
|
+
self.stdout.write("Updated custom field 'ipaddress_dns_record_ttl'")
|
|
91
|
+
except CustomField.DoesNotExist:
|
|
84
92
|
cf_ttl = CustomField.objects.create(
|
|
85
93
|
name="ipaddress_dns_record_ttl",
|
|
86
94
|
description="TTL for DNS records created for this address",
|
|
@@ -94,14 +102,26 @@ class Command(BaseCommand):
|
|
|
94
102
|
weight=200,
|
|
95
103
|
)
|
|
96
104
|
cf_ttl.object_types.set([ipaddress_object_type])
|
|
97
|
-
if options.get("verbosity")
|
|
105
|
+
if options.get("verbosity"):
|
|
98
106
|
self.stdout.write("Created custom field 'ipaddress_dns_record_ttl'")
|
|
99
107
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
108
|
+
try:
|
|
109
|
+
cf_disable_ptr = CustomField.objects.get(
|
|
110
|
+
name="ipaddress_dns_record_disable_ptr",
|
|
111
|
+
type=CustomFieldTypeChoices.TYPE_BOOLEAN,
|
|
112
|
+
object_types=ipaddress_object_type,
|
|
113
|
+
)
|
|
114
|
+
if cf_disable_ptr.group_name != "AutoDNS":
|
|
115
|
+
cf_disable_ptr.group_name = "AutoDNS"
|
|
116
|
+
cf_disable_ptr.description = (
|
|
117
|
+
"Disable DNS PTR record generation for this address",
|
|
118
|
+
)
|
|
119
|
+
cf_disable_ptr.save()
|
|
120
|
+
if options.get("verbosity"):
|
|
121
|
+
self.stdout.write(
|
|
122
|
+
"Updated custom field 'ipaddress_dns_record_disable_ptr'"
|
|
123
|
+
)
|
|
124
|
+
except CustomField.DoesNotExist:
|
|
105
125
|
cf_disable_ptr = CustomField.objects.create(
|
|
106
126
|
name="ipaddress_dns_record_disable_ptr",
|
|
107
127
|
description="Disable DNS PTR record generation for this address",
|
|
@@ -114,7 +134,7 @@ class Command(BaseCommand):
|
|
|
114
134
|
weight=300,
|
|
115
135
|
)
|
|
116
136
|
cf_disable_ptr.object_types.set([ipaddress_object_type])
|
|
117
|
-
if options.get("verbosity")
|
|
137
|
+
if options.get("verbosity"):
|
|
118
138
|
self.stdout.write(
|
|
119
139
|
"Created custom field 'ipaddress_dns_record_disable_ptr'"
|
|
120
140
|
)
|
|
@@ -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
|
-
|
|
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(
|
|
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 {
|
|
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=
|
|
564
|
+
zone=new_zone,
|
|
556
565
|
name=self.name,
|
|
557
566
|
type=self.type,
|
|
558
567
|
value=self.value,
|
|
@@ -562,13 +571,41 @@ class Record(ObjectModificationMixin, NetBoxModel):
|
|
|
562
571
|
if self.pk is not None:
|
|
563
572
|
records = records.exclude(pk=self.pk)
|
|
564
573
|
|
|
565
|
-
if
|
|
574
|
+
if records.exists():
|
|
575
|
+
if self.ipam_ip_address is not None:
|
|
576
|
+
if not records.filter(
|
|
577
|
+
ipam_ip_address__isnull=True
|
|
578
|
+
).exists() or get_plugin_config(
|
|
579
|
+
"netbox_dns", "autodns_conflict_deactivate", False
|
|
580
|
+
):
|
|
581
|
+
return
|
|
582
|
+
|
|
566
583
|
raise ValidationError(
|
|
567
584
|
{
|
|
568
585
|
"value": f"There is already an active {self.type} record for name {self.name} in zone {self.zone} with value {self.value}."
|
|
569
586
|
}
|
|
570
587
|
) from None
|
|
571
588
|
|
|
589
|
+
def handle_conflicting_address_records(self):
|
|
590
|
+
if self.ipam_ip_address is None or not self.is_active:
|
|
591
|
+
return
|
|
592
|
+
|
|
593
|
+
if not get_plugin_config("netbox_dns", "autodns_conflict_deactivate", False):
|
|
594
|
+
return
|
|
595
|
+
|
|
596
|
+
records = Record.objects.filter(
|
|
597
|
+
zone=self.zone,
|
|
598
|
+
name=self.name,
|
|
599
|
+
type=self.type,
|
|
600
|
+
value=self.value,
|
|
601
|
+
status__in=Record.ACTIVE_STATUS_LIST,
|
|
602
|
+
ipam_ip_address__isnull=True,
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
for record in records:
|
|
606
|
+
record.status = RecordStatusChoices.STATUS_INACTIVE
|
|
607
|
+
record.save(update_fields=["status"])
|
|
608
|
+
|
|
572
609
|
def check_unique_rrset_ttl(self):
|
|
573
610
|
if self.pk is not None:
|
|
574
611
|
return
|
|
@@ -587,8 +624,12 @@ class Record(ObjectModificationMixin, NetBoxModel):
|
|
|
587
624
|
)
|
|
588
625
|
.exclude(ttl=self.ttl)
|
|
589
626
|
.exclude(type=RecordTypeChoices.PTR, managed=True)
|
|
627
|
+
.exclude(status=RecordStatusChoices.STATUS_INACTIVE)
|
|
590
628
|
)
|
|
591
629
|
|
|
630
|
+
if self.ipam_ip_address is not None:
|
|
631
|
+
records = records.exclude(ipam_ip_address__isnull=False)
|
|
632
|
+
|
|
592
633
|
if not records.exists():
|
|
593
634
|
return
|
|
594
635
|
|
|
@@ -621,6 +662,7 @@ class Record(ObjectModificationMixin, NetBoxModel):
|
|
|
621
662
|
.exclude(pk=self.pk)
|
|
622
663
|
.exclude(ttl=ttl)
|
|
623
664
|
.exclude(type=RecordTypeChoices.PTR, managed=True)
|
|
665
|
+
.exclude(status=RecordStatusChoices.STATUS_INACTIVE)
|
|
624
666
|
)
|
|
625
667
|
|
|
626
668
|
for record in records:
|
|
@@ -631,10 +673,10 @@ class Record(ObjectModificationMixin, NetBoxModel):
|
|
|
631
673
|
self.type = self.type.upper()
|
|
632
674
|
super().clean_fields(*args, **kwargs)
|
|
633
675
|
|
|
634
|
-
def clean(self, *args, **kwargs):
|
|
635
|
-
self.validate_name()
|
|
676
|
+
def clean(self, *args, new_zone=None, **kwargs):
|
|
677
|
+
self.validate_name(new_zone=new_zone)
|
|
636
678
|
self.validate_value()
|
|
637
|
-
self.check_unique_record()
|
|
679
|
+
self.check_unique_record(new_zone=new_zone)
|
|
638
680
|
if self.pk is None:
|
|
639
681
|
self.check_unique_rrset_ttl()
|
|
640
682
|
|
|
@@ -740,6 +782,7 @@ class Record(ObjectModificationMixin, NetBoxModel):
|
|
|
740
782
|
self.ip_address = None
|
|
741
783
|
|
|
742
784
|
if self.is_address_record:
|
|
785
|
+
self.handle_conflicting_address_records()
|
|
743
786
|
self.update_ptr_record(
|
|
744
787
|
update_rfc2317_cname=update_rfc2317_cname,
|
|
745
788
|
save_zone_serial=save_zone_serial,
|
|
@@ -28,6 +28,7 @@ from netbox_dns.choices import RecordClassChoices, RecordTypeChoices, ZoneStatus
|
|
|
28
28
|
from netbox_dns.fields import NetworkField, RFC2317NetworkField
|
|
29
29
|
from netbox_dns.utilities import (
|
|
30
30
|
update_dns_records,
|
|
31
|
+
check_dns_records,
|
|
31
32
|
get_ip_addresses_by_zone,
|
|
32
33
|
arpa_to_prefix,
|
|
33
34
|
name_to_unicode,
|
|
@@ -652,6 +653,24 @@ class Zone(ObjectModificationMixin, NetBoxModel):
|
|
|
652
653
|
}
|
|
653
654
|
)
|
|
654
655
|
|
|
656
|
+
if old_zone.name != self.name or old_zone.view != self.view:
|
|
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(
|
|
665
|
+
ipam_ip_address__isnull=False
|
|
666
|
+
)
|
|
667
|
+
)
|
|
668
|
+
for ip_address in ip_addresses:
|
|
669
|
+
try:
|
|
670
|
+
check_dns_records(ip_address, zone=self)
|
|
671
|
+
except ValidationError as exc:
|
|
672
|
+
raise ValidationError(exc.messages)
|
|
673
|
+
|
|
655
674
|
if self.is_reverse_zone:
|
|
656
675
|
self.arpa_network = self.network_from_name
|
|
657
676
|
|
|
@@ -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
|
|
@@ -11,6 +12,8 @@ from utilities.exceptions import AbortRequest
|
|
|
11
12
|
|
|
12
13
|
from netbox_dns.models import view as _view
|
|
13
14
|
from netbox_dns.utilities import (
|
|
15
|
+
check_dns_records,
|
|
16
|
+
check_record_permission,
|
|
14
17
|
update_dns_records,
|
|
15
18
|
delete_dns_records,
|
|
16
19
|
get_views_by_prefix,
|
|
@@ -18,22 +21,89 @@ from netbox_dns.utilities import (
|
|
|
18
21
|
get_ip_addresses_by_view,
|
|
19
22
|
)
|
|
20
23
|
|
|
24
|
+
AUTODNS_CUSTOM_FIELDS = {
|
|
25
|
+
"ipaddress_dns_disabled": False,
|
|
26
|
+
"ipaddress_dns_record_ttl": None,
|
|
27
|
+
"ipaddress_dns_record_disable_ptr": False,
|
|
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
|
+
|
|
21
35
|
|
|
22
36
|
@receiver(post_clean, sender=IPAddress)
|
|
23
37
|
def ipam_autodns_ipaddress_post_clean(instance, **kwargs):
|
|
24
38
|
if not isinstance(instance.address, IPNetwork):
|
|
25
39
|
return
|
|
26
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
|
+
|
|
71
|
+
# +
|
|
72
|
+
# Check NetBox DNS record permission for changes to IPAddress custom fields
|
|
73
|
+
#
|
|
74
|
+
# Normally, as the modfication of DNS fields
|
|
75
|
+
if (request := current_request.get()) is not None:
|
|
76
|
+
cf_data = instance.custom_field_data
|
|
77
|
+
if (
|
|
78
|
+
instance.pk is not None
|
|
79
|
+
and any(
|
|
80
|
+
(
|
|
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()
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
and not check_record_permission()
|
|
89
|
+
) or (
|
|
90
|
+
instance.pk is None
|
|
91
|
+
and any(
|
|
92
|
+
(
|
|
93
|
+
cf_data.get(cf, cf_default) != cf_default
|
|
94
|
+
for cf, cf_default in AUTODNS_CUSTOM_FIELDS.items()
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
and not check_record_permission(change=False, delete=False)
|
|
98
|
+
):
|
|
99
|
+
raise ValidationError(
|
|
100
|
+
f"User '{request.user}' is not allowed to alter AutoDNS custom fields"
|
|
101
|
+
)
|
|
102
|
+
|
|
27
103
|
try:
|
|
28
|
-
|
|
104
|
+
check_dns_records(instance)
|
|
29
105
|
except ValidationError as exc:
|
|
30
|
-
|
|
31
|
-
for field in ("name", "ttl", "value", "type"):
|
|
32
|
-
value = exc.error_dict.pop(field, None)
|
|
33
|
-
if value is not None:
|
|
34
|
-
raise ValidationError({"dns_name": value})
|
|
35
|
-
|
|
36
|
-
raise exc
|
|
106
|
+
raise ValidationError({"dns_name": exc.messages})
|
|
37
107
|
|
|
38
108
|
|
|
39
109
|
@receiver(pre_delete, sender=IPAddress)
|
|
@@ -42,8 +112,8 @@ def ipam_autodns_ipaddress_pre_delete(instance, **kwargs):
|
|
|
42
112
|
|
|
43
113
|
|
|
44
114
|
@receiver(pre_save, sender=IPAddress)
|
|
45
|
-
def
|
|
46
|
-
|
|
115
|
+
def ipam_autodns_ipaddress_pre_save(instance, **kwargs):
|
|
116
|
+
check_dns_records(instance)
|
|
47
117
|
|
|
48
118
|
|
|
49
119
|
@receiver(post_save, sender=IPAddress)
|
|
@@ -55,16 +125,29 @@ def ipam_autodns_ipaddress_post_save(instance, **kwargs):
|
|
|
55
125
|
def ipam_autodns_prefix_pre_save(instance, **kwargs):
|
|
56
126
|
"""
|
|
57
127
|
Changes that modify the prefix hierarchy cannot be validated properly before
|
|
58
|
-
commiting them. So the solution in this case is to
|
|
59
|
-
|
|
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.
|
|
60
130
|
"""
|
|
131
|
+
request = current_request.get()
|
|
132
|
+
|
|
61
133
|
if instance.pk is None or not instance.netbox_dns_views.exists():
|
|
62
134
|
return
|
|
63
135
|
|
|
64
|
-
saved_prefix = Prefix.objects.get(
|
|
136
|
+
saved_prefix = Prefix.objects.prefetch_related("netbox_dns_views").get(
|
|
137
|
+
pk=instance.pk
|
|
138
|
+
)
|
|
65
139
|
if saved_prefix.prefix != instance.prefix or saved_prefix.vrf != instance.vrf:
|
|
66
|
-
for view in
|
|
67
|
-
|
|
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
|
+
)
|
|
68
151
|
|
|
69
152
|
|
|
70
153
|
@receiver(pre_delete, sender=Prefix)
|
|
@@ -80,7 +163,7 @@ def ipam_autodns_prefix_pre_delete(instance, **kwargs):
|
|
|
80
163
|
_depth=instance.depth + 1, netbox_dns_views__isnull=True
|
|
81
164
|
):
|
|
82
165
|
for ip_address in get_ip_addresses_by_prefix(prefix):
|
|
83
|
-
|
|
166
|
+
check_dns_records(ip_address)
|
|
84
167
|
except ValidationError as exc:
|
|
85
168
|
if request is not None:
|
|
86
169
|
raise AbortRequest(
|