Merge branch 'develop' into feature

This commit is contained in:
jeremystretch 2021-05-04 14:25:53 -04:00
commit 534b0e4cf6
53 changed files with 1172 additions and 1309 deletions

View File

@ -5,21 +5,25 @@ labels: ["type: bug"]
body:
- type: markdown
attributes:
value: "**NOTE:** This form is only for reporting _reproducible bugs_ in a
current NetBox installation. If you're having trouble with installation or just
looking for assistance with using NetBox, please visit our
[discussion forum](https://github.com/netbox-community/netbox/discussions) instead."
value: >
**NOTE:** This form is only for reporting _reproducible bugs_ in a current NetBox
installation. If you're having trouble with installation or just looking for
assistance with using NetBox, please visit our
[discussion forum](https://github.com/netbox-community/netbox/discussions) instead.
- type: input
attributes:
label: NetBox version
description: "What version of NetBox are you currently running?"
placeholder: v2.10.4
description: >
What version of NetBox are you currently running? (If you don't have access to the most
recent NetBox release, consider testing on our [demo instance](https://demo.netbox.dev/)
before opening a bug report to see if your issue has already been addressed.)
placeholder: v2.11.2
validations:
required: true
- type: dropdown
attributes:
label: Python version
description: "What version of Python are you currently running?"
description: What version of Python are you currently running?
options:
- 3.6
- 3.7
@ -30,12 +34,13 @@ body:
- type: textarea
attributes:
label: Steps to Reproduce
description: "Describe in detail the exact steps that someone else can take to
reproduce this bug using the current stable release of NetBox. Begin with the
creation of any necessary database objects and call out every operation being
performed explicitly. If reporting a bug in the REST API, be sure to reconstruct
the raw HTTP request(s) being made: Don't rely on a client library such as
pynetbox."
description: >
Describe in detail the exact steps that someone else can take to
reproduce this bug using the current stable release of NetBox. Begin with the
creation of any necessary database objects and call out every operation being
performed explicitly. If reporting a bug in the REST API, be sure to reconstruct
the raw HTTP request(s) being made: Don't rely on a client library such as
pynetbox."
placeholder: |
1. Click on "create widget"
2. Set foo to 12 and bar to G
@ -45,14 +50,14 @@ body:
- type: textarea
attributes:
label: Expected Behavior
description: "What did you expect to happen?"
placeholder: "A new widget should have been created with the specified attributes"
description: What did you expect to happen?
placeholder: A new widget should have been created with the specified attributes
validations:
required: true
- type: textarea
attributes:
label: Observed Behavior
description: "What happened instead?"
placeholder: "A TypeError exception was raised"
description: What happened instead?
placeholder: A TypeError exception was raised
validations:
required: true

View File

@ -30,6 +30,6 @@ body:
- type: textarea
attributes:
label: Proposed Changes
description: "Describe the proposed changes and why they are necessary"
description: Describe the proposed changes and why they are necessary.
validations:
required: true

View File

@ -5,14 +5,15 @@ labels: ["type: feature"]
body:
- type: markdown
attributes:
value: "**NOTE:** This form is only for submitting well-formed proposals to extend or
modify NetBox in some way. If you're trying to solve a problem but can't figure out how,
or if you still need time to work on the details of a proposed new feature, please start
a [discussion](https://github.com/netbox-community/netbox/discussions) instead."
value: >
**NOTE:** This form is only for submitting well-formed proposals to extend or modify
NetBox in some way. If you're trying to solve a problem but can't figure out how, or if
you still need time to work on the details of a proposed new feature, please start a
[discussion](https://github.com/netbox-community/netbox/discussions) instead.
- type: input
attributes:
label: NetBox version
description: "What version of NetBox are you currently running?"
description: What version of NetBox are you currently running?
placeholder: v2.10.4
validations:
required: true
@ -28,26 +29,29 @@ body:
- type: textarea
attributes:
label: Proposed functionality
description: "Describe in detail the new feature or behavior you'd like to propose.
Include any specific changes to work flows, data models, or the user interface."
description: >
Describe in detail the new feature or behavior you'd like to propose. Include any specific
changes to work flows, data models, or the user interface.
validations:
required: true
- type: textarea
attributes:
label: Use case
description: "Explain how adding this functionality would benefit NetBox users. What
need does it address?"
description: >
Explain how adding this functionality would benefit NetBox users. What need does it address?
validations:
required: true
- type: textarea
attributes:
label: Database changes
description: "Note any changes to the database schema necessary to support the new
feature. For example, does the proposal require adding a new model or field? (Not
all new features require database changes.)"
description: >
Note any changes to the database schema necessary to support the new feature. For example,
does the proposal require adding a new model or field? (Not all new features require database
changes.)
- type: textarea
attributes:
label: External dependencies
description: "List any new dependencies on external libraries or services that this
new feature would introduce. For example, does the proposal require the installation
of a new Python package? (Not all new features introduce new dependencies.)"
description: >
List any new dependencies on external libraries or services that this new feature would
introduce. For example, does the proposal require the installation of a new Python package?
(Not all new features introduce new dependencies.)

View File

@ -5,18 +5,20 @@ labels: ["type: housekeeping"]
body:
- type: markdown
attributes:
value: "**NOTE:** This template is for use by maintainers only. Please do not submit
an issue using this template unless you have been specifically asked to do so."
value: >
**NOTE:** This template is for use by maintainers only. Please do not submit
an issue using this template unless you have been specifically asked to do so.
- type: textarea
attributes:
label: Proposed Changes
description: "Describe in detail the new feature or behavior you'd like to propose.
Include any specific changes to work flows, data models, or the user interface."
description: >
Describe in detail the new feature or behavior you'd like to propose.
Include any specific changes to work flows, data models, or the user interface.
validations:
required: true
- type: textarea
attributes:
label: Justification
description: "Please provide justification for the proposed change(s)."
description: Please provide justification for the proposed change(s).
validations:
required: true

View File

@ -20,6 +20,7 @@ jobs:
days-before-stale: 45
days-before-close: 15
exempt-issue-labels: 'status: accepted,status: blocked,status: needs milestone'
operations-per-run: 100
remove-stale-when-updated: false
stale-issue-label: 'pending closure'
stale-issue-message: >

View File

@ -10,7 +10,7 @@ NetBox runs as a web application atop the [Django](https://www.djangoproject.com
Python framework with a [PostgreSQL](https://www.postgresql.org/) database. For a
complete list of requirements, see `requirements.txt`. The code is available [on GitHub](https://github.com/netbox-community/netbox).
The complete documentation for NetBox can be found at [Read the Docs](https://netbox.readthedocs.io/en/stable/).
The complete documentation for NetBox can be found at [Read the Docs](https://netbox.readthedocs.io/en/stable/). A public demo instance is available at https://demo.netbox.dev.
### Discussion

View File

@ -515,6 +515,14 @@ The file path to the location where custom scripts will be kept. By default, thi
---
## SESSION_COOKIE_NAME
Default: `sessionid`
The name used for the session cookie. See the [Django documentation](https://docs.djangoproject.com/en/stable/ref/settings/#session-cookie-name) for more detail.
---
## SESSION_FILE_PATH
Default: None

View File

@ -1,5 +1,24 @@
# NetBox v2.11
## v2.11.3 (FUTURE)
### Enhancements
* [#6197](https://github.com/netbox-community/netbox/issues/6197) - Introduced `SESSION_COOKIE_NAME` config parameter
* [#6318](https://github.com/netbox-community/netbox/issues/6318) - Add OM5 MMF cable type
### Bug Fixes
* [#6240](https://github.com/netbox-community/netbox/issues/6240) - Fix display of available VLAN ranges under VLAN group view
* [#6308](https://github.com/netbox-community/netbox/issues/6308) - Fix linking of available VLANs in VLAN group view
* [#6309](https://github.com/netbox-community/netbox/issues/6309) - Restrict parent VM interface assignment to the parent VM
* [#6313](https://github.com/netbox-community/netbox/issues/6313) - Fix device type instance count under manufacturer view
* [#6321](https://github.com/netbox-community/netbox/issues/6321) - Restore "add an IP" button under prefix IPs view
* [#6333](https://github.com/netbox-community/netbox/issues/6333) - Fix filtering of circuit terminations by primary key
* [#6339](https://github.com/netbox-community/netbox/issues/6339) - Improve ordering of interfaces when viewing virtual chassis master
---
## v2.11.2 (2021-04-27)
### Enhancements

View File

@ -1,6 +1,6 @@
from rest_framework.routers import APIRootView
from circuits import filters
from circuits import filtersets
from circuits.models import *
from dcim.api.views import PassThroughPortMixin
from extras.api.views import CustomFieldModelViewSet
@ -26,7 +26,7 @@ class ProviderViewSet(CustomFieldModelViewSet):
circuit_count=count_related(Circuit, 'provider')
)
serializer_class = serializers.ProviderSerializer
filterset_class = filters.ProviderFilterSet
filterset_class = filtersets.ProviderFilterSet
#
@ -38,7 +38,7 @@ class CircuitTypeViewSet(CustomFieldModelViewSet):
circuit_count=count_related(Circuit, 'type')
)
serializer_class = serializers.CircuitTypeSerializer
filterset_class = filters.CircuitTypeFilterSet
filterset_class = filtersets.CircuitTypeFilterSet
#
@ -50,7 +50,7 @@ class CircuitViewSet(CustomFieldModelViewSet):
'type', 'tenant', 'provider', 'termination_a', 'termination_z'
).prefetch_related('tags')
serializer_class = serializers.CircuitSerializer
filterset_class = filters.CircuitFilterSet
filterset_class = filtersets.CircuitFilterSet
#
@ -62,7 +62,7 @@ class CircuitTerminationViewSet(PassThroughPortMixin, ModelViewSet):
'circuit', 'site', 'provider_network', 'cable'
)
serializer_class = serializers.CircuitTerminationSerializer
filterset_class = filters.CircuitTerminationFilterSet
filterset_class = filtersets.CircuitTerminationFilterSet
brief_prefetch_fields = ['circuit']
@ -73,4 +73,4 @@ class CircuitTerminationViewSet(PassThroughPortMixin, ModelViewSet):
class ProviderNetworkViewSet(CustomFieldModelViewSet):
queryset = ProviderNetwork.objects.prefetch_related('tags')
serializer_class = serializers.ProviderNetworkSerializer
filterset_class = filters.ProviderNetworkFilterSet
filterset_class = filtersets.ProviderNetworkFilterSet

View File

@ -1,13 +1,12 @@
import django_filters
from django.db.models import Q
from dcim.filters import CableTerminationFilterSet
from dcim.filtersets import CableTerminationFilterSet
from dcim.models import Region, Site, SiteGroup
from extras.filters import CustomFieldModelFilterSet, CreatedUpdatedFilterSet
from tenancy.filters import TenancyFilterSet
from utilities.filters import (
BaseFilterSet, NameSlugSearchFilterSet, TagFilter, TreeNodeMultipleChoiceFilter
)
from extras.filters import TagFilter
from netbox.filtersets import ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet
from tenancy.filtersets import TenancyFilterSet
from utilities.filters import TreeNodeMultipleChoiceFilter
from .choices import *
from .models import *
@ -20,7 +19,7 @@ __all__ = (
)
class ProviderFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class ProviderFilterSet(PrimaryModelFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -80,7 +79,7 @@ class ProviderFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdated
)
class ProviderNetworkFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class ProviderNetworkFilterSet(PrimaryModelFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -110,14 +109,14 @@ class ProviderNetworkFilterSet(BaseFilterSet, CustomFieldModelFilterSet, Created
).distinct()
class CircuitTypeFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFilterSet):
class CircuitTypeFilterSet(OrganizationalModelFilterSet):
class Meta:
model = CircuitType
fields = ['id', 'name', 'slug']
class CircuitFilterSet(BaseFilterSet, CustomFieldModelFilterSet, TenancyFilterSet, CreatedUpdatedFilterSet):
class CircuitFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -207,7 +206,7 @@ class CircuitFilterSet(BaseFilterSet, CustomFieldModelFilterSet, TenancyFilterSe
).distinct()
class CircuitTerminationFilterSet(BaseFilterSet, CreatedUpdatedFilterSet, CableTerminationFilterSet):
class CircuitTerminationFilterSet(ChangeLoggedModelFilterSet, CableTerminationFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -233,7 +232,7 @@ class CircuitTerminationFilterSet(BaseFilterSet, CreatedUpdatedFilterSet, CableT
class Meta:
model = CircuitTermination
fields = ['term_side', 'port_speed', 'upstream_speed', 'xconnect_id']
fields = ['id', 'term_side', 'port_speed', 'upstream_speed', 'xconnect_id']
def search(self, queryset, name, value):
if not value.strip():

View File

@ -1,13 +1,14 @@
from django.test import TestCase
from circuits.choices import *
from circuits.filters import *
from circuits.filtersets import *
from circuits.models import *
from dcim.models import Cable, Region, Site, SiteGroup
from tenancy.models import Tenant, TenantGroup
from utilities.testing import ChangeLoggedFilterSetTests
class ProviderTestCase(TestCase):
class ProviderTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Provider.objects.all()
filterset = ProviderFilterSet
@ -61,10 +62,6 @@ class ProviderTestCase(TestCase):
CircuitTermination(circuit=circuits[1], site=sites[0], term_side='A'),
))
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Provider 1', 'Provider 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -103,7 +100,7 @@ class ProviderTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class CircuitTypeTestCase(TestCase):
class CircuitTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = CircuitType.objects.all()
filterset = CircuitTypeFilterSet
@ -116,10 +113,6 @@ class CircuitTypeTestCase(TestCase):
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)
@ -129,7 +122,7 @@ class CircuitTypeTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
class CircuitTestCase(TestCase):
class CircuitTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Circuit.objects.all()
filterset = CircuitFilterSet
@ -213,10 +206,6 @@ class CircuitTestCase(TestCase):
))
CircuitTermination.objects.bulk_create(circuit_terminations)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_cid(self):
params = {'cid': ['Test Circuit 1', 'Test Circuit 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -288,7 +277,7 @@ class CircuitTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
class CircuitTerminationTestCase(TestCase):
class CircuitTerminationTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = CircuitTermination.objects.all()
filterset = CircuitTerminationFilterSet
@ -382,7 +371,7 @@ class CircuitTerminationTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class ProviderNetworkTestCase(TestCase):
class ProviderNetworkTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ProviderNetwork.objects.all()
filterset = ProviderNetworkFilterSet
@ -403,10 +392,6 @@ class ProviderNetworkTestCase(TestCase):
)
ProviderNetwork.objects.bulk_create(provider_networks)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Provider Network 1', 'Provider Network 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)

View File

@ -7,7 +7,7 @@ from netbox.views import generic
from utilities.forms import ConfirmationForm
from utilities.tables import paginate_table
from utilities.utils import count_related
from . import filters, forms, tables
from . import filtersets, forms, tables
from .choices import CircuitTerminationSideChoices
from .models import *
@ -20,7 +20,7 @@ class ProviderListView(generic.ObjectListView):
queryset = Provider.objects.annotate(
count_circuits=count_related(Circuit, 'provider')
)
filterset = filters.ProviderFilterSet
filterset = filtersets.ProviderFilterSet
filterset_form = forms.ProviderFilterForm
table = tables.ProviderTable
@ -63,7 +63,7 @@ class ProviderBulkEditView(generic.BulkEditView):
queryset = Provider.objects.annotate(
count_circuits=count_related(Circuit, 'provider')
)
filterset = filters.ProviderFilterSet
filterset = filtersets.ProviderFilterSet
table = tables.ProviderTable
form = forms.ProviderBulkEditForm
@ -72,7 +72,7 @@ class ProviderBulkDeleteView(generic.BulkDeleteView):
queryset = Provider.objects.annotate(
count_circuits=count_related(Circuit, 'provider')
)
filterset = filters.ProviderFilterSet
filterset = filtersets.ProviderFilterSet
table = tables.ProviderTable
@ -82,7 +82,7 @@ class ProviderBulkDeleteView(generic.BulkDeleteView):
class ProviderNetworkListView(generic.ObjectListView):
queryset = ProviderNetwork.objects.all()
filterset = filters.ProviderNetworkFilterSet
filterset = filtersets.ProviderNetworkFilterSet
filterset_form = forms.ProviderNetworkFilterForm
table = tables.ProviderNetworkTable
@ -125,14 +125,14 @@ class ProviderNetworkBulkImportView(generic.BulkImportView):
class ProviderNetworkBulkEditView(generic.BulkEditView):
queryset = ProviderNetwork.objects.all()
filterset = filters.ProviderNetworkFilterSet
filterset = filtersets.ProviderNetworkFilterSet
table = tables.ProviderNetworkTable
form = forms.ProviderNetworkBulkEditForm
class ProviderNetworkBulkDeleteView(generic.BulkDeleteView):
queryset = ProviderNetwork.objects.all()
filterset = filters.ProviderNetworkFilterSet
filterset = filtersets.ProviderNetworkFilterSet
table = tables.ProviderNetworkTable
@ -183,7 +183,7 @@ class CircuitTypeBulkEditView(generic.BulkEditView):
queryset = CircuitType.objects.annotate(
circuit_count=count_related(Circuit, 'type')
)
filterset = filters.CircuitTypeFilterSet
filterset = filtersets.CircuitTypeFilterSet
table = tables.CircuitTypeTable
form = forms.CircuitTypeBulkEditForm
@ -203,7 +203,7 @@ class CircuitListView(generic.ObjectListView):
queryset = Circuit.objects.prefetch_related(
'provider', 'type', 'tenant', 'termination_a', 'termination_z'
)
filterset = filters.CircuitFilterSet
filterset = filtersets.CircuitFilterSet
filterset_form = forms.CircuitFilterForm
table = tables.CircuitTable
@ -252,7 +252,7 @@ class CircuitBulkEditView(generic.BulkEditView):
queryset = Circuit.objects.prefetch_related(
'provider', 'type', 'tenant', 'terminations'
)
filterset = filters.CircuitFilterSet
filterset = filtersets.CircuitFilterSet
table = tables.CircuitTable
form = forms.CircuitBulkEditForm
@ -261,7 +261,7 @@ class CircuitBulkDeleteView(generic.BulkDeleteView):
queryset = Circuit.objects.prefetch_related(
'provider', 'type', 'tenant', 'terminations'
)
filterset = filters.CircuitFilterSet
filterset = filtersets.CircuitFilterSet
table = tables.CircuitTable

View File

@ -16,7 +16,7 @@ from rest_framework.routers import APIRootView
from rest_framework.viewsets import GenericViewSet, ViewSet
from circuits.models import Circuit
from dcim import filters
from dcim import filtersets
from dcim.models import *
from extras.api.views import ConfigContextQuerySetMixin, CustomFieldModelViewSet
from ipam.models import Prefix, VLAN
@ -103,7 +103,7 @@ class RegionViewSet(CustomFieldModelViewSet):
cumulative=True
)
serializer_class = serializers.RegionSerializer
filterset_class = filters.RegionFilterSet
filterset_class = filtersets.RegionFilterSet
#
@ -119,7 +119,7 @@ class SiteGroupViewSet(CustomFieldModelViewSet):
cumulative=True
)
serializer_class = serializers.SiteGroupSerializer
filterset_class = filters.SiteGroupFilterSet
filterset_class = filtersets.SiteGroupFilterSet
#
@ -138,7 +138,7 @@ class SiteViewSet(CustomFieldModelViewSet):
virtualmachine_count=count_related(VirtualMachine, 'cluster__site')
)
serializer_class = serializers.SiteSerializer
filterset_class = filters.SiteFilterSet
filterset_class = filtersets.SiteFilterSet
#
@ -160,7 +160,7 @@ class LocationViewSet(CustomFieldModelViewSet):
cumulative=True
).prefetch_related('site')
serializer_class = serializers.LocationSerializer
filterset_class = filters.LocationFilterSet
filterset_class = filtersets.LocationFilterSet
#
@ -172,7 +172,7 @@ class RackRoleViewSet(CustomFieldModelViewSet):
rack_count=count_related(Rack, 'role')
)
serializer_class = serializers.RackRoleSerializer
filterset_class = filters.RackRoleFilterSet
filterset_class = filtersets.RackRoleFilterSet
#
@ -187,7 +187,7 @@ class RackViewSet(CustomFieldModelViewSet):
powerfeed_count=count_related(PowerFeed, 'rack')
)
serializer_class = serializers.RackSerializer
filterset_class = filters.RackFilterSet
filterset_class = filtersets.RackFilterSet
@swagger_auto_schema(
responses={200: serializers.RackUnitSerializer(many=True)},
@ -244,7 +244,7 @@ class RackViewSet(CustomFieldModelViewSet):
class RackReservationViewSet(ModelViewSet):
queryset = RackReservation.objects.prefetch_related('rack', 'user', 'tenant')
serializer_class = serializers.RackReservationSerializer
filterset_class = filters.RackReservationFilterSet
filterset_class = filtersets.RackReservationFilterSet
# Assign user from request
def perform_create(self, serializer):
@ -262,7 +262,7 @@ class ManufacturerViewSet(CustomFieldModelViewSet):
platform_count=count_related(Platform, 'manufacturer')
)
serializer_class = serializers.ManufacturerSerializer
filterset_class = filters.ManufacturerFilterSet
filterset_class = filtersets.ManufacturerFilterSet
#
@ -274,7 +274,7 @@ class DeviceTypeViewSet(CustomFieldModelViewSet):
device_count=count_related(Device, 'device_type')
)
serializer_class = serializers.DeviceTypeSerializer
filterset_class = filters.DeviceTypeFilterSet
filterset_class = filtersets.DeviceTypeFilterSet
brief_prefetch_fields = ['manufacturer']
@ -285,49 +285,49 @@ class DeviceTypeViewSet(CustomFieldModelViewSet):
class ConsolePortTemplateViewSet(ModelViewSet):
queryset = ConsolePortTemplate.objects.prefetch_related('device_type__manufacturer')
serializer_class = serializers.ConsolePortTemplateSerializer
filterset_class = filters.ConsolePortTemplateFilterSet
filterset_class = filtersets.ConsolePortTemplateFilterSet
class ConsoleServerPortTemplateViewSet(ModelViewSet):
queryset = ConsoleServerPortTemplate.objects.prefetch_related('device_type__manufacturer')
serializer_class = serializers.ConsoleServerPortTemplateSerializer
filterset_class = filters.ConsoleServerPortTemplateFilterSet
filterset_class = filtersets.ConsoleServerPortTemplateFilterSet
class PowerPortTemplateViewSet(ModelViewSet):
queryset = PowerPortTemplate.objects.prefetch_related('device_type__manufacturer')
serializer_class = serializers.PowerPortTemplateSerializer
filterset_class = filters.PowerPortTemplateFilterSet
filterset_class = filtersets.PowerPortTemplateFilterSet
class PowerOutletTemplateViewSet(ModelViewSet):
queryset = PowerOutletTemplate.objects.prefetch_related('device_type__manufacturer')
serializer_class = serializers.PowerOutletTemplateSerializer
filterset_class = filters.PowerOutletTemplateFilterSet
filterset_class = filtersets.PowerOutletTemplateFilterSet
class InterfaceTemplateViewSet(ModelViewSet):
queryset = InterfaceTemplate.objects.prefetch_related('device_type__manufacturer')
serializer_class = serializers.InterfaceTemplateSerializer
filterset_class = filters.InterfaceTemplateFilterSet
filterset_class = filtersets.InterfaceTemplateFilterSet
class FrontPortTemplateViewSet(ModelViewSet):
queryset = FrontPortTemplate.objects.prefetch_related('device_type__manufacturer')
serializer_class = serializers.FrontPortTemplateSerializer
filterset_class = filters.FrontPortTemplateFilterSet
filterset_class = filtersets.FrontPortTemplateFilterSet
class RearPortTemplateViewSet(ModelViewSet):
queryset = RearPortTemplate.objects.prefetch_related('device_type__manufacturer')
serializer_class = serializers.RearPortTemplateSerializer
filterset_class = filters.RearPortTemplateFilterSet
filterset_class = filtersets.RearPortTemplateFilterSet
class DeviceBayTemplateViewSet(ModelViewSet):
queryset = DeviceBayTemplate.objects.prefetch_related('device_type__manufacturer')
serializer_class = serializers.DeviceBayTemplateSerializer
filterset_class = filters.DeviceBayTemplateFilterSet
filterset_class = filtersets.DeviceBayTemplateFilterSet
#
@ -340,7 +340,7 @@ class DeviceRoleViewSet(CustomFieldModelViewSet):
virtualmachine_count=count_related(VirtualMachine, 'role')
)
serializer_class = serializers.DeviceRoleSerializer
filterset_class = filters.DeviceRoleFilterSet
filterset_class = filtersets.DeviceRoleFilterSet
#
@ -353,7 +353,7 @@ class PlatformViewSet(CustomFieldModelViewSet):
virtualmachine_count=count_related(VirtualMachine, 'platform')
)
serializer_class = serializers.PlatformSerializer
filterset_class = filters.PlatformFilterSet
filterset_class = filtersets.PlatformFilterSet
#
@ -365,7 +365,7 @@ class DeviceViewSet(ConfigContextQuerySetMixin, CustomFieldModelViewSet):
'device_type__manufacturer', 'device_role', 'tenant', 'platform', 'site', 'location', 'rack', 'parent_bay',
'virtual_chassis__master', 'primary_ip4__nat_outside', 'primary_ip6__nat_outside', 'tags',
)
filterset_class = filters.DeviceFilterSet
filterset_class = filtersets.DeviceFilterSet
def get_serializer_class(self):
"""
@ -510,7 +510,7 @@ class DeviceViewSet(ConfigContextQuerySetMixin, CustomFieldModelViewSet):
class ConsolePortViewSet(PathEndpointMixin, ModelViewSet):
queryset = ConsolePort.objects.prefetch_related('device', '_path__destination', 'cable', '_cable_peer', 'tags')
serializer_class = serializers.ConsolePortSerializer
filterset_class = filters.ConsolePortFilterSet
filterset_class = filtersets.ConsolePortFilterSet
brief_prefetch_fields = ['device']
@ -519,21 +519,21 @@ class ConsoleServerPortViewSet(PathEndpointMixin, ModelViewSet):
'device', '_path__destination', 'cable', '_cable_peer', 'tags'
)
serializer_class = serializers.ConsoleServerPortSerializer
filterset_class = filters.ConsoleServerPortFilterSet
filterset_class = filtersets.ConsoleServerPortFilterSet
brief_prefetch_fields = ['device']
class PowerPortViewSet(PathEndpointMixin, ModelViewSet):
queryset = PowerPort.objects.prefetch_related('device', '_path__destination', 'cable', '_cable_peer', 'tags')
serializer_class = serializers.PowerPortSerializer
filterset_class = filters.PowerPortFilterSet
filterset_class = filtersets.PowerPortFilterSet
brief_prefetch_fields = ['device']
class PowerOutletViewSet(PathEndpointMixin, ModelViewSet):
queryset = PowerOutlet.objects.prefetch_related('device', '_path__destination', 'cable', '_cable_peer', 'tags')
serializer_class = serializers.PowerOutletSerializer
filterset_class = filters.PowerOutletFilterSet
filterset_class = filtersets.PowerOutletFilterSet
brief_prefetch_fields = ['device']
@ -542,35 +542,35 @@ class InterfaceViewSet(PathEndpointMixin, ModelViewSet):
'device', 'parent', 'lag', '_path__destination', 'cable', '_cable_peer', 'ip_addresses', 'tags'
)
serializer_class = serializers.InterfaceSerializer
filterset_class = filters.InterfaceFilterSet
filterset_class = filtersets.InterfaceFilterSet
brief_prefetch_fields = ['device']
class FrontPortViewSet(PassThroughPortMixin, ModelViewSet):
queryset = FrontPort.objects.prefetch_related('device__device_type__manufacturer', 'rear_port', 'cable', 'tags')
serializer_class = serializers.FrontPortSerializer
filterset_class = filters.FrontPortFilterSet
filterset_class = filtersets.FrontPortFilterSet
brief_prefetch_fields = ['device']
class RearPortViewSet(PassThroughPortMixin, ModelViewSet):
queryset = RearPort.objects.prefetch_related('device__device_type__manufacturer', 'cable', 'tags')
serializer_class = serializers.RearPortSerializer
filterset_class = filters.RearPortFilterSet
filterset_class = filtersets.RearPortFilterSet
brief_prefetch_fields = ['device']
class DeviceBayViewSet(ModelViewSet):
queryset = DeviceBay.objects.prefetch_related('installed_device').prefetch_related('tags')
serializer_class = serializers.DeviceBaySerializer
filterset_class = filters.DeviceBayFilterSet
filterset_class = filtersets.DeviceBayFilterSet
brief_prefetch_fields = ['device']
class InventoryItemViewSet(ModelViewSet):
queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer').prefetch_related('tags')
serializer_class = serializers.InventoryItemSerializer
filterset_class = filters.InventoryItemFilterSet
filterset_class = filtersets.InventoryItemFilterSet
brief_prefetch_fields = ['device']
@ -583,7 +583,7 @@ class ConsoleConnectionViewSet(ListModelMixin, GenericViewSet):
_path__destination_id__isnull=False
)
serializer_class = serializers.ConsolePortSerializer
filterset_class = filters.ConsoleConnectionFilterSet
filterset_class = filtersets.ConsoleConnectionFilterSet
class PowerConnectionViewSet(ListModelMixin, GenericViewSet):
@ -591,7 +591,7 @@ class PowerConnectionViewSet(ListModelMixin, GenericViewSet):
_path__destination_id__isnull=False
)
serializer_class = serializers.PowerPortSerializer
filterset_class = filters.PowerConnectionFilterSet
filterset_class = filtersets.PowerConnectionFilterSet
class InterfaceConnectionViewSet(ListModelMixin, GenericViewSet):
@ -603,7 +603,7 @@ class InterfaceConnectionViewSet(ListModelMixin, GenericViewSet):
pk__lt=F('_path__destination_id')
)
serializer_class = serializers.InterfaceConnectionSerializer
filterset_class = filters.InterfaceConnectionFilterSet
filterset_class = filtersets.InterfaceConnectionFilterSet
#
@ -616,7 +616,7 @@ class CableViewSet(ModelViewSet):
'termination_a', 'termination_b'
)
serializer_class = serializers.CableSerializer
filterset_class = filters.CableFilterSet
filterset_class = filtersets.CableFilterSet
#
@ -628,7 +628,7 @@ class VirtualChassisViewSet(ModelViewSet):
member_count=count_related(Device, 'virtual_chassis')
)
serializer_class = serializers.VirtualChassisSerializer
filterset_class = filters.VirtualChassisFilterSet
filterset_class = filtersets.VirtualChassisFilterSet
brief_prefetch_fields = ['master']
@ -643,7 +643,7 @@ class PowerPanelViewSet(ModelViewSet):
powerfeed_count=count_related(PowerFeed, 'power_panel')
)
serializer_class = serializers.PowerPanelSerializer
filterset_class = filters.PowerPanelFilterSet
filterset_class = filtersets.PowerPanelFilterSet
#
@ -655,7 +655,7 @@ class PowerFeedViewSet(PathEndpointMixin, CustomFieldModelViewSet):
'power_panel', 'rack', '_path__destination', 'cable', '_cable_peer', 'tags'
)
serializer_class = serializers.PowerFeedSerializer
filterset_class = filters.PowerFeedFilterSet
filterset_class = filtersets.PowerFeedFilterSet
#

View File

@ -1001,6 +1001,7 @@ class CableTypeChoices(ChoiceSet):
TYPE_MMF_OM2 = 'mmf-om2'
TYPE_MMF_OM3 = 'mmf-om3'
TYPE_MMF_OM4 = 'mmf-om4'
TYPE_MMF_OM5 = 'mmf-om5'
TYPE_SMF = 'smf'
TYPE_SMF_OS1 = 'smf-os1'
TYPE_SMF_OS2 = 'smf-os2'
@ -1031,6 +1032,7 @@ class CableTypeChoices(ChoiceSet):
(TYPE_MMF_OM2, 'Multimode Fiber (OM2)'),
(TYPE_MMF_OM3, 'Multimode Fiber (OM3)'),
(TYPE_MMF_OM4, 'Multimode Fiber (OM4)'),
(TYPE_MMF_OM5, 'Multimode Fiber (OM5)'),
(TYPE_SMF, 'Singlemode Fiber'),
(TYPE_SMF_OS1, 'Singlemode Fiber (OS1)'),
(TYPE_SMF_OS2, 'Singlemode Fiber (OS2)'),

View File

@ -1,13 +1,16 @@
import django_filters
from django.contrib.auth.models import User
from extras.filters import CustomFieldModelFilterSet, LocalConfigContextFilterSet, CreatedUpdatedFilterSet
from tenancy.filters import TenancyFilterSet
from extras.filters import TagFilter
from extras.filtersets import LocalConfigContextFilterSet
from netbox.filtersets import (
BaseFilterSet, ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet,
)
from tenancy.filtersets import TenancyFilterSet
from tenancy.models import Tenant
from utilities.choices import ColorChoices
from utilities.filters import (
BaseFilterSet, MultiValueCharFilter, MultiValueMACAddressFilter, MultiValueNumberFilter,
NameSlugSearchFilterSet, TagFilter, TreeNodeMultipleChoiceFilter,
MultiValueCharFilter, MultiValueMACAddressFilter, MultiValueNumberFilter, TreeNodeMultipleChoiceFilter,
)
from virtualization.models import Cluster
from .choices import *
@ -57,7 +60,7 @@ __all__ = (
)
class RegionFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFilterSet):
class RegionFilterSet(OrganizationalModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=Region.objects.all(),
label='Parent region (ID)',
@ -74,7 +77,7 @@ class RegionFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFilt
fields = ['id', 'name', 'slug', 'description']
class SiteGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFilterSet):
class SiteGroupFilterSet(OrganizationalModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=SiteGroup.objects.all(),
label='Parent site group (ID)',
@ -91,7 +94,7 @@ class SiteGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedF
fields = ['id', 'name', 'slug', 'description']
class SiteFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class SiteFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -154,7 +157,7 @@ class SiteFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet,
return queryset.filter(qs_filter)
class LocationFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFilterSet):
class LocationFilterSet(OrganizationalModelFilterSet):
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
@ -218,14 +221,14 @@ class LocationFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFi
)
class RackRoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFilterSet):
class RackRoleFilterSet(OrganizationalModelFilterSet):
class Meta:
model = RackRole
fields = ['id', 'name', 'slug', 'color']
class RackFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class RackFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -323,7 +326,7 @@ class RackFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet,
)
class RackReservationFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class RackReservationFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -383,14 +386,14 @@ class RackReservationFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModel
)
class ManufacturerFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFilterSet):
class ManufacturerFilterSet(OrganizationalModelFilterSet):
class Meta:
model = Manufacturer
fields = ['id', 'name', 'slug', 'description']
class DeviceTypeFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class DeviceTypeFilterSet(PrimaryModelFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -476,7 +479,7 @@ class DeviceTypeFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdat
return queryset.exclude(devicebaytemplates__isnull=value)
class DeviceTypeComponentFilterSet(NameSlugSearchFilterSet, CreatedUpdatedFilterSet):
class DeviceTypeComponentFilterSet(django_filters.FilterSet):
devicetype_id = django_filters.ModelMultipleChoiceFilter(
queryset=DeviceType.objects.all(),
field_name='device_type_id',
@ -484,28 +487,28 @@ class DeviceTypeComponentFilterSet(NameSlugSearchFilterSet, CreatedUpdatedFilter
)
class ConsolePortTemplateFilterSet(BaseFilterSet, DeviceTypeComponentFilterSet):
class ConsolePortTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet):
class Meta:
model = ConsolePortTemplate
fields = ['id', 'name', 'type']
class ConsoleServerPortTemplateFilterSet(BaseFilterSet, DeviceTypeComponentFilterSet):
class ConsoleServerPortTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet):
class Meta:
model = ConsoleServerPortTemplate
fields = ['id', 'name', 'type']
class PowerPortTemplateFilterSet(BaseFilterSet, DeviceTypeComponentFilterSet):
class PowerPortTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet):
class Meta:
model = PowerPortTemplate
fields = ['id', 'name', 'type', 'maximum_draw', 'allocated_draw']
class PowerOutletTemplateFilterSet(BaseFilterSet, DeviceTypeComponentFilterSet):
class PowerOutletTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet):
feed_leg = django_filters.MultipleChoiceFilter(
choices=PowerOutletFeedLegChoices,
null_value=None
@ -516,7 +519,7 @@ class PowerOutletTemplateFilterSet(BaseFilterSet, DeviceTypeComponentFilterSet):
fields = ['id', 'name', 'type', 'feed_leg']
class InterfaceTemplateFilterSet(BaseFilterSet, DeviceTypeComponentFilterSet):
class InterfaceTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet):
type = django_filters.MultipleChoiceFilter(
choices=InterfaceTypeChoices,
null_value=None
@ -527,7 +530,7 @@ class InterfaceTemplateFilterSet(BaseFilterSet, DeviceTypeComponentFilterSet):
fields = ['id', 'name', 'type', 'mgmt_only']
class FrontPortTemplateFilterSet(BaseFilterSet, DeviceTypeComponentFilterSet):
class FrontPortTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet):
type = django_filters.MultipleChoiceFilter(
choices=PortTypeChoices,
null_value=None
@ -538,7 +541,7 @@ class FrontPortTemplateFilterSet(BaseFilterSet, DeviceTypeComponentFilterSet):
fields = ['id', 'name', 'type']
class RearPortTemplateFilterSet(BaseFilterSet, DeviceTypeComponentFilterSet):
class RearPortTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet):
type = django_filters.MultipleChoiceFilter(
choices=PortTypeChoices,
null_value=None
@ -549,21 +552,21 @@ class RearPortTemplateFilterSet(BaseFilterSet, DeviceTypeComponentFilterSet):
fields = ['id', 'name', 'type', 'positions']
class DeviceBayTemplateFilterSet(BaseFilterSet, DeviceTypeComponentFilterSet):
class DeviceBayTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet):
class Meta:
model = DeviceBayTemplate
fields = ['id', 'name']
class DeviceRoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFilterSet):
class DeviceRoleFilterSet(OrganizationalModelFilterSet):
class Meta:
model = DeviceRole
fields = ['id', 'name', 'slug', 'color', 'vm_role']
class PlatformFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFilterSet):
class PlatformFilterSet(OrganizationalModelFilterSet):
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
field_name='manufacturer',
queryset=Manufacturer.objects.all(),
@ -581,13 +584,7 @@ class PlatformFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFi
fields = ['id', 'name', 'slug', 'napalm_driver', 'description']
class DeviceFilterSet(
BaseFilterSet,
TenancyFilterSet,
LocalConfigContextFilterSet,
CustomFieldModelFilterSet,
CreatedUpdatedFilterSet
):
class DeviceFilterSet(PrimaryModelFilterSet, TenancyFilterSet, LocalConfigContextFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -792,7 +789,7 @@ class DeviceFilterSet(
return queryset.exclude(devicebays__isnull=value)
class DeviceComponentFilterSet(CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class DeviceComponentFilterSet(django_filters.FilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -876,7 +873,7 @@ class PathEndpointFilterSet(django_filters.FilterSet):
return queryset.filter(Q(_path__isnull=True) | Q(_path__is_active=False))
class ConsolePortFilterSet(BaseFilterSet, DeviceComponentFilterSet, CableTerminationFilterSet, PathEndpointFilterSet):
class ConsolePortFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet, CableTerminationFilterSet, PathEndpointFilterSet):
type = django_filters.MultipleChoiceFilter(
choices=ConsolePortTypeChoices,
null_value=None
@ -887,12 +884,7 @@ class ConsolePortFilterSet(BaseFilterSet, DeviceComponentFilterSet, CableTermina
fields = ['id', 'name', 'label', 'description']
class ConsoleServerPortFilterSet(
BaseFilterSet,
DeviceComponentFilterSet,
CableTerminationFilterSet,
PathEndpointFilterSet
):
class ConsoleServerPortFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet, CableTerminationFilterSet, PathEndpointFilterSet):
type = django_filters.MultipleChoiceFilter(
choices=ConsolePortTypeChoices,
null_value=None
@ -903,7 +895,7 @@ class ConsoleServerPortFilterSet(
fields = ['id', 'name', 'label', 'description']
class PowerPortFilterSet(BaseFilterSet, DeviceComponentFilterSet, CableTerminationFilterSet, PathEndpointFilterSet):
class PowerPortFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet, CableTerminationFilterSet, PathEndpointFilterSet):
type = django_filters.MultipleChoiceFilter(
choices=PowerPortTypeChoices,
null_value=None
@ -914,7 +906,7 @@ class PowerPortFilterSet(BaseFilterSet, DeviceComponentFilterSet, CableTerminati
fields = ['id', 'name', 'label', 'maximum_draw', 'allocated_draw', 'description']
class PowerOutletFilterSet(BaseFilterSet, DeviceComponentFilterSet, CableTerminationFilterSet, PathEndpointFilterSet):
class PowerOutletFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet, CableTerminationFilterSet, PathEndpointFilterSet):
type = django_filters.MultipleChoiceFilter(
choices=PowerOutletTypeChoices,
null_value=None
@ -929,7 +921,7 @@ class PowerOutletFilterSet(BaseFilterSet, DeviceComponentFilterSet, CableTermina
fields = ['id', 'name', 'label', 'feed_leg', 'description']
class InterfaceFilterSet(BaseFilterSet, DeviceComponentFilterSet, CableTerminationFilterSet, PathEndpointFilterSet):
class InterfaceFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet, CableTerminationFilterSet, PathEndpointFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -1027,7 +1019,7 @@ class InterfaceFilterSet(BaseFilterSet, DeviceComponentFilterSet, CableTerminati
}.get(value, queryset.none())
class FrontPortFilterSet(BaseFilterSet, DeviceComponentFilterSet, CableTerminationFilterSet):
class FrontPortFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet, CableTerminationFilterSet):
type = django_filters.MultipleChoiceFilter(
choices=PortTypeChoices,
null_value=None
@ -1038,7 +1030,7 @@ class FrontPortFilterSet(BaseFilterSet, DeviceComponentFilterSet, CableTerminati
fields = ['id', 'name', 'label', 'type', 'description']
class RearPortFilterSet(BaseFilterSet, DeviceComponentFilterSet, CableTerminationFilterSet):
class RearPortFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet, CableTerminationFilterSet):
type = django_filters.MultipleChoiceFilter(
choices=PortTypeChoices,
null_value=None
@ -1049,14 +1041,14 @@ class RearPortFilterSet(BaseFilterSet, DeviceComponentFilterSet, CableTerminatio
fields = ['id', 'name', 'label', 'type', 'positions', 'description']
class DeviceBayFilterSet(BaseFilterSet, DeviceComponentFilterSet):
class DeviceBayFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet):
class Meta:
model = DeviceBay
fields = ['id', 'name', 'label', 'description']
class InventoryItemFilterSet(BaseFilterSet, DeviceComponentFilterSet):
class InventoryItemFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -1129,7 +1121,7 @@ class InventoryItemFilterSet(BaseFilterSet, DeviceComponentFilterSet):
return queryset.filter(qs_filter)
class VirtualChassisFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class VirtualChassisFilterSet(PrimaryModelFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -1209,7 +1201,7 @@ class VirtualChassisFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedU
return queryset.filter(qs_filter).distinct()
class CableFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class CableFilterSet(PrimaryModelFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -1273,7 +1265,7 @@ class CableFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFil
return queryset
class ConnectionFilterSet:
class ConnectionFilterSet(BaseFilterSet):
def filter_site(self, queryset, name, value):
if not value.strip():
@ -1286,7 +1278,7 @@ class ConnectionFilterSet:
return queryset.filter(**{f'{name}__in': value})
class ConsoleConnectionFilterSet(ConnectionFilterSet, BaseFilterSet):
class ConsoleConnectionFilterSet(ConnectionFilterSet):
site = django_filters.CharFilter(
method='filter_site',
label='Site (slug)',
@ -1304,7 +1296,7 @@ class ConsoleConnectionFilterSet(ConnectionFilterSet, BaseFilterSet):
fields = ['name']
class PowerConnectionFilterSet(ConnectionFilterSet, BaseFilterSet):
class PowerConnectionFilterSet(ConnectionFilterSet):
site = django_filters.CharFilter(
method='filter_site',
label='Site (slug)',
@ -1322,7 +1314,7 @@ class PowerConnectionFilterSet(ConnectionFilterSet, BaseFilterSet):
fields = ['name']
class InterfaceConnectionFilterSet(ConnectionFilterSet, BaseFilterSet):
class InterfaceConnectionFilterSet(ConnectionFilterSet):
site = django_filters.CharFilter(
method='filter_site',
label='Site (slug)',
@ -1340,7 +1332,7 @@ class InterfaceConnectionFilterSet(ConnectionFilterSet, BaseFilterSet):
fields = []
class PowerPanelFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class PowerPanelFilterSet(PrimaryModelFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -1402,13 +1394,7 @@ class PowerPanelFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdat
return queryset.filter(qs_filter)
class PowerFeedFilterSet(
BaseFilterSet,
CableTerminationFilterSet,
PathEndpointFilterSet,
CustomFieldModelFilterSet,
CreatedUpdatedFilterSet
):
class PowerFeedFilterSet(PrimaryModelFilterSet, CableTerminationFilterSet, PathEndpointFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',

View File

@ -520,6 +520,7 @@ class DeviceInterfaceTable(InterfaceTable):
'description', 'mark_connected', 'cable', 'cable_color', 'cable_peer', 'connection', 'tags', 'ip_addresses',
'untagged_vlan', 'tagged_vlans', 'actions',
)
order_by = ('name',)
default_columns = (
'pk', 'name', 'label', 'enabled', 'type', 'parent', 'lag', 'mtu', 'mode', 'description', 'ip_addresses',
'cable', 'connection', 'actions',

View File

@ -2,14 +2,15 @@ from django.contrib.auth.models import User
from django.test import TestCase
from dcim.choices import *
from dcim.filters import *
from dcim.filtersets import *
from dcim.models import *
from ipam.models import IPAddress
from tenancy.models import Tenant, TenantGroup
from utilities.testing import ChangeLoggedFilterSetTests
from virtualization.models import Cluster, ClusterType
class RegionTestCase(TestCase):
class RegionTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Region.objects.all()
filterset = RegionFilterSet
@ -35,10 +36,6 @@ class RegionTestCase(TestCase):
for region in child_regions:
region.save()
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Region 1', 'Region 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -59,7 +56,7 @@ class RegionTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
class SiteGroupTestCase(TestCase):
class SiteGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = SiteGroup.objects.all()
filterset = SiteGroupFilterSet
@ -85,10 +82,6 @@ class SiteGroupTestCase(TestCase):
for sitegroup in child_sitegroups:
sitegroup.save()
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Site Group 1', 'Site Group 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -109,7 +102,7 @@ class SiteGroupTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
class SiteTestCase(TestCase):
class SiteTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Site.objects.all()
filterset = SiteFilterSet
@ -154,10 +147,6 @@ class SiteTestCase(TestCase):
)
Site.objects.bulk_create(sites)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -227,7 +216,7 @@ class SiteTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class LocationTestCase(TestCase):
class LocationTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Location.objects.all()
filterset = LocationFilterSet
@ -273,10 +262,6 @@ class LocationTestCase(TestCase):
for location in locations:
location.save()
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Location 1', 'Location 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -318,7 +303,7 @@ class LocationTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class RackRoleTestCase(TestCase):
class RackRoleTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = RackRole.objects.all()
filterset = RackRoleFilterSet
@ -332,10 +317,6 @@ class RackRoleTestCase(TestCase):
)
RackRole.objects.bulk_create(rack_roles)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -349,7 +330,7 @@ class RackRoleTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class RackTestCase(TestCase):
class RackTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Rack.objects.all()
filterset = RackFilterSet
@ -416,10 +397,6 @@ class RackTestCase(TestCase):
)
Rack.objects.bulk_create(racks)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -523,7 +500,7 @@ class RackTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class RackReservationTestCase(TestCase):
class RackReservationTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = RackReservation.objects.all()
filterset = RackReservationFilterSet
@ -581,10 +558,6 @@ class RackReservationTestCase(TestCase):
)
RackReservation.objects.bulk_create(reservations)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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]}
@ -621,7 +594,7 @@ class RackReservationTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class ManufacturerTestCase(TestCase):
class ManufacturerTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Manufacturer.objects.all()
filterset = ManufacturerFilterSet
@ -635,10 +608,6 @@ class ManufacturerTestCase(TestCase):
)
Manufacturer.objects.bulk_create(manufacturers)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -652,7 +621,7 @@ class ManufacturerTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class DeviceTypeTestCase(TestCase):
class DeviceTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = DeviceType.objects.all()
filterset = DeviceTypeFilterSet
@ -708,10 +677,6 @@ class DeviceTypeTestCase(TestCase):
DeviceBayTemplate(device_type=device_types[1], name='Device Bay 2'),
))
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -788,7 +753,7 @@ class DeviceTypeTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
class ConsolePortTemplateTestCase(TestCase):
class ConsolePortTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ConsolePortTemplate.objects.all()
filterset = ConsolePortTemplateFilterSet
@ -810,10 +775,6 @@ class ConsolePortTemplateTestCase(TestCase):
ConsolePortTemplate(device_type=device_types[2], name='Console Port 3'),
))
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -824,7 +785,7 @@ class ConsolePortTemplateTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class ConsoleServerPortTemplateTestCase(TestCase):
class ConsoleServerPortTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ConsoleServerPortTemplate.objects.all()
filterset = ConsoleServerPortTemplateFilterSet
@ -846,10 +807,6 @@ class ConsoleServerPortTemplateTestCase(TestCase):
ConsoleServerPortTemplate(device_type=device_types[2], name='Console Server Port 3'),
))
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -860,7 +817,7 @@ class ConsoleServerPortTemplateTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class PowerPortTemplateTestCase(TestCase):
class PowerPortTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = PowerPortTemplate.objects.all()
filterset = PowerPortTemplateFilterSet
@ -882,10 +839,6 @@ class PowerPortTemplateTestCase(TestCase):
PowerPortTemplate(device_type=device_types[2], name='Power Port 3', maximum_draw=300, allocated_draw=150),
))
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -904,7 +857,7 @@ class PowerPortTemplateTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class PowerOutletTemplateTestCase(TestCase):
class PowerOutletTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = PowerOutletTemplate.objects.all()
filterset = PowerOutletTemplateFilterSet
@ -926,10 +879,6 @@ class PowerOutletTemplateTestCase(TestCase):
PowerOutletTemplate(device_type=device_types[2], name='Power Outlet 3', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_C),
))
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -944,7 +893,7 @@ class PowerOutletTemplateTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class InterfaceTemplateTestCase(TestCase):
class InterfaceTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = InterfaceTemplate.objects.all()
filterset = InterfaceTemplateFilterSet
@ -966,10 +915,6 @@ class InterfaceTemplateTestCase(TestCase):
InterfaceTemplate(device_type=device_types[2], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_SFP, mgmt_only=False),
))
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -990,7 +935,7 @@ class InterfaceTemplateTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class FrontPortTemplateTestCase(TestCase):
class FrontPortTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = FrontPortTemplate.objects.all()
filterset = FrontPortTemplateFilterSet
@ -1019,10 +964,6 @@ class FrontPortTemplateTestCase(TestCase):
FrontPortTemplate(device_type=device_types[2], name='Front Port 3', rear_port=rear_ports[2], type=PortTypeChoices.TYPE_BNC),
))
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -1037,7 +978,7 @@ class FrontPortTemplateTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class RearPortTemplateTestCase(TestCase):
class RearPortTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = RearPortTemplate.objects.all()
filterset = RearPortTemplateFilterSet
@ -1059,10 +1000,6 @@ class RearPortTemplateTestCase(TestCase):
RearPortTemplate(device_type=device_types[2], name='Rear Port 3', type=PortTypeChoices.TYPE_BNC, positions=3),
))
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -1081,7 +1018,7 @@ class RearPortTemplateTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class DeviceBayTemplateTestCase(TestCase):
class DeviceBayTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = DeviceBayTemplate.objects.all()
filterset = DeviceBayTemplateFilterSet
@ -1103,10 +1040,6 @@ class DeviceBayTemplateTestCase(TestCase):
DeviceBayTemplate(device_type=device_types[2], name='Device Bay 3'),
))
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -1117,7 +1050,7 @@ class DeviceBayTemplateTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class DeviceRoleTestCase(TestCase):
class DeviceRoleTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = DeviceRole.objects.all()
filterset = DeviceRoleFilterSet
@ -1131,10 +1064,6 @@ class DeviceRoleTestCase(TestCase):
)
DeviceRole.objects.bulk_create(device_roles)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -1154,7 +1083,7 @@ class DeviceRoleTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
class PlatformTestCase(TestCase):
class PlatformTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Platform.objects.all()
filterset = PlatformFilterSet
@ -1175,10 +1104,6 @@ class PlatformTestCase(TestCase):
)
Platform.objects.bulk_create(platforms)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -1203,7 +1128,7 @@ class PlatformTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class DeviceTestCase(TestCase):
class DeviceTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Device.objects.all()
filterset = DeviceFilterSet
@ -1356,10 +1281,6 @@ class DeviceTestCase(TestCase):
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):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -1549,7 +1470,7 @@ class DeviceTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class ConsolePortTestCase(TestCase):
class ConsolePortTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ConsolePort.objects.all()
filterset = ConsolePortFilterSet
@ -1608,10 +1529,6 @@ class ConsolePortTestCase(TestCase):
Cable(termination_a=console_ports[1], termination_b=console_server_ports[1]).save()
# Third port is not connected
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -1665,7 +1582,7 @@ class ConsolePortTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
class ConsoleServerPortTestCase(TestCase):
class ConsoleServerPortTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ConsoleServerPort.objects.all()
filterset = ConsoleServerPortFilterSet
@ -1724,10 +1641,6 @@ class ConsoleServerPortTestCase(TestCase):
Cable(termination_a=console_server_ports[1], termination_b=console_ports[1]).save()
# Third port is not connected
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -1781,7 +1694,7 @@ class ConsoleServerPortTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
class PowerPortTestCase(TestCase):
class PowerPortTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = PowerPort.objects.all()
filterset = PowerPortFilterSet
@ -1840,10 +1753,6 @@ class PowerPortTestCase(TestCase):
Cable(termination_a=power_ports[1], termination_b=power_outlets[1]).save()
# Third port is not connected
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -1905,7 +1814,7 @@ class PowerPortTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
class PowerOutletTestCase(TestCase):
class PowerOutletTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = PowerOutlet.objects.all()
filterset = PowerOutletFilterSet
@ -1964,10 +1873,6 @@ class PowerOutletTestCase(TestCase):
Cable(termination_a=power_outlets[1], termination_b=power_ports[1]).save()
# Third port is not connected
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -2025,7 +1930,7 @@ class PowerOutletTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
class InterfaceTestCase(TestCase):
class InterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Interface.objects.all()
filterset = InterfaceFilterSet
@ -2081,10 +1986,6 @@ class InterfaceTestCase(TestCase):
Cable(termination_a=interfaces[1], termination_b=interfaces[4]).save()
# Third pair is not connected
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -2200,7 +2101,7 @@ class InterfaceTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class FrontPortTestCase(TestCase):
class FrontPortTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = FrontPort.objects.all()
filterset = FrontPortFilterSet
@ -2266,10 +2167,6 @@ class FrontPortTestCase(TestCase):
Cable(termination_a=front_ports[1], termination_b=front_ports[4]).save()
# Third port is not connected
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -2321,7 +2218,7 @@ class FrontPortTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class RearPortTestCase(TestCase):
class RearPortTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = RearPort.objects.all()
filterset = RearPortFilterSet
@ -2377,10 +2274,6 @@ class RearPortTestCase(TestCase):
Cable(termination_a=rear_ports[1], termination_b=rear_ports[4]).save()
# Third port is not connected
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -2436,7 +2329,7 @@ class RearPortTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class DeviceBayTestCase(TestCase):
class DeviceBayTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = DeviceBay.objects.all()
filterset = DeviceBayFilterSet
@ -2483,10 +2376,6 @@ class DeviceBayTestCase(TestCase):
)
DeviceBay.objects.bulk_create(device_bays)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -2528,7 +2417,7 @@ class DeviceBayTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class InventoryItemTestCase(TestCase):
class InventoryItemTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = InventoryItem.objects.all()
filterset = InventoryItemFilterSet
@ -2591,10 +2480,6 @@ class InventoryItemTestCase(TestCase):
for i in child_inventory_items:
i.save()
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -2666,7 +2551,7 @@ class InventoryItemTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
class VirtualChassisTestCase(TestCase):
class VirtualChassisTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = VirtualChassis.objects.all()
filterset = VirtualChassisFilterSet
@ -2721,10 +2606,6 @@ class VirtualChassisTestCase(TestCase):
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):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -2762,7 +2643,7 @@ class VirtualChassisTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class CableTestCase(TestCase):
class CableTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Cable.objects.all()
filterset = CableFilterSet
@ -2827,10 +2708,6 @@ class CableTestCase(TestCase):
Cable(termination_a=interfaces[9], termination_b=interfaces[10], label='Cable 5', type=CableTypeChoices.TYPE_CAT6, status=CableStatusChoices.STATUS_PLANNED, color='e91e63', length=10, length_unit=CableLengthUnitChoices.UNIT_METER).save()
Cable(termination_a=interfaces[11], termination_b=interfaces[0], label='Cable 6', type=CableTypeChoices.TYPE_CAT6, status=CableStatusChoices.STATUS_PLANNED, color='e91e63', length=20, length_unit=CableLengthUnitChoices.UNIT_METER).save()
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -2886,7 +2763,7 @@ class CableTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
class PowerPanelTestCase(TestCase):
class PowerPanelTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = PowerPanel.objects.all()
filterset = PowerPanelFilterSet
@ -2931,10 +2808,6 @@ class PowerPanelTestCase(TestCase):
)
PowerPanel.objects.bulk_create(power_panels)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Power Panel 1', 'Power Panel 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -2966,7 +2839,7 @@ class PowerPanelTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class PowerFeedTestCase(TestCase):
class PowerFeedTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = PowerFeed.objects.all()
filterset = PowerFeedFilterSet
@ -3029,10 +2902,6 @@ class PowerFeedTestCase(TestCase):
Cable(termination_a=power_feeds[0], termination_b=power_ports[0]).save()
Cable(termination_a=power_feeds[1], termination_b=power_ports[1]).save()
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Power Feed 1', 'Power Feed 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)

View File

@ -24,7 +24,7 @@ from utilities.tables import paginate_table
from utilities.utils import csv_format, count_related
from utilities.views import GetReturnURLMixin, ObjectPermissionRequiredMixin
from virtualization.models import VirtualMachine
from . import filters, forms, tables
from . import filtersets, forms, tables
from .choices import DeviceFaceChoices
from .constants import NONCONNECTABLE_IFACE_TYPES
from .models import (
@ -107,7 +107,7 @@ class RegionListView(generic.ObjectListView):
'site_count',
cumulative=True
)
filterset = filters.RegionFilterSet
filterset = filtersets.RegionFilterSet
filterset_form = forms.RegionFilterForm
table = tables.RegionTable
@ -163,7 +163,7 @@ class RegionBulkEditView(generic.BulkEditView):
'site_count',
cumulative=True
)
filterset = filters.RegionFilterSet
filterset = filtersets.RegionFilterSet
table = tables.RegionTable
form = forms.RegionBulkEditForm
@ -176,7 +176,7 @@ class RegionBulkDeleteView(generic.BulkDeleteView):
'site_count',
cumulative=True
)
filterset = filters.RegionFilterSet
filterset = filtersets.RegionFilterSet
table = tables.RegionTable
@ -192,7 +192,7 @@ class SiteGroupListView(generic.ObjectListView):
'site_count',
cumulative=True
)
filterset = filters.SiteGroupFilterSet
filterset = filtersets.SiteGroupFilterSet
filterset_form = forms.SiteGroupFilterForm
table = tables.SiteGroupTable
@ -248,7 +248,7 @@ class SiteGroupBulkEditView(generic.BulkEditView):
'site_count',
cumulative=True
)
filterset = filters.SiteGroupFilterSet
filterset = filtersets.SiteGroupFilterSet
table = tables.SiteGroupTable
form = forms.SiteGroupBulkEditForm
@ -261,7 +261,7 @@ class SiteGroupBulkDeleteView(generic.BulkDeleteView):
'site_count',
cumulative=True
)
filterset = filters.SiteGroupFilterSet
filterset = filtersets.SiteGroupFilterSet
table = tables.SiteGroupTable
@ -271,7 +271,7 @@ class SiteGroupBulkDeleteView(generic.BulkDeleteView):
class SiteListView(generic.ObjectListView):
queryset = Site.objects.all()
filterset = filters.SiteFilterSet
filterset = filtersets.SiteFilterSet
filterset_form = forms.SiteFilterForm
table = tables.SiteTable
@ -326,14 +326,14 @@ class SiteBulkImportView(generic.BulkImportView):
class SiteBulkEditView(generic.BulkEditView):
queryset = Site.objects.prefetch_related('region', 'tenant')
filterset = filters.SiteFilterSet
filterset = filtersets.SiteFilterSet
table = tables.SiteTable
form = forms.SiteBulkEditForm
class SiteBulkDeleteView(generic.BulkDeleteView):
queryset = Site.objects.prefetch_related('region', 'tenant')
filterset = filters.SiteFilterSet
filterset = filtersets.SiteFilterSet
table = tables.SiteTable
@ -355,7 +355,7 @@ class LocationListView(generic.ObjectListView):
'rack_count',
cumulative=True
)
filterset = filters.LocationFilterSet
filterset = filtersets.LocationFilterSet
filterset_form = forms.LocationFilterForm
table = tables.LocationTable
@ -414,7 +414,7 @@ class LocationBulkEditView(generic.BulkEditView):
'rack_count',
cumulative=True
).prefetch_related('site')
filterset = filters.LocationFilterSet
filterset = filtersets.LocationFilterSet
table = tables.LocationTable
form = forms.LocationBulkEditForm
@ -427,7 +427,7 @@ class LocationBulkDeleteView(generic.BulkDeleteView):
'rack_count',
cumulative=True
).prefetch_related('site')
filterset = filters.LocationFilterSet
filterset = filtersets.LocationFilterSet
table = tables.LocationTable
@ -478,7 +478,7 @@ class RackRoleBulkEditView(generic.BulkEditView):
queryset = RackRole.objects.annotate(
rack_count=count_related(Rack, 'role')
)
filterset = filters.RackRoleFilterSet
filterset = filtersets.RackRoleFilterSet
table = tables.RackRoleTable
form = forms.RackRoleBulkEditForm
@ -500,7 +500,7 @@ class RackListView(generic.ObjectListView):
).annotate(
device_count=count_related(Device, 'rack')
)
filterset = filters.RackFilterSet
filterset = filtersets.RackFilterSet
filterset_form = forms.RackFilterForm
table = tables.RackDetailTable
@ -513,7 +513,7 @@ class RackElevationListView(generic.ObjectListView):
def get(self, request):
racks = filters.RackFilterSet(request.GET, self.queryset).qs
racks = filtersets.RackFilterSet(request.GET, self.queryset).qs
total_count = racks.count()
# Determine ordering
@ -602,14 +602,14 @@ class RackBulkImportView(generic.BulkImportView):
class RackBulkEditView(generic.BulkEditView):
queryset = Rack.objects.prefetch_related('site', 'location', 'tenant', 'role')
filterset = filters.RackFilterSet
filterset = filtersets.RackFilterSet
table = tables.RackTable
form = forms.RackBulkEditForm
class RackBulkDeleteView(generic.BulkDeleteView):
queryset = Rack.objects.prefetch_related('site', 'location', 'tenant', 'role')
filterset = filters.RackFilterSet
filterset = filtersets.RackFilterSet
table = tables.RackTable
@ -619,7 +619,7 @@ class RackBulkDeleteView(generic.BulkDeleteView):
class RackReservationListView(generic.ObjectListView):
queryset = RackReservation.objects.all()
filterset = filters.RackReservationFilterSet
filterset = filtersets.RackReservationFilterSet
filterset_form = forms.RackReservationFilterForm
table = tables.RackReservationTable
@ -662,14 +662,14 @@ class RackReservationImportView(generic.BulkImportView):
class RackReservationBulkEditView(generic.BulkEditView):
queryset = RackReservation.objects.prefetch_related('rack', 'user')
filterset = filters.RackReservationFilterSet
filterset = filtersets.RackReservationFilterSet
table = tables.RackReservationTable
form = forms.RackReservationBulkEditForm
class RackReservationBulkDeleteView(generic.BulkDeleteView):
queryset = RackReservation.objects.prefetch_related('rack', 'user')
filterset = filters.RackReservationFilterSet
filterset = filtersets.RackReservationFilterSet
table = tables.RackReservationTable
@ -692,6 +692,8 @@ class ManufacturerView(generic.ObjectView):
def get_extra_context(self, request, instance):
devicetypes = DeviceType.objects.restrict(request.user, 'view').filter(
manufacturer=instance
).annotate(
instance_count=count_related(Device, 'device_type')
)
devicetypes_table = tables.DeviceTypeTable(devicetypes)
@ -722,7 +724,7 @@ class ManufacturerBulkEditView(generic.BulkEditView):
queryset = Manufacturer.objects.annotate(
devicetype_count=count_related(DeviceType, 'manufacturer')
)
filterset = filters.ManufacturerFilterSet
filterset = filtersets.ManufacturerFilterSet
table = tables.ManufacturerTable
form = forms.ManufacturerBulkEditForm
@ -742,7 +744,7 @@ class DeviceTypeListView(generic.ObjectListView):
queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(
instance_count=count_related(Device, 'device_type')
)
filterset = filters.DeviceTypeFilterSet
filterset = filtersets.DeviceTypeFilterSet
filterset_form = forms.DeviceTypeFilterForm
table = tables.DeviceTypeTable
@ -848,7 +850,7 @@ class DeviceTypeBulkEditView(generic.BulkEditView):
queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(
instance_count=count_related(Device, 'device_type')
)
filterset = filters.DeviceTypeFilterSet
filterset = filtersets.DeviceTypeFilterSet
table = tables.DeviceTypeTable
form = forms.DeviceTypeBulkEditForm
@ -857,7 +859,7 @@ class DeviceTypeBulkDeleteView(generic.BulkDeleteView):
queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(
instance_count=count_related(Device, 'device_type')
)
filterset = filters.DeviceTypeFilterSet
filterset = filtersets.DeviceTypeFilterSet
table = tables.DeviceTypeTable
@ -1190,7 +1192,7 @@ class DeviceRoleBulkEditView(generic.BulkEditView):
device_count=count_related(Device, 'device_role'),
vm_count=count_related(VirtualMachine, 'role')
)
filterset = filters.DeviceRoleFilterSet
filterset = filtersets.DeviceRoleFilterSet
table = tables.DeviceRoleTable
form = forms.DeviceRoleBulkEditForm
@ -1249,7 +1251,7 @@ class PlatformBulkImportView(generic.BulkImportView):
class PlatformBulkEditView(generic.BulkEditView):
queryset = Platform.objects.all()
filterset = filters.PlatformFilterSet
filterset = filtersets.PlatformFilterSet
table = tables.PlatformTable
form = forms.PlatformBulkEditForm
@ -1265,7 +1267,7 @@ class PlatformBulkDeleteView(generic.BulkDeleteView):
class DeviceListView(generic.ObjectListView):
queryset = Device.objects.all()
filterset = filters.DeviceFilterSet
filterset = filtersets.DeviceFilterSet
filterset_form = forms.DeviceFilterForm
table = tables.DeviceTable
template_name = 'dcim/device_list.html'
@ -1600,14 +1602,14 @@ class ChildDeviceBulkImportView(generic.BulkImportView):
class DeviceBulkEditView(generic.BulkEditView):
queryset = Device.objects.prefetch_related('tenant', 'site', 'rack', 'device_role', 'device_type__manufacturer')
filterset = filters.DeviceFilterSet
filterset = filtersets.DeviceFilterSet
table = tables.DeviceTable
form = forms.DeviceBulkEditForm
class DeviceBulkDeleteView(generic.BulkDeleteView):
queryset = Device.objects.prefetch_related('tenant', 'site', 'rack', 'device_role', 'device_type__manufacturer')
filterset = filters.DeviceFilterSet
filterset = filtersets.DeviceFilterSet
table = tables.DeviceTable
@ -1617,7 +1619,7 @@ class DeviceBulkDeleteView(generic.BulkDeleteView):
class ConsolePortListView(generic.ObjectListView):
queryset = ConsolePort.objects.all()
filterset = filters.ConsolePortFilterSet
filterset = filtersets.ConsolePortFilterSet
filterset_form = forms.ConsolePortFilterForm
table = tables.ConsolePortTable
action_buttons = ('import', 'export')
@ -1652,7 +1654,7 @@ class ConsolePortBulkImportView(generic.BulkImportView):
class ConsolePortBulkEditView(generic.BulkEditView):
queryset = ConsolePort.objects.all()
filterset = filters.ConsolePortFilterSet
filterset = filtersets.ConsolePortFilterSet
table = tables.ConsolePortTable
form = forms.ConsolePortBulkEditForm
@ -1667,7 +1669,7 @@ class ConsolePortBulkDisconnectView(BulkDisconnectView):
class ConsolePortBulkDeleteView(generic.BulkDeleteView):
queryset = ConsolePort.objects.all()
filterset = filters.ConsolePortFilterSet
filterset = filtersets.ConsolePortFilterSet
table = tables.ConsolePortTable
@ -1677,7 +1679,7 @@ class ConsolePortBulkDeleteView(generic.BulkDeleteView):
class ConsoleServerPortListView(generic.ObjectListView):
queryset = ConsoleServerPort.objects.all()
filterset = filters.ConsoleServerPortFilterSet
filterset = filtersets.ConsoleServerPortFilterSet
filterset_form = forms.ConsoleServerPortFilterForm
table = tables.ConsoleServerPortTable
action_buttons = ('import', 'export')
@ -1712,7 +1714,7 @@ class ConsoleServerPortBulkImportView(generic.BulkImportView):
class ConsoleServerPortBulkEditView(generic.BulkEditView):
queryset = ConsoleServerPort.objects.all()
filterset = filters.ConsoleServerPortFilterSet
filterset = filtersets.ConsoleServerPortFilterSet
table = tables.ConsoleServerPortTable
form = forms.ConsoleServerPortBulkEditForm
@ -1727,7 +1729,7 @@ class ConsoleServerPortBulkDisconnectView(BulkDisconnectView):
class ConsoleServerPortBulkDeleteView(generic.BulkDeleteView):
queryset = ConsoleServerPort.objects.all()
filterset = filters.ConsoleServerPortFilterSet
filterset = filtersets.ConsoleServerPortFilterSet
table = tables.ConsoleServerPortTable
@ -1737,7 +1739,7 @@ class ConsoleServerPortBulkDeleteView(generic.BulkDeleteView):
class PowerPortListView(generic.ObjectListView):
queryset = PowerPort.objects.all()
filterset = filters.PowerPortFilterSet
filterset = filtersets.PowerPortFilterSet
filterset_form = forms.PowerPortFilterForm
table = tables.PowerPortTable
action_buttons = ('import', 'export')
@ -1772,7 +1774,7 @@ class PowerPortBulkImportView(generic.BulkImportView):
class PowerPortBulkEditView(generic.BulkEditView):
queryset = PowerPort.objects.all()
filterset = filters.PowerPortFilterSet
filterset = filtersets.PowerPortFilterSet
table = tables.PowerPortTable
form = forms.PowerPortBulkEditForm
@ -1787,7 +1789,7 @@ class PowerPortBulkDisconnectView(BulkDisconnectView):
class PowerPortBulkDeleteView(generic.BulkDeleteView):
queryset = PowerPort.objects.all()
filterset = filters.PowerPortFilterSet
filterset = filtersets.PowerPortFilterSet
table = tables.PowerPortTable
@ -1797,7 +1799,7 @@ class PowerPortBulkDeleteView(generic.BulkDeleteView):
class PowerOutletListView(generic.ObjectListView):
queryset = PowerOutlet.objects.all()
filterset = filters.PowerOutletFilterSet
filterset = filtersets.PowerOutletFilterSet
filterset_form = forms.PowerOutletFilterForm
table = tables.PowerOutletTable
action_buttons = ('import', 'export')
@ -1832,7 +1834,7 @@ class PowerOutletBulkImportView(generic.BulkImportView):
class PowerOutletBulkEditView(generic.BulkEditView):
queryset = PowerOutlet.objects.all()
filterset = filters.PowerOutletFilterSet
filterset = filtersets.PowerOutletFilterSet
table = tables.PowerOutletTable
form = forms.PowerOutletBulkEditForm
@ -1847,7 +1849,7 @@ class PowerOutletBulkDisconnectView(BulkDisconnectView):
class PowerOutletBulkDeleteView(generic.BulkDeleteView):
queryset = PowerOutlet.objects.all()
filterset = filters.PowerOutletFilterSet
filterset = filtersets.PowerOutletFilterSet
table = tables.PowerOutletTable
@ -1857,7 +1859,7 @@ class PowerOutletBulkDeleteView(generic.BulkDeleteView):
class InterfaceListView(generic.ObjectListView):
queryset = Interface.objects.all()
filterset = filters.InterfaceFilterSet
filterset = filtersets.InterfaceFilterSet
filterset_form = forms.InterfaceFilterForm
table = tables.InterfaceTable
action_buttons = ('import', 'export')
@ -1927,7 +1929,7 @@ class InterfaceBulkImportView(generic.BulkImportView):
class InterfaceBulkEditView(generic.BulkEditView):
queryset = Interface.objects.all()
filterset = filters.InterfaceFilterSet
filterset = filtersets.InterfaceFilterSet
table = tables.InterfaceTable
form = forms.InterfaceBulkEditForm
@ -1942,7 +1944,7 @@ class InterfaceBulkDisconnectView(BulkDisconnectView):
class InterfaceBulkDeleteView(generic.BulkDeleteView):
queryset = Interface.objects.all()
filterset = filters.InterfaceFilterSet
filterset = filtersets.InterfaceFilterSet
table = tables.InterfaceTable
@ -1952,7 +1954,7 @@ class InterfaceBulkDeleteView(generic.BulkDeleteView):
class FrontPortListView(generic.ObjectListView):
queryset = FrontPort.objects.all()
filterset = filters.FrontPortFilterSet
filterset = filtersets.FrontPortFilterSet
filterset_form = forms.FrontPortFilterForm
table = tables.FrontPortTable
action_buttons = ('import', 'export')
@ -1987,7 +1989,7 @@ class FrontPortBulkImportView(generic.BulkImportView):
class FrontPortBulkEditView(generic.BulkEditView):
queryset = FrontPort.objects.all()
filterset = filters.FrontPortFilterSet
filterset = filtersets.FrontPortFilterSet
table = tables.FrontPortTable
form = forms.FrontPortBulkEditForm
@ -2002,7 +2004,7 @@ class FrontPortBulkDisconnectView(BulkDisconnectView):
class FrontPortBulkDeleteView(generic.BulkDeleteView):
queryset = FrontPort.objects.all()
filterset = filters.FrontPortFilterSet
filterset = filtersets.FrontPortFilterSet
table = tables.FrontPortTable
@ -2012,7 +2014,7 @@ class FrontPortBulkDeleteView(generic.BulkDeleteView):
class RearPortListView(generic.ObjectListView):
queryset = RearPort.objects.all()
filterset = filters.RearPortFilterSet
filterset = filtersets.RearPortFilterSet
filterset_form = forms.RearPortFilterForm
table = tables.RearPortTable
action_buttons = ('import', 'export')
@ -2047,7 +2049,7 @@ class RearPortBulkImportView(generic.BulkImportView):
class RearPortBulkEditView(generic.BulkEditView):
queryset = RearPort.objects.all()
filterset = filters.RearPortFilterSet
filterset = filtersets.RearPortFilterSet
table = tables.RearPortTable
form = forms.RearPortBulkEditForm
@ -2062,7 +2064,7 @@ class RearPortBulkDisconnectView(BulkDisconnectView):
class RearPortBulkDeleteView(generic.BulkDeleteView):
queryset = RearPort.objects.all()
filterset = filters.RearPortFilterSet
filterset = filtersets.RearPortFilterSet
table = tables.RearPortTable
@ -2072,7 +2074,7 @@ class RearPortBulkDeleteView(generic.BulkDeleteView):
class DeviceBayListView(generic.ObjectListView):
queryset = DeviceBay.objects.all()
filterset = filters.DeviceBayFilterSet
filterset = filtersets.DeviceBayFilterSet
filterset_form = forms.DeviceBayFilterForm
table = tables.DeviceBayTable
action_buttons = ('import', 'export')
@ -2172,7 +2174,7 @@ class DeviceBayBulkImportView(generic.BulkImportView):
class DeviceBayBulkEditView(generic.BulkEditView):
queryset = DeviceBay.objects.all()
filterset = filters.DeviceBayFilterSet
filterset = filtersets.DeviceBayFilterSet
table = tables.DeviceBayTable
form = forms.DeviceBayBulkEditForm
@ -2183,7 +2185,7 @@ class DeviceBayBulkRenameView(generic.BulkRenameView):
class DeviceBayBulkDeleteView(generic.BulkDeleteView):
queryset = DeviceBay.objects.all()
filterset = filters.DeviceBayFilterSet
filterset = filtersets.DeviceBayFilterSet
table = tables.DeviceBayTable
@ -2193,7 +2195,7 @@ class DeviceBayBulkDeleteView(generic.BulkDeleteView):
class InventoryItemListView(generic.ObjectListView):
queryset = InventoryItem.objects.all()
filterset = filters.InventoryItemFilterSet
filterset = filtersets.InventoryItemFilterSet
filterset_form = forms.InventoryItemFilterForm
table = tables.InventoryItemTable
action_buttons = ('import', 'export')
@ -2227,7 +2229,7 @@ class InventoryItemBulkImportView(generic.BulkImportView):
class InventoryItemBulkEditView(generic.BulkEditView):
queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer')
filterset = filters.InventoryItemFilterSet
filterset = filtersets.InventoryItemFilterSet
table = tables.InventoryItemTable
form = forms.InventoryItemBulkEditForm
@ -2252,7 +2254,7 @@ class DeviceBulkAddConsolePortView(generic.BulkComponentCreateView):
form = forms.ConsolePortBulkCreateForm
queryset = ConsolePort.objects.all()
model_form = forms.ConsolePortForm
filterset = filters.DeviceFilterSet
filterset = filtersets.DeviceFilterSet
table = tables.DeviceTable
default_return_url = 'dcim:device_list'
@ -2263,7 +2265,7 @@ class DeviceBulkAddConsoleServerPortView(generic.BulkComponentCreateView):
form = forms.ConsoleServerPortBulkCreateForm
queryset = ConsoleServerPort.objects.all()
model_form = forms.ConsoleServerPortForm
filterset = filters.DeviceFilterSet
filterset = filtersets.DeviceFilterSet
table = tables.DeviceTable
default_return_url = 'dcim:device_list'
@ -2274,7 +2276,7 @@ class DeviceBulkAddPowerPortView(generic.BulkComponentCreateView):
form = forms.PowerPortBulkCreateForm
queryset = PowerPort.objects.all()
model_form = forms.PowerPortForm
filterset = filters.DeviceFilterSet
filterset = filtersets.DeviceFilterSet
table = tables.DeviceTable
default_return_url = 'dcim:device_list'
@ -2285,7 +2287,7 @@ class DeviceBulkAddPowerOutletView(generic.BulkComponentCreateView):
form = forms.PowerOutletBulkCreateForm
queryset = PowerOutlet.objects.all()
model_form = forms.PowerOutletForm
filterset = filters.DeviceFilterSet
filterset = filtersets.DeviceFilterSet
table = tables.DeviceTable
default_return_url = 'dcim:device_list'
@ -2296,7 +2298,7 @@ class DeviceBulkAddInterfaceView(generic.BulkComponentCreateView):
form = forms.InterfaceBulkCreateForm
queryset = Interface.objects.all()
model_form = forms.InterfaceForm
filterset = filters.DeviceFilterSet
filterset = filtersets.DeviceFilterSet
table = tables.DeviceTable
default_return_url = 'dcim:device_list'
@ -2307,7 +2309,7 @@ class DeviceBulkAddInterfaceView(generic.BulkComponentCreateView):
# form = forms.FrontPortBulkCreateForm
# queryset = FrontPort.objects.all()
# model_form = forms.FrontPortForm
# filterset = filters.DeviceFilterSet
# filterset = filtersets.DeviceFilterSet
# table = tables.DeviceTable
# default_return_url = 'dcim:device_list'
@ -2318,7 +2320,7 @@ class DeviceBulkAddRearPortView(generic.BulkComponentCreateView):
form = forms.RearPortBulkCreateForm
queryset = RearPort.objects.all()
model_form = forms.RearPortForm
filterset = filters.DeviceFilterSet
filterset = filtersets.DeviceFilterSet
table = tables.DeviceTable
default_return_url = 'dcim:device_list'
@ -2329,7 +2331,7 @@ class DeviceBulkAddDeviceBayView(generic.BulkComponentCreateView):
form = forms.DeviceBayBulkCreateForm
queryset = DeviceBay.objects.all()
model_form = forms.DeviceBayForm
filterset = filters.DeviceFilterSet
filterset = filtersets.DeviceFilterSet
table = tables.DeviceTable
default_return_url = 'dcim:device_list'
@ -2340,7 +2342,7 @@ class DeviceBulkAddInventoryItemView(generic.BulkComponentCreateView):
form = forms.InventoryItemBulkCreateForm
queryset = InventoryItem.objects.all()
model_form = forms.InventoryItemForm
filterset = filters.DeviceFilterSet
filterset = filtersets.DeviceFilterSet
table = tables.DeviceTable
default_return_url = 'dcim:device_list'
@ -2351,7 +2353,7 @@ class DeviceBulkAddInventoryItemView(generic.BulkComponentCreateView):
class CableListView(generic.ObjectListView):
queryset = Cable.objects.all()
filterset = filters.CableFilterSet
filterset = filtersets.CableFilterSet
filterset_form = forms.CableFilterForm
table = tables.CableTable
action_buttons = ('import', 'export')
@ -2484,14 +2486,14 @@ class CableBulkImportView(generic.BulkImportView):
class CableBulkEditView(generic.BulkEditView):
queryset = Cable.objects.prefetch_related('termination_a', 'termination_b')
filterset = filters.CableFilterSet
filterset = filtersets.CableFilterSet
table = tables.CableTable
form = forms.CableBulkEditForm
class CableBulkDeleteView(generic.BulkDeleteView):
queryset = Cable.objects.prefetch_related('termination_a', 'termination_b')
filterset = filters.CableFilterSet
filterset = filtersets.CableFilterSet
table = tables.CableTable
@ -2501,7 +2503,7 @@ class CableBulkDeleteView(generic.BulkDeleteView):
class ConsoleConnectionsListView(generic.ObjectListView):
queryset = ConsolePort.objects.filter(_path__isnull=False).order_by('device')
filterset = filters.ConsoleConnectionFilterSet
filterset = filtersets.ConsoleConnectionFilterSet
filterset_form = forms.ConsoleConnectionFilterForm
table = tables.ConsoleConnectionTable
template_name = 'dcim/connections_list.html'
@ -2531,7 +2533,7 @@ class ConsoleConnectionsListView(generic.ObjectListView):
class PowerConnectionsListView(generic.ObjectListView):
queryset = PowerPort.objects.filter(_path__isnull=False).order_by('device')
filterset = filters.PowerConnectionFilterSet
filterset = filtersets.PowerConnectionFilterSet
filterset_form = forms.PowerConnectionFilterForm
table = tables.PowerConnectionTable
template_name = 'dcim/connections_list.html'
@ -2565,7 +2567,7 @@ class InterfaceConnectionsListView(generic.ObjectListView):
_path__isnull=False,
pk__lt=F('_path__destination_id')
).order_by('device')
filterset = filters.InterfaceConnectionFilterSet
filterset = filtersets.InterfaceConnectionFilterSet
filterset_form = forms.InterfaceConnectionFilterForm
table = tables.InterfaceConnectionTable
template_name = 'dcim/connections_list.html'
@ -2604,7 +2606,7 @@ class VirtualChassisListView(generic.ObjectListView):
member_count=count_related(Device, 'virtual_chassis')
)
table = tables.VirtualChassisTable
filterset = filters.VirtualChassisFilterSet
filterset = filtersets.VirtualChassisFilterSet
filterset_form = forms.VirtualChassisFilterForm
@ -2812,14 +2814,14 @@ class VirtualChassisBulkImportView(generic.BulkImportView):
class VirtualChassisBulkEditView(generic.BulkEditView):
queryset = VirtualChassis.objects.all()
filterset = filters.VirtualChassisFilterSet
filterset = filtersets.VirtualChassisFilterSet
table = tables.VirtualChassisTable
form = forms.VirtualChassisBulkEditForm
class VirtualChassisBulkDeleteView(generic.BulkDeleteView):
queryset = VirtualChassis.objects.all()
filterset = filters.VirtualChassisFilterSet
filterset = filtersets.VirtualChassisFilterSet
table = tables.VirtualChassisTable
@ -2833,7 +2835,7 @@ class PowerPanelListView(generic.ObjectListView):
).annotate(
powerfeed_count=count_related(PowerFeed, 'power_panel')
)
filterset = filters.PowerPanelFilterSet
filterset = filtersets.PowerPanelFilterSet
filterset_form = forms.PowerPanelFilterForm
table = tables.PowerPanelTable
@ -2873,7 +2875,7 @@ class PowerPanelBulkImportView(generic.BulkImportView):
class PowerPanelBulkEditView(generic.BulkEditView):
queryset = PowerPanel.objects.prefetch_related('site', 'location')
filterset = filters.PowerPanelFilterSet
filterset = filtersets.PowerPanelFilterSet
table = tables.PowerPanelTable
form = forms.PowerPanelBulkEditForm
@ -2884,7 +2886,7 @@ class PowerPanelBulkDeleteView(generic.BulkDeleteView):
).annotate(
powerfeed_count=count_related(PowerFeed, 'power_panel')
)
filterset = filters.PowerPanelFilterSet
filterset = filtersets.PowerPanelFilterSet
table = tables.PowerPanelTable
@ -2894,7 +2896,7 @@ class PowerPanelBulkDeleteView(generic.BulkDeleteView):
class PowerFeedListView(generic.ObjectListView):
queryset = PowerFeed.objects.all()
filterset = filters.PowerFeedFilterSet
filterset = filtersets.PowerFeedFilterSet
filterset_form = forms.PowerFeedFilterForm
table = tables.PowerFeedTable
@ -2920,7 +2922,7 @@ class PowerFeedBulkImportView(generic.BulkImportView):
class PowerFeedBulkEditView(generic.BulkEditView):
queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack')
filterset = filters.PowerFeedFilterSet
filterset = filtersets.PowerFeedFilterSet
table = tables.PowerFeedTable
form = forms.PowerFeedBulkEditForm
@ -2931,5 +2933,5 @@ class PowerFeedBulkDisconnectView(BulkDisconnectView):
class PowerFeedBulkDeleteView(generic.BulkDeleteView):
queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack')
filterset = filters.PowerFeedFilterSet
filterset = filtersets.PowerFeedFilterSet
table = tables.PowerFeedTable

View File

@ -9,7 +9,7 @@ from rest_framework.routers import APIRootView
from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet
from rq import Worker
from extras import filters
from extras import filtersets
from extras.choices import JobResultStatusChoices
from extras.models import *
from extras.models import CustomField
@ -61,7 +61,7 @@ class WebhookViewSet(ModelViewSet):
metadata_class = ContentTypeMetadata
queryset = Webhook.objects.all()
serializer_class = serializers.WebhookSerializer
filterset_class = filters.WebhookFilterSet
filterset_class = filtersets.WebhookFilterSet
#
@ -72,7 +72,7 @@ class CustomFieldViewSet(ModelViewSet):
metadata_class = ContentTypeMetadata
queryset = CustomField.objects.all()
serializer_class = serializers.CustomFieldSerializer
filterset_class = filters.CustomFieldFilterSet
filterset_class = filtersets.CustomFieldFilterSet
class CustomFieldModelViewSet(ModelViewSet):
@ -101,7 +101,7 @@ class CustomLinkViewSet(ModelViewSet):
metadata_class = ContentTypeMetadata
queryset = CustomLink.objects.all()
serializer_class = serializers.CustomLinkSerializer
filterset_class = filters.CustomLinkFilterSet
filterset_class = filtersets.CustomLinkFilterSet
#
@ -112,7 +112,7 @@ class ExportTemplateViewSet(ModelViewSet):
metadata_class = ContentTypeMetadata
queryset = ExportTemplate.objects.all()
serializer_class = serializers.ExportTemplateSerializer
filterset_class = filters.ExportTemplateFilterSet
filterset_class = filtersets.ExportTemplateFilterSet
#
@ -124,7 +124,7 @@ class TagViewSet(ModelViewSet):
tagged_items=count_related(TaggedItem, 'tag')
)
serializer_class = serializers.TagSerializer
filterset_class = filters.TagFilterSet
filterset_class = filtersets.TagFilterSet
#
@ -135,7 +135,7 @@ class ImageAttachmentViewSet(ModelViewSet):
metadata_class = ContentTypeMetadata
queryset = ImageAttachment.objects.all()
serializer_class = serializers.ImageAttachmentSerializer
filterset_class = filters.ImageAttachmentFilterSet
filterset_class = filtersets.ImageAttachmentFilterSet
#
@ -146,7 +146,7 @@ class JournalEntryViewSet(ModelViewSet):
metadata_class = ContentTypeMetadata
queryset = JournalEntry.objects.all()
serializer_class = serializers.JournalEntrySerializer
filterset_class = filters.JournalEntryFilterSet
filterset_class = filtersets.JournalEntryFilterSet
#
@ -158,7 +158,7 @@ class ConfigContextViewSet(ModelViewSet):
'regions', 'site_groups', 'sites', 'roles', 'platforms', 'tenant_groups', 'tenants',
)
serializer_class = serializers.ConfigContextSerializer
filterset_class = filters.ConfigContextFilterSet
filterset_class = filtersets.ConfigContextFilterSet
#
@ -358,7 +358,7 @@ class ObjectChangeViewSet(ReadOnlyModelViewSet):
metadata_class = ContentTypeMetadata
queryset = ObjectChange.objects.prefetch_related('user')
serializer_class = serializers.ObjectChangeSerializer
filterset_class = filters.ObjectChangeFilterSet
filterset_class = filtersets.ObjectChangeFilterSet
#
@ -371,7 +371,7 @@ class JobResultViewSet(ReadOnlyModelViewSet):
"""
queryset = JobResult.objects.prefetch_related('user')
serializer_class = serializers.JobResultSerializer
filterset_class = filters.JobResultFilterSet
filterset_class = filtersets.JobResultFilterSet
#
@ -384,4 +384,4 @@ class ContentTypeViewSet(ReadOnlyModelViewSet):
"""
queryset = ContentType.objects.order_by('app_label', 'model')
serializer_class = serializers.ContentTypeSerializer
filterset_class = filters.ContentTypeFilterSet
filterset_class = filtersets.ContentTypeFilterSet

View File

@ -1,31 +1,12 @@
import django_filters
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from django.forms import DateField, IntegerField, NullBooleanField
from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup
from tenancy.models import Tenant, TenantGroup
from utilities.filters import BaseFilterSet, ContentTypeFilter
from virtualization.models import Cluster, ClusterGroup
from .models import Tag
from .choices import *
from .models import *
__all__ = (
'ConfigContextFilterSet',
'ContentTypeFilterSet',
'CreatedUpdatedFilterSet',
'CustomFieldFilter',
'CustomLinkFilterSet',
'CustomFieldModelFilterSet',
'ExportTemplateFilterSet',
'ImageAttachmentFilterSet',
'JournalEntryFilterSet',
'LocalConfigContextFilterSet',
'ObjectChangeFilterSet',
'TagFilterSet',
'WebhookFilterSet',
'TagFilter',
)
EXACT_FILTER_TYPES = (
@ -36,41 +17,6 @@ EXACT_FILTER_TYPES = (
)
class CreatedUpdatedFilterSet(django_filters.FilterSet):
created = django_filters.DateFilter()
created__gte = django_filters.DateFilter(
field_name='created',
lookup_expr='gte'
)
created__lte = django_filters.DateFilter(
field_name='created',
lookup_expr='lte'
)
last_updated = django_filters.DateTimeFilter()
last_updated__gte = django_filters.DateTimeFilter(
field_name='last_updated',
lookup_expr='gte'
)
last_updated__lte = django_filters.DateTimeFilter(
field_name='last_updated',
lookup_expr='lte'
)
class WebhookFilterSet(BaseFilterSet):
content_types = ContentTypeFilter()
http_method = django_filters.MultipleChoiceFilter(
choices=WebhookHttpMethodChoices
)
class Meta:
model = Webhook
fields = [
'id', 'content_types', 'name', 'type_create', 'type_update', 'type_delete', 'payload_url', 'enabled',
'http_method', 'http_content_type', 'secret', 'ssl_verification', 'ca_file_path',
]
class CustomFieldFilter(django_filters.Filter):
"""
Filter objects by the presence of a CustomFieldValue. The filter's name is used as the CustomField name.
@ -94,310 +40,16 @@ class CustomFieldFilter(django_filters.Filter):
self.lookup_expr = 'icontains'
class CustomFieldModelFilterSet(django_filters.FilterSet):
class TagFilter(django_filters.ModelMultipleChoiceFilter):
"""
Dynamically add a Filter for each CustomField applicable to the parent model.
Match on one or more assigned tags. If multiple tags are specified (e.g. ?tag=foo&tag=bar), the queryset is filtered
to objects matching all tags.
"""
def __init__(self, *args, **kwargs):
kwargs.setdefault('field_name', 'tags__slug')
kwargs.setdefault('to_field_name', 'slug')
kwargs.setdefault('conjoined', True)
kwargs.setdefault('queryset', Tag.objects.all())
super().__init__(*args, **kwargs)
custom_fields = CustomField.objects.filter(
content_types=ContentType.objects.get_for_model(self._meta.model)
).exclude(
filter_logic=CustomFieldFilterLogicChoices.FILTER_DISABLED
)
for cf in custom_fields:
self.filters['cf_{}'.format(cf.name)] = CustomFieldFilter(field_name=cf.name, custom_field=cf)
class CustomFieldFilterSet(django_filters.FilterSet):
content_types = ContentTypeFilter()
class Meta:
model = CustomField
fields = ['id', 'content_types', 'name', 'required', 'filter_logic', 'weight']
class CustomLinkFilterSet(BaseFilterSet):
class Meta:
model = CustomLink
fields = ['id', 'content_type', 'name', 'link_text', 'link_url', 'weight', 'group_name', 'new_window']
class ExportTemplateFilterSet(BaseFilterSet):
class Meta:
model = ExportTemplate
fields = ['id', 'content_type', 'name']
class ImageAttachmentFilterSet(BaseFilterSet):
content_type = ContentTypeFilter()
class Meta:
model = ImageAttachment
fields = ['id', 'content_type_id', 'object_id', 'name']
class JournalEntryFilterSet(BaseFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
)
created = django_filters.DateTimeFromToRangeFilter()
assigned_object_type = ContentTypeFilter()
created_by_id = django_filters.ModelMultipleChoiceFilter(
queryset=User.objects.all(),
label='User (ID)',
)
created_by = django_filters.ModelMultipleChoiceFilter(
field_name='created_by__username',
queryset=User.objects.all(),
to_field_name='username',
label='User (name)',
)
kind = django_filters.MultipleChoiceFilter(
choices=JournalEntryKindChoices
)
class Meta:
model = JournalEntry
fields = ['id', 'assigned_object_type_id', 'assigned_object_id', 'created', 'kind']
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(comments__icontains=value)
class TagFilterSet(BaseFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
)
class Meta:
model = Tag
fields = ['id', 'name', 'slug', 'color']
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(slug__icontains=value)
)
class ConfigContextFilterSet(BaseFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
)
region_id = django_filters.ModelMultipleChoiceFilter(
field_name='regions',
queryset=Region.objects.all(),
label='Region',
)
region = django_filters.ModelMultipleChoiceFilter(
field_name='regions__slug',
queryset=Region.objects.all(),
to_field_name='slug',
label='Region (slug)',
)
site_group = django_filters.ModelMultipleChoiceFilter(
field_name='site_groups__slug',
queryset=SiteGroup.objects.all(),
to_field_name='slug',
label='Site group (slug)',
)
site_group_id = django_filters.ModelMultipleChoiceFilter(
field_name='site_groups',
queryset=SiteGroup.objects.all(),
label='Site group',
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='sites',
queryset=Site.objects.all(),
label='Site',
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='sites__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site (slug)',
)
device_type_id = django_filters.ModelMultipleChoiceFilter(
field_name='device_types',
queryset=DeviceType.objects.all(),
label='Device type',
)
role_id = django_filters.ModelMultipleChoiceFilter(
field_name='roles',
queryset=DeviceRole.objects.all(),
label='Role',
)
role = django_filters.ModelMultipleChoiceFilter(
field_name='roles__slug',
queryset=DeviceRole.objects.all(),
to_field_name='slug',
label='Role (slug)',
)
platform_id = django_filters.ModelMultipleChoiceFilter(
field_name='platforms',
queryset=Platform.objects.all(),
label='Platform',
)
platform = django_filters.ModelMultipleChoiceFilter(
field_name='platforms__slug',
queryset=Platform.objects.all(),
to_field_name='slug',
label='Platform (slug)',
)
cluster_group_id = django_filters.ModelMultipleChoiceFilter(
field_name='cluster_groups',
queryset=ClusterGroup.objects.all(),
label='Cluster group',
)
cluster_group = django_filters.ModelMultipleChoiceFilter(
field_name='cluster_groups__slug',
queryset=ClusterGroup.objects.all(),
to_field_name='slug',
label='Cluster group (slug)',
)
cluster_id = django_filters.ModelMultipleChoiceFilter(
field_name='clusters',
queryset=Cluster.objects.all(),
label='Cluster',
)
tenant_group_id = django_filters.ModelMultipleChoiceFilter(
field_name='tenant_groups',
queryset=TenantGroup.objects.all(),
label='Tenant group',
)
tenant_group = django_filters.ModelMultipleChoiceFilter(
field_name='tenant_groups__slug',
queryset=TenantGroup.objects.all(),
to_field_name='slug',
label='Tenant group (slug)',
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
field_name='tenants',
queryset=Tenant.objects.all(),
label='Tenant',
)
tenant = django_filters.ModelMultipleChoiceFilter(
field_name='tenants__slug',
queryset=Tenant.objects.all(),
to_field_name='slug',
label='Tenant (slug)',
)
tag = django_filters.ModelMultipleChoiceFilter(
field_name='tags__slug',
queryset=Tag.objects.all(),
to_field_name='slug',
label='Tag (slug)',
)
class Meta:
model = ConfigContext
fields = ['id', 'name', 'is_active']
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(description__icontains=value) |
Q(data__icontains=value)
)
#
# Filter for Local Config Context Data
#
class LocalConfigContextFilterSet(django_filters.FilterSet):
local_context_data = django_filters.BooleanFilter(
method='_local_context_data',
label='Has local config context data',
)
def _local_context_data(self, queryset, name, value):
return queryset.exclude(local_context_data__isnull=value)
class ObjectChangeFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
)
time = django_filters.DateTimeFromToRangeFilter()
changed_object_type = ContentTypeFilter()
user_id = django_filters.ModelMultipleChoiceFilter(
queryset=User.objects.all(),
label='User (ID)',
)
user = django_filters.ModelMultipleChoiceFilter(
field_name='user__username',
queryset=User.objects.all(),
to_field_name='username',
label='User name',
)
class Meta:
model = ObjectChange
fields = [
'id', 'user', 'user_name', 'request_id', 'action', 'changed_object_type_id', 'changed_object_id',
'object_repr',
]
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(user_name__icontains=value) |
Q(object_repr__icontains=value)
)
#
# Job Results
#
class JobResultFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
)
created = django_filters.DateTimeFilter()
completed = django_filters.DateTimeFilter()
status = django_filters.MultipleChoiceFilter(
choices=JobResultStatusChoices,
null_value=None
)
class Meta:
model = JobResult
fields = [
'id', 'created', 'completed', 'status', 'user', 'obj_type', 'name'
]
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(user__username__icontains=value)
)
#
# ContentTypes
#
class ContentTypeFilterSet(django_filters.FilterSet):
class Meta:
model = ContentType
fields = ['id', 'app_label', 'model']

