netbox-plugin-dns 0.22.4__tar.gz → 0.22.6__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 (118) hide show
  1. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/PKG-INFO +11 -11
  2. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/README.md +9 -10
  3. netbox_plugin_dns-0.22.6/netbox_dns/__init__.py +50 -0
  4. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/api/serializers.py +1 -0
  5. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/filters/registrar.py +1 -0
  6. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/forms/registrar.py +11 -1
  7. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/forms/zone.py +31 -21
  8. netbox_plugin_dns-0.22.6/netbox_dns/management/commands/cleanup_rrset_ttl.py +62 -0
  9. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/models/record.py +151 -25
  10. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/models/zone.py +82 -18
  11. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/templates/netbox_dns/record.html +6 -22
  12. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/templates/netbox_dns/registrar.html +4 -0
  13. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/templates/netbox_dns/zone.html +7 -0
  14. netbox_plugin_dns-0.22.6/netbox_dns/views/record.py +159 -0
  15. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/pyproject.toml +5 -2
  16. netbox_plugin_dns-0.22.4/netbox_dns/__init__.py +0 -86
  17. netbox_plugin_dns-0.22.4/netbox_dns/views/record.py +0 -85
  18. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/LICENSE +0 -0
  19. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/api/nested_serializers.py +0 -0
  20. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/api/urls.py +0 -0
  21. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/api/views.py +0 -0
  22. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/apps.py +0 -0
  23. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/fields/__init__.py +0 -0
  24. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/fields/address.py +0 -0
  25. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/fields/network.py +0 -0
  26. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/fields/rfc2317.py +0 -0
  27. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/filters/__init__.py +0 -0
  28. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/filters/contact.py +0 -0
  29. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/filters/nameserver.py +0 -0
  30. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/filters/record.py +0 -0
  31. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/filters/view.py +0 -0
  32. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/filters/zone.py +0 -0
  33. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/forms/__init__.py +0 -0
  34. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/forms/contact.py +0 -0
  35. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/forms/nameserver.py +0 -0
  36. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/forms/record.py +0 -0
  37. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/forms/view.py +0 -0
  38. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/graphql/__init__.py +0 -0
  39. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/graphql/contact.py +0 -0
  40. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/graphql/nameserver.py +0 -0
  41. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/graphql/record.py +0 -0
  42. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/graphql/registrar.py +0 -0
  43. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/graphql/schema.py +0 -0
  44. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/graphql/view.py +0 -0
  45. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/graphql/zone.py +0 -0
  46. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/management/commands/cleanup_database.py +0 -0
  47. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/management/commands/setup_coupling.py +0 -0
  48. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/management/commands/update_soa.py +0 -0
  49. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0001_initial.py +0 -0
  50. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0001_squashed_netbox_dns_0_15.py +0 -0
  51. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0002_zone_default_ttl.py +0 -0
  52. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0003_soa_managed_records.py +0 -0
  53. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0004_create_ptr_for_a_aaaa_records.py +0 -0
  54. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0005_update_ns_records.py +0 -0
  55. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0006_zone_soa_serial_auto.py +0 -0
  56. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0007_alter_zone_soa_serial_auto.py +0 -0
  57. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0008_zone_status_names.py +0 -0
  58. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0009_netbox32.py +0 -0
  59. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0010_update_soa_records.py +0 -0
  60. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0011_add_view_model.py +0 -0
  61. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0012_adjust_zone_and_record.py +0 -0
  62. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0013_add_nameserver_zone_record_description.py +0 -0
  63. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0014_add_view_description.py +0 -0
  64. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0015_add_record_status.py +0 -0
  65. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0016_cleanup_ptr_records.py +0 -0
  66. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0017_alter_record_ttl.py +0 -0
  67. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0018_zone_arpa_network.py +0 -0
  68. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0019_update_ns_ttl.py +0 -0
  69. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0020_netbox_3_4.py +0 -0
  70. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0021_record_ip_address.py +0 -0
  71. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0022_search.py +0 -0
  72. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0023_alter_record_value.py +0 -0
  73. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0024_tenancy.py +0 -0
  74. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0025_ipam_coupling_cf.py +0 -0
  75. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0026_domain_registration.py +0 -0
  76. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0027_alter_registrar_iana_id.py +0 -0
  77. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/0028_rfc2317_fields.py +0 -0
  78. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/migrations/__init__.py +0 -0
  79. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/models/__init__.py +0 -0
  80. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/models/contact.py +0 -0
  81. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/models/nameserver.py +0 -0
  82. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/models/registrar.py +0 -0
  83. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/models/view.py +0 -0
  84. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/navigation.py +0 -0
  85. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/signals/__init__.py +0 -0
  86. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/signals/ipam_coupling.py +0 -0
  87. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/tables/__init__.py +0 -0
  88. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/tables/contact.py +0 -0
  89. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/tables/nameserver.py +0 -0
  90. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/tables/record.py +0 -0
  91. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/tables/registrar.py +0 -0
  92. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/tables/view.py +0 -0
  93. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/tables/zone.py +0 -0
  94. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/template_content.py +0 -0
  95. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/templates/netbox_dns/contact.html +0 -0
  96. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/templates/netbox_dns/nameserver.html +0 -0
  97. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/templates/netbox_dns/record/managed.html +0 -0
  98. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/templates/netbox_dns/record/related.html +0 -0
  99. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/templates/netbox_dns/related_dns_objects.html +0 -0
  100. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/templates/netbox_dns/view.html +0 -0
  101. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/templates/netbox_dns/zone/base.html +0 -0
  102. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/templates/netbox_dns/zone/child.html +0 -0
  103. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/templates/netbox_dns/zone/managed_record.html +0 -0
  104. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/templates/netbox_dns/zone/record.html +0 -0
  105. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/templates/netbox_dns/zone/registration.html +0 -0
  106. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/templates/netbox_dns/zone/rfc2317_child_zone.html +0 -0
  107. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/urls.py +0 -0
  108. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/utilities/__init__.py +0 -0
  109. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/utilities/ipam_coupling.py +0 -0
  110. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/validators/__init__.py +0 -0
  111. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/validators/dns_name.py +0 -0
  112. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/validators/rfc2317.py +0 -0
  113. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/views/__init__.py +0 -0
  114. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/views/contact.py +0 -0
  115. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/views/nameserver.py +0 -0
  116. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/views/registrar.py +0 -0
  117. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/netbox_dns/views/view.py +0 -0
  118. {netbox_plugin_dns-0.22.4 → netbox_plugin_dns-0.22.6}/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.4
