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.
Files changed (232) hide show
  1. netbox_dns/__init__.py +106 -41
  2. netbox_dns/api/field_serializers.py +25 -0
  3. netbox_dns/api/nested_serializers.py +95 -52
  4. netbox_dns/api/serializers.py +14 -296
  5. netbox_dns/api/serializers_/__init__.py +0 -0
  6. netbox_dns/api/serializers_/dnssec_key_template.py +69 -0
  7. netbox_dns/api/serializers_/dnssec_policy.py +165 -0
  8. netbox_dns/api/serializers_/nameserver.py +56 -0
  9. netbox_dns/api/serializers_/prefix.py +18 -0
  10. netbox_dns/api/serializers_/record.py +105 -0
  11. netbox_dns/api/serializers_/record_template.py +71 -0
  12. netbox_dns/api/serializers_/registrar.py +45 -0
  13. netbox_dns/api/serializers_/registration_contact.py +50 -0
  14. netbox_dns/api/serializers_/view.py +81 -0
  15. netbox_dns/api/serializers_/zone.py +247 -0
  16. netbox_dns/api/serializers_/zone_template.py +157 -0
  17. netbox_dns/api/urls.py +13 -2
  18. netbox_dns/api/views.py +96 -58
  19. netbox_dns/choices/__init__.py +4 -0
  20. netbox_dns/choices/dnssec_key_template.py +67 -0
  21. netbox_dns/choices/dnssec_policy.py +40 -0
  22. netbox_dns/choices/record.py +104 -0
  23. netbox_dns/choices/utilities.py +4 -0
  24. netbox_dns/choices/zone.py +119 -0
  25. netbox_dns/fields/__init__.py +4 -0
  26. netbox_dns/fields/address.py +22 -16
  27. netbox_dns/fields/choice_array.py +33 -0
  28. netbox_dns/fields/ipam.py +15 -0
  29. netbox_dns/fields/network.py +42 -18
  30. netbox_dns/fields/rfc2317.py +97 -0
  31. netbox_dns/fields/timeperiod.py +33 -0
  32. netbox_dns/filters.py +7 -0
  33. netbox_dns/filtersets/__init__.py +12 -0
  34. netbox_dns/filtersets/dnssec_key_template.py +57 -0
  35. netbox_dns/filtersets/dnssec_policy.py +101 -0
  36. netbox_dns/filtersets/nameserver.py +46 -0
  37. netbox_dns/filtersets/record.py +135 -0
  38. netbox_dns/filtersets/record_template.py +59 -0
  39. netbox_dns/{filters → filtersets}/registrar.py +8 -1
  40. netbox_dns/{filters/contact.py → filtersets/registration_contact.py} +9 -3
  41. netbox_dns/filtersets/view.py +45 -0
  42. netbox_dns/filtersets/zone.py +254 -0
  43. netbox_dns/filtersets/zone_template.py +165 -0
  44. netbox_dns/forms/__init__.py +5 -1
  45. netbox_dns/forms/dnssec_key_template.py +250 -0
  46. netbox_dns/forms/dnssec_policy.py +654 -0
  47. netbox_dns/forms/nameserver.py +121 -27
  48. netbox_dns/forms/record.py +215 -104
  49. netbox_dns/forms/record_template.py +285 -0
  50. netbox_dns/forms/registrar.py +108 -31
  51. netbox_dns/forms/registration_contact.py +282 -0
  52. netbox_dns/forms/view.py +331 -20
  53. netbox_dns/forms/zone.py +769 -373
  54. netbox_dns/forms/zone_template.py +463 -0
  55. netbox_dns/graphql/__init__.py +25 -22
  56. netbox_dns/graphql/enums.py +41 -0
  57. netbox_dns/graphql/filter_lookups.py +13 -0
  58. netbox_dns/graphql/filters/__init__.py +12 -0
  59. netbox_dns/graphql/filters/dnssec_key_template.py +63 -0
  60. netbox_dns/graphql/filters/dnssec_policy.py +124 -0
  61. netbox_dns/graphql/filters/nameserver.py +32 -0
  62. netbox_dns/graphql/filters/record.py +89 -0
  63. netbox_dns/graphql/filters/record_template.py +55 -0
  64. netbox_dns/graphql/filters/registrar.py +30 -0
  65. netbox_dns/graphql/filters/registration_contact.py +27 -0
  66. netbox_dns/graphql/filters/view.py +28 -0
  67. netbox_dns/graphql/filters/zone.py +147 -0
  68. netbox_dns/graphql/filters/zone_template.py +97 -0
  69. netbox_dns/graphql/schema.py +89 -7
  70. netbox_dns/graphql/types.py +355 -0
  71. netbox_dns/locale/de/LC_MESSAGES/django.mo +0 -0
  72. netbox_dns/locale/en/LC_MESSAGES/django.mo +0 -0
  73. netbox_dns/locale/fr/LC_MESSAGES/django.mo +0 -0
  74. netbox_dns/management/commands/cleanup_database.py +175 -156
  75. netbox_dns/management/commands/cleanup_rrset_ttl.py +64 -0
  76. netbox_dns/management/commands/rebuild_dnssync.py +23 -0
  77. netbox_dns/management/commands/setup_dnssync.py +140 -0
  78. netbox_dns/migrations/0001_squashed_netbox_dns_0_15.py +0 -27
  79. netbox_dns/migrations/0001_squashed_netbox_dns_0_22.py +557 -0
  80. netbox_dns/migrations/{0013_add_nameserver_zone_record_description.py → 0002_contact_description_registrar_description.py} +4 -9
  81. netbox_dns/migrations/0003_default_view.py +15 -0
  82. netbox_dns/migrations/0004_create_and_assign_default_view.py +26 -0
  83. netbox_dns/migrations/0005_alter_zone_view_not_null.py +18 -0
  84. netbox_dns/migrations/0006_templating.py +172 -0
  85. netbox_dns/migrations/0007_alter_ordering_options.py +25 -0
  86. netbox_dns/migrations/0008_view_prefixes.py +18 -0
  87. netbox_dns/migrations/0009_rename_contact_registrationcontact.py +36 -0
  88. netbox_dns/migrations/0010_view_ip_address_filter.py +18 -0
  89. netbox_dns/migrations/0011_rename_related_fields.py +63 -0
  90. netbox_dns/migrations/0012_natural_ordering.py +88 -0
  91. netbox_dns/migrations/0013_zonetemplate_soa_mname_zonetemplate_soa_rname.py +30 -0
  92. netbox_dns/migrations/0014_alter_unique_constraints_lowercase.py +42 -0
  93. netbox_dns/migrations/0015_dnssec.py +168 -0
  94. netbox_dns/migrations/{0015_add_record_status.py → 0016_dnssec_policy_status.py} +5 -4
  95. netbox_dns/migrations/0017_dnssec_policy_zone_zone_template.py +41 -0
  96. netbox_dns/migrations/0018_zone_domain_status_zone_expiration_date.py +23 -0
  97. netbox_dns/migrations/0019_dnssecpolicy_parental_agents.py +25 -0
  98. netbox_dns/migrations/0020_netbox_3_4.py +1 -1
  99. netbox_dns/migrations/0020_remove_dnssecpolicy_parental_agents_and_more.py +29 -0
  100. netbox_dns/migrations/0021_alter_record_ptr_record.py +25 -0
  101. netbox_dns/migrations/0021_record_ip_address.py +1 -1
  102. netbox_dns/migrations/0022_alter_record_ipam_ip_address.py +26 -0
  103. netbox_dns/migrations/0023_disable_ptr_false.py +27 -0
  104. netbox_dns/migrations/0024_zonetemplate_parental_agents.py +25 -0
  105. netbox_dns/migrations/0025_remove_zone_inline_signing_and_more.py +22 -0
  106. netbox_dns/migrations/0026_alter_dnssecpolicy_nsec3_opt_out.py +18 -0
  107. netbox_dns/migrations/0026_domain_registration.py +1 -1
  108. netbox_dns/migrations/0027_zone_comments.py +18 -0
  109. netbox_dns/migrations/0028_alter_zone_default_ttl_alter_zone_soa_minimum_and_more.py +54 -0
  110. netbox_dns/migrations/0028_rfc2317_fields.py +44 -0
  111. netbox_dns/migrations/0029_alter_registrationcontact_street.py +18 -0
  112. netbox_dns/migrations/0029_record_fqdn.py +30 -0
  113. netbox_dns/mixins/__init__.py +1 -0
  114. netbox_dns/mixins/object_modification.py +57 -0
  115. netbox_dns/models/__init__.py +5 -1
  116. netbox_dns/models/dnssec_key_template.py +114 -0
  117. netbox_dns/models/dnssec_policy.py +203 -0
  118. netbox_dns/models/nameserver.py +61 -30
  119. netbox_dns/models/record.py +781 -234
  120. netbox_dns/models/record_template.py +198 -0
  121. netbox_dns/models/registrar.py +34 -15
  122. netbox_dns/models/{contact.py → registration_contact.py} +72 -43
  123. netbox_dns/models/view.py +129 -9
  124. netbox_dns/models/zone.py +806 -242
  125. netbox_dns/models/zone_template.py +209 -0
  126. netbox_dns/navigation.py +176 -76
  127. netbox_dns/signals/__init__.py +0 -0
  128. netbox_dns/signals/dnssec.py +32 -0
  129. netbox_dns/signals/ipam_dnssync.py +216 -0
  130. netbox_dns/tables/__init__.py +5 -1
  131. netbox_dns/tables/dnssec_key_template.py +49 -0
  132. netbox_dns/tables/dnssec_policy.py +140 -0
  133. netbox_dns/tables/ipam_dnssync.py +12 -0
  134. netbox_dns/tables/nameserver.py +14 -17
  135. netbox_dns/tables/record.py +117 -59
  136. netbox_dns/tables/record_template.py +91 -0
  137. netbox_dns/tables/registrar.py +20 -10
  138. netbox_dns/tables/{contact.py → registration_contact.py} +22 -11
  139. netbox_dns/tables/view.py +47 -3
  140. netbox_dns/tables/zone.py +62 -31
  141. netbox_dns/tables/zone_template.py +78 -0
  142. netbox_dns/template_content.py +124 -38
  143. netbox_dns/templates/netbox_dns/dnsseckeytemplate.html +70 -0
  144. netbox_dns/templates/netbox_dns/dnssecpolicy.html +163 -0
  145. netbox_dns/templates/netbox_dns/nameserver.html +31 -28
  146. netbox_dns/templates/netbox_dns/record/managed.html +2 -1
  147. netbox_dns/templates/netbox_dns/record/related.html +17 -6
  148. netbox_dns/templates/netbox_dns/record.html +140 -93
  149. netbox_dns/templates/netbox_dns/recordtemplate.html +96 -0
  150. netbox_dns/templates/netbox_dns/registrar.html +41 -34
  151. netbox_dns/templates/netbox_dns/registrationcontact.html +76 -0
  152. netbox_dns/templates/netbox_dns/view/button.html +10 -0
  153. netbox_dns/templates/netbox_dns/view/prefix.html +44 -0
  154. netbox_dns/templates/netbox_dns/view/related.html +33 -0
  155. netbox_dns/templates/netbox_dns/view.html +62 -18
  156. netbox_dns/templates/netbox_dns/zone/base.html +6 -3
  157. netbox_dns/templates/netbox_dns/zone/child.html +6 -5
  158. netbox_dns/templates/netbox_dns/zone/child_zone.html +18 -0
  159. netbox_dns/templates/netbox_dns/zone/delegation_record.html +18 -0
  160. netbox_dns/templates/netbox_dns/zone/managed_record.html +1 -1
  161. netbox_dns/templates/netbox_dns/zone/record.html +6 -5
  162. netbox_dns/templates/netbox_dns/zone/registration.html +43 -24
  163. netbox_dns/templates/netbox_dns/zone/rfc2317_child_zone.html +18 -0
  164. netbox_dns/templates/netbox_dns/zone.html +178 -119
  165. netbox_dns/templates/netbox_dns/zonetemplate/child.html +46 -0
  166. netbox_dns/templates/netbox_dns/zonetemplate.html +124 -0
  167. netbox_dns/templatetags/netbox_dns.py +10 -0
  168. netbox_dns/urls.py +50 -210
  169. netbox_dns/utilities/__init__.py +3 -0
  170. netbox_dns/{utilities.py → utilities/conversions.py} +55 -7
  171. netbox_dns/utilities/dns.py +11 -0
  172. netbox_dns/utilities/ipam_dnssync.py +370 -0
  173. netbox_dns/validators/__init__.py +4 -0
  174. netbox_dns/validators/dns_name.py +116 -0
  175. netbox_dns/validators/dns_value.py +147 -0
  176. netbox_dns/validators/dnssec.py +148 -0
  177. netbox_dns/validators/rfc2317.py +28 -0
  178. netbox_dns/views/__init__.py +5 -1
  179. netbox_dns/views/dnssec_key_template.py +78 -0
  180. netbox_dns/views/dnssec_policy.py +146 -0
  181. netbox_dns/views/nameserver.py +34 -15
  182. netbox_dns/views/record.py +156 -15
  183. netbox_dns/views/record_template.py +93 -0
  184. netbox_dns/views/registrar.py +32 -13
  185. netbox_dns/views/registration_contact.py +101 -0
  186. netbox_dns/views/view.py +58 -14
  187. netbox_dns/views/zone.py +130 -33
  188. netbox_dns/views/zone_template.py +82 -0
  189. netbox_plugin_dns-1.4.7.dist-info/METADATA +132 -0
  190. netbox_plugin_dns-1.4.7.dist-info/RECORD +201 -0
  191. {netbox_plugin_dns-0.21.4.dist-info → netbox_plugin_dns-1.4.7.dist-info}/WHEEL +2 -1
  192. {netbox_plugin_dns-0.21.4.dist-info → netbox_plugin_dns-1.4.7.dist-info/licenses}/LICENSE +2 -1
  193. netbox_plugin_dns-1.4.7.dist-info/top_level.txt +1 -0
  194. netbox_dns/filters/__init__.py +0 -6
  195. netbox_dns/filters/nameserver.py +0 -18
  196. netbox_dns/filters/record.py +0 -53
  197. netbox_dns/filters/view.py +0 -18
  198. netbox_dns/filters/zone.py +0 -112
  199. netbox_dns/forms/contact.py +0 -211
  200. netbox_dns/graphql/contact.py +0 -19
  201. netbox_dns/graphql/nameserver.py +0 -19
  202. netbox_dns/graphql/record.py +0 -19
  203. netbox_dns/graphql/registrar.py +0 -19
  204. netbox_dns/graphql/view.py +0 -19
  205. netbox_dns/graphql/zone.py +0 -19
  206. netbox_dns/management/commands/setup_coupling.py +0 -75
  207. netbox_dns/management/commands/update_soa.py +0 -22
  208. netbox_dns/middleware.py +0 -226
  209. netbox_dns/migrations/0001_initial.py +0 -115
  210. netbox_dns/migrations/0002_zone_default_ttl.py +0 -18
  211. netbox_dns/migrations/0003_soa_managed_records.py +0 -112
  212. netbox_dns/migrations/0004_create_ptr_for_a_aaaa_records.py +0 -80
  213. netbox_dns/migrations/0005_update_ns_records.py +0 -41
  214. netbox_dns/migrations/0006_zone_soa_serial_auto.py +0 -29
  215. netbox_dns/migrations/0007_alter_zone_soa_serial_auto.py +0 -17
  216. netbox_dns/migrations/0008_zone_status_names.py +0 -21
  217. netbox_dns/migrations/0009_netbox32.py +0 -71
  218. netbox_dns/migrations/0010_update_soa_records.py +0 -58
  219. netbox_dns/migrations/0011_add_view_model.py +0 -70
  220. netbox_dns/migrations/0012_adjust_zone_and_record.py +0 -17
  221. netbox_dns/migrations/0014_add_view_description.py +0 -16
  222. netbox_dns/migrations/0016_cleanup_ptr_records.py +0 -38
  223. netbox_dns/migrations/0017_alter_record_ttl.py +0 -17
  224. netbox_dns/migrations/0018_zone_arpa_network.py +0 -51
  225. netbox_dns/migrations/0019_update_ns_ttl.py +0 -19
  226. netbox_dns/templates/netbox_dns/contact.html +0 -71
  227. netbox_dns/templates/netbox_dns/related_dns_objects.html +0 -21
  228. netbox_dns/templatetags/view_helpers.py +0 -15
  229. netbox_dns/validators.py +0 -57
  230. netbox_dns/views/contact.py +0 -83
  231. netbox_plugin_dns-0.21.4.dist-info/METADATA +0 -101
  232. netbox_plugin_dns-0.21.4.dist-info/RECORD +0 -110
