Move Consistency Groups out of Volumes panel

Notes on enabling consistency groups in devstack:
http://docs.openstack.org/admin-guide/blockstorage-consistency-groups.html

You'll also need to modify the local cinder policy file.

Though that's not actually enough to make it work since
devstack only provides the LVM volume type and CGs don't
work with LVM. You can attempt to create CGs, but they
will error.

Change-Id: I0ab541c81570cd5f67bb7d04c01bc92bc5cc3ab5
Implements: blueprint reorganise-volumes
This commit is contained in:
Richard Jones 2017-02-06 11:22:18 +11:00
parent 921f84a7ce
commit f85e0ffa91
28 changed files with 131 additions and 114 deletions

View File

@ -95,16 +95,16 @@
"snapshot_extension:snapshot_manage": "rule:admin_api", "snapshot_extension:snapshot_manage": "rule:admin_api",
"snapshot_extension:snapshot_unmanage": "rule:admin_api", "snapshot_extension:snapshot_unmanage": "rule:admin_api",
"consistencygroup:create" : "group:nobody", "consistencygroup:create" : "",
"consistencygroup:delete": "group:nobody", "consistencygroup:delete": "",
"consistencygroup:update": "group:nobody", "consistencygroup:update": "",
"consistencygroup:get": "group:nobody", "consistencygroup:get": "",
"consistencygroup:get_all": "group:nobody", "consistencygroup:get_all": "",
"consistencygroup:create_cgsnapshot" : "group:nobody", "consistencygroup:create_cgsnapshot" : "",
"consistencygroup:delete_cgsnapshot": "group:nobody", "consistencygroup:delete_cgsnapshot": "",
"consistencygroup:get_cgsnapshot": "group:nobody", "consistencygroup:get_cgsnapshot": "",
"consistencygroup:get_all_cgsnapshots": "group:nobody", "consistencygroup:get_all_cgsnapshots": "",
"scheduler_extension:scheduler_stats:get_pools" : "rule:admin_api", "scheduler_extension:scheduler_stats:get_pools" : "rule:admin_api",
"message:delete": "rule:admin_or_owner", "message:delete": "rule:admin_or_owner",

View File

