Fix excessive volume_get requests in snaps table

The volume snapshots table has a column that shows the associated volume
name and links to its volume detail page. This calls volume_get twice
for each snapshot, so loading the Images & Snapshots page takes forever
if you have a decent number of snapshots.

This commit instead gets the volume list at the same time as the
volume snapshot list, and stores the volume as an attribute on
the snapshot so it can be easily retrieved later.

Closes-bug: 1218259
Change-Id: I004e50aae51a6ed7549da41967bc42babfa6cb45
This commit is contained in:
Kieran Spear 2013-09-18 15:28:40 +10:00
parent e063659d66
commit f4d56fb134
6 changed files with 56 additions and 51 deletions

View File

@ -36,22 +36,16 @@ INDEX_URL = reverse('horizon:project:images_and_snapshots:index')
class ImagesAndSnapshotsTests(test.TestCase):
@test.create_stubs({api.glance: ('image_list_detailed',),
api.cinder: ('volume_snapshot_list', 'volume_get')})
api.cinder: ('volume_snapshot_list',
'volume_list',)})
def test_index(self):
images = self.images.list()
vol_snaps = self.volume_snapshots.list()
volumes = self.volumes.list()
for volume in volumes:
volume.volume_id = volume.id
for volume in volumes:
api.cinder.volume_get(IsA(http.HttpRequest), volume.volume_id) \
.AndReturn(volume)
for volume in volumes:
api.cinder.volume_get(IsA(http.HttpRequest), volume.volume_id) \
.AndReturn(volume)
api.cinder.volume_get(IsA(http.HttpRequest), volume.volume_id)
api.cinder.volume_snapshot_list(IsA(http.HttpRequest)) \
.AndReturn(vol_snaps)
api.cinder.volume_list(IsA(http.HttpRequest)) \
.AndReturn(volumes)
api.glance.image_list_detailed(IsA(http.HttpRequest),
marker=None).AndReturn([images, False])
@ -77,21 +71,15 @@ class ImagesAndSnapshotsTests(test.TestCase):
self.assertTrue(len(row_actions), 3)
@test.create_stubs({api.glance: ('image_list_detailed',),
api.cinder: ('volume_snapshot_list', 'volume_get')})
api.cinder: ('volume_snapshot_list',
'volume_list',)})
def test_index_no_images(self):
vol_snaps = self.volume_snapshots.list()
volumes = self.volumes.list()
for volume in volumes:
volume.volume_id = volume.id
for volume in volumes:
api.cinder.volume_get(IsA(http.HttpRequest), volume.volume_id) \
.AndReturn(volume)
for volume in volumes:
api.cinder.volume_get(IsA(http.HttpRequest), volume.volume_id) \
.AndReturn(volume)
api.cinder.volume_get(IsA(http.HttpRequest), volume.volume_id)
api.cinder.volume_snapshot_list(IsA(http.HttpRequest)) \
.AndReturn(vol_snaps)
api.cinder.volume_list(IsA(http.HttpRequest)) \
.AndReturn(volumes)
api.glance.image_list_detailed(IsA(http.HttpRequest),
marker=None).AndReturn([(), False])
@ -101,21 +89,15 @@ class ImagesAndSnapshotsTests(test.TestCase):
self.assertTemplateUsed(res, 'project/images_and_snapshots/index.html')
@test.create_stubs({api.glance: ('image_list_detailed',),
api.cinder: ('volume_snapshot_list', 'volume_get')})
api.cinder: ('volume_snapshot_list',
'volume_list',)})
def test_index_error(self):
vol_snaps = self.volume_snapshots.list()
volumes = self.volumes.list()
for volume in volumes:
volume.volume_id = volume.id
for volume in volumes:
api.cinder.volume_get(IsA(http.HttpRequest), volume.volume_id) \
.AndReturn(volume)
for volume in volumes:
api.cinder.volume_get(IsA(http.HttpRequest), volume.volume_id) \
.AndReturn(volume)
api.cinder.volume_get(IsA(http.HttpRequest), volume.volume_id)
api.cinder.volume_snapshot_list(IsA(http.HttpRequest)) \
.AndReturn(vol_snaps)
api.cinder.volume_list(IsA(http.HttpRequest)) \
.AndReturn(volumes)
api.glance.image_list_detailed(IsA(http.HttpRequest),
marker=None) \
@ -126,22 +108,16 @@ class ImagesAndSnapshotsTests(test.TestCase):
self.assertTemplateUsed(res, 'project/images_and_snapshots/index.html')
@test.create_stubs({api.glance: ('image_list_detailed',),
api.cinder: ('volume_snapshot_list', 'volume_get')})
api.cinder: ('volume_snapshot_list',
'volume_list',)})
def test_snapshot_actions(self):
snapshots = self.snapshots.list()
vol_snaps = self.volume_snapshots.list()
volumes = self.volumes.list()
for volume in volumes:
volume.volume_id = volume.id
for volume in volumes:
api.cinder.volume_get(IsA(http.HttpRequest), volume.volume_id) \
.AndReturn(volume)
for volume in volumes:
api.cinder.volume_get(IsA(http.HttpRequest), volume.volume_id) \
.AndReturn(volume)
api.cinder.volume_get(IsA(http.HttpRequest), volume.volume_id)
api.cinder.volume_snapshot_list(IsA(http.HttpRequest)) \
.AndReturn(vol_snaps)
api.cinder.volume_list(IsA(http.HttpRequest)) \
.AndReturn(volumes)
api.glance.image_list_detailed(IsA(http.HttpRequest), marker=None) \
.AndReturn([snapshots, False])

