From 6b89da2233b8f424e2cddc9c8bbe32a53cef4e74 Mon Sep 17 00:00:00 2001 From: Artem Kotik Date: Thu, 9 Nov 2023 22:56:43 +0800 Subject: [PATCH] Closes #13936: Add primary_ip4 and primary_ip6 filters to VirtualMachine and VirtualDeviceContext filtersets (#14203) * Add primary_ip4 and primary_ip6 filters for VirtualMachine and VirtualDeviceContext filtersets (#13936) * Add PrimaryIPFilterSet to __all__ --------- Co-authored-by: Artem I. Kotik Co-authored-by: Jeremy Stretch --- netbox/dcim/filtersets.py | 21 ++++++++---------- netbox/dcim/tests/test_filtersets.py | 20 +++++++++++++++++ netbox/extras/tests/test_views.py | 2 +- netbox/ipam/filtersets.py | 17 ++++++++++++++ netbox/virtualization/filtersets.py | 4 +++- .../virtualization/tests/test_filtersets.py | 22 +++++++++++++++++-- 6 files changed, 70 insertions(+), 16 deletions(-) diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index d600667d7..b5bdaf269 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -4,6 +4,7 @@ from django.utils.translation import gettext as _ from extras.filtersets import LocalConfigContextFilterSet from extras.models import ConfigTemplate +from ipam.filtersets import PrimaryIPFilterSet from ipam.models import ASN, L2VPN, IPAddress, VRF from netbox.filtersets import ( BaseFilterSet, ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet, @@ -817,7 +818,13 @@ class PlatformFilterSet(OrganizationalModelFilterSet): fields = ['id', 'name', 'slug', 'description'] -class DeviceFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet, LocalConfigContextFilterSet): +class DeviceFilterSet( + NetBoxModelFilterSet, + TenancyFilterSet, + ContactModelFilterSet, + LocalConfigContextFilterSet, + PrimaryIPFilterSet, +): manufacturer_id = django_filters.ModelMultipleChoiceFilter( field_name='device_type__manufacturer', queryset=Manufacturer.objects.all(), @@ -993,16 +1000,6 @@ class DeviceFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilter method='_device_bays', label=_('Has device bays'), ) - primary_ip4_id = django_filters.ModelMultipleChoiceFilter( - field_name='primary_ip4', - queryset=IPAddress.objects.all(), - label=_('Primary IPv4 (ID)'), - ) - primary_ip6_id = django_filters.ModelMultipleChoiceFilter( - field_name='primary_ip6', - queryset=IPAddress.objects.all(), - label=_('Primary IPv6 (ID)'), - ) oob_ip_id = django_filters.ModelMultipleChoiceFilter( field_name='oob_ip', queryset=IPAddress.objects.all(), @@ -1069,7 +1066,7 @@ class DeviceFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilter return queryset.exclude(devicebays__isnull=value) -class VirtualDeviceContextFilterSet(NetBoxModelFilterSet, TenancyFilterSet): +class VirtualDeviceContextFilterSet(NetBoxModelFilterSet, TenancyFilterSet, PrimaryIPFilterSet): device_id = django_filters.ModelMultipleChoiceFilter( field_name='device', queryset=Device.objects.all(), diff --git a/netbox/dcim/tests/test_filtersets.py b/netbox/dcim/tests/test_filtersets.py index 1f3b557b5..8fbef126e 100644 --- a/netbox/dcim/tests/test_filtersets.py +++ b/netbox/dcim/tests/test_filtersets.py @@ -4712,12 +4712,18 @@ class VirtualDeviceContextTestCase(TestCase, ChangeLoggedFilterSetTests): addresses = ( IPAddress(assigned_object=interfaces[0], address='10.1.1.1/24'), IPAddress(assigned_object=interfaces[1], address='10.1.1.2/24'), + IPAddress(assigned_object=None, address='10.1.1.3/24'), + IPAddress(assigned_object=interfaces[0], address='2001:db8::1/64'), + IPAddress(assigned_object=interfaces[1], address='2001:db8::2/64'), + IPAddress(assigned_object=None, address='2001:db8::3/64'), ) IPAddress.objects.bulk_create(addresses) vdcs[0].primary_ip4 = addresses[0] + vdcs[0].primary_ip6 = addresses[3] vdcs[0].save() vdcs[1].primary_ip4 = addresses[1] + vdcs[1].primary_ip6 = addresses[4] vdcs[1].save() def test_device(self): @@ -4738,3 +4744,17 @@ class VirtualDeviceContextTestCase(TestCase, ChangeLoggedFilterSetTests): self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) params = {'has_primary_ip': False} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + + def test_primary_ip4(self): + addresses = IPAddress.objects.filter(address__family=4) + params = {'primary_ip4_id': [addresses[0].pk, addresses[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'primary_ip4_id': [addresses[2].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0) + + def test_primary_ip6(self): + addresses = IPAddress.objects.filter(address__family=6) + params = {'primary_ip6_id': [addresses[0].pk, addresses[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'primary_ip6_id': [addresses[2].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0) diff --git a/netbox/extras/tests/test_views.py b/netbox/extras/tests/test_views.py index 296ed9f4d..e034abff5 100644 --- a/netbox/extras/tests/test_views.py +++ b/netbox/extras/tests/test_views.py @@ -457,7 +457,7 @@ class ConfigContextTestCase( 'platforms': [], 'tenant_groups': [], 'tenants': [], - 'device_types': [devicetype.id,], + 'device_types': [devicetype.id], 'tags': [], 'data': '{"foo": 123}', } diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py index d1177bdc1..ba944e3ad 100644 --- a/netbox/ipam/filtersets.py +++ b/netbox/ipam/filtersets.py @@ -29,6 +29,7 @@ __all__ = ( 'L2VPNFilterSet', 'L2VPNTerminationFilterSet', 'PrefixFilterSet', + 'PrimaryIPFilterSet', 'RIRFilterSet', 'RoleFilterSet', 'RouteTargetFilterSet', @@ -1232,3 +1233,19 @@ class L2VPNTerminationFilterSet(NetBoxModelFilterSet): ) ) return qs + + +class PrimaryIPFilterSet(django_filters.FilterSet): + """ + An inheritable FilterSet for models which support primary IP assignment. + """ + primary_ip4_id = django_filters.ModelMultipleChoiceFilter( + field_name='primary_ip4', + queryset=IPAddress.objects.all(), + label=_('Primary IPv4 (ID)'), + ) + primary_ip6_id = django_filters.ModelMultipleChoiceFilter( + field_name='primary_ip6', + queryset=IPAddress.objects.all(), + label=_('Primary IPv6 (ID)'), + ) diff --git a/netbox/virtualization/filtersets.py b/netbox/virtualization/filtersets.py index 571dbe64b..b23808b31 100644 --- a/netbox/virtualization/filtersets.py +++ b/netbox/virtualization/filtersets.py @@ -6,6 +6,7 @@ from dcim.filtersets import CommonInterfaceFilterSet from dcim.models import Device, DeviceRole, Platform, Region, Site, SiteGroup from extras.filtersets import LocalConfigContextFilterSet from extras.models import ConfigTemplate +from ipam.filtersets import PrimaryIPFilterSet from netbox.filtersets import OrganizationalModelFilterSet, NetBoxModelFilterSet from tenancy.filtersets import TenancyFilterSet, ContactModelFilterSet from utilities.filters import MultiValueCharFilter, MultiValueMACAddressFilter, TreeNodeMultipleChoiceFilter @@ -114,7 +115,8 @@ class VirtualMachineFilterSet( NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet, - LocalConfigContextFilterSet + LocalConfigContextFilterSet, + PrimaryIPFilterSet, ): status = django_filters.MultipleChoiceFilter( choices=VirtualMachineStatusChoices, diff --git a/netbox/virtualization/tests/test_filtersets.py b/netbox/virtualization/tests/test_filtersets.py index d474af21a..e6fe90297 100644 --- a/netbox/virtualization/tests/test_filtersets.py +++ b/netbox/virtualization/tests/test_filtersets.py @@ -291,10 +291,14 @@ class VirtualMachineTestCase(TestCase, ChangeLoggedFilterSetTests): ipaddresses = ( IPAddress(address='192.0.2.1/24', assigned_object=interfaces[0]), IPAddress(address='192.0.2.2/24', assigned_object=interfaces[1]), + IPAddress(address='192.0.2.3/24', assigned_object=None), + IPAddress(address='2001:db8::1/64', assigned_object=interfaces[0]), + IPAddress(address='2001:db8::2/64', assigned_object=interfaces[1]), + IPAddress(address='2001:db8::3/64', assigned_object=None), ) IPAddress.objects.bulk_create(ipaddresses) - VirtualMachine.objects.filter(pk=vms[0].pk).update(primary_ip4=ipaddresses[0]) - VirtualMachine.objects.filter(pk=vms[1].pk).update(primary_ip4=ipaddresses[1]) + VirtualMachine.objects.filter(pk=vms[0].pk).update(primary_ip4=ipaddresses[0], primary_ip6=ipaddresses[3]) + VirtualMachine.objects.filter(pk=vms[1].pk).update(primary_ip4=ipaddresses[1], primary_ip6=ipaddresses[4]) def test_name(self): params = {'name': ['Virtual Machine 1', 'Virtual Machine 2']} @@ -412,6 +416,20 @@ class VirtualMachineTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'tenant_group': [tenant_groups[0].slug, tenant_groups[1].slug]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_primary_ip4(self): + addresses = IPAddress.objects.filter(address__family=4) + params = {'primary_ip4_id': [addresses[0].pk, addresses[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'primary_ip4_id': [addresses[2].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0) + + def test_primary_ip6(self): + addresses = IPAddress.objects.filter(address__family=6) + params = {'primary_ip6_id': [addresses[0].pk, addresses[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'primary_ip6_id': [addresses[2].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0) + class VMInterfaceTestCase(TestCase, ChangeLoggedFilterSetTests): queryset = VMInterface.objects.all()