netbox-ddns 1.4.0__py3-none-any.whl → 1.7.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. netbox_ddns/__init__.py +3 -3
  2. netbox_ddns/admin.py +0 -156
  3. netbox_ddns/api/serializers.py +20 -2
  4. netbox_ddns/api/views.py +1 -0
  5. netbox_ddns/background_tasks.py +28 -5
  6. netbox_ddns/filtersets.py +23 -1
  7. netbox_ddns/forms.py +54 -3
  8. netbox_ddns/migrations/0011_server_created_server_custom_field_data_and_more.py +36 -0
  9. netbox_ddns/migrations/0012_zone_created_zone_custom_field_data_and_more.py +36 -0
  10. netbox_ddns/migrations/0013_reversezone_created_reversezone_custom_field_data_and_more.py +36 -0
  11. netbox_ddns/migrations/0014_alter_extradnsname_name.py +19 -0
  12. netbox_ddns/migrations/0015_server_protocol.py +18 -0
  13. netbox_ddns/models.py +61 -9
  14. netbox_ddns/navigation.py +62 -0
  15. netbox_ddns/tables.py +78 -32
  16. netbox_ddns/template_content.py +40 -3
  17. netbox_ddns/templates/netbox_ddns/extradnsname.html +23 -131
  18. netbox_ddns/templates/netbox_ddns/ipaddress/dns_extra.html +1 -1
  19. netbox_ddns/templates/netbox_ddns/managed_dns_names.html +17 -0
  20. netbox_ddns/templates/netbox_ddns/reversezone.html +51 -0
  21. netbox_ddns/templates/netbox_ddns/server.html +74 -0
  22. netbox_ddns/templates/netbox_ddns/update_reverse_zone.html +11 -0
  23. netbox_ddns/templates/netbox_ddns/update_zone.html +11 -0
  24. netbox_ddns/templates/netbox_ddns/zone.html +53 -0
  25. netbox_ddns/urls.py +32 -14
  26. netbox_ddns/utils.py +95 -0
  27. netbox_ddns/validators.py +6 -0
  28. netbox_ddns/views.py +268 -84
  29. {netbox_ddns-1.4.0.dist-info → netbox_ddns-1.7.0.dist-info}/METADATA +7 -5
  30. netbox_ddns-1.7.0.dist-info/RECORD +50 -0
  31. {netbox_ddns-1.4.0.dist-info → netbox_ddns-1.7.0.dist-info}/WHEEL +1 -1
  32. netbox_ddns-1.4.0.dist-info/RECORD +0 -38
  33. {netbox_ddns-1.4.0.dist-info → netbox_ddns-1.7.0.dist-info/licenses}/LICENSE.txt +0 -0
  34. {netbox_ddns-1.4.0.dist-info → netbox_ddns-1.7.0.dist-info}/top_level.txt +0 -0
netbox_ddns/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- VERSION = '1.4.0'
1
+ VERSION = '1.7.0'
2
2
 
3
3
  try:
4
4
  from netbox.plugins import PluginConfig
@@ -12,8 +12,8 @@ class NetBoxDDNSConfig(PluginConfig):
12
12
  name = 'netbox_ddns'
13
13
  verbose_name = 'Dynamic DNS'
14
14
  version = VERSION
15
- min_version = '4.0.0'
16
- max_version = '4.1.999'
15
+ min_version = '4.4.0'
16
+ max_version = '4.5.999'
17
17
  author = 'Sander Steffann'
18
18
  author_email = 'sander@steffann.nl'
19
19
  description = 'Dynamic DNS Connector for NetBox'
