Просмотр исходного кода

Merge branch 'develop' into 3440-total-cable-length

hSaria 6 лет назад
Родитель
Сommit
4f9271e9ff

+ 4 - 0
docs/release-notes/version-2.6.md

@@ -3,14 +3,18 @@
 ## Enhancements
 
 * [#2050](https://github.com/netbox-community/netbox/issues/2050) - Preview image attachments when hovering the link
+* [#2589](https://github.com/netbox-community/netbox/issues/2589) - Toggle for showing available prefixes/ip addresses
 * [#3187](https://github.com/netbox-community/netbox/issues/3187) - Add rack selection field to rack elevations
 * [#3440](https://github.com/netbox-community/netbox/issues/3440) - Add total length to cable trace
+* [#3851](https://github.com/netbox-community/netbox/issues/3851) - Allow passing initial data to custom script forms
 
 ## Bug Fixes
 
 * [#3589](https://github.com/netbox-community/netbox/issues/3589) - Fix validation on tagged VLANs of an interface
 * [#3853](https://github.com/netbox-community/netbox/issues/3853) - Fix device role link on config context view
 * [#3856](https://github.com/netbox-community/netbox/issues/3856) - Allow filtering VM interfaces by multiple MAC addresses
+* [#3857](https://github.com/netbox-community/netbox/issues/3857) - Fix group custom links rendering
+* [#3862](https://github.com/netbox-community/netbox/issues/3862) - Allow filtering device components by multiple device names
 
 ---
 

+ 7 - 0
netbox/circuits/filters.py

@@ -8,6 +8,13 @@ from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilte
 from .constants import *
 from .models import Circuit, CircuitTermination, CircuitType, Provider
 
+__all__ = (
+    'CircuitFilter',
+    'CircuitTerminationFilter',
+    'CircuitTypeFilter',
+    'ProviderFilter',
+)
+
 
 class ProviderFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
     id__in = NumericInFilter(

+ 287 - 0
netbox/circuits/tests/test_filters.py

@@ -0,0 +1,287 @@
+from django.test import TestCase
+
+from circuits.constants import CIRCUIT_STATUS_ACTIVE, CIRCUIT_STATUS_OFFLINE, CIRCUIT_STATUS_PLANNED
+from circuits.filters import *
+from circuits.models import Circuit, CircuitTermination, CircuitType, Provider
+from dcim.models import Region, Site
+
+
+class ProviderTestCase(TestCase):
+    queryset = Provider.objects.all()
+    filterset = ProviderFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        providers = (
+            Provider(name='Provider 1', slug='provider-1', asn=65001, account='1234'),
+            Provider(name='Provider 2', slug='provider-2', asn=65002, account='2345'),
+            Provider(name='Provider 3', slug='provider-3', asn=65003, account='3456'),
+            Provider(name='Provider 4', slug='provider-4', asn=65004, account='4567'),
+            Provider(name='Provider 5', slug='provider-5', asn=65005, account='5678'),
+        )
+        Provider.objects.bulk_create(providers)
+
+        regions = (
+            Region(name='Test Region 1', slug='test-region-1'),
+            Region(name='Test Region 2', slug='test-region-2'),
+        )
+        # Can't use bulk_create for models with MPTT fields
+        for r in regions:
+            r.save()
+
+        sites = (
+            Site(name='Test Site 1', slug='test-site-1', region=regions[0]),
+            Site(name='Test Site 2', slug='test-site-2', region=regions[1]),
+        )
+        Site.objects.bulk_create(sites)
+
+        circuit_types = (
+            CircuitType(name='Test Circuit Type 1', slug='test-circuit-type-1'),
+            CircuitType(name='Test Circuit Type 2', slug='test-circuit-type-2'),
+        )
+        CircuitType.objects.bulk_create(circuit_types)
+
+        circuits = (
+            Circuit(provider=providers[0], type=circuit_types[0], cid='Test Circuit 1'),
+            Circuit(provider=providers[1], type=circuit_types[1], cid='Test Circuit 1'),
+        )
+        Circuit.objects.bulk_create(circuits)
+
+        CircuitTermination.objects.bulk_create((
+            CircuitTermination(circuit=circuits[0], site=sites[0], term_side='A', port_speed=1000),
+            CircuitTermination(circuit=circuits[1], site=sites[0], term_side='A', port_speed=1000),
+        ))
+
+    def test_name(self):
+        params = {'name': ['Provider 1', 'Provider 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_slug(self):
+        params = {'slug': ['provider-1', 'provider-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_asn(self):
+        params = {'asn': ['65001', '65002']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_account(self):
+        params = {'account': ['1234', '2345']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_id__in(self):
+        id_list = self.queryset.values_list('id', flat=True)[:3]
+        params = {'id__in': ','.join([str(id) for id in id_list])}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+    def test_site(self):
+        sites = Site.objects.all()[:2]
+        params = {'site_id': [sites[0].pk, sites[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'site': [sites[0].slug, sites[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_region(self):
+        regions = Region.objects.all()[:2]
+        params = {'region_id': [regions[0].pk, regions[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'region': [regions[0].slug, regions[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class CircuitTypeTestCase(TestCase):
+    queryset = CircuitType.objects.all()
+    filterset = CircuitTypeFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        CircuitType.objects.bulk_create((
+            CircuitType(name='Circuit Type 1', slug='circuit-type-1'),
+            CircuitType(name='Circuit Type 2', slug='circuit-type-2'),
+            CircuitType(name='Circuit Type 3', slug='circuit-type-3'),
+        ))
+
+    def test_id(self):
+        params = {'id': [self.queryset.first().pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_name(self):
+        params = {'name': ['Circuit Type 1']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_slug(self):
+        params = {'slug': ['circuit-type-1']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+
+class CircuitTestCase(TestCase):
+    queryset = Circuit.objects.all()
+    filterset = CircuitFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        regions = (
+            Region(name='Test Region 1', slug='test-region-1'),
+            Region(name='Test Region 2', slug='test-region-2'),
+            Region(name='Test Region 3', slug='test-region-3'),
+        )
+        # Can't use bulk_create for models with MPTT fields
+        for r in regions:
+            r.save()
+
+        sites = (
+            Site(name='Test Site 1', slug='test-site-1', region=regions[0]),
+            Site(name='Test Site 2', slug='test-site-2', region=regions[1]),
+            Site(name='Test Site 3', slug='test-site-3', region=regions[2]),
+        )
+        Site.objects.bulk_create(sites)
+
+        circuit_types = (
+            CircuitType(name='Test Circuit Type 1', slug='test-circuit-type-1'),
+            CircuitType(name='Test Circuit Type 2', slug='test-circuit-type-2'),
+        )
+        CircuitType.objects.bulk_create(circuit_types)
+
+        providers = (
+            Provider(name='Provider 1', slug='provider-1'),
+            Provider(name='Provider 2', slug='provider-2'),
+        )
+        Provider.objects.bulk_create(providers)
+
+        circuits = (
+            Circuit(provider=providers[0], type=circuit_types[0], cid='Test Circuit 1', install_date='2020-01-01', commit_rate=1000, status=CIRCUIT_STATUS_ACTIVE),
+            Circuit(provider=providers[0], type=circuit_types[0], cid='Test Circuit 2', install_date='2020-01-02', commit_rate=2000, status=CIRCUIT_STATUS_ACTIVE),
+            Circuit(provider=providers[0], type=circuit_types[0], cid='Test Circuit 3', install_date='2020-01-03', commit_rate=3000, status=CIRCUIT_STATUS_PLANNED),
+            Circuit(provider=providers[1], type=circuit_types[1], cid='Test Circuit 4', install_date='2020-01-04', commit_rate=4000, status=CIRCUIT_STATUS_PLANNED),
+            Circuit(provider=providers[1], type=circuit_types[1], cid='Test Circuit 5', install_date='2020-01-05', commit_rate=5000, status=CIRCUIT_STATUS_OFFLINE),
+            Circuit(provider=providers[1], type=circuit_types[1], cid='Test Circuit 6', install_date='2020-01-06', commit_rate=6000, status=CIRCUIT_STATUS_OFFLINE),
+        )
+        Circuit.objects.bulk_create(circuits)
+
+        circuit_terminations = ((
+            CircuitTermination(circuit=circuits[0], site=sites[0], term_side='A', port_speed=1000),
+            CircuitTermination(circuit=circuits[1], site=sites[1], term_side='A', port_speed=1000),
+            CircuitTermination(circuit=circuits[2], site=sites[2], term_side='A', port_speed=1000),
+        ))
+        CircuitTermination.objects.bulk_create(circuit_terminations)
+
+    def test_cid(self):
+        params = {'cid': ['Test Circuit 1', 'Test Circuit 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_install_date(self):
+        params = {'install_date': ['2020-01-01', '2020-01-02']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_commit_rate(self):
+        params = {'commit_rate': ['1000', '2000']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_id__in(self):
+        id_list = self.queryset.values_list('id', flat=True)[:3]
+        params = {'id__in': ','.join([str(id) for id in id_list])}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+    def test_provider(self):
+        provider = Provider.objects.first()
+        params = {'provider_id': [provider.pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+        params = {'provider': [provider.slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+    def test_type(self):
+        circuit_type = CircuitType.objects.first()
+        params = {'type_id': [circuit_type.pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+        params = {'type': [circuit_type.slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+    def test_status(self):
+        params = {'status': [CIRCUIT_STATUS_ACTIVE, CIRCUIT_STATUS_PLANNED]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_region(self):
+        regions = Region.objects.all()[:2]
+        params = {'region_id': [regions[0].pk, regions[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'region': [regions[0].slug, regions[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_site(self):
+        sites = Site.objects.all()[:2]
+        params = {'site_id': [sites[0].pk, sites[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'site': [sites[0].slug, sites[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class CircuitTerminationTestCase(TestCase):
+    queryset = CircuitTermination.objects.all()
+    filterset = CircuitTerminationFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        sites = (
+            Site(name='Test Site 1', slug='test-site-1'),
+            Site(name='Test Site 2', slug='test-site-2'),
+            Site(name='Test Site 3', slug='test-site-3'),
+        )
+        Site.objects.bulk_create(sites)
+
+        circuit_types = (
+            CircuitType(name='Test Circuit Type 1', slug='test-circuit-type-1'),
+        )
+        CircuitType.objects.bulk_create(circuit_types)
+
+        providers = (
+            Provider(name='Provider 1', slug='provider-1'),
+        )
+        Provider.objects.bulk_create(providers)
+
+        circuits = (
+            Circuit(provider=providers[0], type=circuit_types[0], cid='Test Circuit 1'),
+            Circuit(provider=providers[0], type=circuit_types[0], cid='Test Circuit 2'),
+            Circuit(provider=providers[0], type=circuit_types[0], cid='Test Circuit 3'),
+        )
+        Circuit.objects.bulk_create(circuits)
+
+        circuit_terminations = ((
+            CircuitTermination(circuit=circuits[0], site=sites[0], term_side='A', port_speed=1000, upstream_speed=1000, xconnect_id='ABC'),
+            CircuitTermination(circuit=circuits[0], site=sites[1], term_side='Z', port_speed=1000, upstream_speed=1000, xconnect_id='DEF'),
+            CircuitTermination(circuit=circuits[1], site=sites[1], term_side='A', port_speed=2000, upstream_speed=2000, xconnect_id='GHI'),
+            CircuitTermination(circuit=circuits[1], site=sites[2], term_side='Z', port_speed=2000, upstream_speed=2000, xconnect_id='JKL'),
+            CircuitTermination(circuit=circuits[2], site=sites[2], term_side='A', port_speed=3000, upstream_speed=3000, xconnect_id='MNO'),
+            CircuitTermination(circuit=circuits[2], site=sites[0], term_side='Z', port_speed=3000, upstream_speed=3000, xconnect_id='PQR'),
+        ))
+        CircuitTermination.objects.bulk_create(circuit_terminations)
+
+    def test_term_side(self):
+        params = {'term_side': 'A'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+    def test_port_speed(self):
+        params = {'port_speed': ['1000', '2000']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_upstream_speed(self):
+        params = {'upstream_speed': ['1000', '2000']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_xconnect_id(self):
+        params = {'xconnect_id': ['ABC', 'DEF']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_circuit_id(self):
+        circuits = Circuit.objects.all()[:2]
+        params = {'circuit_id': [circuits[0].pk, circuits[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_site(self):
+        sites = Site.objects.all()[:2]
+        params = {'site_id': [sites[0].pk, sites[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'site': [sites[0].slug, sites[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)

+ 41 - 1
netbox/dcim/filters.py

@@ -21,6 +21,45 @@ from .models import (
 )
 
 
+__all__ = (
+    'CableFilter',
+    'ConsoleConnectionFilter',
+    'ConsolePortFilter',
+    'ConsolePortTemplateFilter',
+    'ConsoleServerPortFilter',
+    'ConsoleServerPortTemplateFilter',
+    'DeviceBayFilter',
+    'DeviceBayTemplateFilter',
+    'DeviceFilter',
+    'DeviceRoleFilter',
+    'DeviceTypeFilter',
+    'FrontPortFilter',
+    'FrontPortTemplateFilter',
+    'InterfaceConnectionFilter',
+    'InterfaceFilter',
+    'InterfaceTemplateFilter',
+    'InventoryItemFilter',
+    'ManufacturerFilter',
+    'PlatformFilter',
+    'PowerConnectionFilter',
+    'PowerFeedFilter',
+    'PowerOutletFilter',
+    'PowerOutletTemplateFilter',
+    'PowerPanelFilter',
+    'PowerPortFilter',
+    'PowerPortTemplateFilter',
+    'RackFilter',
+    'RackGroupFilter',
+    'RackReservationFilter',
+    'RackRoleFilter',
+    'RearPortFilter',
+    'RearPortTemplateFilter',
+    'RegionFilter',
+    'SiteFilter',
+    'VirtualChassisFilter',
+)
+
+
 class RegionFilter(NameSlugSearchFilterSet):
     parent_id = django_filters.ModelMultipleChoiceFilter(
         queryset=Region.objects.all(),
@@ -646,7 +685,8 @@ class DeviceComponentFilterSet(django_filters.FilterSet):
         queryset=Device.objects.all(),
         label='Device (ID)',
     )
-    device = django_filters.ModelChoiceFilter(
+    device = django_filters.ModelMultipleChoiceFilter(
+        field_name='device__name',
         queryset=Device.objects.all(),
         to_field_name='name',
         label='Device (name)',

+ 2381 - 0
netbox/dcim/tests/test_filters.py

@@ -0,0 +1,2381 @@
+from django.contrib.auth.models import User
+from django.test import TestCase
+
+from dcim.constants import *
+from dcim.filters import *
+from dcim.models import (
+    Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
+    DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
+    InventoryItem, Manufacturer, Platform, PowerFeed, PowerPanel, PowerPort, PowerPortTemplate, PowerOutlet,
+    PowerOutletTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
+    VirtualChassis,
+)
+from ipam.models import IPAddress
+from virtualization.models import Cluster, ClusterType
+
+
+class RegionTestCase(TestCase):
+    queryset = Region.objects.all()
+
+    @classmethod
+    def setUpTestData(cls):
+
+        regions = (
+            Region(name='Region 1', slug='region-1'),
+            Region(name='Region 2', slug='region-2'),
+            Region(name='Region 3', slug='region-3'),
+        )
+        for region in regions:
+            region.save()
+
+        child_regions = (
+            Region(name='Region 1A', slug='region-1a', parent=regions[0]),
+            Region(name='Region 1B', slug='region-1b', parent=regions[0]),
+            Region(name='Region 2A', slug='region-2a', parent=regions[1]),
+            Region(name='Region 2B', slug='region-2b', parent=regions[1]),
+            Region(name='Region 3A', slug='region-3a', parent=regions[2]),
+            Region(name='Region 3B', slug='region-3b', parent=regions[2]),
+        )
+        for region in child_regions:
+            region.save()
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(RegionFilter(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Region 1', 'Region 2']}
+        self.assertEqual(RegionFilter(params, self.queryset).qs.count(), 2)
+
+    def test_slug(self):
+        params = {'slug': ['region-1', 'region-2']}
+        self.assertEqual(RegionFilter(params, self.queryset).qs.count(), 2)
+
+    def test_parent(self):
+        parent_regions = Region.objects.filter(parent__isnull=True)[:2]
+        params = {'parent_id': [parent_regions[0].pk, parent_regions[1].pk]}
+        self.assertEqual(RegionFilter(params, self.queryset).qs.count(), 4)
+        params = {'parent': [parent_regions[0].slug, parent_regions[1].slug]}
+        self.assertEqual(RegionFilter(params, self.queryset).qs.count(), 4)
+
+
+class SiteTestCase(TestCase):
+    queryset = Site.objects.all()
+    filterset = SiteFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        regions = (
+            Region(name='Region 1', slug='region-1'),
+            Region(name='Region 2', slug='region-2'),
+            Region(name='Region 3', slug='region-3'),
+        )
+        for region in regions:
+            region.save()
+
+        sites = (
+            Site(name='Site 1', slug='site-1', region=regions[0], status=SITE_STATUS_ACTIVE, facility='Facility 1', asn=65001, latitude=10, longitude=10, contact_name='Contact 1', contact_phone='123-555-0001', contact_email='contact1@example.com'),
+            Site(name='Site 2', slug='site-2', region=regions[1], status=SITE_STATUS_PLANNED, facility='Facility 2', asn=65002, latitude=20, longitude=20, contact_name='Contact 2', contact_phone='123-555-0002', contact_email='contact2@example.com'),
+            Site(name='Site 3', slug='site-3', region=regions[2], status=SITE_STATUS_RETIRED, facility='Facility 3', asn=65003, latitude=30, longitude=30, contact_name='Contact 3', contact_phone='123-555-0003', contact_email='contact3@example.com'),
+        )
+        Site.objects.bulk_create(sites)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Site 1', 'Site 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_slug(self):
+        params = {'slug': ['site-1', 'site-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_facility(self):
+        params = {'facility': ['Facility 1', 'Facility 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_asn(self):
+        params = {'asn': [65001, 65002]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_latitude(self):
+        params = {'latitude': [10, 20]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_longitude(self):
+        params = {'longitude': [10, 20]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_contact_name(self):
+        params = {'contact_name': ['Contact 1', 'Contact 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_contact_phone(self):
+        params = {'contact_phone': ['123-555-0001', '123-555-0002']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_contact_email(self):
+        params = {'contact_email': ['contact1@example.com', 'contact2@example.com']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_id__in(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id__in': ','.join([str(id) for id in id_list])}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_status(self):
+        params = {'status': [SITE_STATUS_ACTIVE, SITE_STATUS_PLANNED]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_region(self):
+        regions = Region.objects.all()[:2]
+        params = {'region_id': [regions[0].pk, regions[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'region': [regions[0].slug, regions[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class RackGroupTestCase(TestCase):
+    queryset = RackGroup.objects.all()
+    filterset = RackGroupFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        regions = (
+            Region(name='Region 1', slug='region-1'),
+            Region(name='Region 2', slug='region-2'),
+            Region(name='Region 3', slug='region-3'),
+        )
+        for region in regions:
+            region.save()
+
+        sites = (
+            Site(name='Site 1', slug='site-1', region=regions[0]),
+            Site(name='Site 2', slug='site-2', region=regions[1]),
+            Site(name='Site 3', slug='site-3', region=regions[2]),
+        )
+        Site.objects.bulk_create(sites)
+
+        rack_groups = (
+            RackGroup(name='Rack Group 1', slug='rack-group-1', site=sites[0]),
+            RackGroup(name='Rack Group 2', slug='rack-group-2', site=sites[1]),
+            RackGroup(name='Rack Group 3', slug='rack-group-3', site=sites[2]),
+        )
+        RackGroup.objects.bulk_create(rack_groups)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Rack Group 1', 'Rack Group 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_slug(self):
+        params = {'slug': ['rack-group-1', 'rack-group-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_region(self):
+        regions = Region.objects.all()[:2]
+        params = {'region_id': [regions[0].pk, regions[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'region': [regions[0].slug, regions[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_site(self):
+        sites = Site.objects.all()[:2]
+        params = {'site_id': [sites[0].pk, sites[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'site': [sites[0].slug, sites[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class RackRoleTestCase(TestCase):
+    queryset = RackRole.objects.all()
+    filterset = RackRoleFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        rack_roles = (
+            RackRole(name='Rack Role 1', slug='rack-role-1', color='ff0000'),
+            RackRole(name='Rack Role 2', slug='rack-role-2', color='00ff00'),
+            RackRole(name='Rack Role 3', slug='rack-role-3', color='0000ff'),
+        )
+        RackRole.objects.bulk_create(rack_roles)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Rack Role 1', 'Rack Role 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_slug(self):
+        params = {'slug': ['rack-role-1', 'rack-role-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_color(self):
+        params = {'color': ['ff0000', '00ff00']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class RackTestCase(TestCase):
+    queryset = Rack.objects.all()
+    filterset = RackFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        regions = (
+            Region(name='Region 1', slug='region-1'),
+            Region(name='Region 2', slug='region-2'),
+            Region(name='Region 3', slug='region-3'),
+        )
+        for region in regions:
+            region.save()
+
+        sites = (
+            Site(name='Site 1', slug='site-1', region=regions[0]),
+            Site(name='Site 2', slug='site-2', region=regions[1]),
+            Site(name='Site 3', slug='site-3', region=regions[2]),
+        )
+        Site.objects.bulk_create(sites)
+
+        rack_groups = (
+            RackGroup(name='Rack Group 1', slug='rack-group-1', site=sites[0]),
+            RackGroup(name='Rack Group 2', slug='rack-group-2', site=sites[1]),
+            RackGroup(name='Rack Group 3', slug='rack-group-3', site=sites[2]),
+        )
+        RackGroup.objects.bulk_create(rack_groups)
+
+        rack_roles = (
+            RackRole(name='Rack Role 1', slug='rack-role-1'),
+            RackRole(name='Rack Role 2', slug='rack-role-2'),
+            RackRole(name='Rack Role 3', slug='rack-role-3'),
+        )
+        RackRole.objects.bulk_create(rack_roles)
+
+        racks = (
+            Rack(name='Rack 1', facility_id='rack-1', site=sites[0], group=rack_groups[0], status=RACK_STATUS_ACTIVE, role=rack_roles[0], serial='ABC', asset_tag='1001', type=RACK_TYPE_2POST, width=RACK_WIDTH_19IN, u_height=42, desc_units=False, outer_width=100, outer_depth=100, outer_unit=LENGTH_UNIT_MILLIMETER),
+            Rack(name='Rack 2', facility_id='rack-2', site=sites[1], group=rack_groups[1], status=RACK_STATUS_PLANNED, role=rack_roles[1], serial='DEF', asset_tag='1002', type=RACK_TYPE_4POST, width=RACK_WIDTH_19IN, u_height=43, desc_units=False, outer_width=200, outer_depth=200, outer_unit=LENGTH_UNIT_MILLIMETER),
+            Rack(name='Rack 3', facility_id='rack-3', site=sites[2], group=rack_groups[2], status=RACK_STATUS_RESERVED, role=rack_roles[2], serial='GHI', asset_tag='1003', type=RACK_TYPE_CABINET, width=RACK_WIDTH_23IN, u_height=44, desc_units=True, outer_width=300, outer_depth=300, outer_unit=LENGTH_UNIT_INCH),
+        )
+        Rack.objects.bulk_create(racks)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Rack 1', 'Rack 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_facility_id(self):
+        params = {'facility_id': ['rack-1', 'rack-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_asset_tag(self):
+        params = {'asset_tag': ['1001', '1002']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_type(self):
+        # TODO: Test for multiple values
+        params = {'type': RACK_TYPE_2POST}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_width(self):
+        # TODO: Test for multiple values
+        params = {'width': RACK_WIDTH_19IN}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_u_height(self):
+        params = {'u_height': [42, 43]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_desc_units(self):
+        params = {'desc_units': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'desc_units': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_outer_width(self):
+        params = {'outer_width': [100, 200]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_outer_depth(self):
+        params = {'outer_depth': [100, 200]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_outer_unit(self):
+        self.assertEqual(Rack.objects.filter(outer_unit__isnull=False).count(), 3)
+        params = {'outer_unit': LENGTH_UNIT_MILLIMETER}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_id__in(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id__in': ','.join([str(id) for id in id_list])}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_region(self):
+        regions = Region.objects.all()[:2]
+        params = {'region_id': [regions[0].pk, regions[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'region': [regions[0].slug, regions[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_site(self):
+        sites = Site.objects.all()[:2]
+        params = {'site_id': [sites[0].pk, sites[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'site': [sites[0].slug, sites[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_group(self):
+        groups = RackGroup.objects.all()[:2]
+        params = {'group_id': [groups[0].pk, groups[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'group': [groups[0].slug, groups[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_status(self):
+        params = {'status': [RACK_STATUS_ACTIVE, RACK_STATUS_PLANNED]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_role(self):
+        roles = RackRole.objects.all()[:2]
+        params = {'role_id': [roles[0].pk, roles[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'role': [roles[0].slug, roles[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_serial(self):
+        params = {'serial': 'ABC'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'serial': 'abc'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+
+class RackReservationTestCase(TestCase):
+    queryset = RackReservation.objects.all()
+    filterset = RackReservationFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        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)
+
+        rack_groups = (
+            RackGroup(name='Rack Group 1', slug='rack-group-1', site=sites[0]),
+            RackGroup(name='Rack Group 2', slug='rack-group-2', site=sites[1]),
+            RackGroup(name='Rack Group 3', slug='rack-group-3', site=sites[2]),
+        )
+        RackGroup.objects.bulk_create(rack_groups)
+
+        racks = (
+            Rack(name='Rack 1', site=sites[0], group=rack_groups[0]),
+            Rack(name='Rack 2', site=sites[1], group=rack_groups[1]),
+            Rack(name='Rack 3', site=sites[2], group=rack_groups[2]),
+        )
+        Rack.objects.bulk_create(racks)
+
+        users = (
+            User(username='User 1'),
+            User(username='User 2'),
+            User(username='User 3'),
+        )
+        User.objects.bulk_create(users)
+
+        reservations = (
+            RackReservation(rack=racks[0], units=[1, 2, 3], user=users[0]),
+            RackReservation(rack=racks[1], units=[4, 5, 6], user=users[1]),
+            RackReservation(rack=racks[2], units=[7, 8, 9], user=users[2]),
+        )
+        RackReservation.objects.bulk_create(reservations)
+
+    def test_id__in(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id__in': ','.join([str(id) for id in id_list])}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_site(self):
+        sites = Site.objects.all()[:2]
+        params = {'site_id': [sites[0].pk, sites[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'site': [sites[0].slug, sites[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_group(self):
+        groups = RackGroup.objects.all()[:2]
+        params = {'group_id': [groups[0].pk, groups[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'group': [groups[0].slug, groups[1].slug]}
+        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)
+        # TODO: Filtering by username is broken
+        # params = {'user': [users[0].username, users[1].username]}
+        # self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class ManufacturerTestCase(TestCase):
+    queryset = Manufacturer.objects.all()
+    filterset = ManufacturerFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        manufacturers = (
+            Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
+            Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
+            Manufacturer(name='Manufacturer 3', slug='manufacturer-3'),
+        )
+        Manufacturer.objects.bulk_create(manufacturers)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Manufacturer 1', 'Manufacturer 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_slug(self):
+        params = {'slug': ['manufacturer-1', 'manufacturer-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class DeviceTypeTestCase(TestCase):
+    queryset = DeviceType.objects.all()
+    filterset = DeviceTypeFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        manufacturers = (
+            Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
+            Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
+            Manufacturer(name='Manufacturer 3', slug='manufacturer-3'),
+        )
+        Manufacturer.objects.bulk_create(manufacturers)
+
+        device_types = (
+            DeviceType(manufacturer=manufacturers[0], model='Model 1', slug='model-1', part_number='Part Number 1', u_height=1, is_full_depth=True, subdevice_role=None),
+            DeviceType(manufacturer=manufacturers[1], model='Model 2', slug='model-2', part_number='Part Number 2', u_height=2, is_full_depth=True, subdevice_role=SUBDEVICE_ROLE_PARENT),
+            DeviceType(manufacturer=manufacturers[2], model='Model 3', slug='model-3', part_number='Part Number 3', u_height=3, is_full_depth=False, subdevice_role=SUBDEVICE_ROLE_CHILD),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
+        # Add component templates for filtering
+        ConsolePortTemplate.objects.bulk_create((
+            ConsolePortTemplate(device_type=device_types[0], name='Console Port 1'),
+            ConsolePortTemplate(device_type=device_types[1], name='Console Port 2'),
+        ))
+        ConsoleServerPortTemplate.objects.bulk_create((
+            ConsoleServerPortTemplate(device_type=device_types[0], name='Console Server Port 1'),
+            ConsoleServerPortTemplate(device_type=device_types[1], name='Console Server Port 2'),
+        ))
+        PowerPortTemplate.objects.bulk_create((
+            PowerPortTemplate(device_type=device_types[0], name='Power Port 1'),
+            PowerPortTemplate(device_type=device_types[1], name='Power Port 2'),
+        ))
+        PowerOutletTemplate.objects.bulk_create((
+            PowerOutletTemplate(device_type=device_types[0], name='Power Outlet 1'),
+            PowerOutletTemplate(device_type=device_types[1], name='Power Outlet 2'),
+        ))
+        InterfaceTemplate.objects.bulk_create((
+            InterfaceTemplate(device_type=device_types[0], name='Interface 1'),
+            InterfaceTemplate(device_type=device_types[1], name='Interface 2'),
+        ))
+        rear_ports = (
+            RearPortTemplate(device_type=device_types[0], name='Rear Port 1', type=PORT_TYPE_8P8C),
+            RearPortTemplate(device_type=device_types[1], name='Rear Port 2', type=PORT_TYPE_8P8C),
+        )
+        RearPortTemplate.objects.bulk_create(rear_ports)
+        FrontPortTemplate.objects.bulk_create((
+            FrontPortTemplate(device_type=device_types[0], name='Front Port 1', type=PORT_TYPE_8P8C, rear_port=rear_ports[0]),
+            FrontPortTemplate(device_type=device_types[1], name='Front Port 2', type=PORT_TYPE_8P8C, rear_port=rear_ports[1]),
+        ))
+        DeviceBayTemplate.objects.bulk_create((
+            DeviceBayTemplate(device_type=device_types[0], name='Device Bay 1'),
+            DeviceBayTemplate(device_type=device_types[1], name='Device Bay 2'),
+        ))
+
+    def test_model(self):
+        params = {'model': ['Model 1', 'Model 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_slug(self):
+        params = {'slug': ['model-1', 'model-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_part_number(self):
+        params = {'part_number': ['Part Number 1', 'Part Number 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_u_height(self):
+        params = {'u_height': [1, 2]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_is_full_depth(self):
+        params = {'is_full_depth': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'is_full_depth': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_subdevice_role(self):
+        params = {'subdevice_role': SUBDEVICE_ROLE_PARENT}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_id__in(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id__in': ','.join([str(id) for id in id_list])}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_manufacturer(self):
+        manufacturers = Manufacturer.objects.all()[:2]
+        params = {'manufacturer_id': [manufacturers[0].pk, manufacturers[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'manufacturer': [manufacturers[0].slug, manufacturers[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_console_ports(self):
+        params = {'console_ports': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'console_ports': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_console_server_ports(self):
+        params = {'console_server_ports': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'console_server_ports': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_power_ports(self):
+        params = {'power_ports': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'power_ports': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_power_outlets(self):
+        params = {'power_outlets': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'power_outlets': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_interfaces(self):
+        params = {'interfaces': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'interfaces': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_pass_through_ports(self):
+        params = {'pass_through_ports': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'pass_through_ports': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    # TODO: Add device_bay filter
+    # def test_device_bays(self):
+    #     params = {'device_bays': 'true'}
+    #     self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+    #     params = {'device_bays': 'false'}
+    #     self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+
+class ConsolePortTemplateTestCase(TestCase):
+    queryset = ConsolePortTemplate.objects.all()
+    filterset = ConsolePortTemplateFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+
+        device_types = (
+            DeviceType(manufacturer=manufacturer, model='Model 1', slug='model-1'),
+            DeviceType(manufacturer=manufacturer, model='Model 2', slug='model-2'),
+            DeviceType(manufacturer=manufacturer, model='Model 3', slug='model-3'),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
+        ConsolePortTemplate.objects.bulk_create((
+            ConsolePortTemplate(device_type=device_types[0], name='Console Port 1'),
+            ConsolePortTemplate(device_type=device_types[1], name='Console Port 2'),
+            ConsolePortTemplate(device_type=device_types[2], name='Console Port 3'),
+        ))
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Console Port 1', 'Console Port 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_devicetype_id(self):
+        device_types = DeviceType.objects.all()[:2]
+        params = {'devicetype_id': [device_types[0].pk, device_types[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class ConsoleServerPortTemplateTestCase(TestCase):
+    queryset = ConsoleServerPortTemplate.objects.all()
+    filterset = ConsoleServerPortTemplateFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+
+        device_types = (
+            DeviceType(manufacturer=manufacturer, model='Model 1', slug='model-1'),
+            DeviceType(manufacturer=manufacturer, model='Model 2', slug='model-2'),
+            DeviceType(manufacturer=manufacturer, model='Model 3', slug='model-3'),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
+        ConsoleServerPortTemplate.objects.bulk_create((
+            ConsoleServerPortTemplate(device_type=device_types[0], name='Console Server Port 1'),
+            ConsoleServerPortTemplate(device_type=device_types[1], name='Console Server Port 2'),
+            ConsoleServerPortTemplate(device_type=device_types[2], name='Console Server Port 3'),
+        ))
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Console Server Port 1', 'Console Server Port 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_devicetype_id(self):
+        device_types = DeviceType.objects.all()[:2]
+        params = {'devicetype_id': [device_types[0].pk, device_types[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class PowerPortTemplateTestCase(TestCase):
+    queryset = PowerPortTemplate.objects.all()
+    filterset = PowerPortTemplateFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+
+        device_types = (
+            DeviceType(manufacturer=manufacturer, model='Model 1', slug='model-1'),
+            DeviceType(manufacturer=manufacturer, model='Model 2', slug='model-2'),
+            DeviceType(manufacturer=manufacturer, model='Model 3', slug='model-3'),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
+        PowerPortTemplate.objects.bulk_create((
+            PowerPortTemplate(device_type=device_types[0], name='Power Port 1', maximum_draw=100, allocated_draw=50),
+            PowerPortTemplate(device_type=device_types[1], name='Power Port 2', maximum_draw=200, allocated_draw=100),
+            PowerPortTemplate(device_type=device_types[2], name='Power Port 3', maximum_draw=300, allocated_draw=150),
+        ))
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Power Port 1', 'Power Port 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_devicetype_id(self):
+        device_types = DeviceType.objects.all()[:2]
+        params = {'devicetype_id': [device_types[0].pk, device_types[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_maximum_draw(self):
+        params = {'maximum_draw': [100, 200]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_allocated_draw(self):
+        params = {'allocated_draw': [50, 100]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class PowerOutletTemplateTestCase(TestCase):
+    queryset = PowerOutletTemplate.objects.all()
+    filterset = PowerOutletTemplateFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+
+        device_types = (
+            DeviceType(manufacturer=manufacturer, model='Model 1', slug='model-1'),
+            DeviceType(manufacturer=manufacturer, model='Model 2', slug='model-2'),
+            DeviceType(manufacturer=manufacturer, model='Model 3', slug='model-3'),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
+        PowerOutletTemplate.objects.bulk_create((
+            PowerOutletTemplate(device_type=device_types[0], name='Power Outlet 1', feed_leg=POWERFEED_LEG_A),
+            PowerOutletTemplate(device_type=device_types[1], name='Power Outlet 2', feed_leg=POWERFEED_LEG_B),
+            PowerOutletTemplate(device_type=device_types[2], name='Power Outlet 3', feed_leg=POWERFEED_LEG_C),
+        ))
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Power Outlet 1', 'Power Outlet 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_devicetype_id(self):
+        device_types = DeviceType.objects.all()[:2]
+        params = {'devicetype_id': [device_types[0].pk, device_types[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_feed_leg(self):
+        # TODO: Support filtering for multiple values
+        params = {'feed_leg': POWERFEED_LEG_A}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+
+class InterfaceTemplateTestCase(TestCase):
+    queryset = InterfaceTemplate.objects.all()
+    filterset = InterfaceTemplateFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+
+        device_types = (
+            DeviceType(manufacturer=manufacturer, model='Model 1', slug='model-1'),
+            DeviceType(manufacturer=manufacturer, model='Model 2', slug='model-2'),
+            DeviceType(manufacturer=manufacturer, model='Model 3', slug='model-3'),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
+        InterfaceTemplate.objects.bulk_create((
+            InterfaceTemplate(device_type=device_types[0], name='Interface 1', type=IFACE_TYPE_1GE_FIXED, mgmt_only=True),
+            InterfaceTemplate(device_type=device_types[1], name='Interface 2', type=IFACE_TYPE_1GE_GBIC, mgmt_only=False),
+            InterfaceTemplate(device_type=device_types[2], name='Interface 3', type=IFACE_TYPE_1GE_SFP, mgmt_only=False),
+        ))
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Interface 1', 'Interface 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_devicetype_id(self):
+        device_types = DeviceType.objects.all()[:2]
+        params = {'devicetype_id': [device_types[0].pk, device_types[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_type(self):
+        # TODO: Support filtering for multiple values
+        params = {'type': IFACE_TYPE_1GE_FIXED}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_mgmt_only(self):
+        params = {'mgmt_only': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'mgmt_only': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class FrontPortTemplateTestCase(TestCase):
+    queryset = FrontPortTemplate.objects.all()
+    filterset = FrontPortTemplateFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+
+        device_types = (
+            DeviceType(manufacturer=manufacturer, model='Model 1', slug='model-1'),
+            DeviceType(manufacturer=manufacturer, model='Model 2', slug='model-2'),
+            DeviceType(manufacturer=manufacturer, model='Model 3', slug='model-3'),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
+        rear_ports = (
+            RearPortTemplate(device_type=device_types[0], name='Rear Port 1', type=PORT_TYPE_8P8C),
+            RearPortTemplate(device_type=device_types[1], name='Rear Port 2', type=PORT_TYPE_8P8C),
+            RearPortTemplate(device_type=device_types[2], name='Rear Port 3', type=PORT_TYPE_8P8C),
+        )
+        RearPortTemplate.objects.bulk_create(rear_ports)
+
+        FrontPortTemplate.objects.bulk_create((
+            FrontPortTemplate(device_type=device_types[0], name='Front Port 1', rear_port=rear_ports[0], type=PORT_TYPE_8P8C),
+            FrontPortTemplate(device_type=device_types[1], name='Front Port 2', rear_port=rear_ports[1], type=PORT_TYPE_110_PUNCH),
+            FrontPortTemplate(device_type=device_types[2], name='Front Port 3', rear_port=rear_ports[2], type=PORT_TYPE_BNC),
+        ))
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Front Port 1', 'Front Port 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_devicetype_id(self):
+        device_types = DeviceType.objects.all()[:2]
+        params = {'devicetype_id': [device_types[0].pk, device_types[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_type(self):
+        # TODO: Support filtering for multiple values
+        params = {'type': PORT_TYPE_8P8C}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+
+class RearPortTemplateTestCase(TestCase):
+    queryset = RearPortTemplate.objects.all()
+    filterset = RearPortTemplateFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+
+        device_types = (
+            DeviceType(manufacturer=manufacturer, model='Model 1', slug='model-1'),
+            DeviceType(manufacturer=manufacturer, model='Model 2', slug='model-2'),
+            DeviceType(manufacturer=manufacturer, model='Model 3', slug='model-3'),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
+        RearPortTemplate.objects.bulk_create((
+            RearPortTemplate(device_type=device_types[0], name='Rear Port 1', type=PORT_TYPE_8P8C, positions=1),
+            RearPortTemplate(device_type=device_types[1], name='Rear Port 2', type=PORT_TYPE_110_PUNCH, positions=2),
+            RearPortTemplate(device_type=device_types[2], name='Rear Port 3', type=PORT_TYPE_BNC, positions=3),
+        ))
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Rear Port 1', 'Rear Port 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_devicetype_id(self):
+        device_types = DeviceType.objects.all()[:2]
+        params = {'devicetype_id': [device_types[0].pk, device_types[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_type(self):
+        # TODO: Support filtering for multiple values
+        params = {'type': PORT_TYPE_8P8C}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_positions(self):
+        params = {'positions': [1, 2]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class DeviceBayTemplateTestCase(TestCase):
+    queryset = DeviceBayTemplate.objects.all()
+    filterset = DeviceBayTemplateFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+
+        device_types = (
+            DeviceType(manufacturer=manufacturer, model='Model 1', slug='model-1'),
+            DeviceType(manufacturer=manufacturer, model='Model 2', slug='model-2'),
+            DeviceType(manufacturer=manufacturer, model='Model 3', slug='model-3'),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
+        DeviceBayTemplate.objects.bulk_create((
+            DeviceBayTemplate(device_type=device_types[0], name='Device Bay 1'),
+            DeviceBayTemplate(device_type=device_types[1], name='Device Bay 2'),
+            DeviceBayTemplate(device_type=device_types[2], name='Device Bay 3'),
+        ))
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Device Bay 1', 'Device Bay 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_devicetype_id(self):
+        device_types = DeviceType.objects.all()[:2]
+        params = {'devicetype_id': [device_types[0].pk, device_types[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class DeviceRoleTestCase(TestCase):
+    queryset = DeviceRole.objects.all()
+    filterset = DeviceRoleFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        device_roles = (
+            DeviceRole(name='Device Role 1', slug='device-role-1', color='ff0000', vm_role=True),
+            DeviceRole(name='Device Role 2', slug='device-role-2', color='00ff00', vm_role=True),
+            DeviceRole(name='Device Role 3', slug='device-role-3', color='0000ff', vm_role=False),
+        )
+        DeviceRole.objects.bulk_create(device_roles)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Device Role 1', 'Device Role 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_slug(self):
+        params = {'slug': ['device-role-1', 'device-role-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_color(self):
+        params = {'color': ['ff0000', '00ff00']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_vm_role(self):
+        params = {'vm_role': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'vm_role': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+
+class PlatformTestCase(TestCase):
+    queryset = Platform.objects.all()
+    filterset = PlatformFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        manufacturers = (
+            Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
+            Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
+            Manufacturer(name='Manufacturer 3', slug='manufacturer-3'),
+        )
+        Manufacturer.objects.bulk_create(manufacturers)
+
+        platforms = (
+            Platform(name='Platform 1', slug='platform-1', manufacturer=manufacturers[0], napalm_driver='driver-1'),
+            Platform(name='Platform 2', slug='platform-2', manufacturer=manufacturers[1], napalm_driver='driver-2'),
+            Platform(name='Platform 3', slug='platform-3', manufacturer=manufacturers[2], napalm_driver='driver-3'),
+        )
+        Platform.objects.bulk_create(platforms)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Platform 1', 'Platform 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_slug(self):
+        params = {'slug': ['platform-1', 'platform-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_napalm_driver(self):
+        params = {'napalm_driver': ['driver-1', 'driver-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_manufacturer(self):
+        manufacturers = Manufacturer.objects.all()[:2]
+        params = {'manufacturer_id': [manufacturers[0].pk, manufacturers[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'manufacturer': [manufacturers[0].slug, manufacturers[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class DeviceTestCase(TestCase):
+    queryset = Device.objects.all()
+    filterset = DeviceFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        manufacturers = (
+            Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
+            Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
+            Manufacturer(name='Manufacturer 3', slug='manufacturer-3'),
+        )
+        Manufacturer.objects.bulk_create(manufacturers)
+
+        device_types = (
+            DeviceType(manufacturer=manufacturers[0], model='Model 1', slug='model-1', is_full_depth=True),
+            DeviceType(manufacturer=manufacturers[1], model='Model 2', slug='model-2', is_full_depth=True),
+            DeviceType(manufacturer=manufacturers[2], model='Model 3', slug='model-3', is_full_depth=False),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
+        device_roles = (
+            DeviceRole(name='Device Role 1', slug='device-role-1'),
+            DeviceRole(name='Device Role 2', slug='device-role-2'),
+            DeviceRole(name='Device Role 3', slug='device-role-3'),
+        )
+        DeviceRole.objects.bulk_create(device_roles)
+
+        platforms = (
+            Platform(name='Platform 1', slug='platform-1'),
+            Platform(name='Platform 2', slug='platform-2'),
+            Platform(name='Platform 3', slug='platform-3'),
+        )
+        Platform.objects.bulk_create(platforms)
+
+        regions = (
+            Region(name='Region 1', slug='region-1'),
+            Region(name='Region 2', slug='region-2'),
+            Region(name='Region 3', slug='region-3'),
+        )
+        for region in regions:
+            region.save()
+
+        sites = (
+            Site(name='Site 1', slug='site-1', region=regions[0]),
+            Site(name='Site 2', slug='site-2', region=regions[1]),
+            Site(name='Site 3', slug='site-3', region=regions[2]),
+        )
+        Site.objects.bulk_create(sites)
+
+        rack_groups = (
+            RackGroup(name='Rack Group 1', slug='rack-group-1', site=sites[0]),
+            RackGroup(name='Rack Group 2', slug='rack-group-2', site=sites[1]),
+            RackGroup(name='Rack Group 3', slug='rack-group-3', site=sites[2]),
+        )
+        RackGroup.objects.bulk_create(rack_groups)
+
+        racks = (
+            Rack(name='Rack 1', site=sites[0], group=rack_groups[0]),
+            Rack(name='Rack 2', site=sites[1], group=rack_groups[1]),
+            Rack(name='Rack 3', site=sites[2], group=rack_groups[2]),
+        )
+        Rack.objects.bulk_create(racks)
+
+        cluster_type = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1')
+        clusters = (
+            Cluster(name='Cluster 1', type=cluster_type),
+            Cluster(name='Cluster 2', type=cluster_type),
+            Cluster(name='Cluster 3', type=cluster_type),
+        )
+        Cluster.objects.bulk_create(clusters)
+
+        devices = (
+            Device(name='Device 1', device_type=device_types[0], device_role=device_roles[0], platform=platforms[0], serial='ABC', asset_tag='1001', site=sites[0], rack=racks[0], position=1, face=RACK_FACE_FRONT, status=DEVICE_STATUS_ACTIVE, cluster=clusters[0]),
+            Device(name='Device 2', device_type=device_types[1], device_role=device_roles[1], platform=platforms[1], serial='DEF', asset_tag='1002', site=sites[1], rack=racks[1], position=2, face=RACK_FACE_FRONT, status=DEVICE_STATUS_STAGED, cluster=clusters[1]),
+            Device(name='Device 3', device_type=device_types[2], device_role=device_roles[2], platform=platforms[2], serial='GHI', asset_tag='1003', site=sites[2], rack=racks[2], position=3, face=RACK_FACE_REAR, status=DEVICE_STATUS_FAILED, cluster=clusters[2]),
+        )
+        Device.objects.bulk_create(devices)
+
+        # Add components for filtering
+        ConsolePort.objects.bulk_create((
+            ConsolePort(device=devices[0], name='Console Port 1'),
+            ConsolePort(device=devices[1], name='Console Port 2'),
+        ))
+        ConsoleServerPort.objects.bulk_create((
+            ConsoleServerPort(device=devices[0], name='Console Server Port 1'),
+            ConsoleServerPort(device=devices[1], name='Console Server Port 2'),
+        ))
+        PowerPort.objects.bulk_create((
+            PowerPort(device=devices[0], name='Power Port 1'),
+            PowerPort(device=devices[1], name='Power Port 2'),
+        ))
+        PowerOutlet.objects.bulk_create((
+            PowerOutlet(device=devices[0], name='Power Outlet 1'),
+            PowerOutlet(device=devices[1], name='Power Outlet 2'),
+        ))
+        interfaces = (
+            Interface(device=devices[0], name='Interface 1', mac_address='00-00-00-00-00-01'),
+            Interface(device=devices[1], name='Interface 2', mac_address='00-00-00-00-00-02'),
+        )
+        Interface.objects.bulk_create(interfaces)
+        rear_ports = (
+            RearPort(device=devices[0], name='Rear Port 1', type=PORT_TYPE_8P8C),
+            RearPort(device=devices[1], name='Rear Port 2', type=PORT_TYPE_8P8C),
+        )
+        RearPort.objects.bulk_create(rear_ports)
+        FrontPort.objects.bulk_create((
+            FrontPort(device=devices[0], name='Front Port 1', type=PORT_TYPE_8P8C, rear_port=rear_ports[0]),
+            FrontPort(device=devices[1], name='Front Port 2', type=PORT_TYPE_8P8C, rear_port=rear_ports[1]),
+        ))
+        DeviceBay.objects.bulk_create((
+            DeviceBay(device=devices[0], name='Device Bay 1'),
+            DeviceBay(device=devices[1], name='Device Bay 2'),
+        ))
+
+        # Assign primary IPs for filtering
+        ipaddresses = (
+            IPAddress(family=4, address='192.0.2.1/24', interface=interfaces[0]),
+            IPAddress(family=4, address='192.0.2.2/24', interface=interfaces[1]),
+        )
+        IPAddress.objects.bulk_create(ipaddresses)
+        Device.objects.filter(pk=devices[0].pk).update(primary_ip4=ipaddresses[0])
+        Device.objects.filter(pk=devices[1].pk).update(primary_ip4=ipaddresses[1])
+
+        # VirtualChassis assignment for filtering
+        virtual_chassis = VirtualChassis.objects.create(master=devices[0])
+        Device.objects.filter(pk=devices[0].pk).update(virtual_chassis=virtual_chassis, vc_position=1, vc_priority=1)
+        Device.objects.filter(pk=devices[1].pk).update(virtual_chassis=virtual_chassis, vc_position=2, vc_priority=2)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Device 1', 'Device 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_asset_tag(self):
+        params = {'asset_tag': ['1001', '1002']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_face(self):
+        params = {'face': RACK_FACE_FRONT}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_position(self):
+        params = {'position': [1, 2]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_vc_position(self):
+        params = {'vc_position': [1, 2]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_vc_priority(self):
+        params = {'vc_priority': [1, 2]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_id__in(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id__in': ','.join([str(id) for id in id_list])}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_manufacturer(self):
+        manufacturers = Manufacturer.objects.all()[:2]
+        params = {'manufacturer_id': [manufacturers[0].pk, manufacturers[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'manufacturer': [manufacturers[0].slug, manufacturers[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_devicetype(self):
+        device_types = DeviceType.objects.all()[:2]
+        params = {'device_type_id': [device_types[0].pk, device_types[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_devicerole(self):
+        device_roles = DeviceRole.objects.all()[:2]
+        params = {'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]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_platform(self):
+        platforms = Platform.objects.all()[:2]
+        params = {'platform_id': [platforms[0].pk, platforms[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'platform': [platforms[0].slug, platforms[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_region(self):
+        regions = Region.objects.all()[:2]
+        params = {'region_id': [regions[0].pk, regions[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'region': [regions[0].slug, regions[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_site(self):
+        sites = Site.objects.all()[:2]
+        params = {'site_id': [sites[0].pk, sites[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'site': [sites[0].slug, sites[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_rackgroup(self):
+        rack_groups = RackGroup.objects.all()[:2]
+        params = {'rack_group_id': [rack_groups[0].pk, rack_groups[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_rack(self):
+        racks = Rack.objects.all()[:2]
+        params = {'rack_id': [racks[0].pk, racks[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_cluster(self):
+        clusters = Cluster.objects.all()[:2]
+        params = {'cluster_id': [clusters[0].pk, clusters[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_model(self):
+        params = {'model': ['model-1', 'model-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_status(self):
+        params = {'status': [DEVICE_STATUS_ACTIVE, DEVICE_STATUS_STAGED]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_is_full_depth(self):
+        params = {'is_full_depth': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'is_full_depth': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_mac_address(self):
+        params = {'mac_address': ['00-00-00-00-00-01', '00-00-00-00-00-02']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_serial(self):
+        params = {'serial': 'ABC'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'serial': 'abc'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_has_primary_ip(self):
+        params = {'has_primary_ip': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'has_primary_ip': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_virtual_chassis_id(self):
+        params = {'virtual_chassis_id': [VirtualChassis.objects.first().pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_virtual_chassis_member(self):
+        params = {'virtual_chassis_member': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'virtual_chassis_member': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_console_ports(self):
+        params = {'console_ports': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'console_ports': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_console_server_ports(self):
+        params = {'console_server_ports': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'console_server_ports': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_power_ports(self):
+        params = {'power_ports': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'power_ports': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_power_outlets(self):
+        params = {'power_outlets': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'power_outlets': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_interfaces(self):
+        params = {'interfaces': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'interfaces': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_pass_through_ports(self):
+        params = {'pass_through_ports': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'pass_through_ports': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    # TODO: Add device_bay filter
+    # def test_device_bays(self):
+    #     params = {'device_bays': 'true'}
+    #     self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+    #     params = {'device_bays': 'false'}
+    #     self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+
+class ConsolePortTestCase(TestCase):
+    queryset = ConsolePort.objects.all()
+    filterset = ConsolePortFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        site = Site.objects.create(name='Site 1', slug='site1')
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Model 1', slug='model-1')
+        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        devices = (
+            Device(name='Device 1', device_type=device_type, device_role=device_role, site=site),
+            Device(name='Device 2', device_type=device_type, device_role=device_role, site=site),
+            Device(name='Device 3', device_type=device_type, device_role=device_role, site=site),
+            Device(name=None, device_type=device_type, device_role=device_role, site=site),  # For cable connections
+        )
+        Device.objects.bulk_create(devices)
+
+        console_server_ports = (
+            ConsoleServerPort(device=devices[3], name='Console Server Port 1'),
+            ConsoleServerPort(device=devices[3], name='Console Server Port 2'),
+        )
+        ConsoleServerPort.objects.bulk_create(console_server_ports)
+
+        console_ports = (
+            ConsolePort(device=devices[0], name='Console Port 1', description='First'),
+            ConsolePort(device=devices[1], name='Console Port 2', description='Second'),
+            ConsolePort(device=devices[2], name='Console Port 3', description='Third'),
+        )
+        ConsolePort.objects.bulk_create(console_ports)
+
+        # Cables
+        Cable(termination_a=console_ports[0], termination_b=console_server_ports[0]).save()
+        Cable(termination_a=console_ports[1], termination_b=console_server_ports[1]).save()
+        # Third port is not connected
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Console Port 1', 'Console Port 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_description(self):
+        params = {'description': ['First', 'Second']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    # TODO: Fix boolean value
+    def test_connection_status(self):
+        params = {'connection_status': 'True'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_device(self):
+        devices = Device.objects.all()[:2]
+        params = {'device_id': [devices[0].pk, devices[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'device': [devices[0].name, devices[1].name]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_cabled(self):
+        params = {'cabled': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'cabled': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+
+class ConsoleServerPortTestCase(TestCase):
+    queryset = ConsoleServerPort.objects.all()
+    filterset = ConsoleServerPortFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        site = Site.objects.create(name='Site 1', slug='site1')
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Model 1', slug='model-1')
+        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        devices = (
+            Device(name='Device 1', device_type=device_type, device_role=device_role, site=site),
+            Device(name='Device 2', device_type=device_type, device_role=device_role, site=site),
+            Device(name='Device 3', device_type=device_type, device_role=device_role, site=site),
+            Device(name=None, device_type=device_type, device_role=device_role, site=site),  # For cable connections
+        )
+        Device.objects.bulk_create(devices)
+
+        console_ports = (
+            ConsolePort(device=devices[3], name='Console Server Port 1'),
+            ConsolePort(device=devices[3], name='Console Server Port 2'),
+        )
+        ConsolePort.objects.bulk_create(console_ports)
+
+        console_server_ports = (
+            ConsoleServerPort(device=devices[0], name='Console Server Port 1', description='First'),
+            ConsoleServerPort(device=devices[1], name='Console Server Port 2', description='Second'),
+            ConsoleServerPort(device=devices[2], name='Console Server Port 3', description='Third'),
+        )
+        ConsoleServerPort.objects.bulk_create(console_server_ports)
+
+        # Cables
+        Cable(termination_a=console_server_ports[0], termination_b=console_ports[0]).save()
+        Cable(termination_a=console_server_ports[1], termination_b=console_ports[1]).save()
+        # Third port is not connected
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Console Server Port 1', 'Console Server Port 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_description(self):
+        params = {'description': ['First', 'Second']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    # TODO: Fix boolean value
+    def test_connection_status(self):
+        params = {'connection_status': 'True'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_device(self):
+        devices = Device.objects.all()[:2]
+        params = {'device_id': [devices[0].pk, devices[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'device': [devices[0].name, devices[1].name]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_cabled(self):
+        params = {'cabled': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'cabled': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+
+class PowerPortTestCase(TestCase):
+    queryset = PowerPort.objects.all()
+    filterset = PowerPortFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        site = Site.objects.create(name='Site 1', slug='site1')
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Model 1', slug='model-1')
+        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        devices = (
+            Device(name='Device 1', device_type=device_type, device_role=device_role, site=site),
+            Device(name='Device 2', device_type=device_type, device_role=device_role, site=site),
+            Device(name='Device 3', device_type=device_type, device_role=device_role, site=site),
+            Device(name=None, device_type=device_type, device_role=device_role, site=site),  # For cable connections
+        )
+        Device.objects.bulk_create(devices)
+
+        power_outlets = (
+            PowerOutlet(device=devices[3], name='Power Outlet 1'),
+            PowerOutlet(device=devices[3], name='Power Outlet 2'),
+        )
+        PowerOutlet.objects.bulk_create(power_outlets)
+
+        power_ports = (
+            PowerPort(device=devices[0], name='Power Port 1', maximum_draw=100, allocated_draw=50, description='First'),
+            PowerPort(device=devices[1], name='Power Port 2', maximum_draw=200, allocated_draw=100, description='Second'),
+            PowerPort(device=devices[2], name='Power Port 3', maximum_draw=300, allocated_draw=150, description='Third'),
+        )
+        PowerPort.objects.bulk_create(power_ports)
+
+        # Cables
+        Cable(termination_a=power_ports[0], termination_b=power_outlets[0]).save()
+        Cable(termination_a=power_ports[1], termination_b=power_outlets[1]).save()
+        # Third port is not connected
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Power Port 1', 'Power Port 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_description(self):
+        params = {'description': ['First', 'Second']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_maximum_draw(self):
+        params = {'maximum_draw': [100, 200]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_allocated_draw(self):
+        params = {'allocated_draw': [50, 100]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    # TODO: Fix boolean value
+    def test_connection_status(self):
+        params = {'connection_status': 'True'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_device(self):
+        devices = Device.objects.all()[:2]
+        params = {'device_id': [devices[0].pk, devices[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'device': [devices[0].name, devices[1].name]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_cabled(self):
+        params = {'cabled': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'cabled': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+
+class PowerOutletTestCase(TestCase):
+    queryset = PowerOutlet.objects.all()
+    filterset = PowerOutletFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        site = Site.objects.create(name='Site 1', slug='site1')
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Model 1', slug='model-1')
+        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        devices = (
+            Device(name='Device 1', device_type=device_type, device_role=device_role, site=site),
+            Device(name='Device 2', device_type=device_type, device_role=device_role, site=site),
+            Device(name='Device 3', device_type=device_type, device_role=device_role, site=site),
+            Device(name=None, device_type=device_type, device_role=device_role, site=site),  # For cable connections
+        )
+        Device.objects.bulk_create(devices)
+
+        power_ports = (
+            PowerPort(device=devices[3], name='Power Outlet 1'),
+            PowerPort(device=devices[3], name='Power Outlet 2'),
+        )
+        PowerPort.objects.bulk_create(power_ports)
+
+        power_outlets = (
+            PowerOutlet(device=devices[0], name='Power Outlet 1', feed_leg=POWERFEED_LEG_A, description='First'),
+            PowerOutlet(device=devices[1], name='Power Outlet 2', feed_leg=POWERFEED_LEG_B, description='Second'),
+            PowerOutlet(device=devices[2], name='Power Outlet 3', feed_leg=POWERFEED_LEG_C, description='Third'),
+        )
+        PowerOutlet.objects.bulk_create(power_outlets)
+
+        # Cables
+        Cable(termination_a=power_outlets[0], termination_b=power_ports[0]).save()
+        Cable(termination_a=power_outlets[1], termination_b=power_ports[1]).save()
+        # Third port is not connected
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Power Outlet 1', 'Power Outlet 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_description(self):
+        params = {'description': ['First', 'Second']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_feed_leg(self):
+        # TODO: Support filtering for multiple values
+        params = {'feed_leg': POWERFEED_LEG_A}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    # TODO: Fix boolean value
+    def test_connection_status(self):
+        params = {'connection_status': 'True'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_device(self):
+        devices = Device.objects.all()[:2]
+        params = {'device_id': [devices[0].pk, devices[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'device': [devices[0].name, devices[1].name]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_cabled(self):
+        params = {'cabled': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'cabled': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+
+class InterfaceTestCase(TestCase):
+    queryset = Interface.objects.all()
+    filterset = InterfaceFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        site = Site.objects.create(name='Site 1', slug='site1')
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Model 1', slug='model-1')
+        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        devices = (
+            Device(name='Device 1', device_type=device_type, device_role=device_role, site=site),
+            Device(name='Device 2', device_type=device_type, device_role=device_role, site=site),
+            Device(name='Device 3', device_type=device_type, device_role=device_role, site=site),
+            Device(name=None, device_type=device_type, device_role=device_role, site=site),  # For cable connections
+        )
+        Device.objects.bulk_create(devices)
+
+        interfaces = (
+            Interface(device=devices[0], name='Interface 1', type=IFACE_TYPE_1GE_SFP, enabled=True, mgmt_only=True, mtu=100, mode=IFACE_MODE_ACCESS, mac_address='00-00-00-00-00-01', description='First'),
+            Interface(device=devices[1], name='Interface 2', type=IFACE_TYPE_1GE_GBIC, enabled=True, mgmt_only=True, mtu=200, mode=IFACE_MODE_TAGGED, mac_address='00-00-00-00-00-02', description='Second'),
+            Interface(device=devices[2], name='Interface 3', type=IFACE_TYPE_1GE_FIXED, enabled=False, mgmt_only=False, mtu=300, mode=IFACE_MODE_TAGGED_ALL, mac_address='00-00-00-00-00-03', description='Third'),
+            Interface(device=devices[3], name='Interface 4', type=IFACE_TYPE_OTHER, enabled=True, mgmt_only=True),
+            Interface(device=devices[3], name='Interface 5', type=IFACE_TYPE_OTHER, enabled=True, mgmt_only=True),
+            Interface(device=devices[3], name='Interface 6', type=IFACE_TYPE_OTHER, enabled=False, mgmt_only=False),
+        )
+        Interface.objects.bulk_create(interfaces)
+
+        # Cables
+        Cable(termination_a=interfaces[0], termination_b=interfaces[3]).save()
+        Cable(termination_a=interfaces[1], termination_b=interfaces[4]).save()
+        # Third pair is not connected
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:3]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+    def test_name(self):
+        params = {'name': ['Interface 1', 'Interface 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    # TODO: Fix boolean value
+    def test_connection_status(self):
+        params = {'connection_status': 'True'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_enabled(self):
+        params = {'enabled': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'enabled': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_mtu(self):
+        params = {'mtu': [100, 200]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_mgmt_only(self):
+        params = {'mgmt_only': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'mgmt_only': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_mode(self):
+        params = {'mode': IFACE_MODE_ACCESS}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_description(self):
+        params = {'description': ['First', 'Second']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_device(self):
+        devices = Device.objects.all()[:2]
+        params = {'device_id': [devices[0].pk, devices[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'device': [devices[0].name, devices[1].name]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_cabled(self):
+        params = {'cabled': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'cabled': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_kind(self):
+        params = {'kind': 'physical'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6)
+        params = {'kind': 'virtual'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0)
+
+    def test_mac_address(self):
+        params = {'mac_address': ['00-00-00-00-00-01', '00-00-00-00-00-02']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_type(self):
+        params = {'type': [IFACE_TYPE_1GE_FIXED, IFACE_TYPE_1GE_GBIC]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class FrontPortTestCase(TestCase):
+    queryset = FrontPort.objects.all()
+    filterset = FrontPortFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        site = Site.objects.create(name='Site 1', slug='site1')
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Model 1', slug='model-1')
+        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        devices = (
+            Device(name='Device 1', device_type=device_type, device_role=device_role, site=site),
+            Device(name='Device 2', device_type=device_type, device_role=device_role, site=site),
+            Device(name='Device 3', device_type=device_type, device_role=device_role, site=site),
+            Device(name=None, device_type=device_type, device_role=device_role, site=site),  # For cable connections
+        )
+        Device.objects.bulk_create(devices)
+
+        rear_ports = (
+            RearPort(device=devices[0], name='Rear Port 1', type=PORT_TYPE_8P8C, positions=6),
+            RearPort(device=devices[1], name='Rear Port 2', type=PORT_TYPE_8P8C, positions=6),
+            RearPort(device=devices[2], name='Rear Port 3', type=PORT_TYPE_8P8C, positions=6),
+            RearPort(device=devices[3], name='Rear Port 4', type=PORT_TYPE_8P8C, positions=6),
+            RearPort(device=devices[3], name='Rear Port 5', type=PORT_TYPE_8P8C, positions=6),
+            RearPort(device=devices[3], name='Rear Port 6', type=PORT_TYPE_8P8C, positions=6),
+        )
+        RearPort.objects.bulk_create(rear_ports)
+
+        front_ports = (
+            FrontPort(device=devices[0], name='Front Port 1', type=PORT_TYPE_8P8C, rear_port=rear_ports[0], rear_port_position=1, description='First'),
+            FrontPort(device=devices[1], name='Front Port 2', type=PORT_TYPE_110_PUNCH, rear_port=rear_ports[1], rear_port_position=2, description='Second'),
+            FrontPort(device=devices[2], name='Front Port 3', type=PORT_TYPE_BNC, rear_port=rear_ports[2], rear_port_position=3, description='Third'),
+            FrontPort(device=devices[3], name='Front Port 4', type=PORT_TYPE_FC, rear_port=rear_ports[3], rear_port_position=1),
+            FrontPort(device=devices[3], name='Front Port 5', type=PORT_TYPE_FC, rear_port=rear_ports[4], rear_port_position=1),
+            FrontPort(device=devices[3], name='Front Port 6', type=PORT_TYPE_FC, rear_port=rear_ports[5], rear_port_position=1),
+        )
+        FrontPort.objects.bulk_create(front_ports)
+
+        # Cables
+        Cable(termination_a=front_ports[0], termination_b=front_ports[3]).save()
+        Cable(termination_a=front_ports[1], termination_b=front_ports[4]).save()
+        # Third port is not connected
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Front Port 1', 'Front Port 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_type(self):
+        # TODO: Test for multiple values
+        params = {'type': PORT_TYPE_8P8C}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_description(self):
+        params = {'description': ['First', 'Second']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_device(self):
+        devices = Device.objects.all()[:2]
+        params = {'device_id': [devices[0].pk, devices[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'device': [devices[0].name, devices[1].name]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_cabled(self):
+        params = {'cabled': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'cabled': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class RearPortTestCase(TestCase):
+    queryset = RearPort.objects.all()
+    filterset = RearPortFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        site = Site.objects.create(name='Site 1', slug='site1')
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Model 1', slug='model-1')
+        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        devices = (
+            Device(name='Device 1', device_type=device_type, device_role=device_role, site=site),
+            Device(name='Device 2', device_type=device_type, device_role=device_role, site=site),
+            Device(name='Device 3', device_type=device_type, device_role=device_role, site=site),
+            Device(name=None, device_type=device_type, device_role=device_role, site=site),  # For cable connections
+        )
+        Device.objects.bulk_create(devices)
+
+        rear_ports = (
+            RearPort(device=devices[0], name='Rear Port 1', type=PORT_TYPE_8P8C, positions=1, description='First'),
+            RearPort(device=devices[1], name='Rear Port 2', type=PORT_TYPE_110_PUNCH, positions=2, description='Second'),
+            RearPort(device=devices[2], name='Rear Port 3', type=PORT_TYPE_BNC, positions=3, description='Third'),
+            RearPort(device=devices[3], name='Rear Port 4', type=PORT_TYPE_FC, positions=4),
+            RearPort(device=devices[3], name='Rear Port 5', type=PORT_TYPE_FC, positions=5),
+            RearPort(device=devices[3], name='Rear Port 6', type=PORT_TYPE_FC, positions=6),
+        )
+        RearPort.objects.bulk_create(rear_ports)
+
+        # Cables
+        Cable(termination_a=rear_ports[0], termination_b=rear_ports[3]).save()
+        Cable(termination_a=rear_ports[1], termination_b=rear_ports[4]).save()
+        # Third port is not connected
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Rear Port 1', 'Rear Port 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_type(self):
+        # TODO: Test for multiple values
+        params = {'type': PORT_TYPE_8P8C}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_positions(self):
+        params = {'positions': [1, 2]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_description(self):
+        params = {'description': ['First', 'Second']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_device(self):
+        devices = Device.objects.all()[:2]
+        params = {'device_id': [devices[0].pk, devices[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'device': [devices[0].name, devices[1].name]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_cabled(self):
+        params = {'cabled': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'cabled': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class DeviceBayTestCase(TestCase):
+    queryset = DeviceBay.objects.all()
+    filterset = DeviceBayFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        site = Site.objects.create(name='Site 1', slug='site1')
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Model 1', slug='model-1')
+        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        devices = (
+            Device(name='Device 1', device_type=device_type, device_role=device_role, site=site),
+            Device(name='Device 2', device_type=device_type, device_role=device_role, site=site),
+            Device(name='Device 3', device_type=device_type, device_role=device_role, site=site),
+        )
+        Device.objects.bulk_create(devices)
+
+        device_bays = (
+            DeviceBay(device=devices[0], name='Device Bay 1', description='First'),
+            DeviceBay(device=devices[1], name='Device Bay 2', description='Second'),
+            DeviceBay(device=devices[2], name='Device Bay 3', description='Third'),
+        )
+        DeviceBay.objects.bulk_create(device_bays)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Device Bay 1', 'Device Bay 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_description(self):
+        params = {'description': ['First', 'Second']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_device(self):
+        devices = Device.objects.all()[:2]
+        params = {'device_id': [devices[0].pk, devices[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'device': [devices[0].name, devices[1].name]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class InventoryItemTestCase(TestCase):
+    queryset = InventoryItem.objects.all()
+    filterset = InventoryItemFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        manufacturers = (
+            Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
+            Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
+            Manufacturer(name='Manufacturer 3', slug='manufacturer-3'),
+        )
+        Manufacturer.objects.bulk_create(manufacturers)
+
+        device_type = DeviceType.objects.create(manufacturer=manufacturers[0], model='Model 1', slug='model-1')
+        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        regions = (
+            Region(name='Region 1', slug='region-1'),
+            Region(name='Region 2', slug='region-2'),
+            Region(name='Region 3', slug='region-3'),
+        )
+        for region in regions:
+            region.save()
+
+        sites = (
+            Site(name='Site 1', slug='site-1', region=regions[0]),
+            Site(name='Site 2', slug='site-2', region=regions[1]),
+            Site(name='Site 3', slug='site-3', region=regions[2]),
+        )
+        Site.objects.bulk_create(sites)
+
+        devices = (
+            Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0]),
+            Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[1]),
+            Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[2]),
+        )
+        Device.objects.bulk_create(devices)
+
+        inventory_items = (
+            InventoryItem(device=devices[0], manufacturer=manufacturers[0], name='Inventory Item 1', part_id='1001', serial='ABC', asset_tag='1001', discovered=True, description='First'),
+            InventoryItem(device=devices[1], manufacturer=manufacturers[1], name='Inventory Item 2', part_id='1002', serial='DEF', asset_tag='1002', discovered=True, description='Second'),
+            InventoryItem(device=devices[2], manufacturer=manufacturers[2], name='Inventory Item 3', part_id='1003', serial='GHI', asset_tag='1003', discovered=False, description='Third'),
+        )
+        InventoryItem.objects.bulk_create(inventory_items)
+
+        child_inventory_items = (
+            InventoryItem(device=devices[0], name='Inventory Item 1A', parent=inventory_items[0]),
+            InventoryItem(device=devices[1], name='Inventory Item 2A', parent=inventory_items[1]),
+            InventoryItem(device=devices[2], name='Inventory Item 3A', parent=inventory_items[2]),
+        )
+        InventoryItem.objects.bulk_create(child_inventory_items)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Inventory Item 1', 'Inventory Item 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_part_id(self):
+        params = {'part_id': ['1001', '1002']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_asset_tag(self):
+        params = {'asset_tag': ['1001', '1002']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_discovered(self):
+        # TODO: Fix boolean value
+        params = {'discovered': True}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'discovered': False}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_region(self):
+        regions = Region.objects.all()[:2]
+        params = {'region_id': [regions[0].pk, regions[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'region': [regions[0].slug, regions[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_site(self):
+        sites = Site.objects.all()[:2]
+        params = {'site_id': [sites[0].pk, sites[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'site': [sites[0].slug, sites[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_device(self):
+        # TODO: Allow multiple values
+        device = Device.objects.first()
+        params = {'device_id': device.pk}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'device': device.name}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_parent_id(self):
+        parent_items = InventoryItem.objects.filter(parent__isnull=True)[:2]
+        params = {'parent_id': [parent_items[0].pk, parent_items[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_manufacturer(self):
+        manufacturers = Manufacturer.objects.all()[:2]
+        params = {'manufacturer_id': [manufacturers[0].pk, manufacturers[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'manufacturer': [manufacturers[0].slug, manufacturers[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_serial(self):
+        params = {'serial': 'ABC'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'serial': 'abc'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+
+class VirtualChassisTestCase(TestCase):
+    queryset = VirtualChassis.objects.all()
+    filterset = VirtualChassisFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Model 1', slug='model-1')
+        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        regions = (
+            Region(name='Region 1', slug='region-1'),
+            Region(name='Region 2', slug='region-2'),
+            Region(name='Region 3', slug='region-3'),
+        )
+        for region in regions:
+            region.save()
+
+        sites = (
+            Site(name='Site 1', slug='site-1', region=regions[0]),
+            Site(name='Site 2', slug='site-2', region=regions[1]),
+            Site(name='Site 3', slug='site-3', region=regions[2]),
+        )
+        Site.objects.bulk_create(sites)
+
+        devices = (
+            Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0], vc_position=1),
+            Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[0], vc_position=2),
+            Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[1], vc_position=1),
+            Device(name='Device 4', device_type=device_type, device_role=device_role, site=sites[1], vc_position=2),
+            Device(name='Device 5', device_type=device_type, device_role=device_role, site=sites[2], vc_position=1),
+            Device(name='Device 6', device_type=device_type, device_role=device_role, site=sites[2], vc_position=2),
+        )
+        Device.objects.bulk_create(devices)
+
+        virtual_chassis = (
+            VirtualChassis(master=devices[0], domain='Domain 1'),
+            VirtualChassis(master=devices[2], domain='Domain 2'),
+            VirtualChassis(master=devices[4], domain='Domain 3'),
+        )
+        VirtualChassis.objects.bulk_create(virtual_chassis)
+
+        Device.objects.filter(pk=devices[1].pk).update(virtual_chassis=virtual_chassis[0])
+        Device.objects.filter(pk=devices[3].pk).update(virtual_chassis=virtual_chassis[1])
+        Device.objects.filter(pk=devices[5].pk).update(virtual_chassis=virtual_chassis[2])
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_domain(self):
+        params = {'domain': ['Domain 1', 'Domain 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_region(self):
+        regions = Region.objects.all()[:2]
+        params = {'region_id': [regions[0].pk, regions[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'region': [regions[0].slug, regions[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_site(self):
+        sites = Site.objects.all()[:2]
+        params = {'site_id': [sites[0].pk, sites[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'site': [sites[0].slug, sites[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class CableTestCase(TestCase):
+    queryset = Cable.objects.all()
+    filterset = CableFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        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)
+
+        racks = (
+            Rack(name='Rack 1', site=sites[0]),
+            Rack(name='Rack 2', site=sites[1]),
+            Rack(name='Rack 3', site=sites[2]),
+        )
+        Rack.objects.bulk_create(racks)
+
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Model 1', slug='model-1')
+        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        devices = (
+            Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0], rack=racks[0], position=1),
+            Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[0], rack=racks[0], position=2),
+            Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[1], rack=racks[1], position=1),
+            Device(name='Device 4', device_type=device_type, device_role=device_role, site=sites[1], rack=racks[1], position=2),
+            Device(name='Device 5', device_type=device_type, device_role=device_role, site=sites[2], rack=racks[2], position=1),
+            Device(name='Device 6', device_type=device_type, device_role=device_role, site=sites[2], rack=racks[2], position=2),
+        )
+        Device.objects.bulk_create(devices)
+
+        interfaces = (
+            Interface(device=devices[0], name='Interface 1', type=IFACE_TYPE_1GE_FIXED),
+            Interface(device=devices[0], name='Interface 2', type=IFACE_TYPE_1GE_FIXED),
+            Interface(device=devices[1], name='Interface 3', type=IFACE_TYPE_1GE_FIXED),
+            Interface(device=devices[1], name='Interface 4', type=IFACE_TYPE_1GE_FIXED),
+            Interface(device=devices[2], name='Interface 5', type=IFACE_TYPE_1GE_FIXED),
+            Interface(device=devices[2], name='Interface 6', type=IFACE_TYPE_1GE_FIXED),
+            Interface(device=devices[3], name='Interface 7', type=IFACE_TYPE_1GE_FIXED),
+            Interface(device=devices[3], name='Interface 8', type=IFACE_TYPE_1GE_FIXED),
+            Interface(device=devices[4], name='Interface 9', type=IFACE_TYPE_1GE_FIXED),
+            Interface(device=devices[4], name='Interface 10', type=IFACE_TYPE_1GE_FIXED),
+            Interface(device=devices[5], name='Interface 11', type=IFACE_TYPE_1GE_FIXED),
+            Interface(device=devices[5], name='Interface 12', type=IFACE_TYPE_1GE_FIXED),
+        )
+        Interface.objects.bulk_create(interfaces)
+
+        # Cables
+        Cable(termination_a=interfaces[1], termination_b=interfaces[2], label='Cable 1', type=CABLE_TYPE_CAT3, status=CONNECTION_STATUS_CONNECTED, color='aa1409', length=10, length_unit=LENGTH_UNIT_FOOT).save()
+        Cable(termination_a=interfaces[3], termination_b=interfaces[4], label='Cable 2', type=CABLE_TYPE_CAT3, status=CONNECTION_STATUS_CONNECTED, color='aa1409', length=20, length_unit=LENGTH_UNIT_FOOT).save()
+        Cable(termination_a=interfaces[5], termination_b=interfaces[6], label='Cable 3', type=CABLE_TYPE_CAT5E, status=CONNECTION_STATUS_CONNECTED, color='f44336', length=30, length_unit=LENGTH_UNIT_FOOT).save()
+        Cable(termination_a=interfaces[7], termination_b=interfaces[8], label='Cable 4', type=CABLE_TYPE_CAT5E, status=CONNECTION_STATUS_PLANNED, color='f44336', length=40, length_unit=LENGTH_UNIT_FOOT).save()
+        Cable(termination_a=interfaces[9], termination_b=interfaces[10], label='Cable 5', type=CABLE_TYPE_CAT6, status=CONNECTION_STATUS_PLANNED, color='e91e63', length=10, length_unit=LENGTH_UNIT_METER).save()
+        Cable(termination_a=interfaces[11], termination_b=interfaces[0], label='Cable 6', type=CABLE_TYPE_CAT6, status=CONNECTION_STATUS_PLANNED, color='e91e63', length=20, length_unit=LENGTH_UNIT_METER).save()
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_label(self):
+        params = {'label': ['Cable 1', 'Cable 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_length(self):
+        params = {'length': [10, 20]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_length_unit(self):
+        params = {'length_unit': LENGTH_UNIT_FOOT}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_type(self):
+        params = {'type': [CABLE_TYPE_CAT3, CABLE_TYPE_CAT5E]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_status(self):
+        params = {'status': [CONNECTION_STATUS_CONNECTED]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+    def test_color(self):
+        params = {'color': ['aa1409', 'f44336']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_device(self):
+        devices = Device.objects.all()[:2]
+        params = {'device_id': [devices[0].pk, devices[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+        params = {'device': [devices[0].name, devices[1].name]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+    def test_rack(self):
+        racks = Rack.objects.all()[:2]
+        params = {'rack_id': [racks[0].pk, racks[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 5)
+        params = {'rack': [racks[0].name, racks[1].name]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 5)
+
+    def test_site(self):
+        site = Site.objects.all()[:2]
+        params = {'site_id': [site[0].pk, site[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 5)
+        params = {'site': [site[0].slug, site[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 5)
+
+
+class PowerPanelTestCase(TestCase):
+    queryset = PowerPanel.objects.all()
+    filterset = PowerPanelFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        regions = (
+            Region(name='Region 1', slug='region-1'),
+            Region(name='Region 2', slug='region-2'),
+            Region(name='Region 3', slug='region-3'),
+        )
+        for region in regions:
+            region.save()
+
+        sites = (
+            Site(name='Site 1', slug='site-1', region=regions[0]),
+            Site(name='Site 2', slug='site-2', region=regions[1]),
+            Site(name='Site 3', slug='site-3', region=regions[2]),
+        )
+        Site.objects.bulk_create(sites)
+
+        rack_groups = (
+            RackGroup(name='Rack Group 1', slug='rack-group-1', site=sites[0]),
+            RackGroup(name='Rack Group 2', slug='rack-group-2', site=sites[1]),
+            RackGroup(name='Rack Group 3', slug='rack-group-3', site=sites[2]),
+        )
+        RackGroup.objects.bulk_create(rack_groups)
+
+        power_panels = (
+            PowerPanel(name='Power Panel 1', site=sites[0], rack_group=rack_groups[0]),
+            PowerPanel(name='Power Panel 2', site=sites[1], rack_group=rack_groups[1]),
+            PowerPanel(name='Power Panel 3', site=sites[2], rack_group=rack_groups[2]),
+        )
+        PowerPanel.objects.bulk_create(power_panels)
+
+    def test_name(self):
+        params = {'name': ['Power Panel 1', 'Power Panel 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_region(self):
+        regions = Region.objects.all()[:2]
+        params = {'region_id': [regions[0].pk, regions[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'region': [regions[0].slug, regions[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_site(self):
+        sites = Site.objects.all()[:2]
+        params = {'site_id': [sites[0].pk, sites[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'site': [sites[0].slug, sites[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_rack_group(self):
+        rack_groups = RackGroup.objects.all()[:2]
+        params = {'rack_group_id': [rack_groups[0].pk, rack_groups[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class PowerFeedTestCase(TestCase):
+    queryset = PowerFeed.objects.all()
+    filterset = PowerFeedFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        regions = (
+            Region(name='Region 1', slug='region-1'),
+            Region(name='Region 2', slug='region-2'),
+            Region(name='Region 3', slug='region-3'),
+        )
+        for region in regions:
+            region.save()
+
+        sites = (
+            Site(name='Site 1', slug='site-1', region=regions[0]),
+            Site(name='Site 2', slug='site-2', region=regions[1]),
+            Site(name='Site 3', slug='site-3', region=regions[2]),
+        )
+        Site.objects.bulk_create(sites)
+
+        racks = (
+            Rack(name='Rack 1', site=sites[0]),
+            Rack(name='Rack 2', site=sites[1]),
+            Rack(name='Rack 3', site=sites[2]),
+        )
+        Rack.objects.bulk_create(racks)
+
+        power_panels = (
+            PowerPanel(name='Power Panel 1', site=sites[0]),
+            PowerPanel(name='Power Panel 2', site=sites[1]),
+            PowerPanel(name='Power Panel 3', site=sites[2]),
+        )
+        PowerPanel.objects.bulk_create(power_panels)
+
+        power_feeds = (
+            PowerFeed(power_panel=power_panels[0], rack=racks[0], name='Power Feed 1', status=POWERFEED_STATUS_ACTIVE, type=POWERFEED_TYPE_PRIMARY, supply=POWERFEED_SUPPLY_AC, phase=POWERFEED_PHASE_3PHASE, voltage=100, amperage=100, max_utilization=10),
+            PowerFeed(power_panel=power_panels[1], rack=racks[1], name='Power Feed 2', status=POWERFEED_STATUS_FAILED, type=POWERFEED_TYPE_PRIMARY, supply=POWERFEED_SUPPLY_AC, phase=POWERFEED_PHASE_3PHASE, voltage=200, amperage=200, max_utilization=20),
+            PowerFeed(power_panel=power_panels[2], rack=racks[2], name='Power Feed 3', status=POWERFEED_STATUS_OFFLINE, type=POWERFEED_TYPE_REDUNDANT, supply=POWERFEED_SUPPLY_DC, phase=POWERFEED_PHASE_SINGLE, voltage=300, amperage=300, max_utilization=30),
+        )
+        PowerFeed.objects.bulk_create(power_feeds)
+
+    def test_name(self):
+        params = {'name': ['Power Feed 1', 'Power Feed 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_status(self):
+        # TODO: Test for multiple values
+        params = {'status': POWERFEED_STATUS_ACTIVE}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_type(self):
+        params = {'type': POWERFEED_TYPE_PRIMARY}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_supply(self):
+        params = {'supply': POWERFEED_SUPPLY_AC}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_phase(self):
+        params = {'phase': POWERFEED_PHASE_3PHASE}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_voltage(self):
+        params = {'voltage': [100, 200]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_amperage(self):
+        params = {'amperage': [100, 200]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_max_utilization(self):
+        params = {'max_utilization': [10, 20]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_region(self):
+        regions = Region.objects.all()[:2]
+        params = {'region_id': [regions[0].pk, regions[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'region': [regions[0].slug, regions[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_site(self):
+        sites = Site.objects.all()[:2]
+        params = {'site_id': [sites[0].pk, sites[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'site': [sites[0].slug, sites[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_power_panel_id(self):
+        power_panels = PowerPanel.objects.all()[:2]
+        params = {'power_panel_id': [power_panels[0].pk, power_panels[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_rack_id(self):
+        racks = Rack.objects.all()[:2]
+        params = {'rack_id': [racks[0].pk, racks[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+# TODO: Connection filters

+ 14 - 0
netbox/extras/filters.py

@@ -8,6 +8,20 @@ from .constants import *
 from .models import ConfigContext, CustomField, Graph, ExportTemplate, ObjectChange, Tag, TopologyMap
 
 
+__all__ = (
+    'ConfigContextFilter',
+    'CreatedUpdatedFilterSet',
+    'CustomFieldFilter',
+    'CustomFieldFilterSet',
+    'ExportTemplateFilter',
+    'GraphFilter',
+    'LocalConfigContextFilter',
+    'ObjectChangeFilter',
+    'TagFilter',
+    'TopologyMapFilter',
+)
+
+
 class CustomFieldFilter(django_filters.Filter):
     """
     Filter objects by the presence of a CustomFieldValue. The filter's name is used as the CustomField name.

+ 2 - 2
netbox/extras/scripts.py

@@ -263,12 +263,12 @@ class BaseScript:
     def run(self, data):
         raise NotImplementedError("The script must define a run() method.")
 
-    def as_form(self, data=None, files=None):
+    def as_form(self, data=None, files=None, initial=None):
         """
         Return a Django form suitable for populating the context data required to run this Script.
         """
         vars = self._get_vars()
-        form = ScriptForm(vars, data, files, commit_default=getattr(self.Meta, 'commit_default', True))
+        form = ScriptForm(vars, data, files, initial=initial, commit_default=getattr(self.Meta, 'commit_default', True))
 
         return form
 

+ 2 - 1
netbox/extras/templatetags/custom_links.py

@@ -68,8 +68,9 @@ def custom_links(obj):
                 text_rendered = render_jinja2(cl.text, context)
                 if text_rendered:
                     link_target = ' target="_blank"' if cl.new_window else ''
+                    link_rendered = render_jinja2(cl.url, context)
                     links_rendered.append(
-                        GROUP_LINK.format(cl.url, link_target, cl.text)
+                        GROUP_LINK.format(link_rendered, link_target, text_rendered)
                     )
             except Exception as e:
                 links_rendered.append(

+ 181 - 0
netbox/extras/tests/test_filters.py

@@ -0,0 +1,181 @@
+from django.contrib.contenttypes.models import ContentType
+from django.test import TestCase
+
+from dcim.models import DeviceRole, Platform, Region, Site
+from extras.constants import *
+from extras.filters import *
+from extras.models import ConfigContext, ExportTemplate, Graph
+from tenancy.models import Tenant, TenantGroup
+
+
+class GraphTestCase(TestCase):
+    queryset = Graph.objects.all()
+    filterset = GraphFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        graphs = (
+            Graph(name='Graph 1', type=GRAPH_TYPE_DEVICE, source='http://example.com/1'),
+            Graph(name='Graph 2', type=GRAPH_TYPE_INTERFACE, source='http://example.com/2'),
+            Graph(name='Graph 3', type=GRAPH_TYPE_SITE, source='http://example.com/3'),
+        )
+        Graph.objects.bulk_create(graphs)
+
+    def test_name(self):
+        params = {'name': 'Graph 1'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_type(self):
+        params = {'type': GRAPH_TYPE_DEVICE}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+
+class ExportTemplateTestCase(TestCase):
+    queryset = ExportTemplate.objects.all()
+    filterset = ExportTemplateFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        content_types = ContentType.objects.filter(model__in=['site', 'rack', 'device'])
+
+        export_templates = (
+            ExportTemplate(name='Export Template 1', content_type=content_types[0], template_language=TEMPLATE_LANGUAGE_DJANGO, template_code='TESTING'),
+            ExportTemplate(name='Export Template 2', content_type=content_types[1], template_language=TEMPLATE_LANGUAGE_JINJA2, template_code='TESTING'),
+            ExportTemplate(name='Export Template 3', content_type=content_types[2], template_language=TEMPLATE_LANGUAGE_JINJA2, template_code='TESTING'),
+        )
+        ExportTemplate.objects.bulk_create(export_templates)
+
+    def test_name(self):
+        params = {'name': 'Export Template 1'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_content_type(self):
+        params = {'content_type': ContentType.objects.get(model='site').pk}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_template_language(self):
+        params = {'template_language': TEMPLATE_LANGUAGE_JINJA2}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class ConfigContextTestCase(TestCase):
+    queryset = ConfigContext.objects.all()
+    filterset = ConfigContextFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        regions = (
+            Region(name='Test Region 1', slug='test-region-1'),
+            Region(name='Test Region 2', slug='test-region-2'),
+            Region(name='Test Region 3', slug='test-region-3'),
+        )
+        # Can't use bulk_create for models with MPTT fields
+        for r in regions:
+            r.save()
+
+        sites = (
+            Site(name='Test Site 1', slug='test-site-1'),
+            Site(name='Test Site 2', slug='test-site-2'),
+            Site(name='Test Site 3', slug='test-site-3'),
+        )
+        Site.objects.bulk_create(sites)
+
+        device_roles = (
+            DeviceRole(name='Device Role 1', slug='device-role-1'),
+            DeviceRole(name='Device Role 2', slug='device-role-2'),
+            DeviceRole(name='Device Role 3', slug='device-role-3'),
+        )
+        DeviceRole.objects.bulk_create(device_roles)
+
+        platforms = (
+            Platform(name='Platform 1', slug='platform-1'),
+            Platform(name='Platform 2', slug='platform-2'),
+            Platform(name='Platform 3', slug='platform-3'),
+        )
+        Platform.objects.bulk_create(platforms)
+
+        tenant_groups = (
+            TenantGroup(name='Tenant Group 1', slug='tenant-group-1'),
+            TenantGroup(name='Tenant Group 2', slug='tenant-group-2'),
+            TenantGroup(name='Tenant Group 3', slug='tenant-group-3'),
+        )
+        TenantGroup.objects.bulk_create(tenant_groups)
+
+        tenants = (
+            Tenant(name='Tenant 1', slug='tenant-1'),
+            Tenant(name='Tenant 2', slug='tenant-2'),
+            Tenant(name='Tenant 3', slug='tenant-3'),
+        )
+        Tenant.objects.bulk_create(tenants)
+
+        for i in range(0, 3):
+            is_active = bool(i % 2)
+            c = ConfigContext.objects.create(
+                name='Config Context {}'.format(i + 1),
+                is_active=is_active,
+                data='{"foo": 123}'
+            )
+            c.regions.set([regions[i]])
+            c.sites.set([sites[i]])
+            c.roles.set([device_roles[i]])
+            c.platforms.set([platforms[i]])
+            c.tenant_groups.set([tenant_groups[i]])
+            c.tenants.set([tenants[i]])
+
+    def test_name(self):
+        params = {'name': 'Config Context 1'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_is_active(self):
+        params = {'is_active': True}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'is_active': False}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_region(self):
+        regions = Region.objects.all()[:2]
+        params = {'region_id': [regions[0].pk, regions[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'region': [regions[0].slug, regions[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_site(self):
+        sites = Site.objects.all()[:2]
+        params = {'site_id': [sites[0].pk, sites[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'site': [sites[0].slug, sites[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_role(self):
+        device_roles = DeviceRole.objects.all()[:2]
+        params = {'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]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_platform(self):
+        platforms = Platform.objects.all()[:2]
+        params = {'platform_id': [platforms[0].pk, platforms[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'platform': [platforms[0].slug, platforms[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_tenant_group(self):
+        tenant_groups = TenantGroup.objects.all()[:2]
+        params = {'tenant_group_id': [tenant_groups[0].pk, tenant_groups[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'tenant_group': [tenant_groups[0].slug, tenant_groups[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_tenant_(self):
+        tenants = Tenant.objects.all()[:2]
+        params = {'tenant_id': [tenants[0].pk, tenants[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'tenant': [tenants[0].slug, tenants[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+# TODO: ObjectChangeFilter test

+ 1 - 1
netbox/extras/views.py

@@ -392,7 +392,7 @@ class ScriptView(PermissionRequiredMixin, View):
     def get(self, request, module, name):
 
         script = self._get_script(module, name)
-        form = script.as_form()
+        form = script.as_form(initial=request.GET)
 
         return render(request, 'extras/script.html', {
             'module': module,

+ 13 - 0
netbox/ipam/filters.py

@@ -13,6 +13,19 @@ from .constants import *
 from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
 
 
+__all__ = (
+    'AggregateFilter',
+    'IPAddressFilter',
+    'PrefixFilter',
+    'RIRFilter',
+    'RoleFilter',
+    'ServiceFilter',
+    'VLANFilter',
+    'VLANGroupFilter',
+    'VRFFilter',
+)
+
+
 class VRFFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
     id__in = NumericInFilter(
         field_name='id',

+ 645 - 0
netbox/ipam/tests/test_filters.py

@@ -0,0 +1,645 @@
+from django.test import TestCase
+
+from dcim.models import Device, DeviceRole, DeviceType, Interface, Manufacturer, Region, Site
+from ipam.constants import *
+from ipam.filters import *
+from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
+from virtualization.models import Cluster, ClusterType, VirtualMachine
+
+
+class VRFTestCase(TestCase):
+    queryset = VRF.objects.all()
+    filterset = VRFFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        vrfs = (
+            VRF(name='VRF 1', rd='65000:100', enforce_unique=False),
+            VRF(name='VRF 2', rd='65000:200', enforce_unique=False),
+            VRF(name='VRF 3', rd='65000:300', enforce_unique=False),
+            VRF(name='VRF 4', rd='65000:400', enforce_unique=True),
+            VRF(name='VRF 5', rd='65000:500', enforce_unique=True),
+            VRF(name='VRF 6', rd='65000:600', enforce_unique=True),
+        )
+        VRF.objects.bulk_create(vrfs)
+
+    def test_name(self):
+        params = {'name': ['VRF 1', 'VRF 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_rd(self):
+        params = {'rd': ['65000:100', '65000:200']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_enforce_unique(self):
+        params = {'enforce_unique': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+        params = {'enforce_unique': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+    def test_id__in(self):
+        id_list = self.queryset.values_list('id', flat=True)[:3]
+        params = {'id__in': ','.join([str(id) for id in id_list])}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+
+class RIRTestCase(TestCase):
+    queryset = RIR.objects.all()
+    filterset = RIRFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        rirs = (
+            RIR(name='RIR 1', slug='rir-1', is_private=False),
+            RIR(name='RIR 2', slug='rir-2', is_private=False),
+            RIR(name='RIR 3', slug='rir-3', is_private=False),
+            RIR(name='RIR 4', slug='rir-4', is_private=True),
+            RIR(name='RIR 5', slug='rir-5', is_private=True),
+            RIR(name='RIR 6', slug='rir-6', is_private=True),
+        )
+        RIR.objects.bulk_create(rirs)
+
+    def test_name(self):
+        params = {'name': ['RIR 1', 'RIR 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_slug(self):
+        params = {'slug': ['rir-1', 'rir-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_is_private(self):
+        params = {'is_private': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+        params = {'is_private': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+    def test_id__in(self):
+        id_list = self.queryset.values_list('id', flat=True)[:3]
+        params = {'id__in': ','.join([str(id) for id in id_list])}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+
+class AggregateTestCase(TestCase):
+    queryset = Aggregate.objects.all()
+    filterset = AggregateFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        rirs = (
+            RIR(name='RIR 1', slug='rir-1'),
+            RIR(name='RIR 2', slug='rir-2'),
+            RIR(name='RIR 3', slug='rir-3'),
+        )
+        RIR.objects.bulk_create(rirs)
+
+        aggregates = (
+            Aggregate(family=4, prefix='10.1.0.0/16', rir=rirs[0], date_added='2020-01-01'),
+            Aggregate(family=4, prefix='10.2.0.0/16', rir=rirs[0], date_added='2020-01-02'),
+            Aggregate(family=4, prefix='10.3.0.0/16', rir=rirs[1], date_added='2020-01-03'),
+            Aggregate(family=6, prefix='2001:db8:1::/48', rir=rirs[1], date_added='2020-01-04'),
+            Aggregate(family=6, prefix='2001:db8:2::/48', rir=rirs[2], date_added='2020-01-05'),
+            Aggregate(family=6, prefix='2001:db8:3::/48', rir=rirs[2], date_added='2020-01-06'),
+        )
+        Aggregate.objects.bulk_create(aggregates)
+
+    def test_family(self):
+        params = {'family': '4'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+    def test_date_added(self):
+        params = {'date_added': ['2020-01-01', '2020-01-02']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    # TODO: Test for multiple values
+    def test_prefix(self):
+        params = {'prefix': '10.1.0.0/16'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_rir(self):
+        rirs = RIR.objects.all()[:2]
+        params = {'rir_id': [rirs[0].pk, rirs[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'rir': [rirs[0].slug, rirs[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+
+class RoleTestCase(TestCase):
+    queryset = Role.objects.all()
+    filterset = RoleFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        roles = (
+            Role(name='Role 1', slug='role-1'),
+            Role(name='Role 2', slug='role-2'),
+            Role(name='Role 3', slug='role-3'),
+        )
+        Role.objects.bulk_create(roles)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Role 1', 'Role 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_slug(self):
+        params = {'slug': ['role-1', 'role-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class PrefixTestCase(TestCase):
+    queryset = Prefix.objects.all()
+    filterset = PrefixFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        regions = (
+            Region(name='Test Region 1', slug='test-region-1'),
+            Region(name='Test Region 2', slug='test-region-2'),
+            Region(name='Test Region 3', slug='test-region-3'),
+        )
+        # Can't use bulk_create for models with MPTT fields
+        for r in regions:
+            r.save()
+
+        sites = (
+            Site(name='Test Site 1', slug='test-site-1', region=regions[0]),
+            Site(name='Test Site 2', slug='test-site-2', region=regions[1]),
+            Site(name='Test Site 3', slug='test-site-3', region=regions[2]),
+        )
+        Site.objects.bulk_create(sites)
+
+        vrfs = (
+            VRF(name='VRF 1', rd='65000:100'),
+            VRF(name='VRF 2', rd='65000:200'),
+            VRF(name='VRF 3', rd='65000:300'),
+        )
+        VRF.objects.bulk_create(vrfs)
+
+        vlans = (
+            VLAN(vid=1, name='VLAN 1'),
+            VLAN(vid=2, name='VLAN 2'),
+            VLAN(vid=3, name='VLAN 3'),
+        )
+        VLAN.objects.bulk_create(vlans)
+
+        roles = (
+            Role(name='Role 1', slug='role-1'),
+            Role(name='Role 2', slug='role-2'),
+            Role(name='Role 3', slug='role-3'),
+        )
+        Role.objects.bulk_create(roles)
+
+        prefixes = (
+            Prefix(family=4, prefix='10.0.0.0/24', site=None, vrf=None, vlan=None, role=None, is_pool=True),
+            Prefix(family=4, prefix='10.0.1.0/24', site=sites[0], vrf=vrfs[0], vlan=vlans[0], role=roles[0]),
+            Prefix(family=4, prefix='10.0.2.0/24', site=sites[1], vrf=vrfs[1], vlan=vlans[1], role=roles[1], status=PREFIX_STATUS_DEPRECATED),
+            Prefix(family=4, prefix='10.0.3.0/24', site=sites[2], vrf=vrfs[2], vlan=vlans[2], role=roles[2], status=PREFIX_STATUS_RESERVED),
+            Prefix(family=6, prefix='2001:db8::/64', site=None, vrf=None, vlan=None, role=None, is_pool=True),
+            Prefix(family=6, prefix='2001:db8:0:1::/64', site=sites[0], vrf=vrfs[0], vlan=vlans[0], role=roles[0]),
+            Prefix(family=6, prefix='2001:db8:0:2::/64', site=sites[1], vrf=vrfs[1], vlan=vlans[1], role=roles[1], status=PREFIX_STATUS_DEPRECATED),
+            Prefix(family=6, prefix='2001:db8:0:3::/64', site=sites[2], vrf=vrfs[2], vlan=vlans[2], role=roles[2], status=PREFIX_STATUS_RESERVED),
+            Prefix(family=4, prefix='10.0.0.0/16'),
+            Prefix(family=6, prefix='2001:db8::/32'),
+        )
+        Prefix.objects.bulk_create(prefixes)
+
+    def test_family(self):
+        params = {'family': '6'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 5)
+
+    def test_is_pool(self):
+        params = {'is_pool': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'is_pool': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 8)
+
+    def test_id__in(self):
+        id_list = self.queryset.values_list('id', flat=True)[:3]
+        params = {'id__in': ','.join([str(id) for id in id_list])}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+    def test_within(self):
+        params = {'within': '10.0.0.0/16'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_within_include(self):
+        params = {'within_include': '10.0.0.0/16'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 5)
+
+    def test_contains(self):
+        params = {'contains': '10.0.1.0/24'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'contains': '2001:db8:0:1::/64'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_mask_length(self):
+        params = {'mask_length': '24'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_vrf(self):
+        vrfs = VRF.objects.all()[:2]
+        params = {'vrf_id': [vrfs[0].pk, vrfs[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'vrf': [vrfs[0].rd, vrfs[1].rd]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_region(self):
+        regions = Region.objects.all()[:2]
+        params = {'region_id': [regions[0].pk, regions[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'region': [regions[0].slug, regions[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_site(self):
+        sites = Site.objects.all()[:2]
+        params = {'site_id': [sites[0].pk, sites[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'site': [sites[0].slug, sites[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_vlan(self):
+        vlans = VLAN.objects.all()[:2]
+        params = {'vlan_id': [vlans[0].pk, vlans[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        # TODO: Test for multiple values
+        params = {'vlan_vid': vlans[0].vid}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_role(self):
+        roles = Role.objects.all()[:2]
+        params = {'role_id': [roles[0].pk, roles[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'role': [roles[0].slug, roles[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_status(self):
+        params = {'status': [PREFIX_STATUS_DEPRECATED, PREFIX_STATUS_RESERVED]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+
+class IPAddressTestCase(TestCase):
+    queryset = IPAddress.objects.all()
+    filterset = IPAddressFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        vrfs = (
+            VRF(name='VRF 1', rd='65000:100'),
+            VRF(name='VRF 2', rd='65000:200'),
+            VRF(name='VRF 3', rd='65000:300'),
+        )
+        VRF.objects.bulk_create(vrfs)
+
+        site = Site.objects.create(name='Site 1', slug='site-1')
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1')
+        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        devices = (
+            Device(device_type=device_type, name='Device 1', site=site, device_role=device_role),
+            Device(device_type=device_type, name='Device 2', site=site, device_role=device_role),
+            Device(device_type=device_type, name='Device 3', site=site, device_role=device_role),
+        )
+        Device.objects.bulk_create(devices)
+
+        clustertype = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1')
+        cluster = Cluster.objects.create(type=clustertype, name='Cluster 1')
+
+        virtual_machines = (
+            VirtualMachine(name='Virtual Machine 1', cluster=cluster),
+            VirtualMachine(name='Virtual Machine 2', cluster=cluster),
+            VirtualMachine(name='Virtual Machine 3', cluster=cluster),
+        )
+        VirtualMachine.objects.bulk_create(virtual_machines)
+
+        interfaces = (
+            Interface(device=devices[0], name='Interface 1'),
+            Interface(device=devices[1], name='Interface 2'),
+            Interface(device=devices[2], name='Interface 3'),
+            Interface(virtual_machine=virtual_machines[0], name='Interface 1'),
+            Interface(virtual_machine=virtual_machines[1], name='Interface 2'),
+            Interface(virtual_machine=virtual_machines[2], name='Interface 3'),
+        )
+        Interface.objects.bulk_create(interfaces)
+
+        ipaddresses = (
+            IPAddress(family=4, address='10.0.0.1/24', vrf=None, interface=None, status=IPADDRESS_STATUS_ACTIVE, role=None, dns_name='ipaddress-a'),
+            IPAddress(family=4, address='10.0.0.2/24', vrf=vrfs[0], interface=interfaces[0], status=IPADDRESS_STATUS_ACTIVE, role=None, dns_name='ipaddress-b'),
+            IPAddress(family=4, address='10.0.0.3/24', vrf=vrfs[1], interface=interfaces[1], status=IPADDRESS_STATUS_RESERVED, role=IPADDRESS_ROLE_VIP, dns_name='ipaddress-c'),
+            IPAddress(family=4, address='10.0.0.4/24', vrf=vrfs[2], interface=interfaces[2], status=IPADDRESS_STATUS_DEPRECATED, role=IPADDRESS_ROLE_SECONDARY, dns_name='ipaddress-d'),
+            IPAddress(family=6, address='2001:db8::1/64', vrf=None, interface=None, status=IPADDRESS_STATUS_ACTIVE, role=None, dns_name='ipaddress-a'),
+            IPAddress(family=6, address='2001:db8::2/64', vrf=vrfs[0], interface=interfaces[3], status=IPADDRESS_STATUS_ACTIVE, role=None, dns_name='ipaddress-b'),
+            IPAddress(family=6, address='2001:db8::3/64', vrf=vrfs[1], interface=interfaces[4], status=IPADDRESS_STATUS_RESERVED, role=IPADDRESS_ROLE_VIP, dns_name='ipaddress-c'),
+            IPAddress(family=6, address='2001:db8::4/64', vrf=vrfs[2], interface=interfaces[5], status=IPADDRESS_STATUS_DEPRECATED, role=IPADDRESS_ROLE_SECONDARY, dns_name='ipaddress-d'),
+        )
+        IPAddress.objects.bulk_create(ipaddresses)
+
+    def test_family(self):
+        params = {'family': '6'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_dns_name(self):
+        params = {'dns_name': ['ipaddress-a', 'ipaddress-b']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_id__in(self):
+        id_list = self.queryset.values_list('id', flat=True)[:3]
+        params = {'id__in': ','.join([str(id) for id in id_list])}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+    def test_parent(self):
+        params = {'parent': '10.0.0.0/24'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'parent': '2001:db8::/64'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def filter_address(self):
+        # Check IPv4 and IPv6, with and without a mask
+        params = {'address': '10.0.0.1/24'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'address': '10.0.0.1'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'address': '2001:db8::1/64'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'address': '2001:db8::1'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_mask_length(self):
+        params = {'mask_length': '24'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_vrf(self):
+        vrfs = VRF.objects.all()[:2]
+        params = {'vrf_id': [vrfs[0].pk, vrfs[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'vrf': [vrfs[0].rd, vrfs[1].rd]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    # TODO: Test for multiple values
+    def test_device(self):
+        device = Device.objects.first()
+        params = {'device_id': device.pk}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'device': device.name}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_virtual_machine(self):
+        vms = VirtualMachine.objects.all()[:2]
+        params = {'virtual_machine_id': [vms[0].pk, vms[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'virtual_machine': [vms[0].name, vms[1].name]}
+        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)
+        params = {'interface': ['Interface 1', 'Interface 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_assigned_to_interface(self):
+        params = {'assigned_to_interface': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6)
+        params = {'assigned_to_interface': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_status(self):
+        params = {'status': [PREFIX_STATUS_DEPRECATED, PREFIX_STATUS_RESERVED]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_role(self):
+        params = {'role': [IPADDRESS_ROLE_SECONDARY, IPADDRESS_ROLE_VIP]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+
+class VLANGroupTestCase(TestCase):
+    queryset = VLANGroup.objects.all()
+    filterset = VLANGroupFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        regions = (
+            Region(name='Test Region 1', slug='test-region-1'),
+            Region(name='Test Region 2', slug='test-region-2'),
+            Region(name='Test Region 3', slug='test-region-3'),
+        )
+        # Can't use bulk_create for models with MPTT fields
+        for r in regions:
+            r.save()
+
+        sites = (
+            Site(name='Test Site 1', slug='test-site-1', region=regions[0]),
+            Site(name='Test Site 2', slug='test-site-2', region=regions[1]),
+            Site(name='Test Site 3', slug='test-site-3', region=regions[2]),
+        )
+        Site.objects.bulk_create(sites)
+
+        vlan_groups = (
+            VLANGroup(name='VLAN Group 1', slug='vlan-group-1', site=sites[0]),
+            VLANGroup(name='VLAN Group 2', slug='vlan-group-2', site=sites[1]),
+            VLANGroup(name='VLAN Group 3', slug='vlan-group-3', site=sites[2]),
+            VLANGroup(name='VLAN Group 4', slug='vlan-group-4', site=None),
+        )
+        VLANGroup.objects.bulk_create(vlan_groups)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['VLAN Group 1', 'VLAN Group 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_slug(self):
+        params = {'slug': ['vlan-group-1', 'vlan-group-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_region(self):
+        regions = Region.objects.all()[:2]
+        params = {'region_id': [regions[0].pk, regions[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'region': [regions[0].slug, regions[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_site(self):
+        sites = Site.objects.all()[:2]
+        params = {'site_id': [sites[0].pk, sites[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'site': [sites[0].slug, sites[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class VLANTestCase(TestCase):
+    queryset = VLAN.objects.all()
+    filterset = VLANFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        regions = (
+            Region(name='Test Region 1', slug='test-region-1'),
+            Region(name='Test Region 2', slug='test-region-2'),
+            Region(name='Test Region 3', slug='test-region-3'),
+        )
+        # Can't use bulk_create for models with MPTT fields
+        for r in regions:
+            r.save()
+
+        sites = (
+            Site(name='Test Site 1', slug='test-site-1', region=regions[0]),
+            Site(name='Test Site 2', slug='test-site-2', region=regions[1]),
+            Site(name='Test Site 3', slug='test-site-3', region=regions[2]),
+        )
+        Site.objects.bulk_create(sites)
+
+        roles = (
+            Role(name='Role 1', slug='role-1'),
+            Role(name='Role 2', slug='role-2'),
+            Role(name='Role 3', slug='role-3'),
+        )
+        Role.objects.bulk_create(roles)
+
+        groups = (
+            VLANGroup(name='VLAN Group 1', slug='vlan-group-1', site=sites[0]),
+            VLANGroup(name='VLAN Group 2', slug='vlan-group-2', site=sites[1]),
+            VLANGroup(name='VLAN Group 3', slug='vlan-group-3', site=None),
+        )
+        VLANGroup.objects.bulk_create(groups)
+
+        vlans = (
+            VLAN(vid=101, name='VLAN 101', site=sites[0], group=groups[0], role=roles[0], status=VLAN_STATUS_ACTIVE),
+            VLAN(vid=102, name='VLAN 102', site=sites[0], group=groups[0], role=roles[0], status=VLAN_STATUS_ACTIVE),
+            VLAN(vid=201, name='VLAN 201', site=sites[1], group=groups[1], role=roles[1], status=VLAN_STATUS_DEPRECATED),
+            VLAN(vid=202, name='VLAN 202', site=sites[1], group=groups[1], role=roles[1], status=VLAN_STATUS_DEPRECATED),
+            VLAN(vid=301, name='VLAN 301', site=sites[2], group=groups[2], role=roles[2], status=VLAN_STATUS_RESERVED),
+            VLAN(vid=302, name='VLAN 302', site=sites[2], group=groups[2], role=roles[2], status=VLAN_STATUS_RESERVED),
+        )
+        VLAN.objects.bulk_create(vlans)
+
+    def test_name(self):
+        params = {'name': ['VLAN 101', 'VLAN 102']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_rd(self):
+        params = {'vid': ['101', '201', '301']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+    def test_id__in(self):
+        id_list = self.queryset.values_list('id', flat=True)[:3]
+        params = {'id__in': ','.join([str(id) for id in id_list])}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+    def test_region(self):
+        regions = Region.objects.all()[:2]
+        params = {'region_id': [regions[0].pk, regions[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'region': [regions[0].slug, regions[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_site(self):
+        sites = Site.objects.all()[:2]
+        params = {'site_id': [sites[0].pk, sites[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'site': [sites[0].slug, sites[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_group(self):
+        groups = VLANGroup.objects.all()[:2]
+        params = {'group_id': [groups[0].pk, groups[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'group': [groups[0].slug, groups[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_role(self):
+        roles = Role.objects.all()[:2]
+        params = {'role_id': [roles[0].pk, roles[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'role': [roles[0].slug, roles[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_status(self):
+        params = {'status': [VLAN_STATUS_ACTIVE, VLAN_STATUS_DEPRECATED]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+
+class ServiceTestCase(TestCase):
+    queryset = Service.objects.all()
+    filterset = ServiceFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        site = Site.objects.create(name='Site 1', slug='site-1')
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1')
+        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        devices = (
+            Device(device_type=device_type, name='Device 1', site=site, device_role=device_role),
+            Device(device_type=device_type, name='Device 2', site=site, device_role=device_role),
+            Device(device_type=device_type, name='Device 3', site=site, device_role=device_role),
+        )
+        Device.objects.bulk_create(devices)
+
+        clustertype = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1')
+        cluster = Cluster.objects.create(type=clustertype, name='Cluster 1')
+
+        virtual_machines = (
+            VirtualMachine(name='Virtual Machine 1', cluster=cluster),
+            VirtualMachine(name='Virtual Machine 2', cluster=cluster),
+            VirtualMachine(name='Virtual Machine 3', cluster=cluster),
+        )
+        VirtualMachine.objects.bulk_create(virtual_machines)
+
+        services = (
+            Service(device=devices[0], name='Service 1', protocol=IP_PROTOCOL_TCP, port=1001),
+            Service(device=devices[1], name='Service 2', protocol=IP_PROTOCOL_TCP, port=1002),
+            Service(device=devices[2], name='Service 3', protocol=IP_PROTOCOL_UDP, port=1003),
+            Service(virtual_machine=virtual_machines[0], name='Service 4', protocol=IP_PROTOCOL_TCP, port=2001),
+            Service(virtual_machine=virtual_machines[1], name='Service 5', protocol=IP_PROTOCOL_TCP, port=2002),
+            Service(virtual_machine=virtual_machines[2], name='Service 6', protocol=IP_PROTOCOL_UDP, port=2003),
+        )
+        Service.objects.bulk_create(services)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:3]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+    def test_name(self):
+        params = {'name': ['Service 1', 'Service 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_protocol(self):
+        params = {'protocol': IP_PROTOCOL_TCP}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_port(self):
+        params = {'port': ['1001', '1002', '1003']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
+    def test_device(self):
+        devices = Device.objects.all()[:2]
+        params = {'device_id': [devices[0].pk, devices[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'device': [devices[0].name, devices[1].name]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_virtual_machine(self):
+        vms = VirtualMachine.objects.all()[:2]
+        params = {'virtual_machine_id': [vms[0].pk, vms[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'virtual_machine': [vms[0].name, vms[1].name]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)

+ 13 - 4
netbox/ipam/views.py

@@ -333,7 +333,10 @@ class AggregateView(PermissionRequiredMixin, View):
         ).annotate_depth(
             limit=0
         )
-        child_prefixes = add_available_prefixes(aggregate.prefix, child_prefixes)
+
+        # Add available prefixes to the table if requested
+        if request.GET.get('show_available', 'true') == 'true':
+            child_prefixes = add_available_prefixes(aggregate.prefix, child_prefixes)
 
         prefix_table = tables.PrefixDetailTable(child_prefixes)
         if request.user.has_perm('ipam.change_prefix') or request.user.has_perm('ipam.delete_prefix'):
@@ -356,6 +359,7 @@ class AggregateView(PermissionRequiredMixin, View):
             'aggregate': aggregate,
             'prefix_table': prefix_table,
             'permissions': permissions,
+            'show_available': request.GET.get('show_available', 'true') == 'true',
         })
 
 
@@ -511,8 +515,8 @@ class PrefixPrefixesView(PermissionRequiredMixin, View):
             'site', 'vlan', 'role',
         ).annotate_depth(limit=0)
 
-        # Annotate available prefixes
-        if child_prefixes:
+        # Add available prefixes to the table if requested
+        if child_prefixes and request.GET.get('show_available', 'true') == 'true':
             child_prefixes = add_available_prefixes(prefix.prefix, child_prefixes)
 
         prefix_table = tables.PrefixDetailTable(child_prefixes)
@@ -539,6 +543,7 @@ class PrefixPrefixesView(PermissionRequiredMixin, View):
             'permissions': permissions,
             'bulk_querystring': 'vrf_id={}&within={}'.format(prefix.vrf.pk if prefix.vrf else '0', prefix.prefix),
             'active_tab': 'prefixes',
+            'show_available': request.GET.get('show_available', 'true') == 'true',
         })
 
 
@@ -553,7 +558,10 @@ class PrefixIPAddressesView(PermissionRequiredMixin, View):
         ipaddresses = prefix.get_child_ips().prefetch_related(
             'vrf', 'interface__device', 'primary_ip4_for', 'primary_ip6_for'
         )
-        ipaddresses = add_available_ipaddresses(prefix.prefix, ipaddresses, prefix.is_pool)
+
+        # Add available IP addresses to the table if requested
+        if request.GET.get('show_available', 'true') == 'true':
+            ipaddresses = add_available_ipaddresses(prefix.prefix, ipaddresses, prefix.is_pool)
 
         ip_table = tables.IPAddressTable(ipaddresses)
         if request.user.has_perm('ipam.change_ipaddress') or request.user.has_perm('ipam.delete_ipaddress'):
@@ -579,6 +587,7 @@ class PrefixIPAddressesView(PermissionRequiredMixin, View):
             'permissions': permissions,
             'bulk_querystring': 'vrf_id={}&parent={}'.format(prefix.vrf.pk if prefix.vrf else '0', prefix.prefix),
             'active_tab': 'ip-addresses',
+            'show_available': request.GET.get('show_available', 'true') == 'true',
         })
 
 

+ 6 - 0
netbox/secrets/filters.py

@@ -7,6 +7,12 @@ from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilte
 from .models import Secret, SecretRole
 
 
+__all__ = (
+    'SecretFilter',
+    'SecretRoleFilter',
+)
+
+
 class SecretRoleFilter(NameSlugSearchFilterSet):
 
     class Meta:

+ 92 - 0
netbox/secrets/tests/test_filters.py

@@ -0,0 +1,92 @@
+from django.test import TestCase
+
+from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
+from secrets.filters import *
+from secrets.models import Secret, SecretRole
+
+
+class SecretRoleTestCase(TestCase):
+    queryset = SecretRole.objects.all()
+    filterset = SecretRoleFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        roles = (
+            SecretRole(name='Secret Role 1', slug='secret-role-1'),
+            SecretRole(name='Secret Role 2', slug='secret-role-2'),
+            SecretRole(name='Secret Role 3', slug='secret-role-3'),
+        )
+        SecretRole.objects.bulk_create(roles)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Secret Role 1', 'Secret Role 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_slug(self):
+        params = {'slug': ['secret-role-1', 'secret-role-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class SecretTestCase(TestCase):
+    queryset = Secret.objects.all()
+    filterset = SecretFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        site = Site.objects.create(name='Site 1', slug='site-1')
+        manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
+        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1')
+        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        devices = (
+            Device(device_type=device_type, name='Device 1', site=site, device_role=device_role),
+            Device(device_type=device_type, name='Device 2', site=site, device_role=device_role),
+            Device(device_type=device_type, name='Device 3', site=site, device_role=device_role),
+        )
+        Device.objects.bulk_create(devices)
+
+        roles = (
+            SecretRole(name='Secret Role 1', slug='secret-role-1'),
+            SecretRole(name='Secret Role 2', slug='secret-role-2'),
+            SecretRole(name='Secret Role 3', slug='secret-role-3'),
+        )
+        SecretRole.objects.bulk_create(roles)
+
+        secrets = (
+            Secret(device=devices[0], role=roles[0], name='Secret 1', plaintext='SECRET DATA'),
+            Secret(device=devices[1], role=roles[1], name='Secret 2', plaintext='SECRET DATA'),
+            Secret(device=devices[2], role=roles[2], name='Secret 3', plaintext='SECRET DATA'),
+        )
+        # Must call save() to encrypt Secrets
+        for s in secrets:
+            s.save()
+
+    def test_name(self):
+        params = {'name': ['Secret 1', 'Secret 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_id__in(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id__in': ','.join([str(id) for id in id_list])}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_role(self):
+        roles = SecretRole.objects.all()[:2]
+        params = {'role_id': [roles[0].pk, roles[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'role': [roles[0].slug, roles[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_device(self):
+        devices = Device.objects.all()[:2]
+        params = {'device_id': [devices[0].pk, devices[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'device': [devices[0].name, devices[1].name]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)

+ 1 - 0
netbox/templates/ipam/aggregate.html

@@ -40,6 +40,7 @@
     </div>
     <h1>{% block title %}{{ aggregate }}{% endblock %}</h1>
     {% include 'inc/created_updated.html' with obj=aggregate %}
+    {% include 'ipam/inc/toggle_available.html' %}
     <div class="pull-right noprint">
         {% custom_links aggregate %}
     </div>

+ 9 - 0
netbox/templates/ipam/inc/toggle_available.html

@@ -0,0 +1,9 @@
+{% load helpers %}
+{% if show_available is not None %}
+    <div class="pull-right">
+        <div class="btn-group" role="group">
+            <a href="{{ request.path }}{% querystring request show_available='true' %}" class="btn btn-default{% if show_available %} active disabled{% endif %}">Show available</a>
+            <a href="{{ request.path }}{% querystring request show_available='false' %}" class="btn btn-default{% if not show_available %} active disabled{% endif %}">Hide available</a>
+        </div>
+    </div>
+{% endif %}

+ 1 - 0
netbox/templates/ipam/prefix.html

@@ -53,6 +53,7 @@
     </div>
     <h1>{% block title %}{{ prefix }}{% endblock %}</h1>
     {% include 'inc/created_updated.html' with obj=prefix %}
+    {% include 'ipam/inc/toggle_available.html' %}
     <div class="pull-right noprint">
         {% custom_links prefix %}
     </div>

+ 6 - 0
netbox/tenancy/filters.py

@@ -6,6 +6,12 @@ from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilte
 from .models import Tenant, TenantGroup
 
 
+__all__ = (
+    'TenantFilter',
+    'TenantGroupFilter',
+)
+
+
 class TenantGroupFilter(NameSlugSearchFilterSet):
 
     class Meta:

+ 74 - 0
netbox/tenancy/tests/test_filters.py

@@ -0,0 +1,74 @@
+from django.test import TestCase
+
+from tenancy.filters import *
+from tenancy.models import Tenant, TenantGroup
+
+
+class TenantGroupTestCase(TestCase):
+    queryset = TenantGroup.objects.all()
+    filterset = TenantGroupFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        groups = (
+            TenantGroup(name='Tenant Group 1', slug='tenant-group-1'),
+            TenantGroup(name='Tenant Group 2', slug='tenant-group-2'),
+            TenantGroup(name='Tenant Group 3', slug='tenant-group-3'),
+        )
+        TenantGroup.objects.bulk_create(groups)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Tenant Group 1', 'Tenant Group 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_slug(self):
+        params = {'slug': ['tenant-group-1', 'tenant-group-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class TenantTestCase(TestCase):
+    queryset = Tenant.objects.all()
+    filterset = TenantFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        groups = (
+            TenantGroup(name='Tenant Group 1', slug='tenant-group-1'),
+            TenantGroup(name='Tenant Group 2', slug='tenant-group-2'),
+            TenantGroup(name='Tenant Group 3', slug='tenant-group-3'),
+        )
+        TenantGroup.objects.bulk_create(groups)
+
+        tenants = (
+            Tenant(name='Tenant 1', slug='tenant-1', group=groups[0]),
+            Tenant(name='Tenant 2', slug='tenant-2', group=groups[1]),
+            Tenant(name='Tenant 3', slug='tenant-3', group=groups[2]),
+        )
+        Tenant.objects.bulk_create(tenants)
+
+    def test_name(self):
+        params = {'name': ['Tenant 1', 'Tenant 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_slug(self):
+        params = {'slug': ['tenant-1', 'tenant-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_id__in(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id__in': ','.join([str(id) for id in id_list])}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_group(self):
+        group = TenantGroup.objects.all()[:2]
+        params = {'group_id': [group[0].pk, group[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'group': [group[0].slug, group[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)

+ 9 - 2
netbox/virtualization/filters.py

@@ -1,7 +1,5 @@
 import django_filters
 from django.db.models import Q
-from netaddr import EUI
-from netaddr.core import AddrFormatError
 
 from dcim.models import DeviceRole, Interface, Platform, Region, Site
 from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
@@ -13,6 +11,15 @@ from .constants import *
 from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 
 
+__all__ = (
+    'ClusterFilter',
+    'ClusterGroupFilter',
+    'ClusterTypeFilter',
+    'InterfaceFilter',
+    'VirtualMachineFilter',
+)
+
+
 class ClusterTypeFilter(NameSlugSearchFilterSet):
 
     class Meta:

+ 367 - 0
netbox/virtualization/tests/test_filters.py

@@ -0,0 +1,367 @@
+from django.test import TestCase
+
+from dcim.models import DeviceRole, Interface, Platform, Region, Site
+from virtualization.constants import *
+from virtualization.filters import *
+from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
+
+
+class ClusterTypeTestCase(TestCase):
+    queryset = ClusterType.objects.all()
+    filterset = ClusterTypeFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        cluster_types = (
+            ClusterType(name='Cluster Type 1', slug='cluster-type-1'),
+            ClusterType(name='Cluster Type 2', slug='cluster-type-2'),
+            ClusterType(name='Cluster Type 3', slug='cluster-type-3'),
+        )
+        ClusterType.objects.bulk_create(cluster_types)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Cluster Type 1', 'Cluster Type 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_slug(self):
+        params = {'slug': ['cluster-type-1', 'cluster-type-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class ClusterGroupTestCase(TestCase):
+    queryset = ClusterGroup.objects.all()
+    filterset = ClusterGroupFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        cluster_groups = (
+            ClusterGroup(name='Cluster Group 1', slug='cluster-group-1'),
+            ClusterGroup(name='Cluster Group 2', slug='cluster-group-2'),
+            ClusterGroup(name='Cluster Group 3', slug='cluster-group-3'),
+        )
+        ClusterGroup.objects.bulk_create(cluster_groups)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Cluster Group 1', 'Cluster Group 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_slug(self):
+        params = {'slug': ['cluster-group-1', 'cluster-group-2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class ClusterTestCase(TestCase):
+    queryset = Cluster.objects.all()
+    filterset = ClusterFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        cluster_types = (
+            ClusterType(name='Cluster Type 1', slug='cluster-type-1'),
+            ClusterType(name='Cluster Type 2', slug='cluster-type-2'),
+            ClusterType(name='Cluster Type 3', slug='cluster-type-3'),
+        )
+        ClusterType.objects.bulk_create(cluster_types)
+
+        cluster_groups = (
+            ClusterGroup(name='Cluster Group 1', slug='cluster-group-1'),
+            ClusterGroup(name='Cluster Group 2', slug='cluster-group-2'),
+            ClusterGroup(name='Cluster Group 3', slug='cluster-group-3'),
+        )
+        ClusterGroup.objects.bulk_create(cluster_groups)
+
+        regions = (
+            Region(name='Test Region 1', slug='test-region-1'),
+            Region(name='Test Region 2', slug='test-region-2'),
+            Region(name='Test Region 3', slug='test-region-3'),
+        )
+        # Can't use bulk_create for models with MPTT fields
+        for r in regions:
+            r.save()
+
+        sites = (
+            Site(name='Test Site 1', slug='test-site-1', region=regions[0]),
+            Site(name='Test Site 2', slug='test-site-2', region=regions[1]),
+            Site(name='Test Site 3', slug='test-site-3', region=regions[2]),
+        )
+        Site.objects.bulk_create(sites)
+
+        clusters = (
+            Cluster(name='Cluster 1', type=cluster_types[0], group=cluster_groups[0], site=sites[0]),
+            Cluster(name='Cluster 2', type=cluster_types[1], group=cluster_groups[1], site=sites[1]),
+            Cluster(name='Cluster 3', type=cluster_types[2], group=cluster_groups[2], site=sites[2]),
+        )
+        Cluster.objects.bulk_create(clusters)
+
+    def test_name(self):
+        params = {'name': ['Cluster 1', 'Cluster 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_id__in(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id__in': ','.join([str(id) for id in id_list])}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_region(self):
+        regions = Region.objects.all()[:2]
+        params = {'region_id': [regions[0].pk, regions[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'region': [regions[0].slug, regions[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_site(self):
+        sites = Site.objects.all()[:2]
+        params = {'site_id': [sites[0].pk, sites[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'site': [sites[0].slug, sites[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_group(self):
+        groups = ClusterGroup.objects.all()[:2]
+        params = {'group_id': [groups[0].pk, groups[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'group': [groups[0].slug, groups[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_type(self):
+        types = ClusterType.objects.all()[:2]
+        params = {'type_id': [types[0].pk, types[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'type': [types[0].slug, types[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class VirtualMachineTestCase(TestCase):
+    queryset = VirtualMachine.objects.all()
+    filterset = VirtualMachineFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        cluster_types = (
+            ClusterType(name='Cluster Type 1', slug='cluster-type-1'),
+            ClusterType(name='Cluster Type 2', slug='cluster-type-2'),
+            ClusterType(name='Cluster Type 3', slug='cluster-type-3'),
+        )
+        ClusterType.objects.bulk_create(cluster_types)
+
+        cluster_groups = (
+            ClusterGroup(name='Cluster Group 1', slug='cluster-group-1'),
+            ClusterGroup(name='Cluster Group 2', slug='cluster-group-2'),
+            ClusterGroup(name='Cluster Group 3', slug='cluster-group-3'),
+        )
+        ClusterGroup.objects.bulk_create(cluster_groups)
+
+        regions = (
+            Region(name='Test Region 1', slug='test-region-1'),
+            Region(name='Test Region 2', slug='test-region-2'),
+            Region(name='Test Region 3', slug='test-region-3'),
+        )
+        # Can't use bulk_create for models with MPTT fields
+        for r in regions:
+            r.save()
+
+        sites = (
+            Site(name='Test Site 1', slug='test-site-1', region=regions[0]),
+            Site(name='Test Site 2', slug='test-site-2', region=regions[1]),
+            Site(name='Test Site 3', slug='test-site-3', region=regions[2]),
+        )
+        Site.objects.bulk_create(sites)
+
+        clusters = (
+            Cluster(name='Cluster 1', type=cluster_types[0], group=cluster_groups[0], site=sites[0]),
+            Cluster(name='Cluster 2', type=cluster_types[1], group=cluster_groups[1], site=sites[1]),
+            Cluster(name='Cluster 3', type=cluster_types[2], group=cluster_groups[2], site=sites[2]),
+        )
+        Cluster.objects.bulk_create(clusters)
+
+        platforms = (
+            Platform(name='Platform 1', slug='platform-1'),
+            Platform(name='Platform 2', slug='platform-2'),
+            Platform(name='Platform 3', slug='platform-3'),
+        )
+        Platform.objects.bulk_create(platforms)
+
+        roles = (
+            DeviceRole(name='Device Role 1', slug='device-role-1'),
+            DeviceRole(name='Device Role 2', slug='device-role-2'),
+            DeviceRole(name='Device Role 3', slug='device-role-3'),
+        )
+        DeviceRole.objects.bulk_create(roles)
+
+        vms = (
+            VirtualMachine(name='Virtual Machine 1', cluster=clusters[0], platform=platforms[0], role=roles[0], status=DEVICE_STATUS_ACTIVE, vcpus=1, memory=1, disk=1),
+            VirtualMachine(name='Virtual Machine 2', cluster=clusters[1], platform=platforms[1], role=roles[1], status=DEVICE_STATUS_STAGED, vcpus=2, memory=2, disk=2),
+            VirtualMachine(name='Virtual Machine 3', cluster=clusters[2], platform=platforms[2], role=roles[2], status=DEVICE_STATUS_OFFLINE, vcpus=3, memory=3, disk=3),
+        )
+        VirtualMachine.objects.bulk_create(vms)
+
+        interfaces = (
+            Interface(virtual_machine=vms[0], name='Interface 1', mac_address='00-00-00-00-00-01'),
+            Interface(virtual_machine=vms[1], name='Interface 2', mac_address='00-00-00-00-00-02'),
+            Interface(virtual_machine=vms[2], name='Interface 3', mac_address='00-00-00-00-00-03'),
+        )
+        Interface.objects.bulk_create(interfaces)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Virtual Machine 1', 'Virtual Machine 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_vcpus(self):
+        params = {'vcpus': [1, 2]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_memory(self):
+        params = {'memory': [1, 2]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_disk(self):
+        params = {'disk': [1, 2]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_id__in(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id__in': ','.join([str(id) for id in id_list])}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_status(self):
+        params = {'status': [DEVICE_STATUS_ACTIVE, DEVICE_STATUS_STAGED]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_cluster_group(self):
+        groups = ClusterGroup.objects.all()[:2]
+        params = {'cluster_group_id': [groups[0].pk, groups[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'cluster_group': [groups[0].slug, groups[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_cluster_type(self):
+        types = ClusterType.objects.all()[:2]
+        params = {'cluster_type_id': [types[0].pk, types[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'cluster_type': [types[0].slug, types[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_cluster(self):
+        clusters = Cluster.objects.all()[:2]
+        params = {'cluster_id': [clusters[0].pk, clusters[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        # TODO: 'cluster' should match on name
+        # params = {'cluster': [clusters[0].name, clusters[1].name]}
+        # self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_region(self):
+        regions = Region.objects.all()[:2]
+        params = {'region_id': [regions[0].pk, regions[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'region': [regions[0].slug, regions[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_site(self):
+        sites = Site.objects.all()[:2]
+        params = {'site_id': [sites[0].pk, sites[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'site': [sites[0].slug, sites[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_role(self):
+        roles = DeviceRole.objects.all()[:2]
+        params = {'role_id': [roles[0].pk, roles[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'role': [roles[0].slug, roles[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_platform(self):
+        platforms = Platform.objects.all()[:2]
+        params = {'platform_id': [platforms[0].pk, platforms[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'platform': [platforms[0].slug, platforms[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_mac_address(self):
+        params = {'mac_address': ['00-00-00-00-00-01', '00-00-00-00-00-02']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class InterfaceTestCase(TestCase):
+    queryset = Interface.objects.all()
+    filterset = InterfaceFilter
+
+    @classmethod
+    def setUpTestData(cls):
+
+        cluster_types = (
+            ClusterType(name='Cluster Type 1', slug='cluster-type-1'),
+            ClusterType(name='Cluster Type 2', slug='cluster-type-2'),
+            ClusterType(name='Cluster Type 3', slug='cluster-type-3'),
+        )
+        ClusterType.objects.bulk_create(cluster_types)
+
+        clusters = (
+            Cluster(name='Cluster 1', type=cluster_types[0]),
+            Cluster(name='Cluster 2', type=cluster_types[1]),
+            Cluster(name='Cluster 3', type=cluster_types[2]),
+        )
+        Cluster.objects.bulk_create(clusters)
+
+        vms = (
+            VirtualMachine(name='Virtual Machine 1', cluster=clusters[0]),
+            VirtualMachine(name='Virtual Machine 2', cluster=clusters[1]),
+            VirtualMachine(name='Virtual Machine 3', cluster=clusters[2]),
+        )
+        VirtualMachine.objects.bulk_create(vms)
+
+        interfaces = (
+            Interface(virtual_machine=vms[0], name='Interface 1', enabled=True, mtu=100, mac_address='00-00-00-00-00-01'),
+            Interface(virtual_machine=vms[1], name='Interface 2', enabled=True, mtu=200, mac_address='00-00-00-00-00-02'),
+            Interface(virtual_machine=vms[2], name='Interface 3', enabled=False, mtu=300, mac_address='00-00-00-00-00-03'),
+        )
+        Interface.objects.bulk_create(interfaces)
+
+    def test_id(self):
+        id_list = self.queryset.values_list('id', flat=True)[:2]
+        params = {'id': [str(id) for id in id_list]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_name(self):
+        params = {'name': ['Interface 1', 'Interface 2']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_assigned_to_interface(self):
+        params = {'enabled': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'enabled': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+    def test_mtu(self):
+        params = {'mtu': [100, 200]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_virtual_machine(self):
+        vms = VirtualMachine.objects.all()[:2]
+        params = {'virtual_machine_id': [vms[0].pk, vms[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'virtual_machine': [vms[0].name, vms[1].name]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_mac_address(self):
+        params = {'mac_address': ['00-00-00-00-00-01', '00-00-00-00-00-02']}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)