Move Volume Backups out of tabbed panel

Notes on enabling backup:
https://github.com/coolsvap/devstack-cinder-backup

Change-Id: Ie6bcfad30d04ee35c75d693f5637197297ca84ef
Implements: blueprint reorganise-volumes
This commit is contained in:
Richard Jones 2017-02-03 16:52:55 +11:00
parent d7e1adeca0
commit 921f84a7ce
18 changed files with 183 additions and 183 deletions

View File

@ -74,7 +74,7 @@ class RestoreBackupForm(forms.SelfHandlingForm):
volumes = api.cinder.volume_list(request) volumes = api.cinder.volume_list(request)
except Exception: except Exception:
msg = _('Unable to lookup volume or backup information.') 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) exceptions.handle(request, msg, redirect=redirect)
raise exceptions.Http302(redirect) raise exceptions.Http302(redirect)
@ -104,5 +104,5 @@ class RestoreBackupForm(forms.SelfHandlingForm):
return restore return restore
except Exception: except Exception:
msg = _('Unable to restore backup.') msg = _('Unable to restore backup.')
redirect = reverse('horizon:project:volumes:index') redirect = reverse('horizon:project:backups:index')
exceptions.handle(request, msg, redirect=redirect) exceptions.handle(request, msg, redirect=redirect)

View File

@ -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"),)

View File

@ -86,7 +86,7 @@ class RestoreBackup(tables.LinkAction):
backup_id = datum.id backup_id = datum.id
backup_name = datum.name backup_name = datum.name
volume_id = getattr(datum, 'volume_id', None) volume_id = getattr(datum, 'volume_id', None)
url = reverse("horizon:project:volumes:backups:restore", url = reverse("horizon:project:backups:restore",
args=(backup_id,)) args=(backup_id,))
url += '?%s' % http.urlencode({'backup_name': backup_name, url += '?%s' % http.urlencode({'backup_name': backup_name,
'volume_id': volume_id}) 'volume_id': volume_id})
@ -133,7 +133,7 @@ class BackupsTable(tables.DataTable):
) )
name = tables.Column("name", name = tables.Column("name",
verbose_name=_("Name"), verbose_name=_("Name"),
link="horizon:project:volumes:backups:detail") link="horizon:project:backups:detail")
description = tables.Column("description", description = tables.Column("description",
verbose_name=_("Description"), verbose_name=_("Description"),
truncate=40) truncate=40)

View File

@ -23,8 +23,7 @@ from openstack_dashboard.api import cinder
class BackupOverviewTab(tabs.Tab): class BackupOverviewTab(tabs.Tab):
name = _("Overview") name = _("Overview")
slug = "overview" slug = "overview"
template_name = ("project/volumes/backups/" template_name = "project/backups/_detail_overview.html"
"_detail_overview.html")
def get_context_data(self, request): def get_context_data(self, request):
try: try:
@ -36,7 +35,7 @@ class BackupOverviewTab(tabs.Tab):
return {'backup': backup, return {'backup': backup,
'volume': volume} 'volume': volume}
except Exception: except Exception:
redirect = reverse('horizon:project:volumes:index') redirect = reverse('horizon:project:backups:index')
exceptions.handle(self.request, exceptions.handle(self.request,
_('Unable to retrieve backup details.'), _('Unable to retrieve backup details.'),
redirect=redirect) redirect=redirect)

View File

