mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Merge branch 'develop' into api2
Conflicts: netbox/dcim/api/serializers.py netbox/dcim/api/urls.py netbox/dcim/api/views.py netbox/dcim/filters.py netbox/dcim/tables.py requirements.txt
This commit is contained in:
@ -10,8 +10,8 @@ from .models import Provider, Circuit, CircuitTermination, CircuitType
|
||||
|
||||
|
||||
class ProviderFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
q = django_filters.MethodFilter(
|
||||
action='search',
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
@ -30,7 +30,9 @@ class ProviderFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
model = Provider
|
||||
fields = ['name', 'account', 'asn']
|
||||
|
||||
def search(self, queryset, value):
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(name__icontains=value) |
|
||||
Q(account__icontains=value) |
|
||||
@ -39,8 +41,8 @@ class ProviderFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
|
||||
|
||||
class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
q = django_filters.MethodFilter(
|
||||
action='search',
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
provider_id = django_filters.ModelMultipleChoiceFilter(
|
||||
@ -92,7 +94,9 @@ class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
model = Circuit
|
||||
fields = ['install_date']
|
||||
|
||||
def search(self, queryset, value):
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(cid__icontains=value) |
|
||||
Q(terminations__xconnect_id__icontains=value) |
|
||||
|
@ -64,6 +64,7 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
model = Provider
|
||||
q = forms.CharField(required=False, label='Search')
|
||||
site = FilterChoiceField(queryset=Site.objects.all(), to_field_name='slug')
|
||||
asn = forms.IntegerField(required=False, label='ASN')
|
||||
|
||||
|
||||
#
|
||||
@ -128,14 +129,23 @@ class CircuitBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
class CircuitFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
model = Circuit
|
||||
q = forms.CharField(required=False, label='Search')
|
||||
type = FilterChoiceField(queryset=CircuitType.objects.annotate(filter_count=Count('circuits')),
|
||||
to_field_name='slug')
|
||||
provider = FilterChoiceField(queryset=Provider.objects.annotate(filter_count=Count('circuits')),
|
||||
to_field_name='slug')
|
||||
tenant = FilterChoiceField(queryset=Tenant.objects.annotate(filter_count=Count('circuits')), to_field_name='slug',
|
||||
null_option=(0, 'None'))
|
||||
site = FilterChoiceField(queryset=Site.objects.annotate(filter_count=Count('circuit_terminations')),
|
||||
to_field_name='slug')
|
||||
type = FilterChoiceField(
|
||||
queryset=CircuitType.objects.annotate(filter_count=Count('circuits')),
|
||||
to_field_name='slug'
|
||||
)
|
||||
provider = FilterChoiceField(
|
||||
queryset=Provider.objects.annotate(filter_count=Count('circuits')),
|
||||
to_field_name='slug'
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.annotate(filter_count=Count('circuits')),
|
||||
to_field_name='slug',
|
||||
null_option=(0, 'None')
|
||||
)
|
||||
site = FilterChoiceField(
|
||||
queryset=Site.objects.annotate(filter_count=Count('circuit_terminations')),
|
||||
to_field_name='slug'
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
|
@ -15,8 +15,8 @@ from .models import (
|
||||
|
||||
|
||||
class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
q = django_filters.MethodFilter(
|
||||
action='search',
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
region_id = NullableModelMultipleChoiceFilter(
|
||||
@ -46,9 +46,16 @@ class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
model = Site
|
||||
fields = ['q', 'name', 'facility', 'asn']
|
||||
|
||||
def search(self, queryset, value):
|
||||
qs_filter = Q(name__icontains=value) | Q(facility__icontains=value) | Q(physical_address__icontains=value) | \
|
||||
Q(shipping_address__icontains=value) | Q(comments__icontains=value)
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
qs_filter = (
|
||||
Q(name__icontains=value) |
|
||||
Q(facility__icontains=value) |
|
||||
Q(physical_address__icontains=value) |
|
||||
Q(shipping_address__icontains=value) |
|
||||
Q(comments__icontains=value)
|
||||
)
|
||||
try:
|
||||
qs_filter |= Q(asn=int(value.strip()))
|
||||
except ValueError:
|
||||
@ -71,11 +78,12 @@ class RackGroupFilter(django_filters.FilterSet):
|
||||
|
||||
class Meta:
|
||||
model = RackGroup
|
||||
fields = ['name']
|
||||
|
||||
|
||||
class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
q = django_filters.MethodFilter(
|
||||
action='search',
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
@ -127,7 +135,9 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
model = Rack
|
||||
fields = ['u_height']
|
||||
|
||||
def search(self, queryset, value):
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(name__icontains=value) |
|
||||
Q(facility_id__icontains=value) |
|
||||
@ -148,8 +158,8 @@ class RackReservationFilter(django_filters.FilterSet):
|
||||
|
||||
|
||||
class DeviceTypeFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
q = django_filters.MethodFilter(
|
||||
action='search',
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
|
||||
@ -166,10 +176,13 @@ class DeviceTypeFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
|
||||
class Meta:
|
||||
model = DeviceType
|
||||
fields = ['model', 'part_number', 'u_height', 'is_console_server', 'is_pdu', 'is_network_device',
|
||||
'subdevice_role']
|
||||
fields = [
|
||||
'model', 'part_number', 'u_height', 'is_console_server', 'is_pdu', 'is_network_device', 'subdevice_role',
|
||||
]
|
||||
|
||||
def search(self, queryset, value):
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(manufacturer__name__icontains=value) |
|
||||
Q(model__icontains=value) |
|
||||
@ -235,12 +248,12 @@ class DeviceBayTemplateFilter(DeviceTypeComponentFilterSet):
|
||||
|
||||
|
||||
class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
q = django_filters.MethodFilter(
|
||||
action='search',
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
mac_address = django_filters.MethodFilter(
|
||||
action='_mac_address',
|
||||
mac_address = django_filters.CharFilter(
|
||||
method='_mac_address',
|
||||
label='MAC address',
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
@ -340,7 +353,9 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
model = Device
|
||||
fields = ['name', 'serial', 'asset_tag']
|
||||
|
||||
def search(self, queryset, value):
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(name__icontains=value) |
|
||||
Q(serial__icontains=value.strip()) |
|
||||
@ -349,7 +364,7 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
Q(comments__icontains=value)
|
||||
).distinct()
|
||||
|
||||
def _mac_address(self, queryset, value):
|
||||
def _mac_address(self, queryset, name, value):
|
||||
value = value.strip()
|
||||
if not value:
|
||||
return queryset
|
||||
@ -402,8 +417,8 @@ class PowerOutletFilter(DeviceComponentFilterSet):
|
||||
|
||||
|
||||
class InterfaceFilter(DeviceComponentFilterSet):
|
||||
type = django_filters.MethodFilter(
|
||||
action='filter_type',
|
||||
type = django_filters.CharFilter(
|
||||
method='filter_type',
|
||||
label='Interface type',
|
||||
)
|
||||
|
||||
@ -411,7 +426,7 @@ class InterfaceFilter(DeviceComponentFilterSet):
|
||||
model = Interface
|
||||
fields = ['name']
|
||||
|
||||
def filter_type(self, queryset, value):
|
||||
def filter_type(self, queryset, name, value):
|
||||
value = value.strip().lower()
|
||||
if value == 'physical':
|
||||
return queryset.exclude(form_factor__in=VIRTUAL_IFACE_TYPES)
|
||||
@ -437,51 +452,51 @@ class ModuleFilter(DeviceComponentFilterSet):
|
||||
|
||||
|
||||
class ConsoleConnectionFilter(django_filters.FilterSet):
|
||||
site = django_filters.MethodFilter(
|
||||
action='filter_site',
|
||||
site = django_filters.CharFilter(
|
||||
method='filter_site',
|
||||
label='Site (slug)',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ConsoleServerPort
|
||||
fields = []
|
||||
|
||||
def filter_site(self, queryset, value):
|
||||
value = value.strip()
|
||||
if not value:
|
||||
def filter_site(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(cs_port__device__rack__site__slug=value)
|
||||
return queryset.filter(cs_port__device__site__slug=value)
|
||||
|
||||
|
||||
class PowerConnectionFilter(django_filters.FilterSet):
|
||||
site = django_filters.MethodFilter(
|
||||
action='filter_site',
|
||||
site = django_filters.CharFilter(
|
||||
method='filter_site',
|
||||
label='Site (slug)',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PowerOutlet
|
||||
fields = []
|
||||
|
||||
def filter_site(self, queryset, value):
|
||||
value = value.strip()
|
||||
if not value:
|
||||
def filter_site(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(power_outlet__device__rack__site__slug=value)
|
||||
return queryset.filter(power_outlet__device__site__slug=value)
|
||||
|
||||
|
||||
class InterfaceConnectionFilter(django_filters.FilterSet):
|
||||
site = django_filters.MethodFilter(
|
||||
action='filter_site',
|
||||
site = django_filters.CharFilter(
|
||||
method='filter_site',
|
||||
label='Site (slug)',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = InterfaceConnection
|
||||
fields = []
|
||||
|
||||
def filter_site(self, queryset, value):
|
||||
value = value.strip()
|
||||
if not value:
|
||||
def filter_site(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(interface_a__device__rack__site__slug=value) |
|
||||
Q(interface_b__device__rack__site__slug=value)
|
||||
Q(interface_a__device__site__slug=value) |
|
||||
Q(interface_b__device__site__slug=value)
|
||||
)
|
||||
|
@ -281,13 +281,25 @@ class RackBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
class RackFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
model = Rack
|
||||
q = forms.CharField(required=False, label='Search')
|
||||
site = FilterChoiceField(queryset=Site.objects.annotate(filter_count=Count('racks')), to_field_name='slug')
|
||||
group_id = FilterChoiceField(queryset=RackGroup.objects.select_related('site')
|
||||
.annotate(filter_count=Count('racks')), label='Rack group', null_option=(0, 'None'))
|
||||
tenant = FilterChoiceField(queryset=Tenant.objects.annotate(filter_count=Count('racks')), to_field_name='slug',
|
||||
null_option=(0, 'None'))
|
||||
role = FilterChoiceField(queryset=RackRole.objects.annotate(filter_count=Count('racks')), to_field_name='slug',
|
||||
null_option=(0, 'None'))
|
||||
site = FilterChoiceField(
|
||||
queryset=Site.objects.annotate(filter_count=Count('racks')),
|
||||
to_field_name='slug'
|
||||
)
|
||||
group_id = FilterChoiceField(
|
||||
queryset=RackGroup.objects.select_related('site').annotate(filter_count=Count('racks')),
|
||||
label='Rack group',
|
||||
null_option=(0, 'None')
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.annotate(filter_count=Count('racks')),
|
||||
to_field_name='slug',
|
||||
null_option=(0, 'None')
|
||||
)
|
||||
role = FilterChoiceField(
|
||||
queryset=RackRole.objects.annotate(filter_count=Count('racks')),
|
||||
to_field_name='slug',
|
||||
null_option=(0, 'None')
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
@ -359,8 +371,10 @@ class DeviceTypeBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
class DeviceTypeFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
model = DeviceType
|
||||
q = forms.CharField(required=False, label='Search')
|
||||
manufacturer = FilterChoiceField(queryset=Manufacturer.objects.annotate(filter_count=Count('device_types')),
|
||||
to_field_name='slug')
|
||||
manufacturer = FilterChoiceField(
|
||||
queryset=Manufacturer.objects.annotate(filter_count=Count('device_types')),
|
||||
to_field_name='slug'
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
@ -724,7 +738,7 @@ class DeviceFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
)
|
||||
rack_group_id = FilterChoiceField(
|
||||
queryset=RackGroup.objects.select_related('site').annotate(filter_count=Count('racks__devices')),
|
||||
label='Rack Group',
|
||||
label='Rack group',
|
||||
)
|
||||
role = FilterChoiceField(
|
||||
queryset=DeviceRole.objects.annotate(filter_count=Count('devices')),
|
||||
|
21
netbox/dcim/migrations/0032_device_increase_name_length.py
Normal file
21
netbox/dcim/migrations/0032_device_increase_name_length.py
Normal file
@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-03-02 15:09
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
import utilities.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0031_regions'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='name',
|
||||
field=utilities.fields.NullableCharField(blank=True, max_length=64, null=True, unique=True),
|
||||
),
|
||||
]
|
@ -915,7 +915,7 @@ class Device(CreatedUpdatedModel, CustomFieldModel):
|
||||
device_role = models.ForeignKey('DeviceRole', related_name='devices', on_delete=models.PROTECT)
|
||||
tenant = models.ForeignKey(Tenant, blank=True, null=True, related_name='devices', on_delete=models.PROTECT)
|
||||
platform = models.ForeignKey('Platform', related_name='devices', blank=True, null=True, on_delete=models.SET_NULL)
|
||||
name = NullableCharField(max_length=50, blank=True, null=True, unique=True)
|
||||
name = NullableCharField(max_length=64, blank=True, null=True, unique=True)
|
||||
serial = models.CharField(max_length=50, blank=True, verbose_name='Serial number')
|
||||
asset_tag = NullableCharField(max_length=50, blank=True, null=True, unique=True, verbose_name='Asset tag',
|
||||
help_text='A unique tag used to identify this device')
|
||||
|
@ -94,6 +94,12 @@ STATUS_ICON = """
|
||||
{% endif %}
|
||||
"""
|
||||
|
||||
DEVICE_PRIMARY_IP = """
|
||||
{{ record.primary_ip6.address.ip|default:"" }}
|
||||
{% if record.primary_ip6 and record.primary_ip4 %}<br />{% endif %}
|
||||
{{ record.primary_ip4.address.ip|default:"" }}
|
||||
"""
|
||||
|
||||
UTILIZATION_GRAPH = """
|
||||
{% load helpers %}
|
||||
{% utilization_graph value %}
|
||||
@ -106,7 +112,6 @@ UTILIZATION_GRAPH = """
|
||||
|
||||
class RegionTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
# name = tables.LinkColumn(verbose_name='Name')
|
||||
name = tables.TemplateColumn(template_code=REGION_LINK, orderable=False)
|
||||
site_count = tables.Column(verbose_name='Sites')
|
||||
slug = tables.Column(verbose_name='Slug')
|
||||
@ -365,7 +370,7 @@ class DeviceTable(BaseTable):
|
||||
device_type = tables.LinkColumn('dcim:devicetype', args=[Accessor('device_type.pk')], verbose_name='Type',
|
||||
text=lambda record: record.device_type.full_name)
|
||||
primary_ip = tables.TemplateColumn(orderable=False, verbose_name='IP Address',
|
||||
template_code="{{ record.primary_ip.address.ip }}")
|
||||
template_code=DEVICE_PRIMARY_IP)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = Device
|
||||
|
@ -6,7 +6,7 @@ from django.db import models
|
||||
from .formfields import IPFormField
|
||||
from .lookups import (
|
||||
EndsWith, IEndsWith, IRegex, IStartsWith, NetContained, NetContainedOrEqual, NetContains, NetContainsOrEquals,
|
||||
NetHost, Regex, StartsWith,
|
||||
NetHost, NetMaskLength, Regex, StartsWith,
|
||||
)
|
||||
|
||||
|
||||
@ -67,6 +67,7 @@ IPNetworkField.register_lookup(NetContainedOrEqual)
|
||||
IPNetworkField.register_lookup(NetContains)
|
||||
IPNetworkField.register_lookup(NetContainsOrEquals)
|
||||
IPNetworkField.register_lookup(NetHost)
|
||||
IPNetworkField.register_lookup(NetMaskLength)
|
||||
|
||||
|
||||
class IPAddressField(BaseIPField):
|
||||
@ -90,3 +91,4 @@ IPAddressField.register_lookup(NetContainedOrEqual)
|
||||
IPAddressField.register_lookup(NetContains)
|
||||
IPAddressField.register_lookup(NetContainsOrEquals)
|
||||
IPAddressField.register_lookup(NetHost)
|
||||
IPAddressField.register_lookup(NetMaskLength)
|
||||
|
@ -13,15 +13,10 @@ from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLAN
|
||||
|
||||
|
||||
class VRFFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
q = django_filters.MethodFilter(
|
||||
action='search',
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
name = django_filters.CharFilter(
|
||||
name='name',
|
||||
lookup_type='icontains',
|
||||
label='Name',
|
||||
)
|
||||
tenant_id = NullableModelMultipleChoiceFilter(
|
||||
name='tenant',
|
||||
queryset=Tenant.objects.all(),
|
||||
@ -34,7 +29,9 @@ class VRFFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
label='Tenant (slug)',
|
||||
)
|
||||
|
||||
def search(self, queryset, value):
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(name__icontains=value) |
|
||||
Q(rd__icontains=value) |
|
||||
@ -43,7 +40,7 @@ class VRFFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
|
||||
class Meta:
|
||||
model = VRF
|
||||
fields = ['rd']
|
||||
fields = ['name', 'rd']
|
||||
|
||||
|
||||
class RIRFilter(django_filters.FilterSet):
|
||||
@ -54,8 +51,8 @@ class RIRFilter(django_filters.FilterSet):
|
||||
|
||||
|
||||
class AggregateFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
q = django_filters.MethodFilter(
|
||||
action='search',
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
rir_id = django_filters.ModelMultipleChoiceFilter(
|
||||
@ -74,7 +71,9 @@ class AggregateFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
model = Aggregate
|
||||
fields = ['family', 'date_added']
|
||||
|
||||
def search(self, queryset, value):
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
qs_filter = Q(description__icontains=value)
|
||||
try:
|
||||
prefix = str(IPNetwork(value.strip()).cidr)
|
||||
@ -85,14 +84,18 @@ class AggregateFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
|
||||
|
||||
class PrefixFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
q = django_filters.MethodFilter(
|
||||
action='search',
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
parent = django_filters.MethodFilter(
|
||||
action='search_by_parent',
|
||||
parent = django_filters.CharFilter(
|
||||
method='search_by_parent',
|
||||
label='Parent prefix',
|
||||
)
|
||||
mask_length = django_filters.NumberFilter(
|
||||
method='filter_mask_length',
|
||||
label='Mask length',
|
||||
)
|
||||
vrf_id = NullableModelMultipleChoiceFilter(
|
||||
name='vrf_id',
|
||||
queryset=VRF.objects.all(),
|
||||
@ -151,7 +154,9 @@ class PrefixFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
model = Prefix
|
||||
fields = ['family', 'status']
|
||||
|
||||
def search(self, queryset, value):
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
qs_filter = Q(description__icontains=value)
|
||||
try:
|
||||
prefix = str(IPNetwork(value.strip()).cidr)
|
||||
@ -160,7 +165,7 @@ class PrefixFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
pass
|
||||
return queryset.filter(qs_filter)
|
||||
|
||||
def search_by_parent(self, queryset, value):
|
||||
def search_by_parent(self, queryset, name, value):
|
||||
value = value.strip()
|
||||
if not value:
|
||||
return queryset
|
||||
@ -170,34 +175,25 @@ class PrefixFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
except AddrFormatError:
|
||||
return queryset.none()
|
||||
|
||||
def _tenant(self, queryset, value):
|
||||
if str(value) == '':
|
||||
def filter_mask_length(self, queryset, name, value):
|
||||
if not value:
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(tenant__slug=value) |
|
||||
Q(tenant__isnull=True, vrf__tenant__slug=value)
|
||||
)
|
||||
|
||||
def _tenant_id(self, queryset, value):
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
return queryset.none()
|
||||
return queryset.filter(
|
||||
Q(tenant__pk=value) |
|
||||
Q(tenant__isnull=True, vrf__tenant__pk=value)
|
||||
)
|
||||
return queryset.filter(prefix__net_mask_length=value)
|
||||
|
||||
|
||||
class IPAddressFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
q = django_filters.MethodFilter(
|
||||
action='search',
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
parent = django_filters.MethodFilter(
|
||||
action='search_by_parent',
|
||||
parent = django_filters.CharFilter(
|
||||
method='search_by_parent',
|
||||
label='Parent prefix',
|
||||
)
|
||||
mask_length = django_filters.NumberFilter(
|
||||
method='filter_mask_length',
|
||||
label='Mask length',
|
||||
)
|
||||
vrf_id = NullableModelMultipleChoiceFilter(
|
||||
name='vrf_id',
|
||||
queryset=VRF.objects.all(),
|
||||
@ -239,9 +235,11 @@ class IPAddressFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
|
||||
class Meta:
|
||||
model = IPAddress
|
||||
fields = ['q', 'family', 'status']
|
||||
fields = ['family', 'status']
|
||||
|
||||
def search(self, queryset, value):
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
qs_filter = Q(description__icontains=value)
|
||||
try:
|
||||
ipaddress = str(IPNetwork(value.strip()))
|
||||
@ -250,16 +248,21 @@ class IPAddressFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
pass
|
||||
return queryset.filter(qs_filter)
|
||||
|
||||
def search_by_parent(self, queryset, value):
|
||||
def search_by_parent(self, queryset, name, value):
|
||||
value = value.strip()
|
||||
if not value:
|
||||
return queryset
|
||||
try:
|
||||
query = str(IPNetwork(value).cidr)
|
||||
query = str(IPNetwork(value.strip()).cidr)
|
||||
return queryset.filter(address__net_contained_or_equal=query)
|
||||
except AddrFormatError:
|
||||
return queryset.none()
|
||||
|
||||
def filter_mask_length(self, queryset, name, value):
|
||||
if not value:
|
||||
return queryset
|
||||
return queryset.filter(address__net_mask_length=value)
|
||||
|
||||
|
||||
class VLANGroupFilter(django_filters.FilterSet):
|
||||
site_id = NullableModelMultipleChoiceFilter(
|
||||
@ -276,11 +279,12 @@ class VLANGroupFilter(django_filters.FilterSet):
|
||||
|
||||
class Meta:
|
||||
model = VLANGroup
|
||||
fields = ['name']
|
||||
|
||||
|
||||
class VLANFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
q = django_filters.MethodFilter(
|
||||
action='search',
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
site_id = NullableModelMultipleChoiceFilter(
|
||||
@ -305,15 +309,6 @@ class VLANFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
to_field_name='slug',
|
||||
label='Group',
|
||||
)
|
||||
name = django_filters.CharFilter(
|
||||
name='name',
|
||||
lookup_type='icontains',
|
||||
label='Name',
|
||||
)
|
||||
vid = django_filters.NumberFilter(
|
||||
name='vid',
|
||||
label='VLAN number (1-4095)',
|
||||
)
|
||||
tenant_id = NullableModelMultipleChoiceFilter(
|
||||
name='tenant',
|
||||
queryset=Tenant.objects.all(),
|
||||
@ -339,12 +334,14 @@ class VLANFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
|
||||
class Meta:
|
||||
model = VLAN
|
||||
fields = ['status']
|
||||
fields = ['name', 'vid', 'status']
|
||||
|
||||
def search(self, queryset, value):
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
qs_filter = Q(name__icontains=value) | Q(description__icontains=value)
|
||||
try:
|
||||
qs_filter |= Q(vid=int(value))
|
||||
qs_filter |= Q(vid=int(value.strip()))
|
||||
except ValueError:
|
||||
pass
|
||||
return queryset.filter(qs_filter)
|
||||
|
@ -21,6 +21,12 @@ IP_FAMILY_CHOICES = [
|
||||
(6, 'IPv6'),
|
||||
]
|
||||
|
||||
PREFIX_MASK_LENGTH_CHOICES = [
|
||||
('', '---------'),
|
||||
] + [(i, i) for i in range(1, 128)]
|
||||
|
||||
IPADDRESS_MASK_LENGTH_CHOICES = PREFIX_MASK_LENGTH_CHOICES + [(128, 128)]
|
||||
|
||||
|
||||
#
|
||||
# VRFs
|
||||
@ -131,8 +137,11 @@ class AggregateFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
model = Aggregate
|
||||
q = forms.CharField(required=False, label='Search')
|
||||
family = forms.ChoiceField(required=False, choices=IP_FAMILY_CHOICES, label='Address Family')
|
||||
rir = FilterChoiceField(queryset=RIR.objects.annotate(filter_count=Count('aggregates')), to_field_name='slug',
|
||||
label='RIR')
|
||||
rir = FilterChoiceField(
|
||||
queryset=RIR.objects.annotate(filter_count=Count('aggregates')),
|
||||
to_field_name='slug',
|
||||
label='RIR'
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
@ -259,19 +268,33 @@ def prefix_status_choices():
|
||||
class PrefixFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
model = Prefix
|
||||
q = forms.CharField(required=False, label='Search')
|
||||
parent = forms.CharField(required=False, label='Parent Prefix', widget=forms.TextInput(attrs={
|
||||
parent = forms.CharField(required=False, label='Parent prefix', widget=forms.TextInput(attrs={
|
||||
'placeholder': 'Prefix',
|
||||
}))
|
||||
family = forms.ChoiceField(required=False, choices=IP_FAMILY_CHOICES, label='Address Family')
|
||||
vrf = FilterChoiceField(queryset=VRF.objects.annotate(filter_count=Count('prefixes')), to_field_name='rd',
|
||||
label='VRF', null_option=(0, 'Global'))
|
||||
tenant = FilterChoiceField(queryset=Tenant.objects.annotate(filter_count=Count('prefixes')), to_field_name='slug',
|
||||
null_option=(0, 'None'))
|
||||
family = forms.ChoiceField(required=False, choices=IP_FAMILY_CHOICES, label='Address family')
|
||||
mask_length = forms.ChoiceField(required=False, choices=PREFIX_MASK_LENGTH_CHOICES, label='Mask length')
|
||||
vrf = FilterChoiceField(
|
||||
queryset=VRF.objects.annotate(filter_count=Count('prefixes')),
|
||||
to_field_name='rd',
|
||||
label='VRF',
|
||||
null_option=(0, 'Global')
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.annotate(filter_count=Count('prefixes')),
|
||||
to_field_name='slug',
|
||||
null_option=(0, 'None')
|
||||
)
|
||||
status = forms.MultipleChoiceField(choices=prefix_status_choices, required=False)
|
||||
site = FilterChoiceField(queryset=Site.objects.annotate(filter_count=Count('prefixes')), to_field_name='slug',
|
||||
null_option=(0, 'None'))
|
||||
role = FilterChoiceField(queryset=Role.objects.annotate(filter_count=Count('prefixes')), to_field_name='slug',
|
||||
null_option=(0, 'None'))
|
||||
site = FilterChoiceField(
|
||||
queryset=Site.objects.annotate(filter_count=Count('prefixes')),
|
||||
to_field_name='slug',
|
||||
null_option=(0, 'None')
|
||||
)
|
||||
role = FilterChoiceField(
|
||||
queryset=Role.objects.annotate(filter_count=Count('prefixes')),
|
||||
to_field_name='slug',
|
||||
null_option=(0, 'None')
|
||||
)
|
||||
expand = forms.BooleanField(required=False, label='Expand prefix hierarchy')
|
||||
|
||||
|
||||
@ -487,11 +510,19 @@ class IPAddressFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
parent = forms.CharField(required=False, label='Parent Prefix', widget=forms.TextInput(attrs={
|
||||
'placeholder': 'Prefix',
|
||||
}))
|
||||
family = forms.ChoiceField(required=False, choices=IP_FAMILY_CHOICES, label='Address Family')
|
||||
vrf = FilterChoiceField(queryset=VRF.objects.annotate(filter_count=Count('ip_addresses')), to_field_name='rd',
|
||||
label='VRF', null_option=(0, 'Global'))
|
||||
tenant = FilterChoiceField(queryset=Tenant.objects.annotate(filter_count=Count('ip_addresses')),
|
||||
to_field_name='slug', null_option=(0, 'None'))
|
||||
family = forms.ChoiceField(required=False, choices=IP_FAMILY_CHOICES, label='Address family')
|
||||
mask_length = forms.ChoiceField(required=False, choices=IPADDRESS_MASK_LENGTH_CHOICES, label='Mask length')
|
||||
vrf = FilterChoiceField(
|
||||
queryset=VRF.objects.annotate(filter_count=Count('ip_addresses')),
|
||||
to_field_name='rd',
|
||||
label='VRF',
|
||||
null_option=(0, 'Global')
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.annotate(filter_count=Count('ip_addresses')),
|
||||
to_field_name='slug',
|
||||
null_option=(0, 'None')
|
||||
)
|
||||
status = forms.MultipleChoiceField(choices=ipaddress_status_choices, required=False)
|
||||
|
||||
|
||||
@ -603,15 +634,27 @@ def vlan_status_choices():
|
||||
class VLANFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
model = VLAN
|
||||
q = forms.CharField(required=False, label='Search')
|
||||
site = FilterChoiceField(queryset=Site.objects.annotate(filter_count=Count('vlans')), to_field_name='slug',
|
||||
null_option=(0, 'Global'))
|
||||
group_id = FilterChoiceField(queryset=VLANGroup.objects.annotate(filter_count=Count('vlans')), label='VLAN group',
|
||||
null_option=(0, 'None'))
|
||||
tenant = FilterChoiceField(queryset=Tenant.objects.annotate(filter_count=Count('vlans')), to_field_name='slug',
|
||||
null_option=(0, 'None'))
|
||||
site = FilterChoiceField(
|
||||
queryset=Site.objects.annotate(filter_count=Count('vlans')),
|
||||
to_field_name='slug',
|
||||
null_option=(0, 'Global')
|
||||
)
|
||||
group_id = FilterChoiceField(
|
||||
queryset=VLANGroup.objects.annotate(filter_count=Count('vlans')),
|
||||
label='VLAN group',
|
||||
null_option=(0, 'None')
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.annotate(filter_count=Count('vlans')),
|
||||
to_field_name='slug',
|
||||
null_option=(0, 'None')
|
||||
)
|
||||
status = forms.MultipleChoiceField(choices=vlan_status_choices, required=False)
|
||||
role = FilterChoiceField(queryset=Role.objects.annotate(filter_count=Count('vlans')), to_field_name='slug',
|
||||
null_option=(0, 'None'))
|
||||
role = FilterChoiceField(
|
||||
queryset=Role.objects.annotate(filter_count=Count('vlans')),
|
||||
to_field_name='slug',
|
||||
null_option=(0, 'None')
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
|
@ -1,4 +1,4 @@
|
||||
from django.db.models import Lookup
|
||||
from django.db.models import Lookup, Transform, IntegerField
|
||||
from django.db.models.lookups import BuiltinLookup
|
||||
|
||||
|
||||
@ -87,3 +87,12 @@ class NetHost(Lookup):
|
||||
rhs_params[0] = rhs_params[0].split('/')[0]
|
||||
params = lhs_params + rhs_params
|
||||
return 'HOST(%s) = %s' % (lhs, rhs), params
|
||||
|
||||
|
||||
class NetMaskLength(Transform):
|
||||
lookup_name = 'net_mask_length'
|
||||
function = 'MASKLEN'
|
||||
|
||||
@property
|
||||
def output_field(self):
|
||||
return IntegerField()
|
||||
|
@ -12,7 +12,7 @@ except ImportError:
|
||||
"the documentation.")
|
||||
|
||||
|
||||
VERSION = '1.8.5-dev'
|
||||
VERSION = '1.9.1-dev'
|
||||
|
||||
# Import local configuration
|
||||
for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY']:
|
||||
|
@ -7,8 +7,8 @@ from dcim.models import Device
|
||||
|
||||
|
||||
class SecretFilter(django_filters.FilterSet):
|
||||
q = django_filters.MethodFilter(
|
||||
action='search',
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
role_id = django_filters.ModelMultipleChoiceFilter(
|
||||
@ -38,7 +38,9 @@ class SecretFilter(django_filters.FilterSet):
|
||||
model = Secret
|
||||
fields = ['name']
|
||||
|
||||
def search(self, queryset, value):
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(name__icontains=value) |
|
||||
Q(device__name__icontains=value)
|
||||
|
@ -96,7 +96,10 @@ class SecretBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||
|
||||
class SecretFilterForm(BootstrapMixin, forms.Form):
|
||||
q = forms.CharField(required=False, label='Search')
|
||||
role = FilterChoiceField(queryset=SecretRole.objects.annotate(filter_count=Count('secrets')), to_field_name='slug')
|
||||
role = FilterChoiceField(
|
||||
queryset=SecretRole.objects.annotate(filter_count=Count('secrets')),
|
||||
to_field_name='slug'
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
|
@ -8,8 +8,8 @@ from .models import Tenant, TenantGroup
|
||||
|
||||
|
||||
class TenantFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
q = django_filters.MethodFilter(
|
||||
action='search',
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
group_id = NullableModelMultipleChoiceFilter(
|
||||
@ -26,9 +26,11 @@ class TenantFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
||||
|
||||
class Meta:
|
||||
model = Tenant
|
||||
fields = ['q', 'group_id', 'group', 'name']
|
||||
fields = ['name']
|
||||
|
||||
def search(self, queryset, value):
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(name__icontains=value) |
|
||||
Q(description__icontains=value) |
|
||||
|
@ -56,5 +56,8 @@ class TenantBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
class TenantFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
model = Tenant
|
||||
q = forms.CharField(required=False, label='Search')
|
||||
group = FilterChoiceField(queryset=TenantGroup.objects.annotate(filter_count=Count('tenants')),
|
||||
to_field_name='slug', null_option=(0, 'None'))
|
||||
group = FilterChoiceField(
|
||||
queryset=TenantGroup.objects.annotate(filter_count=Count('tenants')),
|
||||
to_field_name='slug',
|
||||
null_option=(0, 'None')
|
||||
)
|
||||
|
Reference in New Issue
Block a user