diff --git a/docs/release-notes/version-2.8.md b/docs/release-notes/version-2.8.md index e26450f2b..e92ca7286 100644 --- a/docs/release-notes/version-2.8.md +++ b/docs/release-notes/version-2.8.md @@ -4,6 +4,7 @@ ### Enhancements +* [#4869](https://github.com/netbox-community/netbox/issues/4869) - Allow global search by MAC address * [#4898](https://github.com/netbox-community/netbox/issues/4898) - Add MAC address search field to interfaces list * [#4899](https://github.com/netbox-community/netbox/issues/4899) - Add MAC address column to interfaces table diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index 8c24180bb..4504a9a01 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -741,13 +741,14 @@ class DeviceComponentFilterSet(django_filters.FilterSet): ) tag = TagFilter() - def search(self, queryset, name, value): + def search(self, queryset, name, value, additional_fields): if not value.strip(): return queryset - return queryset.filter( - Q(name__icontains=value) | - Q(description__icontains=value) - ) + fields = ['name__icontains', 'description__icontains'] + if additional_fields: + fields.extend(additional_fields) + query_fields = {field: value for field in fields} + return queryset.filter(Q(**query_fields, _connector=Q.OR)) class ConsolePortFilterSet(BaseFilterSet, DeviceComponentFilterSet): @@ -911,6 +912,10 @@ class InterfaceFilterSet(BaseFilterSet, DeviceComponentFilterSet): 'wireless': queryset.filter(type__in=WIRELESS_IFACE_TYPES), }.get(value, queryset.none()) + def search(self, queryset, name, value): + additional_fields = ['mac_address__icontains'] + return super().search(queryset=queryset, name=name, value=value, additional_fields=additional_fields) + class FrontPortFilterSet(BaseFilterSet, DeviceComponentFilterSet): cabled = django_filters.BooleanFilter( diff --git a/netbox/netbox/views.py b/netbox/netbox/views.py index 37a516409..acd3d2100 100644 --- a/netbox/netbox/views.py +++ b/netbox/netbox/views.py @@ -13,14 +13,14 @@ from circuits.filters import CircuitFilterSet, ProviderFilterSet from circuits.models import Circuit, Provider from circuits.tables import CircuitTable, ProviderTable from dcim.filters import ( - CableFilterSet, DeviceFilterSet, DeviceTypeFilterSet, PowerFeedFilterSet, RackFilterSet, RackGroupFilterSet, SiteFilterSet, + CableFilterSet, DeviceFilterSet, DeviceTypeFilterSet, InterfaceFilterSet, PowerFeedFilterSet, RackFilterSet, RackGroupFilterSet, SiteFilterSet, VirtualChassisFilterSet, ) from dcim.models import ( Cable, ConsolePort, Device, DeviceType, Interface, PowerPanel, PowerFeed, PowerPort, Rack, RackGroup, Site, VirtualChassis ) from dcim.tables import ( - CableTable, DeviceTable, DeviceTypeTable, PowerFeedTable, RackTable, RackGroupTable, SiteTable, + CableTable, DeviceTable, DeviceTypeTable, InterfaceDetailTable, PowerFeedTable, RackTable, RackGroupTable, SiteTable, VirtualChassisTable, ) from extras.models import ObjectChange, ReportResult @@ -96,6 +96,15 @@ SEARCH_TYPES = OrderedDict(( 'table': DeviceTable, 'url': 'dcim:device_list', }), + ('interface', { + 'permission': 'dcim.view_interface', + 'queryset': Interface.objects.prefetch_related( + 'device', 'virtual_machine', + ), + 'filterset': InterfaceFilterSet, + 'table': InterfaceDetailTable, + 'url': 'dcim:interface_list', + }), ('virtualchassis', { 'permission': 'dcim.view_virtualchassis', 'queryset': VirtualChassis.objects.prefetch_related('master').annotate(member_count=Count('members')),