@ -10,21 +10,108 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django import http from django import http
from django.test.utils import override_settings
from django.utils.http import urlencode from django.utils.http import urlencode
from django.utils.http import urlunquote
from mox3.mox import IsA # noqa from mox3.mox import IsA # noqa
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.dashboards.project.backups \
import tables as backup_tables
from openstack_dashboard.test import helpers as test from openstack_dashboard.test import helpers as test
INDEX_URL = reverse('horizon:project:volumes:index') INDEX_URL = reverse('horizon:project:backups:index')
VOLUME_BACKUPS_TAB_URL = reverse('horizon:project:volumes:backups_tab')
class VolumeBackupsViewTests(test.TestCase): 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',)}) @test.create_stubs({api.cinder: ('volume_backup_create',)})
def test_create_backup_post(self): def test_create_backup_post(self):
volume = self.volumes.first() volume = self.volumes.first()
@ -50,10 +137,9 @@ class VolumeBackupsViewTests(test.TestCase):
self.assertNoFormErrors(res) self.assertNoFormErrors(res)
self.assertMessageCount(error=0, warning=0) 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', @test.create_stubs({api.cinder: ('volume_list',
'volume_backup_supported',
'volume_backup_list_paged', 'volume_backup_list_paged',
'volume_backup_delete')}) 'volume_backup_delete')})
def test_delete_volume_backup(self): def test_delete_volume_backup(self):
@ -61,8 +147,6 @@ class VolumeBackupsViewTests(test.TestCase):
volumes = self.cinder_volumes.list() volumes = self.cinder_volumes.list()
backup = self.cinder_volume_backups.first() backup = self.cinder_volume_backups.first()
api.cinder.volume_backup_supported(IsA(http.HttpRequest)). \
MultipleTimes().AndReturn(True)
api.cinder.volume_backup_list_paged( api.cinder.volume_backup_list_paged(
IsA(http.HttpRequest), marker=None, sort_dir='desc', IsA(http.HttpRequest), marker=None, sort_dir='desc',
paginate=True).AndReturn([vol_backups, False, False]) paginate=True).AndReturn([vol_backups, False, False])
@ -74,12 +158,9 @@ class VolumeBackupsViewTests(test.TestCase):
formData = {'action': formData = {'action':
'volume_backups__delete__%s' % backup.id} 'volume_backups__delete__%s' % backup.id}
res = self.client.post(INDEX_URL + res = self.client.post(INDEX_URL, formData)
"?tab=volumes_and_snapshots__backups_tab",
formData)
self.assertRedirectsNoFollow(res, INDEX_URL + self.assertRedirectsNoFollow(res, INDEX_URL)
"?tab=volumes_and_snapshots__backups_tab")
self.assertMessageCount(success=1) self.assertMessageCount(success=1)
@test.create_stubs({api.cinder: ('volume_backup_get', 'volume_get')}) @test.create_stubs({api.cinder: ('volume_backup_get', 'volume_get')})
@ -93,7 +174,7 @@ class VolumeBackupsViewTests(test.TestCase):
AndReturn(volume) AndReturn(volume)
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:project:volumes:backups:detail', url = reverse('horizon:project:backups:detail',
args=[backup.id]) args=[backup.id])
res = self.client.get(url) res = self.client.get(url)
@ -109,7 +190,7 @@ class VolumeBackupsViewTests(test.TestCase):
AndRaise(self.exceptions.cinder) AndRaise(self.exceptions.cinder)
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:project:volumes:backups:detail', url = reverse('horizon:project:backups:detail',
args=[backup.id]) args=[backup.id])
res = self.client.get(url) res = self.client.get(url)
@ -128,7 +209,7 @@ class VolumeBackupsViewTests(test.TestCase):
AndRaise(self.exceptions.cinder) AndRaise(self.exceptions.cinder)
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:project:volumes:backups:detail', url = reverse('horizon:project:backups:detail',
args=[backup.id]) args=[backup.id])
res = self.client.get(url) res = self.client.get(url)
@ -153,7 +234,7 @@ class VolumeBackupsViewTests(test.TestCase):
'backup_id': backup.id, 'backup_id': backup.id,
'backup_name': backup.name, 'backup_name': backup.name,
'volume_id': backup.volume_id} 'volume_id': backup.volume_id}
url = reverse('horizon:project:volumes:backups:restore', url = reverse('horizon:project:backups:restore',
args=[backup.id]) args=[backup.id])
url += '?%s' % urlencode({'backup_name': backup.name, url += '?%s' % urlencode({'backup_name': backup.name,
'volume_id': backup.volume_id}) 'volume_id': backup.volume_id})
@ -161,4 +242,5 @@ class VolumeBackupsViewTests(test.TestCase):
self.assertNoFormErrors(res) self.assertNoFormErrors(res)
self.assertMessageCount(info=1) self.assertMessageCount(info=1)
self.assertRedirectsNoFollow(res, INDEX_URL) self.assertRedirectsNoFollow(res,
reverse('horizon:project:volumes:index'))

View File

