From b5a65bc66c297d2f165647421383bb443fe23ba1 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 9 Oct 2020 11:46:16 -0400 Subject: [PATCH] Fixes #5211: Add missing has_primary_ip filter for virtual machines --- docs/release-notes/version-2.9.md | 1 + netbox/dcim/filters.py | 12 +++--------- netbox/virtualization/filters.py | 10 ++++++++++ netbox/virtualization/forms.py | 7 +++++++ netbox/virtualization/tests/test_filters.py | 16 ++++++++++++++++ 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/docs/release-notes/version-2.9.md b/docs/release-notes/version-2.9.md index 6217a748b..646997b07 100644 --- a/docs/release-notes/version-2.9.md +++ b/docs/release-notes/version-2.9.md @@ -12,6 +12,7 @@ * [#5199](https://github.com/netbox-community/netbox/issues/5199) - Change default LDAP logging to INFO * [#5201](https://github.com/netbox-community/netbox/issues/5201) - Fix missing querystring when bulk editing/deleting VLAN Group VLANs when selecting "select all x items matching query" * [#5206](https://github.com/netbox-community/netbox/issues/5206) - Apply user pagination preferences to all paginated object lists +* [#5211](https://github.com/netbox-community/netbox/issues/5211) - Add missing `has_primary_ip` filter for virtual machines * [#5217](https://github.com/netbox-community/netbox/issues/5217) - Prevent erroneous removal of prefetched GenericForeignKey data from tables * [#5218](https://github.com/netbox-community/netbox/issues/5218) - Raise validation error if a power port's `allocated_draw` exceeds its `maximum_draw` * [#5220](https://github.com/netbox-community/netbox/issues/5220) - Fix API patch request against IP Address endpoint with null assigned_object_type diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index 457483273..773b33f74 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -662,16 +662,10 @@ class DeviceFilterSet( ).distinct() def _has_primary_ip(self, queryset, name, value): + params = Q(primary_ip4__isnull=False) | Q(primary_ip6__isnull=False) if value: - return queryset.filter( - Q(primary_ip4__isnull=False) | - Q(primary_ip6__isnull=False) - ) - else: - return queryset.exclude( - Q(primary_ip4__isnull=False) | - Q(primary_ip6__isnull=False) - ) + return queryset.filter(params) + return queryset.exclude(params) def _virtual_chassis_member(self, queryset, name, value): return queryset.exclude(virtual_chassis__isnull=value) diff --git a/netbox/virtualization/filters.py b/netbox/virtualization/filters.py index 7cb37f323..886b5484b 100644 --- a/netbox/virtualization/filters.py +++ b/netbox/virtualization/filters.py @@ -186,6 +186,10 @@ class VirtualMachineFilterSet( field_name='interfaces__mac_address', label='MAC address', ) + has_primary_ip = django_filters.BooleanFilter( + method='_has_primary_ip', + label='Has a primary IP', + ) tag = TagFilter() class Meta: @@ -200,6 +204,12 @@ class VirtualMachineFilterSet( Q(comments__icontains=value) ) + def _has_primary_ip(self, queryset, name, value): + params = Q(primary_ip4__isnull=False) | Q(primary_ip6__isnull=False) + if value: + return queryset.filter(params) + return queryset.exclude(params) + class VMInterfaceFilterSet(BaseFilterSet): q = django_filters.CharFilter( diff --git a/netbox/virtualization/forms.py b/netbox/virtualization/forms.py index 5d002decc..712b39f9f 100644 --- a/netbox/virtualization/forms.py +++ b/netbox/virtualization/forms.py @@ -516,6 +516,13 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFil required=False, label='MAC address' ) + has_primary_ip = forms.NullBooleanField( + required=False, + label='Has a primary IP', + widget=StaticSelect2( + choices=BOOLEAN_WITH_BLANK_CHOICES + ) + ) tag = TagFilterField(model) diff --git a/netbox/virtualization/tests/test_filters.py b/netbox/virtualization/tests/test_filters.py index ad452ec51..e27c063e1 100644 --- a/netbox/virtualization/tests/test_filters.py +++ b/netbox/virtualization/tests/test_filters.py @@ -1,6 +1,7 @@ from django.test import TestCase from dcim.models import DeviceRole, Platform, Region, Site +from ipam.models import IPAddress from tenancy.models import Tenant, TenantGroup from virtualization.choices import * from virtualization.filters import * @@ -266,6 +267,15 @@ class VirtualMachineTestCase(TestCase): ) VMInterface.objects.bulk_create(interfaces) + # Assign primary IPs for filtering + ipaddresses = ( + IPAddress(address='192.0.2.1/24', assigned_object=interfaces[0]), + IPAddress(address='192.0.2.2/24', assigned_object=interfaces[1]), + ) + 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]) + def test_id(self): params = {'id': self.queryset.values_list('pk', flat=True)[:2]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) @@ -344,6 +354,12 @@ class VirtualMachineTestCase(TestCase): params = {'mac_address': ['00-00-00-00-00-01', '00-00-00-00-00-02']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_has_primary_ip(self): + params = {'has_primary_ip': 'true'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'has_primary_ip': 'false'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_local_context_data(self): params = {'local_context_data': 'true'} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)