netbox-plugin-dns 1.0.2__tar.gz → 1.0.4__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.
Files changed (116) hide show
  1. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/PKG-INFO +1 -1
  2. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/__init__.py +6 -2
  3. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/graphql/types.py +3 -4
  4. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/models/record.py +10 -5
  5. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/models/zone.py +18 -1
  6. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/navigation.py +34 -18
  7. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/templates/netbox_dns/record.html +4 -4
  8. netbox_plugin_dns-1.0.4/netbox_dns/templates/netbox_dns/zone/child_zone.html +18 -0
  9. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/templates/netbox_dns/zone.html +11 -15
  10. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/urls/zone.py +6 -0
  11. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/utilities/__init__.py +1 -1
  12. netbox_plugin_dns-1.0.4/netbox_dns/validators/dns_name.py +86 -0
  13. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/views/zone.py +20 -0
  14. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/pyproject.toml +1 -1
  15. netbox_plugin_dns-1.0.2/netbox_dns/validators/dns_name.py +0 -65
  16. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/LICENSE +0 -0
  17. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/README.md +0 -0
  18. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/api/nested_serializers.py +0 -0
  19. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/api/serializers.py +0 -0
  20. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/api/serializers_/__init__.py +0 -0
  21. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/api/serializers_/contact.py +0 -0
  22. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/api/serializers_/nameserver.py +0 -0
  23. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/api/serializers_/record.py +0 -0
  24. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/api/serializers_/registrar.py +0 -0
  25. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/api/serializers_/view.py +0 -0
  26. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/api/serializers_/zone.py +0 -0
  27. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/api/urls.py +0 -0
  28. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/api/views.py +0 -0
  29. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/apps.py +0 -0
  30. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/fields/__init__.py +0 -0
  31. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/fields/address.py +0 -0
  32. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/fields/network.py +0 -0
  33. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/fields/rfc2317.py +0 -0
  34. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/filtersets/__init__.py +0 -0
  35. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/filtersets/contact.py +0 -0
  36. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/filtersets/nameserver.py +0 -0
  37. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/filtersets/record.py +0 -0
  38. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/filtersets/registrar.py +0 -0
  39. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/filtersets/view.py +0 -0
  40. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/filtersets/zone.py +0 -0
  41. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/forms/__init__.py +0 -0
  42. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/forms/contact.py +0 -0
  43. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/forms/nameserver.py +0 -0
  44. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/forms/record.py +0 -0
  45. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/forms/registrar.py +0 -0
  46. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/forms/view.py +0 -0
  47. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/forms/zone.py +0 -0
  48. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/graphql/__init__.py +0 -0
  49. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/graphql/filters.py +0 -0
  50. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/graphql/schema.py +0 -0
  51. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/management/commands/cleanup_database.py +0 -0
  52. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/management/commands/cleanup_rrset_ttl.py +0 -0
  53. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/management/commands/setup_coupling.py +0 -0
  54. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/management/commands/update_soa.py +0 -0
  55. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/migrations/0001_squashed_netbox_dns_0_15.py +0 -0
  56. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/migrations/0001_squashed_netbox_dns_0_22.py +0 -0
  57. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/migrations/0002_contact_description_registrar_description.py +0 -0
  58. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/migrations/0003_default_view.py +0 -0
  59. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/migrations/0004_create_and_assign_default_view.py +0 -0
  60. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/migrations/0005_alter_zone_view_not_null.py +0 -0
  61. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/migrations/0020_netbox_3_4.py +0 -0
  62. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/migrations/0021_record_ip_address.py +0 -0
  63. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/migrations/0022_search.py +0 -0
  64. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/migrations/0023_alter_record_value.py +0 -0
  65. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/migrations/0024_tenancy.py +0 -0
  66. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/migrations/0025_ipam_coupling_cf.py +0 -0
  67. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/migrations/0026_domain_registration.py +0 -0
  68. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/migrations/0027_alter_registrar_iana_id.py +0 -0
  69. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/migrations/0028_rfc2317_fields.py +0 -0
  70. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/migrations/0029_record_fqdn.py +0 -0
  71. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/migrations/__init__.py +0 -0
  72. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/mixins/__init__.py +0 -0
  73. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/mixins/object_modification.py +0 -0
  74. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/models/__init__.py +0 -0
  75. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/models/contact.py +0 -0
  76. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/models/nameserver.py +0 -0
  77. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/models/registrar.py +0 -0
  78. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/models/view.py +0 -0
  79. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/signals/__init__.py +0 -0
  80. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/signals/ipam_coupling.py +0 -0
  81. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/tables/__init__.py +0 -0
  82. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/tables/contact.py +0 -0
  83. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/tables/nameserver.py +0 -0
  84. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/tables/record.py +0 -0
  85. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/tables/registrar.py +0 -0
  86. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/tables/view.py +0 -0
  87. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/tables/zone.py +0 -0
  88. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/template_content.py +0 -0
  89. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/templates/netbox_dns/contact.html +0 -0
  90. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/templates/netbox_dns/nameserver.html +0 -0
  91. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/templates/netbox_dns/record/managed.html +0 -0
  92. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/templates/netbox_dns/record/related.html +0 -0
  93. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/templates/netbox_dns/registrar.html +0 -0
  94. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/templates/netbox_dns/related_dns_objects.html +0 -0
  95. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/templates/netbox_dns/view.html +0 -0
  96. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/templates/netbox_dns/zone/base.html +0 -0
  97. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/templates/netbox_dns/zone/child.html +0 -0
  98. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/templates/netbox_dns/zone/managed_record.html +0 -0
  99. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/templates/netbox_dns/zone/record.html +0 -0
  100. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/templates/netbox_dns/zone/registration.html +0 -0
  101. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/templates/netbox_dns/zone/rfc2317_child_zone.html +0 -0
  102. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/urls/__init__.py +0 -0
  103. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/urls/contact.py +0 -0
  104. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/urls/nameserver.py +0 -0
  105. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/urls/record.py +0 -0
  106. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/urls/registrar.py +0 -0
  107. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/urls/view.py +0 -0
  108. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/utilities/ipam_coupling.py +0 -0
  109. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/validators/__init__.py +0 -0
  110. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/validators/rfc2317.py +0 -0
  111. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/views/__init__.py +0 -0
  112. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/views/contact.py +0 -0
  113. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/views/nameserver.py +0 -0
  114. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/views/record.py +0 -0
  115. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/views/registrar.py +0 -0
  116. {netbox_plugin_dns-1.0.2 → netbox_plugin_dns-1.0.4}/netbox_dns/views/view.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: netbox-plugin-dns
