Move Consistency Group Snapshots out of Volumes panel

Change-Id: I2c02ff63c4e98040b3e46ded5331e706033dc746
Implements: blueprint reorganise-volumes
This commit is contained in:
Richard Jones 2017-02-06 16:26:23 +11:00
parent f85e0ffa91
commit 0b340eccc0
18 changed files with 87 additions and 73 deletions

View File

@ -68,7 +68,7 @@ class CreateCGroupForm(forms.SelfHandlingForm):
messages.info(request, message) messages.info(request, message)
return cgroup return cgroup
except Exception: except Exception:
redirect = reverse("horizon:project:volumes:index") redirect = reverse("horizon:project:cg_snapshots:index")
msg = _('Unable to create consistency ' msg = _('Unable to create consistency '
'group "%s" from snapshot.') % data['name'] 'group "%s" from snapshot.') % data['name']
exceptions.handle(request, exceptions.handle(request,

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 CGSnapshots(horizon.Panel):
name = _("Consistency Group Snapshots")
slug = 'cg_snapshots'
permissions = (
('openstack.services.volume', 'openstack.services.volumev2'),
)
policy_rules = (("volume", "consistencygroup:get_all_cgsnapshots"),)

View File

@ -23,7 +23,7 @@ from openstack_dashboard import policy
class CreateVolumeCGroup(policy.PolicyTargetMixin, tables.LinkAction): class CreateVolumeCGroup(policy.PolicyTargetMixin, tables.LinkAction):
name = "create_cgroup" name = "create_cgroup"
verbose_name = _("Create Consistency Group") verbose_name = _("Create Consistency Group")
url = "horizon:project:volumes:cg_snapshots:create_cgroup" url = "horizon:project:cg_snapshots:create_cgroup"
classes = ("ajax-modal",) classes = ("ajax-modal",)
policy_rules = (("volume", "consistencygroup:create"),) policy_rules = (("volume", "consistencygroup:create"),)
@ -90,8 +90,7 @@ class CGSnapshotsTable(tables.DataTable):
name = tables.Column("name", name = tables.Column("name",
verbose_name=_("Name"), verbose_name=_("Name"),
link="horizon:project:volumes:" link="horizon:project:cg_snapshots:detail")
"cg_snapshots:cg_snapshot_detail")
description = tables.Column("description", description = tables.Column("description",
verbose_name=_("Description"), verbose_name=_("Description"),
truncate=40) truncate=40)
@ -113,4 +112,6 @@ class CGSnapshotsTable(tables.DataTable):
DeleteVolumeCGSnapshot,) DeleteVolumeCGSnapshot,)
row_class = UpdateRow row_class = UpdateRow
status_columns = ("status",) status_columns = ("status",)
permissions = ['openstack.services.volume'] permissions = [
('openstack.services.volume', 'openstack.services.volumev2')
]

View File

@ -19,14 +19,14 @@ from horizon import tabs
class OverviewTab(tabs.Tab): class OverviewTab(tabs.Tab):
name = _("Overview") name = _("Overview")
slug = "overview" slug = "overview"
template_name = ("project/volumes/cg_snapshots/_detail_overview.html") template_name = "project/cg_snapshots/_detail_overview.html"
def get_context_data(self, request): def get_context_data(self, request):
cg_snapshot = self.tab_group.kwargs['cg_snapshot'] cg_snapshot = self.tab_group.kwargs['cg_snapshot']
return {"cg_snapshot": cg_snapshot} return {"cg_snapshot": cg_snapshot}
def get_redirect_url(self): def get_redirect_url(self):
return reverse('horizon:project:volumes:cg_snapshots:index') return reverse('horizon:project:cg_snapshots:index')
class CGSnapshotsDetailTabs(tabs.TabGroup): class CGSnapshotsDetailTabs(tabs.TabGroup):

View File

