Merge pull request #15369 from netbox-community/15237-audit-filtersets

Closes #15237: Add tests for missing filters
This commit is contained in:
Jeremy Stretch 2024-03-12 13:09:14 -04:00 committed by GitHub
commit 2d4295e2ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 918 additions and 239 deletions

View File

@ -67,7 +67,7 @@ class ProviderFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
class Meta: class Meta:
model = Provider model = Provider
fields = ['id', 'name', 'slug', 'description'] fields = ('id', 'name', 'slug', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -95,7 +95,7 @@ class ProviderAccountFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = ProviderAccount model = ProviderAccount
fields = ['id', 'name', 'account', 'description'] fields = ('id', 'name', 'account', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -122,7 +122,7 @@ class ProviderNetworkFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = ProviderNetwork model = ProviderNetwork
fields = ['id', 'name', 'service_id', 'description'] fields = ('id', 'name', 'service_id', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -139,7 +139,7 @@ class CircuitTypeFilterSet(OrganizationalModelFilterSet):
class Meta: class Meta:
model = CircuitType model = CircuitType
fields = ['id', 'name', 'slug', 'color', 'description'] fields = ('id', 'name', 'slug', 'color', 'description')
class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet): class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
@ -158,6 +158,12 @@ class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilte
queryset=ProviderAccount.objects.all(), queryset=ProviderAccount.objects.all(),
label=_('Provider account (ID)'), label=_('Provider account (ID)'),
) )
provider_account = django_filters.ModelMultipleChoiceFilter(
field_name='provider_account__account',
queryset=Provider.objects.all(),
to_field_name='account',
label=_('Provider account (account)'),
)
provider_network_id = django_filters.ModelMultipleChoiceFilter( provider_network_id = django_filters.ModelMultipleChoiceFilter(
field_name='terminations__provider_network', field_name='terminations__provider_network',
queryset=ProviderNetwork.objects.all(), queryset=ProviderNetwork.objects.all(),
@ -214,10 +220,18 @@ class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilte
to_field_name='slug', to_field_name='slug',
label=_('Site (slug)'), label=_('Site (slug)'),
) )
termination_a_id = django_filters.ModelMultipleChoiceFilter(
queryset=CircuitTermination.objects.all(),
label=_('Termination A (ID)'),
)
termination_z_id = django_filters.ModelMultipleChoiceFilter(
queryset=CircuitTermination.objects.all(),
label=_('Termination A (ID)'),
)
class Meta: class Meta:
model = Circuit model = Circuit
fields = ['id', 'cid', 'description', 'install_date', 'termination_date', 'commit_rate'] fields = ('id', 'cid', 'description', 'install_date', 'termination_date', 'commit_rate')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -258,7 +272,10 @@ class CircuitTerminationFilterSet(NetBoxModelFilterSet, CabledObjectFilterSet):
class Meta: class Meta:
model = CircuitTermination model = CircuitTermination
fields = ['id', 'term_side', 'port_speed', 'upstream_speed', 'xconnect_id', 'description', 'cable_end'] fields = (
'id', 'term_side', 'port_speed', 'upstream_speed', 'xconnect_id', 'description', 'mark_connected',
'pp_info', 'cable_end',
)
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():

View File

@ -330,6 +330,7 @@ class CircuitTestCase(TestCase, ChangeLoggedFilterSetTests):
class CircuitTerminationTestCase(TestCase, ChangeLoggedFilterSetTests): class CircuitTerminationTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = CircuitTermination.objects.all() queryset = CircuitTermination.objects.all()
filterset = CircuitTerminationFilterSet filterset = CircuitTerminationFilterSet
ignore_fields = ('cable',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):

View File

@ -28,7 +28,7 @@ class DataSourceFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = DataSource model = DataSource
fields = ('id', 'name', 'enabled', 'description') fields = ('id', 'name', 'enabled', 'description', 'source_url', 'last_synced')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -115,7 +115,7 @@ class JobFilterSet(BaseFilterSet):
class Meta: class Meta:
model = Job model = Job
fields = ('id', 'object_type', 'object_id', 'name', 'interval', 'status', 'user') fields = ('id', 'object_type', 'object_id', 'name', 'interval', 'status', 'user', 'job_id')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -134,9 +134,7 @@ class ConfigRevisionFilterSet(BaseFilterSet):
class Meta: class Meta:
model = ConfigRevision model = ConfigRevision
fields = [ fields = ('id', 'created', 'comment')
'id',
]
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():

View File

@ -10,6 +10,7 @@ from ..models import *
class DataSourceTestCase(TestCase, ChangeLoggedFilterSetTests): class DataSourceTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = DataSource.objects.all() queryset = DataSource.objects.all()
filterset = DataSourceFilterSet filterset = DataSourceFilterSet
ignore_fields = ('ignore_rules', 'parameters')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -70,6 +71,7 @@ class DataSourceTestCase(TestCase, ChangeLoggedFilterSetTests):
class DataFileTestCase(TestCase, ChangeLoggedFilterSetTests): class DataFileTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = DataFile.objects.all() queryset = DataFile.objects.all()
filterset = DataFileFilterSet filterset = DataFileFilterSet
ignore_fields = ('data',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):

View File

