From 81247fda009e3c27e377b605c5b997279b2f91f6 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Thu, 14 Mar 2013 08:43:33 -0600 Subject: [PATCH] Adding pagination to the instance views Modifying the api.nova.server_list() method to optionally handle pagination. The method will also work without pagination to support the many other place than the instance views that continue to call the method. Fixes: bug #1046915 Change-Id: I8195f1f2d8922e1722743d7a2d627a8645e8b3bd --- openstack_dashboard/api/nova.py | 17 ++- openstack_dashboard/api/quantum.py | 2 +- .../dashboards/admin/instances/tests.py | 24 +++-- .../dashboards/admin/instances/views.py | 12 ++- .../dashboards/admin/volumes/tests.py | 4 +- .../access_and_security/floating_ips/tests.py | 6 +- .../project/access_and_security/tabs.py | 3 +- .../project/access_and_security/tests.py | 3 +- .../dashboards/project/instances/tests.py | 102 +++++++++++------- .../dashboards/project/instances/views.py | 11 +- .../dashboards/project/loadbalancers/tests.py | 11 +- .../project/loadbalancers/workflows.py | 2 +- .../dashboards/project/volumes/tests.py | 14 +-- .../dashboards/project/volumes/views.py | 5 +- .../test/api_tests/nova_tests.py | 46 +++++++- openstack_dashboard/test/tests/quotas.py | 8 +- openstack_dashboard/usage/quotas.py | 2 +- 17 files changed, 194 insertions(+), 78 deletions(-) diff --git a/openstack_dashboard/api/nova.py b/openstack_dashboard/api/nova.py index af9a457c1d..e02c9fa22b 100644 --- a/openstack_dashboard/api/nova.py +++ b/openstack_dashboard/api/nova.py @@ -362,14 +362,27 @@ def server_get(request, instance_id): def server_list(request, search_opts=None, all_tenants=False): + page_size = getattr(settings, 'API_RESULT_PAGE_SIZE', 20) + paginate = False if search_opts is None: search_opts = {} + elif 'paginate' in search_opts: + paginate = search_opts.pop('paginate') + if paginate: + search_opts['limit'] = page_size + 1 + if all_tenants: search_opts['all_tenants'] = True else: search_opts['project_id'] = request.user.tenant_id - return [Server(s, request) - for s in novaclient(request).servers.list(True, search_opts)] + servers = [Server(s, request) + for s in novaclient(request).servers.list(True, search_opts)] + + has_more_data = False + if paginate and len(servers) > page_size: + servers.pop(-1) + has_more_data = True + return (servers, has_more_data) def server_console_output(request, instance_id, tail_length=None): diff --git a/openstack_dashboard/api/quantum.py b/openstack_dashboard/api/quantum.py index b04fadceec..194e68f29b 100644 --- a/openstack_dashboard/api/quantum.py +++ b/openstack_dashboard/api/quantum.py @@ -168,7 +168,7 @@ class FloatingIpManager(network.FloatingIpManager): def list_targets(self): ports = port_list(self.request) - servers = nova.server_list(self.request) + servers, has_more = nova.server_list(self.request) server_dict = SortedDict([(s.id, s.name) for s in servers]) targets = [] for p in ports: diff --git a/openstack_dashboard/dashboards/admin/instances/tests.py b/openstack_dashboard/dashboards/admin/instances/tests.py index 546e9b56ce..893223a9c2 100644 --- a/openstack_dashboard/dashboards/admin/instances/tests.py +++ b/openstack_dashboard/dashboards/admin/instances/tests.py @@ -35,8 +35,10 @@ class InstanceViewTest(test.BaseAdminViewTests): tenants = self.tenants.list() api.keystone.tenant_list(IsA(http.HttpRequest), admin=True).\ AndReturn(tenants) + search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), - all_tenants=True).AndReturn(servers) + all_tenants=True, search_opts=search_opts) \ + .AndReturn([servers, False]) api.nova.flavor_list(IsA(http.HttpRequest)).AndReturn(flavors) self.mox.ReplayAll() @@ -54,8 +56,10 @@ class InstanceViewTest(test.BaseAdminViewTests): flavors = self.flavors.list() full_flavors = SortedDict([(f.id, f) for f in flavors]) + search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), - all_tenants=True).AndReturn(servers) + all_tenants=True, search_opts=search_opts) \ + .AndReturn([servers, False]) api.nova.flavor_list(IsA(http.HttpRequest)). \ AndRaise(self.exceptions.nova) api.keystone.tenant_list(IsA(http.HttpRequest), admin=True).\ @@ -83,8 +87,10 @@ class InstanceViewTest(test.BaseAdminViewTests): for i, server in enumerate(servers): server.flavor['id'] = str(uuid.UUID(int=i)) + search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), - all_tenants=True).AndReturn(servers) + all_tenants=True, search_opts=search_opts) \ + .AndReturn([servers, False]) api.nova.flavor_list(IsA(http.HttpRequest)). \ AndReturn(flavors) api.keystone.tenant_list(IsA(http.HttpRequest), admin=True).\ @@ -102,8 +108,10 @@ class InstanceViewTest(test.BaseAdminViewTests): @test.create_stubs({api.nova: ('server_list',)}) def test_index_server_list_exception(self): + search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), - all_tenants=True).AndRaise(self.exceptions.nova) + all_tenants=True, search_opts=search_opts) \ + .AndRaise(self.exceptions.nova) self.mox.ReplayAll() @@ -146,8 +154,10 @@ class InstanceViewTest(test.BaseAdminViewTests): def test_index_options_before_migrate(self): api.keystone.tenant_list(IsA(http.HttpRequest), admin=True).\ AndReturn(self.tenants.list()) + search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), - all_tenants=True).AndReturn(self.servers.list()) + all_tenants=True, search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.flavor_list(IsA(http.HttpRequest)).\ AndReturn(self.flavors.list()) self.mox.ReplayAll() @@ -164,8 +174,10 @@ class InstanceViewTest(test.BaseAdminViewTests): server.status = "VERIFY_RESIZE" api.keystone.tenant_list(IsA(http.HttpRequest), admin=True).\ AndReturn(self.tenants.list()) + search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), - all_tenants=True).AndReturn(self.servers.list()) + all_tenants=True, search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.flavor_list(IsA(http.HttpRequest)).\ AndReturn(self.flavors.list()) self.mox.ReplayAll() diff --git a/openstack_dashboard/dashboards/admin/instances/views.py b/openstack_dashboard/dashboards/admin/instances/views.py index 230999a74f..edb0af30c6 100644 --- a/openstack_dashboard/dashboards/admin/instances/views.py +++ b/openstack_dashboard/dashboards/admin/instances/views.py @@ -46,11 +46,21 @@ class AdminIndexView(tables.DataTableView): table_class = AdminInstancesTable template_name = 'admin/instances/index.html' + def has_more_data(self, table): + return self._more + def get_data(self): instances = [] + marker = self.request.GET.get( + AdminInstancesTable._meta.pagination_param, None) try: - instances = api.nova.server_list(self.request, all_tenants=True) + instances, self._more = api.nova.server_list( + self.request, + search_opts={'marker': marker, + 'paginate': True}, + all_tenants=True) except: + self._more = False exceptions.handle(self.request, _('Unable to retrieve instance list.')) if instances: diff --git a/openstack_dashboard/dashboards/admin/volumes/tests.py b/openstack_dashboard/dashboards/admin/volumes/tests.py index a7c4d04ffb..10741888b7 100644 --- a/openstack_dashboard/dashboards/admin/volumes/tests.py +++ b/openstack_dashboard/dashboards/admin/volumes/tests.py @@ -33,7 +33,7 @@ class VolumeTests(test.BaseAdminViewTests): cinder.volume_list(IsA(http.HttpRequest), search_opts={ 'all_tenants': 1}).AndReturn(self.volumes.list()) api.nova.server_list(IsA(http.HttpRequest)).\ - AndReturn(self.servers.list()) + AndReturn([self.servers.list(), False]) cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) keystone.tenant_list(IsA(http.HttpRequest), @@ -75,7 +75,7 @@ class VolumeTests(test.BaseAdminViewTests): cinder.volume_list(IsA(http.HttpRequest), search_opts={ 'all_tenants': 1}).AndReturn(self.volumes.list()) api.nova.server_list(IsA(http.HttpRequest)).\ - AndReturn(self.servers.list()) + AndReturn([self.servers.list(), False]) cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) cinder.volume_type_delete(IsA(http.HttpRequest), diff --git a/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tests.py b/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tests.py index 3116a6d7df..dac8a619d8 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tests.py +++ b/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tests.py @@ -132,7 +132,8 @@ class FloatingIpViewTests(test.TestCase): self.mox.StubOutWithMock(api.nova, 'server_list') api.nova.server_list(IsA(http.HttpRequest), - all_tenants=True).AndReturn(self.servers.list()) + all_tenants=True).AndReturn([self.servers.list(), + False]) api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \ .AndReturn(self.floating_ips.list()) api.network.floating_ip_disassociate(IsA(http.HttpRequest), @@ -154,7 +155,8 @@ class FloatingIpViewTests(test.TestCase): self.mox.StubOutWithMock(api.nova, 'server_list') api.nova.server_list(IsA(http.HttpRequest), - all_tenants=True).AndReturn(self.servers.list()) + all_tenants=True).AndReturn([self.servers.list(), + False]) api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \ .AndReturn(self.floating_ips.list()) diff --git a/openstack_dashboard/dashboards/project/access_and_security/tabs.py b/openstack_dashboard/dashboards/project/access_and_security/tabs.py index 05dee9f2a3..54cdc4ff70 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/tabs.py +++ b/openstack_dashboard/dashboards/project/access_and_security/tabs.py @@ -91,7 +91,8 @@ class FloatingIPsTab(tabs.TableTab): instances = [] try: - instances = nova.server_list(self.request, all_tenants=True) + instances, has_more = nova.server_list(self.request, + all_tenants=True) except: exceptions.handle(self.request, _('Unable to retrieve instance list.')) diff --git a/openstack_dashboard/dashboards/project/access_and_security/tests.py b/openstack_dashboard/dashboards/project/access_and_security/tests.py index 6b65bf2f64..6c69de6855 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/tests.py +++ b/openstack_dashboard/dashboards/project/access_and_security/tests.py @@ -43,7 +43,8 @@ class AccessAndSecurityTests(test.TestCase): self.mox.StubOutWithMock(api.nova, 'server_list') api.nova.server_list(IsA(http.HttpRequest), - all_tenants=True).AndReturn(self.servers.list()) + all_tenants=True).AndReturn([self.servers.list(), + False]) api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(keypairs) api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \ .AndReturn(floating_ips) diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py index 0feb8267ac..62a177bb3e 100644 --- a/openstack_dashboard/dashboards/project/instances/tests.py +++ b/openstack_dashboard/dashboards/project/instances/tests.py @@ -46,8 +46,9 @@ class InstanceTests(test.TestCase): def test_index(self): api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) - api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \ .MultipleTimes().AndReturn(self.limits['absolute']) @@ -65,7 +66,8 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.nova: ('server_list', 'tenant_absolute_limits')}) def test_index_server_list_exception(self): - api.nova.server_list(IsA(http.HttpRequest)) \ + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ .AndRaise(self.exceptions.nova) api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \ .MultipleTimes().AndReturn(self.limits['absolute']) @@ -86,8 +88,9 @@ class InstanceTests(test.TestCase): servers = self.servers.list() flavors = self.flavors.list() full_flavors = SortedDict([(f.id, f) for f in flavors]) - - api.nova.server_list(IsA(http.HttpRequest)).AndReturn(servers) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([servers, False]) api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndRaise(self.exceptions.nova) for server in servers: @@ -117,7 +120,9 @@ class InstanceTests(test.TestCase): for i, server in enumerate(servers): server.flavor['id'] = str(uuid.UUID(int=i)) - api.nova.server_list(IsA(http.HttpRequest)).AndReturn(servers) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([servers, False]) api.nova.flavor_list(IsA(http.HttpRequest)).AndReturn(flavors) for server in servers: api.nova.flavor_get(IsA(http.HttpRequest), server.flavor["id"]). \ @@ -141,8 +146,9 @@ class InstanceTests(test.TestCase): def test_terminate_instance(self): server = self.servers.first() - api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list()) api.nova.server_delete(IsA(http.HttpRequest), server.id) @@ -159,8 +165,9 @@ class InstanceTests(test.TestCase): def test_terminate_instance_exception(self): server = self.servers.first() - api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list()) api.nova.server_delete(IsA(http.HttpRequest), server.id) \ .AndRaise(self.exceptions.nova) @@ -180,8 +187,9 @@ class InstanceTests(test.TestCase): api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) - api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.server_pause(IsA(http.HttpRequest), server.id) self.mox.ReplayAll() @@ -199,8 +207,9 @@ class InstanceTests(test.TestCase): api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) - api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.server_pause(IsA(http.HttpRequest), server.id) \ .AndRaise(self.exceptions.nova) @@ -220,8 +229,9 @@ class InstanceTests(test.TestCase): api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) - api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.server_unpause(IsA(http.HttpRequest), server.id) self.mox.ReplayAll() @@ -240,8 +250,9 @@ class InstanceTests(test.TestCase): api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) - api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.server_unpause(IsA(http.HttpRequest), server.id) \ .AndRaise(self.exceptions.nova) @@ -260,8 +271,9 @@ class InstanceTests(test.TestCase): api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) - api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.server_reboot(IsA(http.HttpRequest), server.id, api.nova.REBOOT_HARD) @@ -280,8 +292,9 @@ class InstanceTests(test.TestCase): api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) - api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.server_reboot(IsA(http.HttpRequest), server.id, api.nova.REBOOT_HARD) \ .AndRaise(self.exceptions.nova) @@ -301,8 +314,9 @@ class InstanceTests(test.TestCase): api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) - api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.server_reboot(IsA(http.HttpRequest), server.id, api.nova.REBOOT_SOFT) @@ -321,8 +335,9 @@ class InstanceTests(test.TestCase): api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) - api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.server_suspend(IsA(http.HttpRequest), unicode(server.id)) self.mox.ReplayAll() @@ -340,8 +355,9 @@ class InstanceTests(test.TestCase): api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) - api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.server_suspend(IsA(http.HttpRequest), unicode(server.id)) \ .AndRaise(self.exceptions.nova) @@ -361,8 +377,9 @@ class InstanceTests(test.TestCase): api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) - api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.server_resume(IsA(http.HttpRequest), unicode(server.id)) self.mox.ReplayAll() @@ -381,8 +398,9 @@ class InstanceTests(test.TestCase): api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) - api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.server_resume(IsA(http.HttpRequest), unicode(server.id)) \ .AndRaise(self.exceptions.nova) @@ -1181,8 +1199,9 @@ class InstanceTests(test.TestCase): api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) - api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \ .MultipleTimes().AndReturn(limits) @@ -1209,8 +1228,9 @@ class InstanceTests(test.TestCase): api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) - api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \ .MultipleTimes().AndReturn(self.limits['absolute']) @@ -1281,8 +1301,9 @@ class InstanceTests(test.TestCase): server = self.servers.first() fip = self.q_floating_ips.first() - api.nova.server_list( - IsA(http.HttpRequest)).AndReturn(self.servers.list()) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list()) api.network.floating_ip_target_get_by_instance( IsA(http.HttpRequest), @@ -1310,8 +1331,9 @@ class InstanceTests(test.TestCase): fip = self.q_floating_ips.first() fip.port_id = server.id - api.nova.server_list( - IsA(http.HttpRequest)).AndReturn(self.servers.list()) + search_opts = {'marker': None, 'paginate': True} + api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ + .AndReturn([self.servers.list(), False]) api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list()) api.network.floating_ip_target_get_by_instance( IsA(http.HttpRequest), diff --git a/openstack_dashboard/dashboards/project/instances/views.py b/openstack_dashboard/dashboards/project/instances/views.py index 20fa619a62..99dc6a6b4c 100644 --- a/openstack_dashboard/dashboards/project/instances/views.py +++ b/openstack_dashboard/dashboards/project/instances/views.py @@ -48,11 +48,20 @@ class IndexView(tables.DataTableView): table_class = InstancesTable template_name = 'project/instances/index.html' + def has_more_data(self, table): + return self._more + def get_data(self): + marker = self.request.GET.get( + InstancesTable._meta.pagination_param, None) # Gather our instances try: - instances = api.nova.server_list(self.request) + instances, self._more = api.nova.server_list( + self.request, + search_opts={'marker': marker, + 'paginate': True}) except: + self._more = False instances = [] exceptions.handle(self.request, _('Unable to retrieve instances.')) diff --git a/openstack_dashboard/dashboards/project/loadbalancers/tests.py b/openstack_dashboard/dashboards/project/loadbalancers/tests.py index ece43142ce..42f456577d 100644 --- a/openstack_dashboard/dashboards/project/loadbalancers/tests.py +++ b/openstack_dashboard/dashboards/project/loadbalancers/tests.py @@ -244,8 +244,8 @@ class LoadBalancerTests(test.TestCase): api.lbaas.pools_get(IsA(http.HttpRequest)).AndReturn(self.pools.list()) - api.nova.server_list(IsA(http.HttpRequest)).AndReturn([server1, - server2]) + api.nova.server_list(IsA(http.HttpRequest)).AndReturn( + [[server1, server2], False]) api.quantum.port_list(IsA(http.HttpRequest), device_id=server1.id).AndReturn([port1, ]) @@ -288,8 +288,9 @@ class LoadBalancerTests(test.TestCase): api.lbaas.pools_get(IsA(http.HttpRequest)).AndReturn(self.pools.list()) - api.nova.server_list(IsA(http.HttpRequest)).AndReturn([server1, - server2]) + api.nova.server_list(IsA(http.HttpRequest)).AndReturn([[server1, + server2], + False]) self.mox.ReplayAll() @@ -317,7 +318,7 @@ class LoadBalancerTests(test.TestCase): api.lbaas.pools_get(IsA(http.HttpRequest)).AndReturn(self.pools.list()) api.nova.server_list( - IsA(http.HttpRequest)).AndReturn([server1, server2]) + IsA(http.HttpRequest)).AndReturn([[server1, server2], False]) self.mox.ReplayAll() diff --git a/openstack_dashboard/dashboards/project/loadbalancers/workflows.py b/openstack_dashboard/dashboards/project/loadbalancers/workflows.py index a8641a9f7c..5804ee7f86 100644 --- a/openstack_dashboard/dashboards/project/loadbalancers/workflows.py +++ b/openstack_dashboard/dashboards/project/loadbalancers/workflows.py @@ -285,7 +285,7 @@ class AddMemberAction(workflows.Action): members_choices = [] try: - servers = api.nova.server_list(request) + servers, has_more = api.nova.server_list(request) except: servers = [] exceptions.handle(request, diff --git a/openstack_dashboard/dashboards/project/volumes/tests.py b/openstack_dashboard/dashboards/project/volumes/tests.py index 6b0689666d..6dbde8dc2b 100644 --- a/openstack_dashboard/dashboards/project/volumes/tests.py +++ b/openstack_dashboard/dashboards/project/volumes/tests.py @@ -314,11 +314,11 @@ class VolumeViewTests(test.TestCase): AndReturn(self.volumes.list()) cinder.volume_delete(IsA(http.HttpRequest), volume.id) api.nova.server_list(IsA(http.HttpRequest)).\ - AndReturn(self.servers.list()) + AndReturn([self.servers.list(), False]) cinder.volume_list(IsA(http.HttpRequest), search_opts=None).\ AndReturn(self.volumes.list()) api.nova.server_list(IsA(http.HttpRequest)).\ - AndReturn(self.servers.list()) + AndReturn([self.servers.list(), False]) self.mox.ReplayAll() @@ -342,11 +342,11 @@ class VolumeViewTests(test.TestCase): cinder.volume_delete(IsA(http.HttpRequest), volume.id).\ AndRaise(exc) api.nova.server_list(IsA(http.HttpRequest)).\ - AndReturn(self.servers.list()) + AndReturn([self.servers.list(), False]) cinder.volume_list(IsA(http.HttpRequest), search_opts=None).\ AndReturn(self.volumes.list()) api.nova.server_list(IsA(http.HttpRequest)).\ - AndReturn(self.servers.list()) + AndReturn([self.servers.list(), False]) self.mox.ReplayAll() @@ -364,7 +364,7 @@ class VolumeViewTests(test.TestCase): servers = self.servers.list() cinder.volume_get(IsA(http.HttpRequest), volume.id).AndReturn(volume) - api.nova.server_list(IsA(http.HttpRequest)).AndReturn(servers) + api.nova.server_list(IsA(http.HttpRequest)).AndReturn([servers, False]) self.mox.ReplayAll() url = reverse('horizon:project:volumes:attach', args=[volume.id]) @@ -387,7 +387,7 @@ class VolumeViewTests(test.TestCase): servers = self.servers.list() cinder.volume_get(IsA(http.HttpRequest), volume.id).AndReturn(volume) - api.nova.server_list(IsA(http.HttpRequest)).AndReturn(servers) + api.nova.server_list(IsA(http.HttpRequest)).AndReturn([servers, False]) self.mox.ReplayAll() url = reverse('horizon:project:volumes:attach', args=[volume.id]) @@ -407,7 +407,7 @@ class VolumeViewTests(test.TestCase): cinder.volume_get(IsA(http.HttpRequest), volume.id) \ .AndReturn(volume) api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + .AndReturn([self.servers.list(), False]) self.mox.ReplayAll() diff --git a/openstack_dashboard/dashboards/project/volumes/views.py b/openstack_dashboard/dashboards/project/volumes/views.py index 1c4cb239f8..f25c822412 100644 --- a/openstack_dashboard/dashboards/project/volumes/views.py +++ b/openstack_dashboard/dashboards/project/volumes/views.py @@ -50,7 +50,8 @@ class VolumeTableMixIn(object): def _get_instances(self): try: - return api.nova.server_list(self.request) + instances, has_more = api.nova.server_list(self.request) + return instances except: exceptions.handle(self.request, _("Unable to retrieve volume/instance " @@ -145,7 +146,7 @@ class EditAttachmentsView(tables.DataTableView, forms.ModalFormView): def get_initial(self): try: - instances = api.nova.server_list(self.request) + instances, has_more = api.nova.server_list(self.request) except: instances = [] exceptions.handle(self.request, diff --git a/openstack_dashboard/test/api_tests/nova_tests.py b/openstack_dashboard/test/api_tests/nova_tests.py index f66aeb5bda..71e354f8e3 100644 --- a/openstack_dashboard/test/api_tests/nova_tests.py +++ b/openstack_dashboard/test/api_tests/nova_tests.py @@ -22,6 +22,9 @@ from __future__ import absolute_import from django import http +from django.conf import settings +from django.test.utils import override_settings + from mox import IsA from novaclient.v1_1 import servers @@ -112,10 +115,51 @@ class ComputeApiTests(test.APITestCase): novaclient.servers.list(True, {'all_tenants': True}).AndReturn(servers) self.mox.ReplayAll() - ret_val = api.nova.server_list(self.request, all_tenants=True) + ret_val, has_more = api.nova.server_list(self.request, + all_tenants=True) for server in ret_val: self.assertIsInstance(server, api.nova.Server) + def test_server_list_pagination(self): + page_size = getattr(settings, 'API_RESULT_PAGE_SIZE', 20) + servers = self.servers.list() + novaclient = self.stub_novaclient() + novaclient.servers = self.mox.CreateMockAnything() + novaclient.servers.list(True, + {'all_tenants': True, + 'marker': None, + 'limit': page_size + 1}).AndReturn(servers) + self.mox.ReplayAll() + + ret_val, has_more = api.nova.server_list(self.request, + {'marker': None, + 'paginate': True}, + all_tenants=True) + for server in ret_val: + self.assertIsInstance(server, api.nova.Server) + self.assertFalse(has_more) + + @override_settings(API_RESULT_PAGE_SIZE=1) + def test_server_list_pagination_more(self): + page_size = getattr(settings, 'API_RESULT_PAGE_SIZE', 1) + servers = self.servers.list() + novaclient = self.stub_novaclient() + novaclient.servers = self.mox.CreateMockAnything() + novaclient.servers.list(True, + {'all_tenants': True, + 'marker': None, + 'limit': page_size + 1}).AndReturn(servers) + self.mox.ReplayAll() + + ret_val, has_more = api.nova.server_list(self.request, + {'marker': None, + 'paginate': True}, + all_tenants=True) + for server in ret_val: + self.assertIsInstance(server, api.nova.Server) + self.assertEquals(page_size, len(ret_val)) + self.assertTrue(has_more) + def test_usage_get(self): novaclient = self.stub_novaclient() novaclient.usage = self.mox.CreateMockAnything() diff --git a/openstack_dashboard/test/tests/quotas.py b/openstack_dashboard/test/tests/quotas.py index bae395e5ae..1c65f40cad 100644 --- a/openstack_dashboard/test/tests/quotas.py +++ b/openstack_dashboard/test/tests/quotas.py @@ -65,7 +65,7 @@ class QuotaTests(test.APITestCase): api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \ .AndReturn(self.floating_ips.list()) api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + .AndReturn([self.servers.list(), False]) cinder.volume_list(IsA(http.HttpRequest)) \ .AndReturn(self.volumes.list()) cinder.tenant_quota_get(IsA(http.HttpRequest), '1') \ @@ -94,7 +94,7 @@ class QuotaTests(test.APITestCase): api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \ .AndReturn(self.floating_ips.list()) api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + .AndReturn([self.servers.list(), False]) self.mox.ReplayAll() @@ -118,7 +118,7 @@ class QuotaTests(test.APITestCase): .AndReturn(self.quotas.first()) api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \ .AndReturn([]) - api.nova.server_list(IsA(http.HttpRequest)).AndReturn([]) + api.nova.server_list(IsA(http.HttpRequest)).AndReturn([[], False]) self.mox.ReplayAll() @@ -153,7 +153,7 @@ class QuotaTests(test.APITestCase): api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \ .AndReturn(self.floating_ips.list()) api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn(self.servers.list()) + .AndReturn([self.servers.list(), False]) cinder.volume_list(IsA(http.HttpRequest)) \ .AndReturn(self.volumes.list()) cinder.tenant_quota_get(IsA(http.HttpRequest), '1') \ diff --git a/openstack_dashboard/usage/quotas.py b/openstack_dashboard/usage/quotas.py index c3cbe5faa0..99245097c6 100644 --- a/openstack_dashboard/usage/quotas.py +++ b/openstack_dashboard/usage/quotas.py @@ -119,7 +119,7 @@ def tenant_quota_usages(request): # Get our usages. floating_ips = network.tenant_floating_ip_list(request) flavors = dict([(f.id, f) for f in nova.flavor_list(request)]) - instances = nova.server_list(request) + instances, has_more = nova.server_list(request) # Fetch deleted flavors if necessary. missing_flavors = [instance.flavor['id'] for instance in instances if instance.flavor['id'] not in flavors]