3
- Version: 1.0.2
3
+ Version: 1.0.4
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
@@ -1,6 +1,6 @@
1
1
  from netbox.plugins import PluginConfig
2
2
 
3
- __version__ = "1.0.2"
3
+ __version__ = "1.0.4"
4
4
 
5
5
 
6
6
  class DNSConfig(PluginConfig):
@@ -21,7 +21,9 @@ class DNSConfig(PluginConfig):
21
21
  "zone_soa_expire": 2419200,
22
22
  "zone_soa_minimum": 3600,
23
23
  "feature_ipam_coupling": False,
24
- "tolerate_underscores_in_hostnames": False,
24
+ "tolerate_characters_in_zone_labels": "",
25
+ "tolerate_underscores_in_labels": False,
26
+ "tolerate_underscores_in_hostnames": False, # Deprecated, will be removed in 1.2.0
25
27
  "tolerate_leading_underscore_types": [
26
28
  "TXT",
27
29
  "SRV",
@@ -30,6 +32,8 @@ class DNSConfig(PluginConfig):
30
32
  "enable_root_zones": False,
31
33
  "enforce_unique_records": True,
32
34
  "enforce_unique_rrset_ttl": True,
35
+ "menu_name": "NetBox DNS",
36
+ "top_level_menu": True,
33
37
  }
34
38
  base_url = "netbox-dns"
35
39
 
@@ -1,4 +1,4 @@
1
- from typing import Annotated
1
+ from typing import Annotated, List
2
2
 
3
3
  import strawberry
4
4
  import strawberry_django
@@ -37,12 +37,11 @@ class NetBoxDNSZoneType(NetBoxObjectType):
37
37
  status: str
38
38
  active: bool
39
39
  view: Annotated["NetBoxDNSViewType", strawberry.lazy("netbox_dns.graphql.types")]
40
- nameservers: (
40
+ nameservers: List[
41
41
  Annotated[
42
42
  "NetBoxDNSNameServerType", strawberry.lazy("netbox_dns.graphql.types")
43
43
  ]
44
- | None
45
- )
44
+ ]
46
45
  default_ttl: BigInt
