diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index 2fb1e9949..0848966e8 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -20,7 +20,7 @@ from utilities.filters import ( ContentTypeFilter, MultiValueCharFilter, MultiValueMACAddressFilter, MultiValueNumberFilter, MultiValueWWNFilter, NumericArrayFilter, TreeNodeMultipleChoiceFilter, ) -from virtualization.models import Cluster +from virtualization.models import Cluster, ClusterGroup from vpn.models import L2VPN from wireless.choices import WirelessRoleChoices, WirelessChannelChoices from wireless.models import WirelessLAN, WirelessLink @@ -1018,6 +1018,17 @@ class DeviceFilterSet( queryset=Cluster.objects.all(), label=_('VM cluster (ID)'), ) + cluster_group = django_filters.ModelMultipleChoiceFilter( + field_name='cluster__group__slug', + queryset=ClusterGroup.objects.all(), + to_field_name='slug', + label=_('Cluster group (slug)'), + ) + cluster_group_id = django_filters.ModelMultipleChoiceFilter( + field_name='cluster__group', + queryset=ClusterGroup.objects.all(), + label=_('Cluster group (ID)'), + ) model = django_filters.ModelMultipleChoiceFilter( field_name='device_type__slug', queryset=DeviceType.objects.all(), diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py index 0a28a4ec4..22e66763b 100644 --- a/netbox/dcim/forms/filtersets.py +++ b/netbox/dcim/forms/filtersets.py @@ -14,6 +14,7 @@ from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm, add_blank_ch from utilities.forms.fields import ColorField, DynamicModelMultipleChoiceField, TagFilterField from utilities.forms.rendering import FieldSet from utilities.forms.widgets import NumberWithOptions +from virtualization.models import Cluster, ClusterGroup from vpn.models import L2VPN from wireless.choices import * @@ -655,6 +656,7 @@ class DeviceFilterForm( 'console_ports', 'console_server_ports', 'power_ports', 'power_outlets', 'interfaces', 'pass_through_ports', name=_('Components') ), + FieldSet('cluster_group_id', 'cluster_id', name=_('Cluster')), FieldSet( 'has_primary_ip', 'has_oob_ip', 'virtual_chassis_member', 'config_template_id', 'local_context_data', 'has_virtual_device_context', @@ -821,6 +823,16 @@ class DeviceFilterForm( choices=BOOLEAN_WITH_BLANK_CHOICES ) ) + cluster_id = DynamicModelMultipleChoiceField( + queryset=Cluster.objects.all(), + required=False, + label=_('Cluster') + ) + cluster_group_id = DynamicModelMultipleChoiceField( + queryset=ClusterGroup.objects.all(), + required=False, + label=_('Cluster group') + ) tag = TagFilterField(model) diff --git a/netbox/dcim/tests/test_filtersets.py b/netbox/dcim/tests/test_filtersets.py index 0a22f5a82..d08e2707f 100644 --- a/netbox/dcim/tests/test_filtersets.py +++ b/netbox/dcim/tests/test_filtersets.py @@ -9,7 +9,7 @@ from ipam.models import ASN, IPAddress, RIR, VRF from netbox.choices import ColorChoices from tenancy.models import Tenant, TenantGroup from utilities.testing import ChangeLoggedFilterSetTests, create_test_device -from virtualization.models import Cluster, ClusterType +from virtualization.models import Cluster, ClusterType, ClusterGroup from wireless.choices import WirelessChannelChoices, WirelessRoleChoices User = get_user_model() @@ -1959,10 +1959,16 @@ class DeviceTestCase(TestCase, ChangeLoggedFilterSetTests): Rack.objects.bulk_create(racks) cluster_type = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1') + cluster_groups = ( + ClusterGroup(name='Cluster Group 1', slug='cluster-group-1'), + ClusterGroup(name='Cluster Group 2', slug='cluster-group-2'), + ClusterGroup(name='Cluster Group 3', slug='cluster-group-3'), + ) + ClusterGroup.objects.bulk_create(cluster_groups) clusters = ( - Cluster(name='Cluster 1', type=cluster_type), - Cluster(name='Cluster 2', type=cluster_type), - Cluster(name='Cluster 3', type=cluster_type), + Cluster(name='Cluster 1', type=cluster_type, group=cluster_groups[0]), + Cluster(name='Cluster 2', type=cluster_type, group=cluster_groups[1]), + Cluster(name='Cluster 3', type=cluster_type, group=cluster_groups[2]), ) Cluster.objects.bulk_create(clusters) @@ -2213,6 +2219,13 @@ class DeviceTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'cluster_id': [clusters[0].pk, clusters[1].pk]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_cluster_group(self): + cluster_groups = ClusterGroup.objects.all()[:2] + params = {'cluster_group_id': [cluster_groups[0].pk, cluster_groups[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'cluster_group': [cluster_groups[0].slug, cluster_groups[1].slug]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_model(self): params = {'model': ['model-1', 'model-2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)