3
+ Version: 0.22.6
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,6 +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.8,<4.0
11
+ Classifier: Development Status :: 5 - Production/Stable
11
12
  Classifier: License :: OSI Approved :: MIT License
12
13
  Classifier: Programming Language :: Python :: 3
13
14
  Classifier: Programming Language :: Python :: 3.8
@@ -19,16 +20,15 @@ Requires-Dist: dnspython (>=2.2.1,<3.0.0)
19
20
  Project-URL: Repository, https://github.com/peteeckel/netbox-plugin-dns
20
21
  Description-Content-Type: text/markdown
21
22
 
22
- <h1 align="center">NetBox DNS</h1>
23
-
24
- <p align="center"><i>NetBox DNS is a NetBox plugin for managing DNS views, zones, name servers and records.</i></p>
23
+ # NetBox DNS
24
+ The NetBox DNS plugin enables NetBox to manage operational DNS data such as name servers, zones, records and views, as well as registration data for domains. It can automate tasks like creating PTR records, generating zone serial numbers, NS and SOA records, as well as validate names and values values for resource records to ensure zone data is consistent, current and conforming to the relevant RFCs.
25
25
 
26
26
  <div align="center">
27
27
  <a href="https://pypi.org/project/netbox-plugin-dns/"><img src="https://img.shields.io/pypi/v/netbox-plugin-dns" alt="PyPi"/></a>
28
- <a href="https://github.com/peteeckel/netbox-plugin-dns/stargazers"><img src="https://img.shields.io/github/stars/peteeckel/netbox-plugin-dns" alt="Stars Badge"/></a>
29
- <a href="https://github.com/peteeckel/netbox-plugin-dns/network/members"><img src="https://img.shields.io/github/forks/peteeckel/netbox-plugin-dns" alt="Forks Badge"/></a>
30
- <a href="https://github.com/peteeckel/netbox-plugin-dns/pulls"><img src="https://img.shields.io/github/issues-pr/peteeckel/netbox-plugin-dns" alt="Pull Requests Badge"/></a>
28
+ <a href="https://github.com/peteeckel/netbox-plugin-dns/stargazers"><img src="https://img.shields.io/github/stars/peteeckel/netbox-plugin-dns?style=flat" alt="Stars Badge"/></a>
29
+ <a href="https://github.com/peteeckel/netbox-plugin-dns/network/members"><img src="https://img.shields.io/github/forks/peteeckel/netbox-plugin-dns?style=flat" alt="Forks Badge"/></a>
31
30
  <a href="https://github.com/peteeckel/netbox-plugin-dns/issues"><img src="https://img.shields.io/github/issues/peteeckel/netbox-plugin-dns" alt="Issues Badge"/></a>