341
netbox/extras/filtersets.py Normal file
View File

@ -0,0 +1,341 @@
import django_filters
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup
from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet
from tenancy.models import Tenant, TenantGroup
from utilities.filters import ContentTypeFilter
from virtualization.models import Cluster, ClusterGroup
from .choices import *
from .models import *
__all__ = (
'ConfigContextFilterSet',
'ContentTypeFilterSet',
'CustomLinkFilterSet',
'ExportTemplateFilterSet',
'ImageAttachmentFilterSet',
'JournalEntryFilterSet',
'LocalConfigContextFilterSet',
'ObjectChangeFilterSet',
'TagFilterSet',
'WebhookFilterSet',
)
EXACT_FILTER_TYPES = (
CustomFieldTypeChoices.TYPE_BOOLEAN,
CustomFieldTypeChoices.TYPE_DATE,
CustomFieldTypeChoices.TYPE_INTEGER,
CustomFieldTypeChoices.TYPE_SELECT,
)
class WebhookFilterSet(BaseFilterSet):
content_types = ContentTypeFilter()
http_method = django_filters.MultipleChoiceFilter(
choices=WebhookHttpMethodChoices
)
class Meta:
model = Webhook
fields = [
'id', 'content_types', 'name', 'type_create', 'type_update', 'type_delete', 'payload_url', 'enabled',
'http_method', 'http_content_type', 'secret', 'ssl_verification', 'ca_file_path',
]
class CustomFieldFilterSet(django_filters.FilterSet):
content_types = ContentTypeFilter()
class Meta:
model = CustomField
fields = ['id', 'content_types', 'name', 'required', 'filter_logic', 'weight']
class CustomLinkFilterSet(BaseFilterSet):
class Meta:
model = CustomLink
fields = ['id', 'content_type', 'name', 'link_text', 'link_url', 'weight', 'group_name', 'new_window']
class ExportTemplateFilterSet(BaseFilterSet):
class Meta:
model = ExportTemplate
fields = ['id', 'content_type', 'name']
class ImageAttachmentFilterSet(BaseFilterSet):
created = django_filters.DateTimeFilter()
content_type = ContentTypeFilter()
class Meta:
model = ImageAttachment
fields = ['id', 'content_type_id', 'object_id', 'name']
class JournalEntryFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
)
created = django_filters.DateTimeFromToRangeFilter()
assigned_object_type = ContentTypeFilter()
created_by_id = django_filters.ModelMultipleChoiceFilter(
queryset=User.objects.all(),
label='User (ID)',
)
created_by = django_filters.ModelMultipleChoiceFilter(
field_name='created_by__username',
queryset=User.objects.all(),
to_field_name='username',
label='User (name)',
)
kind = django_filters.MultipleChoiceFilter(
choices=JournalEntryKindChoices
)
class Meta:
model = JournalEntry
fields = ['id', 'assigned_object_type_id', 'assigned_object_id', 'created', 'kind']
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(comments__icontains=value)
class TagFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
)
class Meta:
model = Tag
fields = ['id', 'name', 'slug', 'color']
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(slug__icontains=value)
)
class ConfigContextFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
)
region_id = django_filters.ModelMultipleChoiceFilter(
field_name='regions',
queryset=Region.objects.all(),
label='Region',
)
region = django_filters.ModelMultipleChoiceFilter(
field_name='regions__slug',
queryset=Region.objects.all(),
to_field_name='slug',
label='Region (slug)',
)
site_group = django_filters.ModelMultipleChoiceFilter(
field_name='site_groups__slug',
queryset=SiteGroup.objects.all(),
to_field_name='slug',
label='Site group (slug)',
)
site_group_id = django_filters.ModelMultipleChoiceFilter(
field_name='site_groups',
queryset=SiteGroup.objects.all(),
label='Site group',
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='sites',
queryset=Site.objects.all(),
label='Site',
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='sites__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site (slug)',
)
device_type_id = django_filters.ModelMultipleChoiceFilter(
field_name='device_types',
queryset=DeviceType.objects.all(),
label='Device type',
)
role_id = django_filters.ModelMultipleChoiceFilter(
field_name='roles',
queryset=DeviceRole.objects.all(),
label='Role',
)
role = django_filters.ModelMultipleChoiceFilter(
field_name='roles__slug',
queryset=DeviceRole.objects.all(),
to_field_name='slug',
label='Role (slug)',
)
platform_id = django_filters.ModelMultipleChoiceFilter(
field_name='platforms',
queryset=Platform.objects.all(),
label='Platform',
)
platform = django_filters.ModelMultipleChoiceFilter(
field_name='platforms__slug',
queryset=Platform.objects.all(),
to_field_name='slug',
label='Platform (slug)',
)
cluster_group_id = django_filters.ModelMultipleChoiceFilter(
field_name='cluster_groups',
queryset=ClusterGroup.objects.all(),
label='Cluster group',
)
cluster_group = django_filters.ModelMultipleChoiceFilter(
field_name='cluster_groups__slug',
queryset=ClusterGroup.objects.all(),
to_field_name='slug',
label='Cluster group (slug)',
)
cluster_id = django_filters.ModelMultipleChoiceFilter(
field_name='clusters',
queryset=Cluster.objects.all(),
label='Cluster',
)
tenant_group_id = django_filters.ModelMultipleChoiceFilter(
field_name='tenant_groups',
queryset=TenantGroup.objects.all(),
label='Tenant group',
)
tenant_group = django_filters.ModelMultipleChoiceFilter(
field_name='tenant_groups__slug',
queryset=TenantGroup.objects.all(),
to_field_name='slug',
label='Tenant group (slug)',
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
field_name='tenants',
queryset=Tenant.objects.all(),
label='Tenant',
)
tenant = django_filters.ModelMultipleChoiceFilter(
field_name='tenants__slug',
queryset=Tenant.objects.all(),
to_field_name='slug',
label='Tenant (slug)',
)
tag = django_filters.ModelMultipleChoiceFilter(
field_name='tags__slug',
queryset=Tag.objects.all(),
to_field_name='slug',
label='Tag (slug)',
)
class Meta:
model = ConfigContext
fields = ['id', 'name', 'is_active']
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(description__icontains=value) |
Q(data__icontains=value)
)
#
# Filter for Local Config Context Data
#
class LocalConfigContextFilterSet(django_filters.FilterSet):
local_context_data = django_filters.BooleanFilter(
method='_local_context_data',
label='Has local config context data',
)
def _local_context_data(self, queryset, name, value):
return queryset.exclude(local_context_data__isnull=value)
class ObjectChangeFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
)
time = django_filters.DateTimeFromToRangeFilter()
changed_object_type = ContentTypeFilter()
user_id = django_filters.ModelMultipleChoiceFilter(
queryset=User.objects.all(),
label='User (ID)',
)
user = django_filters.ModelMultipleChoiceFilter(
field_name='user__username',
queryset=User.objects.all(),
to_field_name='username',
label='User name',
)
class Meta:
model = ObjectChange
fields = [
'id', 'user', 'user_name', 'request_id', 'action', 'changed_object_type_id', 'changed_object_id',
'object_repr',
]
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(user_name__icontains=value) |
Q(object_repr__icontains=value)
)
#
# Job Results
#
class JobResultFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
)
created = django_filters.DateTimeFilter()
completed = django_filters.DateTimeFilter()
status = django_filters.MultipleChoiceFilter(
choices=JobResultStatusChoices,
null_value=None
)
class Meta:
model = JobResult
fields = [
'id', 'created', 'completed', 'status', 'user', 'obj_type', 'name'
]
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(user__username__icontains=value)
)
#
# ContentTypes
#
class ContentTypeFilterSet(django_filters.FilterSet):
class Meta:
model = ContentType
fields = ['id', 'app_label', 'model']

