Просмотр исходного кода

Fixes #3315: Enable filtering devices/interfaces by multiple MAC addresses

Jeremy Stretch 6 лет назад
Родитель
Сommit
86b6b9bf8b
4 измененных файлов с 39 добавлено и 29 удалено
  1. 1 0
      CHANGELOG.md
  2. 5 29
      netbox/dcim/filters.py
  3. 24 0
      netbox/dcim/forms.py
  4. 9 0
      netbox/utilities/filters.py

+ 1 - 0
CHANGELOG.md

@@ -8,6 +8,7 @@ v2.6.2 (FUTURE)
 ## Bug Fixes
 
 * [#3293](https://github.com/netbox-community/netbox/issues/3293) - Enable filtering device components by multiple device IDs
+* [#3315](https://github.com/netbox-community/netbox/issues/3315) - Enable filtering devices/interfaces by multiple MAC addresses
 * [#3317](https://github.com/netbox-community/netbox/issues/3317) - Fix permissions for ConfigContextBulkDeleteView
 * [#3323](https://github.com/netbox-community/netbox/issues/3323) - Fix permission evaluation for interface connections view
 * [#3342](https://github.com/netbox-community/netbox/issues/3342) - Fix cluster delete button

+ 5 - 29
netbox/dcim/filters.py

@@ -2,15 +2,14 @@ import django_filters
 from django.contrib.auth.models import User
 from django.core.exceptions import ObjectDoesNotExist
 from django.db.models import Q
-from netaddr import EUI
-from netaddr.core import AddrFormatError
 
 from extras.filters import CustomFieldFilterSet
 from tenancy.filtersets import TenancyFilterSet
 from tenancy.models import Tenant
 from utilities.constants import COLOR_CHOICES
 from utilities.filters import (
-    MultiValueNumberFilter, NameSlugSearchFilterSet, NumericInFilter, TagFilter, TreeNodeMultipleChoiceFilter,
+    MultiValueMACAddressFilter, MultiValueNumberFilter, NameSlugSearchFilterSet, NumericInFilter, TagFilter,
+    TreeNodeMultipleChoiceFilter,
 )
 from virtualization.models import Cluster
 from .constants import *
@@ -516,8 +515,8 @@ class DeviceFilter(TenancyFilterSet, CustomFieldFilterSet):
         field_name='device_type__is_full_depth',
         label='Is full depth',
     )
-    mac_address = django_filters.CharFilter(
-        method='_mac_address',
+    mac_address = MultiValueMACAddressFilter(
+        field_name='interfaces__mac_address',
         label='MAC address',
     )
     has_primary_ip = django_filters.BooleanFilter(
@@ -574,16 +573,6 @@ class DeviceFilter(TenancyFilterSet, CustomFieldFilterSet):
             Q(comments__icontains=value)
         ).distinct()
 
-    def _mac_address(self, queryset, name, value):
-        value = value.strip()
-        if not value:
-            return queryset
-        try:
-            mac = EUI(value.strip())
-            return queryset.filter(interfaces__mac_address=mac).distinct()
-        except AddrFormatError:
-            return queryset.none()
-
     def _has_primary_ip(self, queryset, name, value):
         if value:
             return queryset.filter(
@@ -726,10 +715,7 @@ class InterfaceFilter(django_filters.FilterSet):
         queryset=Interface.objects.all(),
         label='LAG interface (ID)',
     )
-    mac_address = django_filters.CharFilter(
-        method='_mac_address',
-        label='MAC address',
-    )
+    mac_address = MultiValueMACAddressFilter()
     tag = TagFilter()
     vlan_id = django_filters.CharFilter(
         method='filter_vlan_id',
@@ -801,16 +787,6 @@ class InterfaceFilter(django_filters.FilterSet):
             'wireless': queryset.filter(type__in=WIRELESS_IFACE_TYPES),
         }.get(value, queryset.none())
 
-    def _mac_address(self, queryset, name, value):
-        value = value.strip()
-        if not value:
-            return queryset
-        try:
-            mac = EUI(value.strip())
-            return queryset.filter(mac_address=mac)
-        except AddrFormatError:
-            return queryset.none()
-
 
 class FrontPortFilter(DeviceComponentFilterSet):
     cabled = django_filters.BooleanFilter(

+ 24 - 0
netbox/dcim/forms.py

@@ -7,6 +7,8 @@ from django.contrib.postgres.forms.array import SimpleArrayField
 from django.core.exceptions import ObjectDoesNotExist
 from django.db.models import Q
 from mptt.forms import TreeNodeChoiceField
+from netaddr import EUI
+from netaddr.core import AddrFormatError
 from taggit.forms import TagField
 from timezone_field import TimeZoneFormField
 
@@ -76,6 +78,28 @@ class BulkRenameForm(forms.Form):
                 })
 
 
+#
+# Fields
+#
+
+class MACAddressField(forms.Field):
+    widget = forms.CharField
+    default_error_messages = {
+        'invalid': 'MAC address must be in EUI-48 format',
+    }
+
+    def to_python(self, value):
+        value = super().to_python(value)
+
+        # Validate MAC address format
+        try:
+            value = EUI(value.strip())
+        except AddrFormatError:
+            raise forms.ValidationError(self.error_messages['invalid'], code='invalid')
+
+        return value
+
+
 #
 # Regions
 #

+ 9 - 0
netbox/utilities/filters.py

@@ -3,6 +3,7 @@ from django import forms
 from django.conf import settings
 from django.db import models
 
+from dcim.forms import MACAddressField
 from extras.models import Tag
 
 
@@ -49,6 +50,14 @@ class MultiValueTimeFilter(django_filters.MultipleChoiceFilter):
     field_class = multivalue_field_factory(forms.TimeField)
 
 
+class MACAddressFilter(django_filters.CharFilter):
+    field_class = MACAddressField
+
+
+class MultiValueMACAddressFilter(django_filters.MultipleChoiceFilter):
+    field_class = multivalue_field_factory(MACAddressField)
+
+
 class TreeNodeMultipleChoiceFilter(django_filters.ModelMultipleChoiceFilter):
     """
     Filters for a set of Models, including all descendant models within a Tree.  Example: [<Region: R1>,<Region: R2>]