Transition ObjectListView to use ObjectPermissionRequiredMixin

This commit is contained in:
Jeremy Stretch 2020-05-21 13:22:09 -04:00
parent cc6e74dfd5
commit 993ee8c900
8 changed files with 69 additions and 112 deletions

View File

@ -23,8 +23,7 @@ from .models import Circuit, CircuitTermination, CircuitType, Provider
# Providers
#
class ProviderListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'circuits.view_provider'
class ProviderListView(ObjectListView):
queryset = Provider.objects.annotate(count_circuits=Count('circuits'))
filterset = filters.ProviderFilterSet
filterset_form = forms.ProviderFilterForm
@ -107,8 +106,7 @@ class ProviderBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Circuit Types
#
class CircuitTypeListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'circuits.view_circuittype'
class CircuitTypeListView(ObjectListView):
queryset = CircuitType.objects.annotate(circuit_count=Count('circuits'))
table = tables.CircuitTypeTable
@ -143,8 +141,7 @@ class CircuitTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Circuits
#
class CircuitListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'circuits.view_circuit'
class CircuitListView(ObjectListView):
_terminations = CircuitTermination.objects.filter(circuit=OuterRef('pk'))
queryset = Circuit.objects.prefetch_related(
'provider', 'type', 'tenant', 'terminations__site'

View File

@ -141,8 +141,7 @@ class BulkDisconnectView(GetReturnURLMixin, View):
# Regions
#
class RegionListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_region'
class RegionListView(ObjectListView):
queryset = Region.objects.add_related_count(
Region.objects.all(),
Site,
@ -186,8 +185,7 @@ class RegionBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Sites
#
class SiteListView(ObjectPermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_site'
class SiteListView(ObjectListView):
queryset = Site.objects.prefetch_related('region', 'tenant')
filterset = filters.SiteFilterSet
filterset_form = forms.SiteFilterForm
@ -267,8 +265,7 @@ class SiteBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Rack groups
#
class RackGroupListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_rackgroup'
class RackGroupListView(ObjectListView):
queryset = RackGroup.objects.add_related_count(
RackGroup.objects.all(),
Rack,
@ -312,8 +309,7 @@ class RackGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Rack roles
#
class RackRoleListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_rackrole'
class RackRoleListView(ObjectListView):
queryset = RackRole.objects.annotate(rack_count=Count('racks'))
table = tables.RackRoleTable
@ -348,8 +344,7 @@ class RackRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Racks
#
class RackListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_rack'
class RackListView(ObjectListView):
queryset = Rack.objects.prefetch_related(
'site', 'group', 'tenant', 'role', 'devices__device_type'
).annotate(
@ -476,8 +471,7 @@ class RackBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Rack reservations
#
class RackReservationListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_rackreservation'
class RackReservationListView(ObjectListView):
queryset = RackReservation.objects.prefetch_related('rack__site')
filterset = filters.RackReservationFilterSet
filterset_form = forms.RackReservationFilterForm
@ -561,8 +555,7 @@ class RackReservationBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Manufacturers
#
class ManufacturerListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_manufacturer'
class ManufacturerListView(ObjectListView):
queryset = Manufacturer.objects.annotate(
devicetype_count=Count('device_types', distinct=True),
inventoryitem_count=Count('inventory_items', distinct=True),
@ -601,8 +594,7 @@ class ManufacturerBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Device types
#
class DeviceTypeListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_devicetype'
class DeviceTypeListView(ObjectListView):
queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(instance_count=Count('instances'))
filterset = filters.DeviceTypeFilterSet
filterset_form = forms.DeviceTypeFilterForm
@ -1026,8 +1018,7 @@ class DeviceBayTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Device roles
#
class DeviceRoleListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_devicerole'
class DeviceRoleListView(ObjectListView):
queryset = DeviceRole.objects.all()
table = tables.DeviceRoleTable
@ -1062,8 +1053,7 @@ class DeviceRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Platforms
#
class PlatformListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_platform'
class PlatformListView(ObjectListView):
queryset = Platform.objects.all()
table = tables.PlatformTable
@ -1098,8 +1088,7 @@ class PlatformBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Devices
#
class DeviceListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_device'
class DeviceListView(ObjectListView):
queryset = Device.objects.prefetch_related(
'device_type__manufacturer', 'device_role', 'tenant', 'site', 'rack', 'primary_ip4', 'primary_ip6'
)
@ -1323,8 +1312,7 @@ class DeviceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Console ports
#
class ConsolePortListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_consoleport'
class ConsolePortListView(ObjectListView):
queryset = ConsolePort.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable')
filterset = filters.ConsolePortFilterSet
filterset_form = forms.ConsolePortFilterForm
@ -1379,8 +1367,7 @@ class ConsolePortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Console server ports
#
class ConsoleServerPortListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_consoleserverport'
class ConsoleServerPortListView(ObjectListView):
queryset = ConsoleServerPort.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable')
filterset = filters.ConsoleServerPortFilterSet
filterset_form = forms.ConsoleServerPortFilterForm
@ -1447,8 +1434,7 @@ class ConsoleServerPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Power ports
#
class PowerPortListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_powerport'
class PowerPortListView(ObjectListView):
queryset = PowerPort.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable')
filterset = filters.PowerPortFilterSet
filterset_form = forms.PowerPortFilterForm
@ -1503,8 +1489,7 @@ class PowerPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Power outlets
#
class PowerOutletListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_poweroutlet'
class PowerOutletListView(ObjectListView):
queryset = PowerOutlet.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable')
filterset = filters.PowerOutletFilterSet
filterset_form = forms.PowerOutletFilterForm
@ -1571,8 +1556,7 @@ class PowerOutletBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Interfaces
#
class InterfaceListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_interface'
class InterfaceListView(ObjectListView):
queryset = Interface.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable')
filterset = filters.InterfaceFilterSet
filterset_form = forms.InterfaceFilterForm
@ -1676,8 +1660,7 @@ class InterfaceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Front ports
#
class FrontPortListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_frontport'
class FrontPortListView(ObjectListView):
queryset = FrontPort.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable')
filterset = filters.FrontPortFilterSet
filterset_form = forms.FrontPortFilterForm
@ -1744,8 +1727,7 @@ class FrontPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Rear ports
#
class RearPortListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_rearport'
class RearPortListView(ObjectListView):
queryset = RearPort.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable')
filterset = filters.RearPortFilterSet
filterset_form = forms.RearPortFilterForm
@ -1812,8 +1794,7 @@ class RearPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Device bays
#
class DeviceBayListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_devicebay'
class DeviceBayListView(ObjectListView):
queryset = DeviceBay.objects.prefetch_related(
'device', 'device__site', 'installed_device', 'installed_device__site'
)
@ -2045,8 +2026,7 @@ class DeviceBulkAddDeviceBayView(PermissionRequiredMixin, BulkComponentCreateVie
# Cables
#
class CableListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_cable'
class CableListView(ObjectListView):
queryset = Cable.objects.prefetch_related(
'termination_a', 'termination_b'
)
@ -2215,7 +2195,7 @@ class CableBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Connections
#
class ConsoleConnectionsListView(PermissionRequiredMixin, ObjectListView):
class ConsoleConnectionsListView(ObjectListView):
permission_required = ('dcim.view_consoleport', 'dcim.view_consoleserverport')
queryset = ConsolePort.objects.prefetch_related(
'device', 'connected_endpoint__device'
@ -2247,7 +2227,7 @@ class ConsoleConnectionsListView(PermissionRequiredMixin, ObjectListView):
return '\n'.join(csv_data)
class PowerConnectionsListView(PermissionRequiredMixin, ObjectListView):
class PowerConnectionsListView(ObjectListView):
permission_required = ('dcim.view_powerport', 'dcim.view_poweroutlet')
queryset = PowerPort.objects.prefetch_related(
'device', '_connected_poweroutlet__device'
@ -2279,8 +2259,7 @@ class PowerConnectionsListView(PermissionRequiredMixin, ObjectListView):
return '\n'.join(csv_data)
class InterfaceConnectionsListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_interface'
class InterfaceConnectionsListView(ObjectListView):
queryset = Interface.objects.prefetch_related(
'device', 'cable', '_connected_interface__device'
).filter(
@ -2319,8 +2298,7 @@ class InterfaceConnectionsListView(PermissionRequiredMixin, ObjectListView):
# Inventory items
#
class InventoryItemListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_inventoryitem'
class InventoryItemListView(ObjectListView):
queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer')
filterset = filters.InventoryItemFilterSet
filterset_form = forms.InventoryItemFilterForm
@ -2376,8 +2354,7 @@ class InventoryItemBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Virtual chassis
#
class VirtualChassisListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_virtualchassis'
class VirtualChassisListView(ObjectListView):
queryset = VirtualChassis.objects.prefetch_related('master').annotate(member_count=Count('members'))
table = tables.VirtualChassisTable
filterset = filters.VirtualChassisFilterSet
@ -2644,8 +2621,7 @@ class VirtualChassisBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Power panels
#
class PowerPanelListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_powerpanel'
class PowerPanelListView(ObjectListView):
queryset = PowerPanel.objects.prefetch_related(
'site', 'rack_group'
).annotate(
@ -2724,8 +2700,7 @@ class PowerPanelBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Power feeds
#
class PowerFeedListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_powerfeed'
class PowerFeedListView(ObjectListView):
queryset = PowerFeed.objects.prefetch_related(
'power_panel', 'rack'
)

View File

@ -25,8 +25,7 @@ from .tables import ConfigContextTable, ObjectChangeTable, TagTable, TaggedItemT
# Tags
#
class TagListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'extras.view_tag'
class TagListView(ObjectListView):
queryset = Tag.objects.annotate(
items=Count('extras_taggeditem_items', distinct=True)
).order_by(
@ -106,8 +105,7 @@ class TagBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Config contexts
#
class ConfigContextListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'extras.view_configcontext'
class ConfigContextListView(ObjectListView):
queryset = ConfigContext.objects.all()
filterset = filters.ConfigContextFilterSet
filterset_form = forms.ConfigContextFilterForm
@ -200,8 +198,7 @@ class ObjectConfigContextView(View):
# Change logging
#
class ObjectChangeListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'extras.view_objectchange'
class ObjectChangeListView(ObjectListView):
queryset = ObjectChange.objects.prefetch_related('user', 'changed_object_type')
filterset = filters.ObjectChangeFilterSet
filterset_form = forms.ObjectChangeFilterForm

View File

@ -113,8 +113,7 @@ def add_available_vlans(vlan_group, vlans):
# VRFs
#
class VRFListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'ipam.view_vrf'
class VRFListView(ObjectListView):
queryset = VRF.objects.prefetch_related('tenant')
filterset = filters.VRFFilterSet
filterset_form = forms.VRFFilterForm
@ -182,8 +181,7 @@ class VRFBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# RIRs
#
class RIRListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'ipam.view_rir'
class RIRListView(ObjectListView):
queryset = RIR.objects.annotate(aggregate_count=Count('aggregates'))
filterset = filters.RIRFilterSet
filterset_form = forms.RIRFilterForm
@ -290,8 +288,7 @@ class RIRBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Aggregates
#
class AggregateListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'ipam.view_aggregate'
class AggregateListView(ObjectListView):
queryset = Aggregate.objects.prefetch_related('rir').annotate(
child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
)
@ -409,8 +406,7 @@ class AggregateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Prefix/VLAN roles
#
class RoleListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'ipam.view_role'
class RoleListView(ObjectListView):
queryset = Role.objects.all()
table = tables.RoleTable
@ -445,8 +441,7 @@ class RoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Prefixes
#
class PrefixListView(ObjectPermissionRequiredMixin, ObjectListView):
permission_required = 'ipam.view_prefix'
class PrefixListView(ObjectListView):
queryset = Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
filterset = filters.PrefixFilterSet
filterset_form = forms.PrefixFilterForm
@ -638,8 +633,7 @@ class PrefixBulkDeleteView(ObjectPermissionRequiredMixin, BulkDeleteView):
# IP addresses
#
class IPAddressListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'ipam.view_ipaddress'
class IPAddressListView(ObjectListView):
queryset = IPAddress.objects.prefetch_related(
'vrf__tenant', 'tenant', 'nat_inside', 'interface__device', 'interface__virtual_machine'
)
@ -813,8 +807,7 @@ class IPAddressBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# VLAN groups
#
class VLANGroupListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'ipam.view_vlangroup'
class VLANGroupListView(ObjectListView):
queryset = VLANGroup.objects.prefetch_related('site').annotate(vlan_count=Count('vlans'))
filterset = filters.VLANGroupFilterSet
filterset_form = forms.VLANGroupFilterForm
@ -889,8 +882,7 @@ class VLANGroupVLANsView(PermissionRequiredMixin, View):
# VLANs
#
class VLANListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'ipam.view_vlan'
class VLANListView(ObjectListView):
queryset = VLAN.objects.prefetch_related('site', 'group', 'tenant', 'role').prefetch_related('prefixes')
filterset = filters.VLANFilterSet
filterset_form = forms.VLANFilterForm
@ -985,8 +977,7 @@ class VLANBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Services
#
class ServiceListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'ipam.view_service'
class ServiceListView(ObjectListView):
queryset = Service.objects.prefetch_related('device', 'virtual_machine')
filterset = filters.ServiceFilterSet
filterset_form = forms.ServiceFilterForm

View File

@ -30,8 +30,7 @@ def get_session_key(request):
# Secret roles
#
class SecretRoleListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'secrets.view_secretrole'
class SecretRoleListView(ObjectListView):
queryset = SecretRole.objects.annotate(secret_count=Count('secrets'))
table = tables.SecretRoleTable
@ -66,8 +65,7 @@ class SecretRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Secrets
#
class SecretListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'secrets.view_secret'
class SecretListView(ObjectListView):
queryset = Secret.objects.prefetch_related('role', 'device')
filterset = filters.SecretFilterSet
filterset_form = forms.SecretFilterForm

View File

@ -18,8 +18,7 @@ from .models import Tenant, TenantGroup
# Tenant groups
#
class TenantGroupListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'tenancy.view_tenantgroup'
class TenantGroupListView(ObjectListView):
queryset = TenantGroup.objects.add_related_count(
TenantGroup.objects.all(),
Tenant,
@ -60,8 +59,7 @@ class TenantGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Tenants
#
class TenantListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'tenancy.view_tenant'
class TenantListView(ObjectListView):
queryset = Tenant.objects.prefetch_related('group')
filterset = filters.TenantFilterSet
filterset_form = forms.TenantFilterForm

View File

@ -27,6 +27,7 @@ from extras.querysets import CustomFieldQueryset
from users.models import ObjectPermission
from utilities.exceptions import AbortTransaction
from utilities.forms import BootstrapMixin, CSVDataField, TableConfigForm
from utilities.permissions import get_permission_for_model
from utilities.utils import csv_format, prepare_cloned_fields
from .error_handlers import handle_protectederror
from .forms import ConfirmationForm, ImportForm
@ -45,11 +46,15 @@ class ObjectPermissionRequiredMixin(AccessMixin):
"""
permission_required = None
def get_required_permission(self):
return self.permission_required
def has_permission(self):
user = self.request.user
permission_required = self.get_required_permission()
# First, check that the user is granted the required permission at either the model or object level.
if not user.has_perm(self.permission_required):
if not user.has_perm(permission_required):
return False
# Superusers implicitly have all permissions
@ -58,23 +63,18 @@ class ObjectPermissionRequiredMixin(AccessMixin):
# Determine whether the permission is model-level or object-level. Model-level permissions grant the
# specified action to *all* objects, so no further action is needed.
if self.permission_required in {*user._user_perm_cache, *user._group_perm_cache}:
if permission_required in {*user._user_perm_cache, *user._group_perm_cache}:
return True
# If the permission is granted only at the object level, filter the view's queryset to return only objects
# on which the user is permitted to perform the specified action.
attrs = ObjectPermission.objects.get_attr_constraints(user, self.permission_required)
attrs = ObjectPermission.objects.get_attr_constraints(user, permission_required)
if attrs:
# Update the view's QuerySet to filter only the permitted objects
self.queryset = self.queryset.filter(attrs)
return True
def dispatch(self, request, *args, **kwargs):
if self.permission_required is None:
raise ImproperlyConfigured(
'{0} is missing the permission_required attribute. Define {0}.permission_required, or override '
'{0}.get_permission_required().'.format(self.__class__.__name__)
)
if not hasattr(self, 'queryset'):
raise ImproperlyConfigured(
@ -118,15 +118,15 @@ class GetReturnURLMixin(object):
# Generic views
#
class ObjectListView(View):
class ObjectListView(ObjectPermissionRequiredMixin, View):
"""
List a series of objects.
queryset: The queryset of objects to display
filter: A django-filter FilterSet that is applied to the queryset
filter_form: The form used to render filter options
table: The django-tables2 Table used to render the objects list
template_name: The name of the template
:param queryset: The queryset of objects to display
:param filter: A django-filter FilterSet that is applied to the queryset
:param filter_form: The form used to render filter options
:param table: The django-tables2 Table used to render the objects list
:param template_name: The name of the template
"""
queryset = None
filterset = None
@ -135,6 +135,11 @@ class ObjectListView(View):
template_name = 'utilities/obj_list.html'
action_buttons = ('add', 'import', 'export')
def get_required_permission(self):
if getattr(self, 'permission_required') is not None:
return self.permission_required
return get_permission_for_model(self.queryset.model, 'view')
def queryset_to_yaml(self):
"""
Export the queryset of objects as concatenated YAML documents.

View File

@ -22,8 +22,7 @@ from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
# Cluster types
#
class ClusterTypeListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'virtualization.view_clustertype'
class ClusterTypeListView(ObjectListView):
queryset = ClusterType.objects.annotate(cluster_count=Count('clusters'))
table = tables.ClusterTypeTable
@ -58,8 +57,7 @@ class ClusterTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Cluster groups
#
class ClusterGroupListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'virtualization.view_clustergroup'
class ClusterGroupListView(ObjectListView):
queryset = ClusterGroup.objects.annotate(cluster_count=Count('clusters'))
table = tables.ClusterGroupTable
@ -94,8 +92,7 @@ class ClusterGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Clusters
#
class ClusterListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'virtualization.view_cluster'
class ClusterListView(ObjectListView):
queryset = Cluster.objects.prefetch_related('type', 'group', 'site', 'tenant')
table = tables.ClusterTable
filterset = filters.ClusterFilterSet
@ -251,8 +248,7 @@ class ClusterRemoveDevicesView(PermissionRequiredMixin, View):
# Virtual machines
#
class VirtualMachineListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'virtualization.view_virtualmachine'
class VirtualMachineListView(ObjectListView):
queryset = VirtualMachine.objects.prefetch_related('cluster', 'tenant', 'role', 'primary_ip4', 'primary_ip6')
filterset = filters.VirtualMachineFilterSet
filterset_form = forms.VirtualMachineFilterForm