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

This commit is contained in:
Jeremy Stretch 2019-07-18 21:21:56 -04:00
parent 71551893b1
commit 86b6b9bf8b
4 changed files with 39 additions and 29 deletions

View File

@ -8,6 +8,7 @@ v2.6.2 (FUTURE)
## Bug Fixes ## Bug Fixes
* [#3293](https://github.com/netbox-community/netbox/issues/3293) - Enable filtering device components by multiple device IDs * [#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 * [#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 * [#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 * [#3342](https://github.com/netbox-community/netbox/issues/3342) - Fix cluster delete button

View File

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

View File

@ -7,6 +7,8 @@ from django.contrib.postgres.forms.array import SimpleArrayField
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q from django.db.models import Q
from mptt.forms import TreeNodeChoiceField from mptt.forms import TreeNodeChoiceField
from netaddr import EUI
from netaddr.core import AddrFormatError
from taggit.forms import TagField from taggit.forms import TagField
from timezone_field import TimeZoneFormField 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 # Regions
# #

View File

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