diff --git a/CHANGELOG.md b/CHANGELOG.md index cc10c7c50..a769c5e0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,14 @@ NetBox now supports modeling physical cables for console, power, and interface c * Introduced a new API endpoint for cables at `/dcim/cables/` * New endpoints for front and rear pass-through ports (and their templates) in parallel with existing device components * New fields for dcim.Rack: `status`, `asset_tag`, `outer_width`, `outer_depth`, `outer_unit` +* The following boolean filters on dcim.Device and dcim.DeviceType have been renamed: + * `is_console_server`: `console_server_ports` + * `is_pdu`: `power_outlets` + * `is_network_device`: `interfaces` +* The following new boolean filters have been introduced for dcim.Device and dcim.DeviceType: + * `console_ports` + * `power_ports` + * `pass_through_ports` --- diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index df2428e23..6def9d4c2 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -309,6 +309,30 @@ class DeviceTypeFilter(CustomFieldFilterSet, django_filters.FilterSet): to_field_name='slug', label='Manufacturer (slug)', ) + console_ports = django_filters.CharFilter( + method='_console_ports', + label='Has console ports', + ) + console_server_ports = django_filters.CharFilter( + method='_console_server_ports', + label='Has console server ports', + ) + power_ports = django_filters.CharFilter( + method='_power_ports', + label='Has power ports', + ) + power_outlets = django_filters.CharFilter( + method='_power_outlets', + label='Has power outlets', + ) + interfaces = django_filters.CharFilter( + method='_interfaces', + label='Has interfaces', + ) + pass_through_ports = django_filters.CharFilter( + method='_pass_through_ports', + label='Has pass-through ports', + ) tag = django_filters.CharFilter( name='tags__slug', ) @@ -329,6 +353,33 @@ class DeviceTypeFilter(CustomFieldFilterSet, django_filters.FilterSet): Q(comments__icontains=value) ) + def _console_ports(self, queryset, name, value): + value = value.strip() + return queryset.exclude(console_port_templates__isnull=bool(value)) + + def _console_server_ports(self, queryset, name, value): + value = value.strip() + return queryset.exclude(cs_port_templates__isnull=bool(value)) + + def _power_ports(self, queryset, name, value): + value = value.strip() + return queryset.exclude(power_port_templates__isnull=bool(value)) + + def _power_outlets(self, queryset, name, value): + value = value.strip() + return queryset.exclude(power_outlet_templates__isnull=bool(value)) + + def _interfaces(self, queryset, name, value): + value = value.strip() + return queryset.exclude(interface_templates__isnull=bool(value)) + + def _pass_through_ports(self, queryset, name, value): + value = value.strip() + return queryset.exclude( + front_port_templates__isnull=bool(value), + rear_port_templates__isnull=bool(value) + ) + class DeviceTypeComponentFilterSet(django_filters.FilterSet): devicetype_id = django_filters.ModelMultipleChoiceFilter( @@ -511,19 +562,30 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet): name='device_type__is_full_depth', label='Is full depth', ) - # TODO: Replace these filters - # is_console_server = django_filters.BooleanFilter( - # name='device_type__is_console_server', - # label='Is a console server', - # ) - # is_pdu = django_filters.BooleanFilter( - # name='device_type__is_pdu', - # label='Is a PDU', - # ) - # is_network_device = django_filters.BooleanFilter( - # name='device_type__is_network_device', - # label='Is a network device', - # ) + console_ports = django_filters.CharFilter( + method='_console_ports', + label='Has console ports', + ) + console_server_ports = django_filters.CharFilter( + method='_console_server_ports', + label='Has console server ports', + ) + power_ports = django_filters.CharFilter( + method='_power_ports', + label='Has power ports', + ) + power_outlets = django_filters.CharFilter( + method='_power_outlets', + label='Has power outlets', + ) + interfaces = django_filters.CharFilter( + method='_interfaces', + label='Has interfaces', + ) + pass_through_ports = django_filters.CharFilter( + method='_pass_through_ports', + label='Has pass-through ports', + ) mac_address = django_filters.CharFilter( method='_mac_address', label='MAC address', @@ -578,6 +640,33 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet): Q(primary_ip6__isnull=False) ) + def _console_ports(self, queryset, name, value): + value = value.strip() + return queryset.exclude(consoleports__isnull=bool(value)) + + def _console_server_ports(self, queryset, name, value): + value = value.strip() + return queryset.exclude(consoleserverports__isnull=bool(value)) + + def _power_ports(self, queryset, name, value): + value = value.strip() + return queryset.exclude(powerports__isnull=bool(value)) + + def _power_outlets(self, queryset, name, value): + value = value.strip() + return queryset.exclude(poweroutlets__isnull=bool(value)) + + def _interfaces(self, queryset, name, value): + value = value.strip() + return queryset.exclude(interfaces__isnull=bool(value)) + + def _pass_through_ports(self, queryset, name, value): + value = value.strip() + return queryset.exclude( + frontports__isnull=bool(value), + rearports__isnull=bool(value) + ) + class DeviceComponentFilterSet(django_filters.FilterSet): device_id = django_filters.ModelChoiceFilter( diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 3fd6f85c5..d4f1a6c55 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -661,12 +661,34 @@ class DeviceTypeFilterForm(BootstrapMixin, CustomFieldFilterForm): queryset=Manufacturer.objects.annotate(filter_count=Count('device_types')), to_field_name='slug' ) + console_ports = forms.BooleanField( + required=False, + label='Has console ports' + ) + console_server_ports = forms.BooleanField( + required=False, + label='Has console server ports' + ) + power_ports = forms.BooleanField( + required=False, + label='Has power ports' + ) + power_outlets = forms.BooleanField( + required=False, + label='Has power outlets' + ) + interfaces = forms.BooleanField( + required=False, + label='Has interfaces' + ) + pass_through_ports = forms.BooleanField( + required=False, + label='Has pass-through ports' + ) subdevice_role = forms.NullBooleanField( - required=False, label='Subdevice role', widget=forms.Select(choices=( - ('', '---------'), - (SUBDEVICE_ROLE_PARENT, 'Parent'), - (SUBDEVICE_ROLE_CHILD, 'Child'), - )) + required=False, + label='Subdevice role', + widget=forms.Select(choices=add_blank_choice(SUBDEVICE_ROLE_CHOICES)) )