netbox_ddns/admin.py CHANGED
@@ -1,156 +0,0 @@
1
- import logging
2
-
3
- from django.contrib import admin, messages
4
- from django.contrib.admin.filters import SimpleListFilter
5
- from django.contrib.admin.options import ModelAdmin
6
- from django.db.models import QuerySet
7
- from django.db.models.query_utils import Q
8
- from django.http.request import HttpRequest
9
- from django.utils.translation import gettext_lazy as _
10
-
11
- from ipam.models import IPAddress
12
- from netbox.admin import admin_site
13
- from netbox_ddns.models import DNSStatus, ExtraDNSName
14
- from .background_tasks import dns_create
15
- from .models import ReverseZone, Server, Zone
16
- from .utils import normalize_fqdn
17
-
18
- logger = logging.getLogger('netbox_ddns')
19
-
20
-
21
- class IPFamilyFilter(SimpleListFilter):
22
- title = _('Prefix family')
23
- parameter_name = 'family'
24
-
25
- def lookups(self, request: HttpRequest, model_admin: ModelAdmin):
26
- return (
27
- ('ipv4', _('IPv4')),
28
- ('ipv6', _('IPv6')),
29
- )
30
-
31
- def queryset(self, request: HttpRequest, queryset: QuerySet):
32
- if self.value() == 'ipv4':
33
- return queryset.filter(prefix__family=4)
34
- if self.value() == 'ipv6':
35
- return queryset.filter(prefix__family=6)
36
-
37
-
38
- class ZoneInlineAdmin(admin.TabularInline):
39
- model = Zone
40
-
41
-
42
- class ReverseZoneInlineAdmin(admin.TabularInline):
43
- model = ReverseZone
44
-
45
-
46
- @admin.register(Server, site=admin_site)
47
- class ServerAdmin(admin.ModelAdmin):
48
- list_display = ('server', 'server_port', 'tsig_key_name', 'tsig_algorithm')
49
- inlines = [
50
- ZoneInlineAdmin,
51
- ReverseZoneInlineAdmin,
52
- ]
53
-
54
-
55
- @admin.register(Zone, site=admin_site)
56
- class ZoneAdmin(admin.ModelAdmin):
57
- list_display = ('name', 'ttl', 'server')
58
- actions = [
59
- 'update_all_records'
60
- ]
61
-
62
- def update_all_records(self, request: HttpRequest, queryset: QuerySet):
63
- for zone in queryset:
64
- counter = 0
65
-
66
- # Find all more-specific zones
67
- more_specifics = Zone.objects.filter(name__endswith=zone.name).exclude(pk=zone.pk)
68
-
69
- # Find all IPAddress objects in this zone but not in the more-specifics
70
- ip_addresses = IPAddress.objects.filter(Q(dns_name__endswith=zone.name) |
71
- Q(dns_name__endswith=zone.name.rstrip('.')))
72
- for more_specific in more_specifics:
73
- ip_addresses = ip_addresses.exclude(Q(dns_name__endswith=more_specific.name) |
74
- Q(dns_name__endswith=more_specific.name.rstrip('.')))
75
-
76
- for ip_address in ip_addresses:
77
- new_address = ip_address.address.ip
78
- new_dns_name = normalize_fqdn(ip_address.dns_name)
79
-
80
- if new_dns_name:
81
- status, created = DNSStatus.objects.get_or_create(ip_address=ip_address)
82
-
83
- dns_create.delay(
84
- dns_name=new_dns_name,
85
- address=new_address,
86
- status=status,
87
- reverse=False,
88
- )
89
-
90
- counter += 1
91
-
92
- # Find all ExtraDNSName objects in this zone but not in the more-specifics
93
- extra_names = ExtraDNSName.objects.filter(name__endswith=zone.name)
94
- for more_specific in more_specifics:
95
- extra_names = extra_names.exclude(name__endswith=more_specific.name)
96
-
97
- for extra in extra_names:
98
- new_address = extra.ip_address.address.ip
99
- new_dns_name = extra.name
100
-
101
- dns_create.delay(
102
- dns_name=new_dns_name,
103
- address=new_address,
104
- status=extra,
105
- reverse=False,
106
- )
107
-
108
- counter += 1
109
-
110
- messages.info(request, _("Updating {count} forward records in {name}").format(count=counter,
111
- name=zone.name))
112
-
113
-
114
- @admin.register(ReverseZone, site=admin_site)
115
- class ReverseZoneAdmin(admin.ModelAdmin):
116
- list_display = ('prefix', 'name', 'ttl', 'server')
117
- list_filter = [IPFamilyFilter]
118
- actions = [
119
- 'update_all_records'
120
- ]
121
-
122
- def update_all_records(self, request: HttpRequest, queryset: QuerySet):
123
- for zone in queryset:
124
- counter = 0
125
-
126
- # Find all more-specific zones
127
- more_specifics = ReverseZone.objects.filter(prefix__net_contained=zone.prefix).exclude(pk=zone.pk)
128
-
129
- # Find all IPAddress objects in this zone but not in the more-specifics
130
- ip_addresses = IPAddress.objects.filter(address__net_contained_or_equal=zone.prefix)
131
- for more_specific in more_specifics:
132
- ip_addresses = ip_addresses.exclude(address__net_contained_or_equal=more_specific.prefix)
133
-
134
- for ip_address in ip_addresses:
135
- new_address = ip_address.address.ip
136
- new_dns_name = normalize_fqdn(ip_address.dns_name)
137
-
138
- if new_dns_name:
139
- status, created = DNSStatus.objects.get_or_create(ip_address=ip_address)
140
-
141
- dns_create.delay(
142
- dns_name=new_dns_name,
143
- address=new_address,
144
- status=status,
145
- forward=False,
146
- )
147
-
148
- counter += 1
149
-
150
- messages.info(request, _("Updating {count} reverse records in {name}").format(count=counter,
151
- name=zone.name))
152
-
153
-
154
- @admin.register(ExtraDNSName, site=admin_site)
155
- class ExtraDNSNameAdmin(admin.ModelAdmin):
156
- pass
@@ -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', 'protocol')
30
+
31
+
32
+ class ReverseZoneSerializer(NetBoxModelSerializer):
33
+ class Meta:
34
+ model = ReverseZone
35
+ fields = ('name', 'prefix', 'ttl', 'server')
netbox_ddns/api/views.py CHANGED
@@ -8,3 +8,4 @@ class ExtraDNSNameViewSet(NetBoxModelViewSet):
8
8
  queryset = ExtraDNSName.objects.all()
