diff --git a/openstack_dashboard/dashboards/project/volumes/backups/__init__.py b/openstack_dashboard/dashboards/project/backups/__init__.py similarity index 100% rename from openstack_dashboard/dashboards/project/volumes/backups/__init__.py rename to openstack_dashboard/dashboards/project/backups/__init__.py diff --git a/openstack_dashboard/dashboards/project/volumes/backups/forms.py b/openstack_dashboard/dashboards/project/backups/forms.py similarity index 97% rename from openstack_dashboard/dashboards/project/volumes/backups/forms.py rename to openstack_dashboard/dashboards/project/backups/forms.py index 31a4fff3be..e588c4658c 100644 --- a/openstack_dashboard/dashboards/project/volumes/backups/forms.py +++ b/openstack_dashboard/dashboards/project/backups/forms.py @@ -74,7 +74,7 @@ class RestoreBackupForm(forms.SelfHandlingForm): volumes = api.cinder.volume_list(request) except Exception: msg = _('Unable to lookup volume or backup information.') - redirect = reverse('horizon:project:volumes:index') + redirect = reverse('horizon:project:backups:index') exceptions.handle(request, msg, redirect=redirect) raise exceptions.Http302(redirect) @@ -104,5 +104,5 @@ class RestoreBackupForm(forms.SelfHandlingForm): return restore except Exception: msg = _('Unable to restore backup.') - redirect = reverse('horizon:project:volumes:index') + redirect = reverse('horizon:project:backups:index') exceptions.handle(request, msg, redirect=redirect) diff --git a/openstack_dashboard/dashboards/project/backups/panel.py b/openstack_dashboard/dashboards/project/backups/panel.py new file mode 100644 index 0000000000..5f896c4e91 --- /dev/null +++ b/openstack_dashboard/dashboards/project/backups/panel.py @@ -0,0 +1,26 @@ +# Copyright 2017 Rackspace, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from django.utils.translation import ugettext_lazy as _ + +import horizon + + +class Backups(horizon.Panel): + name = _("Backups") + slug = 'backups' + permissions = ( + ('openstack.services.volume', 'openstack.services.volumev2'), + ) + policy_rules = (("volume", "backup:get_all"),) diff --git a/openstack_dashboard/dashboards/project/volumes/backups/tables.py b/openstack_dashboard/dashboards/project/backups/tables.py similarity index 97% rename from openstack_dashboard/dashboards/project/volumes/backups/tables.py rename to openstack_dashboard/dashboards/project/backups/tables.py index d982baa90c..c35eb908f1 100644 --- a/openstack_dashboard/dashboards/project/volumes/backups/tables.py +++ b/openstack_dashboard/dashboards/project/backups/tables.py @@ -86,7 +86,7 @@ class RestoreBackup(tables.LinkAction): backup_id = datum.id backup_name = datum.name volume_id = getattr(datum, 'volume_id', None) - url = reverse("horizon:project:volumes:backups:restore", + url = reverse("horizon:project:backups:restore", args=(backup_id,)) url += '?%s' % http.urlencode({'backup_name': backup_name, 'volume_id': volume_id}) @@ -133,7 +133,7 @@ class BackupsTable(tables.DataTable): ) name = tables.Column("name", verbose_name=_("Name"), - link="horizon:project:volumes:backups:detail") + link="horizon:project:backups:detail") description = tables.Column("description", verbose_name=_("Description"), truncate=40) diff --git a/openstack_dashboard/dashboards/project/volumes/backups/tabs.py b/openstack_dashboard/dashboards/project/backups/tabs.py similarity index 90% rename from openstack_dashboard/dashboards/project/volumes/backups/tabs.py rename to openstack_dashboard/dashboards/project/backups/tabs.py index fde01dcdf0..322e1cbf01 100644 --- a/openstack_dashboard/dashboards/project/volumes/backups/tabs.py +++ b/openstack_dashboard/dashboards/project/backups/tabs.py @@ -23,8 +23,7 @@ from openstack_dashboard.api import cinder class BackupOverviewTab(tabs.Tab): name = _("Overview") slug = "overview" - template_name = ("project/volumes/backups/" - "_detail_overview.html") + template_name = "project/backups/_detail_overview.html" def get_context_data(self, request): try: @@ -36,7 +35,7 @@ class BackupOverviewTab(tabs.Tab): return {'backup': backup, 'volume': volume} except Exception: - redirect = reverse('horizon:project:volumes:index') + redirect = reverse('horizon:project:backups:index') exceptions.handle(self.request, _('Unable to retrieve backup details.'), redirect=redirect) diff --git a/openstack_dashboard/dashboards/project/volumes/templates/volumes/backups/_create_backup.html b/openstack_dashboard/dashboards/project/backups/templates/backups/_create_backup.html similarity index 100% rename from openstack_dashboard/dashboards/project/volumes/templates/volumes/backups/_create_backup.html rename to openstack_dashboard/dashboards/project/backups/templates/backups/_create_backup.html diff --git a/openstack_dashboard/dashboards/project/volumes/templates/volumes/backups/_detail_overview.html b/openstack_dashboard/dashboards/project/backups/templates/backups/_detail_overview.html similarity index 100% rename from openstack_dashboard/dashboards/project/volumes/templates/volumes/backups/_detail_overview.html rename to openstack_dashboard/dashboards/project/backups/templates/backups/_detail_overview.html diff --git a/openstack_dashboard/dashboards/project/volumes/templates/volumes/backups/_restore_backup.html b/openstack_dashboard/dashboards/project/backups/templates/backups/_restore_backup.html similarity index 100% rename from openstack_dashboard/dashboards/project/volumes/templates/volumes/backups/_restore_backup.html rename to openstack_dashboard/dashboards/project/backups/templates/backups/_restore_backup.html diff --git a/openstack_dashboard/dashboards/project/volumes/templates/volumes/backups/create_backup.html b/openstack_dashboard/dashboards/project/backups/templates/backups/create_backup.html similarity index 100% rename from openstack_dashboard/dashboards/project/volumes/templates/volumes/backups/create_backup.html rename to openstack_dashboard/dashboards/project/backups/templates/backups/create_backup.html diff --git a/openstack_dashboard/dashboards/project/volumes/templates/volumes/backups/restore_backup.html b/openstack_dashboard/dashboards/project/backups/templates/backups/restore_backup.html similarity index 100% rename from openstack_dashboard/dashboards/project/volumes/templates/volumes/backups/restore_backup.html rename to openstack_dashboard/dashboards/project/backups/templates/backups/restore_backup.html diff --git a/openstack_dashboard/dashboards/project/volumes/backups/tests.py b/openstack_dashboard/dashboards/project/backups/tests.py similarity index 57% rename from openstack_dashboard/dashboards/project/volumes/backups/tests.py rename to openstack_dashboard/dashboards/project/backups/tests.py index 7778674ae6..49c7f9e341 100644 --- a/openstack_dashboard/dashboards/project/volumes/backups/tests.py +++ b/openstack_dashboard/dashboards/project/backups/tests.py @@ -10,21 +10,108 @@ # 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 django.utils.http import urlencode +from django.utils.http import urlunquote from mox3.mox import IsA # noqa from openstack_dashboard import api +from openstack_dashboard.dashboards.project.backups \ + import tables as backup_tables from openstack_dashboard.test import helpers as test -INDEX_URL = reverse('horizon:project:volumes:index') -VOLUME_BACKUPS_TAB_URL = reverse('horizon:project:volumes:backups_tab') +INDEX_URL = reverse('horizon:project:backups:index') class VolumeBackupsViewTests(test.TestCase): + @test.create_stubs({api.cinder: ('tenant_absolute_limits', + 'volume_backup_list_paged', + 'volume_list'), + api.nova: ('server_list',)}) + def _test_backups_index_paginated(self, marker, sort_dir, backups, url, + has_more, has_prev): + 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(urlunquote(url)) + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'horizon/common/_data_table_view.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 = INDEX_URL + 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 = base_url + "?%s=%s" % (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 = base_url + "?%s=%s" % (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 = INDEX_URL + 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 = base_url + "?%s=%s" % (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 = base_url + "?%s=%s" % (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) + @test.create_stubs({api.cinder: ('volume_backup_create',)}) def test_create_backup_post(self): volume = self.volumes.first() @@ -50,10 +137,9 @@ class VolumeBackupsViewTests(test.TestCase): self.assertNoFormErrors(res) self.assertMessageCount(error=0, warning=0) - self.assertRedirectsNoFollow(res, VOLUME_BACKUPS_TAB_URL) + self.assertRedirectsNoFollow(res, INDEX_URL) @test.create_stubs({api.cinder: ('volume_list', - 'volume_backup_supported', 'volume_backup_list_paged', 'volume_backup_delete')}) def test_delete_volume_backup(self): @@ -61,8 +147,6 @@ class VolumeBackupsViewTests(test.TestCase): volumes = self.cinder_volumes.list() backup = self.cinder_volume_backups.first() - api.cinder.volume_backup_supported(IsA(http.HttpRequest)). \ - MultipleTimes().AndReturn(True) api.cinder.volume_backup_list_paged( IsA(http.HttpRequest), marker=None, sort_dir='desc', paginate=True).AndReturn([vol_backups, False, False]) @@ -74,12 +158,9 @@ class VolumeBackupsViewTests(test.TestCase): formData = {'action': 'volume_backups__delete__%s' % backup.id} - res = self.client.post(INDEX_URL + - "?tab=volumes_and_snapshots__backups_tab", - formData) + res = self.client.post(INDEX_URL, formData) - self.assertRedirectsNoFollow(res, INDEX_URL + - "?tab=volumes_and_snapshots__backups_tab") + self.assertRedirectsNoFollow(res, INDEX_URL) self.assertMessageCount(success=1) @test.create_stubs({api.cinder: ('volume_backup_get', 'volume_get')}) @@ -93,7 +174,7 @@ class VolumeBackupsViewTests(test.TestCase): AndReturn(volume) self.mox.ReplayAll() - url = reverse('horizon:project:volumes:backups:detail', + url = reverse('horizon:project:backups:detail', args=[backup.id]) res = self.client.get(url) @@ -109,7 +190,7 @@ class VolumeBackupsViewTests(test.TestCase): AndRaise(self.exceptions.cinder) self.mox.ReplayAll() - url = reverse('horizon:project:volumes:backups:detail', + url = reverse('horizon:project:backups:detail', args=[backup.id]) res = self.client.get(url) @@ -128,7 +209,7 @@ class VolumeBackupsViewTests(test.TestCase): AndRaise(self.exceptions.cinder) self.mox.ReplayAll() - url = reverse('horizon:project:volumes:backups:detail', + url = reverse('horizon:project:backups:detail', args=[backup.id]) res = self.client.get(url) @@ -153,7 +234,7 @@ class VolumeBackupsViewTests(test.TestCase): 'backup_id': backup.id, 'backup_name': backup.name, 'volume_id': backup.volume_id} - url = reverse('horizon:project:volumes:backups:restore', + url = reverse('horizon:project:backups:restore', args=[backup.id]) url += '?%s' % urlencode({'backup_name': backup.name, 'volume_id': backup.volume_id}) @@ -161,4 +242,5 @@ class VolumeBackupsViewTests(test.TestCase): self.assertNoFormErrors(res) self.assertMessageCount(info=1) - self.assertRedirectsNoFollow(res, INDEX_URL) + self.assertRedirectsNoFollow(res, + reverse('horizon:project:volumes:index')) diff --git a/openstack_dashboard/dashboards/project/volumes/backups/urls.py b/openstack_dashboard/dashboards/project/backups/urls.py similarity index 86% rename from openstack_dashboard/dashboards/project/volumes/backups/urls.py rename to openstack_dashboard/dashboards/project/backups/urls.py index 97c190df1a..77800a1705 100644 --- a/openstack_dashboard/dashboards/project/volumes/backups/urls.py +++ b/openstack_dashboard/dashboards/project/backups/urls.py @@ -12,10 +12,11 @@ from django.conf.urls import url -from openstack_dashboard.dashboards.project.volumes.backups import views +from openstack_dashboard.dashboards.project.backups import views urlpatterns = [ + url(r'^$', views.BackupsView.as_view(), name='index'), url(r'^(?P[^/]+)/$', views.BackupDetailView.as_view(), name='detail'), diff --git a/openstack_dashboard/dashboards/project/volumes/backups/views.py b/openstack_dashboard/dashboards/project/backups/views.py similarity index 70% rename from openstack_dashboard/dashboards/project/volumes/backups/views.py rename to openstack_dashboard/dashboards/project/backups/views.py index e1eb542102..ea1a3e55e4 100644 --- a/openstack_dashboard/dashboards/project/volumes/backups/views.py +++ b/openstack_dashboard/dashboards/project/backups/views.py @@ -16,24 +16,53 @@ from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import forms +from horizon import tables from horizon import tabs from horizon.utils import memoized from openstack_dashboard import api -from openstack_dashboard.dashboards.project.volumes.backups \ +from openstack_dashboard.dashboards.project.backups \ import forms as backup_forms -from openstack_dashboard.dashboards.project.volumes.backups \ +from openstack_dashboard.dashboards.project.backups \ import tables as backup_tables -from openstack_dashboard.dashboards.project.volumes.backups \ +from openstack_dashboard.dashboards.project.backups \ import tabs as backup_tabs +from openstack_dashboard.dashboards.project.volumes \ + import tabs as volume_tabs + + +class BackupsView(tables.DataTableView, tables.PagedTableMixin, + volume_tabs.VolumeTableMixIn): + table_class = backup_tables.BackupsTable + page_title = _("Volume Backups") + + def allowed(self, request): + return api.cinder.volume_backup_supported(self.request) + + def get_data(self): + try: + 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: + backup.volume = volumes.get(backup.volume_id) + except Exception: + backups = [] + exceptions.handle(self.request, _("Unable to retrieve " + "volume backups.")) + return backups class CreateBackupView(forms.ModalFormView): form_class = backup_forms.CreateBackupForm - template_name = 'project/volumes/backups/create_backup.html' + template_name = 'project/backups/create_backup.html' submit_label = _("Create Volume Backup") submit_url = "horizon:project:volumes:volumes:create_backup" - success_url = reverse_lazy("horizon:project:volumes:backups_tab") + success_url = reverse_lazy("horizon:project:backups:index") page_title = _("Create Volume Backup") def get_context_data(self, **kwargs): @@ -79,14 +108,14 @@ class BackupDetailView(tabs.TabView): @staticmethod def get_redirect_url(): - return reverse('horizon:project:volumes:index') + return reverse('horizon:project:backups:index') class RestoreBackupView(forms.ModalFormView): form_class = backup_forms.RestoreBackupForm - template_name = 'project/volumes/backups/restore_backup.html' + template_name = 'project/backups/restore_backup.html' submit_label = _("Restore Backup to Volume") - submit_url = "horizon:project:volumes:backups:restore" + submit_url = "horizon:project:backups:restore" success_url = reverse_lazy('horizon:project:volumes:index') page_title = _("Restore Volume Backup") diff --git a/openstack_dashboard/dashboards/project/volumes/tabs.py b/openstack_dashboard/dashboards/project/volumes/tabs.py index 66513d244f..1dced0cb07 100644 --- a/openstack_dashboard/dashboards/project/volumes/tabs.py +++ b/openstack_dashboard/dashboards/project/volumes/tabs.py @@ -23,8 +23,6 @@ from horizon import tabs from openstack_dashboard import api from openstack_dashboard import policy -from openstack_dashboard.dashboards.project.volumes.backups \ - import tables as backups_tables from openstack_dashboard.dashboards.project.volumes.cg_snapshots \ import tables as cg_snapshots_tables from openstack_dashboard.dashboards.project.volumes.cgroups \ @@ -126,34 +124,6 @@ class VolumeTab(PagedTableMixin, tabs.TableTab, VolumeTableMixIn): return volumes -class BackupsTab(PagedTableMixin, tabs.TableTab, VolumeTableMixIn): - table_classes = (backups_tables.BackupsTable,) - name = _("Volume Backups") - slug = "backups_tab" - template_name = ("horizon/common/_detail_table.html") - preload = False - - def allowed(self, request): - return api.cinder.volume_backup_supported(self.request) - - def get_volume_backups_data(self): - try: - 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: - backup.volume = volumes.get(backup.volume_id) - except Exception: - backups = [] - exceptions.handle(self.request, _("Unable to retrieve " - "volume backups.")) - return backups - - class CGroupsTab(tabs.TableTab): table_classes = (cgroup_tables.VolumeCGroupsTable,) name = _("Consistency Groups") @@ -206,5 +176,5 @@ class CGSnapshotsTab(tabs.TableTab): class VolumeAndSnapshotTabs(tabs.TabGroup): slug = "volumes_and_snapshots" - tabs = (VolumeTab, BackupsTab, CGroupsTab, CGSnapshotsTab) + tabs = (VolumeTab, CGroupsTab, CGSnapshotsTab) sticky = True diff --git a/openstack_dashboard/dashboards/project/volumes/test.py b/openstack_dashboard/dashboards/project/volumes/test.py index ebb5bb08ef..9b5f00c837 100644 --- a/openstack_dashboard/dashboards/project/volumes/test.py +++ b/openstack_dashboard/dashboards/project/volumes/test.py @@ -23,16 +23,12 @@ from django.utils.http import urlunquote 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.volumes \ import tables as volume_tables from openstack_dashboard.test import helpers as test INDEX_URL = reverse('horizon:project:volumes:index') -VOLUME_BACKUPS_TAB_URL = urlunquote(reverse( - 'horizon:project:volumes:backups_tab')) class VolumeAndSnapshotsAndBackupsTests(test.TestCase): @@ -44,8 +40,7 @@ class VolumeAndSnapshotsAndBackupsTests(test.TestCase): 'volume_backup_list_paged', ), api.nova: ('server_list',)}) - def _test_index(self, backup_supported=True, instanceless_volumes=False): - vol_backups = self.cinder_volume_backups.list() + def test_index(self, instanceless_volumes=False): vol_snaps = self.cinder_volume_snapshots.list() volumes = self.cinder_volumes.list() if instanceless_volumes: @@ -53,7 +48,7 @@ class VolumeAndSnapshotsAndBackupsTests(test.TestCase): volume.attachments = [] api.cinder.volume_backup_supported(IsA(http.HttpRequest)).\ - MultipleTimes().AndReturn(backup_supported) + MultipleTimes().AndReturn(False) api.cinder.volume_list_paged( IsA(http.HttpRequest), marker=None, search_opts=None, sort_dir='desc', paginate=True).\ @@ -65,11 +60,6 @@ class VolumeAndSnapshotsAndBackupsTests(test.TestCase): api.cinder.volume_snapshot_list(IsA(http.HttpRequest)).\ AndReturn(vol_snaps) - if backup_supported: - 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']) self.mox.ReplayAll() @@ -78,18 +68,8 @@ class VolumeAndSnapshotsAndBackupsTests(test.TestCase): self.assertEqual(res.status_code, 200) self.assertTemplateUsed(res, 'project/volumes/index.html') - if backup_supported: - res = self.client.get(VOLUME_BACKUPS_TAB_URL) - self.assertTemplateUsed(res, 'project/volumes/index.html') - - def test_index_backup_supported(self): - self._test_index(backup_supported=True) - - def test_index_backup_not_supported(self): - self._test_index(backup_supported=False) - def test_index_no_volume_attachments(self): - self._test_index(instanceless_volumes=True) + self.test_index(instanceless_volumes=True) @test.create_stubs({api.cinder: ('tenant_absolute_limits', 'volume_list_paged', @@ -193,92 +173,3 @@ class VolumeAndSnapshotsAndBackupsTests(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_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(urlunquote(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/dashboards/project/volumes/urls.py b/openstack_dashboard/dashboards/project/volumes/urls.py index 9fd006c9bc..af52fdd274 100644 --- a/openstack_dashboard/dashboards/project/volumes/urls.py +++ b/openstack_dashboard/dashboards/project/volumes/urls.py @@ -15,8 +15,6 @@ from django.conf.urls import include from django.conf.urls import url -from openstack_dashboard.dashboards.project.volumes.backups \ - import urls as backups_urls from openstack_dashboard.dashboards.project.volumes.cg_snapshots \ import urls as cg_snapshots_urls from openstack_dashboard.dashboards.project.volumes.cgroups \ @@ -29,8 +27,6 @@ urlpatterns = [ url(r'^$', views.IndexView.as_view(), name='index'), url(r'^\?tab=volumes_and_snapshots__volumes_tab$', views.IndexView.as_view(), name='volumes_tab'), - url(r'^\?tab=volumes_and_snapshots__backups_tab$', - views.IndexView.as_view(), name='backups_tab'), url(r'^\?tab=volumes_and_snapshots__cgroups_tab$', views.IndexView.as_view(), name='cgroups_tab'), url(r'^\?tab=volumes_and_snapshots__cg_snapshots_tab$', @@ -38,9 +34,6 @@ urlpatterns = [ url(r'', include( volume_urls, namespace='volumes')), - url(r'backups/', include( - backups_urls, - namespace='backups')), url(r'cgroups/', include( cgroup_urls, namespace='cgroups')), diff --git a/openstack_dashboard/dashboards/project/volumes/volumes/urls.py b/openstack_dashboard/dashboards/project/volumes/volumes/urls.py index 0d97f11b24..30b96c476b 100644 --- a/openstack_dashboard/dashboards/project/volumes/volumes/urls.py +++ b/openstack_dashboard/dashboards/project/volumes/volumes/urls.py @@ -16,7 +16,7 @@ from django.conf.urls import url from openstack_dashboard.dashboards.project.volumes \ .volumes import views -from openstack_dashboard.dashboards.project.volumes.backups \ +from openstack_dashboard.dashboards.project.backups \ import views as backup_views diff --git a/openstack_dashboard/enabled/_1330_project_backups_panel.py b/openstack_dashboard/enabled/_1330_project_backups_panel.py new file mode 100644 index 0000000000..cec549cca8 --- /dev/null +++ b/openstack_dashboard/enabled/_1330_project_backups_panel.py @@ -0,0 +1,9 @@ +# The slug of the panel to be added to HORIZON_CONFIG. Required. +PANEL = 'backups' +# The slug of the dashboard the PANEL associated with. Required. +PANEL_DASHBOARD = 'project' +# The slug of the panel group the PANEL is associated with. +PANEL_GROUP = 'volumes' + +# Python panel class of the PANEL to be added. +ADD_PANEL = 'openstack_dashboard.dashboards.project.backups.panel.Backups'