@ -54,7 +54,7 @@ class UpdateForm(forms.SelfHandlingForm):
messages.info(request, message) messages.info(request, message)
return True return True
except Exception: except Exception:
redirect = reverse("horizon:project:volumes:index") redirect = reverse("horizon:project:cgroups:index")
exceptions.handle(request, exceptions.handle(request,
_('Unable to update volume consistency group.'), _('Unable to update volume consistency group.'),
redirect=redirect) redirect=redirect)
@ -85,7 +85,7 @@ class RemoveVolsForm(forms.SelfHandlingForm):
return True return True
except Exception: except Exception:
redirect = reverse("horizon:project:volumes:index") redirect = reverse("horizon:project:groups:index")
exceptions.handle(request, _('Errors occurred in removing volumes ' exceptions.handle(request, _('Errors occurred in removing volumes '
'from consistency group.'), 'from consistency group.'),
redirect=redirect) redirect=redirect)
@ -110,7 +110,7 @@ class DeleteForm(forms.SelfHandlingForm):
return True return True
except Exception: except Exception:
redirect = reverse("horizon:project:volumes:index") redirect = reverse("horizon:project:cgroups:index")
exceptions.handle(request, _('Errors occurred in deleting ' exceptions.handle(request, _('Errors occurred in deleting '
'consistency group.'), 'consistency group.'),
redirect=redirect) redirect=redirect)
@ -143,7 +143,7 @@ class CreateSnapshotForm(forms.SelfHandlingForm):
messages.info(request, message) messages.info(request, message)
return snapshot return snapshot
except Exception as e: except Exception as e:
redirect = reverse("horizon:project:volumes:index") redirect = reverse("horizon:project:cgroups:index")
msg = _('Unable to create consistency group snapshot.') msg = _('Unable to create consistency group snapshot.')
if e.code == 413: if e.code == 413:
msg = _('Requested snapshot would exceed the allowed quota.') msg = _('Requested snapshot would exceed the allowed quota.')
@ -204,7 +204,7 @@ class CloneCGroupForm(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:cgroups:index")
msg = _('Unable to clone consistency group.') msg = _('Unable to clone consistency group.')
search_opts = {'consistentcygroup_id': data['cgroup_id']} search_opts = {'consistentcygroup_id': data['cgroup_id']}

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 CGroups(horizon.Panel):
name = _("Consistency Groups")
slug = 'cgroups'
permissions = (
('openstack.services.volume', 'openstack.services.volumev2'),
)
policy_rules = (("volume", "consistencygroup:get_all"),)

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" name = "create"
verbose_name = _("Create Consistency Group") verbose_name = _("Create Consistency Group")
url = "horizon:project:volumes:cgroups:create" url = "horizon:project:cgroups:create"
classes = ("ajax-modal",) classes = ("ajax-modal",)
icon = "plus" icon = "plus"
policy_rules = (("volume", "consistencygroup:create"),) policy_rules = (("volume", "consistencygroup:create"),)
@ -32,7 +32,7 @@ class CreateVolumeCGroup(policy.PolicyTargetMixin, tables.LinkAction):
class DeleteVolumeCGroup(policy.PolicyTargetMixin, tables.LinkAction): class DeleteVolumeCGroup(policy.PolicyTargetMixin, tables.LinkAction):
name = "deletecg" name = "deletecg"
verbose_name = _("Delete Consistency Group") verbose_name = _("Delete Consistency Group")
url = "horizon:project:volumes:cgroups:delete" url = "horizon:project:cgroups:delete"
classes = ("ajax-modal", "btn-danger") classes = ("ajax-modal", "btn-danger")
policy_rules = (("volume", "consistencygroup:delete"), ) policy_rules = (("volume", "consistencygroup:delete"), )
@ -40,7 +40,7 @@ class DeleteVolumeCGroup(policy.PolicyTargetMixin, tables.LinkAction):
class RemoveAllVolumes(policy.PolicyTargetMixin, tables.LinkAction): class RemoveAllVolumes(policy.PolicyTargetMixin, tables.LinkAction):
name = "remove_vols" name = "remove_vols"
verbose_name = _("Remove Volumes from Consistency Group") verbose_name = _("Remove Volumes from Consistency Group")
url = "horizon:project:volumes:cgroups:remove_volumes" url = "horizon:project:cgroups:remove_volumes"
classes = ("ajax-modal",) classes = ("ajax-modal",)
policy_rules = (("volume", "consistencygroup:update"), ) policy_rules = (("volume", "consistencygroup:update"), )
@ -48,7 +48,7 @@ class RemoveAllVolumes(policy.PolicyTargetMixin, tables.LinkAction):
class EditVolumeCGroup(policy.PolicyTargetMixin, tables.LinkAction): class EditVolumeCGroup(policy.PolicyTargetMixin, tables.LinkAction):
name = "edit" name = "edit"
verbose_name = _("Edit Consistency Group") verbose_name = _("Edit Consistency Group")
url = "horizon:project:volumes:cgroups:update" url = "horizon:project:cgroups:update"
classes = ("ajax-modal",) classes = ("ajax-modal",)
policy_rules = (("volume", "consistencygroup:update"),) policy_rules = (("volume", "consistencygroup:update"),)
@ -56,7 +56,7 @@ class EditVolumeCGroup(policy.PolicyTargetMixin, tables.LinkAction):
class ManageVolumes(policy.PolicyTargetMixin, tables.LinkAction): class ManageVolumes(policy.PolicyTargetMixin, tables.LinkAction):
name = "manage" name = "manage"
verbose_name = _("Manage Volumes") verbose_name = _("Manage Volumes")
url = "horizon:project:volumes:cgroups:manage" url = "horizon:project:cgroups:manage"
classes = ("ajax-modal",) classes = ("ajax-modal",)
policy_rules = (("volume", "consistencygroup:update"),) policy_rules = (("volume", "consistencygroup:update"),)
@ -70,7 +70,7 @@ class ManageVolumes(policy.PolicyTargetMixin, tables.LinkAction):
class CreateSnapshot(policy.PolicyTargetMixin, tables.LinkAction): class CreateSnapshot(policy.PolicyTargetMixin, tables.LinkAction):
name = "create_snapshot" name = "create_snapshot"
verbose_name = _("Create Snapshot") verbose_name = _("Create Snapshot")
url = "horizon:project:volumes:cgroups:create_snapshot" url = "horizon:project:cgroups:create_snapshot"
classes = ("ajax-modal",) classes = ("ajax-modal",)
policy_rules = (("volume", "consistencygroup:create_cgsnapshot"),) policy_rules = (("volume", "consistencygroup:create_cgsnapshot"),)
@ -84,7 +84,7 @@ class CreateSnapshot(policy.PolicyTargetMixin, tables.LinkAction):
class CloneCGroup(policy.PolicyTargetMixin, tables.LinkAction): class CloneCGroup(policy.PolicyTargetMixin, tables.LinkAction):
name = "clone_cgroup" name = "clone_cgroup"
verbose_name = _("Clone Consistency Group") verbose_name = _("Clone Consistency Group")
url = "horizon:project:volumes:cgroups:clone_cgroup" url = "horizon:project:cgroups:clone_cgroup"
classes = ("ajax-modal",) classes = ("ajax-modal",)
policy_rules = (("volume", "consistencygroup:create"),) policy_rules = (("volume", "consistencygroup:create"),)
@ -142,7 +142,7 @@ class VolumeCGroupsTable(tables.DataTable):
name = tables.WrappingColumn("name", name = tables.WrappingColumn("name",
verbose_name=_("Name"), verbose_name=_("Name"),
link="horizon:project:volumes:cgroups:detail") link="horizon:project:cgroups:detail")
description = tables.Column("description", description = tables.Column("description",
verbose_name=_("Description"), verbose_name=_("Description"),
truncate=40) truncate=40)

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/cgroups/_detail_overview.html") template_name = ("project/cgroups/_detail_overview.html")
def get_context_data(self, request): def get_context_data(self, request):
cgroup = self.tab_group.kwargs['cgroup'] cgroup = self.tab_group.kwargs['cgroup']
return {"cgroup": cgroup} return {"cgroup": cgroup}
def get_redirect_url(self): def get_redirect_url(self):
return reverse('horizon:project:volumes:index') return reverse('horizon:project:cgroups:index')
class CGroupsDetailTabs(tabs.TabGroup): class CGroupsDetailTabs(tabs.TabGroup):

View File

@ -19,9 +19,7 @@ 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:cgroups:index')
VOLUME_CGROUPS_TAB_URL = urlunquote(reverse(
'horizon:project:volumes:cgroups_tab'))
VOLUME_CGROUPS_SNAP_TAB_URL = urlunquote(reverse( VOLUME_CGROUPS_SNAP_TAB_URL = urlunquote(reverse(
'horizon:project:volumes:cg_snapshots_tab')) 'horizon:project:volumes:cg_snapshots_tab'))
@ -62,10 +60,10 @@ class ConsistencyGroupTests(test.TestCase):
.AndReturn(cgroup) .AndReturn(cgroup)
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:project:volumes:cgroups:create') url = reverse('horizon:project:cgroups:create')
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: ('extension_supported', @test.create_stubs({cinder: ('extension_supported',
'availability_zone_list', 'availability_zone_list',
@ -101,10 +99,10 @@ class ConsistencyGroupTests(test.TestCase):
.AndRaise(self.exceptions.cinder) .AndRaise(self.exceptions.cinder)
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:project:volumes:cgroups:create') url = reverse('horizon:project:cgroups:create')
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)
self.assertIn("Unable to create consistency group.", self.assertIn("Unable to create consistency group.",
res.cookies.output()) res.cookies.output())
@ -119,11 +117,11 @@ class ConsistencyGroupTests(test.TestCase):
force=False) force=False)
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:project:volumes:cgroups:delete', url = reverse('horizon:project:cgroups:delete',
args=[cgroup.id]) args=[cgroup.id])
res = self.client.post(url) res = self.client.post(url)
self.assertNoFormErrors(res) self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, VOLUME_INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({cinder: ('volume_cgroup_get', @test.create_stubs({cinder: ('volume_cgroup_get',
'volume_cgroup_delete')}) 'volume_cgroup_delete')})
@ -137,11 +135,11 @@ class ConsistencyGroupTests(test.TestCase):
force=True) force=True)
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:project:volumes:cgroups:delete', url = reverse('horizon:project:cgroups:delete',
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_INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({cinder: ('volume_cgroup_get', @test.create_stubs({cinder: ('volume_cgroup_get',
'volume_cgroup_delete')}) 'volume_cgroup_delete')})
@ -157,11 +155,11 @@ class ConsistencyGroupTests(test.TestCase):
AndRaise(self.exceptions.cinder) AndRaise(self.exceptions.cinder)
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:project:volumes:cgroups:delete', url = reverse('horizon:project:cgroups:delete',
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_INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({cinder: ('volume_cgroup_update', @test.create_stubs({cinder: ('volume_cgroup_update',
'volume_cgroup_get')}) 'volume_cgroup_get')})
@ -183,11 +181,11 @@ class ConsistencyGroupTests(test.TestCase):
.AndReturn(cgroup) .AndReturn(cgroup)
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:project:volumes:cgroups:update', url = reverse('horizon:project:cgroups:update',
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_INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({cinder: ('volume_cgroup_update', @test.create_stubs({cinder: ('volume_cgroup_update',
'volume_cgroup_get')}) 'volume_cgroup_get')})
@ -209,11 +207,11 @@ class ConsistencyGroupTests(test.TestCase):
.AndReturn(cgroup) .AndReturn(cgroup)
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:project:volumes:cgroups:update', url = reverse('horizon:project:cgroups:update',
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_INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({cinder: ('volume_cgroup_update', @test.create_stubs({cinder: ('volume_cgroup_update',
'volume_cgroup_get')}) 'volume_cgroup_get')})
@ -233,11 +231,11 @@ class ConsistencyGroupTests(test.TestCase):
.AndReturn(cgroup) .AndReturn(cgroup)
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:project:volumes:cgroups:update', url = reverse('horizon:project:cgroups:update',
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_INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({cinder: ('volume_cgroup_update', @test.create_stubs({cinder: ('volume_cgroup_update',
'volume_cgroup_get')}) 'volume_cgroup_get')})
@ -257,11 +255,11 @@ class ConsistencyGroupTests(test.TestCase):
.AndRaise(self.exceptions.cinder) .AndRaise(self.exceptions.cinder)
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:project:volumes:cgroups:update', url = reverse('horizon:project:cgroups:update',
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_INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({cinder: ('volume_cgroup_get',)}) @test.create_stubs({cinder: ('volume_cgroup_get',)})
def test_detail_view_with_exception(self): def test_detail_view_with_exception(self):
@ -272,11 +270,11 @@ class ConsistencyGroupTests(test.TestCase):
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:project:volumes:cgroups:detail', url = reverse('horizon:project:cgroups:detail',
args=[cgroup.id]) args=[cgroup.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)
@test.create_stubs({cinder: ('volume_cg_snapshot_create',)}) @test.create_stubs({cinder: ('volume_cg_snapshot_create',)})
def test_create_snapshot(self): def test_create_snapshot(self):
@ -294,7 +292,7 @@ class ConsistencyGroupTests(test.TestCase):
.AndReturn(cg_snapshot) .AndReturn(cg_snapshot)
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:project:volumes:cgroups:create_snapshot', url = reverse('horizon:project:cgroups:create_snapshot',
args=[cgroup.id]) args=[cgroup.id])
res = self.client.post(url, formData) res = self.client.post(url, formData)
self.assertNoFormErrors(res) self.assertNoFormErrors(res)
@ -315,8 +313,8 @@ class ConsistencyGroupTests(test.TestCase):
.AndReturn(cgroup) .AndReturn(cgroup)
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:project:volumes:cgroups:clone_cgroup', url = reverse('horizon:project:cgroups:clone_cgroup',
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_TAB_URL) self.assertRedirectsNoFollow(res, INDEX_URL)