31
+ <a href="https://github.com/peteeckel/netbox-plugin-dns/pulls"><img src="https://img.shields.io/github/issues-pr/peteeckel/netbox-plugin-dns" alt="Pull Requests Badge"/></a>
32
32
  <a href="https://github.com/peteeckel/netbox-plugin-dns/graphs/contributors"><img alt="GitHub contributors" src="https://img.shields.io/github/contributors/peteeckel/netbox-plugin-dns?color=2b9348"></a>
33
33
  <a href="https://github.com/peteeckel/netbox-plugin-dns/blob/master/LICENSE"><img src="https://img.shields.io/github/license/peteeckel/netbox-plugin-dns?color=2b9348" alt="License Badge"/></a>
34
34
  <a href="https://pepy.tech/project/netbox-plugin-dns"><img alt="Downloads" src="https://static.pepy.tech/badge/netbox-plugin-dns"></a>
@@ -40,10 +40,10 @@ Description-Content-Type: text/markdown
40
40
 
41
41
  * Manage name servers, zones and records
42
42
  * Automatically generate SOA and NS records for zones
43
- * Automatically create and update PTR records for IPv4 and IPv6 records
44
- * Optionally organize DNS zones in views for split horizon DNS and multi-site deployments
45
- * Optionally maintain domain registrar and registrant information for zones
46
- * Maintain RFC2317 reverse zones for IPv4 prefixes with a network mask length longer than 24 bits
43
+ * Automatically create and update PTR records for IPv4 and IPv6 address records
44
+ * Organize DNS zones in views for split horizon DNS and multi-site deployments
45
+ * Manage domain registrar and registrant information for domains related to zones
46
+ * Manage RFC2317 reverse zones for IPv4 prefixes with a network mask length longer than 24 bits
47
47
 
48
48
  NetBox DNS is using the standardized NetBox plugin interface, so it also takes advantage of the NetBox tagging and change log features.
49
49
 
@@ -1,13 +1,12 @@
1
- <h1 align="center">NetBox DNS</h1>
2
-
3
- <p align="center"><i>NetBox DNS is a NetBox plugin for managing DNS views, zones, name servers and records.</i></p>
1
+ # NetBox DNS
2
+ The NetBox DNS plugin enables NetBox to manage operational DNS data such as name servers, zones, records and views, as well as registration data for domains. It can automate tasks like creating PTR records, generating zone serial numbers, NS and SOA records, as well as validate names and values values for resource records to ensure zone data is consistent, current and conforming to the relevant RFCs.
4
3
 
5
4
  <div align="center">
6
5
  <a href="https://pypi.org/project/netbox-plugin-dns/"><img src="https://img.shields.io/pypi/v/netbox-plugin-dns" alt="PyPi"/></a>
7
- <a href="https://github.com/peteeckel/netbox-plugin-dns/stargazers"><img src="https://img.shields.io/github/stars/peteeckel/netbox-plugin-dns" alt="Stars Badge"/></a>
8
- <a href="https://github.com/peteeckel/netbox-plugin-dns/network/members"><img src="https://img.shields.io/github/forks/peteeckel/netbox-plugin-dns" alt="Forks Badge"/></a>
9
- <a href="https://github.com/peteeckel/netbox-plugin-dns/pulls"><img src="https://img.shields.io/github/issues-pr/peteeckel/netbox-plugin-dns" alt="Pull Requests Badge"/></a>
6
+ <a href="https://github.com/peteeckel/netbox-plugin-dns/stargazers"><img src="https://img.shields.io/github/stars/peteeckel/netbox-plugin-dns?style=flat" alt="Stars Badge"/></a>
7
+ <a href="https://github.com/peteeckel/netbox-plugin-dns/network/members"><img src="https://img.shields.io/github/forks/peteeckel/netbox-plugin-dns?style=flat" alt="Forks Badge"/></a>
10
8
  <a href="https://github.com/peteeckel/netbox-plugin-dns/issues"><img src="https://img.shields.io/github/issues/peteeckel/netbox-plugin-dns" alt="Issues Badge"/></a>
9
+ <a href="https://github.com/peteeckel/netbox-plugin-dns/pulls"><img src="https://img.shields.io/github/issues-pr/peteeckel/netbox-plugin-dns" alt="Pull Requests Badge"/></a>
11
10
  <a href="https://github.com/peteeckel/netbox-plugin-dns/graphs/contributors"><img alt="GitHub contributors" src="https://img.shields.io/github/contributors/peteeckel/netbox-plugin-dns?color=2b9348"></a>
12
11
  <a href="https://github.com/peteeckel/netbox-plugin-dns/blob/master/LICENSE"><img src="https://img.shields.io/github/license/peteeckel/netbox-plugin-dns?color=2b9348" alt="License Badge"/></a>
13
12
  <a href="https://pepy.tech/project/netbox-plugin-dns"><img alt="Downloads" src="https://static.pepy.tech/badge/netbox-plugin-dns"></a>
