diff --git a/awx/api/generics.py b/awx/api/generics.py index 51598979d8..1a3e5e2910 100644 --- a/awx/api/generics.py +++ b/awx/api/generics.py @@ -235,6 +235,13 @@ class ListAPIView(generics.ListAPIView, GenericAPIView): def get_queryset(self): return self.request.user.get_queryset(self.model) + def paginate_queryset(self, queryset): + page = super(ListAPIView, self).paginate_queryset(queryset) + # Queries RBAC info & stores into list objects + if hasattr(self, 'capabilities_prefetch') and page is not None: + cache_list_capabilities(page, self.capabilities_prefetch, self.model, self.request.user) + return page + def get_description_context(self): opts = self.model._meta if 'username' in opts.get_all_field_names(): diff --git a/awx/api/views.py b/awx/api/views.py index d7a2291ec9..dc9d411086 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -963,6 +963,7 @@ class ProjectList(ListCreateAPIView): model = Project serializer_class = ProjectSerializer + capabilities_prefetch = ['admin', 'update'] def get_queryset(self): projects_qs = Project.accessible_objects(self.request.user, 'read_role') @@ -1156,6 +1157,7 @@ class UserList(ListCreateAPIView): model = User serializer_class = UserSerializer + capabilities_prefetch = ['admin'] def post(self, request, *args, **kwargs): ret = super(UserList, self).post( request, *args, **kwargs) @@ -1522,28 +1524,6 @@ class InventoryList(ListCreateAPIView): qs = qs.select_related('admin_role', 'read_role', 'update_role', 'use_role', 'adhoc_role') return qs - def list(self, request, *args, **kwargs): - if not hasattr(self, 'capabilities_prefetch'): - return super(ListCreateAPIView, self).list(request, *args, **kwargs) - queryset = self.filter_queryset(self.get_queryset()) - - page = self.paginate_queryset(queryset) - readable_ids = [obj.id for obj in page] - editable_ids = Inventory.accessible_objects(request.user, 'admin_role').filter(pk__in=readable_ids).values_list('pk', flat=True) - adhoc_ids = Inventory.accessible_objects(request.user, 'adhoc_role').filter(pk__in=readable_ids).values_list('pk', flat=True) - for obj in page: - obj.capabilities_cache = {'edit': False, 'adhoc': False} - if obj.pk in editable_ids: - obj.capabilities_cache['edit'] = True - if obj.pk in adhoc_ids: - obj.capabilities_cache['adhoc'] = True - if page is not None: - serializer = self.get_serializer(page, many=True) - return self.get_paginated_response(serializer.data) - - serializer = self.get_serializer(queryset, many=True) - return Response(serializer.data) - class InventoryDetail(RetrieveUpdateDestroyAPIView): model = Inventory @@ -2224,6 +2204,7 @@ class JobTemplateList(ListCreateAPIView): model = JobTemplate serializer_class = JobTemplateSerializer always_allow_superuser = False + capabilities_prefetch = ['admin', 'execute'] def post(self, request, *args, **kwargs): ret = super(JobTemplateList, self).post(request, *args, **kwargs) diff --git a/awx/main/utils.py b/awx/main/utils.py index 63235ffca3..f65698f052 100644 --- a/awx/main/utils.py +++ b/awx/main/utils.py @@ -33,7 +33,7 @@ logger = logging.getLogger('awx.main.utils') __all__ = ['get_object_or_400', 'get_object_or_403', 'camelcase_to_underscore', 'memoize', 'get_ansible_version', 'get_ssh_version', 'get_awx_version', 'update_scm_url', - 'get_type_for_model', 'get_model_for_type', 'to_python_boolean', + 'get_type_for_model', 'get_model_for_type', 'cache_list_capabilities', 'to_python_boolean', 'ignore_inventory_computed_fields', 'ignore_inventory_group_removal', '_inventory_updates', 'get_pk_from_dict', 'getattrd', 'NoDefaultProvided', 'get_current_apps', 'set_current_apps'] @@ -409,6 +409,31 @@ def get_model_for_type(type): return ct_model +def cache_list_capabilities(page, role_types, model, user): + ''' + Given a `page` list of objects, the specified roles for the specified user + are save on each object in the list, using 1 query for each role type + ''' + page_ids = [obj.id for obj in page] + id_lists = {} + for role_type in role_types: + # Role name translation to UI names for methods + display_method = role_type + if role_type == 'admin': + display_method = 'edit' + elif role_type in ['execute', 'update']: + display_method = 'start' + # Query for union of page objects & role accessible_objects + id_lists[display_method] = model.accessible_objects( + user, '%s_role' % role_type).filter(pk__in=page_ids).values_list('pk', flat=True) + # Save data item-by-item + for obj in page: + obj.capabilities_cache = {display_method: False for display_method in id_lists.keys()} + for display_method, id_list in id_lists.iteritems(): + if obj.pk in id_list: + obj.capabilities_cache[display_method] = True + + def get_system_task_capacity(): ''' Measure system memory and use it as a baseline for determining the system's capacity