netbox-ddns 1.3.0__tar.gz → 1.5.0__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 (61) hide show
  1. {netbox_ddns-1.3.0/netbox_ddns.egg-info → netbox_ddns-1.5.0}/PKG-INFO +2 -2
  2. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns/__init__.py +2 -2
  3. netbox_ddns-1.5.0/netbox_ddns/api/__init__.py +0 -0
  4. netbox_ddns-1.5.0/netbox_ddns/api/serializers.py +35 -0
  5. netbox_ddns-1.5.0/netbox_ddns/api/urls.py +10 -0
  6. netbox_ddns-1.5.0/netbox_ddns/api/views.py +11 -0
  7. netbox_ddns-1.5.0/netbox_ddns/filtersets.py +30 -0
  8. netbox_ddns-1.5.0/netbox_ddns/forms.py +60 -0
  9. netbox_ddns-1.5.0/netbox_ddns/migrations/0009_alter_dnsstatus_id_alter_extradnsname_id_and_more.py +38 -0
  10. netbox_ddns-1.5.0/netbox_ddns/migrations/0010_extradnsname_created_extradnsname_custom_field_data_and_more.py +36 -0
  11. netbox_ddns-1.5.0/netbox_ddns/migrations/0011_server_created_server_custom_field_data_and_more.py +36 -0
  12. netbox_ddns-1.5.0/netbox_ddns/migrations/0012_zone_created_zone_custom_field_data_and_more.py +36 -0
  13. netbox_ddns-1.5.0/netbox_ddns/migrations/0013_reversezone_created_reversezone_custom_field_data_and_more.py +36 -0
  14. netbox_ddns-1.5.0/netbox_ddns/migrations/__init__.py +0 -0
  15. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns/models.py +54 -14
  16. netbox_ddns-1.5.0/netbox_ddns/navigation.py +58 -0
  17. netbox_ddns-1.5.0/netbox_ddns/search.py +10 -0
  18. netbox_ddns-1.5.0/netbox_ddns/tables.py +51 -0
  19. netbox_ddns-1.5.0/netbox_ddns/template_content.py +65 -0
  20. netbox_ddns-1.5.0/netbox_ddns/templates/netbox_ddns/extradnsname.html +61 -0
  21. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns/templates/netbox_ddns/ipaddress/dns_extra.html +2 -2
  22. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns/templates/netbox_ddns/ipaddress/dns_refresh_button.html +1 -1
  23. netbox_ddns-1.5.0/netbox_ddns/templates/netbox_ddns/reversezone.html +51 -0
  24. netbox_ddns-1.5.0/netbox_ddns/templates/netbox_ddns/server.html +68 -0
  25. netbox_ddns-1.5.0/netbox_ddns/templates/netbox_ddns/update_reverse_zone.html +11 -0
  26. netbox_ddns-1.5.0/netbox_ddns/templates/netbox_ddns/update_zone.html +11 -0
  27. netbox_ddns-1.5.0/netbox_ddns/templates/netbox_ddns/zone.html +53 -0
  28. netbox_ddns-1.5.0/netbox_ddns/urls.py +34 -0
  29. netbox_ddns-1.5.0/netbox_ddns/views.py +321 -0
  30. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0/netbox_ddns.egg-info}/PKG-INFO +2 -2
  31. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns.egg-info/SOURCES.txt +18 -0
  32. netbox_ddns-1.3.0/netbox_ddns/admin.py +0 -156
  33. netbox_ddns-1.3.0/netbox_ddns/forms.py +0 -9
  34. netbox_ddns-1.3.0/netbox_ddns/tables.py +0 -50
  35. netbox_ddns-1.3.0/netbox_ddns/template_content.py +0 -37
  36. netbox_ddns-1.3.0/netbox_ddns/urls.py +0 -18
  37. netbox_ddns-1.3.0/netbox_ddns/views.py +0 -114
  38. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/LICENSE.txt +0 -0
  39. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/MANIFEST.in +0 -0
  40. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/README.md +0 -0
  41. /netbox_ddns-1.3.0/netbox_ddns/migrations/__init__.py → /netbox_ddns-1.5.0/netbox_ddns/admin.py +0 -0
  42. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns/background_tasks.py +0 -0
  43. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns/migrations/0001_initial.py +0 -0
  44. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns/migrations/0002_add_ttl.py +0 -0
  45. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns/migrations/0003_dnsstatus.py +0 -0
  46. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns/migrations/0004_ensure_trailing_dot.py +0 -0
  47. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns/migrations/0005_extradnsname.py +0 -0
  48. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns/migrations/0006_extradns_cname.py +0 -0
  49. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns/migrations/0007_zone_meta.py +0 -0
  50. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns/migrations/0008_server_server_port.py +0 -0
  51. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns/signals.py +0 -0
  52. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns/templates/netbox_ddns/ipaddress/dns_info.html +0 -0
  53. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns/utils.py +0 -0
  54. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns/validators.py +0 -0
  55. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns.egg-info/dependency_links.txt +0 -0
  56. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns.egg-info/not-zip-safe +0 -0
  57. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns.egg-info/requires.txt +0 -0
  58. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/netbox_ddns.egg-info/top_level.txt +0 -0
  59. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/pyproject.toml +0 -0
  60. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/setup.cfg +0 -0
  61. {netbox_ddns-1.3.0 → netbox_ddns-1.5.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: netbox-ddns
3
- Version: 1.3.0
3
+ Version: 1.5.0
4
4
  Summary: Dynamic DNS Connector for NetBox
5
5
  Home-page: https://github.com/sjm-steffann/netbox-ddns
6
6
  Author: Sander Steffann
@@ -1,4 +1,4 @@
1
- VERSION = '1.3.0'
1
+ VERSION = '1.5.0'
2
2
 
3
3
  try:
4
4
  from netbox.plugins import PluginConfig
@@ -13,7 +13,7 @@ class NetBoxDDNSConfig(PluginConfig):
13
13
  verbose_name = 'Dynamic DNS'
14
14
  version = VERSION
15
15
  min_version = '4.0.0'
16
- max_version = '4.0.999'
16
+ max_version = '4.2.999'
17
17
  author = 'Sander Steffann'
18
18
  author_email = 'sander@steffann.nl'
19
19
  description = 'Dynamic DNS Connector for NetBox'
File without changes
@@ -0,0 +1,35 @@
1
+ from rest_framework import serializers
2
+ from rest_framework.relations import PrimaryKeyRelatedField
3
+ from ipam.models import IPAddress
4
+ from netbox.api.serializers import NetBoxModelSerializer
5
+ from ..models import ExtraDNSName, Server, Zone, ReverseZone
6
+
7
+
8
+ class ExtraDNSNameSerializer(NetBoxModelSerializer):
9
+ url = serializers.HyperlinkedIdentityField(
10
+ view_name='plugins-api:netbox_ddns-api:extradnsname-detail'
11
+ )
12
+ ip_address = PrimaryKeyRelatedField(queryset=IPAddress.objects.all())
13
+
14
+ class Meta:
15
+ model = ExtraDNSName
16
+ fields = ('id', 'ip_address', 'name', 'url')
17
+ read_only_fields = ('id', 'url')
18
+
19
+
20
+ class ServerSerializer(NetBoxModelSerializer):
21
+ class Meta:
22
+ model = Server
23
+ fields = ('server', 'server_port', 'tsig_key_name', 'tsig_algorithm', "tsig_key")
24
+
25
+
26
+ class ZoneSerializer(NetBoxModelSerializer):
27
+ class Meta:
28
+ model = Zone
29
+ fields = ('name', 'ttl', 'server')
30
+
31
+
32
+ class ReverseZoneSerializer(NetBoxModelSerializer):
33
+ class Meta:
34
+ model = ReverseZone
35
+ fields = ('name', 'prefix', 'ttl', 'server')
@@ -0,0 +1,10 @@
1
+ from netbox.api.routers import NetBoxRouter
2
+ from . import views
3
+
4
+
5
+ app_name = 'netbox_ddns'
6
+
7
+ router = NetBoxRouter()
8
+ router.register('extra-dns-name', views.ExtraDNSNameViewSet)
9
+
10
+ urlpatterns = router.urls
@@ -0,0 +1,11 @@
1
+ from netbox.api.viewsets import NetBoxModelViewSet
2
+ from ..filtersets import ExtraDNSNameFilterSet
3
+ from ..models import ExtraDNSName
4
+ from .serializers import ExtraDNSNameSerializer
5
+
6
+
7
+ class ExtraDNSNameViewSet(NetBoxModelViewSet):
8
+ queryset = ExtraDNSName.objects.all()
9
+ serializer_class = ExtraDNSNameSerializer
10
+ filterset_class = ExtraDNSNameFilterSet
11
+
@@ -0,0 +1,30 @@
1
+ import django_filters
2
+
3
+ from netbox.filtersets import NetBoxModelFilterSet
4
+ from .models import ExtraDNSName, Server, Zone, ReverseZone
5
+
6
+
7
+ class ExtraDNSNameFilterSet(NetBoxModelFilterSet):
8
+ class Meta:
9
+ model = ExtraDNSName
10
+ fields = ('id', 'name', 'ip_address', 'forward_rcode')
11
+
12
+
13
+ class ServerFilterSet(NetBoxModelFilterSet):
14
+ class Meta:
15
+ model = Server
16
+ fields = ('id', 'server', 'server_port', 'tsig_key_name', 'tsig_algorithm', "tsig_key")
17
+
18
+
19
+ class ZoneFilterSet(NetBoxModelFilterSet):
20
+ class Meta:
21
+ model = Zone
22
+ fields = ('id', 'name', 'ttl', 'server')
23
+
24
+
25
+ class ReverseZoneFilterSet(NetBoxModelFilterSet):
26
+ prefix = django_filters.CharFilter(lookup_expr='icontains')
27
+
28
+ class Meta:
29
+ model = ReverseZone
30
+ fields = ('id', 'name', 'prefix', 'ttl', 'server')
@@ -0,0 +1,60 @@
1
+ from django.forms import IntegerField
2
+
3
+ from ipam.models import IPAddress
4
+ from netbox.forms import NetBoxModelForm, NetBoxModelBulkEditForm
5
+ from netbox_ddns.models import ExtraDNSName, Server, Zone, ReverseZone
6
+ from utilities.forms.fields import DynamicModelChoiceField
7
+ from utilities.forms.rendering import FieldSet
8
+
9
+
10
+ class ReverseZoneForm(NetBoxModelForm):
11
+ class Meta:
12
+ model = ReverseZone
13
+ fields = ('prefix', 'name', 'ttl', 'server')
14
+
15
+
16
+ class ZoneForm(NetBoxModelForm):
17
+ class Meta:
18
+ model = Zone
19
+ fields = ('name', 'ttl', 'server')
20
+
21
+
22
+ class ZoneBulkEditForm(NetBoxModelBulkEditForm):
23
+ model = Zone
24
+
25
+ ttl = IntegerField(
26
+ min_value=1,
27
+ required=False
28
+ )
29
+ server = DynamicModelChoiceField(
30
+ queryset=Server.objects.all(),
31
+ required=False
32
+ )
33
+
34
+
35
+ class ServerForm(NetBoxModelForm):
36
+ fieldsets = (
37
+ FieldSet('server', 'server_port', name='Server'),
38
+ FieldSet('tsig_key_name', 'tsig_algorithm', "tsig_key", name='Authentication'),
39
+ )
40
+
41
+ class Meta:
42
+ model = Server
43
+ fields = ('server', 'server_port', 'tsig_key_name', 'tsig_algorithm', "tsig_key")
44
+
45
+
46
+ class ExtraDNSNameIPAddressForm(NetBoxModelForm):
47
+ class Meta:
48
+ model = ExtraDNSName
49
+ fields = ['name']
50
+
51
+
52
+ class ExtraDNSNameForm(NetBoxModelForm):
53
+ ip_address = DynamicModelChoiceField(
54
+ queryset=IPAddress.objects.all(),
55
+ required=True
56
+ )
57
+
58
+ class Meta:
59
+ model = ExtraDNSName
60
+ fields = ['name', "ip_address"]
@@ -0,0 +1,38 @@
1
+ # Generated by Django 5.0.9 on 2024-11-11 17:12
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('netbox_ddns', '0008_server_server_port'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name='dnsstatus',
15
+ name='id',
16
+ field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
17
+ ),
18
+ migrations.AlterField(
19
+ model_name='extradnsname',
20
+ name='id',
21
+ field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
22
+ ),
23
+ migrations.AlterField(
24
+ model_name='reversezone',
25
+ name='id',
26
+ field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
27
+ ),
28
+ migrations.AlterField(
29
+ model_name='server',
30
+ name='id',
31
+ field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
32
+ ),
33
+ migrations.AlterField(
34
+ model_name='zone',
35
+ name='id',
36
+ field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
37
+ ),
38
+ ]
@@ -0,0 +1,36 @@
1
+ # Generated by Django 5.0.9 on 2024-11-11 17:14
2
+
3
+ import taggit.managers
4
+ import utilities.json
5
+ from django.db import migrations, models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ dependencies = [
11
+ ('extras', '0121_customfield_related_object_filter'),
12
+ ('netbox_ddns', '0009_alter_dnsstatus_id_alter_extradnsname_id_and_more'),
13
+ ]
14
+
15
+ operations = [
16
+ migrations.AddField(
17
+ model_name='extradnsname',
18
+ name='created',
19
+ field=models.DateTimeField(auto_now_add=True, null=True),
20
+ ),
21
+ migrations.AddField(
22
+ model_name='extradnsname',
23
+ name='custom_field_data',
24
+ field=models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
25
+ ),
26
+ migrations.AddField(
27
+ model_name='extradnsname',
28
+ name='last_updated',
29
+ field=models.DateTimeField(auto_now=True, null=True),
30
+ ),
31
+ migrations.AddField(
32
+ model_name='extradnsname',
33
+ name='tags',
34
+ field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
35
+ ),
36
+ ]
@@ -0,0 +1,36 @@
1
+ # Generated by Django 5.0.9 on 2025-01-08 11:03
2
+
3
+ import taggit.managers
4
+ import utilities.json
5
+ from django.db import migrations, models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ dependencies = [
11
+ ('extras', '0122_charfield_null_choices'),
12
+ ('netbox_ddns', '0010_extradnsname_created_extradnsname_custom_field_data_and_more'),
13
+ ]
14
+
15
+ operations = [
16
+ migrations.AddField(
17
+ model_name='server',
18
+ name='created',
19
+ field=models.DateTimeField(auto_now_add=True, null=True),
20
+ ),
21
+ migrations.AddField(
22
+ model_name='server',
23
+ name='custom_field_data',
24
+ field=models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
25
+ ),
26
+ migrations.AddField(
27
+ model_name='server',
28
+ name='last_updated',
29
+ field=models.DateTimeField(auto_now=True, null=True),
30
+ ),
31
+ migrations.AddField(
32
+ model_name='server',
33
+ name='tags',
34
+ field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
35
+ ),
36
+ ]
@@ -0,0 +1,36 @@
1
+ # Generated by Django 5.0.9 on 2025-01-08 18:46
2
+
3
+ import taggit.managers
4
+ import utilities.json
5
+ from django.db import migrations, models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ dependencies = [
11
+ ('extras', '0122_charfield_null_choices'),
12
+ ('netbox_ddns', '0011_server_created_server_custom_field_data_and_more'),
13
+ ]
14
+
15
+ operations = [
16
+ migrations.AddField(
17
+ model_name='zone',
18
+ name='created',
19
+ field=models.DateTimeField(auto_now_add=True, null=True),
20
+ ),
21
+ migrations.AddField(
22
+ model_name='zone',
23
+ name='custom_field_data',
24
+ field=models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
25
+ ),
26
+ migrations.AddField(
27
+ model_name='zone',
28
+ name='last_updated',
29
+ field=models.DateTimeField(auto_now=True, null=True),
30
+ ),
31
+ migrations.AddField(
32
+ model_name='zone',
33
+ name='tags',
34
+ field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
35
+ ),
36
+ ]
@@ -0,0 +1,36 @@
1
+ # Generated by Django 5.0.9 on 2025-01-08 19:51
2
+
3
+ import taggit.managers
4
+ import utilities.json
5
+ from django.db import migrations, models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ dependencies = [
11
+ ('extras', '0122_charfield_null_choices'),
12
+ ('netbox_ddns', '0012_zone_created_zone_custom_field_data_and_more'),
13
+ ]
14
+
15
+ operations = [
16
+ migrations.AddField(
17
+ model_name='reversezone',
18
+ name='created',
19
+ field=models.DateTimeField(auto_now_add=True, null=True),
20
+ ),
21
+ migrations.AddField(
22
+ model_name='reversezone',
23
+ name='custom_field_data',
24
+ field=models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
25
+ ),
26
+ migrations.AddField(
27
+ model_name='reversezone',
28
+ name='last_updated',
29
+ field=models.DateTimeField(auto_now=True, null=True),
30
+ ),
31
+ migrations.AddField(
32
+ model_name='reversezone',
33
+ name='tags',
34
+ field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
35
+ ),
36
+ ]
File without changes
@@ -4,6 +4,7 @@ import logging
4
4
  import socket
