mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-28 10:16:10 -06:00
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
This commit is contained in:
parent
44f173f01d
commit
9f605a2db1
@ -1885,6 +1885,16 @@ class InterfaceFilterSet(
|
|||||||
PathEndpointFilterSet,
|
PathEndpointFilterSet,
|
||||||
CommonInterfaceFilterSet
|
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(
|
virtual_chassis_member = MultiValueCharFilter(
|
||||||
method='filter_virtual_chassis_member',
|
method='filter_virtual_chassis_member',
|
||||||
field_name='name',
|
field_name='name',
|
||||||
@ -1995,11 +2005,14 @@ class InterfaceFilterSet(
|
|||||||
'cable_id', 'cable_end',
|
'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:
|
try:
|
||||||
vc_interface_ids = []
|
vc_interface_ids = []
|
||||||
for device in Device.objects.filter(**{f'{name}__in': value}):
|
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)
|
return queryset.filter(pk__in=vc_interface_ids)
|
||||||
except Device.DoesNotExist:
|
except Device.DoesNotExist:
|
||||||
return queryset.none()
|
return queryset.none()
|
||||||
|
@ -19,6 +19,11 @@ def get_cable_form(a_type, b_type):
|
|||||||
# Device component
|
# Device component
|
||||||
if hasattr(term_cls, 'device'):
|
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(
|
attrs[f'termination_{cable_end}_device'] = DynamicModelMultipleChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
label=_('Device'),
|
label=_('Device'),
|
||||||
@ -36,7 +41,7 @@ def get_cable_form(a_type, b_type):
|
|||||||
'parent': 'device',
|
'parent': 'device',
|
||||||
},
|
},
|
||||||
query_params={
|
query_params={
|
||||||
'device_id': f'$termination_{cable_end}_device',
|
query_param_device_field: f'$termination_{cable_end}_device',
|
||||||
'kind': 'physical', # Exclude virtual interfaces
|
'kind': 'physical', # Exclude virtual interfaces
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -4373,6 +4373,9 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil
|
|||||||
)
|
)
|
||||||
Device.objects.bulk_create(devices)
|
Device.objects.bulk_create(devices)
|
||||||
|
|
||||||
|
virtual_chassis.master = devices[0]
|
||||||
|
virtual_chassis.save()
|
||||||
|
|
||||||
module_bays = (
|
module_bays = (
|
||||||
ModuleBay(device=devices[0], name='Module Bay 1'),
|
ModuleBay(device=devices[0], name='Module Bay 1'),
|
||||||
ModuleBay(device=devices[1], name='Module Bay 2'),
|
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]}
|
params = {'device': [devices[0].name, devices[1].name]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
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):
|
def test_virtual_chassis_member(self):
|
||||||
# Device 1A & 3 have 1 management interface, Device 1B has 1 interfaces
|
# Device 1A & 3 have 1 management interface, Device 1B has 1 interfaces
|
||||||
devices = Device.objects.filter(name__in=['Device 1A', 'Device 3'])
|
devices = Device.objects.filter(name__in=['Device 1A', 'Device 3'])
|
||||||
|
Loading…
Reference in New Issue
Block a user