Răsfoiți Sursa

Fixes #18978 - Allow filtering of Interfaces in the GUI by 802.1Q Mode (#19183)

* feat(dcim): Add VLAN mode filter to CommonInterface

Introduces a new FilterSet for VLAN mode in CommonInterfaceFilterSet.
This allows filtering interfaces based on their VLAN mode using defined
choices.

* feat(dcim): Add VLAN mode filter to Interface FilterForm

Add a field to InterfaceFilterSet to filter interfaces by 802.1Q VLAN
mode.

* feat(virtualization): Add VLAN mode filter to VMInterface

Add a field to VMInterfaceFilterSet to filter interfaces by 802.1Q VLAN
mode.

* fix(dcim): Correct mode filter parameter type in tests

Updates the `mode` filter parameter to accept a list instead of a single
value in `test_filtersets.py`. Ensures proper count assertion for
accurate test behavior.

* feat(virtualization): Add tests for VLAN mode filtering

Introduces tests to validate filtering by `mode` for VMInterface.
Ensures correct filtering for 802.1Q VLAN mode.

* refactor(virtualization): Reorganize FieldSets in FilterSets

Splits the 'Attributes' FieldSet into two distinct FieldSets for better
clarity: 'Attributes' and 'Addressing'. This improves form organization
and makes it more intuitive for users.
Martin Hauser 10 luni în urmă
părinte
comite
1f93471659

+ 4 - 0
netbox/dcim/filtersets.py

@@ -1689,6 +1689,10 @@ class MACAddressFilterSet(NetBoxModelFilterSet):
 
 
 class CommonInterfaceFilterSet(django_filters.FilterSet):
+    mode = django_filters.MultipleChoiceFilter(
+        choices=InterfaceModeChoices,
+        label=_('802.1Q Mode')
+    )
     vlan_id = django_filters.CharFilter(
         method='filter_vlan_id',
         label=_('Assigned VLAN')

+ 6 - 0
netbox/dcim/forms/filtersets.py

@@ -1332,6 +1332,7 @@ class InterfaceFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
         FieldSet('name', 'label', 'kind', 'type', 'speed', 'duplex', 'enabled', 'mgmt_only', name=_('Attributes')),
         FieldSet('vrf_id', 'l2vpn_id', 'mac_address', 'wwn', name=_('Addressing')),
         FieldSet('poe_mode', 'poe_type', name=_('PoE')),
+        FieldSet('mode', name=_('802.1Q Switching')),
         FieldSet('rf_role', 'rf_channel', 'rf_channel_width', 'tx_power', name=_('Wireless')),
         FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
         FieldSet(
@@ -1403,6 +1404,11 @@ class InterfaceFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
         required=False,
         label=_('PoE type')
     )
+    mode = forms.MultipleChoiceField(
+        choices=InterfaceModeChoices,
+        required=False,
+        label=_('802.1Q mode')
+    )
     rf_role = forms.MultipleChoiceField(
         choices=WirelessRoleChoices,
         required=False,

+ 1 - 1
netbox/dcim/tests/test_filtersets.py

@@ -4153,7 +4153,7 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
 
     def test_mode(self):
-        params = {'mode': InterfaceModeChoices.MODE_ACCESS}
+        params = {'mode': [InterfaceModeChoices.MODE_ACCESS]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
 
     def test_description(self):

+ 9 - 1
netbox/virtualization/forms/filtersets.py

@@ -1,6 +1,7 @@
 from django import forms
 from django.utils.translation import gettext_lazy as _
 
+from dcim.choices import *
 from dcim.models import Device, DeviceRole, Location, Platform, Region, Site, SiteGroup
 from extras.forms import LocalConfigContextFilterForm
 from extras.models import ConfigTemplate
@@ -200,7 +201,9 @@ class VMInterfaceFilterForm(NetBoxModelFilterSetForm):
     fieldsets = (
         FieldSet('q', 'filter_id', 'tag'),
         FieldSet('cluster_id', 'virtual_machine_id', name=_('Virtual Machine')),
-        FieldSet('enabled', 'mac_address', 'vrf_id', 'l2vpn_id', name=_('Attributes')),
+        FieldSet('enabled', name=_('Attributes')),
+        FieldSet('vrf_id', 'l2vpn_id', 'mac_address', name=_('Addressing')),
+        FieldSet('mode', name=_('802.1Q Switching')),
     )
     selector_fields = ('filter_id', 'q', 'virtual_machine_id')
     cluster_id = DynamicModelMultipleChoiceField(
@@ -237,6 +240,11 @@ class VMInterfaceFilterForm(NetBoxModelFilterSetForm):
         required=False,
         label=_('L2VPN')
     )
+    mode = forms.MultipleChoiceField(
+        choices=InterfaceModeChoices,
+        required=False,
+        label=_('802.1Q mode')
+    )
     tag = TagFilterField(model)
 
 

+ 6 - 0
netbox/virtualization/tests/test_filtersets.py

@@ -605,6 +605,7 @@ class VMInterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
                 mtu=100,
                 vrf=vrfs[0],
                 description='foobar1',
+                mode=InterfaceModeChoices.MODE_ACCESS,
                 vlan_translation_policy=vlan_translation_policies[0],
             ),
             VMInterface(
@@ -614,6 +615,7 @@ class VMInterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
                 mtu=200,
                 vrf=vrfs[1],
                 description='foobar2',
+                mode=InterfaceModeChoices.MODE_TAGGED,
                 vlan_translation_policy=vlan_translation_policies[0],
             ),
             VMInterface(
@@ -699,6 +701,10 @@ class VMInterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
         params = {'description': ['foobar1', 'foobar2']}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
+    def test_mode(self):
+        params = {'mode': [InterfaceModeChoices.MODE_ACCESS]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
     def test_vlan(self):
         vlan = VLAN.objects.filter(qinq_role=VLANQinQRoleChoices.ROLE_SERVICE).first()
         params = {'vlan_id': vlan.pk}