Przeglądaj źródła

Fixes #19645: Correct Interface selection for Cable add when VC master is the selected device (#20041)

* Fixes: #19645 - Correct Interface selection for Cable add when VC master is the selected device

* Clarify label

* Add test
Daniel Sheppard 6 miesięcy temu
rodzic
commit
9f605a2db1

+ 15 - 2
netbox/dcim/filtersets.py

@@ -1885,6 +1885,16 @@ class InterfaceFilterSet(
     PathEndpointFilterSet,
     CommonInterfaceFilterSet
 ):
+    virtual_chassis_member_or_master = MultiValueCharFilter(
+        method='filter_virtual_chassis_member_or_master',
+        field_name='name',
+        label=_('Virtual Chassis Interfaces for Device when device is master')
+    )
+    virtual_chassis_member_or_master_id = MultiValueNumberFilter(
+        method='filter_virtual_chassis_member_or_master',
+        field_name='pk',
+        label=_('Virtual Chassis Interfaces for Device when device is master (ID)')
+    )
     virtual_chassis_member = MultiValueCharFilter(
         method='filter_virtual_chassis_member',
         field_name='name',
@@ -1995,11 +2005,14 @@ class InterfaceFilterSet(
             'cable_id', 'cable_end',
         )
 
-    def filter_virtual_chassis_member(self, queryset, name, value):
+    def filter_virtual_chassis_member_or_master(self, queryset, name, value):
+        return self.filter_virtual_chassis_member(queryset, name, value, if_master=True)
+
+    def filter_virtual_chassis_member(self, queryset, name, value, if_master=False):
         try:
             vc_interface_ids = []
             for device in Device.objects.filter(**{f'{name}__in': value}):
-                vc_interface_ids.extend(device.vc_interfaces(if_master=False).values_list('id', flat=True))
+                vc_interface_ids.extend(device.vc_interfaces(if_master=if_master).values_list('id', flat=True))
             return queryset.filter(pk__in=vc_interface_ids)
         except Device.DoesNotExist:
             return queryset.none()

+ 6 - 1
netbox/dcim/forms/connections.py

@@ -19,6 +19,11 @@ def get_cable_form(a_type, b_type):
                 # Device component
                 if hasattr(term_cls, 'device'):
 
+                    # Dynamically change the param field for interfaces to use virtual_chassis filter
+                    query_param_device_field = 'device_id'
+                    if term_cls == Interface:
+                        query_param_device_field = 'virtual_chassis_member_or_master_id'
+
                     attrs[f'termination_{cable_end}_device'] = DynamicModelMultipleChoiceField(
                         queryset=Device.objects.all(),
                         label=_('Device'),
@@ -36,7 +41,7 @@ def get_cable_form(a_type, b_type):
                             'parent': 'device',
                         },
                         query_params={
-                            'device_id': f'$termination_{cable_end}_device',
+                            query_param_device_field: f'$termination_{cable_end}_device',
                             'kind': 'physical',  # Exclude virtual interfaces
                         }
                     )

+ 16 - 0
netbox/dcim/tests/test_filtersets.py

@@ -4373,6 +4373,9 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil
         )
         Device.objects.bulk_create(devices)
 
+        virtual_chassis.master = devices[0]
+        virtual_chassis.save()
+
         module_bays = (
             ModuleBay(device=devices[0], name='Module Bay 1'),
             ModuleBay(device=devices[1], name='Module Bay 2'),
@@ -4759,6 +4762,19 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil
         params = {'device': [devices[0].name, devices[1].name]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
+    def test_virtual_chassis_member_or_master(self):
+        vc = VirtualChassis.objects.first()
+        master = vc.master
+        member = vc.members.exclude(pk=master.pk).first()
+        params = {'virtual_chassis_member_or_master_id': [master.pk,]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'virtual_chassis_member_or_master_id': [member.pk,]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'virtual_chassis_member_or_master': [master.name,]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'virtual_chassis_member_or_master': [member.name,]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
     def test_virtual_chassis_member(self):
         # Device 1A & 3 have 1 management interface, Device 1B has 1 interfaces
         devices = Device.objects.filter(name__in=['Device 1A', 'Device 3'])