Add pagination support to the volume page
Cinder v2 supports pagination. Added to the volumes table for both admin and project. Also fix the Cinder REST API to handle pagination (like Glance REST API). To test: set 'Items Per Page' in the UI Settings page to a low number. Co-Authored-By: Cindy Lu <clu@us.ibm.com> Co-Authored-By: Timur Sufiev <tsufiev@mirantis.com> Change-Id: Ib1772d6e6214dc96a09ce32fb4d9f9fb79d161f0 Closes-Bug: #1316793
This commit is contained in:
parent
31b92c7a3b
commit
091d351553
@ -30,6 +30,7 @@ from cinderclient import exceptions as cinder_exception
|
||||
from cinderclient.v2.contrib import list_extensions as cinder_list_extensions
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon.utils import functions as utils
|
||||
from horizon.utils.memoized import memoized # noqa
|
||||
|
||||
from openstack_dashboard.api import base
|
||||
@ -194,25 +195,59 @@ def version_get():
|
||||
return api_version['version']
|
||||
|
||||
|
||||
def volume_list(request, search_opts=None):
|
||||
def volume_list(request, search_opts=None, marker=None, sort_dir="desc"):
|
||||
volumes, _, __ = volume_list_paged(
|
||||
request, search_opts=search_opts, marker=marker, paginate=False,
|
||||
sort_dir=sort_dir)
|
||||
return volumes
|
||||
|
||||
|
||||
def volume_list_paged(request, search_opts=None, marker=None, paginate=False,
|
||||
sort_dir="desc"):
|
||||
"""To see all volumes in the cloud as an admin you can pass in a special
|
||||
search option: {'all_tenants': 1}
|
||||
"""
|
||||
has_more_data = False
|
||||
has_prev_data = False
|
||||
volumes = []
|
||||
|
||||
c_client = cinderclient(request)
|
||||
if c_client is None:
|
||||
return []
|
||||
return volumes, has_more_data, has_prev_data
|
||||
|
||||
# build a dictionary of volume_id -> transfer
|
||||
transfers = {t.volume_id: t
|
||||
for t in transfer_list(request, search_opts=search_opts)}
|
||||
|
||||
volumes = []
|
||||
if VERSIONS.active > 1 and paginate:
|
||||
page_size = utils.get_page_size(request)
|
||||
# sort_key and sort_dir deprecated in kilo, use sort
|
||||
# if pagination is true, we use a single sort parameter
|
||||
# by default, it is "created_at"
|
||||
sort = 'created_at:' + sort_dir
|
||||
for v in c_client.volumes.list(search_opts=search_opts,
|
||||
limit=page_size + 1,
|
||||
marker=marker,
|
||||
sort=sort):
|
||||
v.transfer = transfers.get(v.id)
|
||||
volumes.append(Volume(v))
|
||||
if len(volumes) > page_size:
|
||||
has_more_data = True
|
||||
volumes.pop()
|
||||
if marker is not None:
|
||||
has_prev_data = True
|
||||
# first page condition when reached via prev back
|
||||
elif sort_dir == 'asc' and marker is not None:
|
||||
has_more_data = True
|
||||
# last page condition
|
||||
elif marker is not None:
|
||||
has_prev_data = True
|
||||
else:
|
||||
for v in c_client.volumes.list(search_opts=search_opts):
|
||||
v.transfer = transfers.get(v.id)
|
||||
volumes.append(Volume(v))
|
||||
|
||||
return volumes
|
||||
return volumes, has_more_data, has_prev_data
|
||||
|
||||
|
||||
def volume_get(request, volume_id):
|
||||
|
@ -22,6 +22,9 @@ from openstack_dashboard.api.rest import utils as rest_utils
|
||||
from openstack_dashboard.api.rest import urls
|
||||
|
||||
|
||||
CLIENT_KEYWORDS = {'marker', 'sort_dir', 'paginate'}
|
||||
|
||||
|
||||
@urls.register
|
||||
class Volumes(generic.View):
|
||||
"""API for cinder volumes.
|
||||
@ -33,26 +36,43 @@ class Volumes(generic.View):
|
||||
"""Get a detailed list of volumes associated with the current user's
|
||||
project.
|
||||
|
||||
Example GET:
|
||||
http://localhost/api/cinder/volumes?paginate=true&sort_dir=asc #flake8: noqa
|
||||
|
||||
If invoked as an admin, you may set the GET parameter "all_projects"
|
||||
to 'true'.
|
||||
to 'true' to return details for all projects.
|
||||
|
||||
The following get parameters may be passed in the GET
|
||||
|
||||
:param search_opts: include options such as name, status, bootable
|
||||
:param search_opts: includes options such as name, status, bootable
|
||||
:param paginate: If true will perform pagination based on settings.
|
||||
:param marker: Specifies the namespace of the last-seen image.
|
||||
The typical pattern of limit and marker is to make an
|
||||
initial limited request and then to use the last
|
||||
namespace from the response as the marker parameter
|
||||
in a subsequent limited request. With paginate, limit
|
||||
is automatically set.
|
||||
:param sort_dir: The sort direction ('asc' or 'desc').
|
||||
|
||||
The listing result is an object with property "items".
|
||||
"""
|
||||
# TODO(clu_): when v2 pagination stuff in Cinder API merges
|
||||
# (https://review.openstack.org/#/c/118450), handle here accordingly
|
||||
|
||||
if request.GET.get('all_projects') == 'true':
|
||||
result = api.cinder.volume_list(request, {'all_tenants': 1})
|
||||
else:
|
||||
result = api.cinder.volume_list(
|
||||
result, has_more, has_prev = api.cinder.volume_list_paged(
|
||||
request,
|
||||
search_opts=rest_utils.parse_filters_kwargs(request)[0]
|
||||
{'all_tenants': 1}
|
||||
)
|
||||
return {'items': [u.to_dict() for u in result]}
|
||||
else:
|
||||
search_opts, kwargs = rest_utils.parse_filters_kwargs(request, CLIENT_KEYWORDS)
|
||||
result, has_more, has_prev = api.cinder.volume_list_paged(
|
||||
request,
|
||||
search_opts=search_opts, **kwargs
|
||||
)
|
||||
return {
|
||||
'items': [u.to_dict() for u in result],
|
||||
'has_more_data': has_more,
|
||||
'has_prev_data': has_prev
|
||||
}
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def post(self, request):
|
||||
|
@ -61,6 +61,12 @@ class VolumeTab(tabs.TableTab, volumes_tabs.VolumeTableMixIn):
|
||||
|
||||
return volumes
|
||||
|
||||
def has_prev_data(self, table):
|
||||
return self._has_prev_data
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._has_more_data
|
||||
|
||||
|
||||
class VolumeTypesTab(tabs.TableTab, volumes_tabs.VolumeTableMixIn):
|
||||
table_classes = (volume_types_tables.VolumeTypesTable,
|
||||
|
@ -12,24 +12,34 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django import http
|
||||
from django.test.utils import override_settings
|
||||
from mox3.mox import IsA # noqa
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.api import cinder
|
||||
from openstack_dashboard.api import keystone
|
||||
from openstack_dashboard.dashboards.project.volumes.volumes \
|
||||
import tables as volume_tables
|
||||
from openstack_dashboard.test import helpers as test
|
||||
|
||||
|
||||
INDEX_URL = reverse('horizon:admin:volumes:index')
|
||||
|
||||
|
||||
class VolumeTests(test.BaseAdminViewTests):
|
||||
|
||||
@test.create_stubs({api.nova: ('server_list',),
|
||||
cinder: ('volume_list',
|
||||
cinder: ('volume_list_paged',
|
||||
'volume_snapshot_list'),
|
||||
keystone: ('tenant_list',)})
|
||||
def test_index(self):
|
||||
cinder.volume_list(IsA(http.HttpRequest), search_opts={
|
||||
'all_tenants': True}).AndReturn(self.cinder_volumes.list())
|
||||
cinder.volume_list_paged(IsA(http.HttpRequest), sort_dir="desc",
|
||||
marker=None, paginate=True,
|
||||
search_opts={'all_tenants': True})\
|
||||
.AndReturn([self.cinder_volumes.list(), False, False])
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest), search_opts={
|
||||
'all_tenants': True}).AndReturn([])
|
||||
api.nova.server_list(IsA(http.HttpRequest), search_opts={
|
||||
@ -39,12 +49,100 @@ class VolumeTests(test.BaseAdminViewTests):
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
|
||||
self.mox.ReplayAll()
|
||||
res = self.client.get(reverse('horizon:admin:volumes:index'))
|
||||
res = self.client.get(INDEX_URL)
|
||||
|
||||
self.assertTemplateUsed(res, 'admin/volumes/index.html')
|
||||
volumes = res.context['volumes_table'].data
|
||||
self.assertItemsEqual(volumes, self.cinder_volumes.list())
|
||||
|
||||
@test.create_stubs({api.nova: ('server_list',),
|
||||
cinder: ('volume_list_paged',),
|
||||
keystone: ('tenant_list',)})
|
||||
def _test_index_paginated(self, marker, sort_dir, volumes, url,
|
||||
has_more, has_prev):
|
||||
cinder.volume_list_paged(IsA(http.HttpRequest), sort_dir=sort_dir,
|
||||
marker=marker, paginate=True,
|
||||
search_opts={'all_tenants': True}) \
|
||||
.AndReturn([volumes, has_more, has_prev])
|
||||
api.nova.server_list(IsA(http.HttpRequest), search_opts={
|
||||
'all_tenants': True}) \
|
||||
.AndReturn([self.servers.list(), False])
|
||||
keystone.tenant_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(url)
|
||||
|
||||
self.assertTemplateUsed(res, 'admin/volumes/index.html')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
self.mox.UnsetStubs()
|
||||
return res
|
||||
|
||||
@override_settings(API_RESULT_PAGE_SIZE=2)
|
||||
def test_index_paginated(self):
|
||||
size = settings.API_RESULT_PAGE_SIZE
|
||||
mox_volumes = self.cinder_volumes.list()
|
||||
|
||||
# get first page
|
||||
expected_volumes = mox_volumes[:size]
|
||||
url = INDEX_URL
|
||||
res = self._test_index_paginated(marker=None, sort_dir="desc",
|
||||
volumes=expected_volumes, url=url,
|
||||
has_more=True, has_prev=False)
|
||||
volumes = res.context['volumes_table'].data
|
||||
self.assertItemsEqual(volumes, expected_volumes)
|
||||
|
||||
# get second page
|
||||
expected_volumes = mox_volumes[size:2 * size]
|
||||
marker = expected_volumes[0].id
|
||||
next = volume_tables.VolumesTable._meta.pagination_param
|
||||
url = "?".join([INDEX_URL, "=".join([next, marker])])
|
||||
res = self._test_index_paginated(marker=marker, sort_dir="desc",
|
||||
volumes=expected_volumes, url=url,
|
||||
has_more=True, has_prev=True)
|
||||
volumes = res.context['volumes_table'].data
|
||||
self.assertItemsEqual(volumes, expected_volumes)
|
||||
|
||||
# get last page
|
||||
expected_volumes = mox_volumes[-size:]
|
||||
marker = expected_volumes[0].id
|
||||
next = volume_tables.VolumesTable._meta.pagination_param
|
||||
url = "?".join([INDEX_URL, "=".join([next, marker])])
|
||||
res = self._test_index_paginated(marker=marker, sort_dir="desc",
|
||||
volumes=expected_volumes, url=url,
|
||||
has_more=False, has_prev=True)
|
||||
volumes = res.context['volumes_table'].data
|
||||
self.assertItemsEqual(volumes, expected_volumes)
|
||||
|
||||
@override_settings(API_RESULT_PAGE_SIZE=2)
|
||||
def test_index_paginated_prev(self):
|
||||
size = settings.API_RESULT_PAGE_SIZE
|
||||
mox_volumes = self.cinder_volumes.list()
|
||||
|
||||
# prev from some page
|
||||
expected_volumes = mox_volumes[size:2 * size]
|
||||
marker = mox_volumes[0].id
|
||||
prev = volume_tables.VolumesTable._meta.prev_pagination_param
|
||||
url = "?".join([INDEX_URL, "=".join([prev, marker])])
|
||||
res = self._test_index_paginated(marker=marker, sort_dir="asc",
|
||||
volumes=expected_volumes, url=url,
|
||||
has_more=False, has_prev=True)
|
||||
volumes = res.context['volumes_table'].data
|
||||
self.assertItemsEqual(volumes, expected_volumes)
|
||||
|
||||
# back to first page
|
||||
expected_volumes = mox_volumes[:size]
|
||||
marker = mox_volumes[0].id
|
||||
prev = volume_tables.VolumesTable._meta.prev_pagination_param
|
||||
url = "?".join([INDEX_URL, "=".join([prev, marker])])
|
||||
res = self._test_index_paginated(marker=marker, sort_dir="asc",
|
||||
volumes=expected_volumes, url=url,
|
||||
has_more=True, has_prev=False)
|
||||
volumes = res.context['volumes_table'].data
|
||||
self.assertItemsEqual(volumes, expected_volumes)
|
||||
|
||||
@test.create_stubs({cinder: ('volume_type_list_with_qos_associations',
|
||||
'qos_spec_list',
|
||||
'extension_supported',
|
||||
|
@ -30,10 +30,21 @@ from openstack_dashboard.dashboards.project.volumes.volumes \
|
||||
|
||||
|
||||
class VolumeTableMixIn(object):
|
||||
_has_more_data = False
|
||||
_has_prev_data = False
|
||||
|
||||
def _get_volumes(self, search_opts=None):
|
||||
try:
|
||||
return api.cinder.volume_list(self.request,
|
||||
search_opts=search_opts)
|
||||
marker, sort_dir = self._get_marker()
|
||||
volumes, self._has_more_data, self._has_prev_data = \
|
||||
api.cinder.volume_list_paged(self.request, marker=marker,
|
||||
search_opts=search_opts,
|
||||
sort_dir=sort_dir, paginate=True)
|
||||
|
||||
if sort_dir == "asc":
|
||||
volumes.reverse()
|
||||
|
||||
return volumes
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve volume list.'))
|
||||
@ -78,6 +89,18 @@ class VolumeTableMixIn(object):
|
||||
server_id = att.get('server_id', None)
|
||||
att['instance'] = instances.get(server_id, None)
|
||||
|
||||
def _get_marker(self):
|
||||
prev_marker = self.request.GET.get(
|
||||
volume_tables.VolumesTable._meta.prev_pagination_param, None)
|
||||
if prev_marker:
|
||||
return prev_marker, "asc"
|
||||
else:
|
||||
marker = self.request.GET.get(
|
||||
volume_tables.VolumesTable._meta.pagination_param, None)
|
||||
if marker:
|
||||
return marker, "desc"
|
||||
return None, "desc"
|
||||
|
||||
|
||||
class VolumeTab(tabs.TableTab, VolumeTableMixIn):
|
||||
table_classes = (volume_tables.VolumesTable,)
|
||||
@ -94,6 +117,12 @@ class VolumeTab(tabs.TableTab, VolumeTableMixIn):
|
||||
volumes, instances, volume_ids_with_snapshots)
|
||||
return volumes
|
||||
|
||||
def has_prev_data(self, table):
|
||||
return self._has_prev_data
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._has_more_data
|
||||
|
||||
|
||||
class SnapshotTab(tabs.TableTab):
|
||||
table_classes = (vol_snapshot_tables.VolumeSnapshotsTable,)
|
||||
|
@ -12,12 +12,16 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django import http
|
||||
from django.test.utils import override_settings
|
||||
|
||||
from mox3.mox import IsA # noqa
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.dashboards.project.volumes.volumes \
|
||||
import tables as volume_tables
|
||||
from openstack_dashboard.test import helpers as test
|
||||
|
||||
|
||||
@ -29,6 +33,7 @@ VOLUME_BACKUPS_TAB_URL = reverse('horizon:project:volumes:backups_tab')
|
||||
class VolumeAndSnapshotsTests(test.TestCase):
|
||||
@test.create_stubs({api.cinder: ('tenant_absolute_limits',
|
||||
'volume_list',
|
||||
'volume_list_paged',
|
||||
'volume_snapshot_list',
|
||||
'volume_backup_supported',
|
||||
'volume_backup_list',
|
||||
@ -41,8 +46,10 @@ class VolumeAndSnapshotsTests(test.TestCase):
|
||||
|
||||
api.cinder.volume_backup_supported(IsA(http.HttpRequest)).\
|
||||
MultipleTimes().AndReturn(backup_supported)
|
||||
api.cinder.volume_list(IsA(http.HttpRequest), search_opts=None).\
|
||||
AndReturn(volumes)
|
||||
api.cinder.volume_list_paged(IsA(http.HttpRequest), marker=None,
|
||||
sort_dir='desc', search_opts=None,
|
||||
paginate=True).\
|
||||
AndReturn([volumes, False, False])
|
||||
api.nova.server_list(IsA(http.HttpRequest), search_opts=None).\
|
||||
AndReturn([self.servers.list(), False])
|
||||
api.cinder.volume_snapshot_list(
|
||||
@ -77,3 +84,94 @@ class VolumeAndSnapshotsTests(test.TestCase):
|
||||
|
||||
def test_index_backup_not_supported(self):
|
||||
self._test_index(backup_supported=False)
|
||||
|
||||
@test.create_stubs({api.cinder: ('tenant_absolute_limits',
|
||||
'volume_list_paged',
|
||||
'volume_backup_supported',
|
||||
),
|
||||
api.nova: ('server_list',)})
|
||||
def _test_index_paginated(self, marker, sort_dir, volumes, url,
|
||||
has_more, has_prev):
|
||||
backup_supported = True
|
||||
|
||||
api.cinder.volume_backup_supported(IsA(http.HttpRequest)).\
|
||||
MultipleTimes().AndReturn(backup_supported)
|
||||
api.cinder.volume_list_paged(IsA(http.HttpRequest), marker=marker,
|
||||
sort_dir=sort_dir, search_opts=None,
|
||||
paginate=True).\
|
||||
AndReturn([volumes, has_more, has_prev])
|
||||
api.nova.server_list(IsA(http.HttpRequest), search_opts=None).\
|
||||
AndReturn([self.servers.list(), False])
|
||||
api.cinder.tenant_absolute_limits(IsA(http.HttpRequest)).MultipleTimes().\
|
||||
AndReturn(self.cinder_limits['absolute'])
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(url)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertTemplateUsed(res, 'project/volumes/index.html')
|
||||
|
||||
self.mox.UnsetStubs()
|
||||
return res
|
||||
|
||||
@override_settings(API_RESULT_PAGE_SIZE=2)
|
||||
def test_index_paginated(self):
|
||||
mox_volumes = self.cinder_volumes.list()
|
||||
size = settings.API_RESULT_PAGE_SIZE
|
||||
|
||||
# get first page
|
||||
expected_volumes = mox_volumes[:size]
|
||||
url = INDEX_URL
|
||||
res = self._test_index_paginated(marker=None, sort_dir="desc",
|
||||
volumes=expected_volumes, url=url,
|
||||
has_more=True, has_prev=False)
|
||||
volumes = res.context['volumes_table'].data
|
||||
self.assertItemsEqual(volumes, expected_volumes)
|
||||
|
||||
# get second page
|
||||
expected_volumes = mox_volumes[size:2 * size]
|
||||
marker = expected_volumes[0].id
|
||||
next = volume_tables.VolumesTable._meta.pagination_param
|
||||
url = "?".join([INDEX_URL, "=".join([next, marker])])
|
||||
res = self._test_index_paginated(marker=marker, sort_dir="desc",
|
||||
volumes=expected_volumes, url=url,
|
||||
has_more=True, has_prev=True)
|
||||
volumes = res.context['volumes_table'].data
|
||||
self.assertItemsEqual(volumes, expected_volumes)
|
||||
|
||||
# get last page
|
||||
expected_volumes = mox_volumes[-size:]
|
||||
marker = expected_volumes[0].id
|
||||
next = volume_tables.VolumesTable._meta.pagination_param
|
||||
url = "?".join([INDEX_URL, "=".join([next, marker])])
|
||||
res = self._test_index_paginated(marker=marker, sort_dir="desc",
|
||||
volumes=expected_volumes, url=url,
|
||||
has_more=False, has_prev=True)
|
||||
volumes = res.context['volumes_table'].data
|
||||
self.assertItemsEqual(volumes, expected_volumes)
|
||||
|
||||
@override_settings(API_RESULT_PAGE_SIZE=2)
|
||||
def test_index_paginated_prev_page(self):
|
||||
mox_volumes = self.cinder_volumes.list()
|
||||
size = settings.API_RESULT_PAGE_SIZE
|
||||
|
||||
# prev from some page
|
||||
expected_volumes = mox_volumes[size:2 * size]
|
||||
marker = expected_volumes[0].id
|
||||
prev = volume_tables.VolumesTable._meta.prev_pagination_param
|
||||
url = "?".join([INDEX_URL, "=".join([prev, marker])])
|
||||
res = self._test_index_paginated(marker=marker, sort_dir="asc",
|
||||
volumes=expected_volumes, url=url,
|
||||
has_more=True, has_prev=True)
|
||||
volumes = res.context['volumes_table'].data
|
||||
self.assertItemsEqual(volumes, expected_volumes)
|
||||
|
||||
# back to first page
|
||||
expected_volumes = mox_volumes[:size]
|
||||
marker = expected_volumes[0].id
|
||||
prev = volume_tables.VolumesTable._meta.prev_pagination_param
|
||||
url = "?".join([INDEX_URL, "=".join([prev, marker])])
|
||||
res = self._test_index_paginated(marker=marker, sort_dir="asc",
|
||||
volumes=expected_volumes, url=url,
|
||||
has_more=True, has_prev=False)
|
||||
volumes = res.context['volumes_table'].data
|
||||
self.assertItemsEqual(volumes, expected_volumes)
|
||||
|
@ -42,6 +42,7 @@ class VolumeViewTests(test.TestCase):
|
||||
'volume_type_list',
|
||||
'volume_type_default',
|
||||
'volume_list',
|
||||
'volume_list_paged',
|
||||
'availability_zone_list',
|
||||
'extension_supported'),
|
||||
api.glance: ('image_list_detailed',),
|
||||
@ -853,7 +854,7 @@ class VolumeViewTests(test.TestCase):
|
||||
self.assertEqual(res.context['form'].errors['__all__'], expected_error)
|
||||
|
||||
@test.create_stubs({cinder: ('tenant_absolute_limits',
|
||||
'volume_list',
|
||||
'volume_list_paged',
|
||||
'volume_snapshot_list',
|
||||
'volume_backup_supported',
|
||||
'volume_delete',),
|
||||
@ -866,16 +867,18 @@ class VolumeViewTests(test.TestCase):
|
||||
|
||||
cinder.volume_backup_supported(IsA(http.HttpRequest)). \
|
||||
MultipleTimes().AndReturn(True)
|
||||
cinder.volume_list(IsA(http.HttpRequest), search_opts=None).\
|
||||
AndReturn(volumes)
|
||||
cinder.volume_list_paged(
|
||||
IsA(http.HttpRequest), marker=None, paginate=True, sort_dir='desc',
|
||||
search_opts=None).AndReturn([volumes, False, False])
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest),
|
||||
search_opts=None).\
|
||||
AndReturn([])
|
||||
cinder.volume_delete(IsA(http.HttpRequest), volume.id)
|
||||
api.nova.server_list(IsA(http.HttpRequest), search_opts=None).\
|
||||
AndReturn([self.servers.list(), False])
|
||||
cinder.volume_list(IsA(http.HttpRequest), search_opts=None).\
|
||||
AndReturn(volumes)
|
||||
cinder.volume_list_paged(
|
||||
IsA(http.HttpRequest), marker=None, paginate=True, sort_dir='desc',
|
||||
search_opts=None).AndReturn([volumes, False, False])
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest),
|
||||
search_opts=None).\
|
||||
AndReturn([])
|
||||
@ -1086,7 +1089,7 @@ class VolumeViewTests(test.TestCase):
|
||||
'The create snapshot button should be disabled')
|
||||
|
||||
@test.create_stubs({cinder: ('tenant_absolute_limits',
|
||||
'volume_list',
|
||||
'volume_list_paged',
|
||||
'volume_snapshot_list',
|
||||
'volume_backup_supported',),
|
||||
api.nova: ('server_list',)})
|
||||
@ -1098,8 +1101,9 @@ class VolumeViewTests(test.TestCase):
|
||||
|
||||
api.cinder.volume_backup_supported(IsA(http.HttpRequest)). \
|
||||
MultipleTimes().AndReturn(True)
|
||||
cinder.volume_list(IsA(http.HttpRequest), search_opts=None)\
|
||||
.AndReturn(volumes)
|
||||
cinder.volume_list_paged(IsA(http.HttpRequest), sort_dir='desc',
|
||||
marker=None, paginate=True, search_opts=None)\
|
||||
.AndReturn([volumes, False, False])
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest),
|
||||
search_opts=None).\
|
||||
AndReturn([])
|
||||
@ -1127,7 +1131,7 @@ class VolumeViewTests(test.TestCase):
|
||||
create_action.policy_rules)
|
||||
|
||||
@test.create_stubs({cinder: ('tenant_absolute_limits',
|
||||
'volume_list',
|
||||
'volume_list_paged',
|
||||
'volume_snapshot_list',
|
||||
'volume_backup_supported',),
|
||||
api.nova: ('server_list',)})
|
||||
@ -1138,8 +1142,9 @@ class VolumeViewTests(test.TestCase):
|
||||
|
||||
api.cinder.volume_backup_supported(IsA(http.HttpRequest)). \
|
||||
MultipleTimes().AndReturn(True)
|
||||
cinder.volume_list(IsA(http.HttpRequest), search_opts=None)\
|
||||
.AndReturn(volumes)
|
||||
cinder.volume_list_paged(
|
||||
IsA(http.HttpRequest), marker=None, paginate=True, sort_dir='desc',
|
||||
search_opts=None).AndReturn([volumes, False, False])
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest),
|
||||
search_opts=None).\
|
||||
AndReturn([])
|
||||
@ -1515,7 +1520,7 @@ class VolumeViewTests(test.TestCase):
|
||||
def test_encryption_true(self):
|
||||
self._test_encryption(True)
|
||||
|
||||
@test.create_stubs({cinder: ('volume_list',
|
||||
@test.create_stubs({cinder: ('volume_list_paged',
|
||||
'volume_snapshot_list',
|
||||
'volume_backup_supported',
|
||||
'tenant_absolute_limits'),
|
||||
@ -1528,8 +1533,11 @@ class VolumeViewTests(test.TestCase):
|
||||
|
||||
cinder.volume_backup_supported(IsA(http.HttpRequest))\
|
||||
.MultipleTimes('backup_supported').AndReturn(False)
|
||||
cinder.volume_list(IsA(http.HttpRequest), search_opts=None)\
|
||||
.AndReturn(self.volumes.list())
|
||||
|
||||
cinder.volume_list_paged(
|
||||
IsA(http.HttpRequest), marker=None, sort_dir='desc',
|
||||
search_opts=None, paginate=True)\
|
||||
.AndReturn([self.volumes.list(), False, False])
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest),
|
||||
search_opts=None).\
|
||||
AndReturn(self.cinder_volume_snapshots.list())
|
||||
@ -1580,7 +1588,7 @@ class VolumeViewTests(test.TestCase):
|
||||
"only have 80GiB of your quota available.")
|
||||
|
||||
@test.create_stubs({cinder: ('volume_backup_supported',
|
||||
'volume_list',
|
||||
'volume_list_paged',
|
||||
'volume_snapshot_list',
|
||||
'tenant_absolute_limits'),
|
||||
api.nova: ('server_list',)})
|
||||
@ -1589,8 +1597,10 @@ class VolumeViewTests(test.TestCase):
|
||||
|
||||
cinder.volume_backup_supported(IsA(http.HttpRequest))\
|
||||
.MultipleTimes().AndReturn(False)
|
||||
cinder.volume_list(IsA(http.HttpRequest), search_opts=None)\
|
||||
.AndReturn(self.volumes.list())
|
||||
cinder.volume_list_paged(
|
||||
IsA(http.HttpRequest), marker=None, sort_dir='desc',
|
||||
search_opts=None, paginate=True)\
|
||||
.AndReturn([self.volumes.list(), False, False])
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest),
|
||||
search_opts=None).\
|
||||
AndReturn([])
|
||||
@ -1632,7 +1642,7 @@ class VolumeViewTests(test.TestCase):
|
||||
self.assertNoFormErrors(res)
|
||||
|
||||
@test.create_stubs({cinder: ('volume_backup_supported',
|
||||
'volume_list',
|
||||
'volume_list_paged',
|
||||
'volume_snapshot_list',
|
||||
'transfer_delete',
|
||||
'tenant_absolute_limits'),
|
||||
@ -1652,8 +1662,10 @@ class VolumeViewTests(test.TestCase):
|
||||
|
||||
cinder.volume_backup_supported(IsA(http.HttpRequest))\
|
||||
.MultipleTimes().AndReturn(False)
|
||||
cinder.volume_list(IsA(http.HttpRequest), search_opts=None)\
|
||||
.AndReturn(volumes)
|
||||
cinder.volume_list_paged(
|
||||
IsA(http.HttpRequest), marker=None, search_opts=None,
|
||||
sort_dir='desc', paginate=True)\
|
||||
.AndReturn([volumes, False, False])
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest),
|
||||
search_opts=None).\
|
||||
AndReturn([])
|
||||
|
@ -62,6 +62,21 @@
|
||||
* @param {Object} params
|
||||
* Query parameters. Optional.
|
||||
*
|
||||
* @param {boolean} params.paginate
|
||||
* True to paginate automatically.
|
||||
*
|
||||
* @param {string} params.marker
|
||||
* Specifies the image of the last-seen image.
|
||||
*
|
||||
* The typical pattern of limit and marker is to make an
|
||||
* initial limited request and then to use the last
|
||||
* image from the response as the marker parameter
|
||||
* in a subsequent limited request. With paginate, limit
|
||||
* is automatically set.
|
||||
*
|
||||
* @param {string} params.sort_dir
|
||||
* The sort direction ('asc' or 'desc').
|
||||
*
|
||||
* @param {string} param.search_opts
|
||||
* Filters to pass through the API.
|
||||
* For example, "status": "available" will show all available volumes.
|
||||
|
@ -42,19 +42,21 @@ class CinderRestTestCase(test.TestCase):
|
||||
request = self.mock_rest_request(GET={'all_projects': 'true'})
|
||||
else:
|
||||
request = self.mock_rest_request(**{'GET': filters})
|
||||
cc.volume_list.return_value = [
|
||||
cc.volume_list_paged.return_value = [
|
||||
mock.Mock(**{'to_dict.return_value': {'id': 'one'}}),
|
||||
mock.Mock(**{'to_dict.return_value': {'id': 'two'}}),
|
||||
]
|
||||
], False, False
|
||||
response = cinder.Volumes().get(request)
|
||||
self.assertStatusCode(response, 200)
|
||||
self.assertEqual(response.json,
|
||||
{"items": [{"id": "one"}, {"id": "two"}]})
|
||||
{"items": [{"id": "one"}, {"id": "two"}],
|
||||
"has_more_data": False,
|
||||
"has_prev_data": False})
|
||||
if all:
|
||||
cc.volume_list.assert_called_once_with(request,
|
||||
cc.volume_list_paged.assert_called_once_with(request,
|
||||
{'all_tenants': 1})
|
||||
else:
|
||||
cc.volume_list.assert_called_once_with(request,
|
||||
cc.volume_list_paged.assert_called_once_with(request,
|
||||
search_opts=filters)
|
||||
|
||||
@mock.patch.object(cinder.api, 'cinder')
|
||||
|
@ -12,6 +12,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf import settings
|
||||
from django.test.utils import override_settings
|
||||
import six
|
||||
|
||||
@ -37,8 +38,182 @@ class CinderApiTests(test.APITestCase):
|
||||
search_opts=search_opts,).AndReturn(volume_transfers)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
# No assertions are necessary. Verification is handled by mox.
|
||||
api.cinder.volume_list(self.request, search_opts=search_opts)
|
||||
api_volumes = api.cinder.volume_list(self.request,
|
||||
search_opts=search_opts)
|
||||
self.assertEqual(len(volumes), len(api_volumes))
|
||||
|
||||
def test_volume_list_paged(self):
|
||||
search_opts = {'all_tenants': 1}
|
||||
detailed = True
|
||||
volumes = self.cinder_volumes.list()
|
||||
volume_transfers = self.cinder_volume_transfers.list()
|
||||
cinderclient = self.stub_cinderclient()
|
||||
cinderclient.volumes = self.mox.CreateMockAnything()
|
||||
cinderclient.volumes.list(search_opts=search_opts,).AndReturn(volumes)
|
||||
cinderclient.transfers = self.mox.CreateMockAnything()
|
||||
cinderclient.transfers.list(
|
||||
detailed=detailed,
|
||||
search_opts=search_opts,).AndReturn(volume_transfers)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
api_volumes, has_more, has_prev = api.cinder.volume_list_paged(
|
||||
self.request, search_opts=search_opts)
|
||||
self.assertEqual(len(volumes), len(api_volumes))
|
||||
self.assertFalse(has_more)
|
||||
self.assertFalse(has_prev)
|
||||
|
||||
@override_settings(API_RESULT_PAGE_SIZE=2)
|
||||
@override_settings(OPENSTACK_API_VERSIONS={'volume': 2})
|
||||
def test_volume_list_paginate_first_page(self):
|
||||
api.cinder.VERSIONS._active = None
|
||||
page_size = settings.API_RESULT_PAGE_SIZE
|
||||
volumes = self.cinder_volumes.list()
|
||||
volume_transfers = self.cinder_volume_transfers.list()
|
||||
|
||||
search_opts = {'all_tenants': 1}
|
||||
mox_volumes = volumes[:page_size + 1]
|
||||
expected_volumes = mox_volumes[:-1]
|
||||
|
||||
cinderclient = self.stub_cinderclient()
|
||||
cinderclient.volumes = self.mox.CreateMockAnything()
|
||||
cinderclient.volumes.list(search_opts=search_opts, limit=page_size + 1,
|
||||
sort='created_at:desc', marker=None).\
|
||||
AndReturn(mox_volumes)
|
||||
cinderclient.transfers = self.mox.CreateMockAnything()
|
||||
cinderclient.transfers.list(
|
||||
detailed=True,
|
||||
search_opts=search_opts,).AndReturn(volume_transfers)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
api_volumes, more_data, prev_data = api.cinder.volume_list_paged(
|
||||
self.request, search_opts=search_opts, paginate=True)
|
||||
self.assertEqual(len(expected_volumes), len(api_volumes))
|
||||
self.assertTrue(more_data)
|
||||
self.assertFalse(prev_data)
|
||||
|
||||
@override_settings(API_RESULT_PAGE_SIZE=2)
|
||||
@override_settings(OPENSTACK_API_VERSIONS={'volume': 2})
|
||||
def test_volume_list_paginate_second_page(self):
|
||||
api.cinder.VERSIONS._active = None
|
||||
page_size = settings.API_RESULT_PAGE_SIZE
|
||||
volumes = self.cinder_volumes.list()
|
||||
volume_transfers = self.cinder_volume_transfers.list()
|
||||
|
||||
search_opts = {'all_tenants': 1}
|
||||
mox_volumes = volumes[page_size:page_size * 2 + 1]
|
||||
expected_volumes = mox_volumes[:-1]
|
||||
marker = expected_volumes[0].id
|
||||
|
||||
cinderclient = self.stub_cinderclient()
|
||||
cinderclient.volumes = self.mox.CreateMockAnything()
|
||||
cinderclient.volumes.list(search_opts=search_opts, limit=page_size + 1,
|
||||
sort='created_at:desc', marker=marker).\
|
||||
AndReturn(mox_volumes)
|
||||
cinderclient.transfers = self.mox.CreateMockAnything()
|
||||
cinderclient.transfers.list(
|
||||
detailed=True,
|
||||
search_opts=search_opts,).AndReturn(volume_transfers)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
api_volumes, more_data, prev_data = api.cinder.volume_list_paged(
|
||||
self.request, search_opts=search_opts, marker=marker,
|
||||
paginate=True)
|
||||
self.assertEqual(len(expected_volumes), len(api_volumes))
|
||||
self.assertTrue(more_data)
|
||||
self.assertTrue(prev_data)
|
||||
|
||||
@override_settings(API_RESULT_PAGE_SIZE=2)
|
||||
@override_settings(OPENSTACK_API_VERSIONS={'volume': 2})
|
||||
def test_volume_list_paginate_last_page(self):
|
||||
api.cinder.VERSIONS._active = None
|
||||
page_size = settings.API_RESULT_PAGE_SIZE
|
||||
volumes = self.cinder_volumes.list()
|
||||
volume_transfers = self.cinder_volume_transfers.list()
|
||||
|
||||
search_opts = {'all_tenants': 1}
|
||||
mox_volumes = volumes[-1 * page_size:]
|
||||
expected_volumes = mox_volumes
|
||||
marker = expected_volumes[0].id
|
||||
|
||||
cinderclient = self.stub_cinderclient()
|
||||
cinderclient.volumes = self.mox.CreateMockAnything()
|
||||
cinderclient.volumes.list(search_opts=search_opts, limit=page_size + 1,
|
||||
sort='created_at:desc', marker=marker).\
|
||||
AndReturn(mox_volumes)
|
||||
cinderclient.transfers = self.mox.CreateMockAnything()
|
||||
cinderclient.transfers.list(
|
||||
detailed=True,
|
||||
search_opts=search_opts,).AndReturn(volume_transfers)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
api_volumes, more_data, prev_data = api.cinder.volume_list_paged(
|
||||
self.request, search_opts=search_opts, marker=marker,
|
||||
paginate=True)
|
||||
self.assertEqual(len(expected_volumes), len(api_volumes))
|
||||
self.assertFalse(more_data)
|
||||
self.assertTrue(prev_data)
|
||||
|
||||
@override_settings(API_RESULT_PAGE_SIZE=2)
|
||||
@override_settings(OPENSTACK_API_VERSIONS={'volume': 2})
|
||||
def test_volume_list_paginate_back_from_some_page(self):
|
||||
api.cinder.VERSIONS._active = None
|
||||
page_size = settings.API_RESULT_PAGE_SIZE
|
||||
volumes = self.cinder_volumes.list()
|
||||
volume_transfers = self.cinder_volume_transfers.list()
|
||||
|
||||
search_opts = {'all_tenants': 1}
|
||||
mox_volumes = volumes[page_size:page_size * 2 + 1]
|
||||
expected_volumes = mox_volumes[:-1]
|
||||
marker = expected_volumes[0].id
|
||||
|
||||
cinderclient = self.stub_cinderclient()
|
||||
cinderclient.volumes = self.mox.CreateMockAnything()
|
||||
cinderclient.volumes.list(search_opts=search_opts, limit=page_size + 1,
|
||||
sort='created_at:asc', marker=marker).\
|
||||
AndReturn(mox_volumes)
|
||||
cinderclient.transfers = self.mox.CreateMockAnything()
|
||||
cinderclient.transfers.list(
|
||||
detailed=True,
|
||||
search_opts=search_opts,).AndReturn(volume_transfers)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
api_volumes, more_data, prev_data = api.cinder.volume_list_paged(
|
||||
self.request, search_opts=search_opts, sort_dir="asc",
|
||||
marker=marker, paginate=True)
|
||||
self.assertEqual(len(expected_volumes), len(api_volumes))
|
||||
self.assertTrue(more_data)
|
||||
self.assertTrue(prev_data)
|
||||
|
||||
@override_settings(API_RESULT_PAGE_SIZE=2)
|
||||
@override_settings(OPENSTACK_API_VERSIONS={'volume': 2})
|
||||
def test_volume_list_paginate_back_to_first_page(self):
|
||||
api.cinder.VERSIONS._active = None
|
||||
page_size = settings.API_RESULT_PAGE_SIZE
|
||||
volumes = self.cinder_volumes.list()
|
||||
volume_transfers = self.cinder_volume_transfers.list()
|
||||
|
||||
search_opts = {'all_tenants': 1}
|
||||
mox_volumes = volumes[:page_size]
|
||||
expected_volumes = mox_volumes
|
||||
marker = expected_volumes[0].id
|
||||
|
||||
cinderclient = self.stub_cinderclient()
|
||||
cinderclient.volumes = self.mox.CreateMockAnything()
|
||||
cinderclient.volumes.list(search_opts=search_opts, limit=page_size + 1,
|
||||
sort='created_at:asc', marker=marker).\
|
||||
AndReturn(mox_volumes)
|
||||
cinderclient.transfers = self.mox.CreateMockAnything()
|
||||
cinderclient.transfers.list(
|
||||
detailed=True,
|
||||
search_opts=search_opts,).AndReturn(volume_transfers)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
api_volumes, more_data, prev_data = api.cinder.volume_list_paged(
|
||||
self.request, search_opts=search_opts, sort_dir="asc",
|
||||
marker=marker, paginate=True)
|
||||
self.assertEqual(len(expected_volumes), len(api_volumes))
|
||||
self.assertTrue(more_data)
|
||||
self.assertFalse(prev_data)
|
||||
|
||||
def test_volume_snapshot_list(self):
|
||||
search_opts = {'all_tenants': 1}
|
||||
|
Loading…
Reference in New Issue
Block a user