Merge branch 'feature' into 9856-strawberry-2

This commit is contained in:
Arthur 2024-03-13 11:20:33 -07:00
commit 2b7c1d1845
22 changed files with 918 additions and 239 deletions

View File

@ -67,7 +67,7 @@ class ProviderFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
class Meta:
model = Provider
fields = ['id', 'name', 'slug', 'description']
fields = ('id', 'name', 'slug', 'description')
def search(self, queryset, name, value):
if not value.strip():
@ -95,7 +95,7 @@ class ProviderAccountFilterSet(NetBoxModelFilterSet):
class Meta:
model = ProviderAccount
fields = ['id', 'name', 'account', 'description']
fields = ('id', 'name', 'account', 'description')
def search(self, queryset, name, value):
if not value.strip():
@ -122,7 +122,7 @@ class ProviderNetworkFilterSet(NetBoxModelFilterSet):
class Meta:
model = ProviderNetwork
fields = ['id', 'name', 'service_id', 'description']
fields = ('id', 'name', 'service_id', 'description')
def search(self, queryset, name, value):
if not value.strip():
@ -139,7 +139,7 @@ class CircuitTypeFilterSet(OrganizationalModelFilterSet):
class Meta:
model = CircuitType
fields = ['id', 'name', 'slug', 'color', 'description']
fields = ('id', 'name', 'slug', 'color', 'description')
class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
@ -158,6 +158,12 @@ class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilte
queryset=ProviderAccount.objects.all(),
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(
field_name='terminations__provider_network',
queryset=ProviderNetwork.objects.all(),
@ -214,10 +220,18 @@ class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilte
to_field_name='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:
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):
if not value.strip():
@ -258,7 +272,10 @@ class CircuitTerminationFilterSet(NetBoxModelFilterSet, CabledObjectFilterSet):
class Meta:
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):
if not value.strip():

View File

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

View File