@@ -1,186 +1,205 @@
1
- import dns
2
- from dns import rdtypes, rdata, rdatatype, rdataclass
3
-
4
- from netaddr import IPAddress, IPNetwork, AddrFormatError
1
+ from netaddr import IPAddress
5
2
 
6
3
  from django.core.management.base import BaseCommand
7
4
 
8
- from netbox_dns.models import (
9
- Zone,
10
- ZoneStatusChoices,
11
- Record,
12
- RecordTypeChoices,
13
- RecordClassChoices,
14
- )
15
-
16
-
17
- def zone_rename_passive_status_to_parked(verbose=False):
18
- passive_zones = Zone.objects.filter(status="passive")
19
- if passive_zones:
20
- if verbose:
21
- print("Renaming 'passive' zone status to 'parked'")
22
-
23
- for zone in passive_zones:
24
- zone.status = ZoneStatusChoices.STATUS_PARKED
25
- zone.save()
26
-
5
+ from netbox_dns.models import Zone, Record
6
+ from netbox_dns.choices import RecordTypeChoices
27
7
 
28
- def zone_cleanup_ns_records(verbose=False):
29
- ns_name = "@"
30
8
 
31
- for zone in Zone.objects.all():
32
- nameservers = zone.nameservers.all()
33
- nameserver_names = [f'{ns.name.rstrip(".")}.' for ns in nameservers]
34
-
35
- delete_ns = zone.record_set.filter(
36
- name=ns_name, type=RecordTypeChoices.NS
37
- ).exclude(value__in=nameserver_names)
38
- for record in delete_ns:
39
- if verbose:
40
- print(f"Deleting obsolete NS record {record}")
41
- record.delete()
42
-
43
- for ns in nameserver_names:
44
- ns_records = zone.record_set.filter(
45
- name=ns_name,
46
- type=RecordTypeChoices.NS,
47
- value=ns,
48
- )
9
+ class Command(BaseCommand):
10
+ help = "Clean up NetBox DNS database"
49
11
 
