netbox-plugin-dns 1.0.7__py3-none-any.whl → 1.1.0b1__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.

Potentially problematic release.


This version of netbox-plugin-dns might be problematic. Click here for more details.

Files changed (69) hide show
  1. netbox_dns/__init__.py +21 -5
  2. netbox_dns/api/nested_serializers.py +16 -17
  3. netbox_dns/api/serializers.py +1 -0
  4. netbox_dns/api/serializers_/prefix.py +18 -0
  5. netbox_dns/api/serializers_/record.py +0 -1
  6. netbox_dns/api/serializers_/view.py +34 -2
  7. netbox_dns/api/urls.py +3 -0
  8. netbox_dns/api/views.py +36 -0
  9. netbox_dns/fields/__init__.py +1 -0
  10. netbox_dns/fields/ipam.py +18 -0
  11. netbox_dns/filtersets/record.py +1 -1
  12. netbox_dns/filtersets/view.py +16 -0
  13. netbox_dns/forms/view.py +116 -4
  14. netbox_dns/forms/zone.py +0 -8
  15. netbox_dns/graphql/schema.py +40 -16
  16. netbox_dns/graphql/types.py +1 -0
  17. netbox_dns/management/commands/setup_autodns.py +120 -0
  18. netbox_dns/migrations/0007_view_prefixes.py +18 -0
  19. netbox_dns/models/__init__.py +0 -2
  20. netbox_dns/models/contact.py +3 -9
  21. netbox_dns/models/nameserver.py +3 -8
  22. netbox_dns/models/record.py +71 -17
  23. netbox_dns/models/record_template.py +1 -4
  24. netbox_dns/models/registrar.py +1 -7
  25. netbox_dns/models/view.py +7 -9
  26. netbox_dns/models/zone.py +28 -29
  27. netbox_dns/models/zone_template.py +5 -8
  28. netbox_dns/signals/ipam_autodns.py +138 -0
  29. netbox_dns/tables/contact.py +1 -0
  30. netbox_dns/tables/nameserver.py +7 -1
  31. netbox_dns/tables/record.py +30 -10
  32. netbox_dns/tables/record_template.py +17 -0
  33. netbox_dns/tables/registrar.py +2 -0
  34. netbox_dns/tables/view.py +32 -3
  35. netbox_dns/tables/zone.py +15 -0
  36. netbox_dns/tables/zone_template.py +16 -2
  37. netbox_dns/template_content.py +28 -39
  38. netbox_dns/templates/netbox_dns/record.html +6 -6
  39. netbox_dns/templates/netbox_dns/view/related.html +17 -0
  40. netbox_dns/templates/netbox_dns/view.html +29 -0
  41. netbox_dns/urls/contact.py +32 -10
  42. netbox_dns/urls/nameserver.py +38 -14
  43. netbox_dns/urls/record.py +19 -7
  44. netbox_dns/urls/record_template.py +27 -18
  45. netbox_dns/urls/registrar.py +35 -11
  46. netbox_dns/urls/view.py +22 -8
  47. netbox_dns/urls/zone.py +46 -8
  48. netbox_dns/urls/zone_template.py +26 -16
  49. netbox_dns/utilities/__init__.py +2 -74
  50. netbox_dns/utilities/conversions.py +83 -0
  51. netbox_dns/utilities/ipam_autodns.py +205 -0
  52. netbox_dns/validators/dns_name.py +0 -9
  53. netbox_dns/views/contact.py +1 -0
  54. netbox_dns/views/nameserver.py +3 -7
  55. netbox_dns/views/record.py +2 -9
  56. netbox_dns/views/record_template.py +1 -1
  57. netbox_dns/views/registrar.py +1 -0
  58. netbox_dns/views/view.py +1 -6
  59. netbox_dns/views/zone.py +6 -7
  60. netbox_dns/views/zone_template.py +2 -2
  61. {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0b1.dist-info}/METADATA +2 -2
  62. {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0b1.dist-info}/RECORD +64 -61
  63. netbox_dns/management/commands/setup_coupling.py +0 -109
  64. netbox_dns/migrations/0007_alter_ordering_options.py +0 -25
  65. netbox_dns/signals/ipam_coupling.py +0 -168
  66. netbox_dns/templates/netbox_dns/related_dns_objects.html +0 -21
  67. netbox_dns/utilities/ipam_coupling.py +0 -112
  68. {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0b1.dist-info}/LICENSE +0 -0
  69. {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0b1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,138 @@
1
+ from netaddr import IPNetwork
2
+
3
+ from django.dispatch import receiver
4
+ from django.db.models.signals import pre_delete, pre_save, post_save, m2m_changed
5
+ from django.core.exceptions import ValidationError
6
+
7
+ from netbox.context import current_request
8
+ from netbox.signals import post_clean
9
+ from ipam.models import IPAddress, Prefix
10
+ from utilities.exceptions import AbortRequest
11
+
12
+ from netbox_dns.models import view as _view
13
+ from netbox_dns.utilities import (
14
+ update_dns_records,
15
+ delete_dns_records,
16
+ get_views_by_prefix,
17
+ get_ip_addresses_by_prefix,
18
+ get_ip_addresses_by_view,
19
+ )
20
+
21
+
22
+ @receiver(post_clean, sender=IPAddress)
23
+ def ipam_autodns_ipaddress_post_clean(instance, **kwargs):
24
+ if not isinstance(instance.address, IPNetwork):
25
+ return
26
+
27
+ try:
28
+ update_dns_records(instance, commit=False)
29
+ except ValidationError as exc:
30
+ if hasattr(exc, "error_dict"):
31
+ for field in ("name", "ttl", "value", "type"):
32
+ value = exc.error_dict.pop(field, None)
33
+ if value is not None:
34
+ raise ValidationError({"dns_name": value})
35
+
36
+ raise exc
37
+
38
+
39
+ @receiver(pre_delete, sender=IPAddress)
40
+ def ipam_autodns_ipaddress_pre_delete(instance, **kwargs):
41
+ delete_dns_records(instance)
42
+
43
+
44
+ @receiver(pre_save, sender=IPAddress)
45
+ def ipam_autodns_ipaddress_post_save(instance, **kwargs):
46
+ update_dns_records(instance, commit=False)
47
+
48
+
49
+ @receiver(post_save, sender=IPAddress)
50
+ def ipam_autodns_ipaddress_post_save(instance, **kwargs):
51
+ update_dns_records(instance)
52
+
53
+
54
+ @receiver(pre_save, sender=Prefix)
55
+ def ipam_autodns_prefix_pre_save(instance, **kwargs):
56
+ """
57
+ Changes that modify the prefix hierarchy cannot be validated properly before
58
+ commiting them. So the solution in this case is to remove a prefix whose
59
+ VRF or network has changed from all views it currently is assigned to.
60
+ """
61
+ if instance.pk is None or not instance.netbox_dns_views.exists():
62
+ return
63
+
64
+ saved_prefix = Prefix.objects.get(pk=instance.pk)
65
+ if saved_prefix.prefix != instance.prefix or saved_prefix.vrf != instance.vrf:
66
+ for view in saved_prefix.netbox_dns_views.all():
67
+ view.prefixes.remove(saved_prefix)
68
+
69
+
70
+ @receiver(pre_delete, sender=Prefix)
71
+ def ipam_autodns_prefix_pre_delete(instance, **kwargs):
72
+ parent = instance.get_parents().last()
73
+ request = current_request.get()
74
+
75
+ if parent is not None and get_views_by_prefix(instance) != get_views_by_prefix(
76
+ parent
77
+ ):
78
+ try:
79
+ for prefix in instance.get_children().filter(
80
+ _depth=instance.depth + 1, netbox_dns_views__isnull=True
81
+ ):
82
+ for ip_address in get_ip_addresses_by_prefix(prefix):
83
+ update_dns_records(ip_address, commit=False)
84
+ except ValidationError as exc:
85
+ if request is not None:
86
+ raise AbortRequest(
87
+ f"Prefix deletion would cause DNS errors: {exc.messages[0]} "
88
+ "Please review DNS View assignments for this and the parent prefix"
89
+ )
90
+ else:
91
+ raise exc
92
+
93
+ # +
94
+ # CAUTION: This only works because the NetBox workaround for an ancient
95
+ # Django bug (see https://code.djangoproject.com/ticket/17688) has already
96
+ # removed the relations between the prefix and the views when this signal
97
+ # handler runs.
98
+ #
99
+ # Should anything be fixed, this code will stop working and need to be
100
+ # revisited.
101
+ #
102
+ # The NetBox workaround only works for requests, not for model level
103
+ # operations. The following code replicates it for non-requests.
104
+ # -
105
+ if request is None:
106
+ for view in instance.netbox_dns_views.all():
107
+ view.snapshot()
108
+ view.prefixes.remove(instance)
109
+
110
+ for ip_address in get_ip_addresses_by_prefix(instance):
111
+ update_dns_records(ip_address)
112
+
113
+
114
+ @receiver(m2m_changed, sender=_view.View.prefixes.through)
115
+ def ipam_autodns_view_prefix_changed(**kwargs):
116
+ action = kwargs.get("action")
117
+ request = current_request.get()
118
+
119
+ # +
120
+ # Handle all post_add and post_remove signals except the ones directly
121
+ # handled by the pre_delete handler for the Prefix model.
122
+ #
123
+ # Yes. This IS ugly.
124
+ # -
125
+ if action not in ("post_add", "post_remove") or (
126
+ request is not None
127
+ and action == "post_remove"
128
+ and (
129
+ request.path.startswith("/ipam/prefixes/")
130
+ or request.path.startswith("/api/ipam/prefixes/")
131
+ )
132
+ ):
133
+ return
134
+
135
+ check_view = action != "post_remove"
136
+ for prefix in Prefix.objects.filter(pk__in=kwargs.get("pk_set")):
137
+ for ip_address in get_ip_addresses_by_prefix(prefix, check_view=check_view):
138
+ update_dns_records(ip_address)
@@ -19,6 +19,7 @@ class ContactTable(NetBoxTable):
19
19
  class Meta(NetBoxTable.Meta):
20
20
  model = Contact
21
21
  fields = (
22
+ "contact_id",
22
23
  "name",
23
24
  "description",
24
25
  "organization",
@@ -22,7 +22,13 @@ class NameServerTable(TenancyColumnsMixin, NetBoxTable):
22
22
 
23
23
  class Meta(NetBoxTable.Meta):
24
24
  model = NameServer
25
- fields = ("description",)
25
+ fields = (
26
+ "name",
27
+ "description",
28
+ "tags",
29
+ "tenant",
30
+ "tenant_group",
31
+ )
26
32
  default_columns = (
27
33
  "name",
28
34
  "tags",
@@ -23,18 +23,10 @@ class RecordBaseTable(TenancyColumnsMixin, NetBoxTable):
23
23
  zone = tables.Column(
24
24
  linkify=True,
25
25
  )
26
- view = tables.Column(
27
- accessor="zone__view",
28
- linkify=True,
29
- )
30
26
  type = tables.Column()
31
27
  name = tables.Column(
32
28
  linkify=True,
33
29
  )
34
- fqdn = tables.Column(
35
- verbose_name="FQDN",
36
- linkify=True,
37
- )
38
30
  value = tables.TemplateColumn(
39
31
  template_code="{{ value|truncatechars:64 }}",
40
32
  )
@@ -73,8 +65,20 @@ class RecordTable(RecordBaseTable):
73
65
  class Meta(NetBoxTable.Meta):
74
66
  model = Record
75
67
  fields = (
68
+ "name",
69
+ "zone",
70
+ "ttl",
71
+ "type",
72
+ "value",
73
+ "unicode_value",
76
74
  "status",
75
+ "disable_ptr",
76
+ "ptr_record",
77
+ "tags",
78
+ "active",
77
79
  "description",
80
+ "tenant",
81
+ "tenant_group",
78
82
  )
79
83
  default_columns = (
80
84
  "name",
@@ -100,7 +104,17 @@ class ManagedRecordTable(RecordBaseTable):
100
104
 
101
105
  class Meta(NetBoxTable.Meta):
102
106
  model = Record
103
- fields = ()
107
+ fields = (
108
+ "name",
109
+ "zone",
110
+ "ttl",
111
+ "type",
112
+ "value",
113
+ "unicode_value",
114
+ "address_record",
115
+ "ipam_ip_address",
116
+ "active",
117
+ )
104
118
  default_columns = (
105
119
  "name",
106
120
  "zone",
@@ -116,7 +130,13 @@ class RelatedRecordTable(RecordBaseTable):
116
130
 
117
131
  class Meta(NetBoxTable.Meta):
118
132
  model = Record
119
- fields = ()
133
+ fields = (
134
+ "name",
135
+ "zone",
136
+ "type",
137
+ "value",
138
+ "unicode_value",
139
+ )
120
140
  default_columns = (
121
141
  "name",
122
142
  "zone",
@@ -43,8 +43,18 @@ class RecordTemplateTable(TenancyColumnsMixin, NetBoxTable):
43
43
  class Meta(NetBoxTable.Meta):
44
44
  model = RecordTemplate
45
45
  fields = (
46
+ "name",
47
+ "record_name",
48
+ "ttl",
49
+ "type",
50
+ "value",
51
+ "unicode_value",
46
52
  "status",
53
+ "disable_ptr",
54
+ "tags",
47
55
  "description",
56
+ "tenant",
57
+ "tenant_group",
48
58
  )
49
59
  default_columns = (
50
60
  "name",
@@ -62,7 +72,14 @@ class RecordTemplateDisplayTable(RecordTemplateTable):
62
72
  class Meta(NetBoxTable.Meta):
63
73
  model = RecordTemplate
64
74
  fields = (
75
+ "name",
76
+ "record_name",
77
+ "ttl",
78
+ "type",
79
+ "value",
80
+ "unicode_value",
65
81
  "status",
82
+ "disable_ptr",
66
83
  "description",
67
84
  )
68
85
  default_columns = (
@@ -19,11 +19,13 @@ class RegistrarTable(NetBoxTable):
19
19
  class Meta(NetBoxTable.Meta):
20
20
  model = Registrar
21
21
  fields = (
22
+ "name",
22
23
  "description",
23
24
  "iana_id",
24
25
  "referral_url",
25
26
  "whois_server",
26
27
  "abuse_email",
27
28
  "abuse_phone",
29
+ "tags",
28
30
  )
29
31
  default_columns = ("name", "iana_id", "referral_url")
netbox_dns/tables/view.py CHANGED
@@ -1,12 +1,15 @@
1
1
  import django_tables2 as tables
2
2
 
3
- from netbox.tables import NetBoxTable, TagColumn
3
+ from netbox.tables import NetBoxTable, TagColumn, ActionsColumn
4
4
  from tenancy.tables import TenancyColumnsMixin
5
5
 
6
6
  from netbox_dns.models import View
7
7
 
8
8
 
9
- __all__ = ("ViewTable",)
9
+ __all__ = (
10
+ "ViewTable",
11
+ "RelatedViewTable",
12
+ )
10
13
 
11
14
 
12
15
  class ViewTable(TenancyColumnsMixin, NetBoxTable):
@@ -20,5 +23,31 @@ class ViewTable(TenancyColumnsMixin, NetBoxTable):
20
23
 
21
24
  class Meta(NetBoxTable.Meta):
22
25
  model = View
23
- fields = ("description",)
26
+ fields = (
27
+ "name",
28
+ "default_view",
29
+ "description",
30
+ "tenant",
31
+ "tenant_group",
32
+ "tags",
33
+ )
24
34
  default_columns = ("name", "default_view")
35
+
36
+
37
+ class RelatedViewTable(TenancyColumnsMixin, NetBoxTable):
38
+ actions = ActionsColumn(actions=())
39
+
40
+ name = tables.Column(
41
+ linkify=True,
42
+ )
43
+
44
+ class Meta(NetBoxTable.Meta):
45
+ model = View
46
+ fields = (
47
+ "name",
48
+ "description",
49
+ "tenant",
50
+ "tenant_group",
51
+ "tags",
52
+ )
53
+ default_columns = ("name", "description")
netbox_dns/tables/zone.py CHANGED
@@ -58,11 +58,26 @@ class ZoneTable(TenancyColumnsMixin, NetBoxTable):
58
58
  class Meta(NetBoxTable.Meta):
59
59
  model = Zone
60
60
  fields = (
61
+ "name",
62
+ "view",
63
+ "status",
61
64
  "description",
65
+ "tags",
66
+ "default_ttl",
67
+ "soa_mname",
62
68
  "soa_rname",
63
69
  "soa_serial",
70
+ "rfc2317_prefix",
64
71
  "rfc2317_parent_managed",
72
+ "rfc2317_parent_zone",
73
+ "registrar",
65
74
  "registry_domain_id",
75
+ "registrant",
76
+ "admin_c",
77
+ "tech_c",
78
+ "billing_c",
79
+ "tenant",
80
+ "tenant_group",
66
81
  )
67
82
  default_columns = (
68
83
  "name",
@@ -37,7 +37,18 @@ class ZoneTemplateTable(TenancyColumnsMixin, NetBoxTable):
37
37
 
38
38
  class Meta(NetBoxTable.Meta):
39
39
  model = ZoneTemplate
40
- fields = ("description",)
40
+ fields = (
41
+ "name",
42
+ "description",
43
+ "tags",
44
+ "registrar",
45
+ "registrant",
46
+ "admin_c",
47
+ "tech_c",
48
+ "billing_c",
49
+ "tenant",
50
+ "tenant_group",
51
+ )
41
52
  default_columns = (
42
53
  "name",
43
54
  "tags",
@@ -49,7 +60,10 @@ class ZoneTemplateDisplayTable(ZoneTemplateTable):
49
60
 
50
61
  class Meta(NetBoxTable.Meta):
51
62
  model = ZoneTemplate
52
- fields = ("description",)
63
+ fields = (
64
+ "name",
65
+ "description",
66
+ )
53
67
  default_columns = (
54
68
  "name",
55
69
  "description",
@@ -1,9 +1,12 @@
1
+ from django.conf import settings
2
+
1
3
  from netbox.plugins.utils import get_plugin_config
2
4
  from netbox.plugins import PluginTemplateExtension
3
5
 
4
6
  from netbox_dns.models import Record, Zone, View, NameServer
5
7
  from netbox_dns.choices import RecordTypeChoices
6
- from netbox_dns.tables import RelatedRecordTable
8
+ from netbox_dns.tables import RelatedRecordTable, RelatedViewTable
9
+ from netbox_dns.utilities import get_views_by_prefix
7
10
 
8
11
 
9
12
  class RelatedDNSRecords(PluginTemplateExtension):
@@ -42,6 +45,25 @@ class RelatedDNSRecords(PluginTemplateExtension):
42
45
  )
43
46
 
44
47
 
48
+ class RelatedDNSViews(PluginTemplateExtension):
49
+ model = "ipam.prefix"
50
+
51
+ def right_page(self):
52
+ prefix = self.context.get("object")
53
+
54
+ if assigned_views := prefix.netbox_dns_views.all():
55
+ context = {"assigned_views": RelatedViewTable(data=assigned_views)}
56
+ elif inherited_views := get_views_by_prefix(prefix):
57
+ context = {"inherited_views": RelatedViewTable(data=inherited_views)}
58
+ else:
59
+ context = {}
60
+
61
+ return self.render(
62
+ "netbox_dns/view/related.html",
63
+ extra_context=context,
64
+ )
65
+
66
+
45
67
  class IPRelatedDNSRecords(PluginTemplateExtension):
46
68
  model = "ipam.ipaddress"
47
69
 
@@ -80,43 +102,10 @@ class IPRelatedDNSRecords(PluginTemplateExtension):
80
102
  )
81
103
 
82
104
 
83
- class RelatedDNSObjects(PluginTemplateExtension):
84
- model = "tenancy.tenant"
85
-
86
- def left_page(self):
87
- obj = self.context.get("object")
88
- request = self.context.get("request")
89
-
90
- related_dns_models = (
91
- (
92
- View.objects.restrict(request.user, "view").filter(tenant=obj),
93
- "tenant_id",
94
- ),
95
- (
96
- NameServer.objects.restrict(request.user, "view").filter(tenant=obj),
97
- "tenant_id",
98
- ),
99
- (
100
- Zone.objects.restrict(request.user, "view").filter(tenant=obj),
101
- "tenant_id",
102
- ),
103
- (
104
- Record.objects.restrict(request.user, "view").filter(tenant=obj),
105
- "tenant_id",
106
- ),
107
- )
108
-
109
- return self.render(
110
- "netbox_dns/related_dns_objects.html",
111
- extra_context={
112
- "related_dns_models": related_dns_models,
113
- },
114
- )
115
-
116
-
117
- template_extensions = []
105
+ if not settings.PLUGINS_CONFIG["netbox_dns"].get("autodns_disabled"):
106
+ template_extensions = [RelatedDNSRecords, RelatedDNSViews]
107
+ else:
108
+ template_extensions = []
118
109
 
119
- if get_plugin_config("netbox_dns", "feature_ipam_coupling"):
120
- template_extensions.append(RelatedDNSRecords)
121
- elif get_plugin_config("netbox_dns", "feature_ipam_dns_info"):
110
+ if get_plugin_config("netbox_dns", "feature_ipam_dns_info"):
122
111
  template_extensions.append(IPRelatedDNSRecords)
@@ -102,12 +102,12 @@
102
102
  <th scope="row">Address Record</th>
103
103
  <td>{{ object.address_record|linkify }}</td>
104
104
  </tr>
105
- {% if object.address_record.ipam_ip_address %}
106
- <tr>
107
- <th scope="row">IPAM IP Address</th>
108
- <td>{{ object.address_record.ipam_ip_address|linkify }}</td>
109
- </tr>
110
- {% endif %}
105
+ {% if object.address_record.ipam_ip_address %}
106
+ <tr>
107
+ <th scope="row">IPAM IP Address</th>
108
+ <td>{{ object.address_record.ipam_ip_address|linkify }}</td>
109
+ </tr>
110
+ {% endif %}
111
111
  {% endif %}
112
112
  {% if object.ipam_ip_address %}
113
113
  <tr>
@@ -0,0 +1,17 @@
1
+ {% load perms %}
2
+
3
+ {% if perms.netbox_dns.view_view %}
4
+ {% if assigned_views %}
5
+ {% if assigned_views.rows|length == 1 %}
6
+ {% include 'inc/panel_table.html' with table=assigned_views heading='Assigned DNS View' %}
7
+ {% else %}
8
+ {% include 'inc/panel_table.html' with table=assigned_views heading='Assigned DNS Views' %}
9
+ {% endif %}
10
+ {% elif inherited_views %}
11
+ {% if inherited_views.rows|length == 1 %}
12
+ {% include 'inc/panel_table.html' with table=inherited_views heading='Inherited DNS View' %}
13
+ {% else %}
14
+ {% include 'inc/panel_table.html' with table=inherited_views heading='Inherited DNS Views' %}
15
+ {% endif %}
16
+ {% endif %}
17
+ {% endif %}
@@ -35,6 +35,35 @@
35
35
  </div>
36
36
  <div class="col col-md-6">
37
37
  {% include 'inc/panels/tags.html' %}
38
+ {% if not settings.PLUGINS_CONFIG.netbox_dns.autodns_disabled %}
39
+ <div class="card">
40
+ <h5 class="card-header">
41
+ IPAM Prefixes
42
+ </h5>
43
+ <div class="card-body">
44
+ <table class="table table-hover attr-table">
45
+ {% for prefix in object.prefixes.all %}
46
+ <tr>
47
+ <td>
48
+ <a href="{% url 'ipam:prefix' pk=prefix.pk %}">
49
+ {{ prefix }}
50
+ </a>
51
+ </td>
52
+ {% if prefix.vrf %}
53
+ <td>
54
+ <a href="{% url 'ipam:vrf' pk=prefix.vrf.pk %}">
55
+ {{ prefix.vrf }}
56
+ </a>
57
+ </td>
58
+ {% else %}
59
+ <td>Global</td>
60
+ {% endif %}
61
+ </tr>
62
+ {% endfor %}
63
+ </table>
64
+ </div>
65
+ </div>
66
+ {% endif %}
38
67
  </div>
39
68
  </div>
40
69
  {% endblock %}
@@ -1,29 +1,51 @@
1
- from django.urls import include, path
1
+ from django.urls import path
2
2
 
3
- from utilities.urls import get_model_urls
3
+ from netbox.views.generic import ObjectChangeLogView, ObjectJournalView
4
4
 
5
+ from netbox_dns.models import Contact
5
6
  from netbox_dns.views import (
6
- ContactView,
7
7
  ContactListView,
8
- ContactEditView,
8
+ ContactView,
9
9
  ContactDeleteView,
10
+ ContactEditView,
10
11
  ContactBulkImportView,
11
12
  ContactBulkEditView,
12
13
  ContactBulkDeleteView,
14
+ ContactZoneListView,
13
15
  )
14
16
 
15
17
  contact_urlpatterns = [
16
- path("contacts/<int:pk>/", ContactView.as_view(), name="contact"),
17
18
  path("contacts/", ContactListView.as_view(), name="contact_list"),
18
19
  path("contacts/add/", ContactEditView.as_view(), name="contact_add"),
20
+ path("contacts/import/", ContactBulkImportView.as_view(), name="contact_import"),
21
+ path("contacts/edit/", ContactBulkEditView.as_view(), name="contact_bulk_edit"),
22
+ path(
23
+ "contacts/delete/",
24
+ ContactBulkDeleteView.as_view(),
25
+ name="contact_bulk_delete",
26
+ ),
27
+ path("contacts/<int:pk>/", ContactView.as_view(), name="contact"),
19
28
  path("contacts/<int:pk>/edit/", ContactEditView.as_view(), name="contact_edit"),
20
29
  path(
21
- "contacts/<int:pk>/delete/", ContactDeleteView.as_view(), name="contact_delete"
30
+ "contacts/<int:pk>/delete/",
31
+ ContactDeleteView.as_view(),
32
+ name="contact_delete",
33
+ ),
34
+ path(
35
+ "contacts/<int:pk>/zones/",
36
+ ContactZoneListView.as_view(),
37
+ name="contact_zones",
38
+ ),
39
+ path(
40
+ "contacts/<int:pk>/journal/",
41
+ ObjectJournalView.as_view(),
42
+ name="contact_journal",
43
+ kwargs={"model": Contact},
22
44
  ),
23
- path("contacts/import/", ContactBulkImportView.as_view(), name="contact_import"),
24
- path("contacts/edit/", ContactBulkEditView.as_view(), name="contact_bulk_edit"),
25
45
  path(
26
- "contacts/delete/", ContactBulkDeleteView.as_view(), name="contact_bulk_delete"
46
+ "contacts/<int:pk>/changelog/",
47
+ ObjectChangeLogView.as_view(),
48
+ name="contact_changelog",
49
+ kwargs={"model": Contact},
27
50
  ),
28
- path("contacts/<int:pk>/", include(get_model_urls("netbox_dns", "contact"))),
29
51
  ]