5
5
  from django.core.exceptions import ValidationError
6
6
  from django.db import models
7
+ from django.db.models import Q
7
8
  from django.db.models.functions import Length
8
9
  from django.utils.html import format_html
9
10
  from django.utils.translation import gettext_lazy as _
@@ -12,11 +13,13 @@ from dns import rcode
12
13
  from dns.tsig import HMAC_MD5, HMAC_SHA1, HMAC_SHA224, HMAC_SHA256, HMAC_SHA384, HMAC_SHA512
13
14
  from netaddr import IPNetwork, ip
14
15
  from typing import Optional
15
-
16
+ from netbox.models import NetBoxModel
16
17
  from ipam.fields import IPNetworkField
17
18
  from ipam.models import IPAddress
19
+ from utilities.querysets import RestrictedQuerySet
18
20
  from .utils import normalize_fqdn
19
- from .validators import HostnameAddressValidator, HostnameValidator, validate_base64, MinValueValidator, MaxValueValidator
21
+ from .validators import HostnameAddressValidator, HostnameValidator, validate_base64, MinValueValidator, \
22
+ MaxValueValidator
20
23
 
21
24
  logger = logging.getLogger('netbox_ddns')
22
25
 
@@ -62,7 +65,7 @@ def get_rcode_display(code):
62
65
  return _('Unknown response: {}').format(code)
