Add support for finding all front ports in a virtual chassis

Implements filter_virtual_chassis_member.
This also displays all front ports on the master member of a virtual chassis.
This commit is contained in:
Jon Schewe 2023-11-22 17:07:13 -06:00
parent 3d941411d4
commit 784eba4796
3 changed files with 40 additions and 1 deletions

View File

@ -1576,6 +1576,17 @@ class FrontPortFilterSet(
NetBoxModelFilterSet, NetBoxModelFilterSet,
CabledObjectFilterSet CabledObjectFilterSet
): ):
virtual_chassis_member = MultiValueCharFilter(
method='filter_virtual_chassis_member',
field_name='name',
label=_('Virtual Chassis Front Ports for Device')
)
virtual_chassis_member_id = MultiValueNumberFilter(
method='filter_virtual_chassis_member',
field_name='pk',
label=_('Virtual Chassis Front Ports for Device (ID)')
)
type = django_filters.MultipleChoiceFilter( type = django_filters.MultipleChoiceFilter(
choices=PortTypeChoices, choices=PortTypeChoices,
null_value=None null_value=None
@ -1585,6 +1596,15 @@ class FrontPortFilterSet(
model = FrontPort model = FrontPort
fields = ['id', 'name', 'label', 'type', 'color', 'description', 'cable_end'] fields = ['id', 'name', 'label', 'type', 'color', 'description', 'cable_end']
def filter_virtual_chassis_member(self, queryset, name, value):
try:
vc_front_port_ids = []
for device in Device.objects.filter(**{f'{name}__in': value}):
vc_front_port_ids.extend(device.vc_front_ports(if_master=False).values_list('id', flat=True))
return queryset.filter(pk__in=vc_front_port_ids)
except Device.DoesNotExist:
return queryset.none()
class RearPortFilterSet( class RearPortFilterSet(
ModularDeviceComponentFilterSet, ModularDeviceComponentFilterSet,

View File

@ -1103,6 +1103,22 @@ class Device(
filter |= Q(device__virtual_chassis=self.virtual_chassis, mgmt_only=False) filter |= Q(device__virtual_chassis=self.virtual_chassis, mgmt_only=False)
return Interface.objects.filter(filter) return Interface.objects.filter(filter)
@property
def front_ports_count(self):
return self.vc_front_ports().count()
def vc_front_ports(self, if_master=True):
"""
Return a QuerySet matching all FrontPorts assigned to this Device or, if this Device is a VC master, to another
Device belonging to the same VirtualChassis.
:param if_master: If True, return VC member front ports only if this Device is the VC master.
"""
filter = Q(device=self)
if self.virtual_chassis and (self.virtual_chassis.master == self or not if_master):
filter |= Q(device__virtual_chassis=self.virtual_chassis)
return FrontPort.objects.filter(filter)
def get_cables(self, pk_list=False): def get_cables(self, pk_list=False):
""" """
Return a QuerySet or PK list matching all Cables connected to a component of this Device. Return a QuerySet or PK list matching all Cables connected to a component of this Device.

View File

@ -1945,12 +1945,15 @@ class DeviceFrontPortsView(DeviceComponentsView):
template_name = 'dcim/device/frontports.html' template_name = 'dcim/device/frontports.html'
tab = ViewTab( tab = ViewTab(
label=_('Front Ports'), label=_('Front Ports'),
badge=lambda obj: obj.front_port_count, badge=lambda obj: obj.vc_front_ports().count(),
permission='dcim.view_frontport', permission='dcim.view_frontport',
weight=530, weight=530,
hide_if_empty=True hide_if_empty=True
) )
def get_children(self, request, parent):
return parent.vc_front_ports().restrict(request.user, 'view')
@register_model_view(Device, 'rearports', path='rear-ports') @register_model_view(Device, 'rearports', path='rear-ports')
class DeviceRearPortsView(DeviceComponentsView): class DeviceRearPortsView(DeviceComponentsView):