View File

@ -3,7 +3,7 @@ from django.core.exceptions import ValidationError
from django.urls import reverse
from rest_framework import status
from dcim.filters import SiteFilterSet
from dcim.filtersets import SiteFilterSet
from dcim.forms import SiteCSVForm
from dcim.models import Site, Rack
from extras.choices import *

View File

@ -1,4 +1,5 @@
import uuid
from datetime import datetime, timezone
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
@ -6,14 +7,15 @@ from django.test import TestCase
from dcim.models import DeviceRole, DeviceType, Manufacturer, Platform, Rack, Region, Site, SiteGroup
from extras.choices import JournalEntryKindChoices, ObjectChangeActionChoices
from extras.filters import *
from extras.filtersets import *
from extras.models import *
from ipam.models import IPAddress
from tenancy.models import Tenant, TenantGroup
from utilities.testing import BaseFilterSetTests, ChangeLoggedFilterSetTests
from virtualization.models import Cluster, ClusterGroup, ClusterType
class WebhookTestCase(TestCase):
class WebhookTestCase(TestCase, BaseFilterSetTests):
queryset = Webhook.objects.all()
filterset = WebhookFilterSet
@ -52,10 +54,6 @@ class WebhookTestCase(TestCase):
webhooks[1].content_types.add(content_types[1])
webhooks[2].content_types.add(content_types[2])
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Webhook 1', 'Webhook 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -89,7 +87,7 @@ class WebhookTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class CustomLinkTestCase(TestCase):
class CustomLinkTestCase(TestCase, BaseFilterSetTests):
queryset = CustomLink.objects.all()
filterset = CustomLinkFilterSet
@ -125,10 +123,6 @@ class CustomLinkTestCase(TestCase):
)
CustomLink.objects.bulk_create(custom_links)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Custom Link 1', 'Custom Link 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -148,7 +142,7 @@ class CustomLinkTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
class ExportTemplateTestCase(TestCase):
class ExportTemplateTestCase(TestCase, BaseFilterSetTests):
queryset = ExportTemplate.objects.all()
filterset = ExportTemplateFilterSet
@ -164,10 +158,6 @@ class ExportTemplateTestCase(TestCase):
)
ExportTemplate.objects.bulk_create(export_templates)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Export Template 1', 'Export Template 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -177,7 +167,7 @@ class ExportTemplateTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
class ImageAttachmentTestCase(TestCase):
class ImageAttachmentTestCase(TestCase, BaseFilterSetTests):
queryset = ImageAttachment.objects.all()
filterset = ImageAttachmentFilterSet
@ -235,10 +225,6 @@ class ImageAttachmentTestCase(TestCase):
)
ImageAttachment.objects.bulk_create(image_attachments)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Image Attachment 1', 'Image Attachment 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -254,8 +240,14 @@ class ImageAttachmentTestCase(TestCase):
}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
def test_created(self):
pk_list = self.queryset.values_list('pk', flat=True)[:2]
self.queryset.filter(pk__in=pk_list).update(created=datetime(2021, 1, 1, 0, 0, 0, tzinfo=timezone.utc))
params = {'created': '2021-01-01T00:00:00'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class JournalEntryTestCase(TestCase):
class JournalEntryTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = JournalEntry.objects.all()
filterset = JournalEntryFilterSet
@ -320,10 +312,6 @@ class JournalEntryTestCase(TestCase):
)
JournalEntry.objects.bulk_create(journal_entries)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_created_by(self):
users = User.objects.filter(username__in=['Alice', 'Bob'])
params = {'created_by': [users[0].username, users[1].username]}
@ -348,8 +336,17 @@ class JournalEntryTestCase(TestCase):
params = {'kind': [JournalEntryKindChoices.KIND_INFO, JournalEntryKindChoices.KIND_SUCCESS]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
def test_created(self):
pk_list = self.queryset.values_list('pk', flat=True)[:2]
self.queryset.filter(pk__in=pk_list).update(created=datetime(2021, 1, 1, 0, 0, 0, tzinfo=timezone.utc))
params = {
'created_after': '2020-12-31T00:00:00',
'created_before': '2021-01-02T00:00:00',
}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class ConfigContextTestCase(TestCase):
class ConfigContextTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ConfigContext.objects.all()
filterset = ConfigContextFilterSet
@ -449,10 +446,6 @@ class ConfigContextTestCase(TestCase):
c.tenant_groups.set([tenant_groups[i]])
c.tenants.set([tenants[i]])
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Config Context 1', 'Config Context 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -530,7 +523,7 @@ class ConfigContextTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class TagTestCase(TestCase):
class TagTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Tag.objects.all()
filterset = TagFilterSet
@ -544,10 +537,6 @@ class TagTestCase(TestCase):
)
Tag.objects.bulk_create(tags)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Tag 1', 'Tag 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -561,7 +550,7 @@ class TagTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class ObjectChangeTestCase(TestCase):
class ObjectChangeTestCase(TestCase, BaseFilterSetTests):
queryset = ObjectChange.objects.all()
filterset = ObjectChangeFilterSet
@ -635,10 +624,6 @@ class ObjectChangeTestCase(TestCase):
)
ObjectChange.objects.bulk_create(object_changes)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:3]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
def test_user(self):
params = {'user_id': User.objects.filter(username__in=['user1', 'user2']).values_list('pk', flat=True)}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)