63
66
 
64
67
 
65
- class Server(models.Model):
68
+ class Server(NetBoxModel):
66
69
  server = models.CharField(
67
70
  verbose_name=_('DDNS Server'),
68
71
  max_length=255,
@@ -111,12 +114,12 @@ class Server(models.Model):
111
114
  # Ensure trailing dots from domain-style fields
112
115
  self.tsig_key_name = normalize_fqdn(self.tsig_key_name.lower().rstrip('.'))
113
116
 
117
+ def get_absolute_url(self):
118
+ return reverse('plugins:netbox_ddns:server', args=[self.pk])
119
+
114
120
  @property
115
121
  def address(self) -> Optional[str]:
116
- addrinfo = socket.getaddrinfo(self.server, self.server_port, proto=socket.IPPROTO_UDP)
117
- for family, _, _, _, sockaddr in addrinfo:
118
- if family in (socket.AF_INET, socket.AF_INET6) and sockaddr[0]:
119
- return sockaddr[0]
122
+ return socket.gethostbyname(self.server)
120
123
 
121
124
  def create_update(self, zone: str) -> dns.update.Update:
122
125
  return dns.update.Update(
@@ -129,7 +132,7 @@ class Server(models.Model):
129
132
  )
130
133
 
131
134
 
132
- class ZoneQuerySet(models.QuerySet):
135
+ class ZoneQuerySet(RestrictedQuerySet):
133
136
  def find_for_dns_name(self, dns_name: str) -> Optional['Zone']:
134
137
  # Generate all possible zones
135
138
  zones = []
@@ -141,7 +144,7 @@ class ZoneQuerySet(models.QuerySet):
141
144
  return self.filter(name__in=zones).order_by(Length('name').desc()).first()
142
145
 
143
146
 
144
- class Zone(models.Model):
147
+ class Zone(NetBoxModel):
145
148
  name = models.CharField(
146
149
  verbose_name=_('zone name'),
147
150
  max_length=255,
@@ -167,6 +170,28 @@ class Zone(models.Model):
167
170
  def __str__(self):
168
171
  return self.name
169
172
 
173
+ def get_managed_ip_address(self):
174
+ # Find all more-specific zones
175
+ more_specifics = Zone.objects.filter(name__endswith=self.name).exclude(pk=self.pk)
176
+
177
+ # Find all IPAddress objects in this self but not in the more-specifics
178
+ ip_addresses = IPAddress.objects.filter(Q(dns_name__endswith=self.name) |
179
+ Q(dns_name__endswith=self.name.rstrip('.')))
180
+ for more_specific in more_specifics:
181
+ ip_addresses = ip_addresses.exclude(Q(dns_name__endswith=more_specific.name) |
182
+ Q(dns_name__endswith=more_specific.name.rstrip('.')))
183
+ return ip_addresses
184
+
185
+ def get_managed_extra_dns_name(self):
186
+ # Find all more-specific zones
187
+ more_specifics = Zone.objects.filter(name__endswith=self.name).exclude(pk=self.pk)
188
+
189
+ # Find all ExtraDNSName objects in this zone but not in the more-specifics
190
+ extra_names = ExtraDNSName.objects.filter(name__endswith=self.name)
191
+ for more_specific in more_specifics:
192
+ extra_names = extra_names.exclude(name__endswith=more_specific.name)
193
+ return extra_names
194
+
170
195
  def clean(self):
171
196
  # Ensure trailing dots from domain-style fields
172
197
  self.name = normalize_fqdn(self.name)
@@ -174,8 +199,11 @@ class Zone(models.Model):
174
199
  def get_updater(self):
175
200
  return self.server.create_update(self.name)
176
201
 
202
+ def get_absolute_url(self):
203
+ return reverse('plugins:netbox_ddns:zone', args=[self.pk])
204
+
177
205
 
178
- class ReverseZoneQuerySet(models.QuerySet):
206
+ class ReverseZoneQuerySet(RestrictedQuerySet):
179
207
  def find_for_address(self, address: ip.IPAddress) -> Optional['ReverseZone']:
180
208
  # Find the zone, if any
181
209
  zones = list(ReverseZone.objects.filter(prefix__net_contains=address))
@@ -186,7 +214,7 @@ class ReverseZoneQuerySet(models.QuerySet):
186
214
  return zones[-1]
187
215
 
188
216
 
189
- class ReverseZone(models.Model):
217
+ class ReverseZone(NetBoxModel):
190
218
  prefix = IPNetworkField(
191
219
  verbose_name=_('prefix'),
192
220
  unique=True,
@@ -214,7 +242,19 @@ class ReverseZone(models.Model):
214
242
  verbose_name_plural = _('reverse zones')
215
243
 
216
244
  def __str__(self):
217
- return f'for {self.prefix}'
245
+ return f'{self.prefix}'
246
+
247
+ def get_managed_ip_address(self):
248
+ # Find all more-specific zones
249
+ more_specifics = ReverseZone.objects.filter(prefix__net_contained=self.prefix).exclude(pk=self.pk)
250
+ # Find all IPAddress objects in this zone but not in the more-specifics
251
+ ip_addresses = IPAddress.objects.filter(address__net_contained_or_equal=self.prefix)
252
+ for more_specific in more_specifics:
253
+ ip_addresses = ip_addresses.exclude(address__net_contained_or_equal=more_specific.prefix)
254
+ return ip_addresses
255
+
256
+ def get_absolute_url(self):
257
+ return reverse('plugins:netbox_ddns:reversezone', args=[self.pk])
218
258
 
219
259
  def record_name(self, address: ip.IPAddress):
220
260
  record_name = self.name
@@ -325,7 +365,7 @@ class DNSStatus(models.Model):
325
365
  return format_html('<span style="color:{colour}">{output}</span', colour=colour, output=output)
326
366
 
327
367
 
328
- class ExtraDNSName(models.Model):
368
+ class ExtraDNSName(NetBoxModel):
329
369
  ip_address = models.ForeignKey(
330
370
  to=IPAddress,
331
371
  verbose_name=_('IP address'),
@@ -367,7 +407,7 @@ class ExtraDNSName(models.Model):
367
407
  return self.name
368
408
 
369
409
  def get_absolute_url(self):
370
- return reverse('plugins:netbox_ddns:extradnsname_edit', args=[self.ip_address.pk, self.pk])
410
+ return reverse('plugins:netbox_ddns:extradnsname', args=[self.pk])
371
411
 
372
412
  def clean(self):
373
413
  # Ensure trailing dots from domain-style fields
@@ -0,0 +1,58 @@
1
+ from netbox.plugins import PluginMenuItem, PluginMenuButton, PluginMenu
2
+
3
+ menu = PluginMenu(
4
+ label='DDNS',
5
+ groups=(
6
+ ('Configuration', (
7
+ PluginMenuItem(
8
+ link='plugins:netbox_ddns:server_list',
9
+ link_text='DDNS Servers',
10
+ buttons=[
11
+ PluginMenuButton(
12
+ link='plugins:netbox_ddns:server_add',
13
+ title='Add',
14
+ icon_class='mdi mdi-plus-thick',
15
+ )
16
+ ]
17
+ ),
18
+ PluginMenuItem(
19
+ link='plugins:netbox_ddns:zone_list',
20
+ link_text='Forward Zones',
21
+ buttons=[
22
+ PluginMenuButton(
23
+ link='plugins:netbox_ddns:zone_add',
24
+ title='Add',
25
+ icon_class='mdi mdi-plus-thick',
26
+ )
27
+ ]
28
+ ),
29
+ PluginMenuItem(
30
+ link='plugins:netbox_ddns:reversezone_list',
31
+ link_text='Reverse Zones',
32
+ buttons=[
33
+ PluginMenuButton(
34
+ link='plugins:netbox_ddns:reversezone_add',
35
+ title='Add',
36
+ icon_class='mdi mdi-plus-thick',
37
+ )
38
+ ]
39
+ ),
40
+ ),
41
+ ),
42
+ ('Extra DNS Names', (
43
+ PluginMenuItem(
44
+ link='plugins:netbox_ddns:extradnsname_list',
45
+ link_text='Extra DNS names',
46
+ buttons=[
47
+ PluginMenuButton(
48
+ link='plugins:netbox_ddns:extradnsname_add',
49
+ title='Add',
50
+ icon_class='mdi mdi-plus-thick',
51
+ )
52
+ ]
53
+ ),
54
+ ),
55
+ ),
56
+ ),
57
+ icon_class='mdi mdi-router'
58
+ )
@@ -0,0 +1,10 @@
1
+ from netbox.search import SearchIndex, register_search
2
+ from .models import ExtraDNSName
3
+
4
+
5
+ @register_search
6
+ class AccessListIndex(SearchIndex):
7
+ model = ExtraDNSName
8
+ fields = (
9
+ ('name', 100),
10
+ )
@@ -0,0 +1,51 @@
1
+ import django_tables2 as tables
2
+ from django_tables2 import LinkColumn, RelatedLinkColumn
3
+
4
+ from netbox_ddns.models import ExtraDNSName, Server, ReverseZone, Zone
5
+
6
+ from netbox.tables import NetBoxTable
7
+
8
+ FORWARD_DNS = """
9
+ {% if record.forward_action is not None %}
10
+ {{ record.get_forward_action_display }}:
11
+ {{ record.get_forward_rcode_html_display }}
12
+ {% else %}
13
+ <span class="text-muted">Not created</span>
14
+ {% endif %}
15
+ """
16
+
17
+
18
+ class ReverseZoneTable(NetBoxTable):
19
+ name = LinkColumn()
20
+ server = RelatedLinkColumn()
21
+
22
+ class Meta(NetBoxTable.Meta):
23
+ model = ReverseZone
24
+ fields = ("id", "name", "prefix", "server", "ttl")
25
+
26
+
27
+ class ZoneTable(NetBoxTable):
28
+ name = LinkColumn()
29
+ server = RelatedLinkColumn()
30
+
31
+ class Meta(NetBoxTable.Meta):
32
+ model = Zone
33
+ fields = ("id", "name", "server", "ttl")
34
+
35
+
36
+ class ServerTable(NetBoxTable):
37
+ server = LinkColumn()
38
+
39
+ class Meta(NetBoxTable.Meta):
40
+ model = Server
41
+ fields = ("id", "server", "server_port", "tsig_key_name", "tsig_algorithm")
42
+
43
+
44
+ class ExtraDNSNameTable(NetBoxTable):
45
+ ip_address = RelatedLinkColumn()
46
+ name = LinkColumn()
47
+ forward_dns = tables.TemplateColumn(template_code=FORWARD_DNS)
48
+
49
+ class Meta(NetBoxTable.Meta):
50
+ model = ExtraDNSName
51
+ fields = ('id', 'name', 'ip_address', 'last_update', 'forward_dns')