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
@@ -0,0 +1,198 @@
1
+ import dns
2
+ from dns import name as dns_name
3
+
4
+ from django.core.exceptions import ValidationError
5
+ from django.db import models
6
+ from django.utils.translation import gettext_lazy as _
7
+ from django.core.validators import MaxValueValidator
8
+
9
+ from netbox.models import NetBoxModel
10
+ from netbox.search import SearchIndex, register_search
11
+ from netbox.plugins.utils import get_plugin_config
12
+
13
+ from netbox_dns.choices import RecordTypeChoices, RecordStatusChoices
14
+ from netbox_dns.validators import validate_generic_name, validate_record_value
15
+
16
+ from .record import Record
17
+
18
+
19
+ __all__ = (
20
+ "RecordTemplate",
21
+ "RecordTemplateIndex",
22
+ )
23
+
24
+
25
+ class RecordTemplate(NetBoxModel):
26
+ class Meta:
27
+ verbose_name = _("Record Template")
28
+ verbose_name_plural = _("Record Templates")
29
+
30
+ ordering = ("name",)
31
+
32
+ clone_fields = (
33
+ "record_name",
34
+ "description",
35
+ "type",
36
+ "value",
37
+ "status",
38
+ "ttl",
39
+ "disable_ptr",
40
+ "tenant",
41
+ )
42
+
43
+ template_fields = (
44
+ "type",
45
+ "value",
46
+ "status",
47
+ "ttl",
48
+ "disable_ptr",
49
+ "tenant",
50
+ )
51
+
52
+ def __str__(self):
53
+ return str(self.name)
54
+
55
+ name = models.CharField(
56
+ verbose_name=_("Template Name"),
57
+ unique=True,
58
+ max_length=200,
59
+ db_collation="natural_sort",
60
+ )
61
+ record_name = models.CharField(
62
+ verbose_name=_("Name"),
63
+ max_length=255,
64
+ db_collation="natural_sort",
65
+ )
66
+ description = models.CharField(
67
+ verbose_name=_("Description"),
68
+ max_length=200,
69
+ blank=True,
70
+ )
71
+ type = models.CharField(
72
+ verbose_name=_("Type"),
73
+ choices=RecordTypeChoices,
74
+ )
75
+ value = models.CharField(
76
+ verbose_name=_("Value"),
77
+ max_length=65535,
78
+ )
79
+ status = models.CharField(
80
+ verbose_name=_("Status"),
81
+ choices=RecordStatusChoices,
82
+ default=RecordStatusChoices.STATUS_ACTIVE,
83
+ blank=False,
84
+ )
85
+ ttl = models.PositiveIntegerField(
86
+ verbose_name=_("TTL"),
87
+ null=True,
88
+ blank=True,
89
+ validators=[MaxValueValidator(2147483647)],
90
+ )
91
+ disable_ptr = models.BooleanField(
92
+ verbose_name=_("Disable PTR"),
93
+ help_text=_("Disable PTR record creation"),
94
+ default=False,
95
+ )
96
+ tenant = models.ForeignKey(
97
+ verbose_name=_("Tenant"),
98
+ to="tenancy.Tenant",
99
+ on_delete=models.PROTECT,
100
+ related_name="+",
101
+ blank=True,
102
+ null=True,
103
+ )
104
+
105
+ def get_status_color(self):
106
+ return RecordStatusChoices.colors.get(self.status)
107
+
108
+ def validate_name(self):
109
+ try:
110
+ name = dns_name.from_text(self.record_name, origin=None)
111
+ name.to_unicode()
112
+
113
+ except dns.exception.DNSException as exc:
114
+ raise ValidationError({"record_name": str(exc)})
115
+
116
+ if self.type not in get_plugin_config(
117
+ "netbox_dns", "tolerate_non_rfc1035_types", default=[]
118
+ ):
119
+ try:
120
+ validate_generic_name(
121
+ self.record_name,
122
+ (
123
+ self.type
124
+ in get_plugin_config(
125
+ "netbox_dns",
126
+ "tolerate_leading_underscore_types",
127
+ default=[],
128
+ )
129
+ ),
130
+ )
131
+ except ValidationError as exc:
132
+ raise ValidationError(
133
+ {
134
+ "record_name": exc,
135
+ }
136
+ )
137
+
138
+ def validate_value(self):
139
+ try:
140
+ validate_record_value(self)
141
+ except ValidationError as exc:
142
+ raise ValidationError({"value": exc})
143
+
144
+ def matching_records(self, zone):
145
+ return zone.records.filter(
146
+ name=self.record_name, type=self.type, value=self.value
147
+ )
148
+
149
+ def create_record(self, zone):
150
+ if self.matching_records(zone).exists():
151
+ return
152
+
153
+ record_data = {
154
+ "zone": zone,
155
+ "name": self.record_name,
156
+ }
157
+ for field in self.template_fields:
158
+ record_data[field] = getattr(self, field)
159
+
160
+ try:
161
+ record = Record.objects.create(**record_data)
162
+ except ValidationError as exc:
163
+ raise ValidationError(
164
+ {
165
+ None: _(
166
+ "Error while processing record template {template}: {error}"
167
+ ).format(template=self, error=exc.messages[0])
168
+ }
169
+ )
170
+
171
+ if tags := self.tags.all():
172
+ record.tags.set(tags)
173
+
174
+ def clean_fields(self, exclude=None):
175
+ self.type = self.type.upper()
176
+ if get_plugin_config("netbox_dns", "convert_names_to_lowercase", False):
177
+ self.record_name = self.record_name.lower()
178
+
179
+ super().clean_fields(exclude=exclude)
180
+
181
+ def clean(self, *args, **kwargs):
182
+ self.validate_name()
183
+ self.validate_value()
184
+
185
+ super().clean(*args, **kwargs)
186
+
187
+
188
+ @register_search
189
+ class RecordTemplateIndex(SearchIndex):
190
+ model = RecordTemplate
191
+
192
+ fields = (
193
+ ("name", 100),
194
+ ("record_name", 120),
195
+ ("value", 150),
196
+ ("type", 200),
197
+ ("description", 500),
198
+ )
@@ -1,61 +1,80 @@
1
1
  from django.db import models