@ -12,10 +12,11 @@
from django.conf.urls import url from django.conf.urls import url
from openstack_dashboard.dashboards.project.volumes.backups import views from openstack_dashboard.dashboards.project.backups import views
urlpatterns = [ urlpatterns = [
url(r'^$', views.BackupsView.as_view(), name='index'),
url(r'^(?P<backup_id>[^/]+)/$', url(r'^(?P<backup_id>[^/]+)/$',
views.BackupDetailView.as_view(), views.BackupDetailView.as_view(),
name='detail'), name='detail'),

View File

@ -16,24 +16,53 @@ from django.utils.translation import ugettext_lazy as _
from horizon import exceptions from horizon import exceptions
from horizon import forms from horizon import forms
from horizon import tables
from horizon import tabs from horizon import tabs
from horizon.utils import memoized from horizon.utils import memoized
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.dashboards.project.volumes.backups \ from openstack_dashboard.dashboards.project.backups \
import forms as backup_forms import forms as backup_forms
from openstack_dashboard.dashboards.project.volumes.backups \ from openstack_dashboard.dashboards.project.backups \
import tables as backup_tables import tables as backup_tables
from openstack_dashboard.dashboards.project.volumes.backups \ from openstack_dashboard.dashboards.project.backups \
import tabs as backup_tabs 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): class CreateBackupView(forms.ModalFormView):
form_class = backup_forms.CreateBackupForm 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_label = _("Create Volume Backup")
submit_url = "horizon:project:volumes:volumes:create_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") page_title = _("Create Volume Backup")
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -79,14 +108,14 @@ class BackupDetailView(tabs.TabView):
@staticmethod @staticmethod
def get_redirect_url(): def get_redirect_url():
return reverse('horizon:project:volumes:index') return reverse('horizon:project:backups:index')
class RestoreBackupView(forms.ModalFormView): class RestoreBackupView(forms.ModalFormView):
form_class = backup_forms.RestoreBackupForm 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_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') success_url = reverse_lazy('horizon:project:volumes:index')
page_title = _("Restore Volume Backup") page_title = _("Restore Volume Backup")

View File

@ -23,8 +23,6 @@ from horizon import tabs
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard import policy 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 \ from openstack_dashboard.dashboards.project.volumes.cg_snapshots \
import tables as cg_snapshots_tables import tables as cg_snapshots_tables
from openstack_dashboard.dashboards.project.volumes.cgroups \ from openstack_dashboard.dashboards.project.volumes.cgroups \
@ -126,34 +124,6 @@ class VolumeTab(PagedTableMixin, tabs.TableTab, VolumeTableMixIn):
return volumes 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): class CGroupsTab(tabs.TableTab):
table_classes = (cgroup_tables.VolumeCGroupsTable,) table_classes = (cgroup_tables.VolumeCGroupsTable,)
name = _("Consistency Groups") name = _("Consistency Groups")
@ -206,5 +176,5 @@ class CGSnapshotsTab(tabs.TableTab):
class VolumeAndSnapshotTabs(tabs.TabGroup): class VolumeAndSnapshotTabs(tabs.TabGroup):
slug = "volumes_and_snapshots" slug = "volumes_and_snapshots"
tabs = (VolumeTab, BackupsTab, CGroupsTab, CGSnapshotsTab) tabs = (VolumeTab, CGroupsTab, CGSnapshotsTab)
sticky = True sticky = True

View File