9
9
  serializer_class = ExtraDNSNameSerializer
10
10
  filterset_class = ExtraDNSNameFilterSet
11
+
@@ -9,7 +9,7 @@ from django_rq import job
9
9
  from dns import rcode
10
10
  from netaddr import ip
11
11
 
12
- from netbox_ddns.models import ACTION_CREATE, ACTION_DELETE, DNSStatus, RCODE_NO_ZONE, ReverseZone, Zone
12
+ from netbox_ddns.models import ACTION_CREATE, ACTION_DELETE, DNSStatus, RCODE_NO_ZONE, ReverseZone, Zone, Protocol
13
13
  from netbox_ddns.utils import get_soa
14
14
 
15
15
  logger = logging.getLogger('netbox_ddns')
@@ -28,6 +28,17 @@ def status_update(output: List[str], operation: str, response) -> None:
28
28
  output.append(message)
29
29
 
30
30
 
31
+ def send_dns_update(update, server, protocol):
32
+ """Send DNS update via TCP or UDP. Returns response or None on unknown protocol."""
33
+ if protocol == Protocol.TCP:
34
+ return dns.query.tcp(update, server.address, port=server.server_port)
35
+ elif protocol == Protocol.UDP:
36
+ return dns.query.udp(update, server.address, port=server.server_port)
37
+ else:
38
+ logger.error(f"Unknown protocol {protocol} for server {server}")
39
+ return None
40
+
41
+
31
42
  def create_forward(dns_name: str, address: ip.IPAddress, status: Optional[DNSStatus], output: List[str]):
32
43
  if status:
33
44
  status.forward_action = ACTION_CREATE
