Jeremy Stretch 5 лет назад
Родитель
Сommit
31bb70d9a2

+ 1 - 2
netbox/ipam/api/views.py

@@ -233,8 +233,7 @@ class PrefixViewSet(CustomFieldModelViewSet):
 
 class IPAddressViewSet(CustomFieldModelViewSet):
     queryset = IPAddress.objects.prefetch_related(
-        'vrf__tenant', 'tenant', 'nat_inside', 'interface__device__device_type', 'interface__virtual_machine',
-        'nat_outside', 'tags',
+        'vrf__tenant', 'tenant', 'nat_inside', 'nat_outside', 'tags',
     )
     serializer_class = serializers.IPAddressSerializer
     filterset_class = filters.IPAddressFilterSet

+ 44 - 30
netbox/ipam/filters.py

@@ -1,5 +1,6 @@
 import django_filters
 import netaddr
+from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ValidationError
 from django.db.models import Q
 from netaddr.core import AddrFormatError
@@ -11,7 +12,7 @@ from utilities.filters import (
     BaseFilterSet, MultiValueCharFilter, MultiValueNumberFilter, NameSlugSearchFilterSet, TagFilter,
     TreeNodeMultipleChoiceFilter,
 )
-from virtualization.models import VirtualMachine
+from virtualization.models import Interface as VMInterface, VirtualMachine
 from .choices import *
 from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
 
@@ -299,27 +300,26 @@ class IPAddressFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet,
         to_field_name='rd',
         label='VRF (RD)',
     )
-    # device = MultiValueCharFilter(
-    #     method='filter_device',
-    #     field_name='name',
-    #     label='Device (name)',
-    # )
-    # device_id = MultiValueNumberFilter(
-    #     method='filter_device',
-    #     field_name='pk',
-    #     label='Device (ID)',
-    # )
-    # virtual_machine_id = django_filters.ModelMultipleChoiceFilter(
-    #     field_name='interface__virtual_machine',
-    #     queryset=VirtualMachine.objects.unrestricted(),
-    #     label='Virtual machine (ID)',
-    # )
-    # virtual_machine = django_filters.ModelMultipleChoiceFilter(
-    #     field_name='interface__virtual_machine__name',
-    #     queryset=VirtualMachine.objects.unrestricted(),
-    #     to_field_name='name',
-    #     label='Virtual machine (name)',
-    # )
+    device = MultiValueCharFilter(
+        method='filter_device',
+        field_name='name',
+        label='Device (name)',
+    )
+    device_id = MultiValueNumberFilter(
+        method='filter_device',
+        field_name='pk',
+        label='Device (ID)',
+    )
+    virtual_machine = MultiValueCharFilter(
+        method='filter_virtual_machine',
+        field_name='name',
+        label='Virtual machine (name)',
+    )
+    virtual_machine_id = MultiValueNumberFilter(
+        method='filter_virtual_machine',
+        field_name='pk',
+        label='Virtual machine (ID)',
+    )
     # interface = django_filters.ModelMultipleChoiceFilter(
     #     field_name='interface__name',
     #     queryset=Interface.objects.unrestricted(),
@@ -379,17 +379,31 @@ class IPAddressFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet,
         return queryset.filter(address__net_mask_length=value)
 
     def filter_device(self, queryset, name, value):
-        try:
-            devices = Device.objects.prefetch_related('device_type').filter(**{'{}__in'.format(name): value})
-            vc_interface_ids = []
-            for device in devices:
-                vc_interface_ids.extend([i['id'] for i in device.vc_interfaces.values('id')])
-            return queryset.filter(interface_id__in=vc_interface_ids)
-        except Device.DoesNotExist:
+        devices = Device.objects.filter(**{'{}__in'.format(name): value})
+        if not devices.exists():
+            return queryset.none()
+        interface_ids = []
+        for device in devices:
+            interface_ids.extend(device.vc_interfaces.values_list('id', flat=True))
+        return queryset.filter(
+            assigned_object_type=ContentType.objects.get_for_model(Interface),
+            assigned_object_id__in=interface_ids
+        )
+
+    def filter_virtual_machine(self, queryset, name, value):
+        virtual_machines = VirtualMachine.objects.filter(**{'{}__in'.format(name): value})
+        if not virtual_machines.exists():
             return queryset.none()