50
- delete_ns = ns_records[1:]
12
+ def handle(self, *model_names, **options):
13
+ self._zone_cleanup_ns_records(**options)
14
+ self._zone_cleanup_soa_records(**options)
15
+ self._zone_update_arpa_network(**options)
16
+ self._record_cleanup_disable_ptr(**options)
17
+ self._record_update_ptr_records(**options)
18
+ self._record_update_ip_address(**options)
19
+ self._record_remove_orphaned_ptr_records(**options)
20
+ self._record_remove_orphaned_address_records(**options)
21
+
22
+ if options.get("verbosity"):
23
+ self.stdout.write("Database cleanup completed.")
24
+
25
+ def _zone_cleanup_ns_records(self, **options):
26
+ if options.get("verbosity"):
27
+ self.stdout.write("Cleaning up the NS records for all zones")
28
+
29
+ ns_name = "@"
30
+
31
+ for zone in Zone.objects.all():
32
+ nameservers = zone.nameservers.all()
33
+ nameserver_names = [f'{ns.name.rstrip(".")}.' for ns in nameservers]
34
+
35
+ delete_ns = zone.records.filter(
36
+ name=ns_name, type=RecordTypeChoices.NS
37
+ ).exclude(value__in=nameserver_names)
51
38
  for record in delete_ns:
52
- if verbose:
53
- print(f"Deleting duplicate NS record {record}")
39
+ if options.get("verbosity") > 1:
40
+ self.stdout.write(
41
+ f"Removing obsolete NS record '{record}' with value '{record.value}'"
42
+ )
54
43
  record.delete()
55
44
 
56
- try:
57
- ns_record = zone.record_set.get(
45
+ for ns in nameserver_names:
46
+ ns_records = zone.records.filter(
58
47
  name=ns_name,
59
48
  type=RecordTypeChoices.NS,
60
49
  value=ns,
61
50
  )
62
51
 
63
- if ns_record.ttl is not None or not ns_record.managed:
64
- if verbose:
65
- print(f"Updating NS record '{ns_record}'")
66
- ns_record.ttl = None
67
- ns_record.managed = True
68
- ns_record.save()
69
-
70
- except Record.DoesNotExist:
71
- if verbose:
72
- print(f"Creating NS record for '{ns.rstrip('.')}' in zone '{zone}'")
73
- Record.objects.create(
74
- name=ns_name,
75
- zone=zone,
76
- type=RecordTypeChoices.NS,
77
- value=ns,
78
- ttl=None,
79
- managed=True,
80
- )
52
+ delete_ns = ns_records[1:]
53
+ for record in delete_ns:
54
+ if options.get("verbosity") > 1:
55
+ self.stdout.write(
56
+ f"Removing duplicate NS record '{record}' with value '{record.value}'"
57
+ )
58
+ record.delete()
59
+
60
+ try:
61
+ ns_record = zone.records.get(
62
+ name=ns_name,
63
+ type=RecordTypeChoices.NS,
64
+ value=ns,
65
+ )
66
+
67
+ if ns_record.ttl is not None or not ns_record.managed:
68
+ if options.get("verbosity") > 1:
69
+ self.stdout.write(
70
+ f"Updating NS record '{ns_record}' with value '{record.value}'"
71
+ )
72
+ ns_record.ttl = None
73
+ ns_record.managed = True
74
+ ns_record.save()
75
+
76
+ except Record.DoesNotExist:
77
+ if options.get("verbosity") > 1:
78
+ self.stdout.write(
79
+ f"Creating NS record for '{ns.rstrip('.')}' in zone '{zone}'"
80
+ )
81
+ Record.objects.create(
82
+ name=ns_name,
83
+ zone=zone,
84
+ type=RecordTypeChoices.NS,
85
+ value=ns,
86
+ ttl=None,
87
+ managed=True,
88
+ )
81
89
 
90
+ def _zone_cleanup_soa_records(self, **options):
91
+ if options.get("verbosity"):
92
+ self.stdout.write("Cleaning up the SOA record for all zones")
82
93
 
83
- def zone_update_soa_records(verbose=False):
84
- soa_name = "@"
94
+ soa_name = "@"
85
95
 
86
- for zone in Zone.objects.all():
87
- delete_soa = zone.record_set.filter(name=soa_name, type=RecordTypeChoices.SOA)[
88
- 1:
89
- ]
90
- for record in delete_soa:
91
- if verbose:
92
- print(f"Deleting duplicate SOA record {record}")
93
- record.delete()
96
+ for zone in Zone.objects.all():
97
+ delete_soa = zone.records.filter(name=soa_name, type=RecordTypeChoices.SOA)[
98
+ 1:
99
+ ]
100
+ for record in delete_soa:
101
+ if options.get("verbosity") > 1:
102
+ self.stdout.write(
103
+ f"Deleting duplicate SOA record '{record}' for zone '{zone}'"
104
+ )
105
+ record.delete()
94
106
 
95
- zone.update_soa_record()
107
+ zone.update_soa_record()
96
108
 
109
+ def _zone_update_arpa_network(self, **options):
110
+ if options.get("verbosity"):
111
+ self.stdout.write("Updating the ARPA network for reverse zones")
97
112
 
98
- def zone_update_arpa_network(verbose=False):
99
- for zone in Zone.objects.filter(name__endswith=".arpa"):
100
- name = zone.name
113
+ for zone in Zone.objects.filter(name__endswith=".arpa"):
114
+ if zone.arpa_network != (arpa_network := zone.network_from_name):
115
+ if options.get("verbosity") > 1:
116
+ self.stdout.write(
117
+ f"Setting the ARPA network for zone '{zone}' to '{arpa_network}'"
118
+ )
101
119
 
102
- # TODO: Rewrite with utility function
103
- if name.endswith(".in-addr.arpa"):
104
- address = ".".join(reversed(name.replace(".in-addr.arpa", "").split(".")))
105
- mask = len(address.split(".")) * 8
120
+ zone.arpa_network = arpa_network
121
+ zone.save()
106
122
 
107
- try:
108
- prefix = IPNetwork(f"{address}/{mask}")
109
- except AddrFormatError:
110
- prefix = None
123
+ def _record_cleanup_disable_ptr(self, **options):
124
+ if options.get("verbosity"):
125
+ self.stdout.write("Updating 'Disable PTR' for non-address records")
111
126
 
112
- elif name.endswith("ip6.arpa"):
113
- address = "".join(reversed(name.replace(".ip6.arpa", "").split(".")))
114
- mask = len(address)
115
- address = address + "0" * (32 - mask)
127
+ records = Record.objects.filter(
128
+ disable_ptr=True,
129
+ ).exclude(type__in=(RecordTypeChoices.A, RecordTypeChoices.AAAA))
116
130
 
117
- try:
118
- prefix = IPNetwork(
119
- f"{':'.join([(address[i:i+4]) for i in range(0, 32, 4)])}/{mask*4}"
131
+ for record in records:
132
+ if options.get("verbosity") > 1:
133
+ self.stdout.write(
134
+ f"Setting 'Disable PTR' to False for record '{record}'"
120
135
  )
121
- except AddrFormatError:
122
- prefix = None
123
- # TODO: End
124
-
125
- if zone.arpa_network != prefix:
126
- if verbose:
127
- print(f"Updating ARPA prefix for zone '{zone}' to '{prefix}'")
128
- zone.arpa_network = prefix
129
- zone.save()
130
-
131
-
132
- def record_cleanup_disable_ptr(verbose=False):
133
- Record.objects.filter(
134
- disable_ptr=False,
135
- ).exclude(
136
- type__in=(RecordTypeChoices.A, RecordTypeChoices.AAAA)
137
- ).update(disable_ptr=True)
138
-
139
-
140
- def record_update_ptr_records(verbose=False):
141
- for record in Record.objects.filter(
142
- type__in=(RecordTypeChoices.A, RecordTypeChoices.AAAA)
143
- ):
144
- record.update_ptr_record()
145
-
146
-
147
- def record_update_ip_address(verbose=False):
148
- for record in Record.objects.filter(
149
- type__in=(RecordTypeChoices.A, RecordTypeChoices.AAAA, RecordTypeChoices.PTR)
150
- ):
151
- if record.is_ptr_record:
152
- if record.ip_address != record.address_from_name:
153
- if verbose:
154
- print(
155
- f"Updating IP address of pointer record {record} to {record.address_from_name}"
156
- )
157
- record.ip_address = record.address_from_name
158
- record.save()
159
- else:
160
- if record.ip_address != IPAddress(record.value):
161
- if verbose:
162
- print(
163
- f"Updating IP address of address record {record} to {IPAddress(record.value)}"
164
- )
165
- record.ip_address = record.value
166
- record.save()
167
136
 
137
+ record.disable_ptr = False
138
+ record.save(update_fields=["disable_ptr"])
168
139
 
169
- class Command(BaseCommand):
170
- help = "Clean up NetBox DNS database"
140
+ def _record_update_ptr_records(self, **options):
141
+ if options.get("verbosity"):
142
+ self.stdout.write("Updating the PTR record for all address records")
143
+
144
+ for record in Record.objects.filter(
145
+ type__in=(RecordTypeChoices.A, RecordTypeChoices.AAAA)
146
+ ):
147
+ record.save(update_fields=["ptr_record"])
171
148
 
172
- def add_arguments(self, parser):
173
- parser.add_argument(
174
- "--verbose", action="store_true", help="Increase output verbosity"
149
+ def _record_update_ip_address(self, **options):
150
+ if options.get("verbosity"):
151
+ self.stdout.write("Updating the IP address for all address and PTR records")
152
+
153
+ for record in Record.objects.filter(
154
+ type__in=(
155
+ RecordTypeChoices.A,
156
+ RecordTypeChoices.AAAA,
157
+ RecordTypeChoices.PTR,
158
+ )
159
+ ):
160
+ if record.is_ptr_record:
161
+ if record.ip_address != record.address_from_name:
162
+ if options.get("verbosity") > 1:
163
+ self.stdout.write(
164
+ f"Setting the IP Address for pointer record '{record}' to '{record.address_from_name}'"
165
+ )
166
+ record.ip_address = record.address_from_name
167
+ record.save()
168
+ else:
169
+ if record.ip_address != IPAddress(record.value):
170
+ if options.get("verbosity") > 1:
171
+ self.stdout.write(
172
+ f"Updating the IP address for address record '{record}' to '{IPAddress(record.value)}'"
173
+ )
174
+ record.ip_address = record.value
175
+ record.save()
176
+
177
+ def _record_remove_orphaned_ptr_records(self, **options):
178
+ if options.get("verbosity"):
179
+ self.stdout.write("Removing orphaned managed PTR records")
180
+
181
+ orphaned_ptr_records = Record.objects.filter(
182
+ type=RecordTypeChoices.PTR,
183
+ managed=True,
184
+ address_records__isnull=True,
175
185
  )
176
186
 
177
- def handle(self, *model_names, **options):
178
- zone_rename_passive_status_to_parked(options["verbose"])
179
- zone_cleanup_ns_records(options["verbose"])
180
- zone_update_soa_records(options["verbose"])
181
- zone_update_arpa_network(options["verbose"])
182
- record_cleanup_disable_ptr(options["verbose"])
183
- record_update_ptr_records(options["verbose"])
184
- record_update_ip_address(options["verbose"])
185
-
186
- self.stdout.write("Database cleanup completed.")
187
+ for record in orphaned_ptr_records:
188
+ if options.get("verbosity") > 1:
189
+ self.stdout.write(f"Removing orphaned PTR record '{record}'")
190
+ record.delete()
191
+
192
+ def _record_remove_orphaned_address_records(self, **options):
193
+ if options.get("verbosity"):
194
+ self.stdout.write("Removing orphaned managed address records")
195
+
196
+ orphaned_address_records = Record.objects.filter(
197
+ type__in=(RecordTypeChoices.A, RecordTypeChoices.AAAA),
198
+ managed=True,
199
+ ipam_ip_address__isnull=True,
200
+ )
201
+
202
+ for record in orphaned_address_records:
203
+ if options.get("verbosity") > 1:
204
+ self.stdout.write(f"Removing orphaned address record '{record}'")
205
+ record.delete()
@@ -0,0 +1,64 @@
1
+ from django.core.management.base import BaseCommand
2
+ from django.db.models import Max, Min
3
+
4
+ from netbox_dns.models import Record
5
+ from netbox_dns.choices import RecordTypeChoices
6
+
7
+
8
+ class Command(BaseCommand):
9
+ help = "Clean up the TTLs for RRSets"
10
+
11
+ def add_arguments(self, parser):
12
+ min_max = parser.add_mutually_exclusive_group()
13
+ min_max.add_argument(
14
+ "--min",
15
+ action="store_true",
16
+ help="Use the minimum TTL of an RRSet for all Records",
17
+ )
18
+ min_max.add_argument(
19
+ "--max",
20
+ action="store_true",
21
+ help="Use the maximum TTL of an RRSet for all Records",
22
+ )
23
+
24
+ def handle(self, *model_names, **options):
25
+ self._cleanup_rrset_ttl(**options)
26
+
27
+ def _cleanup_rrset_ttl(self, **options):
28
+ if options.get("verbosity"):
29
+ self.stdout.write("Cleaning up diverging RRset TTL values")
30
+
31
+ ttl_records = (
32
+ Record.objects.filter(ttl__isnull=False)
33
+ .exclude(type=RecordTypeChoices.SOA)
34
+ .exclude(type=RecordTypeChoices.PTR, managed=True)
35
+ )
36
+ for record in ttl_records:
37
+ records = Record.objects.filter(
38
+ name=record.name,
39
+ zone=record.zone,
40
+ type=record.type,
41
+ ).exclude(type=RecordTypeChoices.PTR, managed=True)
42
+
43
+ if records.count() == 1:
44
+ if options.get("verbosity") > 2:
45
+ self.stdout.write(
46
+ f"Ignoring single record '{record.pk}' ('{record}')"
47
+ )
48
+ continue
49
+
50
+ if options.get("max"):
51
+ ttl = records.aggregate(Max("ttl")).get("ttl__max")
52
+ else:
53
+ ttl = records.aggregate(Min("ttl")).get("ttl__min")
54
+
55
+ for record in records.exclude(ttl=ttl):
56
+ if options.get("verbosity") > 1:
57
+ self.stdout.write(
58
+ f"Setting TTL for record '{record.pk}' ('{record}') to {ttl}"
59
+ )
60
+ record.ttl = ttl
61
+ record.save(update_fields=["ttl"], update_rrset_ttl=False)
62
+
63
+ if options.get("verbosity"):
64
+ self.stdout.write("RRSet TTL cleanup completed.")
@@ -0,0 +1,23 @@
1
+ from django.core.management.base import BaseCommand
2
+
3
+ from ipam.models import IPAddress
4
+
5
+ from netbox_dns.utilities import update_dns_records, get_zones
6
+
7
+
8
+ class Command(BaseCommand):
9
+ help = "Rebuild DNSsync relationships between IP addresses and records"
10
+
11
+ def handle(self, *model_names, **options):
12
+ ip_addresses = IPAddress.objects.all()
13
+ for ip_address in ip_addresses:
14
+ if options.get("verbosity") >= 2:
15
+ self.stdout.write(
16
+ f"Updating DNS records for IP Address {ip_address}, VRF {ip_address.vrf}"
17
+ )
18
+ if options.get("verbosity") >= 3:
19
+ self.stdout.write(f" Zones: {get_zones(ip_address)}")
20
+ if update_dns_records(ip_address) and options.get("verbosity") >= 1:
21
+ self.stdout.write(
22
+ f"Updated DNS records for IP Address {ip_address}, VRF {ip_address.vrf}"
23
+ )
@@ -0,0 +1,140 @@
1
+ from django.core.management.base import BaseCommand
2
+
3
+ from core.models import ObjectType
4
+ from extras.models import CustomField
5
+ from extras.choices import CustomFieldTypeChoices
6
+ from ipam.models import IPAddress
7
+
8
+
9
+ class Command(BaseCommand):
10
+ help = "Setup IPAddress custom fields for IPAM DNSsync"
11
+
12
+ def add_arguments(self, parser):
13
+ parser.add_argument(
14
+ "--remove", action="store_true", default=False, help="Remove custom fields"
15
+ )
16
+
17
+ def handle(self, *model_names, **options):
18
+ ipaddress_object_type = ObjectType.objects.get_for_model(IPAddress)
19
+
20
+ if options.get("remove"):
21
+ if options.get("verbosity"):
22
+ self.stdout.write("Trying to remove IPAM DNSsync custom fields")
23
+ for cf in (
24
+ "ipaddress_dns_disabled",
25
+ "ipaddress_dns_record_ttl",
26
+ "ipaddress_dns_record_disable_ptr",
27
+ ):
28
+ try:
29
+ CustomField.objects.get(
30
+ name=cf, object_types=ipaddress_object_type
31
+ ).delete()
32
+ if options.get("verbosity"):
33
+ self.stdout.write(f"Removed custom field '{cf}'")
34
+ except CustomField.DoesNotExist:
35
+ pass
36
+ return
37
+
38
+ # +
39
+ # Remove pre-existing IPAM Coupling custom fields
40
+ # -
41
+ if options.get("verbosity") >= 2:
42
+ self.stdout.write("Trying to remove obsolete IPAM Coupling custom fields")
43
+ for cf in (
44
+ "ipaddress_dns_record_name",
45
+ "ipaddress_dns_zone_id",
46
+ ):
47
+ try:
48
+ CustomField.objects.get(
49
+ name=cf, object_types=ipaddress_object_type
50
+ ).delete()
51
+ if options.get("verbosity"):
52
+ self.stdout.write(f"Removed custom field '{cf}'")
53
+ except CustomField.DoesNotExist:
54
+ pass
55
+
56
+ if options.get("verbosity") >= 2:
57
+ self.stdout.write("Creating IPAM DNSsync custom fields")
58
+
59
+ if not CustomField.objects.filter(
60
+ name="ipaddress_dns_disabled",
61
+ type=CustomFieldTypeChoices.TYPE_BOOLEAN,
62
+ object_types=ipaddress_object_type,
63
+ ).exists():
64
+ cf_dnssync_disabled = CustomField.objects.create(
65
+ name="ipaddress_dns_disabled",
66
+ label="Disable DNSsync",
67
+ description="Disable DNS address and pointer record generation for this address",
68
+ type=CustomFieldTypeChoices.TYPE_BOOLEAN,
69
+ required=False,
70
+ default=False,
71
+ group_name="DNSsync",
72
+ is_cloneable=True,
73
+ weight=100,
74
+ )
75
+ cf_dnssync_disabled.object_types.set([ipaddress_object_type])
76
+ if options.get("verbosity"):
77
+ self.stdout.write("Created custom field 'ipaddress_dns_disabled'")
78
+
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 != "DNSsync":
86
+ cf_ttl.group_name = "DNSsync"
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:
92
+ cf_ttl = CustomField.objects.create(
93
+ name="ipaddress_dns_record_ttl",
94
+ description="TTL for DNS records created for this address",
95
+ label="TTL",
96
+ type=CustomFieldTypeChoices.TYPE_INTEGER,
97
+ validation_minimum=0,
98
+ validation_maximum=2147483647,
99
+ required=False,
100
+ group_name="DNSsync",
101
+ is_cloneable=True,
102
+ weight=200,
103
+ )
104
+ cf_ttl.object_types.set([ipaddress_object_type])
105
+ if options.get("verbosity"):
106
+ self.stdout.write("Created custom field 'ipaddress_dns_record_ttl'")
107
+
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 != "DNSsync":
115
+ cf_disable_ptr.group_name = "DNSsync"
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:
125
+ cf_disable_ptr = CustomField.objects.create(
126
+ name="ipaddress_dns_record_disable_ptr",
127
+ description="Disable DNS PTR record generation for this address",
128
+ label="Disable PTR",
129
+ type=CustomFieldTypeChoices.TYPE_BOOLEAN,
130
+ required=False,
131
+ default=False,
132
+ group_name="DNSsync",
133
+ is_cloneable=True,
134
+ weight=300,
135
+ )
136
+ cf_disable_ptr.object_types.set([ipaddress_object_type])
137
+ if options.get("verbosity"):
138
+ self.stdout.write(
139
+ "Created custom field 'ipaddress_dns_record_disable_ptr'"
140
+ )
@@ -9,35 +9,8 @@ import netbox_dns.fields.network
9
9
 
10
10
 
11
11
  class Migration(migrations.Migration):
12
- replaces = [
13
- ("netbox_dns", "0001_initial"),
14
- ("netbox_dns", "0015_add_record_status"),
15
- ("netbox_dns", "0013_add_nameserver_zone_record_description"),
16
- ("netbox_dns", "0011_add_view_model"),
17
- ("netbox_dns", "0002_zone_default_ttl"),
18
- ("netbox_dns", "0003_soa_managed_records"),
19
- ("netbox_dns", "0004_create_ptr_for_a_aaaa_records"),
20
- ("netbox_dns", "0006_zone_soa_serial_auto"),
21
- ("netbox_dns", "0007_alter_zone_soa_serial_auto"),
22
- ("netbox_dns", "0005_update_ns_records"),
23
- ("netbox_dns", "0008_zone_status_names"),
24
- ("netbox_dns", "0009_netbox32"),
25
- ("netbox_dns", "0010_update_soa_records"),
26
- ("netbox_dns", "0012_adjust_zone_and_record"),
27
- ("netbox_dns", "0014_add_view_description"),
28
- ("netbox_dns", "0016_cleanup_ptr_records"),
29
- ("netbox_dns", "0017_alter_record_ttl"),
30
- ("netbox_dns", "0018_zone_arpa_network"),
31
- ("netbox_dns", "0019_update_ns_ttl"),
32
- ]
33
-
34
- initial = True
35
-
36
12
  dependencies = [
37
- ("extras", "0072_created_datetimefield"),
38
- ("extras", "0062_clear_secrets_changelog"),
39
13
  ("extras", "0073_journalentry_tags_custom_fields"),
40
- ("extras", "0059_exporttemplate_as_attachment"),
41
14
  ]
42
15
 
43
16
  operations = [