2
- from django.urls import reverse
2
+ from django.utils.translation import gettext_lazy as _
3
3
 
4
4
  from netbox.models import NetBoxModel
5
5
  from netbox.search import SearchIndex, register_search
6
6
 
7
7
 
8
+ __all__ = (
9
+ "Registrar",
10
+ "RegistrarIndex",
11
+ )
12
+
13
+
8
14
  class Registrar(NetBoxModel):
15
+ class Meta:
16
+ verbose_name = _("Registrar")
17
+ verbose_name_plural = _("Registrars")
18
+
19
+ ordering = (
20
+ "name",
21
+ "iana_id",
22
+ )
23
+
24
+ def __str__(self):
25
+ return str(self.name)
26
+
9
27
  # +
10
28
  # Data fields according to https://www.icann.org/resources/pages/rdds-labeling-policy-2017-02-01-en
11
29
  # -
12
30
  name = models.CharField(
31
+ verbose_name=_("Name"),
13
32
  unique=True,
14
33
  max_length=255,
34
+ db_collation="natural_sort",
35
+ )
36
+ description = models.CharField(
37
+ verbose_name=_("Description"),
38
+ blank=True,
39
+ max_length=200,
15
40
  )
16
41
  iana_id = models.IntegerField(
17
- verbose_name="IANA ID",
42
+ verbose_name=_("IANA ID"),
18
43
  null=True,
19
44
  blank=True,
20
45
  )
21
46
  referral_url = models.URLField(
22
- verbose_name="Referral URL",
47
+ verbose_name=_("Referral URL"),
23
48
  max_length=255,
24
49
  blank=True,
25
50
  )
26
51
  whois_server = models.CharField(
27
- verbose_name="WHOIS Server",
52
+ verbose_name=_("WHOIS Server"),
28
53
  max_length=255,
29
54
  blank=True,
30
55
  )
31
56
  address = models.CharField(
57
+ verbose_name=_("Address"),
32
58
  max_length=200,
33
59
  blank=True,
34
60
  )
35
61
  abuse_email = models.EmailField(
36
- verbose_name="Abuse Email",
62
+ verbose_name=_("Abuse Email"),
37
63
  blank=True,
38
64
  )
