netbox-plugin-dns 0.22.5__tar.gz → 0.22.8__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.

Files changed (119) hide show
  1. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/PKG-INFO +1 -1
  2. netbox_plugin_dns-0.22.8/netbox_dns/__init__.py +51 -0
  3. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/api/serializers.py +1 -0
  4. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/filters/record.py +31 -5
  5. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/filters/zone.py +1 -1
  6. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/forms/record.py +11 -4
  7. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/forms/registrar.py +9 -0
  8. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/forms/zone.py +4 -3
  9. netbox_plugin_dns-0.22.8/netbox_dns/migrations/0029_record_fqdn.py +30 -0
  10. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/models/record.py +73 -59
  11. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/models/zone.py +9 -0
  12. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/templates/netbox_dns/record.html +6 -22
  13. netbox_plugin_dns-0.22.8/netbox_dns/views/record.py +159 -0
  14. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/pyproject.toml +1 -1
  15. netbox_plugin_dns-0.22.5/netbox_dns/__init__.py +0 -87
  16. netbox_plugin_dns-0.22.5/netbox_dns/views/record.py +0 -85
  17. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/LICENSE +0 -0
  18. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/README.md +0 -0
  19. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/api/nested_serializers.py +0 -0
  20. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/api/urls.py +0 -0
  21. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/api/views.py +0 -0
  22. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/apps.py +0 -0
  23. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/fields/__init__.py +0 -0
  24. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/fields/address.py +0 -0
  25. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/fields/network.py +0 -0
  26. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/fields/rfc2317.py +0 -0
  27. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/filters/__init__.py +0 -0
  28. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/filters/contact.py +0 -0
  29. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/filters/nameserver.py +0 -0
  30. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/filters/registrar.py +0 -0
  31. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/filters/view.py +0 -0
  32. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/forms/__init__.py +0 -0
  33. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/forms/contact.py +0 -0
  34. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/forms/nameserver.py +0 -0
  35. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/forms/view.py +0 -0
  36. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/graphql/__init__.py +0 -0
  37. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/graphql/contact.py +0 -0
  38. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/graphql/nameserver.py +0 -0
  39. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/graphql/record.py +0 -0
  40. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/graphql/registrar.py +0 -0
  41. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/graphql/schema.py +0 -0
  42. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/graphql/view.py +0 -0
  43. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/graphql/zone.py +0 -0
  44. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/management/commands/cleanup_database.py +0 -0
  45. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/management/commands/cleanup_rrset_ttl.py +0 -0
  46. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/management/commands/setup_coupling.py +0 -0
  47. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/management/commands/update_soa.py +0 -0
  48. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0001_initial.py +0 -0
  49. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0001_squashed_netbox_dns_0_15.py +0 -0
  50. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0002_zone_default_ttl.py +0 -0
  51. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0003_soa_managed_records.py +0 -0
  52. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0004_create_ptr_for_a_aaaa_records.py +0 -0
  53. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0005_update_ns_records.py +0 -0
  54. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0006_zone_soa_serial_auto.py +0 -0
  55. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0007_alter_zone_soa_serial_auto.py +0 -0
  56. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0008_zone_status_names.py +0 -0
  57. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0009_netbox32.py +0 -0
  58. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0010_update_soa_records.py +0 -0
  59. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0011_add_view_model.py +0 -0
  60. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0012_adjust_zone_and_record.py +0 -0
  61. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0013_add_nameserver_zone_record_description.py +0 -0
  62. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0014_add_view_description.py +0 -0
  63. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0015_add_record_status.py +0 -0
  64. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0016_cleanup_ptr_records.py +0 -0
  65. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0017_alter_record_ttl.py +0 -0
  66. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0018_zone_arpa_network.py +0 -0
  67. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0019_update_ns_ttl.py +0 -0
  68. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0020_netbox_3_4.py +0 -0
  69. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0021_record_ip_address.py +0 -0
  70. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0022_search.py +0 -0
  71. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0023_alter_record_value.py +0 -0
  72. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0024_tenancy.py +0 -0
  73. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0025_ipam_coupling_cf.py +0 -0
  74. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0026_domain_registration.py +0 -0
  75. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0027_alter_registrar_iana_id.py +0 -0
  76. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/0028_rfc2317_fields.py +0 -0
  77. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/migrations/__init__.py +0 -0
  78. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/models/__init__.py +0 -0
  79. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/models/contact.py +0 -0
  80. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/models/nameserver.py +0 -0
  81. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/models/registrar.py +0 -0
  82. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/models/view.py +0 -0
  83. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/navigation.py +0 -0
  84. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/signals/__init__.py +0 -0
  85. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/signals/ipam_coupling.py +0 -0
  86. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/tables/__init__.py +0 -0
  87. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/tables/contact.py +0 -0
  88. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/tables/nameserver.py +0 -0
  89. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/tables/record.py +4 -4
  90. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/tables/registrar.py +0 -0
  91. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/tables/view.py +0 -0
  92. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/tables/zone.py +0 -0
  93. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/template_content.py +0 -0
  94. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/templates/netbox_dns/contact.html +0 -0
  95. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/templates/netbox_dns/nameserver.html +0 -0
  96. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/templates/netbox_dns/record/managed.html +0 -0
  97. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/templates/netbox_dns/record/related.html +0 -0
  98. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/templates/netbox_dns/registrar.html +0 -0
  99. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/templates/netbox_dns/related_dns_objects.html +0 -0
  100. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/templates/netbox_dns/view.html +0 -0
  101. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/templates/netbox_dns/zone/base.html +0 -0
  102. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/templates/netbox_dns/zone/child.html +0 -0
  103. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/templates/netbox_dns/zone/managed_record.html +0 -0
  104. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/templates/netbox_dns/zone/record.html +0 -0
  105. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/templates/netbox_dns/zone/registration.html +0 -0
  106. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/templates/netbox_dns/zone/rfc2317_child_zone.html +0 -0
  107. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/templates/netbox_dns/zone.html +0 -0
  108. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/urls.py +0 -0
  109. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/utilities/__init__.py +0 -0
  110. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/utilities/ipam_coupling.py +0 -0
  111. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/validators/__init__.py +0 -0
  112. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/validators/dns_name.py +0 -0
  113. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/validators/rfc2317.py +0 -0
  114. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/views/__init__.py +0 -0
  115. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/views/contact.py +0 -0
  116. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/views/nameserver.py +0 -0
  117. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/views/registrar.py +0 -0
  118. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/views/view.py +0 -0
  119. {netbox_plugin_dns-0.22.5 → netbox_plugin_dns-0.22.8}/netbox_dns/views/zone.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: netbox-plugin-dns
