Merge "Backport of Folsom Glance pagination review." into stable/essex
This commit is contained in:
commit
67a07bca8a
|
@ -25,6 +25,7 @@ import logging
|
|||
import urlparse
|
||||
|
||||
from django.utils.decorators import available_attrs
|
||||
from django.conf import settings
|
||||
|
||||
from glance import client as glance_client
|
||||
from glance.common import exception as glance_exception
|
||||
|
@ -122,8 +123,17 @@ def image_get_meta(request, image_id):
|
|||
|
||||
|
||||
@catch_glance_exception
|
||||
def image_list_detailed(request):
|
||||
return [Image(i) for i in glanceclient(request).get_images_detailed()]
|
||||
def image_list_detailed(request, marker=None, filters=None):
|
||||
filters = filters or {}
|
||||
limit = getattr(settings, 'API_RESULT_LIMIT', 1000)
|
||||
image_dicts = glanceclient(request).get_images_detailed(limit=limit + 1,
|
||||
marker=marker,
|
||||
filters=filters)
|
||||
images = [Image(i) for i in image_dicts]
|
||||
if(len(images) > limit):
|
||||
return (images[0:-1], True)
|
||||
else:
|
||||
return (images, False)
|
||||
|
||||
|
||||
@catch_glance_exception
|
||||
|
@ -134,9 +144,15 @@ def image_update(request, image_id, image_meta=None):
|
|||
|
||||
|
||||
@catch_glance_exception
|
||||
def snapshot_list_detailed(request):
|
||||
filters = {}
|
||||
filters['property-image_type'] = 'snapshot'
|
||||
filters['is_public'] = 'none'
|
||||
return [Image(i) for i in glanceclient(request)
|
||||
.get_images_detailed(filters=filters)]
|
||||
def snapshot_list_detailed(request, marker=None, extra_filters=None):
|
||||
filters = {'property-image_type': 'snapshot'}
|
||||
filters.update(extra_filters or {})
|
||||
limit = getattr(settings, 'API_RESULT_LIMIT', 1000)
|
||||
image_dicts = glanceclient(request).get_images_detailed(limit=limit + 1,
|
||||
marker=marker,
|
||||
filters=filters)
|
||||
images = [Image(i) for i in image_dicts]
|
||||
if(len(images) > limit):
|
||||
return (images[0:-1], True)
|
||||
else:
|
||||
return (images, False)
|
||||
|
|
|
@ -99,3 +99,4 @@ class ImagesTable(tables.DataTable):
|
|||
verbose_name = _("Images")
|
||||
table_actions = (DeleteImage,)
|
||||
row_actions = (LaunchImage, EditImage, DeleteImage)
|
||||
pagination_param = "image_marker"
|
||||
|
|
|
@ -35,3 +35,4 @@ class SnapshotsTable(ImagesTable):
|
|||
verbose_name = _("Instance Snapshots")
|
||||
table_actions = (DeleteSnapshot,)
|
||||
row_actions = (LaunchImage, EditImage, DeleteSnapshot)
|
||||
pagination_param = "snapshot_marker"
|
||||
|
|
|
@ -38,8 +38,13 @@ class ImagesAndSnapshotsTests(test.TestCase):
|
|||
snapshots = self.snapshots.list()
|
||||
self.mox.StubOutWithMock(api, 'image_list_detailed')
|
||||
self.mox.StubOutWithMock(api, 'snapshot_list_detailed')
|
||||
api.image_list_detailed(IsA(http.HttpRequest)).AndReturn(images)
|
||||
api.snapshot_list_detailed(IsA(http.HttpRequest)).AndReturn(snapshots)
|
||||
self.mox.StubOutWithMock(api, 'volume_snapshot_list')
|
||||
api.volume_snapshot_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.volumes.list())
|
||||
api.image_list_detailed(IsA(http.HttpRequest),
|
||||
marker=None).AndReturn([images, False])
|
||||
api.snapshot_list_detailed(IsA(http.HttpRequest),
|
||||
marker=None).AndReturn([snapshots, False])
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(INDEX_URL)
|
||||
|
@ -53,9 +58,13 @@ class ImagesAndSnapshotsTests(test.TestCase):
|
|||
def test_index_no_images(self):
|
||||
self.mox.StubOutWithMock(api, 'snapshot_list_detailed')
|
||||
self.mox.StubOutWithMock(api, 'image_list_detailed')
|
||||
api.image_list_detailed(IsA(http.HttpRequest)).AndReturn([])
|
||||
api.snapshot_list_detailed(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.snapshots.list())
|
||||
self.mox.StubOutWithMock(api, 'volume_snapshot_list')
|
||||
api.volume_snapshot_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.volumes.list())
|
||||
api.image_list_detailed(IsA(http.HttpRequest),
|
||||
marker=None).AndReturn([(), False])
|
||||
api.snapshot_list_detailed(IsA(http.HttpRequest), marker=None) \
|
||||
.AndReturn([self.snapshots.list(), False])
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(INDEX_URL)
|
||||
|
@ -65,9 +74,13 @@ class ImagesAndSnapshotsTests(test.TestCase):
|
|||
self.mox.StubOutWithMock(api, 'image_list_detailed')
|
||||
self.mox.StubOutWithMock(api, 'snapshot_list_detailed')
|
||||
exc = glance_exception.ClientConnectionError('clientConnError')
|
||||
api.image_list_detailed(IsA(http.HttpRequest)).AndRaise(exc)
|
||||
api.snapshot_list_detailed(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.snapshots.list())
|
||||
self.mox.StubOutWithMock(api, 'volume_snapshot_list')
|
||||
api.volume_snapshot_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.volumes.list())
|
||||
api.image_list_detailed(IsA(http.HttpRequest),
|
||||
marker=None).AndRaise(exc)
|
||||
api.snapshot_list_detailed(IsA(http.HttpRequest), marker=None) \
|
||||
.AndReturn([self.snapshots.list(), False])
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(INDEX_URL)
|
||||
|
@ -86,9 +99,13 @@ class ImagesAndSnapshotsTests(test.TestCase):
|
|||
new_snapshots = [snapshot1, snapshot2]
|
||||
self.mox.StubOutWithMock(api, 'image_list_detailed')
|
||||
self.mox.StubOutWithMock(api, 'snapshot_list_detailed')
|
||||
api.image_list_detailed(IsA(http.HttpRequest)).AndReturn(images)
|
||||
api.snapshot_list_detailed(IsA(http.HttpRequest)).\
|
||||
AndReturn(new_snapshots)
|
||||
self.mox.StubOutWithMock(api, 'volume_snapshot_list')
|
||||
api.volume_snapshot_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.volumes.list())
|
||||
api.image_list_detailed(IsA(http.HttpRequest),
|
||||
marker=None).AndReturn([images, False])
|
||||
api.snapshot_list_detailed(IsA(http.HttpRequest), marker=None) \
|
||||
.AndReturn([new_snapshots, False])
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(INDEX_URL)
|
||||
|
|
|
@ -42,9 +42,16 @@ class IndexView(tables.MultiTableView):
|
|||
table_classes = (ImagesTable, SnapshotsTable, VolumeSnapshotsTable)
|
||||
template_name = 'nova/images_and_snapshots/index.html'
|
||||
|
||||
def has_more_data(self, table):
|
||||
return getattr(self, "_more_%s" % table.name, False)
|
||||
|
||||
def get_images_data(self):
|
||||
marker = self.request.GET.get(ImagesTable._meta.pagination_param, None)
|
||||
try:
|
||||
all_images = api.image_list_detailed(self.request)
|
||||
# FIXME(gabriel): The paging is going to be strange here due to
|
||||
# our filtering after the fact.
|
||||
all_images, _more_images = api.image_list_detailed(self.request,
|
||||
marker=marker)
|
||||
images = [im for im in all_images
|
||||
if im['container_format'] not in ['aki', 'ari'] and
|
||||
getattr(im.properties, "image_type", '') != "snapshot"]
|
||||
|
@ -54,12 +61,15 @@ class IndexView(tables.MultiTableView):
|
|||
return images
|
||||
|
||||
def get_snapshots_data(self):
|
||||
req = self.request
|
||||
marker = req.GET.get(SnapshotsTable._meta.pagination_param, None)
|
||||
try:
|
||||
snapshots = api.snapshot_list_detailed(self.request)
|
||||
snaps, self._more_snapshots = api.snapshot_list_detailed(req,
|
||||
marker=marker)
|
||||
except:
|
||||
snapshots = []
|
||||
exceptions.handle(self.request, _("Unable to retrieve snapshots."))
|
||||
return snapshots
|
||||
snaps = []
|
||||
exceptions.handle(req, _("Unable to retrieve snapshots."))
|
||||
return snaps
|
||||
|
||||
def get_volume_snapshots_data(self):
|
||||
try:
|
||||
|
|
|
@ -287,8 +287,10 @@ class InstanceViewTests(test.TestCase):
|
|||
server.id,
|
||||
"snapshot1")
|
||||
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
||||
api.snapshot_list_detailed(IsA(http.HttpRequest)).AndReturn([])
|
||||
api.image_list_detailed(IsA(http.HttpRequest)).AndReturn([])
|
||||
api.snapshot_list_detailed(IsA(http.HttpRequest), marker=None) \
|
||||
.AndReturn(([], False))
|
||||
api.image_list_detailed(IsA(http.HttpRequest), marker=None) \
|
||||
.AndReturn(([], False))
|
||||
api.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([])
|
||||
|
||||
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
|
||||
|
|
|
@ -37,11 +37,18 @@ class IndexView(tables.DataTableView):
|
|||
table_class = AdminImagesTable
|
||||
template_name = 'syspanel/images/index.html'
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._more
|
||||
|
||||
def get_data(self):
|
||||
images = []
|
||||
marker = self.request.GET.get(AdminImagesTable._meta.pagination_param,
|
||||
None)
|
||||
try:
|
||||
images = api.image_list_detailed(self.request)
|
||||
images, self._more = api.image_list_detailed(self.request,
|
||||
marker=marker)
|
||||
except:
|
||||
self._more = False
|
||||
msg = _('Unable to retrieve image list.')
|
||||
exceptions.handle(self.request, msg)
|
||||
return images
|
||||
|
|
|
@ -558,6 +558,13 @@ class DataTableOptions(object):
|
|||
The name of the context variable which will contain the table when
|
||||
it is rendered. Defaults to ``"table"``.
|
||||
|
||||
.. attribute:: pagination_param
|
||||
|
||||
The name of the query string parameter which will be used when
|
||||
paginating this table. When using multiple tables in a single
|
||||
view this will need to be changed to differentiate between the
|
||||
tables. Default: ``"marker"``.
|
||||
|
||||
.. attribute:: status_columns
|
||||
|
||||
A list or tuple of column names which represents the "state"
|
||||
|
@ -592,6 +599,7 @@ class DataTableOptions(object):
|
|||
self.row_actions = getattr(options, 'row_actions', [])
|
||||
self.row_class = getattr(options, 'row_class', Row)
|
||||
self.column_class = getattr(options, 'column_class', Column)
|
||||
self.pagination_param = getattr(options, 'pagination_param', 'marker')
|
||||
|
||||
# Set self.filter if we have any FilterActions
|
||||
filter_actions = [action for action in self.table_actions if
|
||||
|
@ -1038,6 +1046,10 @@ class DataTable(object):
|
|||
"""
|
||||
return http.urlquote_plus(self.get_object_id(self.data[-1]))
|
||||
|
||||
def get_pagination_string(self):
|
||||
""" Returns the query parameter string to paginate this table. """
|
||||
return "=".join([self._meta.pagination_param, self.get_marker()])
|
||||
|
||||
def calculate_row_status(self, statuses):
|
||||
"""
|
||||
Returns a boolean value determining the overall row status
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<span>{% blocktrans count counter=rows|length %}Displaying {{ counter }} item{% plural %}Displaying {{ counter }} items{% endblocktrans %}</span>
|
||||
{% if table.has_more_data %}
|
||||
<span class="spacer">|</span>
|
||||
<a href="?marker={{ table.get_marker }}">More »</a>
|
||||
<a href="?{{ table.get_pagination_string }}">More »</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</td>
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
# under the License.
|
||||
|
||||
from glance.common import exception as glance_exception
|
||||
from django.conf import settings
|
||||
|
||||
from horizon import api
|
||||
from horizon import test
|
||||
|
@ -48,15 +49,14 @@ class GlanceApiTests(test.APITestCase):
|
|||
def test_image_list_detailed(self):
|
||||
""" Verify "list" returns our custom Image class. """
|
||||
images = self.images.list()
|
||||
|
||||
glanceclient = self.stub_glanceclient()
|
||||
glanceclient.get_images_detailed().AndReturn(images)
|
||||
glanceclient.get_images_detailed(filters={}, limit=1001, marker=None) \
|
||||
.AndReturn([images, False])
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.image_list_detailed(self.request)
|
||||
ret_val, _more = api.image_list_detailed(self.request)
|
||||
for image in ret_val:
|
||||
self.assertIsInstance(image, api.glance.Image)
|
||||
self.assertIn(image._apidict, images)
|
||||
|
||||
def test_glance_exception_wrapping_for_internal_server_errors(self):
|
||||
"""
|
||||
|
@ -66,8 +66,8 @@ class GlanceApiTests(test.APITestCase):
|
|||
"""
|
||||
# TODO(johnp): Remove once Bug 952618 is fixed in the glance client.
|
||||
glanceclient = self.stub_glanceclient()
|
||||
glanceclient.get_images_detailed().AndRaise(
|
||||
Exception("Internal Server error: "))
|
||||
glanceclient.get_images_detailed(filters={}, limit=1001, marker=None) \
|
||||
.AndRaise(Exception("Internal Server error: "))
|
||||
self.mox.ReplayAll()
|
||||
|
||||
with self.assertRaises(glance_exception.ClientConnectionError):
|
||||
|
@ -80,8 +80,9 @@ class GlanceApiTests(test.APITestCase):
|
|||
"""
|
||||
# TODO(johnp): Remove once Bug 952618 is fixed in the glance client.
|
||||
glanceclient = self.stub_glanceclient()
|
||||
glanceclient.get_images_detailed().AndRaise(
|
||||
Exception("Unknown error occurred! 503 Service Unavailable"))
|
||||
exc = Exception("Unknown error occurred! 503 Service Unavailable")
|
||||
glanceclient.get_images_detailed(filters={}, limit=1001, marker=None) \
|
||||
.AndRaise(exc)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
with self.assertRaises(glance_exception.ClientConnectionError):
|
||||
|
|
Loading…
Reference in New Issue