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

Potentially problematic release.


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

Files changed (69) hide show
  1. netbox_dns/__init__.py +23 -4
  2. netbox_dns/api/serializers.py +2 -1
  3. netbox_dns/api/serializers_/prefix.py +18 -0
  4. netbox_dns/api/serializers_/{contact.py → registration_contact.py} +5 -5
  5. netbox_dns/api/serializers_/view.py +34 -2
  6. netbox_dns/api/serializers_/zone.py +5 -5
  7. netbox_dns/api/serializers_/zone_template.py +5 -5
  8. netbox_dns/api/urls.py +5 -2
  9. netbox_dns/api/views.py +17 -7
  10. netbox_dns/fields/__init__.py +1 -0
  11. netbox_dns/fields/ipam.py +15 -0
  12. netbox_dns/filtersets/__init__.py +1 -1
  13. netbox_dns/filtersets/{contact.py → registration_contact.py} +4 -4
  14. netbox_dns/filtersets/view.py +16 -0
  15. netbox_dns/filtersets/zone.py +15 -15
  16. netbox_dns/filtersets/zone_template.py +15 -15
  17. netbox_dns/forms/__init__.py +1 -1
  18. netbox_dns/forms/{contact.py → registration_contact.py} +16 -16
  19. netbox_dns/forms/view.py +204 -4
  20. netbox_dns/forms/zone.py +15 -18
  21. netbox_dns/forms/zone_template.py +13 -13
  22. netbox_dns/graphql/__init__.py +2 -2
  23. netbox_dns/graphql/filters.py +5 -5
  24. netbox_dns/graphql/schema.py +9 -5
  25. netbox_dns/graphql/types.py +41 -12
  26. netbox_dns/management/commands/rebuild_dnssync.py +18 -0
  27. netbox_dns/management/commands/setup_dnssync.py +140 -0
  28. netbox_dns/migrations/0008_view_prefixes.py +18 -0
  29. netbox_dns/migrations/0009_rename_contact_registrationcontact.py +27 -0
  30. netbox_dns/models/__init__.py +1 -3
  31. netbox_dns/models/record.py +139 -20
  32. netbox_dns/models/{contact.py → registration_contact.py} +8 -8
  33. netbox_dns/models/view.py +5 -0
  34. netbox_dns/models/zone.py +66 -30
  35. netbox_dns/models/zone_template.py +4 -4
  36. netbox_dns/navigation.py +7 -7
  37. netbox_dns/signals/ipam_dnssync.py +224 -0
  38. netbox_dns/tables/__init__.py +1 -1
  39. netbox_dns/tables/ipam_dnssync.py +11 -0
  40. netbox_dns/tables/record.py +33 -0
  41. netbox_dns/tables/{contact.py → registration_contact.py} +5 -5
  42. netbox_dns/tables/view.py +24 -2
  43. netbox_dns/template_content.py +41 -40
  44. netbox_dns/templates/netbox_dns/record.html +6 -6
  45. netbox_dns/templates/netbox_dns/{contact.html → registrationcontact.html} +1 -1
  46. netbox_dns/templates/netbox_dns/view/button.html +9 -0
  47. netbox_dns/templates/netbox_dns/view/prefix.html +41 -0
  48. netbox_dns/templates/netbox_dns/view/related.html +17 -0
  49. netbox_dns/templates/netbox_dns/view.html +25 -0
  50. netbox_dns/urls/__init__.py +2 -2
  51. netbox_dns/urls/registration_contact.py +60 -0
  52. netbox_dns/urls/view.py +6 -0
  53. netbox_dns/utilities/__init__.py +2 -74
  54. netbox_dns/utilities/conversions.py +83 -0
  55. netbox_dns/utilities/ipam_dnssync.py +295 -0
  56. netbox_dns/views/__init__.py +1 -1
  57. netbox_dns/views/record.py +3 -5
  58. netbox_dns/views/registration_contact.py +94 -0
  59. netbox_dns/views/view.py +26 -1
  60. {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0.dist-info}/METADATA +2 -1
  61. {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0.dist-info}/RECORD +63 -54
  62. netbox_dns/management/commands/setup_coupling.py +0 -109
  63. netbox_dns/signals/ipam_coupling.py +0 -168
  64. netbox_dns/templates/netbox_dns/related_dns_objects.html +0 -21
  65. netbox_dns/urls/contact.py +0 -29
  66. netbox_dns/utilities/ipam_coupling.py +0 -112
  67. netbox_dns/views/contact.py +0 -94
  68. {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0.dist-info}/LICENSE +0 -0
  69. {netbox_plugin_dns-1.0.7.dist-info → netbox_plugin_dns-1.1.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,18 @@
1
+ from django.core.management.base import BaseCommand
2
+
3
+ from ipam.models import IPAddress
4
+
5
+ from netbox_dns.utilities import update_dns_records
6
+
7
+
8
+ class Command(BaseCommand):
9
+ help = "Rebuild DNSsync relationships between IP addresses and records"
10
+
11
+ def handle(self, *model_names, **options):
12
+ ip_addresses = IPAddress.objects.all()
13
+ for ip_address in ip_addresses:
14
+ if options.get("verbosity") >= 2:
15
+ self.stdout.write(
16
+ f"Updating DNS records for IP Address {ip_address}, VRF {ip_address.vrf}"
17
+ )
18
+ update_dns_records(ip_address)
@@ -0,0 +1,140 @@
1
+ from django.core.management.base import BaseCommand
2
+
3
+ from core.models import ObjectType
4
+ from extras.models import CustomField
5
+ from extras.choices import CustomFieldTypeChoices
6
+ from ipam.models import IPAddress
7
+
8
+
9
+ class Command(BaseCommand):
10
+ help = "Setup IPAddress custom fields for IPAM DNSsync"
11
+
12
+ def add_arguments(self, parser):
13
+ parser.add_argument(
14
+ "--remove", action="store_true", default=False, help="Remove custom fields"
15
+ )
16
+
17
+ def handle(self, *model_names, **options):
18
+ ipaddress_object_type = ObjectType.objects.get_for_model(IPAddress)
19
+
20
+ if options.get("remove"):
21
+ if options.get("verbosity"):
22
+ self.stdout.write("Trying to remove IPAM DNSsync custom fields")
23
+ for cf in (
24
+ "ipaddress_dns_disabled",
25
+ "ipaddress_dns_record_ttl",
26
+ "ipaddress_dns_record_disable_ptr",
27
+ ):
28
+ try:
29
+ CustomField.objects.get(
30
+ name=cf, object_types=ipaddress_object_type
31
+ ).delete()
32
+ if options.get("verbosity"):
33
+ self.stdout.write(f"Removed custom field '{cf}'")
34
+ except CustomField.DoesNotExist:
35
+ pass
36
+ return
37
+
38
+ # +
39
+ # Remove pre-existing IPAM Coupling custom fields
40
+ # -
41
+ if options.get("verbosity") >= 2:
42
+ self.stdout.write("Trying to remove obsolete IPAM Coupling custom fields")
43
+ for cf in (
44
+ "ipaddress_dns_record_name",
45
+ "ipaddress_dns_zone_id",
46
+ ):
47
+ try:
48
+ CustomField.objects.get(
49
+ name=cf, object_types=ipaddress_object_type
50
+ ).delete()
51
+ if options.get("verbosity"):
52
+ self.stdout.write(f"Removed custom field '{cf}'")
53
+ except CustomField.DoesNotExist:
54
+ pass
55
+
56
+ if options.get("verbosity") >= 2:
57
+ self.stdout.write("Creating IPAM DNSsync custom fields")
58
+
59
+ if not CustomField.objects.filter(
60
+ name="ipaddress_dns_disabled",
61
+ type=CustomFieldTypeChoices.TYPE_BOOLEAN,
62
+ object_types=ipaddress_object_type,
63
+ ).exists():
64
+ cf_dnssync_disabled = CustomField.objects.create(
65
+ name="ipaddress_dns_disabled",
66
+ label="Disable DNSsync",
67
+ description="Disable DNS address and pointer record generation for this address",
68
+ type=CustomFieldTypeChoices.TYPE_BOOLEAN,
69
+ required=False,
70
+ default=False,
71
+ group_name="DNSsync",
72
+ is_cloneable=True,
73
+ weight=100,
74
+ )
75
+ cf_dnssync_disabled.object_types.set([ipaddress_object_type])
76
+ if options.get("verbosity"):
77
+ self.stdout.write("Created custom field 'ipaddress_dns_disabled'")
78
+
79
+ try:
80
+ cf_ttl = CustomField.objects.get(
81
+ name="ipaddress_dns_record_ttl",
82
+ type=CustomFieldTypeChoices.TYPE_INTEGER,
83
+ object_types=ipaddress_object_type,
84
+ )
85
+ if cf_ttl.group_name != "DNSsync":
86
+ cf_ttl.group_name = "DNSsync"
87
+ cf_ttl.description = ("TTL for DNS records created for this address",)
88
+ cf_ttl.save()
89
+ if options.get("verbosity"):
90
+ self.stdout.write("Updated custom field 'ipaddress_dns_record_ttl'")
91
+ except CustomField.DoesNotExist:
92
+ cf_ttl = CustomField.objects.create(
93
+ name="ipaddress_dns_record_ttl",
94
+ description="TTL for DNS records created for this address",
95
+ label="TTL",
96
+ type=CustomFieldTypeChoices.TYPE_INTEGER,
97
+ validation_minimum=0,
98
+ validation_maximum=2147483647,
99
+ required=False,
100
+ group_name="DNSsync",
101
+ is_cloneable=True,
102
+ weight=200,
103
+ )
104
+ cf_ttl.object_types.set([ipaddress_object_type])
105
+ if options.get("verbosity"):
106
+ self.stdout.write("Created custom field 'ipaddress_dns_record_ttl'")
107
+
108
+ try:
109
+ cf_disable_ptr = CustomField.objects.get(
110
+ name="ipaddress_dns_record_disable_ptr",
111
+ type=CustomFieldTypeChoices.TYPE_BOOLEAN,
112
+ object_types=ipaddress_object_type,
113
+ )
114
+ if cf_disable_ptr.group_name != "DNSsync":
115
+ cf_disable_ptr.group_name = "DNSsync"
116
+ cf_disable_ptr.description = (
117
+ "Disable DNS PTR record generation for this address",
118
+ )
119
+ cf_disable_ptr.save()
120
+ if options.get("verbosity"):
121
+ self.stdout.write(
122
+ "Updated custom field 'ipaddress_dns_record_disable_ptr'"
123
+ )
124
+ except CustomField.DoesNotExist:
125
+ cf_disable_ptr = CustomField.objects.create(
126
+ name="ipaddress_dns_record_disable_ptr",
127
+ description="Disable DNS PTR record generation for this address",
128
+ label="Disable PTR",
129
+ type=CustomFieldTypeChoices.TYPE_BOOLEAN,
130
+ required=False,
131
+ default=False,
132
+ group_name="DNSsync",
133
+ is_cloneable=True,
134
+ weight=300,
135
+ )
136
+ cf_disable_ptr.object_types.set([ipaddress_object_type])
137
+ if options.get("verbosity"):
138
+ self.stdout.write(
139
+ "Created custom field 'ipaddress_dns_record_disable_ptr'"
140
+ )
@@ -0,0 +1,18 @@
1
+ from django.db import migrations, models
2
+
3
+
4
+ class Migration(migrations.Migration):
5
+ dependencies = [
6
+ ("ipam", "0067_ipaddress_index_host"),
7
+ ("netbox_dns", "0007_alter_ordering_options"),
8
+ ]
9
+
10
+ operations = [
11
+ migrations.AddField(
12
+ model_name="view",
13
+ name="prefixes",
14
+ field=models.ManyToManyField(
15
+ blank=True, related_name="netbox_dns_views", to="ipam.prefix"
16
+ ),
17
+ ),
18
+ ]
@@ -0,0 +1,27 @@
1
+ from django.db import migrations
2
+
3
+
4
+ class Migration(migrations.Migration):
5
+
6
+ dependencies = [
7
+ ("netbox_dns", "0008_view_prefixes"),
8
+ ]
9
+
10
+ operations = [
11
+ migrations.RenameModel(
12
+ old_name="Contact",
13
+ new_name="RegistrationContact",
14
+ ),
15
+ migrations.RunSQL(
16
+ "ALTER TABLE netbox_dns_contact_id_seq RENAME TO netbox_dns_registrationcontact_id_seq"
17
+ ),
18
+ migrations.RunSQL(
19
+ "ALTER INDEX netbox_dns_contact_pkey RENAME TO netbox_dns_registrationcontact_pkey"
20
+ ),
21
+ migrations.RunSQL(
22
+ "ALTER INDEX netbox_dns_contact_contact_id_50e9d89d_like RENAME TO netbox_dns_registrationcontact_contact_id_6ff98464_like"
23
+ ),
24
+ migrations.RunSQL(
25
+ "ALTER INDEX netbox_dns_contact_contact_id_key RENAME TO netbox_dns_contact_registrationcontact_id_key"
26
+ ),
27
+ ]
@@ -2,7 +2,7 @@ from .zone import *
2
2
  from .nameserver import *
3
3
  from .record import *
4
4
  from .view import *
5
- from .contact import *
5
+ from .registration_contact import *
6
6
  from .registrar import *
7
7
  from .zone_template import *
8
8
  from .record_template import *
@@ -11,5 +11,3 @@ from .record_template import *
11
11
  # Backwards compatibility fix, will be removed in version 1.1
12
12
  # -
13
13
  from netbox_dns.choices import *
14
-
15
- from netbox_dns.signals import ipam_coupling
@@ -7,6 +7,7 @@ from django.core.exceptions import ValidationError
7
7
  from django.db import transaction, models
8
8
  from django.db.models import Q, ExpressionWrapper, BooleanField, Min
9
9
  from django.urls import reverse
10
+ from django.conf import settings
10
11
 
11
12
  from netbox.models import NetBoxModel
12
13
  from netbox.models.features import ContactsMixin
@@ -36,6 +37,43 @@ def min_ttl(*ttl_list):
36
37
  return min((ttl for ttl in ttl_list if ttl is not None), default=None)
37
38
 
38
39
 
40
+ def record_data_from_ip_address(ip_address, zone):
41
+ cf_data = ip_address.custom_field_data
42
+
43
+ if cf_data.get("ipaddress_dns_disabled"):
44
+ return None
45
+
46
+ data = {
47
+ "name": (
48
+ dns_name.from_text(ip_address.dns_name)
49
+ .relativize(dns_name.from_text(zone.name))
50
+ .to_text()
51
+ ),
52
+ "type": (
53
+ RecordTypeChoices.A
54
+ if ip_address.address.version == 4
55
+ else RecordTypeChoices.AAAA
56
+ ),
57
+ "value": str(ip_address.address.ip),
58
+ "status": (
59
+ RecordStatusChoices.STATUS_ACTIVE
60
+ if ip_address.status
61
+ in settings.PLUGINS_CONFIG["netbox_dns"].get(
62
+ "dnssync_ipaddress_active_status", []
63
+ )
64
+ else RecordStatusChoices.STATUS_INACTIVE
65
+ ),
66
+ }
67
+
68
+ if "ipaddress_dns_record_ttl" in cf_data:
69
+ data["ttl"] = cf_data.get("ipaddress_dns_record_ttl")
70
+
71
+ if (disable_ptr := cf_data.get("ipaddress_dns_record_disable_ptr")) is not None:
72
+ data["disable_ptr"] = disable_ptr
73
+
74
+ return data
75
+
76
+
39
77
  class RecordManager(models.Manager.from_queryset(RestrictedQuerySet)):
40
78
  """
41
79
  Custom manager for records providing the activity status annotation
@@ -434,29 +472,70 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
434
472
  self.rfc2317_cname_record.delete(save_zone_serial=save_zone_serial)
435
473
  self.rfc2317_cname_record = None
436
474
 
437
- def validate_name(self):
438
- try:
439
- _zone = dns_name.from_text(self.zone.name, origin=dns_name.root)
440
- name = dns_name.from_text(self.name, origin=None)
441
- fqdn = dns_name.from_text(self.name, origin=_zone)
475
+ def update_from_ip_address(self, ip_address, zone=None):
476
+ if zone is None:
477
+ zone = self.zone
442
478
 
443
- _zone.to_unicode()
444
- name.to_unicode()
479
+ data = record_data_from_ip_address(ip_address, zone)
445
480
 
446
- self.name = name.relativize(_zone).to_text()
447
- self.fqdn = fqdn.to_text()
481
+ if data is None:
482
+ self.delete()
483
+ return
448
484
 
449
- except dns.exception.DNSException as exc:
485
+ if all((getattr(self, attr) == data[attr] for attr in data.keys())):
486
+ return
487
+
488
+ for attr, value in data.items():
489
+ setattr(self, attr, value)
490
+
491
+ return self
492
+
493
+ @classmethod
494
+ def create_from_ip_address(cls, ip_address, zone):
495
+ data = record_data_from_ip_address(ip_address, zone)
496
+
497
+ if data is None:
498
+ return
499
+
500
+ return Record(
501
+ zone=zone,
502
+ managed=True,
503
+ ipam_ip_address=ip_address,
504
+ **data,
505
+ )
506
+
507
+ def update_fqdn(self, zone=None):
508
+ if zone is None:
509
+ zone = self.zone
510
+
511
+ _zone = dns_name.from_text(zone.name, origin=dns_name.root)
512
+ name = dns_name.from_text(self.name, origin=None)
513
+ fqdn = dns_name.from_text(self.name, origin=_zone)
514
+
515
+ if not fqdn.is_subdomain(_zone):
450
516
  raise ValidationError(
451
517
  {
452
- "name": str(exc),
518
+ "name": f"{self.name} is not a name in {zone.name}",
453
519
  }
454
520
  )
455
521
 
456
- if not fqdn.is_subdomain(_zone):
522
+ _zone.to_unicode()
523
+ name.to_unicode()
524
+
525
+ self.name = name.relativize(_zone).to_text()
526
+ self.fqdn = fqdn.to_text()
527
+
528
+ def validate_name(self, new_zone=None):
529
+ if new_zone is None:
530
+ new_zone = self.zone
531
+
532
+ try:
533
+ self.update_fqdn(zone=new_zone)
534
+
535
+ except dns.exception.DNSException as exc:
457
536
  raise ValidationError(
458
537
  {
459
- "name": f"{self.name} is not a name in {self.zone.name}",
538
+ "name": str(exc),
460
539
  }
461
540
  )
462
541
 
@@ -488,15 +567,18 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
488
567
  except ValidationError as exc:
489
568
  raise ValidationError({"value": exc}) from None
490
569
 
491
- def check_unique_record(self):
570
+ def check_unique_record(self, new_zone=None):
492
571
  if not get_plugin_config("netbox_dns", "enforce_unique_records", False):
493
572
  return
494
573
 
495
574
  if not self.is_active:
496
575
  return
497
576
 
577
+ if new_zone is None:
578
+ new_zone = self.zone
579
+
498
580
  records = Record.objects.filter(
499
- zone=self.zone,
581
+ zone=new_zone,
500
582
  name=self.name,
501
583
  type=self.type,
502
584
  value=self.value,
@@ -506,13 +588,41 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
506
588
  if self.pk is not None:
507
589
  records = records.exclude(pk=self.pk)
508
590
 
509
- if len(records):
591
+ if records.exists():
592
+ if self.ipam_ip_address is not None:
593
+ if not records.filter(
594
+ ipam_ip_address__isnull=True
595
+ ).exists() or get_plugin_config(
596
+ "netbox_dns", "dnssync_conflict_deactivate", False
597
+ ):
598
+ return
599
+
510
600
  raise ValidationError(
511
601
  {
512
602
  "value": f"There is already an active {self.type} record for name {self.name} in zone {self.zone} with value {self.value}."
513
603
  }
514
604
  ) from None
515
605
 
606
+ def handle_conflicting_address_records(self):
607
+ if self.ipam_ip_address is None or not self.is_active:
608
+ return
609
+
610
+ if not get_plugin_config("netbox_dns", "dnssync_conflict_deactivate", False):
611
+ return
612
+
613
+ records = Record.objects.filter(
614
+ zone=self.zone,
615
+ name=self.name,
616
+ type=self.type,
617
+ value=self.value,
618
+ status__in=Record.ACTIVE_STATUS_LIST,
619
+ ipam_ip_address__isnull=True,
620
+ )
621
+
622
+ for record in records:
623
+ record.status = RecordStatusChoices.STATUS_INACTIVE
624
+ record.save(update_fields=["status"])
625
+
516
626
  def check_unique_rrset_ttl(self):
517
627
  if self.pk is not None:
518
628
  return
@@ -520,6 +630,11 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
520
630
  if not get_plugin_config("netbox_dns", "enforce_unique_rrset_ttl", False):
521
631
  return
522
632
 
633
+ if self.ipam_ip_address is not None and get_plugin_config(
634
+ "netbox_dns", "dnssync_conflict_deactivate", False
635
+ ):
636
+ return
637
+
523
638
  if self.type == RecordTypeChoices.PTR and self.managed:
524
639
  return
525
640
 
@@ -534,10 +649,13 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
534
649
  .exclude(status=RecordStatusChoices.STATUS_INACTIVE)
535
650
  )
536
651
 
652
+ if self.ipam_ip_address is not None:
653
+ records = records.exclude(ipam_ip_address__isnull=False)
654
+
537
655
  if not records.exists():
538
656
  return
539
657
 
540
- conflicting_ttls = ", ".join(set(str(record.ttl) for record in records))
658
+ conflicting_ttls = ", ".join({str(record.ttl) for record in records})
541
659
  raise ValidationError(
542
660
  {
543
661
  "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})."
@@ -577,10 +695,10 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
577
695
  self.type = self.type.upper()
578
696
  super().clean_fields(*args, **kwargs)
579
697
 
580
- def clean(self, *args, **kwargs):
581
- self.validate_name()
698
+ def clean(self, *args, new_zone=None, **kwargs):
699
+ self.validate_name(new_zone=new_zone)
582
700
  self.validate_value()
583
- self.check_unique_record()
701
+ self.check_unique_record(new_zone=new_zone)
584
702
  if self.pk is None:
585
703
  self.check_unique_rrset_ttl()
586
704
 
@@ -686,6 +804,7 @@ class Record(ObjectModificationMixin, ContactsMixin, NetBoxModel):
686
804
  self.ip_address = None
687
805
 
688
806
  if self.is_address_record:
807
+ self.handle_conflicting_address_records()
689
808
  self.update_ptr_record(
690
809
  update_rfc2317_cname=update_rfc2317_cname,
691
810
  save_zone_serial=save_zone_serial,
@@ -8,12 +8,12 @@ from taggit.managers import TaggableManager
8
8
 
9
9
 
10
10
  __all__ = (
11
- "Contact",
12
- "ContactIndex",
11
+ "RegistrationContact",
12
+ "RegistrationContactIndex",
13
13
  )
14
14
 
15
15
 
16
- class Contact(NetBoxModel):
16
+ class RegistrationContact(NetBoxModel):
17
17
  # +
18
18
  # Data fields according to https://www.icann.org/resources/pages/rdds-labeling-policy-2017-02-01-en
19
19
  # -
@@ -105,7 +105,7 @@ class Contact(NetBoxModel):
105
105
  )
106
106
 
107
107
  def get_absolute_url(self):
108
- return reverse("plugins:netbox_dns:contact", kwargs={"pk": self.pk})
108
+ return reverse("plugins:netbox_dns:registrationcontact", kwargs={"pk": self.pk})
109
109
 
110
110
  def __str__(self):
111
111
  if self.name is not None:
@@ -114,8 +114,8 @@ class Contact(NetBoxModel):
114
114
  return self.contact_id
115
115
 
116
116
  class Meta:
117
- verbose_name = "Contact"
118
- verbose_name_plural = "Contacts"
117
+ verbose_name = "Registration Contact"
118
+ verbose_name_plural = "Registration Contacts"
119
119
 
120
120
  ordering = (
121
121
  "name",
@@ -133,8 +133,8 @@ class Contact(NetBoxModel):
133
133
 
134
134
 
135
135
  @register_search
136
- class ContactIndex(SearchIndex):
137
- model = Contact
136
+ class RegistrationContactIndex(SearchIndex):
137
+ model = RegistrationContact
138
138
  fields = (
139
139
  ("name", 100),
140
140
  ("contact_id", 100),
netbox_dns/models/view.py CHANGED
@@ -29,6 +29,11 @@ class View(ObjectModificationMixin, ContactsMixin, NetBoxModel):
29
29
  default_view = models.BooleanField(
30
30
  default=False,
31
31
  )
32
+ prefixes = models.ManyToManyField(
33
+ to="ipam.Prefix",
34
+ related_name="netbox_dns_views",
35
+ blank=True,
36
+ )
32
37
  tenant = models.ForeignKey(
33
38
  to="tenancy.Tenant",
34
39
  on_delete=models.PROTECT,