View File

@ -13,7 +13,7 @@ from utilities.forms import ConfirmationForm
from utilities.tables import paginate_table
from utilities.utils import copy_safe_request, count_related, shallow_compare_dict
from utilities.views import ContentTypePermissionRequiredMixin
from . import filters, forms, tables
from . import filtersets, forms, tables
from .choices import JobResultStatusChoices
from .models import ConfigContext, ImageAttachment, JournalEntry, ObjectChange, JobResult, Tag, TaggedItem
from .reports import get_report, get_reports, run_report
@ -28,7 +28,7 @@ class TagListView(generic.ObjectListView):
queryset = Tag.objects.annotate(
items=count_related(TaggedItem, 'tag')
)
filterset = filters.TagFilterSet
filterset = filtersets.TagFilterSet
filterset_form = forms.TagFilterForm
table = tables.TagTable
@ -94,7 +94,7 @@ class TagBulkDeleteView(generic.BulkDeleteView):
class ConfigContextListView(generic.ObjectListView):
queryset = ConfigContext.objects.all()
filterset = filters.ConfigContextFilterSet
filterset = filtersets.ConfigContextFilterSet
filterset_form = forms.ConfigContextFilterForm
table = tables.ConfigContextTable
action_buttons = ('add',)
@ -127,7 +127,7 @@ class ConfigContextEditView(generic.ObjectEditView):
class ConfigContextBulkEditView(generic.BulkEditView):
queryset = ConfigContext.objects.all()
filterset = filters.ConfigContextFilterSet
filterset = filtersets.ConfigContextFilterSet
table = tables.ConfigContextTable
form = forms.ConfigContextBulkEditForm
@ -173,7 +173,7 @@ class ObjectConfigContextView(generic.ObjectView):
class ObjectChangeListView(generic.ObjectListView):
queryset = ObjectChange.objects.all()
filterset = filters.ObjectChangeFilterSet
filterset = filtersets.ObjectChangeFilterSet
filterset_form = forms.ObjectChangeFilterForm
table = tables.ObjectChangeTable
template_name = 'extras/objectchange_list.html'
@ -300,7 +300,7 @@ class ImageAttachmentDeleteView(generic.ObjectDeleteView):
class JournalEntryListView(generic.ObjectListView):
queryset = JournalEntry.objects.all()
filterset = filters.JournalEntryFilterSet
filterset = filtersets.JournalEntryFilterSet
filterset_form = forms.JournalEntryFilterForm
table = tables.JournalEntryTable
action_buttons = ('export',)
@ -338,14 +338,14 @@ class JournalEntryDeleteView(generic.ObjectDeleteView):
class JournalEntryBulkEditView(generic.BulkEditView):
queryset = JournalEntry.objects.prefetch_related('created_by')
filterset = filters.JournalEntryFilterSet
filterset = filtersets.JournalEntryFilterSet
table = tables.JournalEntryTable
form = forms.JournalEntryBulkEditForm
class JournalEntryBulkDeleteView(generic.BulkDeleteView):
queryset = JournalEntry.objects.prefetch_related('created_by')
filterset = filters.JournalEntryFilterSet
filterset = filtersets.JournalEntryFilterSet
table = tables.JournalEntryTable