@@ -38,6 +49,7 @@ def create_forward(dns_name: str, address: ip.IPAddress, status: Optional[DNSSta
38
49
 
39
50
  # Check the SOA, we don't want to write to a parent zone if it has delegated authority
40
51
  soa = get_soa(zone.name)
52
+ protocol = zone.server.protocol
41
53
  if soa == zone.name:
42
54
  record_type = 'A' if address.version == 4 else 'AAAA'
43
55
  update = zone.server.create_update(zone.name)
@@ -47,7 +59,9 @@ def create_forward(dns_name: str, address: ip.IPAddress, status: Optional[DNSSta
47
59
  record_type,
48
60
  str(address)
49
61
  )
50
- response = dns.query.udp(update, zone.server.address, port=zone.server.server_port)
62
+ response = send_dns_update(update, zone.server, protocol)
63
+ if response is None:
64
+ return
51
65
  status_update(output, f'Adding {dns_name} {record_type} {address}', response)
52
66
  if status:
53
67
  status.forward_rcode = response.rcode()
@@ -72,6 +86,7 @@ def delete_forward(dns_name: str, address: ip.IPAddress, status: Optional[DNSSta
72
86
 
73
87
  # Check the SOA, we don't want to write to a parent zone if it has delegated authority
74
88
  soa = get_soa(zone.name)
89
+ protocol = zone.server.protocol
75
90
  if soa == zone.name:
76
91
  record_type = 'A' if address.version == 4 else 'AAAA'
77
92
  update = zone.server.create_update(zone.name)
@@ -80,7 +95,9 @@ def delete_forward(dns_name: str, address: ip.IPAddress, status: Optional[DNSSta
80
95
  record_type,
81
96
  str(address)
82
97
  )
83
- response = dns.query.udp(update, zone.server.address, port=zone.server.server_port)
98
+ response = send_dns_update(update, zone.server, protocol)
99
+ if response is None:
100
+ return
84
101
  status_update(output, f'Deleting {dns_name} {record_type} {address}', response)
85
102
  if status:
86
103
  status.forward_rcode = response.rcode()
@@ -106,6 +123,7 @@ def create_reverse(dns_name: str, address: ip.IPAddress, status: Optional[DNSSta
106
123
 
107
124
  # Check the SOA, we don't want to write to a parent zone if it has delegated authority
108
125
  soa = get_soa(record_name)
126
+ protocol = zone.server.protocol
109
127
  if soa == zone.name:
110
128
  update = zone.server.create_update(zone.name)
111
129
  update.add(
@@ -114,7 +132,9 @@ def create_reverse(dns_name: str, address: ip.IPAddress, status: Optional[DNSSta
114
132
  'ptr',
115
133
  dns_name
116
134
  )
117
- response = dns.query.udp(update, zone.server.address, port=zone.server.server_port)
135
+ response = send_dns_update(update, zone.server, protocol)
136
+ if response is None:
137
+ return
118
138
  status_update(output, f'Adding {record_name} PTR {dns_name}', response)
119
139
  if status:
120
140
  status.reverse_rcode = response.rcode()
@@ -140,6 +160,7 @@ def delete_reverse(dns_name: str, address: ip.IPAddress, status: Optional[DNSSta
140
160
 
141
161
  # Check the SOA, we don't want to write to a parent zone if it has delegated authority
142
162
  soa = get_soa(record_name)
163
+ protocol = zone.server.protocol
143
164
  if soa == zone.name:
144
165
  update = zone.server.create_update(zone.name)
145
166
  update.delete(
@@ -147,7 +168,9 @@ def delete_reverse(dns_name: str, address: ip.IPAddress, status: Optional[DNSSta
147
168
  'ptr',
148
169
  dns_name
149
170
  )
150
- response = dns.query.udp(update, zone.server.address, port=zone.server.server_port)
171
+ response = send_dns_update(update, zone.server, protocol)
172
+ if response is None:
173
+ return
151
174
  status_update(output, f'Deleting {record_name} PTR {dns_name}', response)
152
175
  if status:
153
176
  status.reverse_rcode = response.rcode()
netbox_ddns/filtersets.py CHANGED
@@ -1,8 +1,30 @@
1
+ import django_filters
2
+
1
3
  from netbox.filtersets import NetBoxModelFilterSet
2
- from .models import ExtraDNSName
4
+ from .models import ExtraDNSName, Server, Zone, ReverseZone
3
5
 
4
6
 
5
7
  class ExtraDNSNameFilterSet(NetBoxModelFilterSet):
6
8
  class Meta:
7
9
  model = ExtraDNSName
8
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')
netbox_ddns/forms.py CHANGED
@@ -1,9 +1,60 @@
1
- from django import forms
1
+ from django.forms import IntegerField
2
2
 
3
- from netbox_ddns.models import ExtraDNSName
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
4
8
 
5
9
 
6
- class ExtraDNSNameEditForm(forms.ModelForm):
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', 'protocol', 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', 'protocol', 'tsig_key_name', 'tsig_algorithm', "tsig_key")
44
+
45
+
46
+ class ExtraDNSNameIPAddressForm(NetBoxModelForm):
7
47
  class Meta:
8
48
  model = ExtraDNSName
9
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,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
+ ]
@@ -0,0 +1,19 @@
1
+ # Generated by Django 5.2.5 on 2025-09-01 09:17
2
+
3
+ import netbox_ddns.validators
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('netbox_ddns', '0013_reversezone_created_reversezone_custom_field_data_and_more'),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AlterField(
15
+ model_name='extradnsname',
16
+ name='name',
17
+ field=models.CharField(max_length=255, validators=[netbox_ddns.validators.DNSNameValidator()]),
18
+ ),
19
+ ]
@@ -0,0 +1,18 @@
1
+ # Generated by Django 5.2.6 on 2025-10-01 14:44
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('netbox_ddns', '0014_alter_extradnsname_name'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name='server',
15
+ name='protocol',
16
+ field=models.CharField(default='udp', max_length=3),
17
+ ),
18
+ ]