47
46
  soa_ttl: BigInt
48
47
  soa_mname: Annotated[
@@ -22,7 +22,7 @@ from netbox_dns.utilities import (
22
22
  )
23
23
  from netbox_dns.validators import (
24
24
  validate_fqdn,
25
- validate_extended_hostname,
25
+ validate_generic_name,
26
26
  validate_domain_name,
27
27
  )
28
28
  from netbox_dns.mixins import ObjectModificationMixin
@@ -494,7 +494,7 @@ class Record(ObjectModificationMixin, NetBoxModel):
494
494
  "netbox_dns", "tolerate_non_rfc1035_types", default=[]
495
495
  ):
496
496
  try:
497
- validate_extended_hostname(
497
+ validate_generic_name(
498
498
  self.name,
499
499
  (
500
500
  self.type
@@ -541,8 +541,7 @@ class Record(ObjectModificationMixin, NetBoxModel):
541
541
  )
542
542
 
543
543
  case (
544
- RecordTypeChoices.DNAME
545
- | RecordTypeChoices.NS
544
+ RecordTypeChoices.NS
546
545
  | RecordTypeChoices.HTTPS
547
546
  | RecordTypeChoices.SRV
548
547
  | RecordTypeChoices.SVCB
@@ -550,6 +549,12 @@ class Record(ObjectModificationMixin, NetBoxModel):
550
549
  _validate_idn(rr.target)
551
550
  validate_domain_name(rr.target.to_text(), always_tolerant=True)
552
551
 
552
+ case RecordTypeChoices.DNAME:
553
+ _validate_idn(rr.target)
554
+ validate_domain_name(
555
+ rr.target.to_text(), always_tolerant=True, zone_name=True
556
+ )
557
+
553
558
  case RecordTypeChoices.PTR | RecordTypeChoices.NSAP_PTR:
554
559
  _validate_idn(rr.target)
555
560
  validate_fqdn(rr.target.to_text(), always_tolerant=True)
@@ -570,7 +575,7 @@ class Record(ObjectModificationMixin, NetBoxModel):
570
575
 
571
576
  case RecordTypeChoices.NAPTR:
572
577
  _validate_idn(rr.replacement)
573
- validate_extended_hostname(
578
+ validate_generic_name(
574
579
  rr.replacement.to_text(), always_tolerant=True
575
580
  )
576
581
 
@@ -1,3 +1,4 @@
1
+ import re
1
2
  from math import ceil
2
3
  from datetime import datetime
3
4
 
@@ -353,6 +354,22 @@ class Zone(ObjectModificationMixin, NetBoxModel):
353
354
  )
354
355
  )
355
356
 
357
+ @property
358
+ def child_zones(self):
359
+ return Zone.objects.filter(
360
+ name__iregex=rf"^[^.]+\.{re.escape(self.name)}$", view=self.view
361
+ )
362
+
363
+ @property
364
+ def parent_zone(self):
365
+ parent_name = (
366
+ dns_name.from_text(self.name).parent().relativize(dns_name.root).to_text()
367
+ )
368
+ try:
369
+ return Zone.objects.get(name=parent_name, view=self.view)
370
+ except Zone.DoesNotExist:
371
+ return None
372
+
356
373
  def record_count(self, managed=False):
357
374
  return record.Record.objects.filter(zone=self, managed=managed).count()
358
375
 
@@ -593,7 +610,7 @@ class Zone(ObjectModificationMixin, NetBoxModel):
593
610
  ) from None
594
611
 
595
612
  try:
596
- validate_domain_name(self.name)
613
+ validate_domain_name(self.name, zone_name=True)
597
614
  except ValidationError as exc:
598
615
  raise ValidationError(
599
616
  {
@@ -1,4 +1,8 @@
1
1
  from netbox.plugins import PluginMenuButton, PluginMenuItem, PluginMenu
2
+ from netbox.plugins.utils import get_plugin_config
3
+
4
+ menu_name = get_plugin_config("netbox_dns", "menu_name")
5
+ top_level_menu = get_plugin_config("netbox_dns", "top_level_menu")
2
6
 
3
7
  view_menu_item = PluginMenuItem(
4
8
  link="plugins:netbox_dns:view_list",
@@ -126,26 +130,38 @@ contact_menu_item = PluginMenuItem(
126
130
  ),
127
131
  )
128
132
 
129
- menu = PluginMenu(
130
- label="NetBox DNS",
131
- groups=(
132
- (
133
- "DNS Configuration",
133
+
134
+ if top_level_menu:
135
+ menu = PluginMenu(
136
+ label=menu_name,
137
+ groups=(
134
138
  (
135
- view_menu_item,
136
- zone_menu_item,
137
- nameserver_menu_item,
138
- record_menu_item,
139
- managed_record_menu_item,
139
+ "DNS Configuration",
140
+ (
141
+ view_menu_item,
142
+ zone_menu_item,
143
+ nameserver_menu_item,
144
+ record_menu_item,
145
+ managed_record_menu_item,
146
+ ),
140
147
  ),
141
- ),
142
- (
143
- "Domain Registration",
144
148
  (
145
- registrar_menu_item,
146
- contact_menu_item,
149
+ "Domain Registration",
150
+ (
151
+ registrar_menu_item,
152
+ contact_menu_item,
153
+ ),
147
154
  ),
148
155
  ),
149
- ),
150
- icon_class="mdi mdi-dns",
151
- )
156
+ icon_class="mdi mdi-dns",
157
+ )
158
+ else:
159
+ menu_items = (
160
+ view_menu_item,
161
+ zone_menu_item,
162
+ nameserver_menu_item,
163
+ record_menu_item,
164
+ managed_record_menu_item,
165
+ registrar_menu_item,
166
+ contact_menu_item,
167
+ )
@@ -94,25 +94,25 @@
94
94
  {% if object.ptr_record %}
95
95
  <tr>
96
96
  <th scope="row">PTR Record</th>
97
- <td><a href="{% url 'plugins:netbox_dns:record' pk=object.ptr_record.pk %}">{{ object.ptr_record }}</td>
97
+ <td>{{ object.ptr_record|linkify }}</td>
98
98
  </tr>
99
99
  {% endif %}
100
100
  {% if object.address_record %}
101
101
  <tr>
102
102
  <th scope="row">Address Record</th>
103
- <td><a href="{% url 'plugins:netbox_dns:record' pk=object.address_record.pk %}">{{ object.address_record }}</td>
103
+ <td>{{ object.address_record|linkify }}</td>
104
104
  </tr>
105
105
  {% if object.address_record.ipam_ip_address %}
106
106
  <tr>
107
107
  <th scope="row">IPAM IP Address</th>
108
- <td><a href="{% url 'ipam:ipaddress' pk=object.address_record.ipam_ip_address.pk %}">{{ object.address_record.ipam_ip_address }}</td>
108
+ <td>{{ object.address_record.ipam_ip_address|linkify }}</td>
109
109
  </tr>
110
110
  {% endif %}
111
111
  {% endif %}
112
112
  {% if object.ipam_ip_address %}
113
113
  <tr>
114
114
  <th scope="row">IPAM IP Address</th>
115
- <td><a href="{% url 'ipam:ipaddress' pk=object.ipam_ip_address.pk %}">{{ object.ipam_ip_address }}</td>
115
+ <td>{{ object.ipam_ip_address|linkify }}</td>
116
116
  </tr>
117
117
  {% endif %}
118
118
  <tr>
@@ -0,0 +1,18 @@
1
+ {% extends 'netbox_dns/zone/base.html' %}
2
+ {% load helpers %}
3
+ {% load render_table from django_tables2 %}
4
+ {% load perms %}
5
+
6
+ {% block content %}
7
+ {% include 'inc/table_controls_htmx.html' with table_modal="ChildZoneTable_config" %}
8
+ <div class="card">
9
+ <div class="htmx-container table-responsive" id="object_list">
10
+ {% include 'htmx/table.html' %}
11
+ </div>
12
+ </div>
13
+ {% endblock %}
14
+
15
+ {% block modals %}
16
+ {{ block.super }}
17
+ {% table_config_form table %}
18
+ {% endblock modals %}
@@ -20,13 +20,15 @@
20
20
  <td>{{ unicode_name }}</td>
21
21
  </tr>
22
22
  {% endif %}
23
+ {% if parent_zone %}
24
+ <tr>
25
+ <th scope="row">Parent Zone</th>
26
+ <td>{{ parent_zone|linkify }}</td>
27
+ </tr>
28
+ {% endif %}
23
29
  <tr>
24
30
  <th scope="row">View</th>
25
- <td>
26
- <a href="{% url 'plugins:netbox_dns:view' pk=object.view.pk %}">
27
- {{ object.view }}
28
- </a>
29
- </td>
31
+ <td>{{ object.view|linkify }}</td>
30
32
  </tr>
31
33
  {% if object.description %}
32
34
  <tr>
@@ -52,13 +54,7 @@
52
54
  <td>
53
55
  <table>
54
56
  {% for nameserver in object.nameservers.all %}
55
- <tr>
56
- <td>
57
- <a href="{% url 'plugins:netbox_dns:nameserver' pk=nameserver.pk %}">
58
- {{ nameserver }}
59
- </a>
60
- </td>
61
- </tr>
57
+ <tr><td>{{ nameserver|linkify }}</td></tr>
62
58
  {% endfor %}
63
59
  </table>
64
60
  </td>
@@ -115,7 +111,7 @@
115
111
  </tr>
116
112
  <tr>
117
113
  <th scope="row">MName</th>
118
- <td><a href="{% url 'plugins:netbox_dns:nameserver' pk=object.soa_mname.pk %}">{{ object.soa_mname }}</a></td>
114
+ <td>{{ object.soa_mname|linkify }}</td>
119
115
  </tr>
120
116
  <tr>
121
117
  <th scope="row">RName</th>
@@ -153,7 +149,7 @@
153
149
  {% if object.rfc2317_prefix %}
154
150
  <div class="card">
155
151
  <h5 class="card-header">RFC2317</h5>
156
- <table class="table table-hover soa-table">
152
+ <table class="table table-hover attr-table">
157
153
  <tr>
158
154
  <th scope="row">Prefix</th>
159
155
  <td>{{ object.rfc2317_prefix }}</td>
@@ -165,7 +161,7 @@
165
161
  {% if object.rfc2317_parent_managed %}
166
162
  <tr>
167
163
  <th scope="row">Parent Zone</th>
168
- <td><a href="{% url 'plugins:netbox_dns:zone' pk=object.rfc2317_parent_zone.pk %}">{{ object.rfc2317_parent_zone }}</a></td>
164
+ <td>{{ object.rfc2317_parent_zone|linkify }}</td>
169
165
  </tr>
170
166
  {% endif %}
171
167
  </table>
@@ -15,6 +15,7 @@ from netbox_dns.views import (
15
15
  ZoneManagedRecordListView,
16
16
  ZoneRegistrationView,
17
17
  ZoneRFC2317ChildZoneListView,
18
+ ZoneChildZoneListView,
18
19
  )
19
20
 
20
21
  zone_urlpatterns = [
@@ -37,6 +38,11 @@ zone_urlpatterns = [
37
38
  ZoneRFC2317ChildZoneListView.as_view(),
38
39
  name="zone_rfc2317_child_zones",
39
40
  ),
41
+ path(
42
+ "zones/<int:pk>/childzones/",
43
+ ZoneChildZoneListView.as_view(),
44
+ name="zone_child_zones",
45
+ ),
40
46
  path(
41
47
  "zones/<int:pk>/registration/",
42
48
  ZoneRegistrationView.as_view(),
@@ -21,7 +21,7 @@ def arpa_to_prefix(arpa_name):
21
21
 
22
22
  try:
23
23
  return IPNetwork(f"{address}/{mask}")
24
- except AddrFormatError:
24
+ except (AddrFormatError, ValueError):
25
25
  return None
26
26
 
27
27
  elif name.endswith("ip6.arpa"):
@@ -0,0 +1,86 @@
1
+ import re
2
+
3
+ from django.core.exceptions import ValidationError
4
+
5
+ from netbox.plugins.utils import get_plugin_config
6
+
7
+ import logging
8
+
9
+
10
+ def _get_label(tolerate_leading_underscores=False, always_tolerant=False):
11
+ tolerate_characters = re.escape(
12
+ get_plugin_config("netbox_dns", "tolerate_characters_in_zone_labels", "")
13
+ )
14
+ label_characters = rf"a-z0-9{tolerate_characters}"
15
+
16
+ if always_tolerant:
17
+ label = rf"[a-z0-9_][a-z0-9_-]*(?<![-_])"
18
+ zone_label = rf"[{label_characters}_][{label_characters}_-]*(?<![-_])"
19
+
20
+ return label, zone_label
21
+
22
+ tolerate_underscores = get_plugin_config(
23
+ "netbox_dns", "tolerate_underscores_in_labels"
24
+ ) or get_plugin_config("netbox_dns", "tolerate_underscores_in_hostnames")
25
+
26
+ if tolerate_leading_underscores:
27
+ if tolerate_underscores:
28
+ label = r"[a-z0-9_][a-z0-9_-]*(?<![-_])"
29
+ zone_label = rf"[{label_characters}_][{label_characters}_-]*(?<![-_])"
30
+ else:
31
+ label = rf"[a-z0-9_][a-z0-9-]*(?<!-)"
32
+ zone_label = rf"[{label_characters}_][{label_characters}-]*(?<!-)"
33
+ elif tolerate_underscores:
34
+ label = rf"[a-z0-9][a-z0-9_-]*(?<![-_])"
35
+ zone_label = rf"[{label_characters}][{label_characters}_-]*(?<![-_])"
36
+ else:
37
+ label = rf"[a-z0-9][a-z0-9-]*(?<!-)"
38
+ zone_label = rf"[{label_characters}][{label_characters}-]*(?<!-)"
39
+
40
+ return label, zone_label
41
+
42
+
43
+ def has_invalid_double_dash(name):
44
+ return bool(re.findall(r"\b(?!xn)..--", name, re.IGNORECASE))
45
+
46
+
47
+ def validate_fqdn(name, always_tolerant=False):
48
+ label, zone_label = _get_label(always_tolerant=always_tolerant)
49
+ regex = rf"^(\*|{label})(\.{zone_label})+\.?$"
50
+
51
+ if not re.match(regex, name, flags=re.IGNORECASE) or has_invalid_double_dash(name):
52
+ raise ValidationError(f"{name} is not a valid fully qualified DNS host name")
53
+
54
+
55
+ def validate_generic_name(
56
+ name, tolerate_leading_underscores=False, always_tolerant=False
57
+ ):
58
+ label, zone_label = _get_label(
59
+ tolerate_leading_underscores=tolerate_leading_underscores,
60
+ always_tolerant=always_tolerant,
61
+ )
62
+ regex = rf"^([*@]|(\*\.)?{label}(\.{zone_label})*\.?)$"
63
+
64
+ if not re.match(regex, name, flags=re.IGNORECASE) or has_invalid_double_dash(name):
65
+ raise ValidationError(f"{name} is not a valid DNS host name")
66
+
67
+
68
+ def validate_domain_name(
69
+ name, always_tolerant=False, allow_empty_label=False, zone_name=False
70
+ ):
71
+ if name == "@" and allow_empty_label:
72
+ return
73
+
74
+ if name == "." and (
75
+ always_tolerant or get_plugin_config("netbox_dns", "enable_root_zones")
76
+ ):
77
+ return
78
+
79
+ label, zone_label = _get_label(always_tolerant=always_tolerant)
80
+ if zone_name:
81
+ regex = rf"^{zone_label}(\.{zone_label})*\.?$"
82
+ else:
83
+ regex = rf"^{label}(\.{zone_label})*\.?$"
84
+
85
+ if not re.match(regex, name, flags=re.IGNORECASE) or has_invalid_double_dash(name):
86
+ raise ValidationError(f"{name} is not a valid DNS domain name")
@@ -40,6 +40,7 @@ class ZoneView(generic.ObjectView):
40
40
  context = {
41
41
  "nameserver_warnings": ns_warnings,
42
42
  "nameserver_errors": ns_errors,
43
+ "parent_zone": instance.parent_zone,
43
44
  }
44
45
 
45
46
  name = dns_name.from_text(instance.name)
@@ -165,3 +166,22 @@ class ZoneRFC2317ChildZoneListView(generic.ObjectChildrenView):
165
166
 
166
167
  def get_children(self, request, parent):
167
168
  return parent.rfc2317_child_zones.all()
169
+
170
+
171
+ @register_model_view(Zone, "child_zones")
172
+ class ZoneChildZoneListView(generic.ObjectChildrenView):
173
+ queryset = Zone.objects.all()
174
+ child_model = Zone
175
+ table = ZoneTable
176
+ filterset = ZoneFilterSet
177
+ template_name = "netbox_dns/zone/child_zone.html"
178
+
179
+ tab = ViewTab(
180
+ label="Child Zones",
181
+ permission="netbox_dns.view_zone",
182
+ badge=lambda obj: obj.child_zones.count(),
183
+ hide_if_empty=True,
184
+ )
185
+
186
+ def get_children(self, request, parent):
187
+ return parent.child_zones
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "netbox-plugin-dns"
3
- version = "1.0.2"
3
+ version = "1.0.4"
4
4
  description = "NetBox DNS is a NetBox plugin for managing DNS data."
5
5
  authors = ["Peter Eckel <pete@netbox-dns.org>"]
6
6
  homepage = "https://github.com/peteeckel/netbox-plugin-dns"
@@ -1,65 +0,0 @@
1
- import re
2
-
3
- from django.core.exceptions import ValidationError
4
-
5
- from netbox.plugins.utils import get_plugin_config
6
-
7
- LABEL = r"[a-z0-9][a-z0-9-]*(?<!-)"
8
- TOLERANT_LABEL = r"[a-z0-9][a-z0-9-_]*(?<![-_])"
9
- LEADING_UNDERSCORE_LABEL = r"[a-z0-9_][a-z0-9-]*(?<!-)"
10
- TOLERANT_LEADING_UNDERSCORE_LABEL = r"[a-z0-9_][a-z0-9-_]*(?<![-_])"
11
-
12
-
13
- def has_invalid_double_dash(name):
14
- return bool(re.findall(r"\b(?!xn)..--", name, re.IGNORECASE))
15
-
16
-
17
- def validate_fqdn(name, always_tolerant=False):
18
- if always_tolerant or get_plugin_config(
19
- "netbox_dns", "tolerate_underscores_in_hostnames"
20
- ):
21
- regex = rf"^(\*|{TOLERANT_LABEL})(\.{TOLERANT_LABEL})+\.?$"
22
- else:
23
- regex = rf"^(\*|{LABEL})(\.{LABEL})+\.?$"
24
-
25
- if not re.match(regex, name, flags=re.IGNORECASE) or has_invalid_double_dash(name):
26
- raise ValidationError(f"{name} is not a valid fully qualified DNS host name")
27
-
28
-
29
- def validate_extended_hostname(
30
- name, tolerate_leading_underscores=False, always_tolerant=False
31
- ):
32
- if always_tolerant or tolerate_leading_underscores:
33
- if always_tolerant or get_plugin_config(
34
- "netbox_dns", "tolerate_underscores_in_hostnames"
35
- ):
36
- regex = rf"^([*@]|(\*\.)?{TOLERANT_LEADING_UNDERSCORE_LABEL}(\.{TOLERANT_LEADING_UNDERSCORE_LABEL})*\.?)$"
37
- else:
38
- regex = rf"^([*@]|(\*\.)?{LEADING_UNDERSCORE_LABEL}(\.{LEADING_UNDERSCORE_LABEL})*\.?)$"
39
- elif get_plugin_config("netbox_dns", "tolerate_underscores_in_hostnames"):
40
- regex = rf"^([*@]|(\*\.)?{TOLERANT_LABEL}(\.{TOLERANT_LABEL})*\.?)$"
41
- else:
42
- regex = rf"^([*@]|(\*\.)?{LABEL}(\.{LABEL})*\.?)$"
43
-
44
- if not re.match(regex, name, flags=re.IGNORECASE) or has_invalid_double_dash(name):
45
- raise ValidationError(f"{name} is not a valid DNS host name")
46
-
47
-
48
- def validate_domain_name(name, always_tolerant=False, allow_empty_label=False):
49
- if name == "@" and allow_empty_label:
50
- return
51
-
52
- if name == "." and (
53
- always_tolerant or get_plugin_config("netbox_dns", "enable_root_zones")
54
- ):
55
- return
56
-
57
- if always_tolerant:
58
- regex = rf"^{TOLERANT_LEADING_UNDERSCORE_LABEL}(\.{TOLERANT_LEADING_UNDERSCORE_LABEL})*\.?$"
59
- elif get_plugin_config("netbox_dns", "tolerate_underscores_in_hostnames"):
60
- regex = rf"^{TOLERANT_LABEL}(\.{TOLERANT_LABEL})*\.?$"
61
- else:
62
- regex = rf"^{LABEL}(\.{LABEL})*\.?$"
63
-
64
- if not re.match(regex, name, flags=re.IGNORECASE) or has_invalid_double_dash(name):
65
- raise ValidationError(f"{name} is not a valid DNS domain name")