@ -28,7 +28,7 @@ class DataSourceFilterSet(NetBoxModelFilterSet):
class Meta:
model = DataSource
fields = ('id', 'name', 'enabled', 'description')
fields = ('id', 'name', 'enabled', 'description', 'source_url', 'last_synced')
def search(self, queryset, name, value):
if not value.strip():
@ -115,7 +115,7 @@ class JobFilterSet(BaseFilterSet):
class Meta:
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):
if not value.strip():
@ -134,9 +134,7 @@ class ConfigRevisionFilterSet(BaseFilterSet):
class Meta:
model = ConfigRevision
fields = [
'id',
]
fields = ('id', 'created', 'comment')
def search(self, queryset, name, value):
if not value.strip():

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,9 +23,10 @@ from virtualization.models import Cluster, ClusterGroup, ClusterType
User = get_user_model()
class CustomFieldTestCase(TestCase, BaseFilterSetTests):
class CustomFieldTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = CustomField.objects.all()
filterset = CustomFieldFilterSet
ignore_fields = ('default',)
@classmethod
def setUpTestData(cls):
@ -155,9 +156,10 @@ class CustomFieldTestCase(TestCase, BaseFilterSetTests):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class CustomFieldChoiceSetTestCase(TestCase, BaseFilterSetTests):
class CustomFieldChoiceSetTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = CustomFieldChoiceSet.objects.all()
filterset = CustomFieldChoiceSetFilterSet
ignore_fields = ('extra_choices',)
@classmethod
def setUpTestData(cls):
@ -188,6 +190,7 @@ class CustomFieldChoiceSetTestCase(TestCase, BaseFilterSetTests):
class WebhookTestCase(TestCase, BaseFilterSetTests):
queryset = Webhook.objects.all()
filterset = WebhookFilterSet
ignore_fields = ('additional_headers', 'body_template')
@classmethod
def setUpTestData(cls):
@ -252,6 +255,7 @@ class WebhookTestCase(TestCase, BaseFilterSetTests):
class EventRuleTestCase(TestCase, BaseFilterSetTests):
queryset = EventRule.objects.all()
filterset = EventRuleFilterSet
ignore_fields = ('action_data', 'conditions')
@classmethod
def setUpTestData(cls):
@ -405,7 +409,7 @@ class EventRuleTestCase(TestCase, BaseFilterSetTests):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
class CustomLinkTestCase(TestCase, BaseFilterSetTests):
class CustomLinkTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = CustomLink.objects.all()
filterset = CustomLinkFilterSet
@ -474,9 +478,10 @@ class CustomLinkTestCase(TestCase, BaseFilterSetTests):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
class SavedFilterTestCase(TestCase, BaseFilterSetTests):
class SavedFilterTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = SavedFilter.objects.all()
filterset = SavedFilterFilterSet
ignore_fields = ('parameters',)
@classmethod
def setUpTestData(cls):
@ -647,9 +652,10 @@ class BookmarkTestCase(TestCase, BaseFilterSetTests):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
class ExportTemplateTestCase(TestCase, BaseFilterSetTests):
class ExportTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ExportTemplate.objects.all()
filterset = ExportTemplateFilterSet
ignore_fields = ('template_code', 'data_path')
@classmethod
def setUpTestData(cls):
@ -683,9 +689,10 @@ class ExportTemplateTestCase(TestCase, BaseFilterSetTests):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class ImageAttachmentTestCase(TestCase, BaseFilterSetTests):
class ImageAttachmentTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ImageAttachment.objects.all()
filterset = ImageAttachmentFilterSet
ignore_fields = ('image',)
@classmethod
def setUpTestData(cls):
@ -760,12 +767,6 @@ class ImageAttachmentTestCase(TestCase, BaseFilterSetTests):
}
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):
queryset = JournalEntry.objects.all()
@ -873,6 +874,7 @@ class JournalEntryTestCase(TestCase, ChangeLoggedFilterSetTests):
class ConfigContextTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ConfigContext.objects.all()
filterset = ConfigContextFilterSet
ignore_fields = ('data', 'data_path')
@classmethod
def setUpTestData(cls):
@ -1041,11 +1043,11 @@ class ConfigContextTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'device_type_id': [device_types[0].pk, device_types[1].pk]}
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]
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)
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)
def test_platform(self):
@ -1096,9 +1098,10 @@ class ConfigContextTestCase(TestCase, ChangeLoggedFilterSetTests):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class ConfigTemplateTestCase(TestCase, BaseFilterSetTests):
class ConfigTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ConfigTemplate.objects.all()
filterset = ConfigTemplateFilterSet
ignore_fields = ('template_code', 'environment_params', 'data_path')
@classmethod
def setUpTestData(cls):
@ -1125,6 +1128,93 @@ class ConfigTemplateTestCase(TestCase, BaseFilterSetTests):
class TagTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Tag.objects.all()
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
def setUpTestData(cls):
@ -1193,6 +1283,7 @@ class TagTestCase(TestCase, ChangeLoggedFilterSetTests):
class ObjectChangeTestCase(TestCase, BaseFilterSetTests):
queryset = ObjectChange.objects.all()
filterset = ObjectChangeFilterSet
ignore_fields = ('prechange_data', 'postchange_data')
@classmethod
def setUpTestData(cls):

View File

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

View File

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

View File

