diff --git a/openstack_dashboard/api/cinder.py b/openstack_dashboard/api/cinder.py index 3cd74c8c55..4fe009a1c9 100644 --- a/openstack_dashboard/api/cinder.py +++ b/openstack_dashboard/api/cinder.py @@ -202,6 +202,23 @@ def volume_list(request, search_opts=None, marker=None, sort_dir="desc"): return volumes +def update_pagination(entities, page_size, marker, sort_dir): + has_more_data, has_prev_data = False, False + if len(entities) > page_size: + has_more_data = True + entities.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 + + return entities, has_more_data, has_prev_data + + 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 @@ -231,17 +248,8 @@ def volume_list_paged(request, search_opts=None, marker=None, paginate=False, 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 + volumes, has_more_data, has_prev_data = update_pagination( + volumes, page_size, marker, sort_dir) else: for v in c_client.volumes.list(search_opts=search_opts): v.transfer = transfers.get(v.id) @@ -346,11 +354,40 @@ def volume_snapshot_get(request, snapshot_id): def volume_snapshot_list(request, search_opts=None): + snapshots, _, __ = volume_snapshot_list_paged(request, + search_opts=search_opts, + paginate=False) + return snapshots + + +def volume_snapshot_list_paged(request, search_opts=None, marker=None, + paginate=False, sort_dir="desc"): + has_more_data = False + has_prev_data = False + snapshots = [] c_client = cinderclient(request) if c_client is None: - return [] - return [VolumeSnapshot(s) for s in c_client.volume_snapshots.list( - search_opts=search_opts)] + return snapshots, has_more_data, has_more_data + + 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 s in c_client.volume_snapshots.list(search_opts=search_opts, + limit=page_size + 1, + marker=marker, + sort=sort): + snapshots.append(VolumeSnapshot(s)) + + snapshots, has_more_data, has_prev_data = update_pagination( + snapshots, page_size, marker, sort_dir) + else: + for s in c_client.volume_snapshots.list(search_opts=search_opts): + snapshots.append(VolumeSnapshot(s)) + + return snapshots, has_more_data, has_prev_data def volume_snapshot_create(request, volume_id, name, @@ -399,10 +436,38 @@ def volume_backup_get(request, backup_id): def volume_backup_list(request): + backups, _, __ = volume_backup_list_paged(request, paginate=False) + return backups + + +def volume_backup_list_paged(request, marker=None, paginate=False, + sort_dir="desc"): + has_more_data = False + has_prev_data = False + backups = [] + c_client = cinderclient(request) if c_client is None: - return [] - return [VolumeBackup(b) for b in c_client.backups.list()] + return backups, has_more_data, has_prev_data + + 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 b in c_client.backups.list(limit=page_size + 1, + marker=marker, + sort=sort): + backups.append(VolumeBackup(b)) + + backups, has_more_data, has_prev_data = update_pagination( + backups, page_size, marker, sort_dir) + else: + for b in c_client.backups.list(): + backups.append(VolumeBackup(b)) + + return backups, has_more_data, has_prev_data def volume_backup_create(request, diff --git a/openstack_dashboard/dashboards/admin/volumes/snapshots/tables.py b/openstack_dashboard/dashboards/admin/volumes/snapshots/tables.py index 44b90942e1..b5902aa6f2 100644 --- a/openstack_dashboard/dashboards/admin/volumes/snapshots/tables.py +++ b/openstack_dashboard/dashboards/admin/volumes/snapshots/tables.py @@ -67,6 +67,8 @@ class VolumeSnapshotsTable(volumes_tables.VolumesTableBase): class Meta(object): name = "volume_snapshots" verbose_name = _("Volume Snapshots") + pagination_param = 'snapshot_marker' + prev_pagination_param = 'prev_snapshot_marker' table_actions = (snapshots_tables.VolumeSnapshotsFilterAction, snapshots_tables.DeleteVolumeSnapshot,) row_actions = (snapshots_tables.DeleteVolumeSnapshot, diff --git a/openstack_dashboard/dashboards/admin/volumes/tabs.py b/openstack_dashboard/dashboards/admin/volumes/tabs.py index bb6fb69ce5..b822eed060 100644 --- a/openstack_dashboard/dashboards/admin/volumes/tabs.py +++ b/openstack_dashboard/dashboards/admin/volumes/tabs.py @@ -30,7 +30,8 @@ from openstack_dashboard.dashboards.project.volumes \ import tabs as volumes_tabs -class VolumeTab(tabs.TableTab, volumes_tabs.VolumeTableMixIn): +class VolumeTab(volumes_tabs.PagedTableMixin, tabs.TableTab, + volumes_tabs.VolumeTableMixIn): table_classes = (volumes_tables.VolumesTable,) name = _("Volumes") slug = "volumes_tab" @@ -61,12 +62,6 @@ 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, @@ -116,7 +111,7 @@ class VolumeTypesTab(tabs.TableTab, volumes_tabs.VolumeTableMixIn): return qos_specs -class SnapshotTab(tabs.TableTab): +class SnapshotTab(volumes_tabs.PagedTableMixin, tabs.TableTab): table_classes = (snapshots_tables.VolumeSnapshotsTable,) name = _("Volume Snapshots") slug = "snapshots_tab" @@ -126,9 +121,11 @@ class SnapshotTab(tabs.TableTab): def get_volume_snapshots_data(self): if api.base.is_service_enabled(self.request, 'volume'): try: - snapshots = cinder.volume_snapshot_list( - self.request, - search_opts={'all_tenants': True}) + marker, sort_dir = self._get_marker() + snapshots, self._has_more_data, self._has_prev_data = \ + cinder.volume_snapshot_list_paged( + self.request, paginate=True, marker=marker, + sort_dir=sort_dir, search_opts={'all_tenants': True}) volumes = cinder.volume_list( self.request, search_opts={'all_tenants': True}) diff --git a/openstack_dashboard/dashboards/admin/volumes/tests.py b/openstack_dashboard/dashboards/admin/volumes/tests.py index 51ba9eaa07..ee71c56eb3 100644 --- a/openstack_dashboard/dashboards/admin/volumes/tests.py +++ b/openstack_dashboard/dashboards/admin/volumes/tests.py @@ -21,6 +21,8 @@ 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.snapshots \ + import tables as snapshot_tables from openstack_dashboard.dashboards.project.volumes.volumes \ import tables as volume_tables from openstack_dashboard.test import helpers as test @@ -174,12 +176,13 @@ class VolumeTests(test.BaseAdminViewTests): self.assertItemsEqual(qos_specs, self.cinder_qos_specs.list()) @test.create_stubs({cinder: ('volume_list', - 'volume_snapshot_list',), + 'volume_snapshot_list_paged',), keystone: ('tenant_list',)}) def test_snapshots_tab(self): - cinder.volume_snapshot_list(IsA(http.HttpRequest), search_opts={ - 'all_tenants': True}). \ - AndReturn(self.cinder_volume_snapshots.list()) + cinder.volume_snapshot_list_paged( + IsA(http.HttpRequest), paginate=True, marker=None, sort_dir='desc', + search_opts={'all_tenants': True},).AndReturn( + [self.cinder_volume_snapshots.list(), False, False]) cinder.volume_list(IsA(http.HttpRequest), search_opts={ 'all_tenants': True}).\ AndReturn(self.cinder_volumes.list()) @@ -193,3 +196,90 @@ class VolumeTests(test.BaseAdminViewTests): self.assertTemplateUsed(res, 'horizon/common/_detail_table.html') snapshots = res.context['volume_snapshots_table'].data self.assertItemsEqual(snapshots, self.cinder_volume_snapshots.list()) + + @test.create_stubs({cinder: ('volume_list', + 'volume_snapshot_list_paged',), + keystone: ('tenant_list',)}) + def _test_snapshots_index_paginated(self, marker, sort_dir, snapshots, url, + has_more, has_prev): + cinder.volume_snapshot_list_paged( + IsA(http.HttpRequest), paginate=True, marker=marker, + sort_dir=sort_dir, search_opts={'all_tenants': True}) \ + .AndReturn([snapshots, has_more, has_prev]) + cinder.volume_list(IsA(http.HttpRequest), search_opts={ + 'all_tenants': True}).\ + AndReturn(self.cinder_volumes.list()) + 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=1) + def test_snapshots_index_paginated(self): + size = settings.API_RESULT_PAGE_SIZE + mox_snapshots = self.cinder_volume_snapshots.list() + base_url = reverse('horizon:admin:volumes:snapshots_tab') + next = snapshot_tables.VolumeSnapshotsTable._meta.pagination_param + + # get first page + expected_snapshots = mox_snapshots[:size] + res = self._test_snapshots_index_paginated( + marker=None, sort_dir="desc", snapshots=expected_snapshots, + url=base_url, has_more=True, has_prev=False) + snapshots = res.context['volume_snapshots_table'].data + self.assertItemsEqual(snapshots, expected_snapshots) + + # get second page + expected_snapshots = mox_snapshots[size:2 * size] + marker = expected_snapshots[0].id + url = "&".join([base_url, "=".join([next, marker])]) + res = self._test_snapshots_index_paginated( + marker=marker, sort_dir="desc", snapshots=expected_snapshots, + url=url, has_more=True, has_prev=True) + snapshots = res.context['volume_snapshots_table'].data + self.assertItemsEqual(snapshots, expected_snapshots) + + # get last page + expected_snapshots = mox_snapshots[-size:] + marker = expected_snapshots[0].id + url = "&".join([base_url, "=".join([next, marker])]) + res = self._test_snapshots_index_paginated( + marker=marker, sort_dir="desc", snapshots=expected_snapshots, + url=url, has_more=False, has_prev=True) + snapshots = res.context['volume_snapshots_table'].data + self.assertItemsEqual(snapshots, expected_snapshots) + + @override_settings(API_RESULT_PAGE_SIZE=1) + def test_snapshots_index_paginated_prev(self): + size = settings.API_RESULT_PAGE_SIZE + max_snapshots = self.cinder_volume_snapshots.list() + base_url = reverse('horizon:admin:volumes:snapshots_tab') + prev = snapshot_tables.VolumeSnapshotsTable._meta.prev_pagination_param + + # prev from some page + expected_snapshots = max_snapshots[size:2 * size] + marker = max_snapshots[0].id + url = "&".join([base_url, "=".join([prev, marker])]) + res = self._test_snapshots_index_paginated( + marker=marker, sort_dir="asc", snapshots=expected_snapshots, + url=url, has_more=False, has_prev=True) + snapshots = res.context['volume_snapshots_table'].data + self.assertItemsEqual(snapshots, expected_snapshots) + + # back to first page + expected_snapshots = max_snapshots[:size] + marker = max_snapshots[0].id + url = "&".join([base_url, "=".join([prev, marker])]) + res = self._test_snapshots_index_paginated( + marker=marker, sort_dir="asc", snapshots=expected_snapshots, + url=url, has_more=True, has_prev=False) + snapshots = res.context['volume_snapshots_table'].data + self.assertItemsEqual(snapshots, expected_snapshots) diff --git a/openstack_dashboard/dashboards/project/volumes/backups/tables.py b/openstack_dashboard/dashboards/project/volumes/backups/tables.py index 20223c9331..c9c7b67a0c 100644 --- a/openstack_dashboard/dashboards/project/volumes/backups/tables.py +++ b/openstack_dashboard/dashboards/project/volumes/backups/tables.py @@ -139,6 +139,8 @@ class BackupsTable(tables.DataTable): class Meta(object): name = "volume_backups" verbose_name = _("Volume Backups") + pagination_param = 'backup_marker' + prev_pagination_param = 'prev_backup_marker' status_columns = ("status",) row_class = UpdateRow table_actions = (DeleteBackup,) diff --git a/openstack_dashboard/dashboards/project/volumes/backups/tests.py b/openstack_dashboard/dashboards/project/volumes/backups/tests.py index b6cb915be1..4e5e3576c7 100644 --- a/openstack_dashboard/dashboards/project/volumes/backups/tests.py +++ b/openstack_dashboard/dashboards/project/volumes/backups/tests.py @@ -54,7 +54,7 @@ class VolumeBackupsViewTests(test.TestCase): @test.create_stubs({api.cinder: ('volume_list', 'volume_backup_supported', - 'volume_backup_list', + 'volume_backup_list_paged', 'volume_backup_delete')}) def test_delete_volume_backup(self): vol_backups = self.cinder_volume_backups.list() @@ -63,14 +63,16 @@ class VolumeBackupsViewTests(test.TestCase): api.cinder.volume_backup_supported(IsA(http.HttpRequest)). \ MultipleTimes().AndReturn(True) - api.cinder.volume_backup_list(IsA(http.HttpRequest)). \ - AndReturn(vol_backups) + api.cinder.volume_backup_list_paged( + IsA(http.HttpRequest), marker=None, sort_dir='desc', + paginate=True).AndReturn([vol_backups, False, False]) api.cinder.volume_list(IsA(http.HttpRequest)). \ AndReturn(volumes) api.cinder.volume_backup_delete(IsA(http.HttpRequest), backup.id) - api.cinder.volume_backup_list(IsA(http.HttpRequest)). \ - AndReturn(vol_backups) + api.cinder.volume_backup_list_paged( + IsA(http.HttpRequest), marker=None, sort_dir='desc', + paginate=True).AndReturn([vol_backups, False, False]) api.cinder.volume_list(IsA(http.HttpRequest)). \ AndReturn(volumes) self.mox.ReplayAll() diff --git a/openstack_dashboard/dashboards/project/volumes/snapshots/tables.py b/openstack_dashboard/dashboards/project/volumes/snapshots/tables.py index f9caeef7f8..4c38e0580e 100644 --- a/openstack_dashboard/dashboards/project/volumes/snapshots/tables.py +++ b/openstack_dashboard/dashboards/project/volumes/snapshots/tables.py @@ -154,6 +154,8 @@ class VolumeSnapshotsTable(volume_tables.VolumesTableBase): class Meta(object): name = "volume_snapshots" verbose_name = _("Volume Snapshots") + pagination_param = 'snapshot_marker' + prev_pagination_param = 'prev_snapshot_marker' table_actions = (VolumeSnapshotsFilterAction, DeleteVolumeSnapshot,) row_actions = (CreateVolumeFromSnapshot, LaunchSnapshot, EditVolumeSnapshot, DeleteVolumeSnapshot) diff --git a/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py b/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py index 6eca73bd2f..a8cc74187d 100644 --- a/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py +++ b/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py @@ -105,7 +105,7 @@ class VolumeSnapshotsViewTests(test.TestCase): res = self.client.post(url, formData) self.assertRedirectsNoFollow(res, VOLUME_SNAPSHOTS_TAB_URL) - @test.create_stubs({api.cinder: ('volume_snapshot_list', + @test.create_stubs({api.cinder: ('volume_snapshot_list_paged', 'volume_list', 'volume_backup_supported', 'volume_snapshot_delete')}) @@ -116,14 +116,16 @@ class VolumeSnapshotsViewTests(test.TestCase): api.cinder.volume_backup_supported(IsA(http.HttpRequest)). \ MultipleTimes().AndReturn(True) - api.cinder.volume_snapshot_list(IsA(http.HttpRequest)). \ - AndReturn(vol_snapshots) + api.cinder.volume_snapshot_list_paged( + IsA(http.HttpRequest), paginate=True, marker=None, + sort_dir='desc').AndReturn([vol_snapshots, False, False]) api.cinder.volume_list(IsA(http.HttpRequest)). \ AndReturn(volumes) api.cinder.volume_snapshot_delete(IsA(http.HttpRequest), snapshot.id) - api.cinder.volume_snapshot_list(IsA(http.HttpRequest)). \ - AndReturn([]) + api.cinder.volume_snapshot_list_paged( + IsA(http.HttpRequest), paginate=True, marker=None, + sort_dir='desc').AndReturn([[], False, False]) api.cinder.volume_list(IsA(http.HttpRequest)). \ AndReturn(volumes) self.mox.ReplayAll() diff --git a/openstack_dashboard/dashboards/project/volumes/tabs.py b/openstack_dashboard/dashboards/project/volumes/tabs.py index 38673cf09a..10737aef87 100644 --- a/openstack_dashboard/dashboards/project/volumes/tabs.py +++ b/openstack_dashboard/dashboards/project/volumes/tabs.py @@ -89,20 +89,32 @@ class VolumeTableMixIn(object): server_id = att.get('server_id', None) att['instance'] = instances.get(server_id, None) + +class PagedTableMixin(object): + def __init__(self, *args, **kwargs): + super(PagedTableMixin, self).__init__(*args, **kwargs) + self._has_prev_data = False + self._has_more_data = False + + def has_prev_data(self, table): + return self._has_prev_data + + def has_more_data(self, table): + return self._has_more_data + def _get_marker(self): - prev_marker = self.request.GET.get( - volume_tables.VolumesTable._meta.prev_pagination_param, None) + meta = self.table_classes[0]._meta + prev_marker = self.request.GET.get(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) + marker = self.request.GET.get(meta.pagination_param, None) if marker: return marker, "desc" return None, "desc" -class VolumeTab(tabs.TableTab, VolumeTableMixIn): +class VolumeTab(PagedTableMixin, tabs.TableTab, VolumeTableMixIn): table_classes = (volume_tables.VolumesTable,) name = _("Volumes") slug = "volumes_tab" @@ -117,14 +129,8 @@ 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): +class SnapshotTab(PagedTableMixin, tabs.TableTab): table_classes = (vol_snapshot_tables.VolumeSnapshotsTable,) name = _("Volume Snapshots") slug = "snapshots_tab" @@ -133,7 +139,11 @@ class SnapshotTab(tabs.TableTab): def get_volume_snapshots_data(self): try: - snapshots = api.cinder.volume_snapshot_list(self.request) + marker, sort_dir = self._get_marker() + snapshots, self._has_more_data, self._has_prev_data = \ + api.cinder.volume_snapshot_list_paged( + self.request, paginate=True, marker=marker, + sort_dir=sort_dir) volumes = api.cinder.volume_list(self.request) volumes = dict((v.id, v) for v in volumes) except Exception: @@ -149,7 +159,7 @@ class SnapshotTab(tabs.TableTab): return snapshots -class BackupsTab(tabs.TableTab, VolumeTableMixIn): +class BackupsTab(PagedTableMixin, tabs.TableTab, VolumeTableMixIn): table_classes = (backups_tables.BackupsTable,) name = _("Volume Backups") slug = "backups_tab" @@ -161,7 +171,11 @@ class BackupsTab(tabs.TableTab, VolumeTableMixIn): def get_volume_backups_data(self): try: - backups = api.cinder.volume_backup_list(self.request) + marker, sort_dir = self._get_marker() + backups, self._has_more_data, self._has_prev_data = \ + api.cinder.volume_backup_list_paged( + self.request, marker=marker, sort_dir=sort_dir, + paginate=True) volumes = api.cinder.volume_list(self.request) volumes = dict((v.id, v) for v in volumes) for backup in backups: diff --git a/openstack_dashboard/dashboards/project/volumes/test.py b/openstack_dashboard/dashboards/project/volumes/test.py index 8524f74bad..f9b27fef35 100644 --- a/openstack_dashboard/dashboards/project/volumes/test.py +++ b/openstack_dashboard/dashboards/project/volumes/test.py @@ -20,6 +20,10 @@ from django.test.utils import override_settings from mox3.mox import IsA # noqa from openstack_dashboard import api +from openstack_dashboard.dashboards.project.volumes.backups \ + import tables as backup_tables +from openstack_dashboard.dashboards.project.volumes.snapshots \ + import tables as snapshot_tables from openstack_dashboard.dashboards.project.volumes.volumes \ import tables as volume_tables from openstack_dashboard.test import helpers as test @@ -30,13 +34,14 @@ VOLUME_SNAPSHOTS_TAB_URL = reverse('horizon:project:volumes:snapshots_tab') VOLUME_BACKUPS_TAB_URL = reverse('horizon:project:volumes:backups_tab') -class VolumeAndSnapshotsTests(test.TestCase): +class VolumeAndSnapshotsAndBackupsTests(test.TestCase): @test.create_stubs({api.cinder: ('tenant_absolute_limits', 'volume_list', 'volume_list_paged', 'volume_snapshot_list', + 'volume_snapshot_list_paged', 'volume_backup_supported', - 'volume_backup_list', + 'volume_backup_list_paged', ), api.nova: ('server_list',)}) def _test_index(self, backup_supported=True): @@ -46,20 +51,23 @@ class VolumeAndSnapshotsTests(test.TestCase): api.cinder.volume_backup_supported(IsA(http.HttpRequest)).\ MultipleTimes().AndReturn(backup_supported) - api.cinder.volume_list_paged(IsA(http.HttpRequest), marker=None, - sort_dir='desc', search_opts=None, - paginate=True).\ + api.cinder.volume_list_paged( + IsA(http.HttpRequest), marker=None, search_opts=None, + sort_dir='desc', 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( - IsA(http.HttpRequest), search_opts=None).AndReturn(vol_snaps) api.cinder.volume_snapshot_list(IsA(http.HttpRequest)).\ AndReturn(vol_snaps) + + api.cinder.volume_snapshot_list_paged( + IsA(http.HttpRequest), paginate=True, marker=None, + sort_dir='desc').AndReturn([vol_snaps, False, False]) api.cinder.volume_list(IsA(http.HttpRequest)).AndReturn(volumes) if backup_supported: - api.cinder.volume_backup_list(IsA(http.HttpRequest)).\ - AndReturn(vol_backups) + api.cinder.volume_backup_list_paged( + IsA(http.HttpRequest), marker=None, sort_dir='desc', + paginate=True).AndReturn([vol_backups, False, False]) api.cinder.volume_list(IsA(http.HttpRequest)).AndReturn(volumes) api.cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\ MultipleTimes().AndReturn(self.cinder_limits['absolute']) @@ -175,3 +183,181 @@ class VolumeAndSnapshotsTests(test.TestCase): has_more=True, has_prev=False) volumes = res.context['volumes_table'].data self.assertItemsEqual(volumes, expected_volumes) + + @test.create_stubs({api.cinder: ('tenant_absolute_limits', + 'volume_snapshot_list_paged', + 'volume_list', + 'volume_backup_supported', + ), + api.nova: ('server_list',)}) + def _test_snapshots_index_paginated(self, marker, sort_dir, snapshots, url, + has_more, has_prev): + backup_supported = True + + api.cinder.volume_backup_supported(IsA(http.HttpRequest)).\ + MultipleTimes().AndReturn(backup_supported) + api.cinder.volume_snapshot_list_paged( + IsA(http.HttpRequest), marker=marker, sort_dir=sort_dir, + paginate=True).AndReturn([snapshots, has_more, has_prev]) + api.cinder.volume_list(IsA(http.HttpRequest)).AndReturn( + self.cinder_volumes.list()) + 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=1) + def test_snapshots_index_paginated(self): + mox_snapshots = self.cinder_volume_snapshots.list() + size = settings.API_RESULT_PAGE_SIZE + base_url = reverse('horizon:project:volumes:snapshots_tab') + next = snapshot_tables.VolumeSnapshotsTable._meta.pagination_param + + # get first page + expected_snapshots = mox_snapshots[:size] + res = self._test_snapshots_index_paginated( + marker=None, sort_dir="desc", snapshots=expected_snapshots, + url=base_url, has_more=True, has_prev=False) + snapshots = res.context['volume_snapshots_table'].data + self.assertItemsEqual(snapshots, expected_snapshots) + + # get second page + expected_snapshots = mox_snapshots[size:2 * size] + marker = expected_snapshots[0].id + + url = "&".join([base_url, "=".join([next, marker])]) + res = self._test_snapshots_index_paginated( + marker=marker, sort_dir="desc", snapshots=expected_snapshots, + url=url, has_more=True, has_prev=True) + snapshots = res.context['volume_snapshots_table'].data + self.assertItemsEqual(snapshots, expected_snapshots) + + # get last page + expected_snapshots = mox_snapshots[-size:] + marker = expected_snapshots[0].id + url = "&".join([base_url, "=".join([next, marker])]) + res = self._test_snapshots_index_paginated( + marker=marker, sort_dir="desc", snapshots=expected_snapshots, + url=url, has_more=False, has_prev=True) + snapshots = res.context['volume_snapshots_table'].data + self.assertItemsEqual(snapshots, expected_snapshots) + + @override_settings(API_RESULT_PAGE_SIZE=1) + def test_snapshots_index_paginated_prev_page(self): + mox_snapshots = self.cinder_volume_snapshots.list() + size = settings.API_RESULT_PAGE_SIZE + base_url = reverse('horizon:project:volumes:snapshots_tab') + prev = snapshot_tables.VolumeSnapshotsTable._meta.prev_pagination_param + + # prev from some page + expected_snapshots = mox_snapshots[size:2 * size] + marker = expected_snapshots[0].id + url = "&".join([base_url, "=".join([prev, marker])]) + res = self._test_snapshots_index_paginated( + marker=marker, sort_dir="asc", snapshots=expected_snapshots, + url=url, has_more=True, has_prev=True) + snapshots = res.context['volume_snapshots_table'].data + self.assertItemsEqual(snapshots, expected_snapshots) + + # back to first page + expected_snapshots = mox_snapshots[:size] + marker = expected_snapshots[0].id + url = "&".join([base_url, "=".join([prev, marker])]) + res = self._test_snapshots_index_paginated( + marker=marker, sort_dir="asc", snapshots=expected_snapshots, + url=url, has_more=True, has_prev=False) + snapshots = res.context['volume_snapshots_table'].data + self.assertItemsEqual(snapshots, expected_snapshots) + + @test.create_stubs({api.cinder: ('tenant_absolute_limits', + 'volume_backup_list_paged', + 'volume_list', + 'volume_backup_supported', + ), + api.nova: ('server_list',)}) + def _test_backups_index_paginated(self, marker, sort_dir, backups, url, + has_more, has_prev): + backup_supported = True + + api.cinder.volume_backup_supported(IsA(http.HttpRequest)).\ + MultipleTimes().AndReturn(backup_supported) + api.cinder.volume_backup_list_paged( + IsA(http.HttpRequest), marker=marker, sort_dir=sort_dir, + paginate=True).AndReturn([backups, has_more, has_prev]) + api.cinder.volume_list(IsA(http.HttpRequest)).AndReturn( + self.cinder_volumes.list()) + 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=1) + def test_backups_index_paginated(self): + mox_backups = self.cinder_volume_backups.list() + size = settings.API_RESULT_PAGE_SIZE + base_url = reverse('horizon:project:volumes:backups_tab') + next = backup_tables.BackupsTable._meta.pagination_param + + # get first page + expected_backups = mox_backups[:size] + res = self._test_backups_index_paginated( + marker=None, sort_dir="desc", backups=expected_backups, + url=base_url, has_more=True, has_prev=False) + backups = res.context['volume_backups_table'].data + self.assertItemsEqual(backups, expected_backups) + + # get second page + expected_backups = mox_backups[size:2 * size] + marker = expected_backups[0].id + + url = "&".join([base_url, "=".join([next, marker])]) + res = self._test_backups_index_paginated( + marker=marker, sort_dir="desc", backups=expected_backups, url=url, + has_more=True, has_prev=True) + backups = res.context['volume_backups_table'].data + self.assertItemsEqual(backups, expected_backups) + + # get last page + expected_backups = mox_backups[-size:] + marker = expected_backups[0].id + url = "&".join([base_url, "=".join([next, marker])]) + res = self._test_backups_index_paginated( + marker=marker, sort_dir="desc", backups=expected_backups, url=url, + has_more=False, has_prev=True) + backups = res.context['volume_backups_table'].data + self.assertItemsEqual(backups, expected_backups) + + @override_settings(API_RESULT_PAGE_SIZE=1) + def test_backups_index_paginated_prev_page(self): + mox_backups = self.cinder_volume_backups.list() + size = settings.API_RESULT_PAGE_SIZE + base_url = reverse('horizon:project:volumes:backups_tab') + prev = backup_tables.BackupsTable._meta.prev_pagination_param + + # prev from some page + expected_backups = mox_backups[size:2 * size] + marker = expected_backups[0].id + url = "&".join([base_url, "=".join([prev, marker])]) + res = self._test_backups_index_paginated( + marker=marker, sort_dir="asc", backups=expected_backups, url=url, + has_more=True, has_prev=True) + backups = res.context['volume_backups_table'].data + self.assertItemsEqual(backups, expected_backups) + + # back to first page + expected_backups = mox_backups[:size] + marker = expected_backups[0].id + url = "&".join([base_url, "=".join([prev, marker])]) + res = self._test_backups_index_paginated( + marker=marker, sort_dir="asc", backups=expected_backups, url=url, + has_more=True, has_prev=False) + backups = res.context['volume_backups_table'].data + self.assertItemsEqual(backups, expected_backups) diff --git a/openstack_dashboard/test/test_data/cinder_data.py b/openstack_dashboard/test/test_data/cinder_data.py index 360ba84b21..e8b41dd542 100644 --- a/openstack_dashboard/test/test_data/cinder_data.py +++ b/openstack_dashboard/test/test_data/cinder_data.py @@ -185,12 +185,21 @@ def data(TEST): 'size': 80, 'status': 'available', 'volume_id': '31023e92-8008-4c8b-8059-7f2293ff1234'}) + snapshot3 = vol_snaps.Snapshot( + vol_snaps.SnapshotManager(None), + {'id': 'c9d0881a-4c0b-4158-a212-ad27e11c2b0e', + 'name': '', + 'description': 'v2 volume snapshot description 2', + 'size': 80, + 'status': 'available', + 'volume_id': '31023e92-8008-4c8b-8059-7f2293ff1234'}) snapshot.bootable = 'true' snapshot2.bootable = 'true' TEST.cinder_volume_snapshots.add(api.cinder.VolumeSnapshot(snapshot)) TEST.cinder_volume_snapshots.add(api.cinder.VolumeSnapshot(snapshot2)) + TEST.cinder_volume_snapshots.add(api.cinder.VolumeSnapshot(snapshot3)) TEST.cinder_volume_snapshots.first()._volume = volume # Volume Type Encryption @@ -233,8 +242,19 @@ def data(TEST): 'container_name': 'volumebackups', 'volume_id': '31023e92-8008-4c8b-8059-7f2293ff1234'}) + volume_backup3 = vol_backups.VolumeBackup( + vol_backups.VolumeBackupManager(None), + {'id': 'c321cbb8-3f99-4c3f-a2ef-3edbec842e53', + 'name': 'backup3', + 'description': 'volume backup 3', + 'size': 20, + 'status': 'available', + 'container_name': 'volumebackups', + 'volume_id': '31023e92-8008-4c8b-8059-7f2293ff1234'}) + TEST.cinder_volume_backups.add(volume_backup1) TEST.cinder_volume_backups.add(volume_backup2) + TEST.cinder_volume_backups.add(volume_backup3) # Volume Encryption vol_enc_metadata1 = volumes.Volume(