View File

@ -10,7 +10,7 @@ from rest_framework.response import Response
from rest_framework.routers import APIRootView
from extras.api.views import CustomFieldModelViewSet
from ipam import filters
from ipam import filtersets
from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, RouteTarget, Service, VLAN, VLANGroup, VRF
from netbox.api.views import ModelViewSet
from utilities.constants import ADVISORY_LOCK_KEYS
@ -38,7 +38,7 @@ class VRFViewSet(CustomFieldModelViewSet):
prefix_count=count_related(Prefix, 'vrf')
)
serializer_class = serializers.VRFSerializer
filterset_class = filters.VRFFilterSet
filterset_class = filtersets.VRFFilterSet
#
@ -48,7 +48,7 @@ class VRFViewSet(CustomFieldModelViewSet):
class RouteTargetViewSet(CustomFieldModelViewSet):
queryset = RouteTarget.objects.prefetch_related('tenant').prefetch_related('tags')
serializer_class = serializers.RouteTargetSerializer
filterset_class = filters.RouteTargetFilterSet
filterset_class = filtersets.RouteTargetFilterSet
#
@ -60,7 +60,7 @@ class RIRViewSet(CustomFieldModelViewSet):
aggregate_count=count_related(Aggregate, 'rir')
)
serializer_class = serializers.RIRSerializer
filterset_class = filters.RIRFilterSet
filterset_class = filtersets.RIRFilterSet
#
@ -70,7 +70,7 @@ class RIRViewSet(CustomFieldModelViewSet):
class AggregateViewSet(CustomFieldModelViewSet):
queryset = Aggregate.objects.prefetch_related('rir').prefetch_related('tags')
serializer_class = serializers.AggregateSerializer
filterset_class = filters.AggregateFilterSet
filterset_class = filtersets.AggregateFilterSet
#
@ -83,7 +83,7 @@ class RoleViewSet(CustomFieldModelViewSet):
vlan_count=count_related(VLAN, 'role')
)
serializer_class = serializers.RoleSerializer
filterset_class = filters.RoleFilterSet
filterset_class = filtersets.RoleFilterSet
#
@ -95,7 +95,7 @@ class PrefixViewSet(CustomFieldModelViewSet):
'site', 'vrf__tenant', 'tenant', 'vlan', 'role', 'tags'
)
serializer_class = serializers.PrefixSerializer
filterset_class = filters.PrefixFilterSet
filterset_class = filtersets.PrefixFilterSet
def get_serializer_class(self):
if self.action == "available_prefixes" and self.request.method == "POST":
@ -275,7 +275,7 @@ class IPAddressViewSet(CustomFieldModelViewSet):
'vrf__tenant', 'tenant', 'nat_inside', 'nat_outside', 'tags', 'assigned_object'
)
serializer_class = serializers.IPAddressSerializer
filterset_class = filters.IPAddressFilterSet
filterset_class = filtersets.IPAddressFilterSet
#
@ -287,7 +287,7 @@ class VLANGroupViewSet(CustomFieldModelViewSet):
vlan_count=count_related(VLAN, 'group')
)
serializer_class = serializers.VLANGroupSerializer
filterset_class = filters.VLANGroupFilterSet
filterset_class = filtersets.VLANGroupFilterSet
#
@ -301,7 +301,7 @@ class VLANViewSet(CustomFieldModelViewSet):
prefix_count=count_related(Prefix, 'vlan')
)
serializer_class = serializers.VLANSerializer
filterset_class = filters.VLANFilterSet
filterset_class = filtersets.VLANFilterSet
#
@ -313,4 +313,4 @@ class ServiceViewSet(ModelViewSet):
'device', 'virtual_machine', 'tags', 'ipaddresses'
)
serializer_class = serializers.ServiceSerializer
filterset_class = filters.ServiceFilterSet
filterset_class = filtersets.ServiceFilterSet

