netbox-ddns 1.4.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.
- {netbox_ddns-1.4.0/netbox_ddns.egg-info → netbox_ddns-1.5.0}/PKG-INFO +2 -2
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/__init__.py +2 -2
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/api/serializers.py +20 -2
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/api/views.py +1 -0
- netbox_ddns-1.5.0/netbox_ddns/filtersets.py +30 -0
- netbox_ddns-1.5.0/netbox_ddns/forms.py +60 -0
- netbox_ddns-1.5.0/netbox_ddns/migrations/0011_server_created_server_custom_field_data_and_more.py +36 -0
- netbox_ddns-1.5.0/netbox_ddns/migrations/0012_zone_created_zone_custom_field_data_and_more.py +36 -0
- netbox_ddns-1.5.0/netbox_ddns/migrations/0013_reversezone_created_reversezone_custom_field_data_and_more.py +36 -0
- netbox_ddns-1.5.0/netbox_ddns/migrations/__init__.py +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/models.py +52 -12
- netbox_ddns-1.5.0/netbox_ddns/navigation.py +58 -0
- netbox_ddns-1.5.0/netbox_ddns/tables.py +51 -0
- netbox_ddns-1.5.0/netbox_ddns/template_content.py +65 -0
- netbox_ddns-1.5.0/netbox_ddns/templates/netbox_ddns/extradnsname.html +61 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/templates/netbox_ddns/ipaddress/dns_extra.html +1 -1
- netbox_ddns-1.5.0/netbox_ddns/templates/netbox_ddns/reversezone.html +51 -0
- netbox_ddns-1.5.0/netbox_ddns/templates/netbox_ddns/server.html +68 -0
- netbox_ddns-1.5.0/netbox_ddns/templates/netbox_ddns/update_reverse_zone.html +11 -0
- netbox_ddns-1.5.0/netbox_ddns/templates/netbox_ddns/update_zone.html +11 -0
- netbox_ddns-1.5.0/netbox_ddns/templates/netbox_ddns/zone.html +53 -0
- netbox_ddns-1.5.0/netbox_ddns/urls.py +34 -0
- netbox_ddns-1.5.0/netbox_ddns/views.py +321 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0/netbox_ddns.egg-info}/PKG-INFO +2 -2
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns.egg-info/SOURCES.txt +9 -0
- netbox_ddns-1.4.0/netbox_ddns/admin.py +0 -156
- netbox_ddns-1.4.0/netbox_ddns/filtersets.py +0 -8
- netbox_ddns-1.4.0/netbox_ddns/forms.py +0 -9
- netbox_ddns-1.4.0/netbox_ddns/tables.py +0 -50
- netbox_ddns-1.4.0/netbox_ddns/template_content.py +0 -37
- netbox_ddns-1.4.0/netbox_ddns/templates/netbox_ddns/extradnsname.html +0 -169
- netbox_ddns-1.4.0/netbox_ddns/urls.py +0 -22
- netbox_ddns-1.4.0/netbox_ddns/views.py +0 -155
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/LICENSE.txt +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/MANIFEST.in +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/README.md +0 -0
- /netbox_ddns-1.4.0/netbox_ddns/api/__init__.py → /netbox_ddns-1.5.0/netbox_ddns/admin.py +0 -0
- {netbox_ddns-1.4.0/netbox_ddns/migrations → netbox_ddns-1.5.0/netbox_ddns/api}/__init__.py +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/api/urls.py +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/background_tasks.py +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/migrations/0001_initial.py +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/migrations/0002_add_ttl.py +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/migrations/0003_dnsstatus.py +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/migrations/0004_ensure_trailing_dot.py +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/migrations/0005_extradnsname.py +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/migrations/0006_extradns_cname.py +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/migrations/0007_zone_meta.py +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/migrations/0008_server_server_port.py +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/migrations/0009_alter_dnsstatus_id_alter_extradnsname_id_and_more.py +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/migrations/0010_extradnsname_created_extradnsname_custom_field_data_and_more.py +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/search.py +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/signals.py +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/templates/netbox_ddns/ipaddress/dns_info.html +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/templates/netbox_ddns/ipaddress/dns_refresh_button.html +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/utils.py +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/validators.py +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns.egg-info/dependency_links.txt +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns.egg-info/not-zip-safe +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns.egg-info/requires.txt +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns.egg-info/top_level.txt +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/pyproject.toml +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/setup.cfg +0 -0
- {netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/setup.py +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
VERSION = '1.
|
|
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.
|
|
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'
|
|
@@ -2,16 +2,34 @@ from rest_framework import serializers
|
|
|
2
2
|
from rest_framework.relations import PrimaryKeyRelatedField
|
|
3
3
|
from ipam.models import IPAddress
|
|
4
4
|
from netbox.api.serializers import NetBoxModelSerializer
|
|
5
|
-
from ..models import ExtraDNSName
|
|
5
|
+
from ..models import ExtraDNSName, Server, Zone, ReverseZone
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class ExtraDNSNameSerializer(NetBoxModelSerializer):
|
|
9
9
|
url = serializers.HyperlinkedIdentityField(
|
|
10
10
|
view_name='plugins-api:netbox_ddns-api:extradnsname-detail'
|
|
11
11
|
)
|
|
12
|
-
ip_address = PrimaryKeyRelatedField(queryset=IPAddress.objects.all()
|
|
12
|
+
ip_address = PrimaryKeyRelatedField(queryset=IPAddress.objects.all())
|
|
13
13
|
|
|
14
14
|
class Meta:
|
|
15
15
|
model = ExtraDNSName
|
|
16
16
|
fields = ('id', 'ip_address', 'name', 'url')
|
|
17
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,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"]
|
netbox_ddns-1.5.0/netbox_ddns/migrations/0011_server_created_server_custom_field_data_and_more.py
ADDED
|
@@ -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 _
|
|
@@ -15,8 +16,10 @@ 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,
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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'
|
|
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
|
|
@@ -367,7 +407,7 @@ class ExtraDNSName(NetBoxModel):
|
|
|
367
407
|
return self.name
|
|
368
408
|
|
|
369
409
|
def get_absolute_url(self):
|
|
370
|
-
return reverse('plugins:netbox_ddns:extradnsname', args=[self.
|
|
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,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')
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from django.contrib.auth.context_processors import PermWrapper
|
|
2
|
+
from django.template.context_processors import csrf
|
|
3
|
+
|
|
4
|
+
from netbox.plugins.templates import PluginTemplateExtension
|
|
5
|
+
from . import tables
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ReverseZoneRecreate(PluginTemplateExtension):
|
|
9
|
+
model = 'netbox_ddns.reversezone'
|
|
10
|
+
|
|
11
|
+
def buttons(self):
|
|
12
|
+
"""
|
|
13
|
+
A button to force DNS re-provisioning
|
|
14
|
+
"""
|
|
15
|
+
context = {
|
|
16
|
+
'perms': PermWrapper(self.context['request'].user),
|
|
17
|
+
}
|
|
18
|
+
context.update(csrf(self.context['request']))
|
|
19
|
+
return self.render('netbox_ddns/update_reverse_zone.html', context)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ZoneRecreate(PluginTemplateExtension):
|
|
23
|
+
model = 'netbox_ddns.zone'
|
|
24
|
+
|
|
25
|
+
def buttons(self):
|
|
26
|
+
"""
|
|
27
|
+
A button to force DNS re-provisioning
|
|
28
|
+
"""
|
|
29
|
+
context = {
|
|
30
|
+
'perms': PermWrapper(self.context['request'].user),
|
|
31
|
+
}
|
|
32
|
+
context.update(csrf(self.context['request']))
|
|
33
|
+
return self.render('netbox_ddns/update_zone.html', context)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class DNSInfo(PluginTemplateExtension):
|
|
37
|
+
model = 'ipam.ipaddress'
|
|
38
|
+
|
|
39
|
+
def buttons(self):
|
|
40
|
+
"""
|
|
41
|
+
A button to force DNS re-provisioning
|
|
42
|
+
"""
|
|
43
|
+
context = {
|
|
44
|
+
'perms': PermWrapper(self.context['request'].user),
|
|
45
|
+
}
|
|
46
|
+
context.update(csrf(self.context['request']))
|
|
47
|
+
return self.render('netbox_ddns/ipaddress/dns_refresh_button.html', context)
|
|
48
|
+
|
|
49
|
+
def left_page(self):
|
|
50
|
+
"""
|
|
51
|
+
An info-box with the status of the DNS modifications and records
|
|
52
|
+
"""
|
|
53
|
+
extra_dns_name_table = tables.ExtraDNSNameTable(list(self.context['object'].extradnsname_set.all()),
|
|
54
|
+
exclude=["id", "ip_address"], orderable=False)
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
self.render('netbox_ddns/ipaddress/dns_info.html') +
|
|
58
|
+
self.render('netbox_ddns/ipaddress/dns_extra.html', {
|
|
59
|
+
'perms': PermWrapper(self.context['request'].user),
|
|
60
|
+
'extra_dns_name_table': extra_dns_name_table,
|
|
61
|
+
})
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
template_extensions = [DNSInfo, ZoneRecreate, ReverseZoneRecreate]
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{% extends 'generic/object.html' %}
|
|
2
|
+
{% load render_table from django_tables2 %}
|
|
3
|
+
|
|
4
|
+
{% block content %}
|
|
5
|
+
<div class="row mb-3">
|
|
6
|
+
<div class="col col-md-6">
|
|
7
|
+
<div class="card">
|
|
8
|
+
<h5 class="card-header">Extra DNS name</h5>
|
|
9
|
+
<div class="card-body">
|
|
10
|
+
<table class="table table-hover attr-table">
|
|
11
|
+
<tr>
|
|
12
|
+
<th scope="row">Name</th>
|
|
13
|
+
<td>{{ object.name }}</td>
|
|
14
|
+
</tr>
|
|
15
|
+
<tr>
|
|
16
|
+
<th scope="row">IP Address</th>
|
|
17
|
+
<td>
|
|
18
|
+
<a href="{{ object.ip_address.get_absolute_url }}">{{ object.ip_address }}</a>
|
|
19
|
+
</td>
|
|
20
|
+
</tr>
|
|
21
|
+
<tr>
|
|
22
|
+
<th scope="row">Last update</th>
|
|
23
|
+
<td>{{ object.last_update|isodatetime }}</td>
|
|
24
|
+
</tr>
|
|
25
|
+
<tr>
|
|
26
|
+
<th scope="row">Forward DNS</th>
|
|
27
|
+
<td>
|
|
28
|
+
{% if object.forward_action is not None %}
|
|
29
|
+
{{ object.get_forward_action_display }}:
|
|
30
|
+
{{ object.get_forward_rcode_html_display }} {% else %}
|
|
31
|
+
<span class="text-muted">Not created</span>
|
|
32
|
+
{% endif %}
|
|
33
|
+
</td>
|
|
34
|
+
</tr>
|
|
35
|
+
</table>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="card">
|
|
39
|
+
<h5 class="card-header">Managed by</h5>
|
|
40
|
+
<div class="card-body">
|
|
41
|
+
<table class="table table-hover attr-table">
|
|
42
|
+
<tr>
|
|
43
|
+
<th scope="row">DDNS Server</th>
|
|
44
|
+
<td>
|
|
45
|
+
<a href="{{ server.get_absolute_url }}">{{ server }}</a>
|
|
46
|
+
</td>
|
|
47
|
+
</tr>
|
|
48
|
+
<tr>
|
|
49
|
+
<th scope="row">Zone</th>
|
|
50
|
+
<td>
|
|
51
|
+
<a href="{{ zone.get_absolute_url }}">{{ zone }}</a>
|
|
52
|
+
</td>
|
|
53
|
+
</tr>
|
|
54
|
+
</table>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
{% include 'inc/panels/custom_fields.html' %}
|
|
58
|
+
</div>
|
|
59
|
+
<div class="col col-md-6">{% include 'inc/panels/tags.html' %}</div>
|
|
60
|
+
</div>
|
|
61
|
+
{% endblock content %}
|
{netbox_ddns-1.4.0 → netbox_ddns-1.5.0}/netbox_ddns/templates/netbox_ddns/ipaddress/dns_extra.html
RENAMED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
{% if perms.netbox_ddns.add_extradnsname %}
|
|
12
12
|
<div class="card-footer text-right noprint">
|
|
13
|
-
<a href="{% url 'plugins:netbox_ddns:
|
|
13
|
+
<a href="{% url 'plugins:netbox_ddns:extradnsname_ip_address_create' ipaddress_pk=object.pk %}"
|
|
14
14
|
class="btn btn-primary">
|
|
15
15
|
<span class="mdi mdi-plus" aria-hidden="true"></span>Add extra DNS name
|
|
16
16
|
</a>
|