@ -2,6 +2,7 @@ from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from netaddr import IPNetwork
from circuits.models import Provider
from dcim.choices import InterfaceTypeChoices
from dcim.models import Device, DeviceRole, DeviceType, Interface, Location, Manufacturer, Rack, Region, Site, SiteGroup
from ipam.choices import *
@ -10,6 +11,8 @@ from ipam.models import *
from tenancy.models import Tenant, TenantGroup
from utilities.testing import ChangeLoggedFilterSetTests, create_test_device, create_test_virtualmachine
from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
from vpn.choices import L2VPNTypeChoices
from vpn.models import L2VPN
class ASNRangeTestCase(TestCase, ChangeLoggedFilterSetTests):
@ -110,13 +113,6 @@ class ASNTestCase(TestCase, ChangeLoggedFilterSetTests):
]
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 = [
Tenant(name='Tenant 1', slug='tenant-1'),
Tenant(name='Tenant 2', slug='tenant-2'),
@ -136,6 +132,12 @@ class ASNTestCase(TestCase, ChangeLoggedFilterSetTests):
)
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[1].sites.set([sites[1]])
asns[2].sites.set([sites[2]])
@ -143,6 +145,16 @@ class ASNTestCase(TestCase, ChangeLoggedFilterSetTests):
asns[4].sites.set([sites[1]])
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):
params = {'q': 'foobar1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
@ -176,11 +188,24 @@ class ASNTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'description': ['foobar1', 'foobar2']}
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):
queryset = VRF.objects.all()
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
def setUpTestData(cls):
@ -277,6 +302,18 @@ class RouteTargetTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = RouteTarget.objects.all()
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
def setUpTestData(cls):
@ -322,6 +359,17 @@ class RouteTargetTestCase(TestCase, ChangeLoggedFilterSetTests):
vrfs[1].import_targets.add(route_targets[4], route_targets[5])
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):
params = {'q': 'foobar1'}
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]}
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):
tenants = Tenant.objects.all()[:2]
params = {'tenant_id': [tenants[0].pk, tenants[1].pk]}
@ -922,6 +984,7 @@ class IPRangeTestCase(TestCase, ChangeLoggedFilterSetTests):
class IPAddressTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = IPAddress.objects.all()
filterset = IPAddressFilterSet
ignore_fields = ('fhrpgroup',)
@classmethod
def setUpTestData(cls):
@ -1092,6 +1155,16 @@ class IPAddressTestCase(TestCase, ChangeLoggedFilterSetTests):
)
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):
params = {'q': 'foobar1'}
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]}
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):
queryset = FHRPGroup.objects.all()
@ -1475,6 +1553,7 @@ class VLANGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
class VLANTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = VLAN.objects.all()
filterset = VLANFilterSet
ignore_fields = ('interfaces_as_tagged', 'vminterfaces_as_tagged')
@classmethod
def setUpTestData(cls):
@ -1733,6 +1812,7 @@ class VLANTestCase(TestCase, ChangeLoggedFilterSetTests):
class ServiceTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ServiceTemplate.objects.all()
filterset = ServiceTemplateFilterSet
ignore_fields = ('ports',)
@classmethod
def setUpTestData(cls):
@ -1797,6 +1877,7 @@ class ServiceTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
class ServiceTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Service.objects.all()
filterset = ServiceFilterSet
ignore_fields = ('ports',)
@classmethod
def setUpTestData(cls):
@ -1883,9 +1964,9 @@ class ServiceTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'virtual_machine': [vms[0].name, vms[1].name]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_ipaddress(self):
def test_ip_address(self):
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)
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)

View File

@ -50,14 +50,14 @@ class ContactGroupFilterSet(OrganizationalModelFilterSet):
class Meta:
model = ContactGroup
fields = ['id', 'name', 'slug', 'description']
fields = ('id', 'name', 'slug', 'description')
class ContactRoleFilterSet(OrganizationalModelFilterSet):
class Meta:
model = ContactRole
fields = ['id', 'name', 'slug', 'description']
fields = ('id', 'name', 'slug', 'description')
class ContactFilterSet(NetBoxModelFilterSet):
@ -77,7 +77,7 @@ class ContactFilterSet(NetBoxModelFilterSet):
class Meta:
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):
if not value.strip():
@ -131,7 +131,7 @@ class ContactAssignmentFilterSet(NetBoxModelFilterSet):
class Meta:
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):
if not value.strip():
@ -192,7 +192,7 @@ class TenantGroupFilterSet(OrganizationalModelFilterSet):
class Meta:
model = TenantGroup
fields = ['id', 'name', 'slug', 'description']
fields = ('id', 'name', 'slug', 'description')
class TenantFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
@ -212,7 +212,7 @@ class TenantFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
class Meta:
model = Tenant
fields = ['id', 'name', 'slug', 'description']
fields = ('id', 'name', 'slug', 'description')
def search(self, queryset, name, value):
if not value.strip():

View File