@@ -19,10 +18,10 @@
19
18
 
20
19
  * Manage name servers, zones and records
21
20
  * Automatically generate SOA and NS records for zones
22
- * Automatically create and update PTR records for IPv4 and IPv6 records
23
- * Optionally organize DNS zones in views for split horizon DNS and multi-site deployments
24
- * Optionally maintain domain registrar and registrant information for zones
25
- * Maintain RFC2317 reverse zones for IPv4 prefixes with a network mask length longer than 24 bits
21
+ * Automatically create and update PTR records for IPv4 and IPv6 address records
22
+ * Organize DNS zones in views for split horizon DNS and multi-site deployments
23
+ * Manage domain registrar and registrant information for domains related to zones
24
+ * Manage RFC2317 reverse zones for IPv4 prefixes with a network mask length longer than 24 bits
26
25
 
27
26
  NetBox DNS is using the standardized NetBox plugin interface, so it also takes advantage of the NetBox tagging and change log features.
28
27
 
@@ -0,0 +1,50 @@
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.6"
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
+ version = __version__
22
+ author = "Peter Eckel"
23
+ author_email = "pete@netbox-dns.org"
24
+ required_settings = []
25
+ default_settings = {
26
+ "zone_default_ttl": 86400,
27
+ "zone_soa_serial_auto": True,
28
+ "zone_soa_serial": 1,
29
+ "zone_soa_refresh": 43200,
30
+ "zone_soa_retry": 7200,
31
+ "zone_soa_expire": 2419200,
32
+ "zone_soa_minimum": 3600,
33
+ "feature_ipam_coupling": False,
34
+ "tolerate_underscores_in_hostnames": False,
35
+ "tolerate_leading_underscore_types": [
36
+ "TXT",
37
+ "SRV",
38
+ ],
39
+ "tolerate_non_rfc1035_types": [],
40
+ "enable_root_zones": False,
41
+ "enforce_unique_records": True,
42
+ "enforce_unique_rrset_ttl": True,
43
+ }
44
+ base_url = "netbox-dns"
45
+
46
+
47
+ #
48
+ # Initialize plugin config
49
+ #
50
+ config = DNSConfig
@@ -272,6 +272,7 @@ class RegistrarSerializer(NetBoxModelSerializer):
272
272
  "display",
273
273
  "name",
274
274
  "iana_id",
275
+ "address",
275
276
  "referral_url",
276
277
  "whois_server",
277
278
  "abuse_email",
@@ -12,6 +12,7 @@ class RegistrarFilter(NetBoxModelFilterSet):
12
12
  "id",
13
13
  "name",
14
14
  "iana_id",
15
+ "address",
15
16
  "referral_url",
16
17
  "whois_server",
17
18
  "abuse_email",
