From bb9b0b8f8afa6690149fcd01d69af39b460f850e Mon Sep 17 00:00:00 2001 From: Martin Hauser Date: Tue, 15 Apr 2025 13:59:32 +0200 Subject: [PATCH] Fixes #18879 - Add prefix filtering by assigned VLAN Group (#19182) * feat(ipam): Add VLAN group filters to IPAM FilterSet Introduces filters for VLAN groups using both ID and slug fields. * feat(ipam): Add VLAN group filter in IPAM FilterForm Introduces a `vlan_group_id` filter to IPAM forms for filtering based on VLAN groups. * feat(ipam): Add VLAN group filtering to tests Introduces tests for VLAN group filtering in FilterSets. This ensures correct validation and behavior when filtering by VLAN group. --- netbox/ipam/filtersets.py | 12 ++++++++++++ netbox/ipam/forms/filtersets.py | 7 ++++++- netbox/ipam/tests/test_filtersets.py | 18 ++++++++++++++++-- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py index 5c8dbc780..e16885ba0 100644 --- a/netbox/ipam/filtersets.py +++ b/netbox/ipam/filtersets.py @@ -351,6 +351,18 @@ class PrefixFilterSet(NetBoxModelFilterSet, ScopedFilterSet, TenancyFilterSet, C to_field_name='rd', label=_('VRF (RD)'), ) + vlan_group_id = django_filters.ModelMultipleChoiceFilter( + field_name='vlan__group', + queryset=VLANGroup.objects.all(), + to_field_name="id", + label=_('VLAN Group (ID)'), + ) + vlan_group = django_filters.ModelMultipleChoiceFilter( + field_name='vlan__group__slug', + queryset=VLANGroup.objects.all(), + to_field_name="slug", + label=_('VLAN Group (slug)'), + ) vlan_id = django_filters.ModelMultipleChoiceFilter( queryset=VLAN.objects.all(), label=_('VLAN (ID)'), diff --git a/netbox/ipam/forms/filtersets.py b/netbox/ipam/forms/filtersets.py index e51ae6dae..4cfe9f872 100644 --- a/netbox/ipam/forms/filtersets.py +++ b/netbox/ipam/forms/filtersets.py @@ -176,7 +176,7 @@ class PrefixFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFil 'within_include', 'family', 'status', 'role_id', 'mask_length', 'is_pool', 'mark_utilized', name=_('Addressing') ), - FieldSet('vlan_id', name=_('VLAN Assignment')), + FieldSet('vlan_group_id', 'vlan_id', name=_('VLAN Assignment')), FieldSet('vrf_id', 'present_in_vrf_id', name=_('VRF')), FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Scope')), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), @@ -260,6 +260,11 @@ class PrefixFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFil choices=BOOLEAN_WITH_BLANK_CHOICES ) ) + vlan_group_id = DynamicModelMultipleChoiceField( + queryset=VLANGroup.objects.all(), + required=False, + label=_('VLAN Group'), + ) vlan_id = DynamicModelMultipleChoiceField( queryset=VLAN.objects.all(), required=False, diff --git a/netbox/ipam/tests/test_filtersets.py b/netbox/ipam/tests/test_filtersets.py index 6281f7b41..a65258a58 100644 --- a/netbox/ipam/tests/test_filtersets.py +++ b/netbox/ipam/tests/test_filtersets.py @@ -645,9 +645,16 @@ class PrefixTestCase(TestCase, ChangeLoggedFilterSetTests): vrfs[1].export_targets.add(route_targets[1]) vrfs[2].export_targets.add(route_targets[2]) + vlan_groups = ( + VLANGroup(name='VLAN Group 1', slug='vlan-group-1'), + VLANGroup(name='VLAN Group 2', slug='vlan-group-2'), + ) + for vlan_group in vlan_groups: + vlan_group.save() + vlans = ( - VLAN(vid=1, name='VLAN 1'), - VLAN(vid=2, name='VLAN 2'), + VLAN(vid=1, name='VLAN 1', group=vlan_groups[0]), + VLAN(vid=2, name='VLAN 2', group=vlan_groups[1]), VLAN(vid=3, name='VLAN 3'), ) VLAN.objects.bulk_create(vlans) @@ -850,6 +857,13 @@ class PrefixTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'site': [sites[0].slug, sites[1].slug]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + def test_vlan_group(self): + vlan_groups = VLANGroup.objects.all()[:2] + params = {'vlan_group_id': [vlan_groups[0].pk, vlan_groups[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + params = {'vlan_group': [vlan_groups[0].slug, vlan_groups[1].slug]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + def test_vlan(self): vlans = VLAN.objects.all()[:2] params = {'vlan_id': [vlans[0].pk, vlans[1].pk]}