@ -12,16 +12,13 @@
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django import http from django import http
from django.utils.http import urlunquote
from mox3.mox import IsA # noqa from mox3.mox import IsA # noqa
from openstack_dashboard.api import cinder from openstack_dashboard.api import cinder
from openstack_dashboard.test import helpers as test from openstack_dashboard.test import helpers as test
VOLUME_INDEX_URL = reverse('horizon:project:volumes:index') INDEX_URL = reverse('horizon:project:cg_snapshots:index')
VOLUME_CG_SNAPSHOTS_TAB_URL = urlunquote(reverse(
'horizon:project:volumes:cg_snapshots_tab'))
class CGroupSnapshotTests(test.TestCase): class CGroupSnapshotTests(test.TestCase):
@ -44,11 +41,11 @@ class CGroupSnapshotTests(test.TestCase):
.AndReturn(cgroup) .AndReturn(cgroup)
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:project:volumes:cg_snapshots:create_cgroup', url = reverse('horizon:project:cg_snapshots:create_cgroup',
args=[cg_snapshot.id]) args=[cg_snapshot.id])
res = self.client.post(url, formData) res = self.client.post(url, formData)
self.assertNoFormErrors(res) self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, VOLUME_INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({cinder: ('volume_cg_snapshot_get', @test.create_stubs({cinder: ('volume_cg_snapshot_get',
'volume_cgroup_create_from_source',)}) 'volume_cgroup_create_from_source',)})
@ -69,7 +66,7 @@ class CGroupSnapshotTests(test.TestCase):
.AndRaise(self.exceptions.cinder) .AndRaise(self.exceptions.cinder)
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:project:volumes:cg_snapshots:create_cgroup', url = reverse('horizon:project:cg_snapshots:create_cgroup',
args=[cg_snapshot.id]) args=[cg_snapshot.id])
res = self.client.post(url, formData) res = self.client.post(url, formData)
self.assertNoFormErrors(res) self.assertNoFormErrors(res)
@ -78,7 +75,7 @@ class CGroupSnapshotTests(test.TestCase):
self.assertIn('Unable to create consistency group "%s" from snapshot.' self.assertIn('Unable to create consistency group "%s" from snapshot.'
% new_cg_name, % new_cg_name,
res.cookies.output().replace('\\', '')) res.cookies.output().replace('\\', ''))
self.assertRedirectsNoFollow(res, VOLUME_INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({cinder: ('volume_cg_snapshot_list', @test.create_stubs({cinder: ('volume_cg_snapshot_list',
'volume_cg_snapshot_delete',)}) 'volume_cg_snapshot_delete',)})
@ -93,8 +90,7 @@ class CGroupSnapshotTests(test.TestCase):
form_data = {'action': 'volume_cg_snapshots__delete_cg_snapshot__%s' form_data = {'action': 'volume_cg_snapshots__delete_cg_snapshot__%s'
% cg_snapshot.id} % cg_snapshot.id}
res = self.client.post(VOLUME_CG_SNAPSHOTS_TAB_URL, form_data, res = self.client.post(INDEX_URL, form_data, follow=True)
follow=True)
self.assertEqual(res.status_code, 200) self.assertEqual(res.status_code, 200)
self.assertIn("Scheduled deletion of Snapshot: %s" % cg_snapshot.name, self.assertIn("Scheduled deletion of Snapshot: %s" % cg_snapshot.name,
[m.message for m in res.context['messages']]) [m.message for m in res.context['messages']])
@ -114,8 +110,7 @@ class CGroupSnapshotTests(test.TestCase):
form_data = {'action': 'volume_cg_snapshots__delete_cg_snapshot__%s' form_data = {'action': 'volume_cg_snapshots__delete_cg_snapshot__%s'
% cg_snapshot.id} % cg_snapshot.id}
res = self.client.post(VOLUME_CG_SNAPSHOTS_TAB_URL, form_data, res = self.client.post(INDEX_URL, form_data, follow=True)
follow=True)
self.assertEqual(res.status_code, 200) self.assertEqual(res.status_code, 200)
self.assertIn("Unable to delete snapshot: %s" % cg_snapshot.name, self.assertIn("Unable to delete snapshot: %s" % cg_snapshot.name,
[m.message for m in res.context['messages']]) [m.message for m in res.context['messages']])
@ -143,7 +138,7 @@ class CGroupSnapshotTests(test.TestCase):
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse( url = reverse(
'horizon:project:volumes:cg_snapshots:cg_snapshot_detail', 'horizon:project:cg_snapshots:cg_snapshot_detail',
args=[cg_snapshot.id]) args=[cg_snapshot.id])
res = self.client.get(url) res = self.client.get(url)
self.assertNoFormErrors(res) self.assertNoFormErrors(res)
@ -159,8 +154,8 @@ class CGroupSnapshotTests(test.TestCase):
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse( url = reverse(
'horizon:project:volumes:cg_snapshots:cg_snapshot_detail', 'horizon:project:cg_snapshots:cg_snapshot_detail',
args=[cg_snapshot.id]) args=[cg_snapshot.id])
res = self.client.get(url) res = self.client.get(url)
self.assertNoFormErrors(res) self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, VOLUME_INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)