39
65
  abuse_phone = models.CharField(
40
- verbose_name="Abuse Phone",
66
+ verbose_name=_("Abuse Phone"),
41
67
  max_length=50,
42
68
  blank=True,
43
69
  )
44
70
 
45
- def get_absolute_url(self):
46
- return reverse("plugins:netbox_dns:registrar", kwargs={"pk": self.id})
47
-
48
- def __str__(self):
49
- return str(self.name)
50
-
51
- class Meta:
52
- ordering = ("name", "iana_id")
53
-
54
71
 
55
72
  @register_search
56
73
  class RegistrarIndex(SearchIndex):
57
74
  model = Registrar
75
+
58
76
  fields = (
59
77
  ("name", 100),
60
78
  ("iana_id", 100),
79
+ ("description", 500),
61
80
  )
@@ -1,5 +1,5 @@
1
1
  from django.db import models
2
- from django.urls import reverse
2
+ from django.utils.translation import gettext_lazy as _
3
3
 
4
4
  from netbox.models import NetBoxModel
5
5
  from netbox.search import SearchIndex, register_search
@@ -7,108 +7,135 @@ from netbox.search import SearchIndex, register_search
7
7
  from taggit.managers import TaggableManager
8
8
 
9
9
 
10
- class Contact(NetBoxModel):
10
+ __all__ = (
11
+ "RegistrationContact",
12
+ "RegistrationContactIndex",
13
+ )
14
+
15
+
16
+ class RegistrationContact(NetBoxModel):
17
+ class Meta:
18
+ verbose_name = _("Registration Contact")
19
+ verbose_name_plural = _("Registration Contacts")
20
+
21
+ ordering = (
22
+ "name",
23
+ "contact_id",
24
+ )
25
+
26
+ clone_fields = (
27
+ "name",
28
+ "description",
29
+ "organization",
30
+ "street",
31
+ "city",
32
+ "state_province",
33
+ "postal_code",
34
+ "country",
35
+ "phone",
36
+ "phone_ext",
37
+ "fax",
38
+ "fax_ext",
39
+ "email",
40
+ "tags",
41
+ )
42
+
43
+ def __str__(self):
44
+ if self.name is not None:
45
+ return f"{self.contact_id} ({self.name})"
46
+
47
+ return self.contact_id
48
+
11
49
  # +
12
50
  # Data fields according to https://www.icann.org/resources/pages/rdds-labeling-policy-2017-02-01-en
13
51
  # -
14
52
  contact_id = models.CharField(
15
- verbose_name="Contact ID",
53
+ verbose_name=_("Contact ID"),
16
54
  max_length=50,
17
55
  unique=True,
56
+ db_collation="natural_sort",
18
57
  )
19
58
  name = models.CharField(
59
+ verbose_name=_("Name"),
20
60
  blank=True,
21
61
  max_length=100,
62
+ db_collation="natural_sort",
63
+ )
64
+ description = models.CharField(
65
+ verbose_name=_("Description"),
66
+ blank=True,
67
+ max_length=200,
22
68
  )
23
69
  organization = models.CharField(
70
+ verbose_name=_("Organization"),
24
71
  blank=True,
25
72
  max_length=200,
26
73
  )
27
74
  street = models.CharField(
75
+ verbose_name=_("Street"),
28
76
  blank=True,
29
- max_length=50,
77
+ max_length=255,
30
78
  )
31
79
  city = models.CharField(
80
+ verbose_name=_("City"),
32
81
  blank=True,
33
82
  max_length=50,
34
83
  )
35
84
  state_province = models.CharField(
36
- verbose_name="State/Province",
85
+ verbose_name=_("State/Province"),
37
86
  blank=True,
38
87
  max_length=255,
39
88
  )
40
89
  postal_code = models.CharField(
41
- verbose_name="Postal Code",
90
+ verbose_name=_("Postal Code"),
42
91
  blank=True,
43
92
  max_length=20,
44
93
  )
45
94
  country = models.CharField(
46
- verbose_name="Country (ISO 3166)",
95
+ verbose_name=_("Country (ISO 3166)"),
47
96
  blank=True,
48
97
  max_length=2,
49
98
  )
50
99
  phone = models.CharField(
51
- verbose_name="Phone",
100
+ verbose_name=_("Phone"),
52
101
  blank=True,
53
102
  max_length=50,
54
103
  )