View File

@ -6,11 +6,11 @@ from django.db.models import Q
from netaddr.core import AddrFormatError
from dcim.models import Device, Interface, Region, Site, SiteGroup
from extras.filters import CustomFieldModelFilterSet, CreatedUpdatedFilterSet
from tenancy.filters import TenancyFilterSet
from extras.filters import TagFilter
from netbox.filtersets import OrganizationalModelFilterSet, PrimaryModelFilterSet
from tenancy.filtersets import TenancyFilterSet
from utilities.filters import (
BaseFilterSet, ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter, NameSlugSearchFilterSet,
NumericArrayFilter, TagFilter, TreeNodeMultipleChoiceFilter,
ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter, NumericArrayFilter, TreeNodeMultipleChoiceFilter,
)
from virtualization.models import VirtualMachine, VMInterface
from .choices import *
@ -31,7 +31,7 @@ __all__ = (
)
class VRFFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class VRFFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -74,7 +74,7 @@ class VRFFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet, C
fields = ['id', 'name', 'rd', 'enforce_unique']
class RouteTargetFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class RouteTargetFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -116,14 +116,14 @@ class RouteTargetFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilt
fields = ['id', 'name']
class RIRFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFilterSet):
class RIRFilterSet(OrganizationalModelFilterSet):
class Meta:
model = RIR
fields = ['id', 'name', 'slug', 'is_private', 'description']
class AggregateFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class AggregateFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -173,7 +173,7 @@ class AggregateFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilter
return queryset.none()
class RoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFilterSet):
class RoleFilterSet(OrganizationalModelFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -184,7 +184,7 @@ class RoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFilter
fields = ['id', 'name', 'slug']
class PrefixFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class PrefixFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -369,7 +369,7 @@ class PrefixFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet
)
class IPAddressFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class IPAddressFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -535,7 +535,7 @@ class IPAddressFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilter
return queryset.exclude(assigned_object_id__isnull=value)
class VLANGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFilterSet):
class VLANGroupFilterSet(OrganizationalModelFilterSet):
scope_type = ContentTypeFilter()
region = django_filters.NumberFilter(
method='filter_scope'
@ -570,7 +570,7 @@ class VLANGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedF
)
class VLANFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class VLANFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -666,7 +666,7 @@ class VLANFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet,
return queryset.get_for_virtualmachine(value)
class ServiceFilterSet(BaseFilterSet, CreatedUpdatedFilterSet):
class ServiceFilterSet(PrimaryModelFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',

View File

@ -2,13 +2,14 @@ from django.test import TestCase
from dcim.models import Device, DeviceRole, DeviceType, Interface, Location, Manufacturer, Rack, Region, Site, SiteGroup
from ipam.choices import *
from ipam.filters import *
from ipam.filtersets import *
from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, RouteTarget, Service, VLAN, VLANGroup, VRF
from utilities.testing import ChangeLoggedFilterSetTests
from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
from tenancy.models import Tenant, TenantGroup
class VRFTestCase(TestCase):
class VRFTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = VRF.objects.all()
filterset = VRFFilterSet
@ -53,10 +54,6 @@ class VRFTestCase(TestCase):
vrfs[2].import_targets.add(route_targets[2])
vrfs[2].export_targets.add(route_targets[2])
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['VRF 1', 'VRF 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -100,7 +97,7 @@ class VRFTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
class RouteTargetTestCase(TestCase):
class RouteTargetTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = RouteTarget.objects.all()
filterset = RouteTargetFilterSet
@ -149,10 +146,6 @@ class RouteTargetTestCase(TestCase):
vrfs[1].import_targets.add(route_targets[4], route_targets[5])
vrfs[1].export_targets.add(route_targets[6], route_targets[7])
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['65000:1001', '65000:1002', '65000:1003']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
@ -186,7 +179,7 @@ class RouteTargetTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 8)
class RIRTestCase(TestCase):
class RIRTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = RIR.objects.all()
filterset = RIRFilterSet
@ -203,10 +196,6 @@ class RIRTestCase(TestCase):
)
RIR.objects.bulk_create(rirs)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['RIR 1', 'RIR 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -226,7 +215,7 @@ class RIRTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
class AggregateTestCase(TestCase):
class AggregateTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Aggregate.objects.all()
filterset = AggregateFilterSet
@ -265,10 +254,6 @@ class AggregateTestCase(TestCase):
)
Aggregate.objects.bulk_create(aggregates)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_family(self):
params = {'family': '4'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
@ -304,7 +289,7 @@ class AggregateTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
class RoleTestCase(TestCase):
class RoleTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Role.objects.all()
filterset = RoleFilterSet
@ -318,10 +303,6 @@ class RoleTestCase(TestCase):
)
Role.objects.bulk_create(roles)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -331,7 +312,7 @@ class RoleTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class PrefixTestCase(TestCase):
class PrefixTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Prefix.objects.all()
filterset = PrefixFilterSet
@ -421,10 +402,6 @@ class PrefixTestCase(TestCase):
)
Prefix.objects.bulk_create(prefixes)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_family(self):
params = {'family': '6'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 5)
@ -528,7 +505,7 @@ class PrefixTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
class IPAddressTestCase(TestCase):
class IPAddressTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = IPAddress.objects.all()
filterset = IPAddressFilterSet
@ -607,10 +584,6 @@ class IPAddressTestCase(TestCase):
)
IPAddress.objects.bulk_create(ipaddresses)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_family(self):
params = {'family': '6'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 5)
@ -708,7 +681,7 @@ class IPAddressTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
class VLANGroupTestCase(TestCase):
class VLANGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = VLANGroup.objects.all()
filterset = VLANGroupFilterSet
@ -751,10 +724,6 @@ class VLANGroupTestCase(TestCase):
)
VLANGroup.objects.bulk_create(vlan_groups)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -796,7 +765,7 @@ class VLANGroupTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
class VLANTestCase(TestCase):
class VLANTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = VLAN.objects.all()
filterset = VLANFilterSet
@ -965,10 +934,6 @@ class VLANTestCase(TestCase):
)
VLAN.objects.bulk_create(vlans)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['VLAN 101', 'VLAN 102']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -1041,7 +1006,7 @@ class VLANTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6) # 5 scoped + 1 global
class ServiceTestCase(TestCase):
class ServiceTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Service.objects.all()
filterset = ServiceFilterSet
@ -1080,10 +1045,6 @@ class ServiceTestCase(TestCase):
)
Service.objects.bulk_create(services)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:3]}
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)

View File