+        interface_ids = []
+        for vm in virtual_machines:
+            interface_ids.extend(vm.interfaces.values_list('id', flat=True))
+        return queryset.filter(
+            assigned_object_type=ContentType.objects.get_for_model(VMInterface),
+            assigned_object_id__in=interface_ids
+        )
 
     def _assigned_to_interface(self, queryset, name, value):
-        return queryset.exclude(interface__isnull=value)
+        return queryset.exclude(assigned_object_id__isnull=value)
 
 
 class VLANGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):

+ 71 - 71
netbox/ipam/forms.py

@@ -523,10 +523,10 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm)
 #
 
 class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModelForm):
-    interface = forms.ModelChoiceField(
-        queryset=Interface.objects.all(),
-        required=False
-    )
+    # interface = forms.ModelChoiceField(
+    #     queryset=Interface.objects.all(),
+    #     required=False
+    # )
     vrf = DynamicModelChoiceField(
         queryset=VRF.objects.all(),
         required=False,
@@ -598,8 +598,8 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
     class Meta:
         model = IPAddress
         fields = [
-            'address', 'vrf', 'status', 'role', 'dns_name', 'description', 'interface', 'primary_for_parent',
-            'nat_site', 'nat_rack', 'nat_inside', 'tenant_group', 'tenant', 'tags',
+            'address', 'vrf', 'status', 'role', 'dns_name', 'description', 'primary_for_parent', 'nat_site', 'nat_rack',
+            'nat_inside', 'tenant_group', 'tenant', 'tags',
         ]
         widgets = {
             'status': StaticSelect2(),
@@ -621,27 +621,27 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
 
         self.fields['vrf'].empty_label = 'Global'
 
-        # Limit interface selections to those belonging to the parent device/VM
-        if self.instance and self.instance.interface:
-            self.fields['interface'].queryset = Interface.objects.filter(
-                device=self.instance.interface.device, virtual_machine=self.instance.interface.virtual_machine
-            ).prefetch_related(
-                'device__primary_ip4',
-                'device__primary_ip6',
-                'virtual_machine__primary_ip4',
-                'virtual_machine__primary_ip6',
-            )  # We prefetch the primary address fields to ensure cache invalidation does not balk on the save()
-        else:
-            self.fields['interface'].choices = []
-
-        # Initialize primary_for_parent if IP address is already assigned
-        if self.instance.pk and self.instance.interface is not None:
-            parent = self.instance.interface.parent
-            if (
-                self.instance.address.version == 4 and parent.primary_ip4_id == self.instance.pk or
-                self.instance.address.version == 6 and parent.primary_ip6_id == self.instance.pk
-            ):
-                self.initial['primary_for_parent'] = True
+        # # Limit interface selections to those belonging to the parent device/VM
+        # if self.instance and self.instance.interface:
+        #     self.fields['interface'].queryset = Interface.objects.filter(
+        #         device=self.instance.interface.device, virtual_machine=self.instance.interface.virtual_machine
+        #     ).prefetch_related(
+        #         'device__primary_ip4',
+        #         'device__primary_ip6',
+        #         'virtual_machine__primary_ip4',
+        #         'virtual_machine__primary_ip6',
+        #     )  # We prefetch the primary address fields to ensure cache invalidation does not balk on the save()
+        # else:
+        #     self.fields['interface'].choices = []
+        #
+        # # Initialize primary_for_parent if IP address is already assigned
+        # if self.instance.pk and self.instance.interface is not None:
+        #     parent = self.instance.interface.parent
+        #     if (
+        #         self.instance.address.version == 4 and parent.primary_ip4_id == self.instance.pk or
+        #         self.instance.address.version == 6 and parent.primary_ip6_id == self.instance.pk
+        #     ):
+        #         self.initial['primary_for_parent'] = True
 
     def clean(self):
         super().clean()
@@ -664,14 +664,14 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
             else:
                 parent.primary_ip6 = ipaddress
             parent.save()
-        elif self.cleaned_data['interface']:
-            parent = self.cleaned_data['interface'].parent
-            if ipaddress.address.version == 4 and parent.primary_ip4 == ipaddress:
-                parent.primary_ip4 = None
-                parent.save()
-            elif ipaddress.address.version == 6 and parent.primary_ip6 == ipaddress:
-                parent.primary_ip6 = None
-                parent.save()
+        # elif self.cleaned_data['interface']:
+        #     parent = self.cleaned_data['interface'].parent
+        #     if ipaddress.address.version == 4 and parent.primary_ip4 == ipaddress:
+        #         parent.primary_ip4 = None
+        #         parent.save()
+        #     elif ipaddress.address.version == 6 and parent.primary_ip6 == ipaddress:
+        #         parent.primary_ip6 = None
+        #         parent.save()
 
         return ipaddress
 
@@ -730,24 +730,24 @@ class IPAddressCSVForm(CustomFieldModelCSVForm):
         required=False,
         help_text='Functional role'
     )
-    device = CSVModelChoiceField(
-        queryset=Device.objects.all(),
-        required=False,
-        to_field_name='name',
-        help_text='Parent device of assigned interface (if any)'
-    )
-    virtual_machine = CSVModelChoiceField(
-        queryset=VirtualMachine.objects.all(),
-        required=False,
-        to_field_name='name',
-        help_text='Parent VM of assigned interface (if any)'
-    )
-    interface = CSVModelChoiceField(
-        queryset=Interface.objects.all(),
-        required=False,
-        to_field_name='name',
-        help_text='Assigned interface'
-    )
+    # device = CSVModelChoiceField(
+    #     queryset=Device.objects.all(),
+    #     required=False,
+    #     to_field_name='name',
+    #     help_text='Parent device of assigned interface (if any)'
+    # )
+    # virtual_machine = CSVModelChoiceField(
+    #     queryset=VirtualMachine.objects.all(),
+    #     required=False,
+    #     to_field_name='name',
+    #     help_text='Parent VM of assigned interface (if any)'
+    # )
+    # interface = CSVModelChoiceField(
+    #     queryset=Interface.objects.all(),
+    #     required=False,
+    #     to_field_name='name',
+    #     help_text='Assigned interface'
+    # )
     is_primary = forms.BooleanField(
         help_text='Make this the primary IP for the assigned device',
         required=False
@@ -760,23 +760,23 @@ class IPAddressCSVForm(CustomFieldModelCSVForm):
     def __init__(self, data=None, *args, **kwargs):
         super().__init__(data, *args, **kwargs)
 
-        if data:
-
-            # Limit interface queryset by assigned device or virtual machine
-            if data.get('device'):
-                params = {
-                    f"device__{self.fields['device'].to_field_name}": data.get('device')
-                }
-            elif data.get('virtual_machine'):
-                params = {
-                    f"virtual_machine__{self.fields['virtual_machine'].to_field_name}": data.get('virtual_machine')
-                }
-            else:
-                params = {
-                    'device': None,
-                    'virtual_machine': None,
-                }
-            self.fields['interface'].queryset = self.fields['interface'].queryset.filter(**params)
+        # if data:
+        #
+        #     # Limit interface queryset by assigned device or virtual machine
+        #     if data.get('device'):
+        #         params = {
+        #             f"device__{self.fields['device'].to_field_name}": data.get('device')
+        #         }
+        #     elif data.get('virtual_machine'):
+        #         params = {
+        #             f"virtual_machine__{self.fields['virtual_machine'].to_field_name}": data.get('virtual_machine')
+        #         }
+        #     else:
+        #         params = {
+        #             'device': None,
+        #             'virtual_machine': None,
+        #         }
+        #     self.fields['interface'].queryset = self.fields['interface'].queryset.filter(**params)
 
     def clean(self):
         super().clean()
@@ -1197,7 +1197,7 @@ class ServiceForm(BootstrapMixin, CustomFieldModelForm):
         if self.instance.device:
             self.fields['ipaddresses'].queryset = IPAddress.objects.filter(
                 assigned_object_type=ContentType.objects.get_for_model(Interface),
-                assigned_object_id__in=self.instance.device.vc_interfaces.values('id', flat=True)
+                assigned_object_id__in=self.instance.device.vc_interfaces.values_list('id', flat=True)
             )
         elif self.instance.virtual_machine:
             self.fields['ipaddresses'].queryset = IPAddress.objects.filter(

+ 5 - 24
netbox/ipam/models.py

@@ -5,7 +5,7 @@ from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ValidationError, ObjectDoesNotExist
 from django.core.validators import MaxValueValidator, MinValueValidator
 from django.db import models
-from django.db.models import F, Q
+from django.db.models import F
 from django.urls import reverse
 from taggit.managers import TaggableManager
 
@@ -653,7 +653,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
     objects = IPAddressManager()
 
     csv_headers = [
-        'address', 'vrf', 'tenant', 'status', 'role', 'device', 'virtual_machine', 'interface', 'is_primary',
+        'address', 'vrf', 'tenant', 'status', 'role', 'assigned_object_type', 'assigned_object_id', 'is_primary',
         'dns_name', 'description',
     ]
     clone_fields = [
@@ -753,17 +753,11 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
         super().save(*args, **kwargs)
 
     def to_objectchange(self, action):
-        # Annotate the assigned Interface (if any)
-        try:
-            parent_obj = self.interface
-        except ObjectDoesNotExist:
-            parent_obj = None
-
         return ObjectChange(
             changed_object=self,
             object_repr=str(self),
             action=action,
-            related_object=parent_obj,
+            related_object=self.assigned_object,
             object_data=serialize_object(self)
         )
 
@@ -783,9 +777,8 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
             self.tenant.name if self.tenant else None,
             self.get_status_display(),
             self.get_role_display(),
-            self.device.identifier if self.device else None,
-            self.virtual_machine.name if self.virtual_machine else None,
-            self.interface.name if self.interface else None,
+            '{}.{}'.format(self.assigned_object_type.app_label, self.assigned_object_type.model) if self.assigned_object_type else None,
+            self.assigned_object_id,
             is_primary,
             self.dns_name,
             self.description,
@@ -806,18 +799,6 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
             self.address.prefixlen = value
     mask_length = property(fset=_set_mask_length)
 
-    @property
-    def device(self):
-        if self.interface:
-            return self.interface.device
-        return None
-
-    @property
-    def virtual_machine(self):
-        if self.interface:
-            return self.interface.virtual_machine
-        return None
-
     def get_status_class(self):
         return self.STATUS_CLASS_MAP.get(self.status)
 

+ 2 - 2
netbox/ipam/tables.py

@@ -481,13 +481,13 @@ class IPAddressAssignTable(BaseTable):
         template_code=IPADDRESS_PARENT,
         orderable=False
     )
-    interface = tables.Column(
+    assigned_object = tables.Column(
         orderable=False
     )
 
     class Meta(BaseTable.Meta):
         model = IPAddress
-        fields = ('address', 'dns_name', 'vrf', 'status', 'role', 'tenant', 'parent', 'interface', 'description')
+        fields = ('address', 'dns_name', 'vrf', 'status', 'role', 'tenant', 'parent', 'assigned_object', 'description')
         orderable = False
 
 

+ 18 - 17
netbox/ipam/tests/test_filters.py

@@ -4,7 +4,7 @@ from dcim.models import Device, DeviceRole, DeviceType, Interface, Manufacturer,
 from ipam.choices import *
 from ipam.filters import *
 from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
-from virtualization.models import Cluster, ClusterType, Interfaces as VMInterface, VirtualMachine
+from virtualization.models import Cluster, ClusterType, Interface as VMInterface, VirtualMachine
 from tenancy.models import Tenant, TenantGroup
 
 
@@ -415,16 +415,16 @@ class IPAddressTestCase(TestCase):
         Tenant.objects.bulk_create(tenants)
 
         ipaddresses = (
-            IPAddress(address='10.0.0.1/24', tenant=None, vrf=None, interface=None, status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-a'),
-            IPAddress(address='10.0.0.2/24', tenant=tenants[0], vrf=vrfs[0], interface=interfaces[0], status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-b'),
-            IPAddress(address='10.0.0.3/24', tenant=tenants[1], vrf=vrfs[1], interface=interfaces[1], status=IPAddressStatusChoices.STATUS_RESERVED, role=IPAddressRoleChoices.ROLE_VIP, dns_name='ipaddress-c'),
-            IPAddress(address='10.0.0.4/24', tenant=tenants[2], vrf=vrfs[2], interface=interfaces[2], status=IPAddressStatusChoices.STATUS_DEPRECATED, role=IPAddressRoleChoices.ROLE_SECONDARY, dns_name='ipaddress-d'),
-            IPAddress(address='10.0.0.1/25', tenant=None, vrf=None, interface=None, status=IPAddressStatusChoices.STATUS_ACTIVE),
-            IPAddress(address='2001:db8::1/64', tenant=None, vrf=None, interface=None, status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-a'),
-            IPAddress(address='2001:db8::2/64', tenant=tenants[0], vrf=vrfs[0], interface=interfaces[3], status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-b'),
-            IPAddress(address='2001:db8::3/64', tenant=tenants[1], vrf=vrfs[1], interface=interfaces[4], status=IPAddressStatusChoices.STATUS_RESERVED, role=IPAddressRoleChoices.ROLE_VIP, dns_name='ipaddress-c'),
-            IPAddress(address='2001:db8::4/64', tenant=tenants[2], vrf=vrfs[2], interface=interfaces[5], status=IPAddressStatusChoices.STATUS_DEPRECATED, role=IPAddressRoleChoices.ROLE_SECONDARY, dns_name='ipaddress-d'),
-            IPAddress(address='2001:db8::1/65', tenant=None, vrf=None, interface=None, status=IPAddressStatusChoices.STATUS_ACTIVE),
+            IPAddress(address='10.0.0.1/24', tenant=None, vrf=None, assigned_object=None, status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-a'),
+            IPAddress(address='10.0.0.2/24', tenant=tenants[0], vrf=vrfs[0], assigned_object=interfaces[0], status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-b'),
+            IPAddress(address='10.0.0.3/24', tenant=tenants[1], vrf=vrfs[1], assigned_object=interfaces[1], status=IPAddressStatusChoices.STATUS_RESERVED, role=IPAddressRoleChoices.ROLE_VIP, dns_name='ipaddress-c'),
+            IPAddress(address='10.0.0.4/24', tenant=tenants[2], vrf=vrfs[2], assigned_object=interfaces[2], status=IPAddressStatusChoices.STATUS_DEPRECATED, role=IPAddressRoleChoices.ROLE_SECONDARY, dns_name='ipaddress-d'),
+            IPAddress(address='10.0.0.1/25', tenant=None, vrf=None, assigned_object=None, status=IPAddressStatusChoices.STATUS_ACTIVE),
+            IPAddress(address='2001:db8::1/64', tenant=None, vrf=None, assigned_object=None, status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-a'),
+            IPAddress(address='2001:db8::2/64', tenant=tenants[0], vrf=vrfs[0], assigned_object=vm_interfaces[0], status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-b'),
+            IPAddress(address='2001:db8::3/64', tenant=tenants[1], vrf=vrfs[1], assigned_object=vm_interfaces[1], status=IPAddressStatusChoices.STATUS_RESERVED, role=IPAddressRoleChoices.ROLE_VIP, dns_name='ipaddress-c'),
+            IPAddress(address='2001:db8::4/64', tenant=tenants[2], vrf=vrfs[2], assigned_object=vm_interfaces[2], status=IPAddressStatusChoices.STATUS_DEPRECATED, role=IPAddressRoleChoices.ROLE_SECONDARY, dns_name='ipaddress-d'),
+            IPAddress(address='2001:db8::1/65', tenant=None, vrf=None, assigned_object=None, status=IPAddressStatusChoices.STATUS_ACTIVE),
         )
         IPAddress.objects.bulk_create(ipaddresses)
 
@@ -486,12 +486,13 @@ class IPAddressTestCase(TestCase):
         params = {'virtual_machine': [vms[0].name, vms[1].name]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
-    def test_interface(self):
-        interfaces = Interface.objects.all()[:2]
-        params = {'interface_id': [interfaces[0].pk, interfaces[1].pk]}
-        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
-        params = {'interface': ['Interface 1', 'Interface 2']}
-        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+    # TODO: Restore filtering by interface
+    # def test_interface(self):
+    #     interfaces = Interface.objects.all()[:2]
+    #     params = {'interface_id': [interfaces[0].pk, interfaces[1].pk]}
+    #     self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+    #     params = {'interface': ['Interface 1', 'Interface 2']}
+    #     self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
 
     def test_assigned_to_interface(self):
         params = {'assigned_to_interface': 'true'}

+ 0 - 1
netbox/ipam/tests/test_views.py

@@ -236,7 +236,6 @@ class IPAddressTestCase(ViewTestCases.PrimaryObjectViewTestCase):
             'tenant': None,
             'status': IPAddressStatusChoices.STATUS_RESERVED,
             'role': IPAddressRoleChoices.ROLE_ANYCAST,
-            'interface': None,
             'nat_inside': None,
             'dns_name': 'example',
             'description': 'A new IP address',

+ 43 - 47
netbox/ipam/views.py

@@ -517,7 +517,7 @@ class PrefixIPAddressesView(ObjectView):
 
         # Find all IPAddresses belonging to this Prefix
         ipaddresses = prefix.get_child_ips().restrict(request.user, 'view').prefetch_related(
-            'vrf', 'interface__device', 'primary_ip4_for', 'primary_ip6_for'
+            'vrf', 'primary_ip4_for', 'primary_ip6_for'
         )
 
         # Add available IP addresses to the table if requested
@@ -593,7 +593,7 @@ class PrefixBulkDeleteView(BulkDeleteView):
 
 class IPAddressListView(ObjectListView):
     queryset = IPAddress.objects.prefetch_related(
-        'vrf__tenant', 'tenant', 'nat_inside', 'interface__device', 'interface__virtual_machine'
+        'vrf__tenant', 'tenant', 'nat_inside'
     )
     filterset = filters.IPAddressFilterSet
     filterset_form = forms.IPAddressFilterForm
@@ -607,49 +607,47 @@ class IPAddressView(ObjectView):
 
         ipaddress = get_object_or_404(self.queryset, pk=pk)
 
-        # Parent prefixes table
-        parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
-            vrf=ipaddress.vrf, prefix__net_contains=str(ipaddress.address.ip)
-        ).prefetch_related(
-            'site', 'role'
-        )
-        parent_prefixes_table = tables.PrefixTable(list(parent_prefixes), orderable=False)
-        parent_prefixes_table.exclude = ('vrf',)
-
-        # Duplicate IPs table
-        duplicate_ips = IPAddress.objects.restrict(request.user, 'view').filter(
-            vrf=ipaddress.vrf, address=str(ipaddress.address)
-        ).exclude(
-            pk=ipaddress.pk
-        ).prefetch_related(
-            'nat_inside', 'interface__device'
-        )
-        # Exclude anycast IPs if this IP is anycast
-        if ipaddress.role == IPAddressRoleChoices.ROLE_ANYCAST:
-            duplicate_ips = duplicate_ips.exclude(role=IPAddressRoleChoices.ROLE_ANYCAST)
-        duplicate_ips_table = tables.IPAddressTable(list(duplicate_ips), orderable=False)
-
-        # Related IP table
-        related_ips = IPAddress.objects.restrict(request.user, 'view').prefetch_related(
-            'interface__device'
-        ).exclude(
-            address=str(ipaddress.address)
-        ).filter(
-            vrf=ipaddress.vrf, address__net_contained_or_equal=str(ipaddress.address)
-        )
-        related_ips_table = tables.IPAddressTable(related_ips, orderable=False)
-
-        paginate = {
-            'paginator_class': EnhancedPaginator,
-            'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT)
-        }
-        RequestConfig(request, paginate).configure(related_ips_table)
+        # # Parent prefixes table
+        # parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
+        #     vrf=ipaddress.vrf, prefix__net_contains=str(ipaddress.address.ip)
+        # ).prefetch_related(
+        #     'site', 'role'
+        # )
+        # parent_prefixes_table = tables.PrefixTable(list(parent_prefixes), orderable=False)
+        # parent_prefixes_table.exclude = ('vrf',)
+        #
+        # # Duplicate IPs table
+        # duplicate_ips = IPAddress.objects.restrict(request.user, 'view').filter(
+        #     vrf=ipaddress.vrf, address=str(ipaddress.address)
+        # ).exclude(
+        #     pk=ipaddress.pk
+        # ).prefetch_related(
+        #     'nat_inside'
+        # )
+        # # Exclude anycast IPs if this IP is anycast
+        # if ipaddress.role == IPAddressRoleChoices.ROLE_ANYCAST:
+        #     duplicate_ips = duplicate_ips.exclude(role=IPAddressRoleChoices.ROLE_ANYCAST)
+        # duplicate_ips_table = tables.IPAddressTable(list(duplicate_ips), orderable=False)
+        #
+        # # Related IP table
+        # related_ips = IPAddress.objects.restrict(request.user, 'view').exclude(
+        #     address=str(ipaddress.address)
+        # ).filter(
+        #     vrf=ipaddress.vrf, address__net_contained_or_equal=str(ipaddress.address)
+        # )
+        # related_ips_table = tables.IPAddressTable(related_ips, orderable=False)
+        #
+        # paginate = {
+        #     'paginator_class': EnhancedPaginator,
+        #     'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT)
+        # }
+        # RequestConfig(request, paginate).configure(related_ips_table)
 
         return render(request, 'ipam/ipaddress.html', {
             'ipaddress': ipaddress,
-            'parent_prefixes_table': parent_prefixes_table,
-            'duplicate_ips_table': duplicate_ips_table,
-            'related_ips_table': related_ips_table,
+            # 'parent_prefixes_table': parent_prefixes_table,
+            # 'duplicate_ips_table': duplicate_ips_table,
+            # 'related_ips_table': related_ips_table,
         })
 
 
@@ -699,9 +697,7 @@ class IPAddressAssignView(ObjectView):
 
         if form.is_valid():
 
-            addresses = self.queryset.prefetch_related(
-                'vrf', 'tenant', 'interface__device', 'interface__virtual_machine'
-            )
+            addresses = self.queryset.prefetch_related('vrf', 'tenant')
             # Limit to 100 results
             addresses = filters.IPAddressFilterSet(request.POST, addresses).qs[:100]
             table = tables.IPAddressAssignTable(addresses)
@@ -734,7 +730,7 @@ class IPAddressBulkImportView(BulkImportView):
 
 
 class IPAddressBulkEditView(BulkEditView):
-    queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant').prefetch_related('interface__device')
+    queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
     filterset = filters.IPAddressFilterSet
     table = tables.IPAddressTable
     form = forms.IPAddressBulkEditForm
@@ -742,7 +738,7 @@ class IPAddressBulkEditView(BulkEditView):
 
 
 class IPAddressBulkDeleteView(BulkDeleteView):
-    queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant').prefetch_related('interface__device')
+    queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
     filterset = filters.IPAddressFilterSet
     table = tables.IPAddressTable
     default_return_url = 'ipam:ipaddress_list'

+ 4 - 4
netbox/templates/ipam/ipaddress.html

@@ -120,8 +120,8 @@
                 <tr>
                     <td>Assignment</td>
                     <td>
-                        {% if ipaddress.interface %}
-                            <span><a href="{{ ipaddress.interface.parent.get_absolute_url }}">{{ ipaddress.interface.parent }}</a> ({{ ipaddress.interface }})</span>
+                        {% if ipaddress.assigned_object %}
+                            <span><a href="{{ ipaddress.assigned_object.parent.get_absolute_url }}">{{ ipaddress.assigned_object.parent }}</a> ({{ ipaddress.assigned_object }})</span>
                         {% else %}
                             <span class="text-muted">&mdash;</span>
                         {% endif %}
@@ -132,8 +132,8 @@
                     <td>
                         {% if ipaddress.nat_inside %}
                             <a href="{% url 'ipam:ipaddress' pk=ipaddress.nat_inside.pk %}">{{ ipaddress.nat_inside }}</a>
-                            {% if ipaddress.nat_inside.interface %}
-                                (<a href="{{ ipaddress.nat_inside.interface.parent.get_absolute_url }}">{{ ipaddress.nat_inside.interface.parent }}</a>)
+                            {% if ipaddress.nat_inside.assigned_object %}
+                                (<a href="{{ ipaddress.nat_inside.assigned_object.parent.get_absolute_url }}">{{ ipaddress.nat_inside.assigned_object.parent }}</a>)
                             {% endif %}
                         {% else %}
                             <span class="text-muted">None</span>

+ 0 - 7
netbox/virtualization/tests/test_views.py

@@ -267,10 +267,3 @@ class InterfaceTestCase(
             # 'untagged_vlan': vlans[0].pk,
             # 'tagged_vlans': [v.pk for v in vlans[1:4]],
         }
-
-        cls.csv_data = (
-            "device,name,type",
-            "Device 1,Interface 4,1000BASE-T (1GE)",
-            "Device 1,Interface 5,1000BASE-T (1GE)",
-            "Device 1,Interface 6,1000BASE-T (1GE)",
-        )