netbox-plugin-dns 0.22.3__py3-none-any.whl → 0.22.5__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.

netbox_dns/__init__.py CHANGED
@@ -10,7 +10,7 @@ except ImportError:
10
10
  # NetBox 3.5.8
11
11
  from extras.plugins.utils import get_plugin_config
12
12
 
13
- __version__ = "0.22.3"
13
+ __version__ = "0.22.5"
14
14
 
15
15
 
16
16
  class DNSConfig(PluginConfig):
@@ -26,9 +26,9 @@ class DNSConfig(PluginConfig):
26
26
  "zone_default_ttl": 86400,
27
27
  "zone_soa_serial_auto": True,
28
28
  "zone_soa_serial": 1,
29
- "zone_soa_refresh": 172800,
29
+ "zone_soa_refresh": 43200,
30
30
  "zone_soa_retry": 7200,
31
- "zone_soa_expire": 2592000,
31
+ "zone_soa_expire": 2419200,
32
32
  "zone_soa_minimum": 3600,
33
33
  "feature_ipam_coupling": False,
34
34
  "tolerate_underscores_in_hostnames": False,
@@ -38,7 +38,8 @@ class DNSConfig(PluginConfig):
38
38
  ],
39
39
  "tolerate_non_rfc1035_types": [],
40
40
  "enable_root_zones": False,
41
- "enforce_unique_records": False,
41
+ "enforce_unique_records": True,
42
+ "enforce_unique_rrset_ttl": True,
42
43
  }
43
44
  base_url = "netbox-dns"
44
45
 
@@ -272,6 +272,7 @@ class RegistrarSerializer(NetBoxModelSerializer):
272
272
  "display",
273
273
  "name",
274
274
  "iana_id",
275
+ "address",
275
276
  "referral_url",
276
277
  "whois_server",
277
278
  "abuse_email",
@@ -12,6 +12,7 @@ class RegistrarFilter(NetBoxModelFilterSet):
12
12
  "id",
13
13
  "name",
14
14
  "iana_id",
15
+ "address",
15
16
  "referral_url",
16
17
  "whois_server",
17
18
  "abuse_email",