View File

@ -12,10 +12,11 @@
from django.conf.urls import url from django.conf.urls import url
from openstack_dashboard.dashboards.project.volumes.cgroups import views from openstack_dashboard.dashboards.project.cgroups import views
urlpatterns = [ urlpatterns = [
url(r'^$', views.CGroupsView.as_view(), name='index'),
url(r'^create/$', url(r'^create/$',
views.CreateView.as_view(), views.CreateView.as_view(),
name='create'), name='create'),

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
from horizon import workflows from horizon import workflows
@ -24,33 +25,49 @@ 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.cgroups \
.cgroups import workflows as vol_cgroup_workflows import forms as vol_cgroup_forms
from openstack_dashboard.dashboards.project.volumes \ from openstack_dashboard.dashboards.project.cgroups \
.cgroups import forms as vol_cgroup_forms import tables as vol_cgroup_tables
from openstack_dashboard.dashboards.project.volumes \ from openstack_dashboard.dashboards.project.cgroups \
.cgroups import tables as vol_cgroup_tables import tabs as vol_cgroup_tabs
from openstack_dashboard.dashboards.project.volumes \ from openstack_dashboard.dashboards.project.cgroups \
.cgroups import tabs as vol_cgroup_tabs import workflows as vol_cgroup_workflows
CGROUP_INFO_FIELDS = ("name", CGROUP_INFO_FIELDS = ("name",
"description") "description")
INDEX_URL = "horizon:project:volumes:index" INDEX_URL = "horizon:project:cgroups:index"
class CGroupsView(tables.DataTableView):
table_class = vol_cgroup_tables.VolumeCGroupsTable
page_title = _("Consistency Groups")
def get_data(self):
try:
cgroups = api.cinder.volume_cgroup_list_with_vol_type_names(
self.request)
except Exception:
cgroups = []
exceptions.handle(self.request, _("Unable to retrieve "
"volume consistency groups."))
return cgroups
class CreateView(workflows.WorkflowView): class CreateView(workflows.WorkflowView):
workflow_class = vol_cgroup_workflows.CreateCGroupWorkflow workflow_class = vol_cgroup_workflows.CreateCGroupWorkflow
template_name = 'project/volumes/cgroups/create.html' template_name = 'project/cgroups/create.html'
page_title = _("Create Volume Consistency Group") page_title = _("Create Volume Consistency Group")
class UpdateView(forms.ModalFormView): class UpdateView(forms.ModalFormView):
template_name = 'project/volumes/cgroups/update.html' template_name = 'project/cgroups/update.html'
page_title = _("Edit Consistency Group") page_title = _("Edit Consistency Group")
form_class = vol_cgroup_forms.UpdateForm form_class = vol_cgroup_forms.UpdateForm
success_url = reverse_lazy('horizon:project:volumes:index') success_url = reverse_lazy('horizon:project:cgroups:index')
submit_url = "horizon:project:volumes:cgroups:update" submit_url = "horizon:project:cgroups:update"
def get_initial(self): def get_initial(self):
cgroup = self.get_object() cgroup = self.get_object()
@ -78,11 +95,11 @@ class UpdateView(forms.ModalFormView):
class RemoveVolumesView(forms.ModalFormView): class RemoveVolumesView(forms.ModalFormView):
template_name = 'project/volumes/cgroups/remove_vols.html' template_name = 'project/cgroups/remove_vols.html'
page_title = _("Remove Volumes from Consistency Group") page_title = _("Remove Volumes from Consistency Group")
form_class = vol_cgroup_forms.RemoveVolsForm form_class = vol_cgroup_forms.RemoveVolsForm
success_url = reverse_lazy('horizon:project:volumes:index') success_url = reverse_lazy('horizon:project:cgroups:index')
submit_url = "horizon:project:volumes:cgroups:remove_volumes" submit_url = "horizon:project:cgroups:remove_volumes"
def get_initial(self): def get_initial(self):
cgroup = self.get_object() cgroup = self.get_object()
@ -109,11 +126,11 @@ class RemoveVolumesView(forms.ModalFormView):
class DeleteView(forms.ModalFormView): class DeleteView(forms.ModalFormView):
template_name = 'project/volumes/cgroups/delete.html' template_name = 'project/cgroups/delete.html'
page_title = _("Delete Consistency Group") page_title = _("Delete Consistency Group")
form_class = vol_cgroup_forms.DeleteForm form_class = vol_cgroup_forms.DeleteForm
success_url = reverse_lazy('horizon:project:volumes:index') success_url = reverse_lazy('horizon:project:cgroups:index')
submit_url = "horizon:project:volumes:cgroups:delete" submit_url = "horizon:project:cgroups:delete"
submit_label = page_title submit_label = page_title
def get_initial(self): def get_initial(self):
@ -170,9 +187,9 @@ class ManageView(workflows.WorkflowView):
class CreateSnapshotView(forms.ModalFormView): class CreateSnapshotView(forms.ModalFormView):
form_class = vol_cgroup_forms.CreateSnapshotForm form_class = vol_cgroup_forms.CreateSnapshotForm
page_title = _("Create Consistency Group Snapshot") page_title = _("Create Consistency Group Snapshot")
template_name = 'project/volumes/cgroups/create_snapshot.html' template_name = 'project/cgroups/create_snapshot.html'
submit_label = _("Create Snapshot") submit_label = _("Create Snapshot")
submit_url = "horizon:project:volumes: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:volumes:cg_snapshots_tab')
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -212,10 +229,10 @@ class CreateSnapshotView(forms.ModalFormView):
class CloneCGroupView(forms.ModalFormView): class CloneCGroupView(forms.ModalFormView):
form_class = vol_cgroup_forms.CloneCGroupForm form_class = vol_cgroup_forms.CloneCGroupForm
page_title = _("Clone Consistency Group") page_title = _("Clone Consistency Group")
template_name = 'project/volumes/cgroups/clone_cgroup.html' template_name = 'project/cgroups/clone_cgroup.html'
submit_label = _("Clone Consistency Group") submit_label = _("Clone Consistency Group")
submit_url = "horizon:project:volumes:cgroups:clone_cgroup" submit_url = "horizon:project:cgroups:clone_cgroup"
success_url = reverse_lazy('horizon:project:volumes:cgroups_tab') success_url = reverse_lazy('horizon:project:cgroups:index')
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(CloneCGroupView, self).get_context_data(**kwargs) context = super(CloneCGroupView, self).get_context_data(**kwargs)
@ -296,7 +313,7 @@ class DetailView(tabs.TabView):
@staticmethod @staticmethod
def get_redirect_url(): def get_redirect_url():
return reverse('horizon:project:volumes:index') return reverse('horizon:project:cgroups:index')
def get_tabs(self, request, *args, **kwargs): def get_tabs(self, request, *args, **kwargs):
cgroup = self.get_data() cgroup = self.get_data()

View File

@ -20,7 +20,7 @@ from horizon import workflows
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.api import cinder from openstack_dashboard.api import cinder
INDEX_URL = "horizon:project:volumes:index" INDEX_URL = "horizon:project:cgroups:index"
CGROUP_VOLUME_MEMBER_SLUG = "update_members" CGROUP_VOLUME_MEMBER_SLUG = "update_members"

View File

@ -95,7 +95,7 @@ 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/volumes/cg_snapshots/create.html'
submit_url = "horizon:project:volumes:cg_snapshots:create_cgroup" submit_url = "horizon:project:volumes:cg_snapshots:create_cgroup"
success_url = reverse_lazy('horizon:project:volumes:cgroups_tab') success_url = reverse_lazy('horizon:project:cgroups:index')
page_title = _("Create Volume Consistency Group") page_title = _("Create Volume Consistency Group")
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):

View File

@ -25,8 +25,6 @@ from openstack_dashboard import policy
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 \
import tables as cgroup_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
@ -124,31 +122,6 @@ class VolumeTab(PagedTableMixin, tabs.TableTab, VolumeTableMixIn):
return volumes return volumes
class CGroupsTab(tabs.TableTab):
table_classes = (cgroup_tables.VolumeCGroupsTable,)
name = _("Consistency Groups")
slug = "cgroups_tab"
template_name = ("horizon/common/_detail_table.html")
preload = False
def allowed(self, request):
return policy.check(
(("volume", "consistencygroup:get_all"),),
request
)
def get_volume_cgroups_data(self):
try:
cgroups = api.cinder.volume_cgroup_list_with_vol_type_names(
self.request)
except Exception:
cgroups = []
exceptions.handle(self.request, _("Unable to retrieve "
"volume consistency groups."))
return cgroups
class CGSnapshotsTab(tabs.TableTab): class CGSnapshotsTab(tabs.TableTab):
table_classes = (cg_snapshots_tables.CGSnapshotsTable,) table_classes = (cg_snapshots_tables.CGSnapshotsTable,)
name = _("Consistency Group Snapshots") name = _("Consistency Group Snapshots")
@ -176,5 +149,5 @@ class CGSnapshotsTab(tabs.TableTab):
class VolumeAndSnapshotTabs(tabs.TabGroup): class VolumeAndSnapshotTabs(tabs.TabGroup):
slug = "volumes_and_snapshots" slug = "volumes_and_snapshots"
tabs = (VolumeTab, CGroupsTab, CGSnapshotsTab) tabs = (VolumeTab, CGSnapshotsTab)
sticky = True sticky = True

View File

@ -14,7 +14,7 @@
<dd>{{ cg_snapshot.status|capfirst }}</dd> <dd>{{ cg_snapshot.status|capfirst }}</dd>
<dt>{% trans "Consistency Group" %}</dt> <dt>{% trans "Consistency Group" %}</dt>
<dd> <dd>
<a href="{% url 'horizon:project:volumes:cgroups:detail' cg_snapshot.consistencygroup_id %}"> <a href="{% url 'horizon:project:cgroups:detail' cg_snapshot.consistencygroup_id %}">
{% if cg_snapshot.cg_name %} {% if cg_snapshot.cg_name %}
{{ cg_snapshot.cg_name }} {{ cg_snapshot.cg_name }}
{% else %} {% else %}

View File

@ -17,8 +17,6 @@ from django.conf.urls import url
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 \
import urls as cgroup_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
@ -27,16 +25,11 @@ 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__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$',
views.IndexView.as_view(), name='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'cgroups/', include(
cgroup_urls,
namespace='cgroups')),
url(r'cg_snapshots/', include( url(r'cg_snapshots/', include(
cg_snapshots_urls, cg_snapshots_urls,
namespace='cg_snapshots')), namespace='cg_snapshots')),

View File

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