@ -18,11 +18,12 @@ from tenancy.models import *
from utilities.choices import ColorChoices from utilities.choices import ColorChoices
from utilities.filters import ( from utilities.filters import (
ContentTypeFilter, MultiValueCharFilter, MultiValueMACAddressFilter, MultiValueNumberFilter, MultiValueWWNFilter, ContentTypeFilter, MultiValueCharFilter, MultiValueMACAddressFilter, MultiValueNumberFilter, MultiValueWWNFilter,
TreeNodeMultipleChoiceFilter, NumericArrayFilter, TreeNodeMultipleChoiceFilter,
) )
from virtualization.models import Cluster from virtualization.models import Cluster
from vpn.models import L2VPN from vpn.models import L2VPN
from wireless.choices import WirelessRoleChoices, WirelessChannelChoices from wireless.choices import WirelessRoleChoices, WirelessChannelChoices
from wireless.models import WirelessLAN, WirelessLink
from .choices import * from .choices import *
from .constants import * from .constants import *
from .models import * from .models import *
@ -105,7 +106,7 @@ class RegionFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
class Meta: class Meta:
model = Region model = Region
fields = ['id', 'name', 'slug', 'description'] fields = ('id', 'name', 'slug', 'description')
class SiteGroupFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet): class SiteGroupFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
@ -135,7 +136,7 @@ class SiteGroupFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
class Meta: class Meta:
model = SiteGroup model = SiteGroup
fields = ['id', 'name', 'slug', 'description'] fields = ('id', 'name', 'slug', 'description')
class SiteFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet): class SiteFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
@ -178,12 +179,11 @@ class SiteFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSe
queryset=ASN.objects.all(), queryset=ASN.objects.all(),
label=_('AS (ID)'), label=_('AS (ID)'),
) )
time_zone = MultiValueCharFilter()
class Meta: class Meta:
model = Site model = Site
fields = ( fields = ('id', 'name', 'slug', 'facility', 'latitude', 'longitude', 'description')
'id', 'name', 'slug', 'facility', 'latitude', 'longitude', 'description'
)
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -270,7 +270,7 @@ class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, OrganizationalM
class Meta: class Meta:
model = Location model = Location
fields = ['id', 'name', 'slug', 'status', 'description'] fields = ('id', 'name', 'slug', 'status', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -285,7 +285,7 @@ class RackRoleFilterSet(OrganizationalModelFilterSet):
class Meta: class Meta:
model = RackRole model = RackRole
fields = ['id', 'name', 'slug', 'color', 'description'] fields = ('id', 'name', 'slug', 'color', 'description')
class RackFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet): class RackFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
@ -364,10 +364,10 @@ class RackFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSe
class Meta: class Meta:
model = Rack model = Rack
fields = [ fields = (
'id', 'name', 'facility_id', 'asset_tag', 'u_height', 'starting_unit', 'desc_units', 'outer_width', 'id', 'name', 'facility_id', 'asset_tag', 'u_height', 'starting_unit', 'desc_units', 'outer_width',
'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit', 'description', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit', 'description',
] )
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -447,10 +447,14 @@ class RackReservationFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
to_field_name='username', to_field_name='username',
label=_('User (name)'), label=_('User (name)'),
) )
unit = NumericArrayFilter(
field_name='units',
lookup_expr='contains'
)
class Meta: class Meta:
model = RackReservation model = RackReservation
fields = ['id', 'created', 'description'] fields = ('id', 'created', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -467,7 +471,7 @@ class ManufacturerFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet)
class Meta: class Meta:
model = Manufacturer model = Manufacturer
fields = ['id', 'name', 'slug', 'description'] fields = ('id', 'name', 'slug', 'description')
class DeviceTypeFilterSet(NetBoxModelFilterSet): class DeviceTypeFilterSet(NetBoxModelFilterSet):
@ -538,10 +542,22 @@ class DeviceTypeFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = DeviceType model = DeviceType
fields = [ fields = (
'id', 'model', 'slug', 'part_number', 'u_height', 'exclude_from_utilization', 'is_full_depth', 'id', 'model', 'slug', 'part_number', 'u_height', 'exclude_from_utilization', 'is_full_depth',
'subdevice_role', 'airflow', 'weight', 'weight_unit', 'description', 'subdevice_role', 'airflow', 'weight', 'weight_unit', 'description',
]
# Counters
'console_port_template_count',
'console_server_port_template_count',
'power_port_template_count',
'power_outlet_template_count',
'interface_template_count',
'front_port_template_count',
'rear_port_template_count',
'device_bay_template_count',
'module_bay_template_count',
'inventory_item_template_count',
)
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -635,7 +651,7 @@ class ModuleTypeFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = ModuleType model = ModuleType
fields = ['id', 'model', 'part_number', 'weight', 'weight_unit', 'description'] fields = ('id', 'model', 'part_number', 'weight', 'weight_unit', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -675,12 +691,15 @@ class DeviceTypeComponentFilterSet(django_filters.FilterSet):
method='search', method='search',
label=_('Search'), label=_('Search'),
) )
devicetype_id = django_filters.ModelMultipleChoiceFilter( device_type_id = django_filters.ModelMultipleChoiceFilter(
queryset=DeviceType.objects.all(), queryset=DeviceType.objects.all(),
field_name='device_type_id', field_name='device_type_id',
label=_('Device type (ID)'), label=_('Device type (ID)'),
) )
# TODO: Remove in v4.1
devicetype_id = device_type_id
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
return queryset return queryset
@ -691,32 +710,35 @@ class DeviceTypeComponentFilterSet(django_filters.FilterSet):
class ModularDeviceTypeComponentFilterSet(DeviceTypeComponentFilterSet): class ModularDeviceTypeComponentFilterSet(DeviceTypeComponentFilterSet):
moduletype_id = django_filters.ModelMultipleChoiceFilter( module_type_id = django_filters.ModelMultipleChoiceFilter(
queryset=ModuleType.objects.all(), queryset=ModuleType.objects.all(),
field_name='module_type_id', field_name='module_type_id',
label=_('Module type (ID)'), label=_('Module type (ID)'),
) )
# TODO: Remove in v4.1
moduletype_id = module_type_id
class ConsolePortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet): class ConsolePortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
class Meta: class Meta:
model = ConsolePortTemplate model = ConsolePortTemplate
fields = ['id', 'name', 'type', 'description'] fields = ('id', 'name', 'label', 'type', 'description')
class ConsoleServerPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet): class ConsoleServerPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
class Meta: class Meta:
model = ConsoleServerPortTemplate model = ConsoleServerPortTemplate
fields = ['id', 'name', 'type', 'description'] fields = ('id', 'name', 'label', 'type', 'description')
class PowerPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet): class PowerPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
class Meta: class Meta:
model = PowerPortTemplate model = PowerPortTemplate
fields = ['id', 'name', 'type', 'maximum_draw', 'allocated_draw', 'description'] fields = ('id', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description')
class PowerOutletTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet): class PowerOutletTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
@ -724,10 +746,14 @@ class PowerOutletTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceType
choices=PowerOutletFeedLegChoices, choices=PowerOutletFeedLegChoices,
null_value=None null_value=None
) )
power_port_id = django_filters.ModelMultipleChoiceFilter(
queryset=PowerPortTemplate.objects.all(),
label=_('Power port (ID)'),
)
class Meta: class Meta:
model = PowerOutletTemplate model = PowerOutletTemplate
fields = ['id', 'name', 'type', 'feed_leg', 'description'] fields = ('id', 'name', 'label', 'type', 'feed_leg', 'description')
class InterfaceTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet): class InterfaceTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
@ -751,7 +777,7 @@ class InterfaceTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeCo
class Meta: class Meta:
model = InterfaceTemplate model = InterfaceTemplate
fields = ['id', 'name', 'type', 'enabled', 'mgmt_only', 'description'] fields = ('id', 'name', 'label', 'type', 'enabled', 'mgmt_only', 'description')
class FrontPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet): class FrontPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
@ -759,10 +785,13 @@ class FrontPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeCo
choices=PortTypeChoices, choices=PortTypeChoices,
null_value=None null_value=None
) )
rear_port_id = django_filters.ModelMultipleChoiceFilter(
queryset=RearPort.objects.all()
)
class Meta: class Meta:
model = FrontPortTemplate model = FrontPortTemplate
fields = ['id', 'name', 'type', 'color', 'description'] fields = ('id', 'name', 'label', 'type', 'color', 'rear_port_position', 'description')
class RearPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet): class RearPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
@ -773,21 +802,21 @@ class RearPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeCom
class Meta: class Meta:
model = RearPortTemplate model = RearPortTemplate
fields = ['id', 'name', 'type', 'color', 'positions', 'description'] fields = ('id', 'name', 'label', 'type', 'color', 'positions', 'description')
class ModuleBayTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet): class ModuleBayTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet):
class Meta: class Meta:
model = ModuleBayTemplate model = ModuleBayTemplate
fields = ['id', 'name', 'description'] fields = ('id', 'name', 'label', 'position', 'description')
class DeviceBayTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet): class DeviceBayTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet):
class Meta: class Meta:
model = DeviceBayTemplate model = DeviceBayTemplate
fields = ['id', 'name', 'description'] fields = ('id', 'name', 'label', 'description')
class InventoryItemTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet): class InventoryItemTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet):
@ -820,7 +849,7 @@ class InventoryItemTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeCompo
class Meta: class Meta:
model = InventoryItemTemplate model = InventoryItemTemplate
fields = ['id', 'name', 'label', 'part_id', 'description'] fields = ('id', 'name', 'label', 'part_id', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -841,7 +870,7 @@ class DeviceRoleFilterSet(OrganizationalModelFilterSet):
class Meta: class Meta:
model = DeviceRole model = DeviceRole
fields = ['id', 'name', 'slug', 'color', 'vm_role', 'description'] fields = ('id', 'name', 'slug', 'color', 'vm_role', 'description')
class PlatformFilterSet(OrganizationalModelFilterSet): class PlatformFilterSet(OrganizationalModelFilterSet):
@ -867,7 +896,7 @@ class PlatformFilterSet(OrganizationalModelFilterSet):
class Meta: class Meta:
model = Platform model = Platform
fields = ['id', 'name', 'slug', 'description'] fields = ('id', 'name', 'slug', 'description')
@extend_schema_field(OpenApiTypes.STR) @extend_schema_field(OpenApiTypes.STR)
def get_for_device_type(self, queryset, name, value): def get_for_device_type(self, queryset, name, value):
@ -979,6 +1008,11 @@ class DeviceFilterSet(
queryset=Rack.objects.all(), queryset=Rack.objects.all(),
label=_('Rack (ID)'), label=_('Rack (ID)'),
) )
parent_bay_id = django_filters.ModelMultipleChoiceFilter(
field_name='parent_bay',
queryset=DeviceBay.objects.all(),
label=_('Parent bay (ID)'),
)
cluster_id = django_filters.ModelMultipleChoiceFilter( cluster_id = django_filters.ModelMultipleChoiceFilter(
queryset=Cluster.objects.all(), queryset=Cluster.objects.all(),
label=_('VM cluster (ID)'), label=_('VM cluster (ID)'),
@ -1068,10 +1102,22 @@ class DeviceFilterSet(
class Meta: class Meta:
model = Device model = Device
fields = [ fields = (
'id', 'asset_tag', 'face', 'position', 'latitude', 'longitude', 'airflow', 'vc_position', 'vc_priority', 'id', 'asset_tag', 'face', 'position', 'latitude', 'longitude', 'airflow', 'vc_position', 'vc_priority',
'description', 'description',
]
# Counters
'console_port_count',
'console_server_port_count',
'power_port_count',
'power_outlet_count',
'interface_count',
'front_port_count',
'rear_port_count',
'device_bay_count',
'module_bay_count',
'inventory_item_count',
)
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -1134,24 +1180,29 @@ class VirtualDeviceContextFilterSet(NetBoxModelFilterSet, TenancyFilterSet, Prim
device_id = django_filters.ModelMultipleChoiceFilter( device_id = django_filters.ModelMultipleChoiceFilter(
field_name='device', field_name='device',
queryset=Device.objects.all(), queryset=Device.objects.all(),
label='VDC (ID)', label=_('VDC (ID)')
) )
device = django_filters.ModelMultipleChoiceFilter( device = django_filters.ModelMultipleChoiceFilter(
field_name='device', field_name='device',
queryset=Device.objects.all(), queryset=Device.objects.all(),
label='Device model', label=_('Device model')
)
interface_id = django_filters.ModelMultipleChoiceFilter(
field_name='interfaces',
queryset=Interface.objects.all(),
label=_('Interface (ID)')
) )
status = django_filters.MultipleChoiceFilter( status = django_filters.MultipleChoiceFilter(
choices=VirtualDeviceContextStatusChoices choices=VirtualDeviceContextStatusChoices
) )
has_primary_ip = django_filters.BooleanFilter( has_primary_ip = django_filters.BooleanFilter(
method='_has_primary_ip', method='_has_primary_ip',
label='Has a primary IP', label=_('Has a primary IP')
) )
class Meta: class Meta:
model = VirtualDeviceContext model = VirtualDeviceContext
fields = ['id', 'device', 'name', 'description'] fields = ('id', 'device', 'name', 'identifier', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -1217,7 +1268,7 @@ class ModuleFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = Module model = Module
fields = ['id', 'status', 'asset_tag', 'description'] fields = ('id', 'status', 'asset_tag', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -1361,6 +1412,10 @@ class ModularDeviceComponentFilterSet(DeviceComponentFilterSet):
class CabledObjectFilterSet(django_filters.FilterSet): class CabledObjectFilterSet(django_filters.FilterSet):
cable_id = django_filters.ModelMultipleChoiceFilter(
queryset=Cable.objects.all(),
label=_('Cable (ID)'),
)
cabled = django_filters.BooleanFilter( cabled = django_filters.BooleanFilter(
field_name='cable', field_name='cable',
lookup_expr='isnull', lookup_expr='isnull',
@ -1402,7 +1457,7 @@ class ConsolePortFilterSet(
class Meta: class Meta:
model = ConsolePort model = ConsolePort
fields = ['id', 'name', 'label', 'description', 'cable_end'] fields = ('id', 'name', 'label', 'speed', 'description', 'mark_connected', 'cable_end')
class ConsoleServerPortFilterSet( class ConsoleServerPortFilterSet(
@ -1418,7 +1473,7 @@ class ConsoleServerPortFilterSet(
class Meta: class Meta:
model = ConsoleServerPort model = ConsoleServerPort
fields = ['id', 'name', 'label', 'description', 'cable_end'] fields = ('id', 'name', 'label', 'speed', 'description', 'mark_connected', 'cable_end')
class PowerPortFilterSet( class PowerPortFilterSet(
@ -1434,7 +1489,9 @@ class PowerPortFilterSet(
class Meta: class Meta:
model = PowerPort model = PowerPort
fields = ['id', 'name', 'label', 'maximum_draw', 'allocated_draw', 'description', 'cable_end'] fields = (
'id', 'name', 'label', 'maximum_draw', 'allocated_draw', 'description', 'mark_connected', 'cable_end',
)
class PowerOutletFilterSet( class PowerOutletFilterSet(
@ -1451,10 +1508,16 @@ class PowerOutletFilterSet(
choices=PowerOutletFeedLegChoices, choices=PowerOutletFeedLegChoices,
null_value=None null_value=None
) )
power_port_id = django_filters.ModelMultipleChoiceFilter(
queryset=PowerPort.objects.all(),
label=_('Power port (ID)'),
)
class Meta: class Meta:
model = PowerOutlet model = PowerOutlet
fields = ['id', 'name', 'label', 'feed_leg', 'description', 'cable_end'] fields = (
'id', 'name', 'label', 'feed_leg', 'description', 'mark_connected', 'cable_end',
)
class CommonInterfaceFilterSet(django_filters.FilterSet): class CommonInterfaceFilterSet(django_filters.FilterSet):
@ -1569,27 +1632,37 @@ class InterfaceFilterSet(
vdc_id = django_filters.ModelMultipleChoiceFilter( vdc_id = django_filters.ModelMultipleChoiceFilter(
field_name='vdcs', field_name='vdcs',
queryset=VirtualDeviceContext.objects.all(), queryset=VirtualDeviceContext.objects.all(),
label='Virtual Device Context', label=_('Virtual Device Context')
) )
vdc_identifier = django_filters.ModelMultipleChoiceFilter( vdc_identifier = django_filters.ModelMultipleChoiceFilter(
field_name='vdcs__identifier', field_name='vdcs__identifier',
queryset=VirtualDeviceContext.objects.all(), queryset=VirtualDeviceContext.objects.all(),
to_field_name='identifier', to_field_name='identifier',
label='Virtual Device Context (Identifier)', label=_('Virtual Device Context (Identifier)')
) )
vdc = django_filters.ModelMultipleChoiceFilter( vdc = django_filters.ModelMultipleChoiceFilter(
field_name='vdcs__name', field_name='vdcs__name',
queryset=VirtualDeviceContext.objects.all(), queryset=VirtualDeviceContext.objects.all(),
to_field_name='name', to_field_name='name',
label='Virtual Device Context', label=_('Virtual Device Context')
)
wireless_lan_id = django_filters.ModelMultipleChoiceFilter(
field_name='wireless_lans',
queryset=WirelessLAN.objects.all(),
label=_('Wireless LAN')
)
wireless_link_id = django_filters.ModelMultipleChoiceFilter(
queryset=WirelessLink.objects.all(),
label=_('Wireless link')
) )
class Meta: class Meta:
model = Interface model = Interface
fields = [ fields = (
'id', 'name', 'label', 'type', 'enabled', 'mtu', 'mgmt_only', 'poe_mode', 'poe_type', 'mode', 'rf_role', 'id', 'name', 'label', 'type', 'enabled', 'mtu', 'mgmt_only', 'poe_mode', 'poe_type', 'mode', 'rf_role',
'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'description', 'cable_end', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'description', 'mark_connected',
] 'cable_id', 'cable_end',
)
def filter_virtual_chassis_member(self, queryset, name, value): def filter_virtual_chassis_member(self, queryset, name, value):
try: try:
@ -1618,10 +1691,15 @@ class FrontPortFilterSet(
choices=PortTypeChoices, choices=PortTypeChoices,
null_value=None null_value=None
) )
rear_port_id = django_filters.ModelMultipleChoiceFilter(
queryset=RearPort.objects.all()
)
class Meta: class Meta:
model = FrontPort model = FrontPort
fields = ['id', 'name', 'label', 'type', 'color', 'description', 'cable_end'] fields = (
'id', 'name', 'label', 'type', 'color', 'rear_port_position', 'description', 'mark_connected', 'cable_end',
)
class RearPortFilterSet( class RearPortFilterSet(
@ -1636,21 +1714,38 @@ class RearPortFilterSet(
class Meta: class Meta:
model = RearPort model = RearPort
fields = ['id', 'name', 'label', 'type', 'color', 'positions', 'description', 'cable_end'] fields = (
'id', 'name', 'label', 'type', 'color', 'positions', 'description', 'mark_connected', 'cable_end',
)
class ModuleBayFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet): class ModuleBayFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet):
installed_module_id = django_filters.ModelMultipleChoiceFilter(
field_name='installed_module',
queryset=ModuleBay.objects.all(),
label=_('Installed module (ID)'),
)
class Meta: class Meta:
model = ModuleBay model = ModuleBay
fields = ['id', 'name', 'label', 'description'] fields = ('id', 'name', 'label', 'position', 'description')
class DeviceBayFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet): class DeviceBayFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet):
installed_device_id = django_filters.ModelMultipleChoiceFilter(
queryset=Device.objects.all(),
label=_('Installed device (ID)'),
)
installed_device = django_filters.ModelMultipleChoiceFilter(
field_name='installed_device__name',
queryset=Device.objects.all(),
to_field_name='name',
label=_('Installed device (name)'),
)
class Meta: class Meta:
model = DeviceBay model = DeviceBay
fields = ['id', 'name', 'label', 'description'] fields = ('id', 'name', 'label', 'description')
class InventoryItemFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet): class InventoryItemFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet):
@ -1686,7 +1781,7 @@ class InventoryItemFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet):
class Meta: class Meta:
model = InventoryItem model = InventoryItem
fields = ['id', 'name', 'label', 'part_id', 'asset_tag', 'discovered'] fields = ('id', 'name', 'label', 'part_id', 'asset_tag', 'description', 'discovered')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -1705,7 +1800,7 @@ class InventoryItemRoleFilterSet(OrganizationalModelFilterSet):
class Meta: class Meta:
model = InventoryItemRole model = InventoryItemRole
fields = ['id', 'name', 'slug', 'color', 'description'] fields = ('id', 'name', 'slug', 'color', 'description')
class VirtualChassisFilterSet(NetBoxModelFilterSet): class VirtualChassisFilterSet(NetBoxModelFilterSet):
@ -1770,7 +1865,7 @@ class VirtualChassisFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = VirtualChassis model = VirtualChassis
fields = ['id', 'domain', 'name', 'description'] fields = ('id', 'domain', 'name', 'description', 'member_count')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -1875,7 +1970,7 @@ class CableFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
class Meta: class Meta:
model = Cable model = Cable
fields = ['id', 'label', 'length', 'length_unit', 'description'] fields = ('id', 'label', 'length', 'length_unit', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -1953,12 +2048,12 @@ class CableFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
return self.filter_by_termination_object(queryset, CircuitTermination, value) return self.filter_by_termination_object(queryset, CircuitTermination, value)
class CableTerminationFilterSet(BaseFilterSet): class CableTerminationFilterSet(ChangeLoggedModelFilterSet):
termination_type = ContentTypeFilter() termination_type = ContentTypeFilter()
class Meta: class Meta:
model = CableTermination model = CableTermination
fields = ['id', 'cable', 'cable_end', 'termination_type', 'termination_id'] fields = ('id', 'cable', 'cable_end', 'termination_type', 'termination_id')
class PowerPanelFilterSet(NetBoxModelFilterSet, ContactModelFilterSet): class PowerPanelFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
@ -2007,7 +2102,7 @@ class PowerPanelFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
class Meta: class Meta:
model = PowerPanel model = PowerPanel
fields = ['id', 'name', 'description'] fields = ('id', 'name', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -2073,10 +2168,10 @@ class PowerFeedFilterSet(NetBoxModelFilterSet, CabledObjectFilterSet, PathEndpoi
class Meta: class Meta:
model = PowerFeed model = PowerFeed
fields = [ fields = (
'id', 'name', 'status', 'type', 'supply', 'phase', 'voltage', 'amperage', 'max_utilization', 'cable_end', 'id', 'name', 'status', 'type', 'supply', 'phase', 'voltage', 'amperage', 'max_utilization',
'description', 'available_power', 'mark_connected', 'cable_end', 'description',
] )
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -2135,18 +2230,18 @@ class ConsoleConnectionFilterSet(ConnectionFilterSet):
class Meta: class Meta:
model = ConsolePort model = ConsolePort
fields = ['name'] fields = ('name',)
class PowerConnectionFilterSet(ConnectionFilterSet): class PowerConnectionFilterSet(ConnectionFilterSet):
class Meta: class Meta:
model = PowerPort model = PowerPort
fields = ['name'] fields = ('name',)
class InterfaceConnectionFilterSet(ConnectionFilterSet): class InterfaceConnectionFilterSet(ConnectionFilterSet):
class Meta: class Meta:
model = Interface model = Interface
fields = [] fields = tuple()

View File

@ -754,7 +754,7 @@ class DeviceFilterForm(
) )
has_oob_ip = forms.NullBooleanField( has_oob_ip = forms.NullBooleanField(
required=False, required=False,
label='Has an OOB IP', label=_('Has an OOB IP'),
widget=forms.Select( widget=forms.Select(
choices=BOOLEAN_WITH_BLANK_CHOICES choices=BOOLEAN_WITH_BLANK_CHOICES
) )

View File

@ -196,6 +196,7 @@ class SiteGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
class SiteTestCase(TestCase, ChangeLoggedFilterSetTests): class SiteTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Site.objects.all() queryset = Site.objects.all()
filterset = SiteFilterSet filterset = SiteFilterSet
ignore_fields = ('physical_address', 'shipping_address')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -467,6 +468,7 @@ class RackRoleTestCase(TestCase, ChangeLoggedFilterSetTests):
class RackTestCase(TestCase, ChangeLoggedFilterSetTests): class RackTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Rack.objects.all() queryset = Rack.objects.all()
filterset = RackFilterSet filterset = RackFilterSet
ignore_fields = ('units',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -726,6 +728,7 @@ class RackTestCase(TestCase, ChangeLoggedFilterSetTests):
class RackReservationTestCase(TestCase, ChangeLoggedFilterSetTests): class RackReservationTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = RackReservation.objects.all() queryset = RackReservation.objects.all()
filterset = RackReservationFilterSet filterset = RackReservationFilterSet
ignore_fields = ('units',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -889,6 +892,7 @@ class ManufacturerTestCase(TestCase, ChangeLoggedFilterSetTests):
class DeviceTypeTestCase(TestCase, ChangeLoggedFilterSetTests): class DeviceTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = DeviceType.objects.all() queryset = DeviceType.objects.all()
filterset = DeviceTypeFilterSet filterset = DeviceTypeFilterSet
ignore_fields = ('front_image', 'rear_image')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1880,6 +1884,7 @@ class PlatformTestCase(TestCase, ChangeLoggedFilterSetTests):
class DeviceTestCase(TestCase, ChangeLoggedFilterSetTests): class DeviceTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Device.objects.all() queryset = Device.objects.all()
filterset = DeviceFilterSet filterset = DeviceFilterSet
ignore_fields = ('local_context_data', 'oob_ip', 'primary_ip4', 'primary_ip6', 'vc_master_for')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -2332,6 +2337,7 @@ class DeviceTestCase(TestCase, ChangeLoggedFilterSetTests):
class ModuleTestCase(TestCase, ChangeLoggedFilterSetTests): class ModuleTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Module.objects.all() queryset = Module.objects.all()
filterset = ModuleFilterSet filterset = ModuleFilterSet
ignore_fields = ('local_context_data',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -3229,6 +3235,7 @@ class PowerOutletTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedF
class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFilterSetTests): class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFilterSetTests):
queryset = Interface.objects.all() queryset = Interface.objects.all()
filterset = InterfaceFilterSet filterset = InterfaceFilterSet
ignore_fields = ('tagged_vlans', 'untagged_vlan', 'vdcs')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -5332,6 +5339,7 @@ class PowerFeedTestCase(TestCase, ChangeLoggedFilterSetTests):
class VirtualDeviceContextTestCase(TestCase, ChangeLoggedFilterSetTests): class VirtualDeviceContextTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = VirtualDeviceContext.objects.all() queryset = VirtualDeviceContext.objects.all()
filterset = VirtualDeviceContextFilterSet filterset = VirtualDeviceContextFilterSet
ignore_fields = ('primary_ip4', 'primary_ip6')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -5401,15 +5409,22 @@ class VirtualDeviceContextTestCase(TestCase, ChangeLoggedFilterSetTests):
VirtualDeviceContext.objects.bulk_create(vdcs) VirtualDeviceContext.objects.bulk_create(vdcs)
interfaces = ( interfaces = (
Interface(device=devices[0], name='Interface 1', type='virtual'), Interface(device=devices[0], name='Interface 1', type=InterfaceTypeChoices.TYPE_VIRTUAL),
Interface(device=devices[0], name='Interface 2', type='virtual'), Interface(device=devices[0], name='Interface 2', type=InterfaceTypeChoices.TYPE_VIRTUAL),
Interface(device=devices[1], name='Interface 3', type=InterfaceTypeChoices.TYPE_VIRTUAL),
Interface(device=devices[1], name='Interface 4', type=InterfaceTypeChoices.TYPE_VIRTUAL),
Interface(device=devices[2], name='Interface 5', type=InterfaceTypeChoices.TYPE_VIRTUAL),
Interface(device=devices[2], name='Interface 6', type=InterfaceTypeChoices.TYPE_VIRTUAL),
) )
Interface.objects.bulk_create(interfaces) Interface.objects.bulk_create(interfaces)
interfaces[0].vdcs.set([vdcs[0]]) interfaces[0].vdcs.set([vdcs[0]])
interfaces[1].vdcs.set([vdcs[1]]) interfaces[1].vdcs.set([vdcs[1]])
interfaces[2].vdcs.set([vdcs[2]])
interfaces[3].vdcs.set([vdcs[3]])
interfaces[4].vdcs.set([vdcs[4]])
interfaces[5].vdcs.set([vdcs[5]])
addresses = ( ip_addresses = (
IPAddress(assigned_object=interfaces[0], address='10.1.1.1/24'), IPAddress(assigned_object=interfaces[0], address='10.1.1.1/24'),
IPAddress(assigned_object=interfaces[1], address='10.1.1.2/24'), IPAddress(assigned_object=interfaces[1], address='10.1.1.2/24'),
IPAddress(assigned_object=None, address='10.1.1.3/24'), IPAddress(assigned_object=None, address='10.1.1.3/24'),
@ -5417,13 +5432,12 @@ class VirtualDeviceContextTestCase(TestCase, ChangeLoggedFilterSetTests):
IPAddress(assigned_object=interfaces[1], address='2001:db8::2/64'), IPAddress(assigned_object=interfaces[1], address='2001:db8::2/64'),
IPAddress(assigned_object=None, address='2001:db8::3/64'), IPAddress(assigned_object=None, address='2001:db8::3/64'),
) )
IPAddress.objects.bulk_create(addresses) IPAddress.objects.bulk_create(ip_addresses)
vdcs[0].primary_ip4 = ip_addresses[0]
vdcs[0].primary_ip4 = addresses[0] vdcs[0].primary_ip6 = ip_addresses[3]
vdcs[0].primary_ip6 = addresses[3]
vdcs[0].save() vdcs[0].save()
vdcs[1].primary_ip4 = addresses[1] vdcs[1].primary_ip4 = ip_addresses[1]
vdcs[1].primary_ip6 = addresses[4] vdcs[1].primary_ip6 = ip_addresses[4]
vdcs[1].save() vdcs[1].save()
def test_q(self): def test_q(self):
@ -5431,8 +5445,11 @@ class VirtualDeviceContextTestCase(TestCase, ChangeLoggedFilterSetTests):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
def test_device(self): def test_device(self):
params = {'device': ['Device 1', 'Device 2']} devices = Device.objects.filter(name__in=['Device 1', 'Device 2'])
params = {'device': [devices[0].name, devices[1].name]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6)
params = {'device_id': [devices[0].pk, devices[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
def test_status(self): def test_status(self):
params = {'status': ['active']} params = {'status': ['active']}
@ -5442,10 +5459,10 @@ class VirtualDeviceContextTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'description': ['foobar1', 'foobar2']} params = {'description': ['foobar1', 'foobar2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_device_id(self): def test_interface(self):
devices = Device.objects.filter(name__in=['Device 1', 'Device 2']) interfaces = Interface.objects.filter(name__in=['Interface 1', 'Interface 3'])
params = {'device_id': [devices[0].pk, devices[1].pk]} params = {'interface_id': [interfaces[0].pk, interfaces[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_has_primary_ip(self): def test_has_primary_ip(self):
params = {'has_primary_ip': True} params = {'has_primary_ip': True}

View File

@ -40,12 +40,14 @@ class ScriptFilterSet(BaseFilterSet):
method='search', method='search',
label=_('Search'), label=_('Search'),
) )
module_id = django_filters.ModelMultipleChoiceFilter(
queryset=ScriptModule.objects.all(),
label=_('Script module (ID)'),
)
class Meta: class Meta:
model = Script model = Script
fields = [ fields = ('id', 'name', 'is_executable')
'id', 'name',
]
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -69,10 +71,10 @@ class WebhookFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = Webhook model = Webhook
fields = [ fields = (
'id', 'name', 'payload_url', 'http_method', 'http_content_type', 'secret', 'ssl_verification', 'id', 'name', 'payload_url', 'http_method', 'http_content_type', 'secret', 'ssl_verification',
'ca_file_path', 'description', 'ca_file_path', 'description',
] )
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -89,8 +91,9 @@ class EventRuleFilterSet(NetBoxModelFilterSet):
method='search', method='search',
label=_('Search'), label=_('Search'),
) )
object_type_id = MultiValueNumberFilter( object_type_id = django_filters.ModelMultipleChoiceFilter(
field_name='object_types__id' queryset=ObjectType.objects.all(),
field_name='object_types'
) )
object_type = ContentTypeFilter( object_type = ContentTypeFilter(
field_name='object_types' field_name='object_types'
@ -103,10 +106,10 @@ class EventRuleFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = EventRule model = EventRule
fields = [ fields = (
'id', 'name', 'type_create', 'type_update', 'type_delete', 'type_job_start', 'type_job_end', 'enabled', 'id', 'name', 'type_create', 'type_update', 'type_delete', 'type_job_start', 'type_job_end', 'enabled',
'action_type', 'description', 'action_type', 'description',
] )
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -118,7 +121,7 @@ class EventRuleFilterSet(NetBoxModelFilterSet):
) )
class CustomFieldFilterSet(BaseFilterSet): class CustomFieldFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter( q = django_filters.CharFilter(
method='search', method='search',
label=_('Search'), label=_('Search'),
@ -126,14 +129,16 @@ class CustomFieldFilterSet(BaseFilterSet):
type = django_filters.MultipleChoiceFilter( type = django_filters.MultipleChoiceFilter(
choices=CustomFieldTypeChoices choices=CustomFieldTypeChoices
) )
object_type_id = MultiValueNumberFilter( object_type_id = django_filters.ModelMultipleChoiceFilter(
field_name='object_types__id' queryset=ObjectType.objects.all(),
field_name='object_types'
) )
object_type = ContentTypeFilter( object_type = ContentTypeFilter(
field_name='object_types' field_name='object_types'
) )
related_object_type_id = MultiValueNumberFilter( related_object_type_id = django_filters.ModelMultipleChoiceFilter(
field_name='related_object_type__id' queryset=ObjectType.objects.all(),
field_name='related_object_type'
) )
related_object_type = ContentTypeFilter() related_object_type = ContentTypeFilter()
choice_set_id = django_filters.ModelMultipleChoiceFilter( choice_set_id = django_filters.ModelMultipleChoiceFilter(
@ -147,10 +152,11 @@ class CustomFieldFilterSet(BaseFilterSet):
class Meta: class Meta:
model = CustomField model = CustomField
fields = [ fields = (
'id', 'name', 'group_name', 'required', 'search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'id', 'name', 'label', 'group_name', 'required', 'search_weight', 'filter_logic', 'ui_visible',
'weight', 'is_cloneable', 'description', 'ui_editable', 'weight', 'is_cloneable', 'description', 'validation_minimum', 'validation_maximum',
] 'validation_regex',
)
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -163,7 +169,7 @@ class CustomFieldFilterSet(BaseFilterSet):
) )
class CustomFieldChoiceSetFilterSet(BaseFilterSet): class CustomFieldChoiceSetFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter( q = django_filters.CharFilter(
method='search', method='search',
label=_('Search'), label=_('Search'),
@ -174,9 +180,9 @@ class CustomFieldChoiceSetFilterSet(BaseFilterSet):
class Meta: class Meta:
model = CustomFieldChoiceSet model = CustomFieldChoiceSet
fields = [ fields = (
'id', 'name', 'description', 'base_choices', 'order_alphabetically', 'id', 'name', 'description', 'base_choices', 'order_alphabetically',
] )
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -191,13 +197,14 @@ class CustomFieldChoiceSetFilterSet(BaseFilterSet):
return queryset.filter(extra_choices__overlap=value) return queryset.filter(extra_choices__overlap=value)
class CustomLinkFilterSet(BaseFilterSet): class CustomLinkFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter( q = django_filters.CharFilter(
method='search', method='search',
label=_('Search'), label=_('Search'),
) )
object_type_id = MultiValueNumberFilter( object_type_id = django_filters.ModelMultipleChoiceFilter(
field_name='object_types__id' queryset=ObjectType.objects.all(),
field_name='object_types'
) )
object_type = ContentTypeFilter( object_type = ContentTypeFilter(
field_name='object_types' field_name='object_types'
@ -205,9 +212,9 @@ class CustomLinkFilterSet(BaseFilterSet):
class Meta: class Meta:
model = CustomLink model = CustomLink
fields = [ fields = (
'id', 'name', 'enabled', 'link_text', 'link_url', 'weight', 'group_name', 'new_window', 'id', 'name', 'enabled', 'link_text', 'link_url', 'weight', 'group_name', 'new_window', 'button_class',
] )
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -220,13 +227,14 @@ class CustomLinkFilterSet(BaseFilterSet):
) )
class ExportTemplateFilterSet(BaseFilterSet): class ExportTemplateFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter( q = django_filters.CharFilter(
method='search', method='search',
label=_('Search'), label=_('Search'),
) )
object_type_id = MultiValueNumberFilter( object_type_id = django_filters.ModelMultipleChoiceFilter(
field_name='object_types__id' queryset=ObjectType.objects.all(),
field_name='object_types'
) )
object_type = ContentTypeFilter( object_type = ContentTypeFilter(
field_name='object_types' field_name='object_types'
@ -242,7 +250,10 @@ class ExportTemplateFilterSet(BaseFilterSet):
class Meta: class Meta:
model = ExportTemplate model = ExportTemplate
fields = ['id', 'name', 'description', 'data_synced'] fields = (
'id', 'name', 'description', 'mime_type', 'file_extension', 'as_attachment', 'auto_sync_enabled',
'data_synced',
)
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -253,13 +264,14 @@ class ExportTemplateFilterSet(BaseFilterSet):
) )
class SavedFilterFilterSet(BaseFilterSet): class SavedFilterFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter( q = django_filters.CharFilter(
method='search', method='search',
label=_('Search'), label=_('Search'),
) )
object_type_id = MultiValueNumberFilter( object_type_id = django_filters.ModelMultipleChoiceFilter(
field_name='object_types__id' queryset=ObjectType.objects.all(),
field_name='object_types'
) )
object_type = ContentTypeFilter( object_type = ContentTypeFilter(
field_name='object_types' field_name='object_types'
@ -280,7 +292,7 @@ class SavedFilterFilterSet(BaseFilterSet):
class Meta: class Meta:
model = SavedFilter model = SavedFilter
fields = ['id', 'name', 'slug', 'description', 'enabled', 'shared', 'weight'] fields = ('id', 'name', 'slug', 'description', 'enabled', 'shared', 'weight')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -321,20 +333,19 @@ class BookmarkFilterSet(BaseFilterSet):
class Meta: class Meta:
model = Bookmark model = Bookmark
fields = ['id', 'object_id'] fields = ('id', 'object_id')
class ImageAttachmentFilterSet(BaseFilterSet): class ImageAttachmentFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter( q = django_filters.CharFilter(
method='search', method='search',
label=_('Search'), label=_('Search'),
) )
created = django_filters.DateTimeFilter()
object_type = ContentTypeFilter() object_type = ContentTypeFilter()
class Meta: class Meta:
model = ImageAttachment model = ImageAttachment
fields = ['id', 'object_type_id', 'object_id', 'name'] fields = ('id', 'object_type_id', 'object_id', 'name', 'image_width', 'image_height')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -364,7 +375,7 @@ class JournalEntryFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = JournalEntry model = JournalEntry
fields = ['id', 'assigned_object_type_id', 'assigned_object_id', 'created', 'kind'] fields = ('id', 'assigned_object_type_id', 'assigned_object_id', 'created', 'kind')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -389,7 +400,7 @@ class TagFilterSet(ChangeLoggedModelFilterSet):
class Meta: class Meta:
model = Tag model = Tag
fields = ['id', 'name', 'slug', 'color', 'description', 'object_types'] fields = ('id', 'name', 'slug', 'color', 'description', 'object_types')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -486,12 +497,12 @@ class ConfigContextFilterSet(ChangeLoggedModelFilterSet):
queryset=DeviceType.objects.all(), queryset=DeviceType.objects.all(),
label=_('Device type'), label=_('Device type'),
) )
role_id = django_filters.ModelMultipleChoiceFilter( device_role_id = django_filters.ModelMultipleChoiceFilter(
field_name='roles', field_name='roles',
queryset=DeviceRole.objects.all(), queryset=DeviceRole.objects.all(),
label=_('Role'), label=_('Role'),
) )
role = django_filters.ModelMultipleChoiceFilter( device_role = django_filters.ModelMultipleChoiceFilter(
field_name='roles__slug', field_name='roles__slug',
queryset=DeviceRole.objects.all(), queryset=DeviceRole.objects.all(),
to_field_name='slug', to_field_name='slug',
@ -577,9 +588,13 @@ class ConfigContextFilterSet(ChangeLoggedModelFilterSet):
label=_('Data file (ID)'), label=_('Data file (ID)'),
) )
# TODO: Remove in v4.1
role = device_role
role_id = device_role_id
class Meta: class Meta:
model = ConfigContext model = ConfigContext
fields = ['id', 'name', 'is_active', 'data_synced', 'description'] fields = ('id', 'name', 'is_active', 'description', 'weight', 'auto_sync_enabled', 'data_synced')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -591,7 +606,7 @@ class ConfigContextFilterSet(ChangeLoggedModelFilterSet):
) )
class ConfigTemplateFilterSet(BaseFilterSet): class ConfigTemplateFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter( q = django_filters.CharFilter(
method='search', method='search',
label=_('Search'), label=_('Search'),
@ -608,7 +623,7 @@ class ConfigTemplateFilterSet(BaseFilterSet):
class Meta: class Meta:
model = ConfigTemplate model = ConfigTemplate
fields = ['id', 'name', 'description', 'data_synced'] fields = ('id', 'name', 'description', 'auto_sync_enabled', 'data_synced')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -656,10 +671,10 @@ class ObjectChangeFilterSet(BaseFilterSet):
class Meta: class Meta:
model = ObjectChange model = ObjectChange
fields = [ fields = (
'id', 'user', 'user_name', 'request_id', 'action', 'changed_object_type_id', 'changed_object_id', 'id', 'user', 'user_name', 'request_id', 'action', 'changed_object_type_id', 'changed_object_id',
'object_repr', 'related_object_type', 'related_object_id', 'object_repr',
] )
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -682,7 +697,7 @@ class ObjectTypeFilterSet(django_filters.FilterSet):
class Meta: class Meta:
model = ObjectType model = ObjectType
fields = ['id', 'app_label', 'model'] fields = ('id', 'app_label', 'model')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():

View File

@ -23,9 +23,10 @@ from virtualization.models import Cluster, ClusterGroup, ClusterType
User = get_user_model() User = get_user_model()
class CustomFieldTestCase(TestCase, BaseFilterSetTests): class CustomFieldTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = CustomField.objects.all() queryset = CustomField.objects.all()
filterset = CustomFieldFilterSet filterset = CustomFieldFilterSet
ignore_fields = ('default',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -155,9 +156,10 @@ class CustomFieldTestCase(TestCase, BaseFilterSetTests):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class CustomFieldChoiceSetTestCase(TestCase, BaseFilterSetTests): class CustomFieldChoiceSetTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = CustomFieldChoiceSet.objects.all() queryset = CustomFieldChoiceSet.objects.all()
filterset = CustomFieldChoiceSetFilterSet filterset = CustomFieldChoiceSetFilterSet
ignore_fields = ('extra_choices',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -188,6 +190,7 @@ class CustomFieldChoiceSetTestCase(TestCase, BaseFilterSetTests):
class WebhookTestCase(TestCase, BaseFilterSetTests): class WebhookTestCase(TestCase, BaseFilterSetTests):
queryset = Webhook.objects.all() queryset = Webhook.objects.all()
filterset = WebhookFilterSet filterset = WebhookFilterSet
ignore_fields = ('additional_headers', 'body_template')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -252,6 +255,7 @@ class WebhookTestCase(TestCase, BaseFilterSetTests):
class EventRuleTestCase(TestCase, BaseFilterSetTests): class EventRuleTestCase(TestCase, BaseFilterSetTests):
queryset = EventRule.objects.all() queryset = EventRule.objects.all()
filterset = EventRuleFilterSet filterset = EventRuleFilterSet
ignore_fields = ('action_data', 'conditions')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -405,7 +409,7 @@ class EventRuleTestCase(TestCase, BaseFilterSetTests):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
class CustomLinkTestCase(TestCase, BaseFilterSetTests): class CustomLinkTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = CustomLink.objects.all() queryset = CustomLink.objects.all()
filterset = CustomLinkFilterSet filterset = CustomLinkFilterSet
@ -474,9 +478,10 @@ class CustomLinkTestCase(TestCase, BaseFilterSetTests):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
class SavedFilterTestCase(TestCase, BaseFilterSetTests): class SavedFilterTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = SavedFilter.objects.all() queryset = SavedFilter.objects.all()
filterset = SavedFilterFilterSet filterset = SavedFilterFilterSet
ignore_fields = ('parameters',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -647,9 +652,10 @@ class BookmarkTestCase(TestCase, BaseFilterSetTests):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
class ExportTemplateTestCase(TestCase, BaseFilterSetTests): class ExportTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ExportTemplate.objects.all() queryset = ExportTemplate.objects.all()
filterset = ExportTemplateFilterSet filterset = ExportTemplateFilterSet
ignore_fields = ('template_code', 'data_path')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -683,9 +689,10 @@ class ExportTemplateTestCase(TestCase, BaseFilterSetTests):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class ImageAttachmentTestCase(TestCase, BaseFilterSetTests): class ImageAttachmentTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ImageAttachment.objects.all() queryset = ImageAttachment.objects.all()
filterset = ImageAttachmentFilterSet filterset = ImageAttachmentFilterSet
ignore_fields = ('image',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -760,12 +767,6 @@ class ImageAttachmentTestCase(TestCase, BaseFilterSetTests):
} }
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
def test_created(self):
pk_list = self.queryset.values_list('pk', flat=True)[:2]
self.queryset.filter(pk__in=pk_list).update(created=datetime(2021, 1, 1, 0, 0, 0, tzinfo=timezone.utc))
params = {'created': '2021-01-01T00:00:00'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class JournalEntryTestCase(TestCase, ChangeLoggedFilterSetTests): class JournalEntryTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = JournalEntry.objects.all() queryset = JournalEntry.objects.all()
@ -873,6 +874,7 @@ class JournalEntryTestCase(TestCase, ChangeLoggedFilterSetTests):
class ConfigContextTestCase(TestCase, ChangeLoggedFilterSetTests): class ConfigContextTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ConfigContext.objects.all() queryset = ConfigContext.objects.all()
filterset = ConfigContextFilterSet filterset = ConfigContextFilterSet
ignore_fields = ('data', 'data_path')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1041,11 +1043,11 @@ class ConfigContextTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'device_type_id': [device_types[0].pk, device_types[1].pk]} params = {'device_type_id': [device_types[0].pk, device_types[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_role(self): def test_device_role(self):
device_roles = DeviceRole.objects.all()[:2] device_roles = DeviceRole.objects.all()[:2]
params = {'role_id': [device_roles[0].pk, device_roles[1].pk]} params = {'device_role_id': [device_roles[0].pk, device_roles[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
params = {'role': [device_roles[0].slug, device_roles[1].slug]} params = {'device_role': [device_roles[0].slug, device_roles[1].slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_platform(self): def test_platform(self):
@ -1096,9 +1098,10 @@ class ConfigContextTestCase(TestCase, ChangeLoggedFilterSetTests):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class ConfigTemplateTestCase(TestCase, BaseFilterSetTests): class ConfigTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ConfigTemplate.objects.all() queryset = ConfigTemplate.objects.all()
filterset = ConfigTemplateFilterSet filterset = ConfigTemplateFilterSet
ignore_fields = ('template_code', 'environment_params', 'data_path')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1125,6 +1128,93 @@ class ConfigTemplateTestCase(TestCase, BaseFilterSetTests):
class TagTestCase(TestCase, ChangeLoggedFilterSetTests): class TagTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Tag.objects.all() queryset = Tag.objects.all()
filterset = TagFilterSet filterset = TagFilterSet
ignore_fields = (
'object_types',
# Reverse relationships (to tagged models) we can ignore
'aggregate',
'asn',
'asnrange',
'cable',
'circuit',
'circuittermination',
'circuittype',
'cluster',
'clustergroup',
'clustertype',
'configtemplate',
'consoleport',
'consoleserverport',
'contact',
'contactassignment',
'contactgroup',
'contactrole',
'datasource',
'device',
'devicebay',
'devicerole',
'devicetype',
'dummymodel', # From dummy_plugin
'eventrule',
'fhrpgroup',
'frontport',
'ikepolicy',
'ikeproposal',
'interface',
'inventoryitem',
'inventoryitemrole',
'ipaddress',
'iprange',
'ipsecpolicy',
'ipsecprofile',
'ipsecproposal',
'journalentry',
'l2vpn',
'l2vpntermination',
'location',
'manufacturer',
'module',
'modulebay',
'moduletype',
'platform',
'powerfeed',
'poweroutlet',
'powerpanel',
'powerport',
'prefix',
'provider',
'provideraccount',
'providernetwork',
'rack',
'rackreservation',
'rackrole',
'rearport',
'region',
'rir',
'role',
'routetarget',
'service',
'servicetemplate',
'site',
'sitegroup',
'tenant',
'tenantgroup',
'tunnel',
'tunnelgroup',
'tunneltermination',
'virtualchassis',
'virtualdevicecontext',
'virtualdisk',
'virtualmachine',
'vlan',
'vlangroup',
'vminterface',
'vrf',
'webhook',
'wirelesslan',
'wirelesslangroup',
'wirelesslink',
)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1193,6 +1283,7 @@ class TagTestCase(TestCase, ChangeLoggedFilterSetTests):
class ObjectChangeTestCase(TestCase, BaseFilterSetTests): class ObjectChangeTestCase(TestCase, BaseFilterSetTests):
queryset = ObjectChange.objects.all() queryset = ObjectChange.objects.all()
filterset = ObjectChangeFilterSet filterset = ObjectChangeFilterSet
ignore_fields = ('prechange_data', 'postchange_data')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):

View File

@ -8,6 +8,7 @@ from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field from drf_spectacular.utils import extend_schema_field
from netaddr.core import AddrFormatError from netaddr.core import AddrFormatError
from circuits.models import Provider
from dcim.models import Device, Interface, Region, Site, SiteGroup from dcim.models import Device, Interface, Region, Site, SiteGroup
from netbox.filtersets import ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet from netbox.filtersets import ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet
from tenancy.filtersets import TenancyFilterSet from tenancy.filtersets import TenancyFilterSet
@ -75,7 +76,7 @@ class VRFFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
class Meta: class Meta:
model = VRF model = VRF
fields = ['id', 'name', 'rd', 'enforce_unique', 'description'] fields = ('id', 'name', 'rd', 'enforce_unique', 'description')
class RouteTargetFilterSet(NetBoxModelFilterSet, TenancyFilterSet): class RouteTargetFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
@ -101,6 +102,28 @@ class RouteTargetFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
to_field_name='rd', to_field_name='rd',
label=_('Export VRF (RD)'), label=_('Export VRF (RD)'),
) )
importing_l2vpn_id = django_filters.ModelMultipleChoiceFilter(
field_name='importing_l2vpns',
queryset=L2VPN.objects.all(),
label=_('Importing L2VPN'),
)
importing_l2vpn = django_filters.ModelMultipleChoiceFilter(
field_name='importing_l2vpns__identifier',
queryset=L2VPN.objects.all(),
to_field_name='identifier',
label=_('Importing L2VPN (identifier)'),
)
exporting_l2vpn_id = django_filters.ModelMultipleChoiceFilter(
field_name='exporting_l2vpns',
queryset=L2VPN.objects.all(),
label=_('Exporting L2VPN'),
)
exporting_l2vpn = django_filters.ModelMultipleChoiceFilter(
field_name='exporting_l2vpns__identifier',
queryset=L2VPN.objects.all(),
to_field_name='identifier',
label=_('Exporting L2VPN (identifier)'),
)
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -112,14 +135,14 @@ class RouteTargetFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
class Meta: class Meta:
model = RouteTarget model = RouteTarget
fields = ['id', 'name', 'description'] fields = ('id', 'name', 'description')
class RIRFilterSet(OrganizationalModelFilterSet): class RIRFilterSet(OrganizationalModelFilterSet):
class Meta: class Meta:
model = RIR model = RIR
fields = ['id', 'name', 'slug', 'is_private', 'description'] fields = ('id', 'name', 'slug', 'is_private', 'description')
class AggregateFilterSet(NetBoxModelFilterSet, TenancyFilterSet): class AggregateFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
@ -144,7 +167,7 @@ class AggregateFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
class Meta: class Meta:
model = Aggregate model = Aggregate
fields = ['id', 'date_added', 'description'] fields = ('id', 'date_added', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -183,7 +206,7 @@ class ASNRangeFilterSet(OrganizationalModelFilterSet, TenancyFilterSet):
class Meta: class Meta:
model = ASNRange model = ASNRange
fields = ['id', 'name', 'start', 'end', 'description'] fields = ('id', 'name', 'slug', 'start', 'end', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -214,10 +237,21 @@ class ASNFilterSet(OrganizationalModelFilterSet, TenancyFilterSet):
to_field_name='slug', to_field_name='slug',
label=_('Site (slug)'), label=_('Site (slug)'),
) )
provider_id = django_filters.ModelMultipleChoiceFilter(
field_name='providers',
queryset=Provider.objects.all(),
label=_('Provider (ID)'),
)
provider = django_filters.ModelMultipleChoiceFilter(
field_name='providers__slug',
queryset=Provider.objects.all(),
to_field_name='slug',
label=_('Provider (slug)'),
)
class Meta: class Meta:
model = ASN model = ASN
fields = ['id', 'asn', 'description'] fields = ('id', 'asn', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -234,7 +268,7 @@ class RoleFilterSet(OrganizationalModelFilterSet):
class Meta: class Meta:
model = Role model = Role
fields = ['id', 'name', 'slug', 'description'] fields = ('id', 'name', 'slug', 'description', 'weight')
class PrefixFilterSet(NetBoxModelFilterSet, TenancyFilterSet): class PrefixFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
@ -359,7 +393,7 @@ class PrefixFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
class Meta: class Meta:
model = Prefix model = Prefix
fields = ['id', 'is_pool', 'mark_utilized', 'description'] fields = ('id', 'is_pool', 'mark_utilized', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -475,7 +509,7 @@ class IPRangeFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
class Meta: class Meta:
model = IPRange model = IPRange
fields = ['id', 'mark_utilized', 'description'] fields = ('id', 'mark_utilized', 'size', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -628,10 +662,20 @@ class IPAddressFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
role = django_filters.MultipleChoiceFilter( role = django_filters.MultipleChoiceFilter(
choices=IPAddressRoleChoices choices=IPAddressRoleChoices
) )
service_id = django_filters.ModelMultipleChoiceFilter(
field_name='services',
queryset=Service.objects.all(),
label=_('Service (ID)'),
)
nat_inside_id = django_filters.ModelMultipleChoiceFilter(
field_name='nat_inside',
queryset=IPAddress.objects.all(),
label=_('NAT inside IP address (ID)'),
)
class Meta: class Meta:
model = IPAddress model = IPAddress
fields = ['id', 'dns_name', 'description'] fields = ('id', 'dns_name', 'description', 'assigned_object_type', 'assigned_object_id')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -758,7 +802,7 @@ class FHRPGroupFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = FHRPGroup model = FHRPGroup
fields = ['id', 'group_id', 'name', 'auth_key', 'description'] fields = ('id', 'group_id', 'name', 'auth_key', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -819,7 +863,7 @@ class FHRPGroupAssignmentFilterSet(ChangeLoggedModelFilterSet):
class Meta: class Meta:
model = FHRPGroupAssignment model = FHRPGroupAssignment
fields = ['id', 'group_id', 'interface_type', 'interface_id', 'priority'] fields = ('id', 'group_id', 'interface_type', 'interface_id', 'priority')
def filter_device(self, queryset, name, value): def filter_device(self, queryset, name, value):
devices = Device.objects.filter(**{f'{name}__in': value}) devices = Device.objects.filter(**{f'{name}__in': value})
@ -849,7 +893,7 @@ class VLANGroupFilterSet(OrganizationalModelFilterSet):
region = django_filters.NumberFilter( region = django_filters.NumberFilter(
method='filter_scope' method='filter_scope'
) )
sitegroup = django_filters.NumberFilter( site_group = django_filters.NumberFilter(
method='filter_scope' method='filter_scope'
) )
site = django_filters.NumberFilter( site = django_filters.NumberFilter(
@ -861,16 +905,20 @@ class VLANGroupFilterSet(OrganizationalModelFilterSet):
rack = django_filters.NumberFilter( rack = django_filters.NumberFilter(
method='filter_scope' method='filter_scope'
) )
clustergroup = django_filters.NumberFilter( cluster_group = django_filters.NumberFilter(
method='filter_scope' method='filter_scope'
) )
cluster = django_filters.NumberFilter( cluster = django_filters.NumberFilter(
method='filter_scope' method='filter_scope'
) )
# TODO: Remove in v4.1
sitegroup = site_group
clustergroup = cluster_group
class Meta: class Meta:
model = VLANGroup model = VLANGroup
fields = ['id', 'name', 'slug', 'min_vid', 'max_vid', 'description', 'scope_id'] fields = ('id', 'name', 'slug', 'min_vid', 'max_vid', 'description', 'scope_id')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -882,8 +930,9 @@ class VLANGroupFilterSet(OrganizationalModelFilterSet):
return queryset.filter(qs_filter) return queryset.filter(qs_filter)
def filter_scope(self, queryset, name, value): def filter_scope(self, queryset, name, value):
model_name = name.replace('_', '')
return queryset.filter( return queryset.filter(
scope_type=ContentType.objects.get(model=name), scope_type=ContentType.objects.get(model=model_name),
scope_id=value scope_id=value
) )
@ -975,7 +1024,7 @@ class VLANFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
class Meta: class Meta:
model = VLAN model = VLAN
fields = ['id', 'vid', 'name', 'description'] fields = ('id', 'vid', 'name', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -1008,7 +1057,7 @@ class ServiceTemplateFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = ServiceTemplate model = ServiceTemplate
fields = ['id', 'name', 'protocol', 'description'] fields = ('id', 'name', 'protocol', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -1041,26 +1090,29 @@ class ServiceFilterSet(NetBoxModelFilterSet):
to_field_name='name', to_field_name='name',
label=_('Virtual machine (name)'), label=_('Virtual machine (name)'),
) )
ipaddress_id = django_filters.ModelMultipleChoiceFilter( ip_address_id = django_filters.ModelMultipleChoiceFilter(
field_name='ipaddresses', field_name='ipaddresses',
queryset=IPAddress.objects.all(), queryset=IPAddress.objects.all(),
label=_('IP address (ID)'), label=_('IP address (ID)'),
) )
ipaddress = django_filters.ModelMultipleChoiceFilter( ip_address = django_filters.ModelMultipleChoiceFilter(
field_name='ipaddresses__address', field_name='ipaddresses__address',
queryset=IPAddress.objects.all(), queryset=IPAddress.objects.all(),
to_field_name='address', to_field_name='address',
label=_('IP address'), label=_('IP address'),
) )
port = NumericArrayFilter( port = NumericArrayFilter(
field_name='ports', field_name='ports',
lookup_expr='contains' lookup_expr='contains'
) )
# TODO: Remove in v4.1
ipaddress = ip_address
ipaddress_id = ip_address_id
class Meta: class Meta:
model = Service model = Service
fields = ['id', 'name', 'protocol', 'description'] fields = ('id', 'name', 'protocol', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():

View File

@ -304,7 +304,7 @@ class IPAddressFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
'placeholder': 'Prefix', 'placeholder': 'Prefix',
} }
), ),
label='Parent Prefix' label=_('Parent Prefix')
) )
family = forms.ChoiceField( family = forms.ChoiceField(
required=False, required=False,

View File

@ -2,6 +2,7 @@ from django.contrib.contenttypes.models import ContentType
from django.test import TestCase from django.test import TestCase
from netaddr import IPNetwork from netaddr import IPNetwork
from circuits.models import Provider
from dcim.choices import InterfaceTypeChoices from dcim.choices import InterfaceTypeChoices
from dcim.models import Device, DeviceRole, DeviceType, Interface, Location, Manufacturer, Rack, Region, Site, SiteGroup from dcim.models import Device, DeviceRole, DeviceType, Interface, Location, Manufacturer, Rack, Region, Site, SiteGroup
from ipam.choices import * from ipam.choices import *
@ -10,6 +11,8 @@ from ipam.models import *
from tenancy.models import Tenant, TenantGroup from tenancy.models import Tenant, TenantGroup
from utilities.testing import ChangeLoggedFilterSetTests, create_test_device, create_test_virtualmachine from utilities.testing import ChangeLoggedFilterSetTests, create_test_device, create_test_virtualmachine
from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
from vpn.choices import L2VPNTypeChoices
from vpn.models import L2VPN
class ASNRangeTestCase(TestCase, ChangeLoggedFilterSetTests): class ASNRangeTestCase(TestCase, ChangeLoggedFilterSetTests):
@ -110,13 +113,6 @@ class ASNTestCase(TestCase, ChangeLoggedFilterSetTests):
] ]
RIR.objects.bulk_create(rirs) RIR.objects.bulk_create(rirs)
sites = [
Site(name='Site 1', slug='site-1'),
Site(name='Site 2', slug='site-2'),
Site(name='Site 3', slug='site-3')
]
Site.objects.bulk_create(sites)
tenants = [ tenants = [
Tenant(name='Tenant 1', slug='tenant-1'), Tenant(name='Tenant 1', slug='tenant-1'),
Tenant(name='Tenant 2', slug='tenant-2'), Tenant(name='Tenant 2', slug='tenant-2'),
@ -136,6 +132,12 @@ class ASNTestCase(TestCase, ChangeLoggedFilterSetTests):
) )
ASN.objects.bulk_create(asns) ASN.objects.bulk_create(asns)
sites = [
Site(name='Site 1', slug='site-1'),
Site(name='Site 2', slug='site-2'),
Site(name='Site 3', slug='site-3')
]
Site.objects.bulk_create(sites)
asns[0].sites.set([sites[0]]) asns[0].sites.set([sites[0]])
asns[1].sites.set([sites[1]]) asns[1].sites.set([sites[1]])
asns[2].sites.set([sites[2]]) asns[2].sites.set([sites[2]])
@ -143,6 +145,16 @@ class ASNTestCase(TestCase, ChangeLoggedFilterSetTests):
asns[4].sites.set([sites[1]]) asns[4].sites.set([sites[1]])
asns[5].sites.set([sites[2]]) asns[5].sites.set([sites[2]])
providers = (
Provider(name='Provider 1', slug='provider-1'),
Provider(name='Provider 2', slug='provider-2'),
Provider(name='Provider 3', slug='provider-3'),
)
Provider.objects.bulk_create(providers)
providers[0].asns.add(asns[0])
providers[1].asns.add(asns[1])
providers[2].asns.add(asns[2])
def test_q(self): def test_q(self):
params = {'q': 'foobar1'} params = {'q': 'foobar1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
@ -176,11 +188,24 @@ class ASNTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'description': ['foobar1', 'foobar2']} params = {'description': ['foobar1', 'foobar2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_provider(self):
providers = Provider.objects.all()[:2]
params = {'provider_id': [providers[0].pk, providers[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class VRFTestCase(TestCase, ChangeLoggedFilterSetTests): class VRFTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = VRF.objects.all() queryset = VRF.objects.all()
filterset = VRFFilterSet filterset = VRFFilterSet
def get_m2m_filter_name(self, field):
# Override filter names for import & export RouteTargets
if field.name == 'import_targets':
return 'import_target'
if field.name == 'export_targets':
return 'export_target'
return ChangeLoggedFilterSetTests.get_m2m_filter_name(field)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -277,6 +302,18 @@ class RouteTargetTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = RouteTarget.objects.all() queryset = RouteTarget.objects.all()
filterset = RouteTargetFilterSet filterset = RouteTargetFilterSet
def get_m2m_filter_name(self, field):
# Override filter names for import & export VRFs and L2VPNs
if field.name == 'importing_vrfs':
return 'importing_vrf'
if field.name == 'exporting_vrfs':
return 'exporting_vrf'
if field.name == 'importing_l2vpns':
return 'importing_l2vpn'
if field.name == 'exporting_l2vpns':
return 'exporting_l2vpn'
return ChangeLoggedFilterSetTests.get_m2m_filter_name(field)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -322,6 +359,17 @@ class RouteTargetTestCase(TestCase, ChangeLoggedFilterSetTests):
vrfs[1].import_targets.add(route_targets[4], route_targets[5]) vrfs[1].import_targets.add(route_targets[4], route_targets[5])
vrfs[1].export_targets.add(route_targets[6], route_targets[7]) vrfs[1].export_targets.add(route_targets[6], route_targets[7])
l2vpns = (
L2VPN(name='L2VPN 1', slug='l2vpn-1', type=L2VPNTypeChoices.TYPE_VXLAN, identifier=100),
L2VPN(name='L2VPN 2', slug='l2vpn-2', type=L2VPNTypeChoices.TYPE_VXLAN, identifier=200),
L2VPN(name='L2VPN 3', slug='l2vpn-3', type=L2VPNTypeChoices.TYPE_VXLAN, identifier=300),
)
L2VPN.objects.bulk_create(l2vpns)
l2vpns[0].import_targets.add(route_targets[0], route_targets[1])
l2vpns[0].export_targets.add(route_targets[2], route_targets[3])
l2vpns[1].import_targets.add(route_targets[4], route_targets[5])
l2vpns[1].export_targets.add(route_targets[6], route_targets[7])
def test_q(self): def test_q(self):
params = {'q': 'foobar1'} params = {'q': 'foobar1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
@ -344,6 +392,20 @@ class RouteTargetTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'exporting_vrf': [vrfs[0].rd, vrfs[1].rd]} params = {'exporting_vrf': [vrfs[0].rd, vrfs[1].rd]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
def test_importing_l2vpn(self):
l2vpns = L2VPN.objects.all()[:2]
params = {'importing_l2vpn_id': [l2vpns[0].pk, l2vpns[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
params = {'importing_l2vpn': [l2vpns[0].identifier, l2vpns[1].identifier]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
def test_exporting_l2vpn(self):
l2vpns = L2VPN.objects.all()[:2]
params = {'exporting_l2vpn_id': [l2vpns[0].pk, l2vpns[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
params = {'exporting_l2vpn': [l2vpns[0].identifier, l2vpns[1].identifier]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
def test_tenant(self): def test_tenant(self):
tenants = Tenant.objects.all()[:2] tenants = Tenant.objects.all()[:2]
params = {'tenant_id': [tenants[0].pk, tenants[1].pk]} params = {'tenant_id': [tenants[0].pk, tenants[1].pk]}
@ -922,6 +984,7 @@ class IPRangeTestCase(TestCase, ChangeLoggedFilterSetTests):
class IPAddressTestCase(TestCase, ChangeLoggedFilterSetTests): class IPAddressTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = IPAddress.objects.all() queryset = IPAddress.objects.all()
filterset = IPAddressFilterSet filterset = IPAddressFilterSet
ignore_fields = ('fhrpgroup',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1092,6 +1155,16 @@ class IPAddressTestCase(TestCase, ChangeLoggedFilterSetTests):
) )
IPAddress.objects.bulk_create(ipaddresses) IPAddress.objects.bulk_create(ipaddresses)
services = (
Service(name='Service 1', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[1]),
Service(name='Service 2', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[1]),
Service(name='Service 3', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[1]),
)
Service.objects.bulk_create(services)
services[0].ipaddresses.add(ipaddresses[0])
services[1].ipaddresses.add(ipaddresses[1])
services[2].ipaddresses.add(ipaddresses[2])
def test_q(self): def test_q(self):
params = {'q': 'foobar1'} params = {'q': 'foobar1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
@ -1231,6 +1304,11 @@ class IPAddressTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'tenant_group': [tenant_groups[0].slug, tenant_groups[1].slug]} params = {'tenant_group': [tenant_groups[0].slug, tenant_groups[1].slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
def test_service(self):
services = Service.objects.all()[:2]
params = {'service_id': [services[0].pk, services[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class FHRPGroupTestCase(TestCase, ChangeLoggedFilterSetTests): class FHRPGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = FHRPGroup.objects.all() queryset = FHRPGroup.objects.all()
@ -1475,6 +1553,7 @@ class VLANGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
class VLANTestCase(TestCase, ChangeLoggedFilterSetTests): class VLANTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = VLAN.objects.all() queryset = VLAN.objects.all()
filterset = VLANFilterSet filterset = VLANFilterSet
ignore_fields = ('interfaces_as_tagged', 'vminterfaces_as_tagged')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1733,6 +1812,7 @@ class VLANTestCase(TestCase, ChangeLoggedFilterSetTests):
class ServiceTemplateTestCase(TestCase, ChangeLoggedFilterSetTests): class ServiceTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ServiceTemplate.objects.all() queryset = ServiceTemplate.objects.all()
filterset = ServiceTemplateFilterSet filterset = ServiceTemplateFilterSet
ignore_fields = ('ports',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1797,6 +1877,7 @@ class ServiceTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
class ServiceTestCase(TestCase, ChangeLoggedFilterSetTests): class ServiceTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Service.objects.all() queryset = Service.objects.all()
filterset = ServiceFilterSet filterset = ServiceFilterSet
ignore_fields = ('ports',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1883,9 +1964,9 @@ class ServiceTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'virtual_machine': [vms[0].name, vms[1].name]} params = {'virtual_machine': [vms[0].name, vms[1].name]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_ipaddress(self): def test_ip_address(self):
ips = IPAddress.objects.all()[:2] ips = IPAddress.objects.all()[:2]
params = {'ipaddress_id': [ips[0].pk, ips[1].pk]} params = {'ip_address_id': [ips[0].pk, ips[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
params = {'ipaddress': [str(ips[0].address), str(ips[1].address)]} params = {'ip_address': [str(ips[0].address), str(ips[1].address)]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)

View File

@ -50,14 +50,14 @@ class ContactGroupFilterSet(OrganizationalModelFilterSet):
class Meta: class Meta:
model = ContactGroup model = ContactGroup
fields = ['id', 'name', 'slug', 'description'] fields = ('id', 'name', 'slug', 'description')
class ContactRoleFilterSet(OrganizationalModelFilterSet): class ContactRoleFilterSet(OrganizationalModelFilterSet):
class Meta: class Meta:
model = ContactRole model = ContactRole
fields = ['id', 'name', 'slug', 'description'] fields = ('id', 'name', 'slug', 'description')
class ContactFilterSet(NetBoxModelFilterSet): class ContactFilterSet(NetBoxModelFilterSet):
@ -77,7 +77,7 @@ class ContactFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = Contact model = Contact
fields = ['id', 'name', 'title', 'phone', 'email', 'address', 'link', 'description'] fields = ('id', 'name', 'title', 'phone', 'email', 'address', 'link', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -131,7 +131,7 @@ class ContactAssignmentFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = ContactAssignment model = ContactAssignment
fields = ['id', 'object_type_id', 'object_id', 'priority', 'tag'] fields = ('id', 'object_type_id', 'object_id', 'priority', 'tag')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -192,7 +192,7 @@ class TenantGroupFilterSet(OrganizationalModelFilterSet):
class Meta: class Meta:
model = TenantGroup model = TenantGroup
fields = ['id', 'name', 'slug', 'description'] fields = ('id', 'name', 'slug', 'description')
class TenantFilterSet(NetBoxModelFilterSet, ContactModelFilterSet): class TenantFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
@ -212,7 +212,7 @@ class TenantFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
class Meta: class Meta:
model = Tenant model = Tenant
fields = ['id', 'name', 'slug', 'description'] fields = ('id', 'name', 'slug', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():

View File

@ -3,8 +3,10 @@ from django.contrib.auth import get_user_model
from django.db.models import Q from django.db.models import Q
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from core.models import ObjectType
from netbox.filtersets import BaseFilterSet from netbox.filtersets import BaseFilterSet
from users.models import Group, ObjectPermission, Token from users.models import Group, ObjectPermission, Token
from utilities.filters import ContentTypeFilter
__all__ = ( __all__ = (
'GroupFilterSet', 'GroupFilterSet',
@ -19,10 +21,20 @@ class GroupFilterSet(BaseFilterSet):
method='search', method='search',
label=_('Search'), label=_('Search'),
) )
user_id = django_filters.ModelMultipleChoiceFilter(
field_name='user',
queryset=get_user_model().objects.all(),
label=_('User (ID)'),
)
permission_id = django_filters.ModelMultipleChoiceFilter(
field_name='object_permissions',
queryset=ObjectPermission.objects.all(),
label=_('Permission (ID)'),
)
class Meta: class Meta:
model = Group model = Group
fields = ['id', 'name'] fields = ('id', 'name', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -46,10 +58,18 @@ class UserFilterSet(BaseFilterSet):
to_field_name='name', to_field_name='name',
label=_('Group (name)'), label=_('Group (name)'),
) )
permission_id = django_filters.ModelMultipleChoiceFilter(
field_name='object_permissions',
queryset=ObjectPermission.objects.all(),
label=_('Permission (ID)'),
)
class Meta: class Meta:
model = get_user_model() model = get_user_model()
fields = ['id', 'username', 'first_name', 'last_name', 'email', 'is_staff', 'is_active', 'is_superuser'] fields = (
'id', 'username', 'first_name', 'last_name', 'email', 'date_joined', 'last_login', 'is_staff', 'is_active',
'is_superuser',
)
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -99,7 +119,7 @@ class TokenFilterSet(BaseFilterSet):
class Meta: class Meta:
model = Token model = Token
fields = ['id', 'key', 'write_enabled', 'description'] fields = ('id', 'key', 'write_enabled', 'description', 'last_used')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -115,6 +135,13 @@ class ObjectPermissionFilterSet(BaseFilterSet):
method='search', method='search',
label=_('Search'), label=_('Search'),
) )
object_type_id = django_filters.ModelMultipleChoiceFilter(
queryset=ObjectType.objects.all(),
field_name='object_types'
)
object_type = ContentTypeFilter(
field_name='object_types'
)
can_view = django_filters.BooleanFilter( can_view = django_filters.BooleanFilter(
method='_check_action' method='_check_action'
) )
@ -152,7 +179,7 @@ class ObjectPermissionFilterSet(BaseFilterSet):
class Meta: class Meta:
model = ObjectPermission model = ObjectPermission
fields = ['id', 'name', 'enabled', 'object_types', 'description'] fields = ('id', 'name', 'enabled', 'object_types', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():

View File

@ -15,6 +15,7 @@ User = get_user_model()
class UserTestCase(TestCase, BaseFilterSetTests): class UserTestCase(TestCase, BaseFilterSetTests):
queryset = User.objects.all() queryset = User.objects.all()
filterset = filtersets.UserFilterSet filterset = filtersets.UserFilterSet
ignore_fields = ('config', 'dashboard', 'password', 'user_permissions')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -66,6 +67,16 @@ class UserTestCase(TestCase, BaseFilterSetTests):
users[1].groups.set([groups[1]]) users[1].groups.set([groups[1]])
users[2].groups.set([groups[2]]) users[2].groups.set([groups[2]])
object_permissions = (
ObjectPermission(name='Permission 1', actions=['add']),
ObjectPermission(name='Permission 2', actions=['change']),
ObjectPermission(name='Permission 3', actions=['delete']),
)
ObjectPermission.objects.bulk_create(object_permissions)
object_permissions[0].users.add(users[0])
object_permissions[1].users.add(users[1])
object_permissions[2].users.add(users[2])
def test_q(self): def test_q(self):
params = {'q': 'user1'} params = {'q': 'user1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
@ -105,10 +116,16 @@ class UserTestCase(TestCase, BaseFilterSetTests):
params = {'group': [groups[0].name, groups[1].name]} params = {'group': [groups[0].name, groups[1].name]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_permission(self):
object_permissions = ObjectPermission.objects.all()[:2]
params = {'permission_id': [object_permissions[0].pk, object_permissions[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class GroupTestCase(TestCase, BaseFilterSetTests): class GroupTestCase(TestCase, BaseFilterSetTests):
queryset = Group.objects.all() queryset = Group.objects.all()
filterset = filtersets.GroupFilterSet filterset = filtersets.GroupFilterSet
ignore_fields = ('permissions',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -120,6 +137,26 @@ class GroupTestCase(TestCase, BaseFilterSetTests):
) )
Group.objects.bulk_create(groups) Group.objects.bulk_create(groups)
users = (
User(username='User 1'),
User(username='User 2'),
User(username='User 3'),
)
User.objects.bulk_create(users)
users[0].groups.set([groups[0]])
users[1].groups.set([groups[1]])
users[2].groups.set([groups[2]])
object_permissions = (
ObjectPermission(name='Permission 1', actions=['add']),
ObjectPermission(name='Permission 2', actions=['change']),
ObjectPermission(name='Permission 3', actions=['delete']),
)
ObjectPermission.objects.bulk_create(object_permissions)
object_permissions[0].groups.add(groups[0])
object_permissions[1].groups.add(groups[1])
object_permissions[2].groups.add(groups[2])
def test_q(self): def test_q(self):
params = {'q': 'group 1'} params = {'q': 'group 1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
@ -128,10 +165,21 @@ class GroupTestCase(TestCase, BaseFilterSetTests):
params = {'name': ['Group 1', 'Group 2']} params = {'name': ['Group 1', 'Group 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_user(self):
users = User.objects.all()[:2]
params = {'user_id': [users[0].pk, users[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_permission(self):
object_permissions = ObjectPermission.objects.all()[:2]
params = {'permission_id': [object_permissions[0].pk, object_permissions[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class ObjectPermissionTestCase(TestCase, BaseFilterSetTests): class ObjectPermissionTestCase(TestCase, BaseFilterSetTests):
queryset = ObjectPermission.objects.all() queryset = ObjectPermission.objects.all()
filterset = filtersets.ObjectPermissionFilterSet filterset = filtersets.ObjectPermissionFilterSet
ignore_fields = ('actions', 'constraints')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -226,6 +274,7 @@ class ObjectPermissionTestCase(TestCase, BaseFilterSetTests):
class TokenTestCase(TestCase, BaseFilterSetTests): class TokenTestCase(TestCase, BaseFilterSetTests):
queryset = Token.objects.all() queryset = Token.objects.all()
filterset = filtersets.TokenFilterSet filterset = filtersets.TokenFilterSet
ignore_fields = ('allowed_ips',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):

View File

@ -1,15 +1,91 @@
from datetime import date, datetime, timezone import django_filters
from datetime import datetime, timezone
from itertools import chain
from mptt.models import MPTTModel
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db.models import ForeignKey, ManyToManyField, ManyToManyRel, ManyToOneRel, OneToOneRel
from django.utils.module_loading import import_string
from taggit.managers import TaggableManager
from extras.filters import TagFilter
from utilities.filters import ContentTypeFilter, TreeNodeMultipleChoiceFilter
from core.models import ObjectType
__all__ = ( __all__ = (
'BaseFilterSetTests', 'BaseFilterSetTests',
'ChangeLoggedFilterSetTests', 'ChangeLoggedFilterSetTests',
) )
EXEMPT_MODEL_FIELDS = (
'comments',
'custom_field_data',
'level', # MPTT
'lft', # MPTT
'rght', # MPTT
'tree_id', # MPTT
)
class BaseFilterSetTests: class BaseFilterSetTests:
queryset = None queryset = None
filterset = None filterset = None
ignore_fields = tuple()
def get_m2m_filter_name(self, field):
"""
Given a ManyToManyField, determine the correct name for its corresponding Filter. Individual test
cases may override this method to prescribe deviations for specific fields.
"""
related_model_name = field.related_model._meta.verbose_name
return related_model_name.lower().replace(' ', '_')
def get_filters_for_model_field(self, field):
"""
Given a model field, return an iterable of (name, class) for each filter that should be defined on
the model's FilterSet class. If the appropriate filter class cannot be determined, it will be None.
"""
# ForeignKey & OneToOneField
if issubclass(field.__class__, ForeignKey) or type(field) is OneToOneRel:
# Relationships to ContentType (used as part of a GFK) do not need a filter
if field.related_model is ContentType:
return [(None, None)]
# ForeignKeys to ObjectType need two filters: 'app.model' & PK
if field.related_model is ObjectType:
return [
(field.name, ContentTypeFilter),
(f'{field.name}_id', django_filters.ModelMultipleChoiceFilter),
]
# ForeignKey to an MPTT-enabled model
if issubclass(field.related_model, MPTTModel) and field.model is not field.related_model:
return [(f'{field.name}_id', TreeNodeMultipleChoiceFilter)]
return [(f'{field.name}_id', django_filters.ModelMultipleChoiceFilter)]
# Many-to-many relationships (forward & backward)
elif type(field) in (ManyToManyField, ManyToManyRel):
filter_name = self.get_m2m_filter_name(field)
# ManyToManyFields to ObjectType need two filters: 'app.model' & PK
if field.related_model is ObjectType:
return [
(filter_name, ContentTypeFilter),
(f'{filter_name}_id', django_filters.ModelMultipleChoiceFilter),
]
return [(f'{filter_name}_id', django_filters.ModelMultipleChoiceFilter)]
# Tag manager
if type(field) is TaggableManager:
return [('tag', TagFilter)]
# Unable to determine the correct filter class
return [(field.name, None)]
def test_id(self): def test_id(self):
""" """
@ -19,6 +95,61 @@ class BaseFilterSetTests:
self.assertGreater(self.queryset.count(), 2) self.assertGreater(self.queryset.count(), 2)
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_missing_filters(self):
"""
Check for any model fields which do not have the required filter(s) defined.
"""
app_label = self.__class__.__module__.split('.')[0]
model = self.queryset.model
model_name = model.__name__
# Import the FilterSet class & sanity check it
filterset = import_string(f'{app_label}.filtersets.{model_name}FilterSet')
self.assertEqual(model, filterset.Meta.model, "FilterSet model does not match!")
filters = filterset.get_filters()
# Check for missing filters
for model_field in model._meta.get_fields():
# Skip private fields
if model_field.name.startswith('_'):
continue
# Skip ignored fields
if model_field.name in chain(self.ignore_fields, EXEMPT_MODEL_FIELDS):
continue
# Skip reverse ForeignKey relationships
if type(model_field) is ManyToOneRel:
continue
# Skip generic relationships
if type(model_field) in (GenericForeignKey, GenericRelation):
continue
for filter_name, filter_class in self.get_filters_for_model_field(model_field):
if filter_name is None:
# Field is exempt
continue
# Check that the filter is defined
self.assertIn(
filter_name,
filters.keys(),
f'No filter defined for {filter_name} ({model_field.name})!'
)
# Check that the filter class is correct
filter = filters[filter_name]
if filter_class is not None:
self.assertIs(
type(filter),
filter_class,
f"Invalid filter class {type(filter)} for {filter_name} (should be {filter_class})!"
)
class ChangeLoggedFilterSetTests(BaseFilterSetTests): class ChangeLoggedFilterSetTests(BaseFilterSetTests):

View File

@ -27,14 +27,14 @@ class ClusterTypeFilterSet(OrganizationalModelFilterSet):
class Meta: class Meta:
model = ClusterType model = ClusterType
fields = ['id', 'name', 'slug', 'description'] fields = ('id', 'name', 'slug', 'description')
class ClusterGroupFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet): class ClusterGroupFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
class Meta: class Meta:
model = ClusterGroup model = ClusterGroup
fields = ['id', 'name', 'slug', 'description'] fields = ('id', 'name', 'slug', 'description')
class ClusterFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet): class ClusterFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
@ -101,7 +101,7 @@ class ClusterFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilte
class Meta: class Meta:
model = Cluster model = Cluster
fields = ['id', 'name', 'description'] fields = ('id', 'name', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -240,7 +240,7 @@ class VirtualMachineFilterSet(
class Meta: class Meta:
model = VirtualMachine model = VirtualMachine
fields = ['id', 'cluster', 'vcpus', 'memory', 'disk', 'description'] fields = ('id', 'cluster', 'vcpus', 'memory', 'disk', 'description', 'interface_count', 'virtual_disk_count')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -299,7 +299,7 @@ class VMInterfaceFilterSet(NetBoxModelFilterSet, CommonInterfaceFilterSet):
class Meta: class Meta:
model = VMInterface model = VMInterface
fields = ['id', 'name', 'enabled', 'mtu', 'description'] fields = ('id', 'name', 'enabled', 'mtu', 'mode', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -325,7 +325,7 @@ class VirtualDiskFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = VirtualDisk model = VirtualDisk
fields = ['id', 'name', 'size', 'description'] fields = ('id', 'name', 'size', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():

View File

@ -522,6 +522,7 @@ class VirtualMachineTestCase(TestCase, ChangeLoggedFilterSetTests):
class VMInterfaceTestCase(TestCase, ChangeLoggedFilterSetTests): class VMInterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = VMInterface.objects.all() queryset = VMInterface.objects.all()
filterset = VMInterfaceFilterSet filterset = VMInterfaceFilterSet
ignore_fields = ('tagged_vlans', 'untagged_vlan',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):

View File

@ -29,7 +29,7 @@ class TunnelGroupFilterSet(OrganizationalModelFilterSet):
class Meta: class Meta:
model = TunnelGroup model = TunnelGroup
fields = ['id', 'name', 'slug', 'description'] fields = ('id', 'name', 'slug', 'description')
class TunnelFilterSet(NetBoxModelFilterSet, TenancyFilterSet): class TunnelFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
@ -62,7 +62,7 @@ class TunnelFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
class Meta: class Meta:
model = Tunnel model = Tunnel
fields = ['id', 'name', 'tunnel_id', 'description'] fields = ('id', 'name', 'tunnel_id', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -120,10 +120,21 @@ class TunnelTerminationFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = TunnelTermination model = TunnelTermination
fields = ['id'] fields = ('id', 'termination_id')
class IKEProposalFilterSet(NetBoxModelFilterSet): class IKEProposalFilterSet(NetBoxModelFilterSet):
ike_policy_id = django_filters.ModelMultipleChoiceFilter(
field_name='ike_policies',
queryset=IKEPolicy.objects.all(),
label=_('IKE policy (ID)'),
)
ike_policy = django_filters.ModelMultipleChoiceFilter(
field_name='ike_policies__name',
queryset=IKEPolicy.objects.all(),
to_field_name='name',
label=_('IKE policy (name)'),
)
authentication_method = django_filters.MultipleChoiceFilter( authentication_method = django_filters.MultipleChoiceFilter(
choices=AuthenticationMethodChoices choices=AuthenticationMethodChoices
) )
@ -139,7 +150,7 @@ class IKEProposalFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = IKEProposal model = IKEProposal
fields = ['id', 'name', 'sa_lifetime', 'description'] fields = ('id', 'name', 'sa_lifetime', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -158,16 +169,23 @@ class IKEPolicyFilterSet(NetBoxModelFilterSet):
mode = django_filters.MultipleChoiceFilter( mode = django_filters.MultipleChoiceFilter(
choices=IKEModeChoices choices=IKEModeChoices
) )
proposal_id = MultiValueNumberFilter( ike_proposal_id = django_filters.ModelMultipleChoiceFilter(
field_name='proposals__id' field_name='proposals',
queryset=IKEProposal.objects.all()
) )
proposal = MultiValueCharFilter( ike_proposal = django_filters.ModelMultipleChoiceFilter(
field_name='proposals__name' field_name='proposals__name',
queryset=IKEProposal.objects.all(),
to_field_name='name'
) )
# TODO: Remove in v4.1
proposal = ike_proposal
proposal_id = ike_proposal_id
class Meta: class Meta:
model = IKEPolicy model = IKEPolicy
fields = ['id', 'name', 'preshared_key', 'description'] fields = ('id', 'name', 'preshared_key', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -180,6 +198,17 @@ class IKEPolicyFilterSet(NetBoxModelFilterSet):
class IPSecProposalFilterSet(NetBoxModelFilterSet): class IPSecProposalFilterSet(NetBoxModelFilterSet):
ipsec_policy_id = django_filters.ModelMultipleChoiceFilter(
field_name='ipsec_policies',
queryset=IPSecPolicy.objects.all(),
label=_('IPSec policy (ID)'),
)
ipsec_policy = django_filters.ModelMultipleChoiceFilter(
field_name='ipsec_policies__name',
queryset=IPSecPolicy.objects.all(),
to_field_name='name',
label=_('IPSec policy (name)'),
)
encryption_algorithm = django_filters.MultipleChoiceFilter( encryption_algorithm = django_filters.MultipleChoiceFilter(
choices=EncryptionAlgorithmChoices choices=EncryptionAlgorithmChoices
) )
@ -189,7 +218,7 @@ class IPSecProposalFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = IPSecProposal model = IPSecProposal
fields = ['id', 'name', 'sa_lifetime_seconds', 'sa_lifetime_data', 'description'] fields = ('id', 'name', 'sa_lifetime_seconds', 'sa_lifetime_data', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -205,16 +234,23 @@ class IPSecPolicyFilterSet(NetBoxModelFilterSet):
pfs_group = django_filters.MultipleChoiceFilter( pfs_group = django_filters.MultipleChoiceFilter(
choices=DHGroupChoices choices=DHGroupChoices
) )
proposal_id = MultiValueNumberFilter( ipsec_proposal_id = django_filters.ModelMultipleChoiceFilter(
field_name='proposals__id' field_name='proposals',
queryset=IPSecProposal.objects.all()
) )
proposal = MultiValueCharFilter( ipsec_proposal = django_filters.ModelMultipleChoiceFilter(
field_name='proposals__name' field_name='proposals__name',
queryset=IPSecProposal.objects.all(),
to_field_name='name'
) )
# TODO: Remove in v4.1
proposal = ipsec_proposal
proposal_id = ipsec_proposal_id
class Meta: class Meta:
model = IPSecPolicy model = IPSecPolicy
fields = ['id', 'name', 'description'] fields = ('id', 'name', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -253,7 +289,7 @@ class IPSecProfileFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = IPSecProfile model = IPSecProfile
fields = ['id', 'name', 'description'] fields = ('id', 'name', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -295,7 +331,7 @@ class L2VPNFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
class Meta: class Meta:
model = L2VPN model = L2VPN
fields = ['id', 'identifier', 'name', 'slug', 'type', 'description'] fields = ('id', 'identifier', 'name', 'slug', 'type', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -402,7 +438,7 @@ class L2VPNTerminationFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = L2VPNTermination model = L2VPNTermination
fields = ('id', 'assigned_object_type_id') fields = ('id', 'assigned_object_id')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():

View File

@ -1,4 +1,3 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase from django.test import TestCase
from dcim.choices import InterfaceTypeChoices from dcim.choices import InterfaceTypeChoices
@ -331,6 +330,16 @@ class IKEProposalTestCase(TestCase, ChangeLoggedFilterSetTests):
) )
IKEProposal.objects.bulk_create(ike_proposals) IKEProposal.objects.bulk_create(ike_proposals)
ike_policies = (
IKEPolicy(name='IKE Policy 1'),
IKEPolicy(name='IKE Policy 2'),
IKEPolicy(name='IKE Policy 3'),
)
IKEPolicy.objects.bulk_create(ike_policies)
ike_policies[0].proposals.add(ike_proposals[0])
ike_policies[1].proposals.add(ike_proposals[1])
ike_policies[2].proposals.add(ike_proposals[2])
def test_q(self): def test_q(self):
params = {'q': 'foobar1'} params = {'q': 'foobar1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
@ -343,6 +352,13 @@ class IKEProposalTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'description': ['foobar1', 'foobar2']} params = {'description': ['foobar1', 'foobar2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_ike_policy(self):
ike_policies = IKEPolicy.objects.all()[:2]
params = {'ike_policy_id': [ike_policies[0].pk, ike_policies[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
params = {'ike_policy': [ike_policies[0].name, ike_policies[1].name]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_authentication_method(self): def test_authentication_method(self):
params = {'authentication_method': [ params = {'authentication_method': [
AuthenticationMethodChoices.PRESHARED_KEYS, AuthenticationMethodChoices.CERTIFICATES AuthenticationMethodChoices.PRESHARED_KEYS, AuthenticationMethodChoices.CERTIFICATES
@ -446,11 +462,11 @@ class IKEPolicyTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'mode': [IKEModeChoices.MAIN]} params = {'mode': [IKEModeChoices.MAIN]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_proposal(self): def test_ike_proposal(self):
proposals = IKEProposal.objects.all()[:2] proposals = IKEProposal.objects.all()[:2]
params = {'proposal_id': [proposals[0].pk, proposals[1].pk]} params = {'ike_proposal_id': [proposals[0].pk, proposals[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
params = {'proposal': [proposals[0].name, proposals[1].name]} params = {'ike_proposal': [proposals[0].name, proposals[1].name]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -488,6 +504,16 @@ class IPSecProposalTestCase(TestCase, ChangeLoggedFilterSetTests):
) )
IPSecProposal.objects.bulk_create(ipsec_proposals) IPSecProposal.objects.bulk_create(ipsec_proposals)
ipsec_policies = (
IPSecPolicy(name='IPSec Policy 1'),
IPSecPolicy(name='IPSec Policy 2'),
IPSecPolicy(name='IPSec Policy 3'),
)
IPSecPolicy.objects.bulk_create(ipsec_policies)
ipsec_policies[0].proposals.add(ipsec_proposals[0])
ipsec_policies[1].proposals.add(ipsec_proposals[1])
ipsec_policies[2].proposals.add(ipsec_proposals[2])
def test_q(self): def test_q(self):
params = {'q': 'foobar1'} params = {'q': 'foobar1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
@ -500,6 +526,13 @@ class IPSecProposalTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'description': ['foobar1', 'foobar2']} params = {'description': ['foobar1', 'foobar2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_ipsec_policy(self):
ipsec_policies = IPSecPolicy.objects.all()[:2]
params = {'ipsec_policy_id': [ipsec_policies[0].pk, ipsec_policies[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
params = {'ipsec_policy': [ipsec_policies[0].name, ipsec_policies[1].name]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_encryption_algorithm(self): def test_encryption_algorithm(self):
params = {'encryption_algorithm': [ params = {'encryption_algorithm': [
EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC, EncryptionAlgorithmChoices.ENCRYPTION_AES192_CBC EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC, EncryptionAlgorithmChoices.ENCRYPTION_AES192_CBC
@ -584,11 +617,11 @@ class IPSecPolicyTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'pfs_group': [DHGroupChoices.GROUP_1, DHGroupChoices.GROUP_2]} params = {'pfs_group': [DHGroupChoices.GROUP_1, DHGroupChoices.GROUP_2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_proposal(self): def test_ipsec_proposal(self):
proposals = IPSecProposal.objects.all()[:2] proposals = IPSecProposal.objects.all()[:2]
params = {'proposal_id': [proposals[0].pk, proposals[1].pk]} params = {'ipsec_proposal_id': [proposals[0].pk, proposals[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
params = {'proposal': [proposals[0].name, proposals[1].name]} params = {'ipsec_proposal': [proposals[0].name, proposals[1].name]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -710,6 +743,14 @@ class L2VPNTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = L2VPN.objects.all() queryset = L2VPN.objects.all()
filterset = L2VPNFilterSet filterset = L2VPNFilterSet
def get_m2m_filter_name(self, field):
# Override filter names for import & export RouteTargets
if field.name == 'import_targets':
return 'import_target'
if field.name == 'export_targets':
return 'export_target'
return ChangeLoggedFilterSetTests.get_m2m_filter_name(field)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -848,8 +889,8 @@ class L2VPNTerminationTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'l2vpn': [l2vpns[0].slug, l2vpns[1].slug]} params = {'l2vpn': [l2vpns[0].slug, l2vpns[1].slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6)
def test_content_type(self): def test_termination_type(self):
params = {'assigned_object_type_id': ContentType.objects.get(model='vlan').pk} params = {'assigned_object_type': 'ipam.vlan'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
def test_interface(self): def test_interface(self):

View File

@ -2,6 +2,7 @@ import django_filters
from django.db.models import Q from django.db.models import Q
from dcim.choices import LinkStatusChoices from dcim.choices import LinkStatusChoices
from dcim.models import Interface
from ipam.models import VLAN from ipam.models import VLAN
from netbox.filtersets import OrganizationalModelFilterSet, NetBoxModelFilterSet from netbox.filtersets import OrganizationalModelFilterSet, NetBoxModelFilterSet
from tenancy.filtersets import TenancyFilterSet from tenancy.filtersets import TenancyFilterSet
@ -39,7 +40,7 @@ class WirelessLANGroupFilterSet(OrganizationalModelFilterSet):
class Meta: class Meta:
model = WirelessLANGroup model = WirelessLANGroup
fields = ['id', 'name', 'slug', 'description'] fields = ('id', 'name', 'slug', 'description')
class WirelessLANFilterSet(NetBoxModelFilterSet, TenancyFilterSet): class WirelessLANFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
@ -60,6 +61,10 @@ class WirelessLANFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
vlan_id = django_filters.ModelMultipleChoiceFilter( vlan_id = django_filters.ModelMultipleChoiceFilter(
queryset=VLAN.objects.all() queryset=VLAN.objects.all()
) )
interface_id = django_filters.ModelMultipleChoiceFilter(
queryset=Interface.objects.all(),
field_name='interfaces'
)
auth_type = django_filters.MultipleChoiceFilter( auth_type = django_filters.MultipleChoiceFilter(
choices=WirelessAuthTypeChoices choices=WirelessAuthTypeChoices
) )
@ -69,7 +74,7 @@ class WirelessLANFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
class Meta: class Meta:
model = WirelessLAN model = WirelessLAN
fields = ['id', 'ssid', 'auth_psk', 'description'] fields = ('id', 'ssid', 'auth_psk', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():
@ -82,8 +87,12 @@ class WirelessLANFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
class WirelessLinkFilterSet(NetBoxModelFilterSet, TenancyFilterSet): class WirelessLinkFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
interface_a_id = MultiValueNumberFilter() interface_a_id = django_filters.ModelMultipleChoiceFilter(
interface_b_id = MultiValueNumberFilter() queryset=Interface.objects.all()
)
interface_b_id = django_filters.ModelMultipleChoiceFilter(
queryset=Interface.objects.all()
)
status = django_filters.MultipleChoiceFilter( status = django_filters.MultipleChoiceFilter(
choices=LinkStatusChoices choices=LinkStatusChoices
) )
@ -96,7 +105,7 @@ class WirelessLinkFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
class Meta: class Meta:
model = WirelessLink model = WirelessLink
fields = ['id', 'ssid', 'auth_psk', 'description'] fields = ('id', 'ssid', 'auth_psk', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():

View File

@ -153,6 +153,17 @@ class WirelessLANTestCase(TestCase, ChangeLoggedFilterSetTests):
) )
WirelessLAN.objects.bulk_create(wireless_lans) WirelessLAN.objects.bulk_create(wireless_lans)
device = create_test_device('Device 1')
interfaces = (
Interface(device=device, name='Interface 1', type=InterfaceTypeChoices.TYPE_80211N),
Interface(device=device, name='Interface 2', type=InterfaceTypeChoices.TYPE_80211N),
Interface(device=device, name='Interface 3', type=InterfaceTypeChoices.TYPE_80211N),
)
Interface.objects.bulk_create(interfaces)
interfaces[0].wireless_lans.add(wireless_lans[0])
interfaces[1].wireless_lans.add(wireless_lans[1])
interfaces[2].wireless_lans.add(wireless_lans[2])
def test_q(self): def test_q(self):
params = {'q': 'foobar1'} params = {'q': 'foobar1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
@ -200,6 +211,11 @@ class WirelessLANTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'tenant': [tenants[0].slug, tenants[1].slug]} params = {'tenant': [tenants[0].slug, tenants[1].slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_interface(self):
interfaces = Interface.objects.all()[:2]
params = {'interface_id': [interfaces[0].pk, interfaces[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class WirelessLinkTestCase(TestCase, ChangeLoggedFilterSetTests): class WirelessLinkTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = WirelessLink.objects.all() queryset = WirelessLink.objects.all()