diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index 5d92af878..1a9887c31 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -1304,6 +1304,9 @@ class InterfaceFilterSet( to_field_name='rd', label='VRF (RD)', ) + is_occupied = django_filters.BooleanFilter( + method='filter_is_occupied' + ) class Meta: model = Interface @@ -1359,6 +1362,12 @@ class InterfaceFilterSet( 'wireless': queryset.filter(type__in=WIRELESS_IFACE_TYPES), }.get(value, queryset.none()) + def filter_is_occupied(self, queryset, name, value): + if value: + return queryset.filter(Q(cable__isnull=False) | Q(mark_connected=True)) + else: + return queryset.filter(cable__isnull=True, mark_connected=False) + class FrontPortFilterSet( ModularDeviceComponentFilterSet, diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py index 173ea5d1e..85fe909c5 100644 --- a/netbox/dcim/forms/filtersets.py +++ b/netbox/dcim/forms/filtersets.py @@ -1009,6 +1009,7 @@ class InterfaceFilterForm(DeviceComponentFilterForm): ('PoE', ('poe_mode', 'poe_type')), ('Wireless', ('rf_role', 'rf_channel', 'rf_channel_width', 'tx_power')), ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), + ('Connection', ('cabled', 'connected', 'is_occupied')) ) kind = MultipleChoiceField( choices=InterfaceKindChoices, @@ -1087,6 +1088,24 @@ class InterfaceFilterForm(DeviceComponentFilterForm): label='VRF' ) tag = TagFilterField(model) + cabled = forms.NullBooleanField( + required=False, + widget=StaticSelect( + choices=BOOLEAN_WITH_BLANK_CHOICES + ) + ) + connected = forms.NullBooleanField( + required=False, + widget=StaticSelect( + choices=BOOLEAN_WITH_BLANK_CHOICES + ) + ) + is_occupied = forms.NullBooleanField( + required=False, + widget=StaticSelect( + choices=BOOLEAN_WITH_BLANK_CHOICES + ) + ) class FrontPortFilterForm(DeviceComponentFilterForm):