55
104
  phone_ext = models.CharField(
56
- verbose_name="Phone Extension",
105
+ verbose_name=_("Phone Extension"),
57
106
  blank=True,
58
107
  max_length=50,
59
108
  )
60
109
  fax = models.CharField(
61
- verbose_name="Fax",
110
+ verbose_name=_("Fax"),
62
111
  blank=True,
63
112
  max_length=50,
64
113
  )
65
114
  fax_ext = models.CharField(
66
- verbose_name="Fax Extension",
115
+ verbose_name=_("Fax Extension"),
67
116
  blank=True,
68
117
  max_length=50,
69
118
  )
70
119
  email = models.EmailField(
71
- verbose_name="Email",
120
+ verbose_name=_("Email"),
72
121
  blank=True,
73
122
  )
74
123
 
124
+ # +
125
+ # TODO: Retained for backwards compatibility with older versions where
126
+ # 'RegistrationContact' was still ambiguously named 'Contact'.
127
+ #
128
+ # Removing it requires a data migration.
129
+ # -
75
130
  tags = TaggableManager(
76
131
  through="extras.TaggedItem",
77
132
  related_name="netbox_dns_contact_set",
78
133
  )
79
134
 
80
- clone_fields = [
81
- "name",
82
- "organization",
83
- "street",
84
- "city",
85
- "state_province",
86
- "postal_code",
87
- "country",
88
- "phone",
89
- "phone_ext",
90
- "fax",
91
- "fax_ext",
92
- "email",
93
- "tags",
94
- ]
95
-
96
- def get_absolute_url(self):
97
- return reverse("plugins:netbox_dns:contact", kwargs={"pk": self.id})
98
-
99
- def __str__(self):
100
- if self.name is not None:
101
- return f"{self.contact_id} ({self.name})"
102
-
103
- return self.contact_id
104
-
105
- class Meta:
106
- ordering = ("name", "contact_id")
107
-
108
135
  @property
109
136
  def zones(self):