View File

@ -12,9 +12,10 @@
from django.conf.urls import url from django.conf.urls import url
from openstack_dashboard.dashboards.project.volumes.cg_snapshots import views from openstack_dashboard.dashboards.project.cg_snapshots import views
urlpatterns = [ urlpatterns = [
url(r'^$', views.CGSnapshotsView.as_view(), name='index'),
url(r'^(?P<cg_snapshot_id>[^/]+)/cg_snapshot_detail/$', url(r'^(?P<cg_snapshot_id>[^/]+)/cg_snapshot_detail/$',
views.DetailView.as_view(), views.DetailView.as_view(),
name='cg_snapshot_detail'), name='cg_snapshot_detail'),

View File

@ -16,6 +16,7 @@ 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
@ -23,17 +24,32 @@ from openstack_dashboard import api
from openstack_dashboard.api import cinder from openstack_dashboard.api import cinder
from openstack_dashboard.usage import quotas from openstack_dashboard.usage import quotas
from openstack_dashboard.dashboards.project.volumes \ from openstack_dashboard.dashboards.project.cg_snapshots \
.cg_snapshots import forms as cg_snapshot_forms import forms as cg_snapshot_forms
from openstack_dashboard.dashboards.project.volumes \ from openstack_dashboard.dashboards.project.cg_snapshots \
.cg_snapshots import tables as cg_snapshot_tables import tables as cg_snapshot_tables
from openstack_dashboard.dashboards.project.volumes \ from openstack_dashboard.dashboards.project.cg_snapshots \
.cg_snapshots import tabs as cg_snapshot_tabs import tabs as cg_snapshot_tabs
CGROUP_INFO_FIELDS = ("name", CGROUP_INFO_FIELDS = ("name",
"description") "description")
INDEX_URL = "horizon:project:volumes:index" INDEX_URL = "horizon:project:cg_snapshots:index"
class CGSnapshotsView(tables.DataTableView):
table_class = cg_snapshot_tables.CGSnapshotsTable
page_title = _("Consistency Group Snapshots")
def get_data(self):
try:
cg_snapshots = api.cinder.volume_cg_snapshot_list(self.request)
except Exception:
cg_snapshots = []
exceptions.handle(self.request, _("Unable to retrieve "
"volume consistency group "
"snapshots."))
return cg_snapshots
class DetailView(tabs.TabView): class DetailView(tabs.TabView):
@ -84,7 +100,7 @@ class DetailView(tabs.TabView):
@staticmethod @staticmethod
def get_redirect_url(): def get_redirect_url():
return reverse('horizon:project:volumes:index') return reverse(INDEX_URL)
def get_tabs(self, request, *args, **kwargs): def get_tabs(self, request, *args, **kwargs):
cg_snapshot = self.get_data() cg_snapshot = self.get_data()
@ -93,8 +109,8 @@ class DetailView(tabs.TabView):
class CreateCGroupView(forms.ModalFormView): class CreateCGroupView(forms.ModalFormView):
form_class = cg_snapshot_forms.CreateCGroupForm form_class = cg_snapshot_forms.CreateCGroupForm
template_name = 'project/volumes/cg_snapshots/create.html' template_name = 'project/cg_snapshots/create.html'
submit_url = "horizon:project:volumes:cg_snapshots:create_cgroup" submit_url = "horizon:project:cg_snapshots:create_cgroup"
success_url = reverse_lazy('horizon:project:cgroups:index') success_url = reverse_lazy('horizon:project:cgroups:index')
page_title = _("Create Volume Consistency Group") page_title = _("Create Volume Consistency Group")

View File

@ -20,8 +20,8 @@ from openstack_dashboard.test import helpers as test
INDEX_URL = reverse('horizon:project:cgroups:index') INDEX_URL = reverse('horizon:project:cgroups:index')
VOLUME_CGROUPS_SNAP_TAB_URL = urlunquote(reverse( VOLUME_CGROUPS_SNAP_INDEX_URL = urlunquote(reverse(
'horizon:project:volumes:cg_snapshots_tab')) 'horizon:project:cg_snapshots:index'))
class ConsistencyGroupTests(test.TestCase): class ConsistencyGroupTests(test.TestCase):
@ -296,7 +296,7 @@ class ConsistencyGroupTests(test.TestCase):
args=[cgroup.id]) args=[cgroup.id])
res = self.client.post(url, formData) res = self.client.post(url, formData)
self.assertNoFormErrors(res) self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, VOLUME_CGROUPS_SNAP_TAB_URL) self.assertRedirectsNoFollow(res, VOLUME_CGROUPS_SNAP_INDEX_URL)
@test.create_stubs({cinder: ('volume_cgroup_get', @test.create_stubs({cinder: ('volume_cgroup_get',
'volume_cgroup_create_from_source',)}) 'volume_cgroup_create_from_source',)})