@ -7,7 +7,7 @@ from netbox.views import generic
from utilities.tables import paginate_table
from utilities.utils import count_related
from virtualization.models import VirtualMachine, VMInterface
from . import filters, forms, tables
from . import filtersets, forms, tables
from .constants import *
from .models import Aggregate, IPAddress, Prefix, RIR, Role, RouteTarget, Service, VLAN, VLANGroup, VRF
from .utils import add_available_ipaddresses, add_available_prefixes, add_available_vlans
@ -19,7 +19,7 @@ from .utils import add_available_ipaddresses, add_available_prefixes, add_availa
class VRFListView(generic.ObjectListView):
queryset = VRF.objects.all()
filterset = filters.VRFFilterSet
filterset = filtersets.VRFFilterSet
filterset_form = forms.VRFFilterForm
table = tables.VRFTable
@ -65,14 +65,14 @@ class VRFBulkImportView(generic.BulkImportView):
class VRFBulkEditView(generic.BulkEditView):
queryset = VRF.objects.prefetch_related('tenant')
filterset = filters.VRFFilterSet
filterset = filtersets.VRFFilterSet
table = tables.VRFTable
form = forms.VRFBulkEditForm
class VRFBulkDeleteView(generic.BulkDeleteView):
queryset = VRF.objects.prefetch_related('tenant')
filterset = filters.VRFFilterSet
filterset = filtersets.VRFFilterSet
table = tables.VRFTable
@ -82,7 +82,7 @@ class VRFBulkDeleteView(generic.BulkDeleteView):
class RouteTargetListView(generic.ObjectListView):
queryset = RouteTarget.objects.all()
filterset = filters.RouteTargetFilterSet
filterset = filtersets.RouteTargetFilterSet
filterset_form = forms.RouteTargetFilterForm
table = tables.RouteTargetTable
@ -123,14 +123,14 @@ class RouteTargetBulkImportView(generic.BulkImportView):
class RouteTargetBulkEditView(generic.BulkEditView):
queryset = RouteTarget.objects.prefetch_related('tenant')
filterset = filters.RouteTargetFilterSet
filterset = filtersets.RouteTargetFilterSet
table = tables.RouteTargetTable
form = forms.RouteTargetBulkEditForm
class RouteTargetBulkDeleteView(generic.BulkDeleteView):
queryset = RouteTarget.objects.prefetch_related('tenant')
filterset = filters.RouteTargetFilterSet
filterset = filtersets.RouteTargetFilterSet
table = tables.RouteTargetTable
@ -142,7 +142,7 @@ class RIRListView(generic.ObjectListView):
queryset = RIR.objects.annotate(
aggregate_count=count_related(Aggregate, 'rir')
)
filterset = filters.RIRFilterSet
filterset = filtersets.RIRFilterSet
filterset_form = forms.RIRFilterForm
table = tables.RIRTable
template_name = 'ipam/rir_list.html'
@ -184,7 +184,7 @@ class RIRBulkEditView(generic.BulkEditView):
queryset = RIR.objects.annotate(
aggregate_count=count_related(Aggregate, 'rir')
)
filterset = filters.RIRFilterSet
filterset = filtersets.RIRFilterSet
table = tables.RIRTable
form = forms.RIRBulkEditForm
@ -193,7 +193,7 @@ class RIRBulkDeleteView(generic.BulkDeleteView):
queryset = RIR.objects.annotate(
aggregate_count=count_related(Aggregate, 'rir')
)
filterset = filters.RIRFilterSet
filterset = filtersets.RIRFilterSet
table = tables.RIRTable
@ -205,7 +205,7 @@ class AggregateListView(generic.ObjectListView):
queryset = Aggregate.objects.annotate(
child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
)
filterset = filters.AggregateFilterSet
filterset = filtersets.AggregateFilterSet
filterset_form = forms.AggregateFilterForm
table = tables.AggregateDetailTable
template_name = 'ipam/aggregate_list.html'
@ -280,14 +280,14 @@ class AggregateBulkImportView(generic.BulkImportView):
class AggregateBulkEditView(generic.BulkEditView):
queryset = Aggregate.objects.prefetch_related('rir')
filterset = filters.AggregateFilterSet
filterset = filtersets.AggregateFilterSet
table = tables.AggregateTable
form = forms.AggregateBulkEditForm
class AggregateBulkDeleteView(generic.BulkDeleteView):
queryset = Aggregate.objects.prefetch_related('rir')
filterset = filters.AggregateFilterSet
filterset = filtersets.AggregateFilterSet
table = tables.AggregateTable
@ -337,7 +337,7 @@ class RoleBulkImportView(generic.BulkImportView):
class RoleBulkEditView(generic.BulkEditView):
queryset = Role.objects.all()
filterset = filters.RoleFilterSet
filterset = filtersets.RoleFilterSet
table = tables.RoleTable
form = forms.RoleBulkEditForm
@ -353,7 +353,7 @@ class RoleBulkDeleteView(generic.BulkDeleteView):
class PrefixListView(generic.ObjectListView):
queryset = Prefix.objects.annotate_tree()
filterset = filters.PrefixFilterSet
filterset = filtersets.PrefixFilterSet
filterset_form = forms.PrefixFilterForm
table = tables.PrefixDetailTable
template_name = 'ipam/prefix_list.html'
@ -493,14 +493,14 @@ class PrefixBulkImportView(generic.BulkImportView):
class PrefixBulkEditView(generic.BulkEditView):
queryset = Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
filterset = filters.PrefixFilterSet
filterset = filtersets.PrefixFilterSet
table = tables.PrefixTable
form = forms.PrefixBulkEditForm
class PrefixBulkDeleteView(generic.BulkDeleteView):
queryset = Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
filterset = filters.PrefixFilterSet
filterset = filtersets.PrefixFilterSet
table = tables.PrefixTable
@ -510,7 +510,7 @@ class PrefixBulkDeleteView(generic.BulkDeleteView):
class IPAddressListView(generic.ObjectListView):
queryset = IPAddress.objects.all()
filterset = filters.IPAddressFilterSet
filterset = filtersets.IPAddressFilterSet
filterset_form = forms.IPAddressFilterForm
table = tables.IPAddressDetailTable
@ -613,7 +613,7 @@ class IPAddressAssignView(generic.ObjectView):
addresses = self.queryset.prefetch_related('vrf', 'tenant')
# Limit to 100 results
addresses = filters.IPAddressFilterSet(request.POST, addresses).qs[:100]
addresses = filtersets.IPAddressFilterSet(request.POST, addresses).qs[:100]
table = tables.IPAddressAssignTable(addresses)
return render(request, 'ipam/ipaddress_assign.html', {
@ -643,14 +643,14 @@ class IPAddressBulkImportView(generic.BulkImportView):
class IPAddressBulkEditView(generic.BulkEditView):
queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
filterset = filters.IPAddressFilterSet
filterset = filtersets.IPAddressFilterSet
table = tables.IPAddressTable
form = forms.IPAddressBulkEditForm
class IPAddressBulkDeleteView(generic.BulkDeleteView):
queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
filterset = filters.IPAddressFilterSet
filterset = filtersets.IPAddressFilterSet
table = tables.IPAddressTable
@ -662,7 +662,7 @@ class VLANGroupListView(generic.ObjectListView):
queryset = VLANGroup.objects.annotate(
vlan_count=count_related(VLAN, 'group')
)
filterset = filters.VLANGroupFilterSet
filterset = filtersets.VLANGroupFilterSet
filterset_form = forms.VLANGroupFilterForm
table = tables.VLANGroupTable
@ -673,7 +673,7 @@ class VLANGroupView(generic.ObjectView):
def get_extra_context(self, request, instance):
vlans = VLAN.objects.restrict(request.user, 'view').filter(group=instance).prefetch_related(
Prefetch('prefixes', queryset=Prefix.objects.restrict(request.user))
)
).order_by('vid')
vlans_count = vlans.count()
vlans = add_available_vlans(instance, vlans)
@ -684,9 +684,17 @@ class VLANGroupView(generic.ObjectView):
vlans_table.columns.hide('group')
paginate_table(vlans_table, request)
# Compile permissions list for rendering the object table
permissions = {
'add': request.user.has_perm('ipam.add_vlan'),
'change': request.user.has_perm('ipam.change_vlan'),
'delete': request.user.has_perm('ipam.delete_vlan'),
}
return {
'vlans_count': vlans_count,
'vlans_table': vlans_table,
'permissions': permissions,
}
@ -710,7 +718,7 @@ class VLANGroupBulkEditView(generic.BulkEditView):
queryset = VLANGroup.objects.annotate(
vlan_count=count_related(VLAN, 'group')
)
filterset = filters.VLANGroupFilterSet
filterset = filtersets.VLANGroupFilterSet
table = tables.VLANGroupTable
form = forms.VLANGroupBulkEditForm
@ -719,7 +727,7 @@ class VLANGroupBulkDeleteView(generic.BulkDeleteView):
queryset = VLANGroup.objects.annotate(
vlan_count=count_related(VLAN, 'group')
)
filterset = filters.VLANGroupFilterSet
filterset = filtersets.VLANGroupFilterSet
table = tables.VLANGroupTable
@ -729,7 +737,7 @@ class VLANGroupBulkDeleteView(generic.BulkDeleteView):
class VLANListView(generic.ObjectListView):
queryset = VLAN.objects.all()
filterset = filters.VLANFilterSet
filterset = filtersets.VLANFilterSet
filterset_form = forms.VLANFilterForm
table = tables.VLANDetailTable
@ -797,14 +805,14 @@ class VLANBulkImportView(generic.BulkImportView):
class VLANBulkEditView(generic.BulkEditView):
queryset = VLAN.objects.prefetch_related('site', 'group', 'tenant', 'role')
filterset = filters.VLANFilterSet
filterset = filtersets.VLANFilterSet
table = tables.VLANTable
form = forms.VLANBulkEditForm
class VLANBulkDeleteView(generic.BulkDeleteView):
queryset = VLAN.objects.prefetch_related('site', 'group', 'tenant', 'role')
filterset = filters.VLANFilterSet
filterset = filtersets.VLANFilterSet
table = tables.VLANTable
@ -814,7 +822,7 @@ class VLANBulkDeleteView(generic.BulkDeleteView):
class ServiceListView(generic.ObjectListView):
queryset = Service.objects.all()
filterset = filters.ServiceFilterSet
filterset = filtersets.ServiceFilterSet
filterset_form = forms.ServiceFilterForm
table = tables.ServiceTable
action_buttons = ('import', 'export')
@ -855,12 +863,12 @@ class ServiceDeleteView(generic.ObjectDeleteView):
class ServiceBulkEditView(generic.BulkEditView):
queryset = Service.objects.prefetch_related('device', 'virtual_machine')
filterset = filters.ServiceFilterSet
filterset = filtersets.ServiceFilterSet
table = tables.ServiceTable
form = forms.ServiceBulkEditForm
class ServiceBulkDeleteView(generic.BulkDeleteView):
queryset = Service.objects.prefetch_related('device', 'virtual_machine')
filterset = filters.ServiceFilterSet
filterset = filtersets.ServiceFilterSet
table = tables.ServiceTable

View File

@ -246,6 +246,9 @@ RQ_DEFAULT_TIMEOUT = 300
# this setting is derived from the installed location.
# SCRIPTS_ROOT = '/opt/netbox/netbox/scripts'
# The name to use for the session cookie.
SESSION_COOKIE_NAME = 'sessionid'
# By default, NetBox will store session data in the database. Alternatively, a file path can be specified here to use
# local file storage instead. (This can be useful for enabling authentication on a standby instance with read-only
# database access.) Note that the user as which NetBox runs must have read and write permissions to this path.

View File

@ -1,9 +1,9 @@
from collections import OrderedDict
from circuits.filters import CircuitFilterSet, ProviderFilterSet, ProviderNetworkFilterSet
from circuits.filtersets import CircuitFilterSet, ProviderFilterSet, ProviderNetworkFilterSet
from circuits.models import Circuit, ProviderNetwork, Provider
from circuits.tables import CircuitTable, ProviderNetworkTable, ProviderTable
from dcim.filters import (
from dcim.filtersets import (
CableFilterSet, DeviceFilterSet, DeviceTypeFilterSet, PowerFeedFilterSet, RackFilterSet, LocationFilterSet,
SiteFilterSet, VirtualChassisFilterSet,
)
@ -12,17 +12,17 @@ from dcim.tables import (
CableTable, DeviceTable, DeviceTypeTable, PowerFeedTable, RackTable, LocationTable, SiteTable,
VirtualChassisTable,
)
from ipam.filters import AggregateFilterSet, IPAddressFilterSet, PrefixFilterSet, VLANFilterSet, VRFFilterSet
from ipam.filtersets import AggregateFilterSet, IPAddressFilterSet, PrefixFilterSet, VLANFilterSet, VRFFilterSet
from ipam.models import Aggregate, IPAddress, Prefix, VLAN, VRF
from ipam.tables import AggregateTable, IPAddressTable, PrefixTable, VLANTable, VRFTable
from secrets.filters import SecretFilterSet
from secrets.filtersets import SecretFilterSet
from secrets.models import Secret
from secrets.tables import SecretTable
from tenancy.filters import TenantFilterSet
from tenancy.filtersets import TenantFilterSet
from tenancy.models import Tenant
from tenancy.tables import TenantTable
from utilities.utils import count_related
from virtualization.filters import ClusterFilterSet, VirtualMachineFilterSet
from virtualization.filtersets import ClusterFilterSet, VirtualMachineFilterSet
from virtualization.models import Cluster, VirtualMachine
from virtualization.tables import ClusterTable, VirtualMachineDetailTable

238
netbox/netbox/filtersets.py Normal file
View File

@ -0,0 +1,238 @@
import django_filters
from copy import deepcopy
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django_filters.utils import get_model_field, resolve_field
from dcim.forms import MACAddressField
from extras.choices import CustomFieldFilterLogicChoices
from extras.filters import CustomFieldFilter, TagFilter
from extras.models import CustomField
from utilities.constants import (
FILTER_CHAR_BASED_LOOKUP_MAP, FILTER_NEGATION_LOOKUP_MAP, FILTER_TREENODE_NEGATION_LOOKUP_MAP,
FILTER_NUMERIC_BASED_LOOKUP_MAP
)
from utilities import filters
__all__ = (
'BaseFilterSet',
'ChangeLoggedModelFilterSet',
'OrganizationalModelFilterSet',
'PrimaryModelFilterSet',
)
#
# FilterSets
#
class BaseFilterSet(django_filters.FilterSet):
"""
A base FilterSet which provides common functionality to all NetBox FilterSets
"""
FILTER_DEFAULTS = deepcopy(django_filters.filterset.FILTER_FOR_DBFIELD_DEFAULTS)
FILTER_DEFAULTS.update({
models.AutoField: {
'filter_class': filters.MultiValueNumberFilter
},
models.CharField: {
'filter_class': filters.MultiValueCharFilter
},
models.DateField: {
'filter_class': filters.MultiValueDateFilter
},
models.DateTimeField: {
'filter_class': filters.MultiValueDateTimeFilter
},
models.DecimalField: {
'filter_class': filters.MultiValueNumberFilter
},
models.EmailField: {
'filter_class': filters.MultiValueCharFilter
},
models.FloatField: {
'filter_class': filters.MultiValueNumberFilter
},
models.IntegerField: {
'filter_class': filters.MultiValueNumberFilter
},
models.PositiveIntegerField: {
'filter_class': filters.MultiValueNumberFilter
},
models.PositiveSmallIntegerField: {
'filter_class': filters.MultiValueNumberFilter
},
models.SlugField: {
'filter_class': filters.MultiValueCharFilter
},
models.SmallIntegerField: {
'filter_class': filters.MultiValueNumberFilter
},
models.TimeField: {
'filter_class': filters.MultiValueTimeFilter
},
models.URLField: {
'filter_class': filters.MultiValueCharFilter
},
MACAddressField: {
'filter_class': filters.MultiValueMACAddressFilter
},
})
@staticmethod
def _get_filter_lookup_dict(existing_filter):
# Choose the lookup expression map based on the filter type
if isinstance(existing_filter, (
filters.MultiValueDateFilter,
filters.MultiValueDateTimeFilter,
filters.MultiValueNumberFilter,
filters.MultiValueTimeFilter
)):
lookup_map = FILTER_NUMERIC_BASED_LOOKUP_MAP
elif isinstance(existing_filter, (
filters.TreeNodeMultipleChoiceFilter,
)):
# TreeNodeMultipleChoiceFilter only support negation but must maintain the `in` lookup expression
lookup_map = FILTER_TREENODE_NEGATION_LOOKUP_MAP
elif isinstance(existing_filter, (
django_filters.ModelChoiceFilter,
django_filters.ModelMultipleChoiceFilter,
TagFilter
)) or existing_filter.extra.get('choices'):
# These filter types support only negation
lookup_map = FILTER_NEGATION_LOOKUP_MAP
elif isinstance(existing_filter, (
django_filters.filters.CharFilter,
django_filters.MultipleChoiceFilter,
filters.MultiValueCharFilter,
filters.MultiValueMACAddressFilter
)):
lookup_map = FILTER_CHAR_BASED_LOOKUP_MAP
else:
lookup_map = None
return lookup_map
@classmethod
def get_filters(cls):
"""
Override filter generation to support dynamic lookup expressions for certain filter types.
For specific filter types, new filters are created based on defined lookup expressions in
the form `<field_name>__<lookup_expr>`
"""
filters = super().get_filters()
new_filters = {}
for existing_filter_name, existing_filter in filters.items():
# Loop over existing filters to extract metadata by which to create new filters
# If the filter makes use of a custom filter method or lookup expression skip it
# as we cannot sanely handle these cases in a generic mannor
if existing_filter.method is not None or existing_filter.lookup_expr not in ['exact', 'in']:
continue
# Choose the lookup expression map based on the filter type
lookup_map = cls._get_filter_lookup_dict(existing_filter)
if lookup_map is None:
# Do not augment this filter type with more lookup expressions
continue
# Get properties of the existing filter for later use
field_name = existing_filter.field_name
field = get_model_field(cls._meta.model, field_name)
# Create new filters for each lookup expression in the map
for lookup_name, lookup_expr in lookup_map.items():
new_filter_name = '{}__{}'.format(existing_filter_name, lookup_name)
try:
if existing_filter_name in cls.declared_filters:
# The filter field has been explicity defined on the filterset class so we must manually
# create the new filter with the same type because there is no guarantee the defined type
# is the same as the default type for the field
resolve_field(field, lookup_expr) # Will raise FieldLookupError if the lookup is invalid
new_filter = type(existing_filter)(
field_name=field_name,
lookup_expr=lookup_expr,
label=existing_filter.label,
exclude=existing_filter.exclude,
distinct=existing_filter.distinct,
**existing_filter.extra
)
else:
# The filter field is listed in Meta.fields so we can safely rely on default behaviour
# Will raise FieldLookupError if the lookup is invalid
new_filter = cls.filter_for_field(field, field_name, lookup_expr)
except django_filters.exceptions.FieldLookupError:
# The filter could not be created because the lookup expression is not supported on the field
continue
if lookup_name.startswith('n'):
# This is a negation filter which requires a queryset.exclude() clause
# Of course setting the negation of the existing filter's exclude attribute handles both cases
new_filter.exclude = not existing_filter.exclude
new_filters[new_filter_name] = new_filter
filters.update(new_filters)
return filters
class ChangeLoggedModelFilterSet(BaseFilterSet):
created = django_filters.DateFilter()
created__gte = django_filters.DateFilter(
field_name='created',
lookup_expr='gte'
)
created__lte = django_filters.DateFilter(
field_name='created',
lookup_expr='lte'
)
last_updated = django_filters.DateTimeFilter()
last_updated__gte = django_filters.DateTimeFilter(
field_name='last_updated',
lookup_expr='gte'
)
last_updated__lte = django_filters.DateTimeFilter(
field_name='last_updated',
lookup_expr='lte'
)
class PrimaryModelFilterSet(ChangeLoggedModelFilterSet):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Dynamically add a Filter for each CustomField applicable to the parent model
custom_fields = CustomField.objects.filter(
content_types=ContentType.objects.get_for_model(self._meta.model)
).exclude(
filter_logic=CustomFieldFilterLogicChoices.FILTER_DISABLED
)
for cf in custom_fields:
self.filters['cf_{}'.format(cf.name)] = CustomFieldFilter(field_name=cf.name, custom_field=cf)
class OrganizationalModelFilterSet(PrimaryModelFilterSet):
"""
A base class for adding the search method to models which only expose the `name` and `slug` fields
"""
q = django_filters.CharFilter(
method='search',
label='Search',
)
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
models.Q(name__icontains=value) |
models.Q(slug__icontains=value)
)

View File

@ -114,6 +114,7 @@ REPORTS_ROOT = getattr(configuration, 'REPORTS_ROOT', os.path.join(BASE_DIR, 're
RQ_DEFAULT_TIMEOUT = getattr(configuration, 'RQ_DEFAULT_TIMEOUT', 300)
SCRIPTS_ROOT = getattr(configuration, 'SCRIPTS_ROOT', os.path.join(BASE_DIR, 'scripts')).rstrip('/')
SESSION_FILE_PATH = getattr(configuration, 'SESSION_FILE_PATH', None)
SESSION_COOKIE_NAME = getattr(configuration, 'SESSION_COOKIE_NAME', 'sessionid')
SHORT_DATE_FORMAT = getattr(configuration, 'SHORT_DATE_FORMAT', 'Y-m-d')
SHORT_DATETIME_FORMAT = getattr(configuration, 'SHORT_DATETIME_FORMAT', 'Y-m-d H:i')
SHORT_TIME_FORMAT = getattr(configuration, 'SHORT_TIME_FORMAT', 'H:i:s')

View File

@ -10,7 +10,7 @@ from rest_framework.viewsets import ViewSet
from extras.api.views import CustomFieldModelViewSet
from netbox.api.views import ModelViewSet
from secrets import filters
from secrets import filtersets
from secrets.exceptions import InvalidKey
from secrets.models import Secret, SecretRole, SessionKey, UserKey
from utilities.utils import count_related
@ -39,7 +39,7 @@ class SecretRoleViewSet(CustomFieldModelViewSet):
secret_count=count_related(Secret, 'role')
)
serializer_class = serializers.SecretRoleSerializer
filterset_class = filters.SecretRoleFilterSet
filterset_class = filtersets.SecretRoleFilterSet
#
@ -49,7 +49,7 @@ class SecretRoleViewSet(CustomFieldModelViewSet):
class SecretViewSet(ModelViewSet):
queryset = Secret.objects.prefetch_related('role', 'tags')
serializer_class = serializers.SecretSerializer
filterset_class = filters.SecretFilterSet
filterset_class = filtersets.SecretFilterSet
master_key = None

View File

@ -2,8 +2,8 @@ import django_filters
from django.db.models import Q
from dcim.models import Device
from extras.filters import CustomFieldModelFilterSet, CreatedUpdatedFilterSet
from utilities.filters import BaseFilterSet, NameSlugSearchFilterSet, TagFilter
from extras.filters import TagFilter
from netbox.filtersets import OrganizationalModelFilterSet, PrimaryModelFilterSet
from virtualization.models import VirtualMachine
from .models import Secret, SecretRole
@ -14,14 +14,14 @@ __all__ = (
)
class SecretRoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFilterSet):
class SecretRoleFilterSet(OrganizationalModelFilterSet):
class Meta:
model = SecretRole
fields = ['id', 'name', 'slug']
class SecretFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class SecretFilterSet(PrimaryModelFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',

View File

@ -1,12 +1,13 @@
from django.test import TestCase
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
from secrets.filters import *
from secrets.filtersets import *
from secrets.models import Secret, SecretRole
from utilities.testing import ChangeLoggedFilterSetTests
from virtualization.models import Cluster, ClusterType, VirtualMachine
class SecretRoleTestCase(TestCase):
class SecretRoleTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = SecretRole.objects.all()
filterset = SecretRoleFilterSet
@ -20,10 +21,6 @@ class SecretRoleTestCase(TestCase):
)
SecretRole.objects.bulk_create(roles)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -33,7 +30,7 @@ class SecretRoleTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class SecretTestCase(TestCase):
class SecretTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Secret.objects.all()
filterset = SecretFilterSet
@ -80,10 +77,6 @@ class SecretTestCase(TestCase):
for s in secrets:
s.save()
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Secret 1', 'Secret 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)

View File

@ -2,14 +2,14 @@ import base64
import logging
from django.contrib import messages
from django.shortcuts import get_object_or_404, redirect, render
from django.shortcuts import redirect, render
from django.utils.html import escape
from django.utils.safestring import mark_safe
from netbox.views import generic
from utilities.tables import paginate_table
from utilities.utils import count_related
from . import filters, forms, tables
from . import filtersets, forms, tables
from .models import SecretRole, Secret, SessionKey, UserKey
@ -70,7 +70,7 @@ class SecretRoleBulkEditView(generic.BulkEditView):
queryset = SecretRole.objects.annotate(
secret_count=count_related(Secret, 'role')
)
filterset = filters.SecretRoleFilterSet
filterset = filtersets.SecretRoleFilterSet
table = tables.SecretRoleTable
form = forms.SecretRoleBulkEditForm
@ -88,7 +88,7 @@ class SecretRoleBulkDeleteView(generic.BulkDeleteView):
class SecretListView(generic.ObjectListView):
queryset = Secret.objects.all()
filterset = filters.SecretFilterSet
filterset = filtersets.SecretFilterSet
filterset_form = forms.SecretFilterForm
table = tables.SecretTable
action_buttons = ('add', 'import', 'export')
@ -220,12 +220,12 @@ class SecretBulkImportView(generic.BulkImportView):
class SecretBulkEditView(generic.BulkEditView):
queryset = Secret.objects.prefetch_related('role')
filterset = filters.SecretFilterSet
filterset = filtersets.SecretFilterSet
table = tables.SecretTable
form = forms.SecretBulkEditForm
class SecretBulkDeleteView(generic.BulkDeleteView):
queryset = Secret.objects.prefetch_related('role')
filterset = filters.SecretFilterSet
filterset = filtersets.SecretFilterSet
table = tables.SecretTable

View File

@ -10,6 +10,15 @@
<li class="breadcrumb-item">{{ object }}</li>
{% endblock %}
{% block buttons %}
{% if perms.ipam.add_vlan %}
<a href="{% url 'ipam:vlan_add' %}?group={{ object.pk }}" class="btn btn-success">
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add VLAN
</a>
{% endif %}
{{ block.super }}
{% endblock %}
{% block content %}
<div class="row mb-3">
<div class="col col-md-6">

View File

@ -4,7 +4,7 @@ from circuits.models import Circuit
from dcim.models import Device, Rack, Site
from extras.api.views import CustomFieldModelViewSet
from ipam.models import IPAddress, Prefix, VLAN, VRF
from tenancy import filters
from tenancy import filtersets
from tenancy.models import Tenant, TenantGroup
from utilities.utils import count_related
from virtualization.models import VirtualMachine
@ -32,7 +32,7 @@ class TenantGroupViewSet(CustomFieldModelViewSet):
cumulative=True
)
serializer_class = serializers.TenantGroupSerializer
filterset_class = filters.TenantGroupFilterSet
filterset_class = filtersets.TenantGroupFilterSet
#
@ -54,4 +54,4 @@ class TenantViewSet(CustomFieldModelViewSet):
vrf_count=count_related(VRF, 'tenant')
)
serializer_class = serializers.TenantSerializer
filterset_class = filters.TenantFilterSet
filterset_class = filtersets.TenantFilterSet

View File

@ -1,8 +1,9 @@
import django_filters
from django.db.models import Q
from extras.filters import CustomFieldModelFilterSet, CreatedUpdatedFilterSet
from utilities.filters import BaseFilterSet, NameSlugSearchFilterSet, TagFilter, TreeNodeMultipleChoiceFilter
from extras.filters import TagFilter
from netbox.filtersets import OrganizationalModelFilterSet, PrimaryModelFilterSet
from utilities.filters import TreeNodeMultipleChoiceFilter
from .models import Tenant, TenantGroup
@ -13,7 +14,7 @@ __all__ = (
)
class TenantGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFilterSet):
class TenantGroupFilterSet(OrganizationalModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
label='Tenant group (ID)',
@ -30,7 +31,7 @@ class TenantGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdate
fields = ['id', 'name', 'slug', 'description']
class TenantFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class TenantFilterSet(PrimaryModelFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',

View File

@ -1,10 +1,11 @@
from django.test import TestCase
from tenancy.filters import *
from tenancy.filtersets import *
from tenancy.models import Tenant, TenantGroup
from utilities.testing import ChangeLoggedFilterSetTests
class TenantGroupTestCase(TestCase):
class TenantGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = TenantGroup.objects.all()
filterset = TenantGroupFilterSet
@ -27,10 +28,6 @@ class TenantGroupTestCase(TestCase):
for tenantgroup in tenant_groups:
tenantgroup.save()
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -51,7 +48,7 @@ class TenantGroupTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class TenantTestCase(TestCase):
class TenantTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Tenant.objects.all()
filterset = TenantFilterSet
@ -73,10 +70,6 @@ class TenantTestCase(TestCase):
)
Tenant.objects.bulk_create(tenants)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Tenant 1', 'Tenant 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)

View File

@ -4,7 +4,7 @@ from ipam.models import IPAddress, Prefix, VLAN, VRF
from netbox.views import generic
from utilities.tables import paginate_table
from virtualization.models import VirtualMachine, Cluster
from . import filters, forms, tables
from . import filtersets, forms, tables
from .models import Tenant, TenantGroup
@ -63,7 +63,7 @@ class TenantGroupBulkEditView(generic.BulkEditView):
'tenant_count',
cumulative=True
)
filterset = filters.TenantGroupFilterSet
filterset = filtersets.TenantGroupFilterSet
table = tables.TenantGroupTable
form = forms.TenantGroupBulkEditForm
@ -85,7 +85,7 @@ class TenantGroupBulkDeleteView(generic.BulkDeleteView):
class TenantListView(generic.ObjectListView):
queryset = Tenant.objects.all()
filterset = filters.TenantFilterSet
filterset = filtersets.TenantFilterSet
filterset_form = forms.TenantFilterForm
table = tables.TenantTable
@ -130,12 +130,12 @@ class TenantBulkImportView(generic.BulkImportView):
class TenantBulkEditView(generic.BulkEditView):
queryset = Tenant.objects.prefetch_related('group')
filterset = filters.TenantFilterSet
filterset = filtersets.TenantFilterSet
table = tables.TenantTable
form = forms.TenantBulkEditForm
class TenantBulkDeleteView(generic.BulkDeleteView):
queryset = Tenant.objects.prefetch_related('group')
filterset = filters.TenantFilterSet
filterset = filtersets.TenantFilterSet
table = tables.TenantTable

View File

@ -6,7 +6,7 @@ from rest_framework.routers import APIRootView
from rest_framework.viewsets import ViewSet
from netbox.api.views import ModelViewSet
from users import filters
from users import filtersets
from users.models import ObjectPermission, UserConfig
from utilities.querysets import RestrictedQuerySet
from utilities.utils import deepmerge
@ -28,13 +28,13 @@ class UsersRootView(APIRootView):
class UserViewSet(ModelViewSet):
queryset = RestrictedQuerySet(model=User).prefetch_related('groups').order_by('username')
serializer_class = serializers.UserSerializer
filterset_class = filters.UserFilterSet
filterset_class = filtersets.UserFilterSet
class GroupViewSet(ModelViewSet):
queryset = RestrictedQuerySet(model=Group).annotate(user_count=Count('user')).order_by('name')
serializer_class = serializers.GroupSerializer
filterset_class = filters.GroupFilterSet
filterset_class = filtersets.GroupFilterSet
#
@ -44,7 +44,7 @@ class GroupViewSet(ModelViewSet):
class ObjectPermissionViewSet(ModelViewSet):
queryset = ObjectPermission.objects.prefetch_related('object_types', 'groups', 'users')
serializer_class = serializers.ObjectPermissionSerializer
filterset_class = filters.ObjectPermissionFilterSet
filterset_class = filtersets.ObjectPermissionFilterSet
#