@ -4,8 +4,10 @@ from django.contrib.auth import get_user_model
from django.db.models import Q
from django.utils.translation import gettext as _
from core.models import ObjectType
from netbox.filtersets import BaseFilterSet
from users.models import Group, ObjectPermission, Token
from utilities.filters import ContentTypeFilter
__all__ = (
'GroupFilterSet',
@ -20,10 +22,20 @@ class GroupFilterSet(BaseFilterSet):
method='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:
model = Group
fields = ['id', 'name']
fields = ('id', 'name', 'description')
def search(self, queryset, name, value):
if not value.strip():
@ -47,10 +59,18 @@ class UserFilterSet(BaseFilterSet):
to_field_name='name',
label=_('Group (name)'),
)
permission_id = django_filters.ModelMultipleChoiceFilter(
field_name='object_permissions',
queryset=ObjectPermission.objects.all(),
label=_('Permission (ID)'),
)
class Meta:
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):
if not value.strip():
@ -100,7 +120,7 @@ class TokenFilterSet(BaseFilterSet):
class Meta:
model = Token
fields = ['id', 'key', 'write_enabled', 'description']
fields = ('id', 'key', 'write_enabled', 'description', 'last_used')
def search(self, queryset, name, value):
if not value.strip():
@ -116,6 +136,13 @@ class ObjectPermissionFilterSet(BaseFilterSet):
method='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(
method='_check_action'
)
@ -153,7 +180,7 @@ class ObjectPermissionFilterSet(BaseFilterSet):
class Meta:
model = ObjectPermission
fields = ['id', 'name', 'enabled', 'object_types', 'description']
fields = ('id', 'name', 'enabled', 'object_types', 'description')
def search(self, queryset, name, value):
if not value.strip():

View File