View File

@ -190,7 +190,7 @@ class CreateSnapshotView(forms.ModalFormView):
template_name = 'project/cgroups/create_snapshot.html' template_name = 'project/cgroups/create_snapshot.html'
submit_label = _("Create Snapshot") submit_label = _("Create Snapshot")
submit_url = "horizon:project:cgroups:create_snapshot" submit_url = "horizon:project:cgroups:create_snapshot"
success_url = reverse_lazy('horizon:project:volumes:cg_snapshots_tab') success_url = reverse_lazy('horizon:project:cg_snapshots:index')
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(CreateSnapshotView, self).get_context_data(**kwargs) context = super(CreateSnapshotView, self).get_context_data(**kwargs)

View File

@ -21,10 +21,7 @@ from horizon.tables import PagedTableMixin
from horizon import tabs from horizon import tabs
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard import policy
from openstack_dashboard.dashboards.project.volumes.cg_snapshots \
import tables as cg_snapshots_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
@ -122,32 +119,7 @@ class VolumeTab(PagedTableMixin, tabs.TableTab, VolumeTableMixIn):
return volumes return volumes
class CGSnapshotsTab(tabs.TableTab):
table_classes = (cg_snapshots_tables.CGSnapshotsTable,)
name = _("Consistency Group Snapshots")
slug = "cg_snapshots_tab"
template_name = ("horizon/common/_detail_table.html")
preload = False
def allowed(self, request):
return policy.check(
(("volume", "consistencygroup:get_all_cgsnapshots"),),
request
)
def get_volume_cg_snapshots_data(self):
try:
cg_snapshots = api.cinder.volume_cg_snapshot_list(
self.request)
except Exception:
cg_snapshots = []
exceptions.handle(self.request, _("Unable to retrieve "
"volume consistency group "
"snapshots."))
return cg_snapshots
class VolumeAndSnapshotTabs(tabs.TabGroup): class VolumeAndSnapshotTabs(tabs.TabGroup):
slug = "volumes_and_snapshots" slug = "volumes_and_snapshots"
tabs = (VolumeTab, CGSnapshotsTab) tabs = (VolumeTab, )
sticky = True sticky = True

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.cg_snapshots \
import urls as cg_snapshots_urls
from openstack_dashboard.dashboards.project.volumes import views from openstack_dashboard.dashboards.project.volumes import views
from openstack_dashboard.dashboards.project.volumes.volumes \ from openstack_dashboard.dashboards.project.volumes.volumes \
import urls as volume_urls import urls as volume_urls
@ -25,12 +23,7 @@ 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__cg_snapshots_tab$',
views.IndexView.as_view(), name='cg_snapshots_tab'),
url(r'', include( url(r'', include(
volume_urls, volume_urls,
namespace='volumes')), namespace='volumes')),
url(r'cg_snapshots/', include(
cg_snapshots_urls,
namespace='cg_snapshots')),
] ]

View File

@ -0,0 +1,10 @@
# The slug of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'cg_snapshots'
# 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.cg_snapshots.panel.CGSnapshots'