3
- Version: 0.22.5
3
+ Version: 0.22.8
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
@@ -0,0 +1,51 @@
1
+ import sys
2
+
3
+ from extras.plugins import PluginConfig
4
+ from django.db.utils import OperationalError
5
+
6
+ try:
7
+ # NetBox 3.5.0 - 3.5.7, 3.5.9+
8
+ from extras.plugins import get_plugin_config
9
+ except ImportError:
10
+ # NetBox 3.5.8
11
+ from extras.plugins.utils import get_plugin_config
12
+
13
+ __version__ = "0.22.8"
14
+
15
+
16
+ class DNSConfig(PluginConfig):
17
+ name = "netbox_dns"
18
+ verbose_name = "NetBox DNS"
19
+ description = "NetBox plugin for DNS data"
20
+ min_version = "3.5.0"
21
+ max_version = "3.99.0"
22
+ version = __version__
23
+ author = "Peter Eckel"
24
+ author_email = "pete@netbox-dns.org"
25
+ required_settings = []
26
+ default_settings = {
27
+ "zone_default_ttl": 86400,
28
+ "zone_soa_serial_auto": True,
29
+ "zone_soa_serial": 1,
30
+ "zone_soa_refresh": 43200,
31
+ "zone_soa_retry": 7200,
32
+ "zone_soa_expire": 2419200,
33
+ "zone_soa_minimum": 3600,
34
+ "feature_ipam_coupling": False,
35
+ "tolerate_underscores_in_hostnames": False,
36
+ "tolerate_leading_underscore_types": [
37
+ "TXT",
38
+ "SRV",
39
+ ],
40
+ "tolerate_non_rfc1035_types": [],
41
+ "enable_root_zones": False,
42
+ "enforce_unique_records": True,
43
+ "enforce_unique_rrset_ttl": True,
44
+ }
45
+ base_url = "netbox-dns"
46
+
47
+
48
+ #
49
+ # Initialize plugin config
50
+ #
51
+ config = DNSConfig
@@ -241,6 +241,7 @@ class RecordSerializer(NetBoxModelSerializer):
241
241
  "display",