@@ -17,6 +17,7 @@ class RegistrarForm(NetBoxModelForm):
17
17
  fields = (
18
18
  "name",
19
19
  "iana_id",
20
+ "address",
20
21
  "referral_url",
21
22
  "whois_server",
22
23
  "abuse_email",
@@ -29,12 +30,18 @@ class RegistrarFilterForm(NetBoxModelFilterSetForm):
29
30
  model = Registrar
30
31
  fieldsets = (
31
32
  (None, ("q", "name", "iana_id", "tags")),
32
- ("Contact", ("referral_url", "whois_server", "abuse_email", "abuse_phone")),
33
+ (
34
+ "Contact",
35
+ ("address", "referral_url", "whois_server", "abuse_email", "abuse_phone"),
36
+ ),
33
37
  )
34
38
 
35
39
  name = forms.CharField(
36
40
  required=False,
37
41
  )
42
+ address = forms.CharField(
43
+ required=False,
44
+ )
38
45
  iana_id = forms.IntegerField(
39
46
  required=False,
40
47
  label="IANA ID",
@@ -64,6 +71,7 @@ class RegistrarImportForm(NetBoxModelImportForm):
64
71
  fields = (
65
72
  "name",
66
73
  "iana_id",
74
+ "address",
67
75
  "referral_url",
68
76
  "whois_server",
69
77
  "abuse_email",
@@ -96,6 +104,7 @@ class RegistrarBulkEditForm(NetBoxModelBulkEditForm):
96
104
  (
97
105
  None,
98
106
  (
107
+ "address",
99
108
  "referral_url",
100
109
  "whois_server",
101
110
  "abuse_email",
@@ -105,6 +114,7 @@ class RegistrarBulkEditForm(NetBoxModelBulkEditForm):
105
114
  )
106
115
 
107
116
  nullable_fields = (
117
+ "address",
108
118
  "referral_url",
109
119
  "whois_server",
110
120
  "abuse_email",
netbox_dns/forms/zone.py CHANGED
@@ -15,6 +15,7 @@ from utilities.forms.fields import (
15
15
  TagFilterField,
16
16
  CSVChoiceField,
17
17
  CSVModelChoiceField,
18
+ CSVModelMultipleChoiceField,
18
19
  DynamicModelChoiceField,
19
20
  )
20
21
  from utilities.forms.widgets import BulkEditNullBooleanSelect, APISelect
@@ -287,6 +288,12 @@ class ZoneImportForm(NetBoxModelImportForm):
287
288
  required=False,
288
289
  help_text="Zone status",
289
290
  )
291
+ nameservers = CSVModelMultipleChoiceField(
292
+ queryset=NameServer.objects.all(),
293
+ to_field_name="name",
294
+ required=False,
295
+ help_text="Name servers for the zone",
296
+ )
290
297
  default_ttl = forms.IntegerField(
291
298
  required=False,
292
299
  help_text="Default TTL",
@@ -308,7 +315,7 @@ class ZoneImportForm(NetBoxModelImportForm):
308
315
  required=False,
309
316
  help_text="Mailbox of the zone's administrator",
310
317
  )
311
- soa_serial_auto = forms.BooleanField(
318
+ soa_serial_auto = forms.NullBooleanField(
312
319
  required=False,
313
320
  help_text="Generate the SOA serial",
314
321
  )
@@ -436,26 +443,6 @@ class ZoneImportForm(NetBoxModelImportForm):
436
443
  def clean_soa_rname(self):
437
444
  return self._clean_field_with_defaults("soa_rname")
438
445
 
439
- def clean_soa_serial_auto(self):
440
- try:
441
- return self._clean_field_with_defaults("soa_serial_auto")
442
- except ValidationError:
443
- if self.cleaned_data["soa_serial"] or self._get_default_value("soa_serial"):
444
- return None
445
-
446
- raise
447
-
448
- def clean_soa_serial(self):
449
- try:
450
- return self._clean_field_with_defaults("soa_serial")
451
- except ValidationError:
452
- if self.cleaned_data["soa_serial_auto"] or self._get_default_value(
453
- "soa_serial_auto"
454
- ):
455
- return None
456
-
457
- raise
458
-
459
446
  def clean_soa_refresh(self):
460
447
  return self._clean_field_with_defaults("soa_refresh")
461
448
 
@@ -468,6 +455,36 @@ class ZoneImportForm(NetBoxModelImportForm):
468
455
  def clean_soa_minimum(self):
469
456
  return self._clean_field_with_defaults("soa_minimum")
470
457
 
458
+ def clean(self, *args, **kwargs):
459
+ super().clean(*args, **kwargs)
460
+
461
+ soa_serial_auto = self.cleaned_data.get("soa_serial_auto")
462
+ soa_serial = self.cleaned_data.get("soa_serial")
463
+
464
+ if soa_serial is None:
465
+ soa_serial = self._get_default_value("soa_serial")
466
+
467
+ if soa_serial_auto is None:
468
+ if self._get_default_value("soa_serial_auto") is not None:
469
+ soa_serial_auto = self._get_default_value("soa_serial_auto")
470
+
471
+ elif soa_serial is not None:
472
+ soa_serial_auto = False
473
+
474
+ else:
475
+ raise ValidationError(
476
+ "SOA Serial Auto not set and no default value and SOA Serial available"
477
+ )
478
+
479
+ if "soa_serial_auto" in self.cleaned_data:
480
+ self.cleaned_data["soa_serial_auto"] = soa_serial_auto
481
+
482
+ if "soa_serial" in self.cleaned_data:
483
+ if soa_serial_auto:
484
+ self.cleaned_data["soa_serial"] = None
485
+ else:
486
+ self.cleaned_data["soa_serial"] = soa_serial
487
+
471
488
  class Meta:
472
489
  model = Zone
473
490
 
@@ -475,6 +492,7 @@ class ZoneImportForm(NetBoxModelImportForm):
475
492
  "view",
476
493
  "name",
477
494
  "status",
495
+ "nameservers",
478
496
  "default_ttl",
479
497
  "description",
480
498
  "soa_ttl",
@@ -141,7 +141,7 @@ def record_update_ptr_records(verbose=False):
141
141
  for record in Record.objects.filter(
142
142
  type__in=(RecordTypeChoices.A, RecordTypeChoices.AAAA)
143
143
  ):
144
- record.update_ptr_record()
144
+ record.save(update_fields=["ptr_record"])
145
145
 
146
146
 
147
147
  def record_update_ip_address(verbose=False):
@@ -0,0 +1,62 @@
1
+ from django.core.management.base import BaseCommand
2
+ from django.db.models import Max, Min
3
+
4
+ from netbox_dns.models import (
5
+ Record,
6
+ RecordTypeChoices,
7
+ )
8
+
9
+
10
+ class Command(BaseCommand):
11
+ help = "Clean up the TTLs for RRSets"
12
+
13
+ def add_arguments(self, parser):
14
+ min_max = parser.add_mutually_exclusive_group()
15
+ min_max.add_argument(
16
+ "--min",
17
+ action="store_true",
18
+ help="Use the minimum TTL of an RRSet for all Records",
19
+ )
20
+ min_max.add_argument(
21
+ "--max",
22
+ action="store_true",
23
+ help="Use the maximum TTL of an RRSet for all Records",
24
+ )
25
+
26
+ def handle(self, *model_names, **options):
27
+ self.cleanup_rrset_ttl(**options)
28
+
29
+ self.stdout.write("RRSet cleanup completed.")
30
+
31
+ def cleanup_rrset_ttl(self, **options):
32
+ verbose = options.get("verbosity") > 1
33
+
34
+ ttl_records = (
35
+ Record.objects.filter(ttl__isnull=False)
36
+ .exclude(type=RecordTypeChoices.SOA)
37
+ .exclude(type=RecordTypeChoices.PTR, maanged=True)
38
+ )
39
+ for record in ttl_records:
40
+ records = Record.objects.filter(
41
+ name=record.name,
42
+ zone=record.zone,
43
+ type=record.type,
44
+ ).exclude(type=RecordTypeChoices.PTR, maanged=True)
45
+
46
+ if records.count() == 1:
47
+ if options.get("verbosity") > 2:
48
+ self.stdout.write(f"Ignoring single record {record.id} ({record})")
49
+ continue
50
+
51
+ if options.get("max"):
52
+ ttl = records.aggregate(Max("ttl")).get("ttl__max")
53
+ else:
54
+ ttl = records.aggregate(Min("ttl")).get("ttl__min")
55
+
56
+ for record in records.exclude(ttl=ttl):
57
+ if options.get("verbosity") > 1:
58
+ self.stdout.write(
59
+ f"Updating TTL for record {record.id} ({record}) to {ttl}"
60
+ )
61
+ record.ttl = ttl
62
+ record.save(update_fields=["ttl"], update_rrset_ttl=False)
@@ -6,7 +6,7 @@ from dns import name as dns_name
6
6
 
7
7
  from django.core.exceptions import ValidationError
8
8
  from django.db import transaction, models
9
- from django.db.models import Q, ExpressionWrapper, BooleanField
9
+ from django.db.models import Q, ExpressionWrapper, BooleanField, Min
10
10
  from django.db.models.functions import Length
11
11
  from django.urls import reverse
12
12
 
@@ -38,6 +38,10 @@ from netbox_dns.validators import (
38
38
  import netbox_dns.models.zone as zone
39
39
 
40
40
 
41
+ def min_ttl(*ttl_list):
42
+ return min((ttl for ttl in ttl_list if ttl is not None), default=None)
43
+
44
+
41
45
  class RecordManager(models.Manager.from_queryset(RestrictedQuerySet)):
42
46
  """Special Manager for records providing the activity status annotation"""
43
47
 
@@ -207,7 +211,8 @@ class Record(NetBoxModel):
207
211
  try:
208
212
  name = (
209
213
  dns_name.from_text(
210
- self.name, origin=dns_name.from_text(self.zone.name, origin=None)
214
+ str(self.name),
215
+ origin=dns_name.from_text(self.zone.name, origin=None),
211
216
  )
212
217
  .relativize(dns_name.root)
213
218
  .to_unicode()
@@ -309,7 +314,7 @@ class Record(NetBoxModel):
309
314
 
310
315
  return ptr_zone
311
316
 
312
- def update_ptr_record(self, update_rfc2317_cname=True):
317
+ def update_ptr_record(self, update_rfc2317_cname=True, save_zone_serial=True):
313
318
  ptr_zone = self.ptr_zone
314
319
 
315
320
  if (
@@ -339,14 +344,16 @@ class Record(NetBoxModel):
339
344
  not ptr_record.zone.is_rfc2317_zone
340
345
  and ptr_record.rfc2317_cname_record is not None
341
346
  ):
342
- ptr_record.rfc2317_cname_record.delete()
347
+ ptr_record.rfc2317_cname_record.delete(
348
+ save_zone_serial=save_zone_serial
349
+ )
343
350
 
344
351
  with transaction.atomic():
345
352
  if ptr_record is not None:
346
353
  if ptr_record.zone.pk != ptr_zone.pk:
347
354
  if ptr_record.rfc2317_cname_record is not None:
348
355
  ptr_record.rfc2317_cname_record.delete()
349
- ptr_record.delete()
356
+ ptr_record.delete(save_zone_serial=save_zone_serial)
350
357
  ptr_record = None
351
358
 
352
359
  else:
@@ -358,7 +365,7 @@ class Record(NetBoxModel):
358
365
  ptr_record.name = ptr_name
359
366
  ptr_record.value = ptr_value
360
367
  ptr_record.ttl = self.ttl
361
- ptr_record.save()
368
+ ptr_record.save(save_zone_serial=save_zone_serial)
362
369
 
363
370
  if ptr_record is None:
364
371
  ptr_record = Record(
@@ -369,13 +376,14 @@ class Record(NetBoxModel):
369
376
  value=ptr_value,
370
377
  managed=True,
371
378
  )
372
- ptr_record.save(update_rfc2317_cname=update_rfc2317_cname)
379
+ ptr_record.save(
380
+ update_rfc2317_cname=update_rfc2317_cname,
381
+ save_zone_serial=save_zone_serial,
382
+ )
373
383
 
374
384
  self.ptr_record = ptr_record
375
- if self.pk:
376
- super().save()
377
385
 
378
- def update_rfc2317_cname_record(self):
386
+ def update_rfc2317_cname_record(self, save_zone_serial=True):
379
387
  if self.zone.rfc2317_parent_managed:
380
388
  cname_name = dns_name.from_text(
381
389
  ipaddress.ip_address(self.ip_address).reverse_pointer
@@ -385,7 +393,13 @@ class Record(NetBoxModel):
385
393
  self.rfc2317_cname_record.name = cname_name
386
394
  self.rfc2317_cname_record.zone = self.zone.rfc2317_parent_zone
387
395
  self.rfc2317_cname_record.value = self.fqdn
388
- self.rfc2317_cname_record.save()
396
+ self.rfc2317_cname_record.ttl = min_ttl(
397
+ self.rfc2317_cname_record.rfc2317_ptr_records.exclude(pk=self.pk)
398
+ .aggregate(Min("ttl"))
399
+ .get("ttl__min"),
400
+ self.ttl,
401
+ )
402
+ self.rfc2317_cname_record.save(save_zone_serial=save_zone_serial)
389
403
  else:
390
404
  rfc2317_cname_record = Record.objects.filter(
391
405
  name=cname_name,
@@ -394,20 +408,34 @@ class Record(NetBoxModel):
394
408
  managed=True,
395
409
  value=self.fqdn,
396
410
  ).first()
397
- if rfc2317_cname_record is None:
398
- rfc2317_cname_record = Record.objects.create(
411
+
412
+ if rfc2317_cname_record is not None:
413
+ rfc2317_cname_record.ttl = min_ttl(
414
+ rfc2317_cname_record.rfc2317_ptr_records.exclude(pk=self.pk)
415
+ .aggregate(Min("ttl"))
416
+ .get("ttl__min"),
417
+ self.ttl,
418
+ )
419
+ rfc2317_cname_record.save(
420
+ update_fields=["ttl"], save_zone_serial=save_zone_serial
421
+ )
422
+
423
+ else:
424
+ rfc2317_cname_record = Record(
399
425
  name=cname_name,
400
426
  type=RecordTypeChoices.CNAME,
401
427
  zone=self.zone.rfc2317_parent_zone,
402
428
  managed=True,
403
429
  value=self.fqdn,
430
+ ttl=self.ttl,
404
431
  )
432
+ rfc2317_cname_record.save(save_zone_serial=save_zone_serial)
405
433
 
406
434
  self.rfc2317_cname_record = rfc2317_cname_record
407
435
 
408
436
  else:
409
437
  if self.rfc2317_cname_record is not None:
410
- self.rfc2317_cname_record.delete()
438
+ self.rfc2317_cname_record.delete(save_zone_serial=save_zone_serial)
411
439
  self.rfc2317_cname_record = None
412
440
 
413
441
  def validate_name(self):
@@ -419,7 +447,7 @@ class Record(NetBoxModel):
419
447
  zone.to_unicode()
420
448
  name.to_unicode()
421
449
 
422
- self.name = name.to_text()
450
+ self.name = name.relativize(zone).to_text()
423
451
 
424
452
  except dns.exception.DNSException as exc:
425
453
  raise ValidationError(
@@ -477,7 +505,7 @@ class Record(NetBoxModel):
477
505
  }
478
506
  ) from None
479
507
 
480
- def check_unique(self):
508
+ def check_unique_record(self):
481
509
  if not get_plugin_config("netbox_dns", "enforce_unique_records", False):
482
510
  return
483
511
 
@@ -491,6 +519,10 @@ class Record(NetBoxModel):
491
519
  value=self.value,
492
520
  status__in=Record.ACTIVE_STATUS_LIST,
493
521
  )
522
+
523
+ if self.pk is not None:
524
+ records = records.exclude(pk=self.pk)
525
+
494
526
  if len(records):
495
527
  raise ValidationError(
496
528
  {
@@ -498,6 +530,64 @@ class Record(NetBoxModel):
498
530
  }
499
531
  ) from None
500
532
 
533
+ def check_unique_rrset_ttl(self):
534
+ if self.pk is not None:
535
+ return
536
+
537
+ if not get_plugin_config("netbox_dns", "enforce_unique_rrset_ttl", False):
538
+ return
539
+
540
+ if self.type == RecordTypeChoices.PTR and self.managed:
541
+ return
542
+
543
+ records = (
544
+ Record.objects.filter(
545
+ zone=self.zone,
546
+ name=self.name,
547
+ type=self.type,
548
+ )
549
+ .exclude(ttl=self.ttl)
550
+ .exclude(type=RecordTypeChoices.PTR, managed=True)
551
+ )
552
+
553
+ if not records.exists():
554
+ return
555
+
556
+ conflicting_ttls = ", ".join(set(str(record.ttl) for record in records))
557
+ raise ValidationError(
558
+ {
559
+ "ttl": f"There is at least one active {self.type} record for name {self.name} in zone {self.zone} and TTL is different ({conflicting_ttls})."
560
+ }
561
+ ) from None
562
+
563
+ def update_rrset_ttl(self, ttl=None):
564
+ if self.pk is None:
565
+ return
566
+
567
+ if not get_plugin_config("netbox_dns", "enforce_unique_rrset_ttl", False):
568
+ return
569
+
570
+ if self.type == RecordTypeChoices.PTR and self.managed:
571
+ return
572
+
573
+ if ttl is None:
574
+ ttl = self.ttl
575
+
576
+ records = (
577
+ Record.objects.filter(
578
+ zone=self.zone,
579
+ name=self.name,
580
+ type=self.type,
581
+ )
582
+ .exclude(pk=self.pk)
583
+ .exclude(ttl=ttl)
584
+ .exclude(type=RecordTypeChoices.PTR, managed=True)
585
+ )
586
+
587
+ for record in records:
588
+ record.ttl = ttl
589
+ record.save(update_fields=["ttl"], update_rrset_ttl=False)
590
+
501
591
  def clean_fields(self, *args, **kwargs):
502
592
  self.type = self.type.upper()
503
593
  super().clean_fields(*args, **kwargs)
@@ -505,7 +595,9 @@ class Record(NetBoxModel):
505
595
  def clean(self, *args, **kwargs):
506
596
  self.validate_name()
507
597
  self.validate_value()
508
- self.check_unique()
598
+ self.check_unique_record()
599
+ if self.pk is None:
600
+ self.check_unique_rrset_ttl()
509
601
 
510
602
  if not self.is_active:
511
603
  return
@@ -580,14 +672,24 @@ class Record(NetBoxModel):
580
672
  }
581
673
  ) from None
582
674
 
583
- def save(self, *args, update_rfc2317_cname=True, **kwargs):
675
+ def save(
676
+ self,
677
+ *args,
678
+ update_rfc2317_cname=True,
679
+ save_zone_serial=True,
680
+ update_rrset_ttl=True,
681
+ **kwargs,
682
+ ):
584
683
  self.full_clean()
585
684
 
685
+ if self.pk is not None and update_rrset_ttl:
686
+ self.update_rrset_ttl()
687
+
586
688
  if self.is_ptr_record:
587
689
  if self.zone.is_rfc2317_zone:
588
690
  self.ip_address = self.address_from_rfc2317_name
589
691
  if update_rfc2317_cname:
590
- self.update_rfc2317_cname_record()
692
+ self.update_rfc2317_cname_record(save_zone_serial=save_zone_serial)
591
693
  else:
592
694
  self.ip_address = self.address_from_name
593
695
 
@@ -597,7 +699,10 @@ class Record(NetBoxModel):
597
699
  self.ip_address = None
598
700
 
599
701
  if self.is_address_record:
600
- self.update_ptr_record()
702
+ self.update_ptr_record(
703
+ update_rfc2317_cname=update_rfc2317_cname,
704
+ save_zone_serial=save_zone_serial,
705
+ )
601
706
  elif self.ptr_record is not None:
602
707
  self.ptr_record.delete()
603
708
  self.ptr_record = None
@@ -606,15 +711,24 @@ class Record(NetBoxModel):
606
711
 
607
712
  zone = self.zone
608
713
  if self.type != RecordTypeChoices.SOA and zone.soa_serial_auto:
609
- zone.update_serial()
714
+ zone.update_serial(save_zone_serial=save_zone_serial)
610
715
 
611
- def delete(self, *args, **kwargs):
716
+ def delete(self, *args, save_zone_serial=True, **kwargs):
612
717
  if self.rfc2317_cname_record:
613
- if (
614
- self.rfc2317_cname_record.pk
615
- and self.rfc2317_cname_record.rfc2317_ptr_records.count() == 1
616
- ):
617
- self.rfc2317_cname_record.delete()
718
+ if self.rfc2317_cname_record.pk:
719
+ if self.rfc2317_cname_record.rfc2317_ptr_records.count() == 1:
720
+ self.rfc2317_cname_record.delete()
721
+ else:
722
+ self.rfc2317_cname_record.ttl = (
723
+ self.rfc2317_cname_record.rfc2317_ptr_records.exclude(
724
+ pk=self.pk
725
+ )
726
+ .aggregate(Min("ttl"))
727
+ .get("ttl__min")
728
+ )
729
+ self.rfc2317_cname_record.save(
730
+ update_fields=["ttl"], save_zone_serial=save_zone_serial
731
+ )
618
732
 
619
733
  if self.ptr_record:
620
734
  self.ptr_record.delete()
@@ -623,7 +737,7 @@ class Record(NetBoxModel):
623
737
 
624
738
  zone = self.zone
625
739
  if zone.soa_serial_auto:
626
- zone.update_serial()
740
+ zone.update_serial(save_zone_serial=save_zone_serial)
627
741
 
628
742
 
629
743
  @register_search
netbox_dns/models/zone.py CHANGED
@@ -249,6 +249,8 @@ class Zone(NetBoxModel):
249
249
  null=True,
250
250
  )
251
251
 
252
+ soa_serial_dirty = False
253
+
252
254
  objects = ZoneManager()
253
255
 
254
256
  clone_fields = [
@@ -470,11 +472,24 @@ class Zone(NetBoxModel):
470
472
 
471
473
  return soa_serial
472
474
 
473
- def update_serial(self):
475
+ def update_serial(self, save_zone_serial=True):
476
+ if not self.soa_serial_auto:
477
+ return
478
+
474
479
  self.last_updated = datetime.now()
475
480
  self.soa_serial = ceil(datetime.now().timestamp())
476
- self.update_soa_record()
477
- super().save()
481
+
482
+ if save_zone_serial:
483
+ super().save(update_fields=["soa_serial", "last_updated"])
484
+ self.soa_serial_dirty = False
485
+ self.update_soa_record()
486
+ else:
487
+ self.soa_serial_dirty = True
488
+
489
+ def save_soa_serial(self):
490
+ if self.soa_serial_auto and self.soa_serial_dirty:
491
+ super().save(update_fields=["soa_serial", "last_updated"])
492
+ self.soa_serial_dirty = False
478
493
 
479
494
  @property
480
495
  def network_from_name(self):
@@ -503,28 +518,45 @@ class Zone(NetBoxModel):
503
518
  if rfc2317_parent_zone is None:
504
519
  self.rfc2317_parent_managed = False
505
520
  self.rfc2317_parent_zone = None
506
- self.save()
521
+ self.save(
522
+ update_fields=["rfc2317_parent_zone", "rfc2317_parent_managed"]
523
+ )
507
524
 
508
525
  elif self.rfc2317_parent_zone != rfc2317_parent_zone:
509
526
  self.rfc2317_parent_zone = rfc2317_parent_zone
510
- self.save()
527
+ self.save(update_fields=["rfc2317_parent_zone"])
511
528
 
512
- ptr_records = self.record_set.filter(type=record.RecordTypeChoices.PTR)
529
+ ptr_records = self.record_set.filter(
530
+ type=record.RecordTypeChoices.PTR
531
+ ).prefetch_related("zone", "rfc2317_cname_record")
532
+ ptr_zones = {ptr_record.zone for ptr_record in ptr_records}
513
533
 
514
534
  if self.rfc2317_parent_managed:
515
535
  for ptr_record in ptr_records:
516
- ptr_record.save()
536
+ ptr_record.save(save_zone_serial=False)
517
537
 
538
+ self.rfc2317_parent_zone.save_soa_serial()
539
+ self.rfc2317_parent_zone.update_soa_record()
518
540
  else:
519
541
  cname_records = {
520
542
  ptr_record.rfc2317_cname_record
521
543
  for ptr_record in ptr_records
522
544
  if ptr_record.rfc2317_cname_record is not None
523
545
  }
546
+ cname_zones = {cname_record.zone for cname_record in cname_records}
547
+
524
548
  for ptr_record in ptr_records:
525
- ptr_record.save(update_rfc2317_cname=False)
549
+ ptr_record.save(update_rfc2317_cname=False, save_zone_serial=False)
526
550
  for cname_record in cname_records:
527
- cname_record.delete()
551
+ cname_record.delete(save_zone_serial=False)
552
+
553
+ for cname_zone in cname_zones:
554
+ cname_zone.save_soa_serial()
555
+ cname_zone.update_soa_record()
556
+
557
+ for ptr_zone in ptr_zones:
558
+ ptr_zone.save_soa_serial()
559
+ ptr_zone.update_soa_record()
528
560
 
529
561
  def clean(self, *args, **kwargs):
530
562
  self.check_name_conflict()
@@ -643,7 +675,12 @@ class Zone(NetBoxModel):
643
675
  )
644
676
 
645
677
  for address_record in address_records:
646
- address_record.update_ptr_record()
678
+ address_record.save(
679
+ update_fields=["ptr_record"], save_zone_serial=False
680
+ )
681
+
682
+ for zone in zones:
683
+ zone.save_soa_serial()
647
684
 
648
685
  if self.arpa_network.version == 4:
649
686
  rfc2317_child_zones = Zone.objects.filter(
@@ -673,7 +710,14 @@ class Zone(NetBoxModel):
673
710
  )
674
711
 
675
712
  for address_record in address_records:
676
- address_record.update_ptr_record(update_rfc2317_cname=False)
713
+ address_record.save(
714
+ update_fields=["ptr_record"],
715
+ update_rfc2317_cname=False,
716
+ save_zone_serial=False,
717
+ )
718
+
719
+ for zone in zones:
720
+ zone.save_soa_serial()
677
721
 
678
722
  self.update_rfc2317_parent_zone()
679
723
 
@@ -681,7 +725,7 @@ class Zone(NetBoxModel):
681
725
  for address_record in self.record_set.filter(
682
726
  type__in=(record.RecordTypeChoices.A, record.RecordTypeChoices.AAAA)
683
727
  ):
684
- address_record.update_ptr_record()
728
+ address_record.save(update_fields=["ptr_record"])
685
729
 
686
730
  # Fix name in IP Address when zone name is changed
687
731
  if (
@@ -694,11 +738,14 @@ class Zone(NetBoxModel):
694
738
  ip.dns_name = f'{ip.custom_field_data["ipaddress_dns_record_name"]}.{self.name}'
695
739
  ip.save(update_fields=["dns_name"])
696
740
 
741
+ self.save_soa_serial()
697
742
  self.update_soa_record()
698
743
 
699
744
  def delete(self, *args, **kwargs):
700
745
  with transaction.atomic():
701
- address_records = self.record_set.filter(ptr_record__isnull=False)
746
+ address_records = self.record_set.filter(
747
+ ptr_record__isnull=False
748
+ ).prefetch_related("ptr_record")
702
749
  for address_record in address_records:
703
750
  address_record.ptr_record.delete()
704
751
 
@@ -715,8 +762,13 @@ class Zone(NetBoxModel):
715
762
  for ptr_record in ptr_records
716
763
  if ptr_record.rfc2317_cname_record is not None
717
764
  }
765
+ cname_zones = {cname_record.zone for cname_record in cname_records}
766
+
718
767
  for cname_record in cname_records:
719
- cname_record.delete()
768
+ cname_record.delete(save_zone_serial=False)
769
+ for cname_zone in cname_zones:
770
+ cname_zone.save_soa_serial()
771
+ cname_zone.update_soa_record()
720
772
 
721
773
  rfc2317_child_zones = [
722
774
  child_zone.pk for child_zone in self.rfc2317_child_zones.all()
@@ -733,11 +785,25 @@ class Zone(NetBoxModel):
733
785
 
734
786
  super().delete(*args, **kwargs)
735
787
 
736
- for address_record in record.Record.objects.filter(pk__in=update_records):
737
- address_record.update_ptr_record()
738
-
739
- for child_zone in Zone.objects.filter(pk__in=rfc2317_child_zones):
740
- child_zone.update_rfc2317_parent_zone()
788
+ address_records = record.Record.objects.filter(
789
+ pk__in=update_records
790
+ ).prefetch_related("zone")
791
+
792
+ for address_record in address_records:
793
+ address_record.save(save_zone_serial=False)
794
+ for address_zone in {address_record.zone for address_record in address_records}:
795
+ address_zone.save_soa_serial()
796
+ address_zone.update_soa_record()
797
+
798
+ rfc2317_child_zones = Zone.objects.filter(pk__in=rfc2317_child_zones)
799
+ if rfc2317_child_zones:
800
+ for child_zone in rfc2317_child_zones:
801
+ child_zone.update_rfc2317_parent_zone()
802
+
803
+ new_rfc2317_parent_zone = rfc2317_child_zones.first().rfc2317_parent_zone
804
+ if new_rfc2317_parent_zone is not None:
805
+ new_rfc2317_parent_zone.save_soa_serial()
806
+ new_rfc2317_parent_zone.update_soa_record()
741
807
 
742
808
 
743
809
  @receiver(m2m_changed, sender=Zone.nameservers.through)
@@ -121,15 +121,27 @@ def ip_address_update_dns_information(instance, **kwargs):
121
121
 
122
122
  name, ttl, disable_ptr, zone_id = ipaddress_cf_data(instance)
123
123
 
124
+ previous_zone_id = None
125
+ if instance.pk is not None:
126
+ try:
127
+ old_instance = IPAddress.objects.get(pk=instance.pk)
128
+ previous_zone_id = old_instance.custom_field_data.get(
129
+ "ipaddress_dns_zone_id"
130
+ )
131
+ except IPAddress.DoesNotExist:
132
+ pass
133
+
124
134
  if zone_id is not None:
125
135
  instance.dns_name = f"{name}.{Zone.objects.get(pk=zone_id).name}"
126
136
  else:
127
- instance.dns_name = ""
128
137
  instance.custom_field_data["ipaddress_dns_record_name"] = None
129
138
  instance.custom_field_data["ipaddress_dns_record_ttl"] = None
130
139
  instance.custom_field_data["ipaddress_dns_record_disable_ptr"] = False
131
140
  instance.custom_field_data["ipaddress_dns_zone_id"] = None
132
141
 
142
+ if previous_zone_id is not None:
143
+ instance.dns_name = ""
144
+
133
145
 
134
146
  #
135
147
  # Handle DNS record operation after IPAddress has been created or modified
@@ -22,6 +22,10 @@
22
22
  <h5 class="card-header">Contact Details</h5>
23
23
  <div class="card-body">
24
24
  <table class="table table-hover attr-table">
25
+ <tr>
26
+ <th scope="row">Address</th>
27
+ <td>{{ object.address }}</td>
28
+ </tr>
25
29
  <tr>
26
30
  <th scope="row">Referral URL</th>
27
31
  <td><a href="{{ object.referral_url }}">{{ object.referral_url }}</a></td>
@@ -120,10 +120,17 @@
120
120
  <th scope="row">Responsible</th>
121
121
  <td>{{ object.soa_rname }}</td>
122
122
  </tr>
123
+ {% if object.soa_serial_auto %}
124
+ <tr>
125
+ <th scope="row">Serial (auto-generated)</th>
126
+ <td>{{ object.soa_serial }}</td>
127
+ </tr>
128
+ {% else %}
123
129
  <tr>
124
130
  <th scope="row">Serial</th>
125
131
  <td>{{ object.soa_serial }}</td>
126
132
  </tr>
133
+ {% endif %}
127
134
  <tr>
128
135
  <th scope="row">Refresh</th>
129
136
  <td>{{ object.soa_refresh }}</td>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: netbox-plugin-dns
3
- Version: 0.22.3
3
+ Version: 0.22.5
4
4
  Summary: NetBox DNS is a NetBox plugin for managing DNS data.
5
5
  Home-page: https://github.com/peteeckel/netbox-plugin-dns
6
6
  License: MIT
@@ -8,6 +8,7 @@ Keywords: netbox,netbox-plugin,dns
8
8
  Author: Peter Eckel
9
9
  Author-email: pete@netbox-dns.org
10
10
  Requires-Python: >=3.8,<4.0
11
+ Classifier: Development Status :: 5 - Production/Stable
11
12
  Classifier: License :: OSI Approved :: MIT License
12
13
  Classifier: Programming Language :: Python :: 3
13
14
  Classifier: Programming Language :: Python :: 3.8
@@ -19,28 +20,30 @@ Requires-Dist: dnspython (>=2.2.1,<3.0.0)
19
20
  Project-URL: Repository, https://github.com/peteeckel/netbox-plugin-dns
20
21
  Description-Content-Type: text/markdown
21
22
 
22
- <h1 align="center">NetBox DNS</h1>
23
-
24
- <p align="center"><i>NetBox DNS is a NetBox plugin for managing DNS views, zones, name servers and records.</i></p>
23
+ # NetBox DNS
24
+ The NetBox DNS plugin enables NetBox to manage operational DNS data such as name servers, zones, records and views, as well as registration data for domains. It can automate tasks like creating PTR records, generating zone serial numbers, NS and SOA records, as well as validate names and values values for resource records to ensure zone data is consistent, current and conforming to the relevant RFCs.
25
25
 
26
26
  <div align="center">
27
27
  <a href="https://pypi.org/project/netbox-plugin-dns/"><img src="https://img.shields.io/pypi/v/netbox-plugin-dns" alt="PyPi"/></a>
28
- <a href="https://github.com/peteeckel/netbox-plugin-dns/stargazers"><img src="https://img.shields.io/github/stars/peteeckel/netbox-plugin-dns" alt="Stars Badge"/></a>
29
- <a href="https://github.com/peteeckel/netbox-plugin-dns/network/members"><img src="https://img.shields.io/github/forks/peteeckel/netbox-plugin-dns" alt="Forks Badge"/></a>
30
- <a href="https://github.com/peteeckel/netbox-plugin-dns/pulls"><img src="https://img.shields.io/github/issues-pr/peteeckel/netbox-plugin-dns" alt="Pull Requests Badge"/></a>
28
+ <a href="https://github.com/peteeckel/netbox-plugin-dns/stargazers"><img src="https://img.shields.io/github/stars/peteeckel/netbox-plugin-dns?style=flat" alt="Stars Badge"/></a>
29
+ <a href="https://github.com/peteeckel/netbox-plugin-dns/network/members"><img src="https://img.shields.io/github/forks/peteeckel/netbox-plugin-dns?style=flat" alt="Forks Badge"/></a>
31
30
  <a href="https://github.com/peteeckel/netbox-plugin-dns/issues"><img src="https://img.shields.io/github/issues/peteeckel/netbox-plugin-dns" alt="Issues Badge"/></a>
31
+ <a href="https://github.com/peteeckel/netbox-plugin-dns/pulls"><img src="https://img.shields.io/github/issues-pr/peteeckel/netbox-plugin-dns" alt="Pull Requests Badge"/></a>
32
32
  <a href="https://github.com/peteeckel/netbox-plugin-dns/graphs/contributors"><img alt="GitHub contributors" src="https://img.shields.io/github/contributors/peteeckel/netbox-plugin-dns?color=2b9348"></a>
33
33
  <a href="https://github.com/peteeckel/netbox-plugin-dns/blob/master/LICENSE"><img src="https://img.shields.io/github/license/peteeckel/netbox-plugin-dns?color=2b9348" alt="License Badge"/></a>
34
+ <a href="https://pepy.tech/project/netbox-plugin-dns"><img alt="Downloads" src="https://static.pepy.tech/badge/netbox-plugin-dns"></a>
35
+ <a href="https://pepy.tech/project/netbox-plugin-dns"><img alt="Downloads/Week" src="https://static.pepy.tech/badge/netbox-plugin-dns/month"></a>
36
+ <a href="https://pepy.tech/project/netbox-plugin-dns"><img alt="Downloads/Month" src="https://static.pepy.tech/badge/netbox-plugin-dns/week"></a>
34
37
  </div>
35
38
 
36
39
  ## Features
37
40
 
38
41
  * Manage name servers, zones and records
39
42
  * Automatically generate SOA and NS records for zones
40
- * Automatically create and update PTR records for IPv4 and IPv6 records
41
- * Optionally organize DNS zones in views for split horizon DNS and multi-site deployments
42
- * Optionally maintain domain registrar and registrant information for zones
43
- * Maintain RFC2317 reverse zones for IPv4 prefixes with a network mask length longer than 24 bits
43
+ * Automatically create and update PTR records for IPv4 and IPv6 address records
44
+ * Organize DNS zones in views for split horizon DNS and multi-site deployments
45
+ * Manage domain registrar and registrant information for domains related to zones
46
+ * Manage RFC2317 reverse zones for IPv4 prefixes with a network mask length longer than 24 bits
44
47
 
45
48
  NetBox DNS is using the standardized NetBox plugin interface, so it also takes advantage of the NetBox tagging and change log features.
46
49
 
@@ -1,6 +1,6 @@
1
- netbox_dns/__init__.py,sha256=Bsztyx4i5bssOgcR0Lt8a-HmYJhF9N6GKbpoFDyNKTY,2778
1
+ netbox_dns/__init__.py,sha256=Wnz89UxRISvgUOkm-cZI50tMo6taJh14iZnSb1Wx0L4,2818
2
2
  netbox_dns/api/nested_serializers.py,sha256=XB7bcCjVMPYrumJWgRicj06PukQ2UCBjdr84AIUJuVQ,3291
3
- netbox_dns/api/serializers.py,sha256=03BYJMUjIK3edvJv1kcU8jt440kIm-YH6ZAw2T374HM,8160
3
+ netbox_dns/api/serializers.py,sha256=H5Vm1O6C4SsxHVzdooYiu5ak3ykxXo-L1mT6ISWVRV8,8183
4
4
  netbox_dns/api/urls.py,sha256=R9VmmWtdrjvr35i5d_SfZK2lGn6JzmPuWEKTQlZ8MJo,575
5
5
  netbox_dns/api/views.py,sha256=DjovvTfS4F2Fq2Ahea6f4LBJiWr01ajk-wSZHNTya5I,3527
6
6
  netbox_dns/apps.py,sha256=JCW5eS-AQBUubDJve1DjP-IRFKTFGQh1NLGWzJpC5MI,151
@@ -12,16 +12,16 @@ netbox_dns/filters/__init__.py,sha256=Aw8HrCTjaJfu5JSwJsQRHfOUz4zKwAmZNByT9q6BrF
12
12
  netbox_dns/filters/contact.py,sha256=_onZ6G2KKgfvm9Emg_kng2RPETRMfbTyIR8Tvs9d2YM,1027
13
13
  netbox_dns/filters/nameserver.py,sha256=sDKsrluSmUZ0aTw_wagYJAh1g5fKzyzHXbJu6DaK1b8,522
14
14
  netbox_dns/filters/record.py,sha256=DH1x4bNkBAN_gVNklAjorcgHoJmH_AXEf1sBNvJS6Kc,1691
15
- netbox_dns/filters/registrar.py,sha256=kAvE2qNah0t5KtW-QniSw1HyhUgNnlvNSyVpHdUbSng,888
15
+ netbox_dns/filters/registrar.py,sha256=8UXJXUb0XmkPyPQ2oACAhzhmZtAmvM2c4hTWMDLuBEo,911
16
16
  netbox_dns/filters/view.py,sha256=Sji4DKy0VKk8BLEdk8xCe8su3rUBtXeJUsUAee0IsOs,497
17
17
  netbox_dns/filters/zone.py,sha256=gNKTLll0nvdkzjEVl5c4O1hncU13IFVKn61iXThLYJA,3556
18
18
  netbox_dns/forms/__init__.py,sha256=Aw8HrCTjaJfu5JSwJsQRHfOUz4zKwAmZNByT9q6BrFU,136
19
19
  netbox_dns/forms/contact.py,sha256=Z8llYOpcGns8ZerOLsldpY836pfjO7427rmAZ4T8Kyg,4492
20
20
  netbox_dns/forms/nameserver.py,sha256=LUYV_tna667flgPdQevDFniMdXZUYRUG8iqCx7HKdxQ,2027
21
21
  netbox_dns/forms/record.py,sha256=TAQ-rZ55lmWE_piogiE5TwTv1v4r5T2yZpNT8K7GrPU,6171
22
- netbox_dns/forms/registrar.py,sha256=9yGh1BSUbgdSGxM1QTOUeff8ExThX6QnDh_GpVTHjX4,2437
22
+ netbox_dns/forms/registrar.py,sha256=Vkw8cafXQRDHf7K7OFPn62R-_BzjNOofnhLmL2Hw7mg,2636
23
23
  netbox_dns/forms/view.py,sha256=KEWlUo8-oXP_QsqT5fW_UK-ZoX9g-f4CqRnJ_P6LHco,1627
24
- netbox_dns/forms/zone.py,sha256=RoRJ1g1zOuC6v_6NrSZe2ILQB8InxdoQCjJYSam1k5U,20984
24
+ netbox_dns/forms/zone.py,sha256=Y9KfeSCzFrJU1LxQjxPcLANu3NEcaHjvxnyMqgna_FY,21702
25
25
  netbox_dns/graphql/__init__.py,sha256=v3lLvVoP-JQbEZ1NY49u1TNqvanF-4TqOtjHXrbsTvU,811
26
26
  netbox_dns/graphql/contact.py,sha256=2iyuvCxG09CLDUMhan5vR3uFZxCVzKNZBrotr_zoUVY,497
27
27
  netbox_dns/graphql/nameserver.py,sha256=mlQw1Po_Ax_fjyyXVBetyxlFLrCqmptYDgOspZvYtP4,527
@@ -30,7 +30,8 @@ netbox_dns/graphql/registrar.py,sha256=TEQSNT8CeIqquzySMRluGk3I2t9rYgQCgDbLJ0gnL
30
30
  netbox_dns/graphql/schema.py,sha256=D_dDusogaI55tmGx_dr1owsgzXS5fd2S-kPZ7xcXxs8,221
31
31
  netbox_dns/graphql/view.py,sha256=S_61hYlQCtPQen1lI1UQs38UBWKQTaWfUxzlbpO07zA,467
32
32
  netbox_dns/graphql/zone.py,sha256=QDBxocezhLSHBGDV4RJnmarBfOsiUTeE9KzBGJ3gJi8,467
33
- netbox_dns/management/commands/cleanup_database.py,sha256=fGkzOPXDsZWTlwetnerzkMmdpluV4vo34XXB_z8-yHU,6035
33
+ netbox_dns/management/commands/cleanup_database.py,sha256=eggyMZrRg--cXDQJ-boofHboG1gJTs8j-oldhn6EuCo,6050
34
+ netbox_dns/management/commands/cleanup_rrset_ttl.py,sha256=gCvwH6hGw32YJtcy_pEWIP29PSRMW23-qzB8SHEdixQ,2077
34
35
  netbox_dns/management/commands/setup_coupling.py,sha256=Yn1nffPR7fBgB6WWBdDqdnp3k8z1QK8k6qi9xbx4U6Y,4580
35
36
  netbox_dns/management/commands/update_soa.py,sha256=qvlApMngTVpauj0CU0yeOy9r3lxxDciKorMxFsyvQhs,661
36
37
  netbox_dns/migrations/0001_initial.py,sha256=R9FbIQ7nO1ROb12NL8YQDkFQuP1r6-TtMcPwg4rwjus,4153
@@ -66,13 +67,13 @@ netbox_dns/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
66
67
  netbox_dns/models/__init__.py,sha256=Q7UIEe2vGh18AZN4er6CykciwXPQGgUq0L-9718wZqU,182
67
68
  netbox_dns/models/contact.py,sha256=Mzj4SR3fczOE96etWjObJElk0RVQetNuXZVtm8sS1h8,2849
68
69
  netbox_dns/models/nameserver.py,sha256=SjKUbMRNE3TSDzmxbKFR9b4AfYPS0UPj0QeO7XpwNRk,2941
69
- netbox_dns/models/record.py,sha256=2eI8UyzRUrDjO61m0rCsAFDr8SjxPsbQnyyN2dEWRSE,19597
70
+ netbox_dns/models/record.py,sha256=nOKjaoVuyMVIfwNw_V4hYYd71fcE4UyXThU983KrXaY,23629
70
71
  netbox_dns/models/registrar.py,sha256=ByKHeqH5KfswqOLjya8DZExJ1omSKFHMfCjIIYfnwTo,1416
71
72
  netbox_dns/models/view.py,sha256=ljs3Q2xQR63ZOSyja5H7DEdFbm7MX2ZjlR6uNVrAsVo,920
72
- netbox_dns/models/zone.py,sha256=F-SMgAScdDlEEk4cp826xfktSGv9OjV8QxqrAnAP0NE,23743
73
+ netbox_dns/models/zone.py,sha256=Ek7Jui3AldNHVAHowbQhC47QoXv0IgkhLzFziN14UUI,26333
73
74
  netbox_dns/navigation.py,sha256=IutEr_TcPgDGzqTT1ZzV4IUABLSOFU9v364BfpCqbro,4587
74
75
  netbox_dns/signals/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
- netbox_dns/signals/ipam_coupling.py,sha256=yQnJO5eHZGAhJxC1W5LfoQluVUcac9_oZ9XFFonuz2w,5365
76
+ netbox_dns/signals/ipam_coupling.py,sha256=xbb37_77ZqtgT3mKXZp18QRduqZuiAuYGPk8R9d7SVc,5727
76
77
  netbox_dns/tables/__init__.py,sha256=Aw8HrCTjaJfu5JSwJsQRHfOUz4zKwAmZNByT9q6BrFU,136
77
78
  netbox_dns/tables/contact.py,sha256=HpblHKrlQqQAPC59gvw8wUV3tc6UBAphHPE6CfN2uVM,747
78
79
  netbox_dns/tables/nameserver.py,sha256=7Ap0px5gAHtgk5XFMpZCHccy9N-v8UKy19GP7fdWuPc,819
@@ -86,7 +87,7 @@ netbox_dns/templates/netbox_dns/nameserver.html,sha256=3AJVbsuhKg4Jy74rlvwrGSHd_
86
87
  netbox_dns/templates/netbox_dns/record/managed.html,sha256=G6LPG1koUGuzUiwYdv1okdVa4sKaofiQegDBnsFL0kA,89
87
88
  netbox_dns/templates/netbox_dns/record/related.html,sha256=Aqor8uGcuHQTHjlX-Xmni2Yp4N7lOBrMOqQiszrQOC0,742
88
89
  netbox_dns/templates/netbox_dns/record.html,sha256=1XOp9Rf7KiV736RKGh7A8LAYkrfI7veG5SsEhaeMghw,6613
89
- netbox_dns/templates/netbox_dns/registrar.html,sha256=Ijy8w0P3UPiyVFkIjk92d4n3GCqsm-0kQjk-QJ-2BCw,1956
90
+ netbox_dns/templates/netbox_dns/registrar.html,sha256=rSShbH68nP0r8EUHn0-TZOsUj6pg7hmfvM7h2tqouUA,2130
90
91
  netbox_dns/templates/netbox_dns/related_dns_objects.html,sha256=KSzlnw1cStrJa3poKkwrt_ycIH0oH0STWIHRNy3ks4g,806
91
92
  netbox_dns/templates/netbox_dns/view.html,sha256=_lDjd2xY3upfGpNpemXXMzgsaKZlX3-PzPPAdYkIjvs,1350
92
93
  netbox_dns/templates/netbox_dns/zone/base.html,sha256=pucf_b7iGg4hCXqwIbR0_WtEQdeH2LQtTCRTSoeBhFc,460
@@ -95,7 +96,7 @@ netbox_dns/templates/netbox_dns/zone/managed_record.html,sha256=5P85eJuQOB7omih2
95
96
  netbox_dns/templates/netbox_dns/zone/record.html,sha256=1oIRGXOZAjwmTMkTgArfKyVrmL54Sh_IN7IAF3qYEKM,2218
96
97
  netbox_dns/templates/netbox_dns/zone/registration.html,sha256=3R5uqLXZxIJkp9_LVnTTgTV61mITfDPDDNofV7s4d1k,1283
97
98
  netbox_dns/templates/netbox_dns/zone/rfc2317_child_zone.html,sha256=kxIrhn0gghb4l1eZhpsICdfkSYegqSXR9zFIWiZLypA,527
98
- netbox_dns/templates/netbox_dns/zone.html,sha256=mB-oyl7vGgtr-DksgoiU7WSx-88_oD52s4YQBrOIajc,6895
99
+ netbox_dns/templates/netbox_dns/zone.html,sha256=_lQhr00_V8ThadlTuTm5zPkcvyjYQxxYGHQsfTOgDCs,7187
99
100
  netbox_dns/urls.py,sha256=NxqvnRxLM6VwUBTY6KA8u-PRjNklmBai7gy6NXc-1xo,9172
100
101
  netbox_dns/utilities/__init__.py,sha256=8DqDx-99kejykwaIt_29W5KgY8RvWBOk6526vrf22UQ,1967
101
102
  netbox_dns/utilities/ipam_coupling.py,sha256=LhdFafF4OAOU8rgROOqY5Dt7XDT_SUZP2JuIqCQYVqw,3573
@@ -109,7 +110,7 @@ netbox_dns/views/record.py,sha256=2xzZnCWmCZIyEVjQfYo0rByhBNItPxNFiehOwDl8U6w,25
109
110
  netbox_dns/views/registrar.py,sha256=aznSKt1L5tILMLGgcZiBR7u7B8rNl-jM1B2-N0fTeK8,2072
110
111
  netbox_dns/views/view.py,sha256=uUvtlNEh5MYoEALvWWaCOqj_Zj8dpGOL2PUyg-UPfEA,1895
111
112
  netbox_dns/views/zone.py,sha256=SyttTAgrPPzf1jIT1B4RexCLdXYjSmPIZsefO_zog1Q,4587
112
- netbox_plugin_dns-0.22.3.dist-info/LICENSE,sha256=tziMJKpkMbySr09L6bIwsu7Ca9ICoqpMO3yAXgEMQA4,1076
113
- netbox_plugin_dns-0.22.3.dist-info/METADATA,sha256=oY3JFLGQ3NGo0b4u2RHWojzl1hdphQL9jyTfabgqnrY,3801
114
- netbox_plugin_dns-0.22.3.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
115
- netbox_plugin_dns-0.22.3.dist-info/RECORD,,
113
+ netbox_plugin_dns-0.22.5.dist-info/LICENSE,sha256=tziMJKpkMbySr09L6bIwsu7Ca9ICoqpMO3yAXgEMQA4,1076
114
+ netbox_plugin_dns-0.22.5.dist-info/METADATA,sha256=U6WLJ-pstL29t54fKW1LAJMkq8r-rtSR8AtutZqxnHg,4572
115
+ netbox_plugin_dns-0.22.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
116
+ netbox_plugin_dns-0.22.5.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.8.1
2
+ Generator: poetry-core 1.9.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any