mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 01:41:22 -06:00
Fixes #3315: Enable filtering devices/interfaces by multiple MAC addresses
This commit is contained in:
parent
71551893b1
commit
86b6b9bf8b
@ -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
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
||||||
#
|
#
|
||||||
|
@ -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>]
|
||||||
|
Loading…
Reference in New Issue
Block a user