@ -23,16 +23,12 @@ from django.utils.http import urlunquote
from mox3.mox import IsA # noqa from mox3.mox import IsA # noqa
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.dashboards.project.volumes.backups \
import tables as backup_tables
from openstack_dashboard.dashboards.project.volumes.volumes \ from openstack_dashboard.dashboards.project.volumes.volumes \
import tables as volume_tables import tables as volume_tables
from openstack_dashboard.test import helpers as test from openstack_dashboard.test import helpers as test
INDEX_URL = reverse('horizon:project:volumes:index') INDEX_URL = reverse('horizon:project:volumes:index')
VOLUME_BACKUPS_TAB_URL = urlunquote(reverse(
'horizon:project:volumes:backups_tab'))
class VolumeAndSnapshotsAndBackupsTests(test.TestCase): class VolumeAndSnapshotsAndBackupsTests(test.TestCase):
@ -44,8 +40,7 @@ class VolumeAndSnapshotsAndBackupsTests(test.TestCase):
'volume_backup_list_paged', 'volume_backup_list_paged',
), ),
api.nova: ('server_list',)}) api.nova: ('server_list',)})
def _test_index(self, backup_supported=True, instanceless_volumes=False): def test_index(self, instanceless_volumes=False):
vol_backups = self.cinder_volume_backups.list()
vol_snaps = self.cinder_volume_snapshots.list() vol_snaps = self.cinder_volume_snapshots.list()
volumes = self.cinder_volumes.list() volumes = self.cinder_volumes.list()
if instanceless_volumes: if instanceless_volumes:
@ -53,7 +48,7 @@ class VolumeAndSnapshotsAndBackupsTests(test.TestCase):
volume.attachments = [] volume.attachments = []
api.cinder.volume_backup_supported(IsA(http.HttpRequest)).\ api.cinder.volume_backup_supported(IsA(http.HttpRequest)).\
MultipleTimes().AndReturn(backup_supported) MultipleTimes().AndReturn(False)
api.cinder.volume_list_paged( api.cinder.volume_list_paged(
IsA(http.HttpRequest), marker=None, search_opts=None, IsA(http.HttpRequest), marker=None, search_opts=None,
sort_dir='desc', paginate=True).\ sort_dir='desc', paginate=True).\
@ -65,11 +60,6 @@ class VolumeAndSnapshotsAndBackupsTests(test.TestCase):
api.cinder.volume_snapshot_list(IsA(http.HttpRequest)).\ api.cinder.volume_snapshot_list(IsA(http.HttpRequest)).\
AndReturn(vol_snaps) 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)).\ api.cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\
MultipleTimes().AndReturn(self.cinder_limits['absolute']) MultipleTimes().AndReturn(self.cinder_limits['absolute'])
self.mox.ReplayAll() self.mox.ReplayAll()
@ -78,18 +68,8 @@ class VolumeAndSnapshotsAndBackupsTests(test.TestCase):
self.assertEqual(res.status_code, 200) self.assertEqual(res.status_code, 200)
self.assertTemplateUsed(res, 'project/volumes/index.html') 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): 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', @test.create_stubs({api.cinder: ('tenant_absolute_limits',
'volume_list_paged', 'volume_list_paged',
@ -193,92 +173,3 @@ class VolumeAndSnapshotsAndBackupsTests(test.TestCase):
has_more=True, has_prev=False) has_more=True, has_prev=False)
volumes = res.context['volumes_table'].data volumes = res.context['volumes_table'].data
self.assertItemsEqual(volumes, expected_volumes) 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)

View File

@ -15,8 +15,6 @@
from django.conf.urls import include from django.conf.urls import include
from django.conf.urls import url 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 \ from openstack_dashboard.dashboards.project.volumes.cg_snapshots \
import urls as cg_snapshots_urls import urls as cg_snapshots_urls
from openstack_dashboard.dashboards.project.volumes.cgroups \ from openstack_dashboard.dashboards.project.volumes.cgroups \
@ -29,8 +27,6 @@ urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'), url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^\?tab=volumes_and_snapshots__volumes_tab$', url(r'^\?tab=volumes_and_snapshots__volumes_tab$',
views.IndexView.as_view(), name='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$', url(r'^\?tab=volumes_and_snapshots__cgroups_tab$',
views.IndexView.as_view(), name='cgroups_tab'), views.IndexView.as_view(), name='cgroups_tab'),
url(r'^\?tab=volumes_and_snapshots__cg_snapshots_tab$', url(r'^\?tab=volumes_and_snapshots__cg_snapshots_tab$',
@ -38,9 +34,6 @@ urlpatterns = [
url(r'', include( url(r'', include(
volume_urls, volume_urls,
namespace='volumes')), namespace='volumes')),
url(r'backups/', include(
backups_urls,
namespace='backups')),
url(r'cgroups/', include( url(r'cgroups/', include(
cgroup_urls, cgroup_urls,
namespace='cgroups')), namespace='cgroups')),

View File

@ -16,7 +16,7 @@ from django.conf.urls import url
from openstack_dashboard.dashboards.project.volumes \ from openstack_dashboard.dashboards.project.volumes \
.volumes import views .volumes import views
from openstack_dashboard.dashboards.project.volumes.backups \ from openstack_dashboard.dashboards.project.backups \
import views as backup_views import views as backup_views

View File

@ -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'