From fa8a8abc984faa62073fd06996c2dce436751bd2 Mon Sep 17 00:00:00 2001 From: Rhys Barrie Date: Sat, 13 Nov 2021 21:30:38 -0500 Subject: [PATCH 1/5] netbox-community/netbox#7424: Add virtual_chassis and virtual_chassis_id filter to device components --- netbox/dcim/filtersets.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index ba7ede783..f7cf011ce 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -861,6 +861,17 @@ class DeviceComponentFilterSet(django_filters.FilterSet): to_field_name='name', label='Device (name)', ) + virtual_chassis_id = django_filters.ModelMultipleChoiceFilter( + field_name='device__virtual_chassis', + queryset=VirtualChassis.objects.all(), + label='Virtual Chassis (ID)' + ) + virtual_chassis = django_filters.ModelMultipleChoiceFilter( + field_name='device__virtual_chassis__name', + queryset=VirtualChassis.objects.all(), + to_field_name='name', + label='Virtual Chassis', + ) tag = TagFilter() def search(self, queryset, name, value): From 6b21c8453fc68cb6bb999f69fd52342759aed22c Mon Sep 17 00:00:00 2001 From: Rhys Barrie Date: Sat, 13 Nov 2021 21:33:52 -0500 Subject: [PATCH 2/5] netbox-community/netbox#7424: Add virtual_chassis field to device component filter form --- netbox/dcim/api/nested_serializers.py | 2 +- netbox/dcim/forms/filtersets.py | 24 +++++++++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/netbox/dcim/api/nested_serializers.py b/netbox/dcim/api/nested_serializers.py index 67ae9b046..1fdde78d7 100644 --- a/netbox/dcim/api/nested_serializers.py +++ b/netbox/dcim/api/nested_serializers.py @@ -340,7 +340,7 @@ class NestedVirtualChassisSerializer(WritableNestedSerializer): class Meta: model = models.VirtualChassis - fields = ['id', 'name', 'url', 'master', 'member_count'] + fields = ['id', 'url', 'display', 'name', 'master', 'member_count'] # diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py index 93299a17e..6d76c4003 100644 --- a/netbox/dcim/forms/filtersets.py +++ b/netbox/dcim/forms/filtersets.py @@ -92,6 +92,12 @@ class DeviceComponentFilterForm(BootstrapMixin, CustomFieldModelFilterForm): label=_('Location'), fetch_trigger='open' ) + virtual_chassis_id = DynamicModelMultipleChoiceField( + queryset=VirtualChassis.objects.all(), + required=False, + label=_('Virtual Chassis'), + fetch_trigger='open' + ) device_id = DynamicModelMultipleChoiceField( queryset=Device.objects.all(), required=False, @@ -888,7 +894,7 @@ class ConsolePortFilterForm(DeviceComponentFilterForm): field_groups = [ ['q', 'tag'], ['name', 'label', 'type', 'speed'], - ['region_id', 'site_group_id', 'site_id', 'location_id', 'device_id'], + ['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'], ] type = forms.MultipleChoiceField( choices=ConsolePortTypeChoices, @@ -908,7 +914,7 @@ class ConsoleServerPortFilterForm(DeviceComponentFilterForm): field_groups = [ ['q', 'tag'], ['name', 'label', 'type', 'speed'], - ['region_id', 'site_group_id', 'site_id', 'location_id', 'device_id'], + ['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'], ] type = forms.MultipleChoiceField( choices=ConsolePortTypeChoices, @@ -928,7 +934,7 @@ class PowerPortFilterForm(DeviceComponentFilterForm): field_groups = [ ['q', 'tag'], ['name', 'label', 'type'], - ['region_id', 'site_group_id', 'site_id', 'location_id', 'device_id'], + ['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'], ] type = forms.MultipleChoiceField( choices=PowerPortTypeChoices, @@ -943,7 +949,7 @@ class PowerOutletFilterForm(DeviceComponentFilterForm): field_groups = [ ['q', 'tag'], ['name', 'label', 'type'], - ['region_id', 'site_group_id', 'site_id', 'location_id', 'device_id'], + ['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'], ] type = forms.MultipleChoiceField( choices=PowerOutletTypeChoices, @@ -958,7 +964,7 @@ class InterfaceFilterForm(DeviceComponentFilterForm): field_groups = [ ['q', 'tag'], ['name', 'label', 'kind', 'type', 'enabled', 'mgmt_only', 'mac_address'], - ['region_id', 'site_group_id', 'site_id', 'location_id', 'device_id'], + ['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'], ] kind = forms.MultipleChoiceField( choices=InterfaceKindChoices, @@ -993,7 +999,7 @@ class FrontPortFilterForm(DeviceComponentFilterForm): field_groups = [ ['q', 'tag'], ['name', 'label', 'type', 'color'], - ['region_id', 'site_group_id', 'site_id', 'location_id', 'device_id'], + ['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'], ] model = FrontPort type = forms.MultipleChoiceField( @@ -1012,7 +1018,7 @@ class RearPortFilterForm(DeviceComponentFilterForm): field_groups = [ ['q', 'tag'], ['name', 'label', 'type', 'color'], - ['region_id', 'site_group_id', 'site_id', 'location_id', 'device_id'], + ['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'], ] type = forms.MultipleChoiceField( choices=PortTypeChoices, @@ -1030,7 +1036,7 @@ class DeviceBayFilterForm(DeviceComponentFilterForm): field_groups = [ ['q', 'tag'], ['name', 'label'], - ['region_id', 'site_group_id', 'site_id', 'location_id', 'device_id'], + ['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'], ] tag = TagFilterField(model) @@ -1040,7 +1046,7 @@ class InventoryItemFilterForm(DeviceComponentFilterForm): field_groups = [ ['q', 'tag'], ['name', 'label', 'manufacturer_id', 'serial', 'asset_tag', 'discovered'], - ['region_id', 'site_group_id', 'site_id', 'location_id', 'device_id'], + ['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'], ] manufacturer_id = DynamicModelMultipleChoiceField( queryset=Manufacturer.objects.all(), From f77f7ca0ec7887ad1f075654f87658908d83346b Mon Sep 17 00:00:00 2001 From: Rhys Barrie Date: Sat, 13 Nov 2021 21:35:13 -0500 Subject: [PATCH 3/5] netbox-community/netbox#7424:make device component device field filter from selected virtual chassis --- netbox/dcim/forms/filtersets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py index 6d76c4003..70a20d8a5 100644 --- a/netbox/dcim/forms/filtersets.py +++ b/netbox/dcim/forms/filtersets.py @@ -104,6 +104,7 @@ class DeviceComponentFilterForm(BootstrapMixin, CustomFieldModelFilterForm): query_params={ 'site_id': '$site_id', 'location_id': '$location_id', + 'virtual_chassis_id': '$virtual_chassis_id' }, label=_('Device'), fetch_trigger='open' From a8c958ece2038be0ac849312663785f4e4791d51 Mon Sep 17 00:00:00 2001 From: Rhys Barrie Date: Sat, 13 Nov 2021 22:01:15 -0500 Subject: [PATCH 4/5] netbox-community/netbox#7424: fix test failure from adding virtual chassis filter field --- netbox/dcim/tests/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/dcim/tests/test_api.py b/netbox/dcim/tests/test_api.py index e5977b760..3fc48beed 100644 --- a/netbox/dcim/tests/test_api.py +++ b/netbox/dcim/tests/test_api.py @@ -1524,7 +1524,7 @@ class ConnectedDeviceTest(APITestCase): class VirtualChassisTest(APIViewTestCases.APIViewTestCase): model = VirtualChassis - brief_fields = ['id', 'master', 'member_count', 'name', 'url'] + brief_fields = ['display', 'id', 'master', 'member_count', 'name', 'url'] @classmethod def setUpTestData(cls): From 68b544c676fba9542d09cd7b5c71e0315c885b80 Mon Sep 17 00:00:00 2001 From: Rhys Barrie Date: Sat, 13 Nov 2021 22:16:18 -0500 Subject: [PATCH 5/5] netbox-community/netbox#7424: add filterset test for virtual_chassis_id --- netbox/dcim/tests/test_filtersets.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/netbox/dcim/tests/test_filtersets.py b/netbox/dcim/tests/test_filtersets.py index fb94bde08..2b5da8576 100644 --- a/netbox/dcim/tests/test_filtersets.py +++ b/netbox/dcim/tests/test_filtersets.py @@ -2048,6 +2048,11 @@ class InterfaceTestCase(TestCase, ChangeLoggedFilterSetTests): ) Device.objects.bulk_create(devices) + # VirtualChassis assignment for filtering + virtual_chassis = VirtualChassis.objects.create(master=devices[0]) + Device.objects.filter(pk=devices[0].pk).update(virtual_chassis=virtual_chassis, vc_position=1, vc_priority=1) + Device.objects.filter(pk=devices[1].pk).update(virtual_chassis=virtual_chassis, vc_position=2, vc_priority=2) + interfaces = ( Interface(device=devices[0], name='Interface 1', label='A', type=InterfaceTypeChoices.TYPE_1GE_SFP, enabled=True, mgmt_only=True, mtu=100, mode=InterfaceModeChoices.MODE_ACCESS, mac_address='00-00-00-00-00-01', description='First'), Interface(device=devices[1], name='Interface 2', label='B', type=InterfaceTypeChoices.TYPE_1GE_GBIC, enabled=True, mgmt_only=True, mtu=200, mode=InterfaceModeChoices.MODE_TAGGED, mac_address='00-00-00-00-00-02', description='Second'), @@ -2157,6 +2162,10 @@ class InterfaceTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'location': [locations[0].slug, locations[1].slug]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_virtual_chassis_id(self): + params = {'virtual_chassis_id': [VirtualChassis.objects.first().pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_device(self): devices = Device.objects.all()[:2] params = {'device_id': [devices[0].pk, devices[1].pk]}