View File

@ -2,8 +2,8 @@ import django_filters
from django.contrib.auth.models import Group, User
from django.db.models import Q
from netbox.filtersets import BaseFilterSet
from users.models import ObjectPermission
from utilities.filters import BaseFilterSet
__all__ = (
'GroupFilterSet',

View File

@ -2,11 +2,12 @@ from django.contrib.auth.models import Group, User
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from users.filters import GroupFilterSet, ObjectPermissionFilterSet, UserFilterSet
from users.filtersets import GroupFilterSet, ObjectPermissionFilterSet, UserFilterSet
from users.models import ObjectPermission
from utilities.testing import BaseFilterSetTests
class UserTestCase(TestCase):
class UserTestCase(TestCase, BaseFilterSetTests):
queryset = User.objects.all()
filterset = UserFilterSet
@ -59,10 +60,6 @@ class UserTestCase(TestCase):
users[1].groups.set([groups[1]])
users[2].groups.set([groups[2]])
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_username(self):
params = {'username': ['User1', 'User2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -95,7 +92,7 @@ class UserTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class GroupTestCase(TestCase):
class GroupTestCase(TestCase, BaseFilterSetTests):
queryset = Group.objects.all()
filterset = GroupFilterSet
@ -109,16 +106,12 @@ class GroupTestCase(TestCase):
)
Group.objects.bulk_create(groups)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Group 1', 'Group 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class ObjectPermissionTestCase(TestCase):
class ObjectPermissionTestCase(TestCase, BaseFilterSetTests):
queryset = ObjectPermission.objects.all()
filterset = ObjectPermissionFilterSet
@ -160,10 +153,6 @@ class ObjectPermissionTestCase(TestCase):
permissions[i].users.set([users[i]])
permissions[i].object_types.set([object_types[i]])
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Permission 1', 'Permission 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)

View File

@ -1,17 +1,9 @@
import django_filters
from django_filters.constants import EMPTY_VALUES
from copy import deepcopy
from dcim.forms import MACAddressField
from django import forms
from django.conf import settings
from django.db import models
from django_filters.utils import get_model_field, resolve_field
from django_filters.constants import EMPTY_VALUES
from extras.models import Tag
from utilities.constants import (
FILTER_CHAR_BASED_LOOKUP_MAP, FILTER_NEGATION_LOOKUP_MAP, FILTER_TREENODE_NEGATION_LOOKUP_MAP,
FILTER_NUMERIC_BASED_LOOKUP_MAP
)
from dcim.forms import MACAddressField
def multivalue_field_factory(field_class):
@ -91,21 +83,6 @@ class NullableCharFieldFilter(django_filters.CharFilter):
return qs.distinct() if self.distinct else qs
class TagFilter(django_filters.ModelMultipleChoiceFilter):
"""
Match on one or more assigned tags. If multiple tags are specified (e.g. ?tag=foo&tag=bar), the queryset is filtered
to objects matching all tags.
"""
def __init__(self, *args, **kwargs):
kwargs.setdefault('field_name', 'tags__slug')
kwargs.setdefault('to_field_name', 'slug')
kwargs.setdefault('conjoined', True)
kwargs.setdefault('queryset', Tag.objects.all())
super().__init__(*args, **kwargs)
class NumericArrayFilter(django_filters.NumberFilter):
"""
Filter based on the presence of an integer within an ArrayField.
@ -134,182 +111,3 @@ class ContentTypeFilter(django_filters.CharFilter):
f'{self.field_name}__model': model
}
)
#
# FilterSets
#
class BaseFilterSet(django_filters.FilterSet):
"""
A base filterset which provides common functionaly to all NetBox filtersets
"""
FILTER_DEFAULTS = deepcopy(django_filters.filterset.FILTER_FOR_DBFIELD_DEFAULTS)
FILTER_DEFAULTS.update({
models.AutoField: {
'filter_class': MultiValueNumberFilter
},
models.CharField: {
'filter_class': MultiValueCharFilter
},
models.DateField: {
'filter_class': MultiValueDateFilter
},
models.DateTimeField: {
'filter_class': MultiValueDateTimeFilter
},
models.DecimalField: {
'filter_class': MultiValueNumberFilter
},
models.EmailField: {
'filter_class': MultiValueCharFilter
},
models.FloatField: {
'filter_class': MultiValueNumberFilter
},
models.IntegerField: {
'filter_class': MultiValueNumberFilter
},
models.PositiveIntegerField: {
'filter_class': MultiValueNumberFilter
},
models.PositiveSmallIntegerField: {
'filter_class': MultiValueNumberFilter
},
models.SlugField: {
'filter_class': MultiValueCharFilter
},
models.SmallIntegerField: {
'filter_class': MultiValueNumberFilter
},
models.TimeField: {
'filter_class': MultiValueTimeFilter
},
models.URLField: {
'filter_class': MultiValueCharFilter
},
MACAddressField: {
'filter_class': MultiValueMACAddressFilter
},
})
@staticmethod
def _get_filter_lookup_dict(existing_filter):
# Choose the lookup expression map based on the filter type
if isinstance(existing_filter, (
MultiValueDateFilter,
MultiValueDateTimeFilter,
MultiValueNumberFilter,
MultiValueTimeFilter
)):
lookup_map = FILTER_NUMERIC_BASED_LOOKUP_MAP
elif isinstance(existing_filter, (
TreeNodeMultipleChoiceFilter,
)):
# TreeNodeMultipleChoiceFilter only support negation but must maintain the `in` lookup expression
lookup_map = FILTER_TREENODE_NEGATION_LOOKUP_MAP
elif isinstance(existing_filter, (
django_filters.ModelChoiceFilter,
django_filters.ModelMultipleChoiceFilter,
TagFilter
)) or existing_filter.extra.get('choices'):
# These filter types support only negation
lookup_map = FILTER_NEGATION_LOOKUP_MAP
elif isinstance(existing_filter, (
django_filters.filters.CharFilter,
django_filters.MultipleChoiceFilter,
MultiValueCharFilter,
MultiValueMACAddressFilter
)):
lookup_map = FILTER_CHAR_BASED_LOOKUP_MAP
else:
lookup_map = None
return lookup_map
@classmethod
def get_filters(cls):
"""
Override filter generation to support dynamic lookup expressions for certain filter types.
For specific filter types, new filters are created based on defined lookup expressions in
the form `<field_name>__<lookup_expr>`
"""
filters = super().get_filters()
new_filters = {}
for existing_filter_name, existing_filter in filters.items():
# Loop over existing filters to extract metadata by which to create new filters
# If the filter makes use of a custom filter method or lookup expression skip it
# as we cannot sanely handle these cases in a generic mannor
if existing_filter.method is not None or existing_filter.lookup_expr not in ['exact', 'in']:
continue
# Choose the lookup expression map based on the filter type
lookup_map = cls._get_filter_lookup_dict(existing_filter)
if lookup_map is None:
# Do not augment this filter type with more lookup expressions
continue
# Get properties of the existing filter for later use
field_name = existing_filter.field_name
field = get_model_field(cls._meta.model, field_name)
# Create new filters for each lookup expression in the map
for lookup_name, lookup_expr in lookup_map.items():
new_filter_name = '{}__{}'.format(existing_filter_name, lookup_name)
try:
if existing_filter_name in cls.declared_filters:
# The filter field has been explicity defined on the filterset class so we must manually
# create the new filter with the same type because there is no guarantee the defined type
# is the same as the default type for the field
resolve_field(field, lookup_expr) # Will raise FieldLookupError if the lookup is invalid
new_filter = type(existing_filter)(
field_name=field_name,
lookup_expr=lookup_expr,
label=existing_filter.label,
exclude=existing_filter.exclude,
distinct=existing_filter.distinct,
**existing_filter.extra
)
else:
# The filter field is listed in Meta.fields so we can safely rely on default behaviour
# Will raise FieldLookupError if the lookup is invalid
new_filter = cls.filter_for_field(field, field_name, lookup_expr)
except django_filters.exceptions.FieldLookupError:
# The filter could not be created because the lookup expression is not supported on the field
continue
if lookup_name.startswith('n'):
# This is a negation filter which requires a queryset.exclude() clause
# Of course setting the negation of the existing filter's exclude attribute handles both cases
new_filter.exclude = not existing_filter.exclude
new_filters[new_filter_name] = new_filter
filters.update(new_filters)
return filters
class NameSlugSearchFilterSet(django_filters.FilterSet):
"""
A base class for adding the search method to models which only expose the `name` and `slug` fields
"""
q = django_filters.CharFilter(
method='search',
label='Search',
)
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
models.Q(name__icontains=value) |
models.Q(slug__icontains=value)
)

View File

@ -1,4 +1,5 @@
from .api import *
from .base import *
from .filtersets import *
from .utils import *
from .views import *

View File

@ -0,0 +1,35 @@
from datetime import date, datetime, timezone
__all__ = (
'BaseFilterSetTests',
'ChangeLoggedFilterSetTests',
)
class BaseFilterSetTests:
queryset = None
filterset = None
def test_id(self):
"""
Test filtering for two PKs from a set of >2 objects.
"""
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertGreater(self.queryset.count(), 2)
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class ChangeLoggedFilterSetTests(BaseFilterSetTests):
def test_created(self):
pk_list = self.queryset.values_list('pk', flat=True)[:2]
self.queryset.filter(pk__in=pk_list).update(created=date(2021, 1, 1))
params = {'created': '2021-01-01'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_last_updated(self):
pk_list = self.queryset.values_list('pk', flat=True)[:2]
self.queryset.filter(pk__in=pk_list).update(last_updated=datetime(2021, 1, 1, 0, 0, 0, tzinfo=timezone.utc))
params = {'last_updated': '2021-01-01T00:00:00'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)

View File

@ -7,14 +7,16 @@ from taggit.managers import TaggableManager
from dcim.choices import *
from dcim.fields import MACAddressField
from dcim.filters import DeviceFilterSet, SiteFilterSet
from dcim.filtersets import DeviceFilterSet, SiteFilterSet
from dcim.models import (
Device, DeviceRole, DeviceType, Interface, Manufacturer, Platform, Rack, Region, Site
)
from extras.filters import TagFilter
from extras.models import TaggedItem
from netbox.filtersets import BaseFilterSet
from utilities.filters import (
BaseFilterSet, MACAddressFilter, MultiValueCharFilter, MultiValueDateFilter, MultiValueDateTimeFilter,
MultiValueNumberFilter, MultiValueTimeFilter, TagFilter, TreeNodeMultipleChoiceFilter,
MACAddressFilter, MultiValueCharFilter, MultiValueDateFilter, MultiValueDateTimeFilter, MultiValueNumberFilter,
MultiValueTimeFilter, TreeNodeMultipleChoiceFilter,
)

View File

@ -3,7 +3,7 @@ from rest_framework.routers import APIRootView
from dcim.models import Device
from extras.api.views import ConfigContextQuerySetMixin, CustomFieldModelViewSet, ModelViewSet
from utilities.utils import count_related
from virtualization import filters
from virtualization import filtersets
from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
from . import serializers
@ -25,7 +25,7 @@ class ClusterTypeViewSet(CustomFieldModelViewSet):
cluster_count=count_related(Cluster, 'type')
)
serializer_class = serializers.ClusterTypeSerializer
filterset_class = filters.ClusterTypeFilterSet
filterset_class = filtersets.ClusterTypeFilterSet
class ClusterGroupViewSet(CustomFieldModelViewSet):
@ -33,7 +33,7 @@ class ClusterGroupViewSet(CustomFieldModelViewSet):
cluster_count=count_related(Cluster, 'group')
)
serializer_class = serializers.ClusterGroupSerializer
filterset_class = filters.ClusterGroupFilterSet
filterset_class = filtersets.ClusterGroupFilterSet
class ClusterViewSet(CustomFieldModelViewSet):
@ -44,7 +44,7 @@ class ClusterViewSet(CustomFieldModelViewSet):
virtualmachine_count=count_related(VirtualMachine, 'cluster')
)
serializer_class = serializers.ClusterSerializer
filterset_class = filters.ClusterFilterSet
filterset_class = filtersets.ClusterFilterSet
#
@ -55,7 +55,7 @@ class VirtualMachineViewSet(ConfigContextQuerySetMixin, CustomFieldModelViewSet)
queryset = VirtualMachine.objects.prefetch_related(
'cluster__site', 'role', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'tags'
)
filterset_class = filters.VirtualMachineFilterSet
filterset_class = filtersets.VirtualMachineFilterSet
def get_serializer_class(self):
"""
@ -83,5 +83,5 @@ class VMInterfaceViewSet(ModelViewSet):
'virtual_machine', 'parent', 'tags', 'tagged_vlans', 'ip_addresses'
)
serializer_class = serializers.VMInterfaceSerializer
filterset_class = filters.VMInterfaceFilterSet
filterset_class = filtersets.VMInterfaceFilterSet
brief_prefetch_fields = ['virtual_machine']

View File

@ -2,12 +2,11 @@ import django_filters
from django.db.models import Q
from dcim.models import DeviceRole, Platform, Region, Site, SiteGroup
from extras.filters import CustomFieldModelFilterSet, CreatedUpdatedFilterSet, LocalConfigContextFilterSet
from tenancy.filters import TenancyFilterSet
from utilities.filters import (
BaseFilterSet, MultiValueMACAddressFilter, NameSlugSearchFilterSet, TagFilter,
TreeNodeMultipleChoiceFilter,
)
from extras.filters import TagFilter
from extras.filtersets import LocalConfigContextFilterSet
from netbox.filtersets import OrganizationalModelFilterSet, PrimaryModelFilterSet
from tenancy.filtersets import TenancyFilterSet
from utilities.filters import MultiValueMACAddressFilter, TreeNodeMultipleChoiceFilter
from .choices import *
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
@ -20,21 +19,21 @@ __all__ = (
)
class ClusterTypeFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFilterSet):
class ClusterTypeFilterSet(OrganizationalModelFilterSet):
class Meta:
model = ClusterType
fields = ['id', 'name', 'slug', 'description']
class ClusterGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet, CreatedUpdatedFilterSet):
class ClusterGroupFilterSet(OrganizationalModelFilterSet):
class Meta:
model = ClusterGroup
fields = ['id', 'name', 'slug', 'description']
class ClusterFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class ClusterFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -110,13 +109,7 @@ class ClusterFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSe
)
class VirtualMachineFilterSet(
BaseFilterSet,
LocalConfigContextFilterSet,
TenancyFilterSet,
CustomFieldModelFilterSet,
CreatedUpdatedFilterSet
):
class VirtualMachineFilterSet(PrimaryModelFilterSet, TenancyFilterSet, LocalConfigContextFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
@ -237,7 +230,7 @@ class VirtualMachineFilterSet(
return queryset.exclude(params)
class VMInterfaceFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
class VMInterfaceFilterSet(PrimaryModelFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',

View File

@ -646,7 +646,7 @@ class VMInterfaceForm(BootstrapMixin, InterfaceCommonForm, CustomFieldModelForm)
vm_id = self.initial.get('virtual_machine') or self.data.get('virtual_machine')
# Restrict parent interface assignment by VM
self.fields['parent'].widget.add_query_param('virtualmachine_id', vm_id)
self.fields['parent'].widget.add_query_param('virtual_machine_id', vm_id)
# Limit VLAN choices by virtual machine
self.fields['untagged_vlan'].widget.add_query_param('available_on_virtualmachine', vm_id)
@ -668,7 +668,7 @@ class VMInterfaceCreateForm(BootstrapMixin, InterfaceCommonForm):
queryset=VMInterface.objects.all(),
required=False,
query_params={
'virtualmachine_id': 'virtual_machine',
'virtual_machine_id': '$virtual_machine',
}
)
mtu = forms.IntegerField(
@ -711,9 +711,6 @@ class VMInterfaceCreateForm(BootstrapMixin, InterfaceCommonForm):
super().__init__(*args, **kwargs)
vm_id = self.initial.get('virtual_machine') or self.data.get('virtual_machine')
# Restrict parent interface assignment by VM
self.fields['parent'].widget.add_query_param('virtualmachine_id', vm_id)
# Limit VLAN choices by virtual machine
self.fields['untagged_vlan'].widget.add_query_param('available_on_virtualmachine', vm_id)
self.fields['tagged_vlans'].widget.add_query_param('available_on_virtualmachine', vm_id)
@ -796,7 +793,7 @@ class VMInterfaceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
vm_id = self.initial.get('virtual_machine')
# Restrict parent interface assignment by VM
self.fields['parent'].widget.add_query_param('virtualmachine_id', vm_id)
self.fields['parent'].widget.add_query_param('virtual_machine_id', vm_id)
# Limit VLAN choices by virtual machine
self.fields['untagged_vlan'].widget.add_query_param('available_on_virtualmachine', vm_id)

View File

@ -3,12 +3,13 @@ from django.test import TestCase
from dcim.models import DeviceRole, Platform, Region, Site, SiteGroup
from ipam.models import IPAddress
from tenancy.models import Tenant, TenantGroup
from utilities.testing import ChangeLoggedFilterSetTests
from virtualization.choices import *
from virtualization.filters import *
from virtualization.filtersets import *
from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
class ClusterTypeTestCase(TestCase):
class ClusterTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ClusterType.objects.all()
filterset = ClusterTypeFilterSet
@ -22,10 +23,6 @@ class ClusterTypeTestCase(TestCase):
)
ClusterType.objects.bulk_create(cluster_types)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -39,7 +36,7 @@ class ClusterTypeTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class ClusterGroupTestCase(TestCase):
class ClusterGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ClusterGroup.objects.all()
filterset = ClusterGroupFilterSet
@ -53,10 +50,6 @@ class ClusterGroupTestCase(TestCase):
)
ClusterGroup.objects.bulk_create(cluster_groups)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -70,7 +63,7 @@ class ClusterGroupTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class ClusterTestCase(TestCase):
class ClusterTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = Cluster.objects.all()
filterset = ClusterFilterSet
@ -136,10 +129,6 @@ class ClusterTestCase(TestCase):
)
Cluster.objects.bulk_create(clusters)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Cluster 1', 'Cluster 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@ -194,7 +183,7 @@ class ClusterTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class VirtualMachineTestCase(TestCase):
class VirtualMachineTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = VirtualMachine.objects.all()
filterset = VirtualMachineFilterSet
@ -297,10 +286,6 @@ class VirtualMachineTestCase(TestCase):
VirtualMachine.objects.filter(pk=vms[0].pk).update(primary_ip4=ipaddresses[0])
VirtualMachine.objects.filter(pk=vms[1].pk).update(primary_ip4=ipaddresses[1])
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
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)
@ -409,7 +394,7 @@ class VirtualMachineTestCase(TestCase):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class VMInterfaceTestCase(TestCase):
class VMInterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = VMInterface.objects.all()
filterset = VMInterfaceFilterSet
@ -444,11 +429,6 @@ class VMInterfaceTestCase(TestCase):
)
VMInterface.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)

View File

@ -13,7 +13,7 @@ from netbox.views import generic
from secrets.models import Secret
from utilities.tables import paginate_table
from utilities.utils import count_related
from . import filters, forms, tables
from . import filtersets, forms, tables
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
@ -64,7 +64,7 @@ class ClusterTypeBulkEditView(generic.BulkEditView):
queryset = ClusterType.objects.annotate(
cluster_count=count_related(Cluster, 'type')
)
filterset = filters.ClusterTypeFilterSet
filterset = filtersets.ClusterTypeFilterSet
table = tables.ClusterTypeTable
form = forms.ClusterTypeBulkEditForm
@ -125,7 +125,7 @@ class ClusterGroupBulkEditView(generic.BulkEditView):
queryset = ClusterGroup.objects.annotate(
cluster_count=count_related(Cluster, 'group')
)
filterset = filters.ClusterGroupFilterSet
filterset = filtersets.ClusterGroupFilterSet
table = tables.ClusterGroupTable
form = forms.ClusterGroupBulkEditForm
@ -148,7 +148,7 @@ class ClusterListView(generic.ObjectListView):
vm_count=count_related(VirtualMachine, 'cluster')
)
table = tables.ClusterTable
filterset = filters.ClusterFilterSet
filterset = filtersets.ClusterFilterSet
filterset_form = forms.ClusterFilterForm
@ -205,14 +205,14 @@ class ClusterBulkImportView(generic.BulkImportView):
class ClusterBulkEditView(generic.BulkEditView):
queryset = Cluster.objects.prefetch_related('type', 'group', 'site')
filterset = filters.ClusterFilterSet
filterset = filtersets.ClusterFilterSet
table = tables.ClusterTable
form = forms.ClusterBulkEditForm
class ClusterBulkDeleteView(generic.BulkDeleteView):
queryset = Cluster.objects.prefetch_related('type', 'group', 'site')
filterset = filters.ClusterFilterSet
filterset = filtersets.ClusterFilterSet
table = tables.ClusterTable
@ -304,7 +304,7 @@ class ClusterRemoveDevicesView(generic.ObjectEditView):
class VirtualMachineListView(generic.ObjectListView):
queryset = VirtualMachine.objects.all()
filterset = filters.VirtualMachineFilterSet
filterset = filtersets.VirtualMachineFilterSet
filterset_form = forms.VirtualMachineFilterForm
table = tables.VirtualMachineDetailTable
template_name = 'virtualization/virtualmachine_list.html'
@ -388,14 +388,14 @@ class VirtualMachineBulkImportView(generic.BulkImportView):
class VirtualMachineBulkEditView(generic.BulkEditView):
queryset = VirtualMachine.objects.prefetch_related('cluster', 'tenant', 'role')
filterset = filters.VirtualMachineFilterSet
filterset = filtersets.VirtualMachineFilterSet
table = tables.VirtualMachineTable
form = forms.VirtualMachineBulkEditForm
class VirtualMachineBulkDeleteView(generic.BulkDeleteView):
queryset = VirtualMachine.objects.prefetch_related('cluster', 'tenant', 'role')
filterset = filters.VirtualMachineFilterSet
filterset = filtersets.VirtualMachineFilterSet
table = tables.VirtualMachineTable
@ -405,7 +405,7 @@ class VirtualMachineBulkDeleteView(generic.BulkDeleteView):
class VMInterfaceListView(generic.ObjectListView):
queryset = VMInterface.objects.all()
filterset = filters.VMInterfaceFilterSet
filterset = filtersets.VMInterfaceFilterSet
filterset_form = forms.VMInterfaceFilterForm
table = tables.VMInterfaceTable
action_buttons = ('export',)
@ -500,7 +500,7 @@ class VirtualMachineBulkAddInterfaceView(generic.BulkComponentCreateView):
form = forms.VMInterfaceBulkCreateForm
queryset = VMInterface.objects.all()
model_form = forms.VMInterfaceForm
filterset = filters.VirtualMachineFilterSet
filterset = filtersets.VirtualMachineFilterSet
table = tables.VirtualMachineTable
def get_required_permission(self):