110
137
  return (
111
- set(self.zone_set.all())
138
+ set(self.registrant_zones.all())
112
139
  | set(self.admin_c_zones.all())
113
140
  | set(self.tech_c_zones.all())
114
141
  | set(self.billing_c_zones.all())
@@ -116,11 +143,13 @@ class Contact(NetBoxModel):
116
143
 
117
144
 
118
145
  @register_search
119
- class ContactIndex(SearchIndex):
120
- model = Contact
146
+ class RegistrationContactIndex(SearchIndex):
147
+ model = RegistrationContact
148
+
121
149
  fields = (
122
150
  ("name", 100),
123
151
  ("contact_id", 100),
124
152
  ("email", 200),
125
153
  ("organization", 500),
154
+ ("description", 500),
126
155
  )
netbox_dns/models/view.py CHANGED
@@ -1,20 +1,73 @@
1
1
  from django.db import models
2
- from django.urls import reverse
2
+ from django.core.exceptions import ValidationError
3
+ from django.utils.translation import gettext_lazy as _
3
4
 
4
5
  from netbox.models import NetBoxModel
6
+ from netbox.models.features import ContactsMixin
5
7
  from netbox.search import SearchIndex, register_search
8
+ from netbox.context import current_request
9
+ from utilities.exceptions import AbortRequest
6
10
 
11
+ from netbox_dns.mixins import ObjectModificationMixin
12
+ from netbox_dns.utilities import (
13
+ get_ip_addresses_by_view,
14
+ check_dns_records,
15
+ update_dns_records,
16
+ delete_dns_records,
17
+ get_query_from_filter,
18
+ )
19
+
20
+
21
+ __all__ = (
22
+ "View",
23
+ "ViewIndex",
24
+ )
25
+
26
+
27
+ class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
28
+ class Meta:
29
+ verbose_name = _("View")
30
+ verbose_name_plural = _("Views")
31
+
32
+ ordering = ("name",)
33
+
34
+ clone_fields = (
35
+ "name",
36
+ "description",
37
+ "tenant",
38
+ )
39
+
40
+ def __str__(self):
41
+ return str(self.name)
7
42
 
8
- class View(NetBoxModel):
9
43
  name = models.CharField(
44
+ verbose_name=_("Name"),
10
45
  unique=True,
11
46
  max_length=255,
47
+ db_collation="natural_sort",
12
48
  )
13
49
  description = models.CharField(
50
+ verbose_name=_("Description"),
14
51
  max_length=200,
15
52
  blank=True,
16
53
  )
54
+ default_view = models.BooleanField(
55
+ verbose_name=_("Default View"),
56
+ default=False,
57
+ )
58
+ prefixes = models.ManyToManyField(
59
+ verbose_name=_("IPAM Prefixes"),
60
+ to="ipam.Prefix",
61
+ related_name="netbox_dns_views",
62
+ blank=True,
63
+ )
64
+ ip_address_filter = models.JSONField(
65
+ verbose_name=_("IP Address Filter"),
66
+ blank=True,
67
+ null=True,
68
+ )
17
69
  tenant = models.ForeignKey(
70
+ verbose_name=_("Tenant"),
18
71
  to="tenancy.Tenant",
19
72
  on_delete=models.PROTECT,
20
73
  related_name="netbox_dns_views",
@@ -22,21 +75,88 @@ class View(NetBoxModel):
22
75
  null=True,
23
76
  )
24
77
 
25
- clone_fields = ["name", "description"]
78
+ @classmethod
79
+ def get_default_view(cls):
80
+ return cls.objects.get(default_view=True)
26
81
 
27
- def get_absolute_url(self):
28
- return reverse("plugins:netbox_dns:view", kwargs={"pk": self.id})
82
+ def delete(self, *args, **kwargs):
83
+ if self.default_view:
84
+ if current_request.get() is not None:
85
+ raise AbortRequest(_("The default view cannot be deleted"))
29
86
 
30
- def __str__(self):
31
- return str(self.name)
87
+ raise ValidationError(_("The default view cannot be deleted"))
32
88
 
33
- class Meta:
34
- ordering = ("name",)
89
+ super().delete(*args, **kwargs)
90
+
91
+ def clean(self, *args, **kwargs):
92
+ if (changed_fields := self.changed_fields) is None:
93
+ return
94
+
95
+ if (
96
+ "default_view" in changed_fields
97
+ and not self.default_view
98
+ and not View.objects.filter(default_view=True).exclude(pk=self.pk).exists()
99
+ ):
100
+ raise ValidationError(
101
+ {
102
+ "default_view": _(
103
+ "Please select a different view as default view to change this setting!"
104
+ )
105
+ }
106
+ )
107
+
108
+ if "ip_address_filter" in changed_fields and self.get_saved_value(
109
+ "ip_address_filter"
110
+ ):
111
+ try:
112
+ for ip_address in get_ip_addresses_by_view(self).filter(
113
+ get_query_from_filter(self.ip_address_filter)
114
+ ):
115
+ check_dns_records(ip_address, view=self)
116
+ except ValidationError as exc:
117
+ raise ValidationError(
118
+ {
119
+ "ip_address_filter": exc.messages,
120
+ }
121
+ )
122
+
123
+ super().clean(*args, **kwargs)
124
+
125
+ def save(self, *args, **kwargs):
126
+ self.clean()
127
+
128
+ changed_fields = self.changed_fields
129
+
130
+ super().save(*args, **kwargs)
131
+
132
+ if (changed_fields is None and self.default_view) or (
133
+ changed_fields is not None
134
+ and self.default_view
135
+ and "default_view" in changed_fields
136
+ ):
137
+ other_views = View.objects.filter(default_view=True).exclude(pk=self.pk)
138
+ for view in other_views:
139
+ view.default_view = False
140
+ view.save()
141
+
142
+ if changed_fields is not None and "ip_address_filter" in changed_fields:
143
+ ip_addresses = get_ip_addresses_by_view(self)
144
+
145
+ for ip_address in ip_addresses.exclude(
146
+ get_query_from_filter(self.ip_address_filter)
147
+ ):
148
+ delete_dns_records(ip_address, view=self)
149
+
150
+ for ip_address in ip_addresses.filter(
151
+ get_query_from_filter(self.ip_address_filter)
152
+ ):
153
+ update_dns_records(ip_address, view=self)
35
154
 
36
155
 
37
156
  @register_search
38
157
  class ViewIndex(SearchIndex):
39
158
  model = View
159
+
40
160
  fields = (
41
161
  ("name", 100),
42
162
  ("description", 500),