View File

@ -72,10 +72,18 @@ class IndexView(tables.MultiTableView):
if base.is_service_enabled(self.request, 'volume'):
try:
snapshots = api.cinder.volume_snapshot_list(self.request)
volumes = api.cinder.volume_list(self.request)
volumes = dict((v.id, v) for v in volumes)
except Exception:
snapshots = []
volumes = {}
exceptions.handle(self.request, _("Unable to retrieve "
"volume snapshots."))
for snapshot in snapshots:
volume = volumes.get(snapshot.volume_id)
setattr(snapshot, '_volume', volume)
else:
snapshots = []
return snapshots

View File

@ -62,20 +62,24 @@ class UpdateRow(tables.Row):
def get_data(self, request, snapshot_id):
snapshot = cinder.volume_snapshot_get(request, snapshot_id)
snapshot._volume = cinder.volume_get(request, snapshot.volume_id)
return snapshot
class SnapshotVolumeNameColumn(tables.Column):
def get_raw_data(self, snapshot):
request = self.table.request
volume_name = api.cinder.volume_get(request,
snapshot.volume_id).display_name
volume = snapshot._volume
if volume:
volume_name = volume.display_name or volume.id
else:
volume_name = _("Unknown")
return safestring.mark_safe(volume_name)
def get_link_url(self, snapshot):
volume_id = api.cinder.volume_get(self.table.request,
snapshot.volume_id).id
return reverse(self.link, args=(volume_id,))
volume = snapshot._volume
if volume:
volume_id = volume.id
return reverse(self.link, args=(volume_id,))
class VolumeSnapshotsTable(volume_tables.VolumesTableBase):

View File

@ -73,20 +73,27 @@ class VolumeSnapshotsViewTests(test.TestCase):
@test.create_stubs({api.glance: ('image_list_detailed',),
api.cinder: ('volume_snapshot_list',
'volume_list',
'volume_snapshot_delete')})
def test_delete_volume_snapshot(self):
vol_snapshots = self.volume_snapshots.list()
volumes = self.volumes.list()
snapshot = self.volume_snapshots.first()
api.glance.image_list_detailed(IsA(http.HttpRequest),
marker=None).AndReturn(([], False))
api.cinder.volume_snapshot_list(IsA(http.HttpRequest)). \
AndReturn(vol_snapshots)
api.cinder.volume_list(IsA(http.HttpRequest)) \
.AndReturn(volumes)
api.cinder.volume_snapshot_delete(IsA(http.HttpRequest), snapshot.id)
api.glance.image_list_detailed(IsA(http.HttpRequest),
marker=None).AndReturn(([], False))
api.cinder.volume_snapshot_list(IsA(http.HttpRequest)). \
AndReturn([])
api.cinder.volume_list(IsA(http.HttpRequest)) \
.AndReturn(volumes)
self.mox.ReplayAll()
formData = {'action':

View File

@ -659,7 +659,8 @@ class InstanceTests(test.TestCase):
'server_list',
'flavor_list',
'server_delete'),
cinder: ('volume_snapshot_list',),
cinder: ('volume_snapshot_list',
'volume_list',),
api.glance: ('image_list_detailed',)})
def test_create_instance_snapshot(self):
server = self.servers.first()
@ -672,6 +673,7 @@ class InstanceTests(test.TestCase):
api.glance.image_list_detailed(IsA(http.HttpRequest),
marker=None).AndReturn([[], False])
cinder.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([])
cinder.volume_list(IsA(http.HttpRequest)).AndReturn([])
self.mox.ReplayAll()

View File

@ -501,7 +501,15 @@ def data(TEST):
'size': 40,
'status': 'available',
'volume_id': '41023e92-8008-4c8b-8059-7f2293ff3775'})
volume_snapshot2 = vol_snaps.Snapshot(vol_snaps.SnapshotManager(None),
{'id': 'a374cbb8-3f99-4c3f-a2ef-3edbec842e31',
'display_name': '',
'display_description': 'vol snap 2!',
'size': 80,
'status': 'available',
'volume_id': '3b189ac8-9166-ac7f-90c9-16c8bf9e01ac'})
TEST.volume_snapshots.add(volume_snapshot)
TEST.volume_snapshots.add(volume_snapshot2)
cert_data = {'private_key': 'private',
'data': 'certificate_data'}