From 65c48fc3e85680e9d51549830915fcd6ec6db8c5 Mon Sep 17 00:00:00 2001 From: Zhenguo Niu Date: Fri, 3 May 2013 14:30:25 +0800 Subject: [PATCH] Adding pagination to the tenant views Modifying the api.keystone.tenants_list() method by setting limit/marker parameters to handle pagination. Change-Id: Ice67a6112ed8b90095e9c9991bee3e715c22ec01 Fixes: bug #904003 --- openstack_dashboard/api/keystone.py | 16 ++++++++-- .../dashboards/admin/instances/tests.py | 10 +++---- .../dashboards/admin/instances/views.py | 2 +- .../dashboards/admin/networks/forms.py | 3 +- .../dashboards/admin/networks/tests.py | 12 ++++---- .../dashboards/admin/networks/views.py | 2 +- .../dashboards/admin/overview/tests.py | 4 +-- .../dashboards/admin/overview/views.py | 2 +- .../dashboards/admin/projects/tables.py | 1 + .../dashboards/admin/projects/tests.py | 6 ++-- .../dashboards/admin/projects/views.py | 12 ++++++-- .../dashboards/admin/routers/tests.py | 3 +- .../dashboards/admin/users/forms.py | 3 +- .../dashboards/admin/users/tests.py | 30 ++++++++++++------- .../dashboards/admin/volumes/tests.py | 4 +-- .../dashboards/admin/volumes/views.py | 2 +- 16 files changed, 72 insertions(+), 40 deletions(-) diff --git a/openstack_dashboard/api/keystone.py b/openstack_dashboard/api/keystone.py index 1dbf03d59..fefc26be1 100644 --- a/openstack_dashboard/api/keystone.py +++ b/openstack_dashboard/api/keystone.py @@ -221,12 +221,22 @@ def tenant_delete(request, project): return manager.delete(project) -def tenant_list(request, domain=None, user=None): +def tenant_list(request, paginate=False, marker=None, domain=None, user=None): manager = VERSIONS.get_project_manager(request, admin=True) + page_size = getattr(settings, 'API_RESULT_PAGE_SIZE', 20) + limit = None + if paginate: + limit = page_size + 1 + + has_more_data = False if VERSIONS.active < 3: - return manager.list() + tenants = manager.list(limit, marker) + if paginate and len(tenants) > page_size: + tenants.pop(-1) + has_more_data = True else: - return manager.list(domain=domain, user=user) + tenants = manager.list(domain=domain, user=user) + return (tenants, has_more_data) def tenant_update(request, project, name=None, description=None, diff --git a/openstack_dashboard/dashboards/admin/instances/tests.py b/openstack_dashboard/dashboards/admin/instances/tests.py index 5b39f4a03..cdb83c206 100644 --- a/openstack_dashboard/dashboards/admin/instances/tests.py +++ b/openstack_dashboard/dashboards/admin/instances/tests.py @@ -34,7 +34,7 @@ class InstanceViewTest(test.BaseAdminViewTests): flavors = self.flavors.list() tenants = self.tenants.list() api.keystone.tenant_list(IsA(http.HttpRequest)).\ - AndReturn(tenants) + AndReturn([tenants, False]) search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), all_tenants=True, search_opts=search_opts) \ @@ -63,7 +63,7 @@ class InstanceViewTest(test.BaseAdminViewTests): api.nova.flavor_list(IsA(http.HttpRequest)). \ AndRaise(self.exceptions.nova) api.keystone.tenant_list(IsA(http.HttpRequest)).\ - AndReturn(tenants) + AndReturn([tenants, False]) for server in servers: api.nova.flavor_get(IsA(http.HttpRequest), server.flavor["id"]). \ AndReturn(full_flavors[server.flavor["id"]]) @@ -94,7 +94,7 @@ class InstanceViewTest(test.BaseAdminViewTests): api.nova.flavor_list(IsA(http.HttpRequest)). \ AndReturn(flavors) api.keystone.tenant_list(IsA(http.HttpRequest)).\ - AndReturn(tenants) + AndReturn([tenants, False]) for server in servers: api.nova.flavor_get(IsA(http.HttpRequest), server.flavor["id"]). \ AndRaise(self.exceptions.nova) @@ -154,7 +154,7 @@ class InstanceViewTest(test.BaseAdminViewTests): api.keystone: ('tenant_list',)}) def test_index_options_before_migrate(self): api.keystone.tenant_list(IsA(http.HttpRequest)).\ - AndReturn(self.tenants.list()) + AndReturn([self.tenants.list(), False]) search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), all_tenants=True, search_opts=search_opts) \ @@ -174,7 +174,7 @@ class InstanceViewTest(test.BaseAdminViewTests): server = self.servers.first() server.status = "VERIFY_RESIZE" api.keystone.tenant_list(IsA(http.HttpRequest)) \ - .AndReturn(self.tenants.list()) + .AndReturn([self.tenants.list(), False]) search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), all_tenants=True, search_opts=search_opts) \ diff --git a/openstack_dashboard/dashboards/admin/instances/views.py b/openstack_dashboard/dashboards/admin/instances/views.py index e8cf27ac9..858d533e8 100644 --- a/openstack_dashboard/dashboards/admin/instances/views.py +++ b/openstack_dashboard/dashboards/admin/instances/views.py @@ -73,7 +73,7 @@ class AdminIndexView(tables.DataTableView): # Gather our tenants to correlate against IDs try: - tenants = api.keystone.tenant_list(self.request) + tenants, has_more = api.keystone.tenant_list(self.request) except: tenants = [] msg = _('Unable to retrieve instance tenant information.') diff --git a/openstack_dashboard/dashboards/admin/networks/forms.py b/openstack_dashboard/dashboards/admin/networks/forms.py index 6938beb49..3f0a7e5e4 100644 --- a/openstack_dashboard/dashboards/admin/networks/forms.py +++ b/openstack_dashboard/dashboards/admin/networks/forms.py @@ -48,7 +48,8 @@ class CreateNetwork(forms.SelfHandlingForm): def __init__(self, request, *args, **kwargs): super(CreateNetwork, self).__init__(request, *args, **kwargs) tenant_choices = [('', _("Select a project"))] - for tenant in api.keystone.tenant_list(request): + tenants, has_more = api.keystone.tenant_list(request) + for tenant in tenants: if tenant.enabled: tenant_choices.append((tenant.id, tenant.name)) self.fields['tenant_id'].choices = tenant_choices diff --git a/openstack_dashboard/dashboards/admin/networks/tests.py b/openstack_dashboard/dashboards/admin/networks/tests.py index 9f9e28db8..d6c012308 100644 --- a/openstack_dashboard/dashboards/admin/networks/tests.py +++ b/openstack_dashboard/dashboards/admin/networks/tests.py @@ -37,7 +37,7 @@ class NetworkTests(test.BaseAdminViewTests): api.quantum.network_list(IsA(http.HttpRequest)) \ .AndReturn(self.networks.list()) api.keystone.tenant_list(IsA(http.HttpRequest))\ - .AndReturn(tenants) + .AndReturn([tenants, False]) self.mox.ReplayAll() @@ -153,7 +153,7 @@ class NetworkTests(test.BaseAdminViewTests): def test_network_create_get(self): tenants = self.tenants.list() api.keystone.tenant_list(IsA(http.HttpRequest))\ - .AndReturn(tenants) + .AndReturn([tenants, False]) self.mox.ReplayAll() url = reverse('horizon:admin:networks:create') @@ -168,7 +168,7 @@ class NetworkTests(test.BaseAdminViewTests): tenant_id = self.tenants.first().id network = self.networks.first() api.keystone.tenant_list(IsA(http.HttpRequest))\ - .AndReturn(tenants) + .AndReturn([tenants, False]) params = {'name': network.name, 'tenant_id': tenant_id, 'admin_state_up': network.admin_state_up, @@ -196,7 +196,7 @@ class NetworkTests(test.BaseAdminViewTests): tenant_id = self.tenants.first().id network = self.networks.first() api.keystone.tenant_list(IsA(http.HttpRequest))\ - .AndReturn(tenants) + .AndReturn([tenants, False]) params = {'name': network.name, 'tenant_id': tenant_id, 'admin_state_up': network.admin_state_up, @@ -303,7 +303,7 @@ class NetworkTests(test.BaseAdminViewTests): tenants = self.tenants.list() network = self.networks.first() api.keystone.tenant_list(IsA(http.HttpRequest))\ - .AndReturn(tenants) + .AndReturn([tenants, False]) api.quantum.network_list(IsA(http.HttpRequest))\ .AndReturn([network]) api.quantum.network_delete(IsA(http.HttpRequest), network.id) @@ -322,7 +322,7 @@ class NetworkTests(test.BaseAdminViewTests): tenants = self.tenants.list() network = self.networks.first() api.keystone.tenant_list(IsA(http.HttpRequest))\ - .AndReturn(tenants) + .AndReturn([tenants, False]) api.quantum.network_list(IsA(http.HttpRequest))\ .AndReturn([network]) api.quantum.network_delete(IsA(http.HttpRequest), network.id)\ diff --git a/openstack_dashboard/dashboards/admin/networks/views.py b/openstack_dashboard/dashboards/admin/networks/views.py index e4de670b4..bb7d96e91 100644 --- a/openstack_dashboard/dashboards/admin/networks/views.py +++ b/openstack_dashboard/dashboards/admin/networks/views.py @@ -42,7 +42,7 @@ class IndexView(tables.DataTableView): def _get_tenant_list(self): if not hasattr(self, "_tenants"): try: - tenants = api.keystone.tenant_list(self.request) + tenants, has_more = api.keystone.tenant_list(self.request) except: tenants = [] msg = _('Unable to retrieve instance tenant information.') diff --git a/openstack_dashboard/dashboards/admin/overview/tests.py b/openstack_dashboard/dashboards/admin/overview/tests.py index 692888513..321c35bde 100644 --- a/openstack_dashboard/dashboards/admin/overview/tests.py +++ b/openstack_dashboard/dashboards/admin/overview/tests.py @@ -46,7 +46,7 @@ class UsageViewTests(test.BaseAdminViewTests): usage_obj = api.nova.NovaUsage(self.usages.first()) quota_data = self.quota_usages.first() api.keystone.tenant_list(IsA(http.HttpRequest)) \ - .AndReturn(self.tenants.list()) + .AndReturn([self.tenants.list(), False]) api.nova.usage_list(IsA(http.HttpRequest), datetime.datetime(now.year, now.month, 1, 0, 0, 0), Func(usage.almost_now)) \ @@ -78,7 +78,7 @@ class UsageViewTests(test.BaseAdminViewTests): usage_obj = api.nova.NovaUsage(self.usages.first()) quota_data = self.quota_usages.first() api.keystone.tenant_list(IsA(http.HttpRequest)) \ - .AndReturn(self.tenants.list()) + .AndReturn([self.tenants.list(), False]) api.nova.usage_list(IsA(http.HttpRequest), datetime.datetime(now.year, now.month, 1, 0, 0, 0), Func(usage.almost_now)) \ diff --git a/openstack_dashboard/dashboards/admin/overview/views.py b/openstack_dashboard/dashboards/admin/overview/views.py index a05e6bf68..8f4f62ba2 100644 --- a/openstack_dashboard/dashboards/admin/overview/views.py +++ b/openstack_dashboard/dashboards/admin/overview/views.py @@ -41,7 +41,7 @@ class GlobalOverview(usage.UsageView): data = super(GlobalOverview, self).get_data() # Pre-fill tenant names try: - tenants = api.keystone.tenant_list(self.request) + tenants, has_more = api.keystone.tenant_list(self.request) except: tenants = [] exceptions.handle(self.request, diff --git a/openstack_dashboard/dashboards/admin/projects/tables.py b/openstack_dashboard/dashboards/admin/projects/tables.py index ec6f0182e..694193fc5 100644 --- a/openstack_dashboard/dashboards/admin/projects/tables.py +++ b/openstack_dashboard/dashboards/admin/projects/tables.py @@ -106,6 +106,7 @@ class TenantsTable(tables.DataTable): ModifyQuotas, DeleteTenantsAction) table_actions = (TenantFilterAction, CreateProject, DeleteTenantsAction) + pagination_param = "tenant_marker" class RemoveUserAction(tables.BatchAction): diff --git a/openstack_dashboard/dashboards/admin/projects/tests.py b/openstack_dashboard/dashboards/admin/projects/tests.py index 5baf03d44..a40783030 100644 --- a/openstack_dashboard/dashboards/admin/projects/tests.py +++ b/openstack_dashboard/dashboards/admin/projects/tests.py @@ -32,11 +32,11 @@ from horizon.workflows.views import WorkflowView INDEX_URL = reverse('horizon:admin:projects:index') +@test.create_stubs({api.keystone: ('tenant_list',)}) class TenantsViewTests(test.BaseAdminViewTests): def test_index(self): - self.mox.StubOutWithMock(api.keystone, 'tenant_list') - api.keystone.tenant_list(IsA(http.HttpRequest)) \ - .AndReturn(self.tenants.list()) + api.keystone.tenant_list(IsA(http.HttpRequest), paginate=True) \ + .AndReturn([self.tenants.list(), False]) self.mox.ReplayAll() res = self.client.get(INDEX_URL) diff --git a/openstack_dashboard/dashboards/admin/projects/views.py b/openstack_dashboard/dashboards/admin/projects/views.py index cded04a5f..2f03d2498 100644 --- a/openstack_dashboard/dashboards/admin/projects/views.py +++ b/openstack_dashboard/dashboards/admin/projects/views.py @@ -68,14 +68,22 @@ class IndexView(tables.DataTableView): table_class = TenantsTable template_name = 'admin/projects/index.html' + def has_more_data(self, table): + return self._more + def get_data(self): tenants = [] + marker = self.request.GET.get( + TenantsTable._meta.pagination_param, None) try: - tenants = api.keystone.tenant_list(self.request) + tenants, self._more = api.keystone.tenant_list( + self.request, + paginate=True, + marker=marker) except: + self._more = False exceptions.handle(self.request, _("Unable to retrieve project list.")) - tenants.sort(key=lambda x: x.id, reverse=True) return tenants diff --git a/openstack_dashboard/dashboards/admin/routers/tests.py b/openstack_dashboard/dashboards/admin/routers/tests.py index ef2222d8a..a48c9fc41 100644 --- a/openstack_dashboard/dashboards/admin/routers/tests.py +++ b/openstack_dashboard/dashboards/admin/routers/tests.py @@ -34,7 +34,8 @@ class RouterTests(test.BaseAdminViewTests, r_test.RouterTests): api.quantum.router_list( IsA(http.HttpRequest), search_opts=None).AndReturn(self.routers.list()) - api.keystone.tenant_list(IsA(http.HttpRequest)).AndReturn(tenants) + api.keystone.tenant_list(IsA(http.HttpRequest))\ + .AndReturn([tenants, False]) self._mock_external_network_list() self.mox.ReplayAll() diff --git a/openstack_dashboard/dashboards/admin/users/forms.py b/openstack_dashboard/dashboards/admin/users/forms.py index ff972f4ec..090bc5889 100644 --- a/openstack_dashboard/dashboards/admin/users/forms.py +++ b/openstack_dashboard/dashboards/admin/users/forms.py @@ -41,7 +41,8 @@ class BaseUserForm(forms.SelfHandlingForm): # Populate project choices project_choices = [('', _("Select a project"))] - for project in api.keystone.tenant_list(request): + projects, has_more = api.keystone.tenant_list(request) + for project in projects: if project.enabled: project_choices.append((project.id, project.name)) self.fields['project'].choices = project_choices diff --git a/openstack_dashboard/dashboards/admin/users/tests.py b/openstack_dashboard/dashboards/admin/users/tests.py index d6fb5bd35..ed029936a 100644 --- a/openstack_dashboard/dashboards/admin/users/tests.py +++ b/openstack_dashboard/dashboards/admin/users/tests.py @@ -55,7 +55,8 @@ class UsersViewTests(test.BaseAdminViewTests): user = self.users.get(id="1") role = self.roles.first() - api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list()) + api.keystone.tenant_list(IgnoreArg()) \ + .AndReturn([self.tenants.list(), False]) api.keystone.user_create(IgnoreArg(), user.name, user.email, @@ -87,7 +88,8 @@ class UsersViewTests(test.BaseAdminViewTests): def test_create_with_password_mismatch(self): user = self.users.get(id="1") - api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list()) + api.keystone.tenant_list(IgnoreArg()) \ + .AndReturn([self.tenants.list(), False]) api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) api.keystone.get_default_role(IgnoreArg()) \ .AndReturn(self.roles.first()) @@ -112,7 +114,8 @@ class UsersViewTests(test.BaseAdminViewTests): def test_create_validation_for_password_too_short(self): user = self.users.get(id="1") - api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list()) + api.keystone.tenant_list(IgnoreArg()) \ + .AndReturn([self.tenants.list(), False]) api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) api.keystone.get_default_role(IgnoreArg()) \ .AndReturn(self.roles.first()) @@ -140,7 +143,8 @@ class UsersViewTests(test.BaseAdminViewTests): def test_create_validation_for_password_too_long(self): user = self.users.get(id="1") - api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list()) + api.keystone.tenant_list(IgnoreArg()) \ + .AndReturn([self.tenants.list(), False]) api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) api.keystone.get_default_role(IgnoreArg()) \ .AndReturn(self.roles.first()) @@ -174,7 +178,8 @@ class UsersViewTests(test.BaseAdminViewTests): api.keystone.user_get(IsA(http.HttpRequest), '1', admin=True).AndReturn(user) - api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list()) + api.keystone.tenant_list(IgnoreArg()) \ + .AndReturn([self.tenants.list(), False]) api.keystone.user_update(IsA(http.HttpRequest), user.id, email=u'test@example.com', @@ -207,7 +212,8 @@ class UsersViewTests(test.BaseAdminViewTests): api.keystone.user_get(IsA(http.HttpRequest), '1', admin=True).AndReturn(user) - api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list()) + api.keystone.tenant_list(IgnoreArg()) \ + .AndReturn([self.tenants.list(), False]) api.keystone.keystone_can_edit_user().AndReturn(False) api.keystone.keystone_can_edit_user().AndReturn(False) @@ -229,7 +235,8 @@ class UsersViewTests(test.BaseAdminViewTests): api.keystone.user_get(IsA(http.HttpRequest), '1', admin=True).AndReturn(user) - api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list()) + api.keystone.tenant_list(IgnoreArg()) \ + .AndReturn([self.tenants.list(), False]) self.mox.ReplayAll() @@ -253,7 +260,8 @@ class UsersViewTests(test.BaseAdminViewTests): api.keystone.user_get(IsA(http.HttpRequest), '1', admin=True).AndReturn(user) - api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list()) + api.keystone.tenant_list(IgnoreArg()) \ + .AndReturn([self.tenants.list(), False]) self.mox.ReplayAll() @@ -353,7 +361,8 @@ class SeleniumTests(test.SeleniumAdminTestCase): 'role_list', 'user_list')}) def test_modal_create_user_with_passwords_not_matching(self): - api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list()) + api.keystone.tenant_list(IgnoreArg()) \ + .AndReturn([self.tenants.list(), False]) api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) api.keystone.user_list(IgnoreArg()).AndReturn(self.users.list()) api.keystone.get_default_role(IgnoreArg()) \ @@ -384,7 +393,8 @@ class SeleniumTests(test.SeleniumAdminTestCase): def test_update_user_with_passwords_not_matching(self): api.keystone.user_get(IsA(http.HttpRequest), '1', admin=True).AndReturn(self.user) - api.keystone.tenant_list(IgnoreArg()).AndReturn(self.tenants.list()) + api.keystone.tenant_list(IgnoreArg()) \ + .AndReturn([self.tenants.list(), False]) self.mox.ReplayAll() self.selenium.get("%s%s" % (self.live_server_url, USER_UPDATE_URL)) diff --git a/openstack_dashboard/dashboards/admin/volumes/tests.py b/openstack_dashboard/dashboards/admin/volumes/tests.py index 20aac21c1..b2c829f2c 100644 --- a/openstack_dashboard/dashboards/admin/volumes/tests.py +++ b/openstack_dashboard/dashboards/admin/volumes/tests.py @@ -37,7 +37,7 @@ class VolumeTests(test.BaseAdminViewTests): cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) keystone.tenant_list(IsA(http.HttpRequest)) \ - .AndReturn(self.tenants.list()) + .AndReturn([self.tenants.list(), False]) self.mox.ReplayAll() @@ -81,7 +81,7 @@ class VolumeTests(test.BaseAdminViewTests): cinder.volume_type_delete(IsA(http.HttpRequest), str(volume_type.id)) keystone.tenant_list(IsA(http.HttpRequest)) \ - .AndReturn(self.tenants.list()) + .AndReturn([self.tenants.list(), False]) self.mox.ReplayAll() res = self.client.post(reverse('horizon:admin:volumes:index'), diff --git a/openstack_dashboard/dashboards/admin/volumes/views.py b/openstack_dashboard/dashboards/admin/volumes/views.py index 58ffdfd2d..92e9097df 100644 --- a/openstack_dashboard/dashboards/admin/volumes/views.py +++ b/openstack_dashboard/dashboards/admin/volumes/views.py @@ -45,7 +45,7 @@ class IndexView(tables.MultiTableView, VolumeTableMixIn): # Gather our tenants to correlate against IDs try: - tenants = keystone.tenant_list(self.request) + tenants, has_more = keystone.tenant_list(self.request) except: tenants = [] msg = _('Unable to retrieve volume tenant information.')