@ -15,6 +15,7 @@ User = get_user_model()
class UserTestCase(TestCase, BaseFilterSetTests):
queryset = User.objects.all()
filterset = filtersets.UserFilterSet
ignore_fields = ('config', 'dashboard', 'password', 'user_permissions')
@classmethod
def setUpTestData(cls):
@ -66,6 +67,16 @@ class UserTestCase(TestCase, BaseFilterSetTests):
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].users.add(users[0])
object_permissions[1].users.add(users[1])
object_permissions[2].users.add(users[2])
def test_q(self):
params = {'q': 'user1'}
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]}
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):
queryset = Group.objects.all()
filterset = filtersets.GroupFilterSet
ignore_fields = ('permissions',)
@classmethod
def setUpTestData(cls):
@ -120,6 +137,26 @@ class GroupTestCase(TestCase, BaseFilterSetTests):
)
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):
params = {'q': 'group 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']}
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):
queryset = ObjectPermission.objects.all()
filterset = filtersets.ObjectPermissionFilterSet
ignore_fields = ('actions', 'constraints')
@classmethod
def setUpTestData(cls):
@ -226,6 +274,7 @@ class ObjectPermissionTestCase(TestCase, BaseFilterSetTests):
class TokenTestCase(TestCase, BaseFilterSetTests):
queryset = Token.objects.all()
filterset = filtersets.TokenFilterSet
ignore_fields = ('allowed_ips',)
@classmethod
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__ = (
'BaseFilterSetTests',
'ChangeLoggedFilterSetTests',
)
EXEMPT_MODEL_FIELDS = (
'comments',
'custom_field_data',
'level', # MPTT
'lft', # MPTT
'rght', # MPTT
'tree_id', # MPTT
)
class BaseFilterSetTests:
queryset = 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):
"""
@ -19,6 +95,61 @@ class BaseFilterSetTests:
self.assertGreater(self.queryset.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):

View File

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

View File

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

View File

@ -29,7 +29,7 @@ class TunnelGroupFilterSet(OrganizationalModelFilterSet):
class Meta:
model = TunnelGroup
fields = ['id', 'name', 'slug', 'description']
fields = ('id', 'name', 'slug', 'description')
class TunnelFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
@ -62,7 +62,7 @@ class TunnelFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
class Meta:
model = Tunnel
fields = ['id', 'name', 'tunnel_id', 'description']
fields = ('id', 'name', 'tunnel_id', 'description')
def search(self, queryset, name, value):
if not value.strip():
@ -120,10 +120,21 @@ class TunnelTerminationFilterSet(NetBoxModelFilterSet):
class Meta:
model = TunnelTermination
fields = ['id']
fields = ('id', 'termination_id')
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(
choices=AuthenticationMethodChoices
)
@ -139,7 +150,7 @@ class IKEProposalFilterSet(NetBoxModelFilterSet):
class Meta:
model = IKEProposal
fields = ['id', 'name', 'sa_lifetime', 'description']
fields = ('id', 'name', 'sa_lifetime', 'description')
def search(self, queryset, name, value):
if not value.strip():
@ -158,16 +169,23 @@ class IKEPolicyFilterSet(NetBoxModelFilterSet):
mode = django_filters.MultipleChoiceFilter(
choices=IKEModeChoices
)
proposal_id = MultiValueNumberFilter(
field_name='proposals__id'
ike_proposal_id = django_filters.ModelMultipleChoiceFilter(
field_name='proposals',
queryset=IKEProposal.objects.all()
)
proposal = MultiValueCharFilter(
field_name='proposals__name'
ike_proposal = django_filters.ModelMultipleChoiceFilter(
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:
model = IKEPolicy
fields = ['id', 'name', 'preshared_key', 'description']
fields = ('id', 'name', 'preshared_key', 'description')
def search(self, queryset, name, value):
if not value.strip():
@ -180,6 +198,17 @@ class IKEPolicyFilterSet(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(
choices=EncryptionAlgorithmChoices
)
@ -189,7 +218,7 @@ class IPSecProposalFilterSet(NetBoxModelFilterSet):
class Meta:
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):
if not value.strip():
@ -205,16 +234,23 @@ class IPSecPolicyFilterSet(NetBoxModelFilterSet):
pfs_group = django_filters.MultipleChoiceFilter(
choices=DHGroupChoices
)
proposal_id = MultiValueNumberFilter(
field_name='proposals__id'
ipsec_proposal_id = django_filters.ModelMultipleChoiceFilter(
field_name='proposals',
queryset=IPSecProposal.objects.all()
)
proposal = MultiValueCharFilter(
field_name='proposals__name'
ipsec_proposal = django_filters.ModelMultipleChoiceFilter(
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:
model = IPSecPolicy
fields = ['id', 'name', 'description']
fields = ('id', 'name', 'description')
def search(self, queryset, name, value):
if not value.strip():
@ -253,7 +289,7 @@ class IPSecProfileFilterSet(NetBoxModelFilterSet):
class Meta:
model = IPSecProfile
fields = ['id', 'name', 'description']
fields = ('id', 'name', 'description')
def search(self, queryset, name, value):
if not value.strip():
@ -295,7 +331,7 @@ class L2VPNFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
class Meta:
model = L2VPN
fields = ['id', 'identifier', 'name', 'slug', 'type', 'description']
fields = ('id', 'identifier', 'name', 'slug', 'type', 'description')
def search(self, queryset, name, value):
if not value.strip():
@ -402,7 +438,7 @@ class L2VPNTerminationFilterSet(NetBoxModelFilterSet):
class Meta:
model = L2VPNTermination
fields = ('id', 'assigned_object_type_id')
fields = ('id', 'assigned_object_id')
def search(self, queryset, name, value):
if not value.strip():

View File

@ -1,4 +1,3 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from dcim.choices import InterfaceTypeChoices
@ -331,6 +330,16 @@ class IKEProposalTestCase(TestCase, ChangeLoggedFilterSetTests):
)
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):
params = {'q': 'foobar1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
@ -343,6 +352,13 @@ class IKEProposalTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'description': ['foobar1', 'foobar2']}
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):
params = {'authentication_method': [
AuthenticationMethodChoices.PRESHARED_KEYS, AuthenticationMethodChoices.CERTIFICATES
@ -446,11 +462,11 @@ class IKEPolicyTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'mode': [IKEModeChoices.MAIN]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_proposal(self):
def test_ike_proposal(self):
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)
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)
@ -488,6 +504,16 @@ class IPSecProposalTestCase(TestCase, ChangeLoggedFilterSetTests):
)
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):
params = {'q': 'foobar1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
@ -500,6 +526,13 @@ class IPSecProposalTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'description': ['foobar1', 'foobar2']}
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):
params = {'encryption_algorithm': [
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]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_proposal(self):
def test_ipsec_proposal(self):
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)
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)
@ -710,6 +743,14 @@ class L2VPNTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = L2VPN.objects.all()
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
def setUpTestData(cls):
@ -848,8 +889,8 @@ class L2VPNTerminationTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'l2vpn': [l2vpns[0].slug, l2vpns[1].slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6)
def test_content_type(self):
params = {'assigned_object_type_id': ContentType.objects.get(model='vlan').pk}
def test_termination_type(self):
params = {'assigned_object_type': 'ipam.vlan'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
def test_interface(self):

View File

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

View File

@ -153,6 +153,17 @@ class WirelessLANTestCase(TestCase, ChangeLoggedFilterSetTests):
)
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):
params = {'q': 'foobar1'}
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]}
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):
queryset = WirelessLink.objects.all()