@@ -17,6 +17,7 @@ class RegistrarForm(NetBoxModelForm):
17
17
  fields = (
18
18
  "name",
19
19
  "iana_id",
20
+ "address",
20
21
  "referral_url",
21
22
  "whois_server",
22
23
  "abuse_email",
@@ -29,12 +30,18 @@ class RegistrarFilterForm(NetBoxModelFilterSetForm):
29
30
  model = Registrar
30
31
  fieldsets = (
31
32
  (None, ("q", "name", "iana_id", "tags")),
32
- ("Contact", ("referral_url", "whois_server", "abuse_email", "abuse_phone")),
33
+ (
34
+ "Contact",
35
+ ("address", "referral_url", "whois_server", "abuse_email", "abuse_phone"),
36
+ ),
33
37
  )
34
38
 
35
39
  name = forms.CharField(
36
40
  required=False,
37
41
  )
42
+ address = forms.CharField(
43
+ required=False,
44
+ )
38
45
  iana_id = forms.IntegerField(
39
46
  required=False,
40
47
  label="IANA ID",
@@ -64,6 +71,7 @@ class RegistrarImportForm(NetBoxModelImportForm):
64
71
  fields = (
65
72
  "name",
66
73
  "iana_id",
74
+ "address",
67
75
  "referral_url",
68
76
  "whois_server",
69
77
  "abuse_email",
@@ -96,6 +104,7 @@ class RegistrarBulkEditForm(NetBoxModelBulkEditForm):
96
104
  (
97
105
  None,
98
106
  (
107
+ "address",
99
108
  "referral_url",
100
109
  "whois_server",
101
110
  "abuse_email",
@@ -105,6 +114,7 @@ class RegistrarBulkEditForm(NetBoxModelBulkEditForm):
105
114
  )
106
115
 
107
116
  nullable_fields = (
117
+ "address",
108
118
  "referral_url",
109
119
  "whois_server",
110
120
  "abuse_email",
@@ -315,7 +315,7 @@ class ZoneImportForm(NetBoxModelImportForm):
315
315
  required=False,
316
316
  help_text="Mailbox of the zone's administrator",
317
317
  )
318
- soa_serial_auto = forms.BooleanField(
318
+ soa_serial_auto = forms.NullBooleanField(
319
319
  required=False,
320
320
  help_text="Generate the SOA serial",
321
321
  )
@@ -443,26 +443,6 @@ class ZoneImportForm(NetBoxModelImportForm):
443
443
  def clean_soa_rname(self):
444
444
  return self._clean_field_with_defaults("soa_rname")
445
445
 
446
- def clean_soa_serial_auto(self):
447
- try:
448
- return self._clean_field_with_defaults("soa_serial_auto")
449
- except ValidationError:
450
- if self.cleaned_data["soa_serial"] or self._get_default_value("soa_serial"):
451
- return None
452
-
453
- raise
454
-
455
- def clean_soa_serial(self):
456
- try:
457
- return self._clean_field_with_defaults("soa_serial")
458
- except ValidationError:
459
- if self.cleaned_data["soa_serial_auto"] or self._get_default_value(
460
- "soa_serial_auto"
461
- ):
462
- return None
463
-
464
- raise
465
-
466
446
  def clean_soa_refresh(self):
467
447
  return self._clean_field_with_defaults("soa_refresh")
468
448
 
@@ -475,6 +455,36 @@ class ZoneImportForm(NetBoxModelImportForm):
475
455
  def clean_soa_minimum(self):
476
456
  return self._clean_field_with_defaults("soa_minimum")
477
457
 
458
+ def clean(self, *args, **kwargs):
459
+ super().clean(*args, **kwargs)
460
+
461
+ soa_serial_auto = self.cleaned_data.get("soa_serial_auto")
462
+ soa_serial = self.cleaned_data.get("soa_serial")
463
+
464
+ if soa_serial is None:
465
+ soa_serial = self._get_default_value("soa_serial")
466
+
467
+ if soa_serial_auto is None:
468
+ if self._get_default_value("soa_serial_auto") is not None:
469
+ soa_serial_auto = self._get_default_value("soa_serial_auto")
470
+
471
+ elif soa_serial is not None:
472
+ soa_serial_auto = False
473
+
474
+ else:
475
+ raise ValidationError(
476
+ "SOA Serial Auto not set and no default value and SOA Serial available"
477
+ )
478
+
479
+ if "soa_serial_auto" in self.cleaned_data:
480
+ self.cleaned_data["soa_serial_auto"] = soa_serial_auto
481
+
482
+ if "soa_serial" in self.cleaned_data:
483
+ if soa_serial_auto:
484
+ self.cleaned_data["soa_serial"] = None
485
+ else:
486
+ self.cleaned_data["soa_serial"] = soa_serial
487
+
478
488
  class Meta:
479
489
  model = Zone
480
490
 
@@ -0,0 +1,62 @@
1
+ from django.core.management.base import BaseCommand
2
+ from django.db.models import Max, Min
3
+
4
+ from netbox_dns.models import (
5
+ Record,
6
+ RecordTypeChoices,
7
+ )
8
+
9
+
10
+ class Command(BaseCommand):
11
+ help = "Clean up the TTLs for RRSets"
12
+
13
+ def add_arguments(self, parser):
14
+ min_max = parser.add_mutually_exclusive_group()
15
+ min_max.add_argument(
16
+ "--min",
17
+ action="store_true",
18
+ help="Use the minimum TTL of an RRSet for all Records",
19
+ )
20
+ min_max.add_argument(
21
+ "--max",
22
+ action="store_true",
23
+ help="Use the maximum TTL of an RRSet for all Records",
24
+ )
25
+
26
+ def handle(self, *model_names, **options):
27
+ self.cleanup_rrset_ttl(**options)
28
+
29
+ self.stdout.write("RRSet cleanup completed.")
30
+
31
+ def cleanup_rrset_ttl(self, **options):
32
+ verbose = options.get("verbosity") > 1
33
+
34
+ ttl_records = (
35
+ Record.objects.filter(ttl__isnull=False)
36
+ .exclude(type=RecordTypeChoices.SOA)
37
+ .exclude(type=RecordTypeChoices.PTR, maanged=True)
38
+ )
39
+ for record in ttl_records:
40
+ records = Record.objects.filter(
41
+ name=record.name,
42
+ zone=record.zone,
43
+ type=record.type,
44
+ ).exclude(type=RecordTypeChoices.PTR, maanged=True)
45
+
46
+ if records.count() == 1:
47
+ if options.get("verbosity") > 2:
48
+ self.stdout.write(f"Ignoring single record {record.id} ({record})")
49
+ continue
50
+
51
+ if options.get("max"):
52
+ ttl = records.aggregate(Max("ttl")).get("ttl__max")
53
+ else:
54
+ ttl = records.aggregate(Min("ttl")).get("ttl__min")
55
+
56
+ for record in records.exclude(ttl=ttl):
57
+ if options.get("verbosity") > 1:
58
+ self.stdout.write(
59
+ f"Updating TTL for record {record.id} ({record}) to {ttl}"
60
+ )
61
+ record.ttl = ttl
62
+ record.save(update_fields=["ttl"], update_rrset_ttl=False)
@@ -6,7 +6,7 @@ from dns import name as dns_name
6
6
 
7
7
  from django.core.exceptions import ValidationError
8
8
  from django.db import transaction, models
9
- from django.db.models import Q, ExpressionWrapper, BooleanField
9
+ from django.db.models import Q, ExpressionWrapper, BooleanField, Min
10
10
  from django.db.models.functions import Length
11
11
  from django.urls import reverse
12
12
 
@@ -38,6 +38,10 @@ from netbox_dns.validators import (
38
38
  import netbox_dns.models.zone as zone
39
39
 
40
40
 
41
+ def min_ttl(*ttl_list):
42
+ return min((ttl for ttl in ttl_list if ttl is not None), default=None)
43
+
44
+
41
45
  class RecordManager(models.Manager.from_queryset(RestrictedQuerySet)):
42
46
  """Special Manager for records providing the activity status annotation"""
43
47
 
@@ -207,7 +211,8 @@ class Record(NetBoxModel):
207
211
  try:
208
212
  name = (
209
213
  dns_name.from_text(
210
- self.name, origin=dns_name.from_text(self.zone.name, origin=None)
214
+ str(self.name),
215
+ origin=dns_name.from_text(self.zone.name, origin=None),
211
216
  )
212
217
  .relativize(dns_name.root)
213
218
  .to_unicode()
@@ -236,6 +241,16 @@ class Record(NetBoxModel):
236
241
 
237
242
  return name.to_text()
238
243
 
244
+ @property
245
+ def value_fqdn(self):
246
+ if self.type != RecordTypeChoices.CNAME:
247
+ return None
248
+
249
+ zone = dns_name.from_text(self.zone.name)
250
+ value_fqdn = dns_name.from_text(self.value, origin=zone)
251
+
252
+ return value_fqdn.to_text()
253
+
239
254
  @property
240
255
  def address_from_name(self):
241
256
  prefix = arpa_to_prefix(self.fqdn)
@@ -309,7 +324,7 @@ class Record(NetBoxModel):
309
324
 
310
325
  return ptr_zone
311
326
 
312
- def update_ptr_record(self, update_rfc2317_cname=True):
327
+ def update_ptr_record(self, update_rfc2317_cname=True, save_zone_serial=True):
313
328
  ptr_zone = self.ptr_zone
314
329
 
315
330
  if (
@@ -339,14 +354,16 @@ class Record(NetBoxModel):
339
354
  not ptr_record.zone.is_rfc2317_zone
340
355
  and ptr_record.rfc2317_cname_record is not None
341
356
  ):
342
- ptr_record.rfc2317_cname_record.delete()
357
+ ptr_record.rfc2317_cname_record.delete(
358
+ save_zone_serial=save_zone_serial
359
+ )
343
360
 
344
361
  with transaction.atomic():
345
362
  if ptr_record is not None:
346
363
  if ptr_record.zone.pk != ptr_zone.pk:
347
364
  if ptr_record.rfc2317_cname_record is not None:
348
365
  ptr_record.rfc2317_cname_record.delete()
349
- ptr_record.delete()
366
+ ptr_record.delete(save_zone_serial=save_zone_serial)
350
367
  ptr_record = None
351
368
 
352
369
  else:
@@ -358,7 +375,7 @@ class Record(NetBoxModel):
358
375
  ptr_record.name = ptr_name
359
376
  ptr_record.value = ptr_value
360
377
  ptr_record.ttl = self.ttl
361
- ptr_record.save()
378
+ ptr_record.save(save_zone_serial=save_zone_serial)
362
379
 
363
380
  if ptr_record is None:
364
381
  ptr_record = Record(
@@ -369,11 +386,14 @@ class Record(NetBoxModel):
369
386
  value=ptr_value,
370
387
  managed=True,
371
388
  )
372
- ptr_record.save(update_rfc2317_cname=update_rfc2317_cname)
389
+ ptr_record.save(
390
+ update_rfc2317_cname=update_rfc2317_cname,
391
+ save_zone_serial=save_zone_serial,
392
+ )
373
393
 
374
394
  self.ptr_record = ptr_record
375
395
 
376
- def update_rfc2317_cname_record(self):
396
+ def update_rfc2317_cname_record(self, save_zone_serial=True):
377
397
  if self.zone.rfc2317_parent_managed:
378
398
  cname_name = dns_name.from_text(
379
399
  ipaddress.ip_address(self.ip_address).reverse_pointer
@@ -383,7 +403,13 @@ class Record(NetBoxModel):
383
403
  self.rfc2317_cname_record.name = cname_name
384
404
  self.rfc2317_cname_record.zone = self.zone.rfc2317_parent_zone
385
405
  self.rfc2317_cname_record.value = self.fqdn
386
- self.rfc2317_cname_record.save()
406
+ self.rfc2317_cname_record.ttl = min_ttl(
407
+ self.rfc2317_cname_record.rfc2317_ptr_records.exclude(pk=self.pk)
408
+ .aggregate(Min("ttl"))
409
+ .get("ttl__min"),
410
+ self.ttl,
411
+ )
412
+ self.rfc2317_cname_record.save(save_zone_serial=save_zone_serial)
387
413
  else:
388
414
  rfc2317_cname_record = Record.objects.filter(
389
415
  name=cname_name,
@@ -392,20 +418,34 @@ class Record(NetBoxModel):
392
418
  managed=True,
393
419
  value=self.fqdn,
394
420
  ).first()
395
- if rfc2317_cname_record is None:
396
- rfc2317_cname_record = Record.objects.create(
421
+
422
+ if rfc2317_cname_record is not None:
423
+ rfc2317_cname_record.ttl = min_ttl(
424
+ rfc2317_cname_record.rfc2317_ptr_records.exclude(pk=self.pk)
425
+ .aggregate(Min("ttl"))
426
+ .get("ttl__min"),
427
+ self.ttl,
428
+ )
429
+ rfc2317_cname_record.save(
430
+ update_fields=["ttl"], save_zone_serial=save_zone_serial
431
+ )
432
+
433
+ else:
434
+ rfc2317_cname_record = Record(
397
435
  name=cname_name,
398
436
  type=RecordTypeChoices.CNAME,
399
437
  zone=self.zone.rfc2317_parent_zone,
400
438
  managed=True,
401
439
  value=self.fqdn,
440
+ ttl=self.ttl,
402
441
  )
442
+ rfc2317_cname_record.save(save_zone_serial=save_zone_serial)
403
443
 
404
444
  self.rfc2317_cname_record = rfc2317_cname_record
405
445
 
406
446
  else:
407
447
  if self.rfc2317_cname_record is not None:
408
- self.rfc2317_cname_record.delete()
448
+ self.rfc2317_cname_record.delete(save_zone_serial=save_zone_serial)
409
449
  self.rfc2317_cname_record = None
410
450
 
411
451
  def validate_name(self):
@@ -475,7 +515,7 @@ class Record(NetBoxModel):
475
515
  }
476
516
  ) from None
477
517
 
478
- def check_unique(self):
518
+ def check_unique_record(self):
479
519
  if not get_plugin_config("netbox_dns", "enforce_unique_records", False):
480
520
  return
481
521
 
@@ -489,6 +529,10 @@ class Record(NetBoxModel):
489
529
  value=self.value,
490
530
  status__in=Record.ACTIVE_STATUS_LIST,
491
531
  )
532
+
533
+ if self.pk is not None:
534
+ records = records.exclude(pk=self.pk)
535
+
492
536
  if len(records):
493
537
  raise ValidationError(
494
538
  {
@@ -496,6 +540,64 @@ class Record(NetBoxModel):
496
540
  }
497
541
  ) from None
498
542
 
543
+ def check_unique_rrset_ttl(self):
544
+ if self.pk is not None:
545
+ return
546
+
547
+ if not get_plugin_config("netbox_dns", "enforce_unique_rrset_ttl", False):
548
+ return
549
+
550
+ if self.type == RecordTypeChoices.PTR and self.managed:
551
+ return
552
+
553
+ records = (
554
+ Record.objects.filter(
555
+ zone=self.zone,
556
+ name=self.name,
557
+ type=self.type,
558
+ )
559
+ .exclude(ttl=self.ttl)
560
+ .exclude(type=RecordTypeChoices.PTR, managed=True)
561
+ )
562
+
563
+ if not records.exists():
564
+ return
565
+
566
+ conflicting_ttls = ", ".join(set(str(record.ttl) for record in records))
567
+ raise ValidationError(
568
+ {
569
+ "ttl": f"There is at least one active {self.type} record for name {self.name} in zone {self.zone} and TTL is different ({conflicting_ttls})."
570
+ }
571
+ ) from None
572
+
573
+ def update_rrset_ttl(self, ttl=None):
574
+ if self.pk is None:
575
+ return
576
+
577
+ if not get_plugin_config("netbox_dns", "enforce_unique_rrset_ttl", False):
578
+ return
579
+
580
+ if self.type == RecordTypeChoices.PTR and self.managed:
581
+ return
582
+
583
+ if ttl is None:
584
+ ttl = self.ttl
585
+
586
+ records = (
587
+ Record.objects.filter(
588
+ zone=self.zone,
589
+ name=self.name,
590
+ type=self.type,
591
+ )
592
+ .exclude(pk=self.pk)
593
+ .exclude(ttl=ttl)
594
+ .exclude(type=RecordTypeChoices.PTR, managed=True)
595
+ )
596
+
597
+ for record in records:
598
+ record.ttl = ttl
599
+ record.save(update_fields=["ttl"], update_rrset_ttl=False)
600
+
499
601
  def clean_fields(self, *args, **kwargs):
500
602
  self.type = self.type.upper()
501
603
  super().clean_fields(*args, **kwargs)
@@ -503,7 +605,9 @@ class Record(NetBoxModel):
503
605
  def clean(self, *args, **kwargs):
504
606
  self.validate_name()
505
607
  self.validate_value()
506
- self.check_unique()
608
+ self.check_unique_record()
609
+ if self.pk is None:
610
+ self.check_unique_rrset_ttl()
507
611
 
508
612
  if not self.is_active:
509
613
  return
@@ -578,14 +682,24 @@ class Record(NetBoxModel):
578
682
  }
579
683
  ) from None
580
684
 
581
- def save(self, *args, update_rfc2317_cname=True, **kwargs):
685
+ def save(
686
+ self,
687
+ *args,
688
+ update_rfc2317_cname=True,
689
+ save_zone_serial=True,
690
+ update_rrset_ttl=True,
691
+ **kwargs,
692
+ ):
582
693
  self.full_clean()
583
694
 
695
+ if self.pk is not None and update_rrset_ttl:
696
+ self.update_rrset_ttl()
697
+
584
698
  if self.is_ptr_record:
585
699
  if self.zone.is_rfc2317_zone:
586
700
  self.ip_address = self.address_from_rfc2317_name
587
701
  if update_rfc2317_cname:
588
- self.update_rfc2317_cname_record()
702
+ self.update_rfc2317_cname_record(save_zone_serial=save_zone_serial)
589
703
  else:
590
704
  self.ip_address = self.address_from_name
591
705
 
@@ -595,7 +709,10 @@ class Record(NetBoxModel):
595
709
  self.ip_address = None
596
710
 
597
711
  if self.is_address_record:
598
- self.update_ptr_record(update_rfc2317_cname=update_rfc2317_cname)
712
+ self.update_ptr_record(
713
+ update_rfc2317_cname=update_rfc2317_cname,
714
+ save_zone_serial=save_zone_serial,
715
+ )
599
716
  elif self.ptr_record is not None:
600
717
  self.ptr_record.delete()
601
718
  self.ptr_record = None
@@ -604,15 +721,24 @@ class Record(NetBoxModel):
604
721
 
605
722
  zone = self.zone
606
723
  if self.type != RecordTypeChoices.SOA and zone.soa_serial_auto:
607
- zone.update_serial()
724
+ zone.update_serial(save_zone_serial=save_zone_serial)
608
725
 
609
- def delete(self, *args, **kwargs):
726
+ def delete(self, *args, save_zone_serial=True, **kwargs):
610
727
  if self.rfc2317_cname_record:
611
- if (
612
- self.rfc2317_cname_record.pk
613
- and self.rfc2317_cname_record.rfc2317_ptr_records.count() == 1
614
- ):
615
- self.rfc2317_cname_record.delete()
728
+ if self.rfc2317_cname_record.pk:
729
+ if self.rfc2317_cname_record.rfc2317_ptr_records.count() == 1:
730
+ self.rfc2317_cname_record.delete()
731
+ else:
732
+ self.rfc2317_cname_record.ttl = (
733
+ self.rfc2317_cname_record.rfc2317_ptr_records.exclude(
734
+ pk=self.pk
735
+ )
736
+ .aggregate(Min("ttl"))
737
+ .get("ttl__min")
738
+ )
739
+ self.rfc2317_cname_record.save(
740
+ update_fields=["ttl"], save_zone_serial=save_zone_serial
741
+ )
616
742
 
617
743
  if self.ptr_record:
618
744
  self.ptr_record.delete()
@@ -621,7 +747,7 @@ class Record(NetBoxModel):
621
747
 
622
748
  zone = self.zone
623
749
  if zone.soa_serial_auto:
624
- zone.update_serial()
750
+ zone.update_serial(save_zone_serial=save_zone_serial)
625
751
 
626
752
 
627
753
  @register_search