242
242
  "type",
243
243
  "name",
244
+ "fqdn",
244
245
  "value",
245
246
  "status",
246
247
  "ttl",
@@ -3,17 +3,22 @@ from django.db.models import Q
3
3
 
4
4
  from netbox.filtersets import NetBoxModelFilterSet
5
5
  from tenancy.filtersets import TenancyFilterSet
6
+ from utilities.filters import MultiValueCharFilter
6
7
 
7
- from netbox_dns.models import View, Zone, Record, RecordTypeChoices
8
+ from netbox_dns.models import View, Zone, Record, RecordTypeChoices, RecordStatusChoices
8
9
 
9
10
 
10
11
  class RecordFilter(TenancyFilterSet, NetBoxModelFilterSet):
11
- """Filter capabilities for Record instances."""
12
-
12
+ fqdn = MultiValueCharFilter(
13
+ method="filter_fqdn",
14
+ )
13
15
  type = django_filters.MultipleChoiceFilter(
14
16
  choices=RecordTypeChoices,
15
17
  null_value=None,
16
18
  )
19
+ status = django_filters.MultipleChoiceFilter(
20
+ choices=RecordStatusChoices,
21
+ )
17
22
  zone_id = django_filters.ModelMultipleChoiceFilter(
18
23
  queryset=Zone.objects.all(),
19
24
  label="Parent Zone ID",
@@ -39,10 +44,31 @@ class RecordFilter(TenancyFilterSet, NetBoxModelFilterSet):
39
44
 
40
45
  class Meta:
41
46
  model = Record
42
- fields = ("id", "type", "name", "value", "status", "zone", "managed", "tenant")
47
+ fields = (
48
+ "id",
49
+ "type",
50
+ "name",
51
+ "fqdn",
52
+ "value",
53
+ "status",
54
+ "zone",
55
+ "managed",
56
+ "tenant",
57
+ )
58
+
59
+ def filter_fqdn(self, queryset, name, value):
60
+ if not value:
61
+ return queryset
62
+
63
+ fqdns = []
64
+ for fqdn in value:
65
+ if not fqdn.endswith("."):
66
+ fqdn = fqdn + "."
67
+ fqdns.append(fqdn)
68
+
69
+ return queryset.filter(fqdn__in=fqdns)
43
70
 
44
71
  def search(self, queryset, name, value):
45
- """Perform the filtered search."""
46
72
  if not value.strip():
47
73
  return queryset
48
74
  qs_filter = (
@@ -8,7 +8,7 @@ from netbox_dns.models import View, Zone, ZoneStatusChoices, Registrar, Contact
8
8
 
9
9
 
10
10
  class ZoneFilter(TenancyFilterSet, NetBoxModelFilterSet):
11
- status = django_filters.ChoiceFilter(
11
+ status = django_filters.MultipleChoiceFilter(
12
12
  choices=ZoneStatusChoices,
13
13
  )
14
14
  view_id = django_filters.ModelMultipleChoiceFilter(
@@ -79,24 +79,31 @@ class RecordFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
79
79
  model = Record
80
80
  fieldsets = (
81
81
  (None, ("q", "filter_id", "tag")),
82
- ("Attributes", ("view_id", "zone_id", "name", "type", "value", "status")),
82
+ (
83
+ "Attributes",
84
+ ("view_id", "zone_id", "name", "fqdn", "type", "value", "status"),
85
+ ),
83
86
  ("Tenant", ("tenant_group_id", "tenant_id")),
84
87
  )
85
88
 
86
89
  type = forms.MultipleChoiceField(
87
- choices=add_blank_choice(RecordTypeChoices),
90
+ choices=RecordTypeChoices,
88
91
  required=False,
89
92
  )
90
93
  name = forms.CharField(
91
94
  required=False,
92
95
  label="Name",
93
96
  )
97
+ fqdn = forms.CharField(
98
+ required=False,
99
+ label="FQDN",
100
+ )
94
101
  value = forms.CharField(
95
102
  required=False,
96
103
  label="Value",
97
104
  )
98
- status = forms.ChoiceField(
99
- choices=add_blank_choice(RecordStatusChoices),
105
+ status = forms.MultipleChoiceField(
106
+ choices=RecordStatusChoices,
100
107
  required=False,
101
108
  )
102
109
  zone_id = DynamicModelMultipleChoiceField(
@@ -83,6 +83,13 @@ class RegistrarImportForm(NetBoxModelImportForm):
83
83
  class RegistrarBulkEditForm(NetBoxModelBulkEditForm):
84
84
  model = Registrar
85
85
 
86
+ iana_id = forms.IntegerField(
87
+ required=False,
88
+ label="IANA ID",
89
+ )
90
+ address = forms.CharField(
91
+ required=False,
92
+ )
86
93
  referral_url = forms.CharField(
87
94
  required=False,
88
95
  label="Referral URL",
@@ -104,6 +111,7 @@ class RegistrarBulkEditForm(NetBoxModelBulkEditForm):
104
111
  (
105
112
  None,
106
113
  (
114
+ "iana_id",
107
115
  "address",
108
116
  "referral_url",
109
117
  "whois_server",
@@ -114,6 +122,7 @@ class RegistrarBulkEditForm(NetBoxModelBulkEditForm):
114
122
  )
115
123
 
116
124
  nullable_fields = (
125
+ "iana_id",
117
126
  "address",
118
127
  "referral_url",
119
128
  "whois_server",
@@ -258,8 +258,8 @@ class ZoneFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
258
258
  required=False,
259
259
  label="View",
260
260
  )
261
- status = forms.ChoiceField(
262
- choices=add_blank_choice(ZoneStatusChoices),
261
+ status = forms.MultipleChoiceField(
262
+ choices=ZoneStatusChoices,
263
263
  required=False,
264
264
  )
265
265
  name = forms.CharField(
@@ -595,8 +595,9 @@ class ZoneBulkEditForm(NetBoxModelBulkEditForm):
595
595
  help_text="IPv4 network prefix with a mask length of at least 25 bits",
596
596
  validators=[validate_ipv4, validate_prefix, validate_rfc2317],
597
597
  )
598
- rfc2317_parent_managed = forms.BooleanField(
598
+ rfc2317_parent_managed = forms.NullBooleanField(
599
599
  required=False,
600
+ widget=BulkEditNullBooleanSelect(),
600
601
  label="RFC2317 Parent Managed",
601
602
  help_text="IPv4 reverse zone for deletgating the RFC2317 PTR records is managed in NetBox DNS",
602
603
  )
@@ -0,0 +1,30 @@
1
+ from dns import name as dns_name
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ def update_record_fqdn(apps, schema_editor):
7
+ Record = apps.get_model("netbox_dns", "Record")
8
+
9
+ for record in Record.objects.filter(fqdn__isnull=True):
10
+ zone = dns_name.from_text(record.zone.name, origin=dns_name.root)
11
+ fqdn = dns_name.from_text(record.name, origin=zone)
12
+
13
+ record.fqdn = fqdn.to_text()
14
+
15
+ record.save()
16
+
17
+
18
+ class Migration(migrations.Migration):
19
+ dependencies = [
20
+ ("netbox_dns", "0028_rfc2317_fields"),
21
+ ]
22
+
23
+ operations = [
24
+ migrations.AddField(
25
+ model_name="record",
26
+ name="fqdn",
27
+ field=models.CharField(default=None, max_length=255, null=True, blank=True),
28
+ ),
29
+ migrations.RunPython(update_record_fqdn),
30
+ ]
@@ -114,17 +114,23 @@ class Record(NetBoxModel):
114
114
  Q(Q(type=RecordTypeChoices.A) | Q(type=RecordTypeChoices.AAAA)),
115
115
  )
116
116
 
117
+ name = models.CharField(
118
+ max_length=255,
119
+ )
117
120
  zone = models.ForeignKey(
118
121
  "Zone",
119
122
  on_delete=models.CASCADE,
120
123
  )
124
+ fqdn = models.CharField(
125
+ max_length=255,
126
+ null=True,
127
+ blank=True,
128
+ default=None,
129
+ )
121
130
  type = models.CharField(
122
131
  choices=RecordTypeChoices,
123
132
  max_length=10,
124
133
  )
125
- name = models.CharField(
126
- max_length=255,
127
- )
128
134
  value = models.CharField(
129
135
  max_length=65535,
130
136
  )
@@ -209,14 +215,7 @@ class Record(NetBoxModel):
209
215
 
210
216
  def __str__(self):
211
217
  try:
212
- name = (
213
- dns_name.from_text(
214
- str(self.name),
215
- origin=dns_name.from_text(self.zone.name, origin=None),
216
- )
217
- .relativize(dns_name.root)
218
- .to_unicode()
219
- )
218
+ name = dns_name.from_text(self.fqdn).relativize(dns_name.root).to_unicode()
220
219
  except dns_name.IDNAException:
221
220
  name = self.name
222
221
  except dns_name.LabelTooLong as exc:
@@ -235,11 +234,14 @@ class Record(NetBoxModel):
235
234
  return reverse("plugins:netbox_dns:record", kwargs={"pk": self.id})
236
235
 
237
236
  @property
238
- def fqdn(self):
237
+ def value_fqdn(self):
238
+ if self.type != RecordTypeChoices.CNAME:
239
+ return None
240
+
239
241
  zone = dns_name.from_text(self.zone.name)
240
- name = dns_name.from_text(self.name, origin=zone)
242
+ value_fqdn = dns_name.from_text(self.value, origin=zone)
241
243
 
242
- return name.to_text()
244
+ return value_fqdn.to_text()
243
245
 
244
246
  @property
245
247
  def address_from_name(self):
@@ -383,6 +385,22 @@ class Record(NetBoxModel):
383
385
 
384
386
  self.ptr_record = ptr_record
385
387
 
388
+ def remove_from_rfc2317_cname_record(self, save_zone_serial=True):
389
+ if self.rfc2317_cname_record.pk:
390
+ rfc2317_ptr_records = self.rfc2317_cname_record.rfc2317_ptr_records.exclude(
391
+ pk=self.pk
392
+ )
393
+
394
+ if rfc2317_ptr_records:
395
+ self.rfc2317_cname_record.ttl = rfc2317_ptr_records.aggregate(
396
+ Min("ttl")
397
+ ).get("ttl__min")
398
+ self.rfc2317_cname_record.save(
399
+ update_fields=["ttl"], save_zone_serial=save_zone_serial
400
+ )
401
+ else:
402
+ self.rfc2317_cname_record.delete()
403
+
386
404
  def update_rfc2317_cname_record(self, save_zone_serial=True):
387
405
  if self.zone.rfc2317_parent_managed:
388
406
  cname_name = dns_name.from_text(
@@ -390,48 +408,56 @@ class Record(NetBoxModel):
390
408
  ).relativize(dns_name.from_text(self.zone.rfc2317_parent_zone.name))
391
409
 
392
410
  if self.rfc2317_cname_record is not None:
393
- self.rfc2317_cname_record.name = cname_name
394
- self.rfc2317_cname_record.zone = self.zone.rfc2317_parent_zone
395
- self.rfc2317_cname_record.value = self.fqdn
396
- self.rfc2317_cname_record.ttl = min_ttl(
397
- self.rfc2317_cname_record.rfc2317_ptr_records.exclude(pk=self.pk)
411
+ if self.rfc2317_cname_record.name == cname_name.to_text():
412
+ self.rfc2317_cname_record.zone = self.zone.rfc2317_parent_zone
413
+ self.rfc2317_cname_record.value = self.fqdn
414
+ self.rfc2317_cname_record.ttl = min_ttl(
415
+ self.rfc2317_cname_record.rfc2317_ptr_records.exclude(
416
+ pk=self.pk
417
+ )
418
+ .aggregate(Min("ttl"))
419
+ .get("ttl__min"),
420
+ self.ttl,
421
+ )
422
+ self.rfc2317_cname_record.save(save_zone_serial=save_zone_serial)
423
+
424
+ return
425
+ else:
426
+ self.remove_from_rfc2317_cname_record(
427
+ save_zone_serial=save_zone_serial
428
+ )
429
+
430
+ rfc2317_cname_record = Record.objects.filter(
431
+ name=cname_name,
432
+ type=RecordTypeChoices.CNAME,
433
+ zone=self.zone.rfc2317_parent_zone,
434
+ managed=True,
435
+ value=self.fqdn,
436
+ ).first()
437
+
438
+ if rfc2317_cname_record is not None:
439
+ rfc2317_cname_record.ttl = min_ttl(
440
+ rfc2317_cname_record.rfc2317_ptr_records.exclude(pk=self.pk)
398
441
  .aggregate(Min("ttl"))
399
442
  .get("ttl__min"),
400
443
  self.ttl,
401
444
  )
402
- self.rfc2317_cname_record.save(save_zone_serial=save_zone_serial)
445
+ rfc2317_cname_record.save(
446
+ update_fields=["ttl"], save_zone_serial=save_zone_serial
447
+ )
448
+
403
449
  else:
404
- rfc2317_cname_record = Record.objects.filter(
450
+ rfc2317_cname_record = Record(
405
451
  name=cname_name,
406
452
  type=RecordTypeChoices.CNAME,
407
453
  zone=self.zone.rfc2317_parent_zone,
408
454
  managed=True,
409
455
  value=self.fqdn,
410
- ).first()
411
-
412
- if rfc2317_cname_record is not None:
413
- rfc2317_cname_record.ttl = min_ttl(
414
- rfc2317_cname_record.rfc2317_ptr_records.exclude(pk=self.pk)
415
- .aggregate(Min("ttl"))
416
- .get("ttl__min"),
417
- self.ttl,
418
- )
419
- rfc2317_cname_record.save(
420
- update_fields=["ttl"], save_zone_serial=save_zone_serial
421
- )
422
-
423
- else:
424
- rfc2317_cname_record = Record(
425
- name=cname_name,
426
- type=RecordTypeChoices.CNAME,
427
- zone=self.zone.rfc2317_parent_zone,
428
- managed=True,
429
- value=self.fqdn,
430
- ttl=self.ttl,
431
- )
432
- rfc2317_cname_record.save(save_zone_serial=save_zone_serial)
456
+ ttl=self.ttl,
457
+ )
458
+ rfc2317_cname_record.save(save_zone_serial=save_zone_serial)
433
459
 
434
- self.rfc2317_cname_record = rfc2317_cname_record
460
+ self.rfc2317_cname_record = rfc2317_cname_record
435
461
 
436
462
  else:
437
463
  if self.rfc2317_cname_record is not None:
@@ -448,6 +474,7 @@ class Record(NetBoxModel):
448
474
  name.to_unicode()
449
475
 
450
476
  self.name = name.relativize(zone).to_text()
477
+ self.fqdn = fqdn.to_text()
451
478
 
452
479
  except dns.exception.DNSException as exc:
453
480
  raise ValidationError(
@@ -715,20 +742,7 @@ class Record(NetBoxModel):
715
742
 
716
743
  def delete(self, *args, save_zone_serial=True, **kwargs):
717
744
  if self.rfc2317_cname_record:
718
- if self.rfc2317_cname_record.pk:
719
- if self.rfc2317_cname_record.rfc2317_ptr_records.count() == 1:
720
- self.rfc2317_cname_record.delete()
721
- else:
722
- self.rfc2317_cname_record.ttl = (
723
- self.rfc2317_cname_record.rfc2317_ptr_records.exclude(
724
- pk=self.pk
725
- )
726
- .aggregate(Min("ttl"))
727
- .get("ttl__min")
728
- )
729
- self.rfc2317_cname_record.save(
730
- update_fields=["ttl"], save_zone_serial=save_zone_serial
731
- )
745
+ self.remove_from_rfc2317_cname_record(save_zone_serial=save_zone_serial)
732
746
 
733
747
  if self.ptr_record:
734
748
  self.ptr_record.delete()
@@ -738,6 +738,15 @@ class Zone(NetBoxModel):
738
738
  ip.dns_name = f'{ip.custom_field_data["ipaddress_dns_record_name"]}.{self.name}'
739
739
  ip.save(update_fields=["dns_name"])
740
740
 
741
+ if name_changed:
742
+ for _record in self.record_set.all():
743
+ _record.save(
744
+ update_fields=["fqdn"],
745
+ save_zone_serial=False,
746
+ update_rrset_ttl=False,
747
+ update_rfc2317_cname=False,
748
+ )
749
+
741
750
  self.save_soa_serial()
742
751
  self.update_soa_record()
743
752
 
@@ -112,12 +112,6 @@
112
112
  <td><a href="{% url 'ipam:ipaddress' pk=object.ipam_ip_address.pk %}">{{ object.ipam_ip_address }}</td>
113
113
  </tr>
114
114
  {% endif %}
115
- {% if object.rfc2317_cname_record %}
116
- <tr>
117
- <th scope="row">RFC2317 CNAME Record</th>
118
- <td><a href="{% url 'plugins:netbox_dns:record' pk=object.rfc2317_cname_record.pk %}">{{ object.rfc2317_cname_record }}</td>
119
- </tr>
120
- {% endif %}
121
115
  <tr>
122
116
  <th scope="row">Status</th>
123
117
  <td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
@@ -131,23 +125,13 @@
131
125
  </table>
132
126
  </div>
133
127
  </div>
134
- {% if object.rfc2317_ptr_records.all|length %}
128
+ {% if cname_target_table and cname_target_table.rows|length > 0 %}
135
129
  <div class="card">
136
- <h5 class="card-header">RFC2317 Targets</h5>
137
- <div class="card-body">
138
- <table class="table table-hover attr-table">
139
- <tr>
140
- <th>Address Record</th>
141
- <th>PTR Record</th>
142
- </tr>
143
- {% for record in object.rfc2317_ptr_records.all %}
144
- <tr>
145
- <td><a href="{% url 'plugins:netbox_dns:record' pk=record.address_record.pk %}">{{ record.address_record }}</td>
146
- <td><a href="{% url 'plugins:netbox_dns:record' pk=record.pk %}">{{ record }}</td>
147
- </td>
148
- {% endfor %}
149
- </table>
150
- </div>
130
+ {% include 'inc/panel_table.html' with table=cname_target_table heading='CNAME Targets' %}
131
+ </div>
132
+ {% elif cname_table and cname_table.rows|length > 0 %}
133
+ <div class="card">
134
+ {% include 'inc/panel_table.html' with table=cname_table heading='CNAMEs' %}
151
135
  </div>
152
136
  {% endif %}
153
137
  {% if not object.managed %}
@@ -0,0 +1,159 @@
1
+ from dns import name as dns_name
2
+
3
+ from django.db.models import Q
4
+ from django.db.models.functions import Length
5
+
6
+ from netbox.views import generic
7
+
8
+ from netbox_dns.filters import RecordFilter
9
+ from netbox_dns.forms import (
10
+ RecordImportForm,
11
+ RecordFilterForm,
12
+ RecordForm,
13
+ RecordBulkEditForm,
14
+ )
15
+ from netbox_dns.models import Record, RecordTypeChoices, Zone
16
+ from netbox_dns.tables import RecordTable, ManagedRecordTable, RelatedRecordTable
17
+ from netbox_dns.utilities import value_to_unicode
18
+
19
+
20
+ class RecordListView(generic.ObjectListView):
21
+ queryset = Record.objects.filter(managed=False).prefetch_related(
22
+ "zone", "ptr_record"
23
+ )
24
+ filterset = RecordFilter
25
+ filterset_form = RecordFilterForm
26
+ table = RecordTable
27
+
28
+
29
+ class ManagedRecordListView(generic.ObjectListView):
30
+ queryset = Record.objects.filter(managed=True).prefetch_related(
31
+ "zone", "address_record"
32
+ )
33
+ filterset = RecordFilter
34
+ filterset_form = RecordFilterForm
35
+ table = ManagedRecordTable
36
+ actions = ("export",)
37
+ template_name = "netbox_dns/record/managed.html"
38
+
39
+
40
+ class RecordView(generic.ObjectView):
41
+ queryset = Record.objects.all().prefetch_related("zone", "ptr_record")
42
+
43
+ def get_value_records(self, instance):
44
+ value_fqdn = dns_name.from_text(instance.value_fqdn)
45
+ value_zone_names = [
46
+ value_fqdn.split(length)[1].to_text().rstrip(".")
47
+ for length in range(2, len(value_fqdn) + 1)
48
+ ]
49
+
50
+ value_zone = (
51
+ Zone.objects.filter(instance.zone.view_filter, name__in=value_zone_names)
52
+ .order_by(Length("name").desc())
53
+ .first()
54
+ )
55
+ if not value_zone:
56
+ return None
57
+
58
+ value_name = value_fqdn.relativize(dns_name.from_text(value_zone.name))
59
+ cname_targets = Record.objects.filter(zone=value_zone, name=value_name)
60
+
61
+ if cname_targets:
62
+ return RelatedRecordTable(
63
+ data=cname_targets,
64
+ )
65
+
66
+ return None
67
+
68
+ def get_cname_records(self, instance):
69
+ view_filter = (
70
+ Q(zone__view__isnull=True)
71
+ if instance.zone.view is None
72
+ else Q(zone__view=instance.zone.view)
73
+ )
74
+ cname_records = set(
75
+ Record.objects.filter(
76
+ view_filter, value=instance.fqdn, type=RecordTypeChoices.CNAME
77
+ )
78
+ )
79
+
80
+ fqdn = dns_name.from_text(instance.fqdn)
81
+ parent_zone_names = [
82
+ fqdn.split(length)[1].to_text().rstrip(".")
83
+ for length in range(1, len(fqdn) + 1)
84
+ ]
85
+
86
+ parent_zones = Zone.objects.filter(
87
+ instance.zone.view_filter, name__in=parent_zone_names
88
+ )
89
+
90
+ for parent_zone in parent_zones:
91
+ parent_cname_records = Record.objects.filter(
92
+ view_filter, type=RecordTypeChoices.CNAME, zone=parent_zone
93
+ )
94
+ cname_records = cname_records.union(
95
+ set(
96
+ record
97
+ for record in parent_cname_records
98
+ if record.value_fqdn == instance.fqdn
99
+ )
100
+ )
101
+
102
+ if cname_records:
103
+ return RelatedRecordTable(
104
+ data=cname_records,
105
+ )
106
+
107
+ return None
108
+
109
+ def get_extra_context(self, request, instance):
110
+ context = {}
111
+
112
+ name = dns_name.from_text(instance.name, origin=None)
113
+ if name.to_text() != name.to_unicode():
114
+ context["unicode_name"] = name.to_unicode()
115
+
116
+ unicode_value = value_to_unicode(instance.value)
117
+ if instance.value != unicode_value:
118
+ context["unicode_value"] = unicode_value
119
+
120
+ if instance.type == RecordTypeChoices.CNAME:
121
+ context["cname_target_table"] = self.get_value_records(instance)
122
+ else:
123
+ context["cname_table"] = self.get_cname_records(instance)
124
+
125
+ return context
126
+
127
+
128
+ class RecordEditView(generic.ObjectEditView):
129
+ queryset = Record.objects.filter(managed=False).prefetch_related(
130
+ "zone", "ptr_record"
131
+ )
132
+ form = RecordForm
133
+ default_return_url = "plugins:netbox_dns:record_list"
134
+
135
+
136
+ class RecordDeleteView(generic.ObjectDeleteView):
137
+ queryset = Record.objects.filter(managed=False)
138
+ default_return_url = "plugins:netbox_dns:record_list"
139
+
140
+
141
+ class RecordBulkImportView(generic.BulkImportView):
142
+ queryset = Record.objects.filter(managed=False).prefetch_related(
143
+ "zone", "ptr_record"
144
+ )
145
+ model_form = RecordImportForm
146
+ table = RecordTable
147
+ default_return_url = "plugins:netbox_dns:record_list"
148
+
149
+
150
+ class RecordBulkEditView(generic.BulkEditView):
151
+ queryset = Record.objects.filter(managed=False).prefetch_related("zone")
152
+ filterset = RecordFilter
153
+ table = RecordTable
154
+ form = RecordBulkEditForm
155
+
156
+
157
+ class RecordBulkDeleteView(generic.BulkDeleteView):
158
+ queryset = Record.objects.filter(managed=False)
159
+ table = RecordTable