Move Volume snapshots out of tabbed panel
In doing this I found that the permission check on the project Volume Snapshots table was incorrectly defined, resulting in always-denied. Removing a level of nesting in the permission structure fixed it. Change-Id: I1bc5bd4820700d29861ec1dcd6c87a41b5230266 Implements: blueprint reorganise-volumes
This commit is contained in:
parent
5679634fed
commit
97589ec0fb
@ -31,3 +31,4 @@ from horizon.tables.views import DataTableView # noqa
|
|||||||
from horizon.tables.views import MixedDataTableView # noqa
|
from horizon.tables.views import MixedDataTableView # noqa
|
||||||
from horizon.tables.views import MultiTableMixin # noqa
|
from horizon.tables.views import MultiTableMixin # noqa
|
||||||
from horizon.tables.views import MultiTableView # noqa
|
from horizon.tables.views import MultiTableView # noqa
|
||||||
|
from horizon.tables.views import PagedTableMixin # noqa
|
||||||
|
@ -357,3 +357,30 @@ class MixedDataTableView(DataTableView):
|
|||||||
'in table %s to use MixedDataTableView.'
|
'in table %s to use MixedDataTableView.'
|
||||||
% self.table._meta.name)
|
% self.table._meta.name)
|
||||||
return self.table
|
return self.table
|
||||||
|
|
||||||
|
|
||||||
|
class PagedTableMixin(object):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(PagedTableMixin, self).__init__(*args, **kwargs)
|
||||||
|
self._has_prev_data = False
|
||||||
|
self._has_more_data = False
|
||||||
|
|
||||||
|
def has_prev_data(self, table):
|
||||||
|
return self._has_prev_data
|
||||||
|
|
||||||
|
def has_more_data(self, table):
|
||||||
|
return self._has_more_data
|
||||||
|
|
||||||
|
def _get_marker(self):
|
||||||
|
try:
|
||||||
|
meta = self.table_class._meta
|
||||||
|
except AttributeError:
|
||||||
|
meta = self.table_classes[0]._meta
|
||||||
|
prev_marker = self.request.GET.get(meta.prev_pagination_param, None)
|
||||||
|
if prev_marker:
|
||||||
|
return prev_marker, "asc"
|
||||||
|
else:
|
||||||
|
marker = self.request.GET.get(meta.pagination_param, None)
|
||||||
|
if marker:
|
||||||
|
return marker, "desc"
|
||||||
|
return None, "desc"
|
||||||
|
@ -18,7 +18,7 @@ from horizon import tables
|
|||||||
from openstack_dashboard.api import cinder
|
from openstack_dashboard.api import cinder
|
||||||
from openstack_dashboard.api import keystone
|
from openstack_dashboard.api import keystone
|
||||||
|
|
||||||
from openstack_dashboard.dashboards.project.volumes.snapshots \
|
from openstack_dashboard.dashboards.project.snapshots \
|
||||||
import tables as snapshots_tables
|
import tables as snapshots_tables
|
||||||
from openstack_dashboard.dashboards.project.volumes.volumes \
|
from openstack_dashboard.dashboards.project.volumes.volumes \
|
||||||
import tables as volumes_tables
|
import tables as volumes_tables
|
||||||
|
@ -15,14 +15,14 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
from horizon import tabs
|
from horizon import tabs
|
||||||
|
|
||||||
from openstack_dashboard.dashboards.project.volumes.snapshots \
|
from openstack_dashboard.dashboards.project.snapshots \
|
||||||
import tabs as overview_tab
|
import tabs as overview_tab
|
||||||
|
|
||||||
|
|
||||||
class OverviewTab(overview_tab.OverviewTab):
|
class OverviewTab(overview_tab.OverviewTab):
|
||||||
name = _("Overview")
|
name = _("Overview")
|
||||||
slug = "overview"
|
slug = "overview"
|
||||||
template_name = ("project/volumes/snapshots/_detail_overview.html")
|
template_name = ("project/snapshots/_detail_overview.html")
|
||||||
|
|
||||||
def get_redirect_url(self):
|
def get_redirect_url(self):
|
||||||
return reverse('horizon:admin:volumes:index')
|
return reverse('horizon:admin:volumes:index')
|
||||||
|
@ -24,7 +24,7 @@ from openstack_dashboard.dashboards.admin.volumes.snapshots \
|
|||||||
import forms as vol_snapshot_forms
|
import forms as vol_snapshot_forms
|
||||||
from openstack_dashboard.dashboards.admin.volumes.snapshots \
|
from openstack_dashboard.dashboards.admin.volumes.snapshots \
|
||||||
import tabs as vol_snapshot_tabs
|
import tabs as vol_snapshot_tabs
|
||||||
from openstack_dashboard.dashboards.project.volumes.snapshots \
|
from openstack_dashboard.dashboards.project.snapshots \
|
||||||
import views
|
import views
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ from openstack_dashboard.dashboards.project.volumes \
|
|||||||
import tabs as volumes_tabs
|
import tabs as volumes_tabs
|
||||||
|
|
||||||
|
|
||||||
class VolumeTab(volumes_tabs.PagedTableMixin, tabs.TableTab,
|
class VolumeTab(tables.PagedTableMixin, tabs.TableTab,
|
||||||
volumes_tabs.VolumeTableMixIn, tables.DataTableView):
|
volumes_tabs.VolumeTableMixIn, tables.DataTableView):
|
||||||
table_classes = (volumes_tables.VolumesTable,)
|
table_classes = (volumes_tables.VolumesTable,)
|
||||||
name = _("Volumes")
|
name = _("Volumes")
|
||||||
@ -159,7 +159,7 @@ class VolumeTypesTab(tabs.TableTab, volumes_tabs.VolumeTableMixIn):
|
|||||||
return qos_specs
|
return qos_specs
|
||||||
|
|
||||||
|
|
||||||
class SnapshotTab(volumes_tabs.PagedTableMixin, tabs.TableTab):
|
class SnapshotTab(tables.PagedTableMixin, tabs.TableTab):
|
||||||
table_classes = (snapshots_tables.VolumeSnapshotsTable,)
|
table_classes = (snapshots_tables.VolumeSnapshotsTable,)
|
||||||
name = _("Volume Snapshots")
|
name = _("Volume Snapshots")
|
||||||
slug = "snapshots_tab"
|
slug = "snapshots_tab"
|
||||||
|
@ -24,7 +24,7 @@ from mox3.mox import IsA # noqa
|
|||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard.api import cinder
|
from openstack_dashboard.api import cinder
|
||||||
from openstack_dashboard.api import keystone
|
from openstack_dashboard.api import keystone
|
||||||
from openstack_dashboard.dashboards.project.volumes.snapshots \
|
from openstack_dashboard.dashboards.project.snapshots \
|
||||||
import tables as snapshot_tables
|
import tables as snapshot_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
|
||||||
|
@ -40,7 +40,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:snapshots:index")
|
||||||
exceptions.handle(request,
|
exceptions.handle(request,
|
||||||
_('Unable to update volume snapshot.'),
|
_('Unable to update volume snapshot.'),
|
||||||
redirect=redirect)
|
redirect=redirect)
|
26
openstack_dashboard/dashboards/project/snapshots/panel.py
Normal file
26
openstack_dashboard/dashboards/project/snapshots/panel.py
Normal 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 Snapshots(horizon.Panel):
|
||||||
|
name = _("Snapshots")
|
||||||
|
slug = 'snapshots'
|
||||||
|
permissions = (
|
||||||
|
('openstack.services.volume', 'openstack.services.volumev2'),
|
||||||
|
)
|
||||||
|
policy_rules = (("volume", "volume:get_all_snapshots"),)
|
@ -52,7 +52,7 @@ class LaunchSnapshot(volume_tables.LaunchVolume):
|
|||||||
class LaunchSnapshotNG(LaunchSnapshot):
|
class LaunchSnapshotNG(LaunchSnapshot):
|
||||||
name = "launch_snapshot_ng"
|
name = "launch_snapshot_ng"
|
||||||
verbose_name = _("Launch as Instance")
|
verbose_name = _("Launch as Instance")
|
||||||
url = "horizon:project:volumes:snapshots_tab"
|
url = "horizon:project:snapshots:index"
|
||||||
classes = ("btn-launch", )
|
classes = ("btn-launch", )
|
||||||
ajax = False
|
ajax = False
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ class DeleteVolumeSnapshot(policy.PolicyTargetMixin, tables.DeleteAction):
|
|||||||
class EditVolumeSnapshot(policy.PolicyTargetMixin, tables.LinkAction):
|
class EditVolumeSnapshot(policy.PolicyTargetMixin, tables.LinkAction):
|
||||||
name = "edit"
|
name = "edit"
|
||||||
verbose_name = _("Edit Snapshot")
|
verbose_name = _("Edit Snapshot")
|
||||||
url = "horizon:project:volumes:snapshots:update"
|
url = "horizon:project:snapshots:update"
|
||||||
classes = ("ajax-modal",)
|
classes = ("ajax-modal",)
|
||||||
icon = "pencil"
|
icon = "pencil"
|
||||||
policy_rules = (("volume", "volume:update_snapshot"),)
|
policy_rules = (("volume", "volume:update_snapshot"),)
|
||||||
@ -189,7 +189,7 @@ class VolumeSnapshotsTable(volume_tables.VolumesTableBase):
|
|||||||
name = tables.WrappingColumn(
|
name = tables.WrappingColumn(
|
||||||
"name",
|
"name",
|
||||||
verbose_name=_("Name"),
|
verbose_name=_("Name"),
|
||||||
link="horizon:project:volumes:snapshots:detail")
|
link="horizon:project:snapshots:detail")
|
||||||
volume_name = SnapshotVolumeNameColumn(
|
volume_name = SnapshotVolumeNameColumn(
|
||||||
"name",
|
"name",
|
||||||
verbose_name=_("Volume Name"),
|
verbose_name=_("Volume Name"),
|
||||||
@ -213,6 +213,6 @@ class VolumeSnapshotsTable(volume_tables.VolumesTableBase):
|
|||||||
UpdateMetadata))
|
UpdateMetadata))
|
||||||
row_class = UpdateRow
|
row_class = UpdateRow
|
||||||
status_columns = ("status",)
|
status_columns = ("status",)
|
||||||
permissions = [(
|
permissions = [
|
||||||
('openstack.services.volume', 'openstack.services.volumev2'),
|
('openstack.services.volume', 'openstack.services.volumev2'),
|
||||||
)]
|
]
|
@ -24,7 +24,7 @@ from openstack_dashboard.api import cinder
|
|||||||
class OverviewTab(tabs.Tab):
|
class OverviewTab(tabs.Tab):
|
||||||
name = _("Overview")
|
name = _("Overview")
|
||||||
slug = "overview"
|
slug = "overview"
|
||||||
template_name = ("project/volumes/snapshots/_detail_overview.html")
|
template_name = ("project/snapshots/_detail_overview.html")
|
||||||
|
|
||||||
def get_context_data(self, request):
|
def get_context_data(self, request):
|
||||||
try:
|
try:
|
||||||
@ -39,7 +39,7 @@ class OverviewTab(tabs.Tab):
|
|||||||
"volume": volume}
|
"volume": volume}
|
||||||
|
|
||||||
def get_redirect_url(self):
|
def get_redirect_url(self):
|
||||||
return reverse('horizon:project:volumes:index')
|
return reverse('horizon:project:snapshots:index')
|
||||||
|
|
||||||
|
|
||||||
class SnapshotDetailTabs(tabs.TabGroup):
|
class SnapshotDetailTabs(tabs.TabGroup):
|
@ -16,21 +16,112 @@
|
|||||||
# 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 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.api import cinder
|
from openstack_dashboard.api import cinder
|
||||||
|
from openstack_dashboard.dashboards.project.snapshots \
|
||||||
|
import tables as snapshot_tables
|
||||||
from openstack_dashboard.test import helpers as test
|
from openstack_dashboard.test import helpers as test
|
||||||
from openstack_dashboard.usage import quotas
|
from openstack_dashboard.usage import quotas
|
||||||
|
|
||||||
|
|
||||||
INDEX_URL = reverse('horizon:project:volumes:index')
|
INDEX_URL = reverse('horizon:project:snapshots:index')
|
||||||
VOLUME_SNAPSHOTS_TAB_URL = reverse('horizon:project:volumes:snapshots_tab')
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeSnapshotsViewTests(test.TestCase):
|
class VolumeSnapshotsViewTests(test.TestCase):
|
||||||
|
@test.create_stubs({api.cinder: ('tenant_absolute_limits',
|
||||||
|
'volume_snapshot_list_paged',
|
||||||
|
'volume_list',),
|
||||||
|
api.base: ('is_service_enabled',)})
|
||||||
|
def _test_snapshots_index_paginated(self, marker, sort_dir, snapshots, url,
|
||||||
|
has_more, has_prev):
|
||||||
|
api.base.is_service_enabled(IsA(http.HttpRequest), 'volumev2') \
|
||||||
|
.AndReturn(True)
|
||||||
|
api.base.is_service_enabled(IsA(http.HttpRequest), 'volume') \
|
||||||
|
.AndReturn(True)
|
||||||
|
api.cinder.volume_snapshot_list_paged(
|
||||||
|
IsA(http.HttpRequest), marker=marker, sort_dir=sort_dir,
|
||||||
|
paginate=True).AndReturn([snapshots, has_more, has_prev])
|
||||||
|
api.cinder.volume_list(IsA(http.HttpRequest)).AndReturn(
|
||||||
|
self.cinder_volumes.list())
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
res = self.client.get(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_snapshots_index_paginated(self):
|
||||||
|
mox_snapshots = self.cinder_volume_snapshots.list()
|
||||||
|
size = settings.API_RESULT_PAGE_SIZE
|
||||||
|
base_url = INDEX_URL
|
||||||
|
next = snapshot_tables.VolumeSnapshotsTable._meta.pagination_param
|
||||||
|
|
||||||
|
# get first page
|
||||||
|
expected_snapshots = mox_snapshots[:size]
|
||||||
|
res = self._test_snapshots_index_paginated(
|
||||||
|
marker=None, sort_dir="desc", snapshots=expected_snapshots,
|
||||||
|
url=base_url, has_more=True, has_prev=False)
|
||||||
|
snapshots = res.context['volume_snapshots_table'].data
|
||||||
|
self.assertItemsEqual(snapshots, expected_snapshots)
|
||||||
|
|
||||||
|
# get second page
|
||||||
|
expected_snapshots = mox_snapshots[size:2 * size]
|
||||||
|
marker = expected_snapshots[0].id
|
||||||
|
|
||||||
|
url = base_url + "?%s=%s" % (next, marker)
|
||||||
|
res = self._test_snapshots_index_paginated(
|
||||||
|
marker=marker, sort_dir="desc", snapshots=expected_snapshots,
|
||||||
|
url=url, has_more=True, has_prev=True)
|
||||||
|
snapshots = res.context['volume_snapshots_table'].data
|
||||||
|
self.assertItemsEqual(snapshots, expected_snapshots)
|
||||||
|
|
||||||
|
# get last page
|
||||||
|
expected_snapshots = mox_snapshots[-size:]
|
||||||
|
marker = expected_snapshots[0].id
|
||||||
|
url = base_url + "?%s=%s" % (next, marker)
|
||||||
|
res = self._test_snapshots_index_paginated(
|
||||||
|
marker=marker, sort_dir="desc", snapshots=expected_snapshots,
|
||||||
|
url=url, has_more=False, has_prev=True)
|
||||||
|
snapshots = res.context['volume_snapshots_table'].data
|
||||||
|
self.assertItemsEqual(snapshots, expected_snapshots)
|
||||||
|
|
||||||
|
@override_settings(API_RESULT_PAGE_SIZE=1)
|
||||||
|
def test_snapshots_index_paginated_prev_page(self):
|
||||||
|
mox_snapshots = self.cinder_volume_snapshots.list()
|
||||||
|
size = settings.API_RESULT_PAGE_SIZE
|
||||||
|
base_url = INDEX_URL
|
||||||
|
prev = snapshot_tables.VolumeSnapshotsTable._meta.prev_pagination_param
|
||||||
|
|
||||||
|
# prev from some page
|
||||||
|
expected_snapshots = mox_snapshots[size:2 * size]
|
||||||
|
marker = expected_snapshots[0].id
|
||||||
|
url = base_url + "?%s=%s" % (prev, marker)
|
||||||
|
res = self._test_snapshots_index_paginated(
|
||||||
|
marker=marker, sort_dir="asc", snapshots=expected_snapshots,
|
||||||
|
url=url, has_more=True, has_prev=True)
|
||||||
|
snapshots = res.context['volume_snapshots_table'].data
|
||||||
|
self.assertItemsEqual(snapshots, expected_snapshots)
|
||||||
|
|
||||||
|
# back to first page
|
||||||
|
expected_snapshots = mox_snapshots[:size]
|
||||||
|
marker = expected_snapshots[0].id
|
||||||
|
url = base_url + "?%s=%s" % (prev, marker)
|
||||||
|
res = self._test_snapshots_index_paginated(
|
||||||
|
marker=marker, sort_dir="asc", snapshots=expected_snapshots,
|
||||||
|
url=url, has_more=True, has_prev=False)
|
||||||
|
snapshots = res.context['volume_snapshots_table'].data
|
||||||
|
self.assertItemsEqual(snapshots, expected_snapshots)
|
||||||
|
|
||||||
@test.create_stubs({cinder: ('volume_get',),
|
@test.create_stubs({cinder: ('volume_get',),
|
||||||
quotas: ('tenant_limit_usages',)})
|
quotas: ('tenant_limit_usages',)})
|
||||||
def test_create_snapshot_get(self):
|
def test_create_snapshot_get(self):
|
||||||
@ -77,7 +168,7 @@ class VolumeSnapshotsViewTests(test.TestCase):
|
|||||||
url = reverse('horizon:project:volumes:volumes:create_snapshot',
|
url = reverse('horizon:project:volumes:volumes:create_snapshot',
|
||||||
args=[volume.id])
|
args=[volume.id])
|
||||||
res = self.client.post(url, formData)
|
res = self.client.post(url, formData)
|
||||||
self.assertRedirectsNoFollow(res, VOLUME_SNAPSHOTS_TAB_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
@test.create_stubs({cinder: ('volume_get',
|
@test.create_stubs({cinder: ('volume_get',
|
||||||
'volume_snapshot_create',)})
|
'volume_snapshot_create',)})
|
||||||
@ -103,11 +194,10 @@ class VolumeSnapshotsViewTests(test.TestCase):
|
|||||||
url = reverse('horizon:project:volumes:volumes:create_snapshot',
|
url = reverse('horizon:project:volumes:volumes:create_snapshot',
|
||||||
args=[volume.id])
|
args=[volume.id])
|
||||||
res = self.client.post(url, formData)
|
res = self.client.post(url, formData)
|
||||||
self.assertRedirectsNoFollow(res, VOLUME_SNAPSHOTS_TAB_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
@test.create_stubs({api.cinder: ('volume_snapshot_list_paged',
|
@test.create_stubs({api.cinder: ('volume_snapshot_list_paged',
|
||||||
'volume_list',
|
'volume_list',
|
||||||
'volume_backup_supported',
|
|
||||||
'volume_snapshot_delete',
|
'volume_snapshot_delete',
|
||||||
'tenant_absolute_limits')})
|
'tenant_absolute_limits')})
|
||||||
def test_delete_volume_snapshot(self):
|
def test_delete_volume_snapshot(self):
|
||||||
@ -115,8 +205,6 @@ class VolumeSnapshotsViewTests(test.TestCase):
|
|||||||
volumes = self.cinder_volumes.list()
|
volumes = self.cinder_volumes.list()
|
||||||
snapshot = self.cinder_volume_snapshots.first()
|
snapshot = self.cinder_volume_snapshots.first()
|
||||||
|
|
||||||
api.cinder.volume_backup_supported(IsA(http.HttpRequest)). \
|
|
||||||
MultipleTimes().AndReturn(True)
|
|
||||||
api.cinder.volume_snapshot_list_paged(
|
api.cinder.volume_snapshot_list_paged(
|
||||||
IsA(http.HttpRequest), paginate=True, marker=None,
|
IsA(http.HttpRequest), paginate=True, marker=None,
|
||||||
sort_dir='desc').AndReturn([vol_snapshots, False, False])
|
sort_dir='desc').AndReturn([vol_snapshots, False, False])
|
||||||
@ -126,11 +214,10 @@ class VolumeSnapshotsViewTests(test.TestCase):
|
|||||||
api.cinder.volume_snapshot_delete(IsA(http.HttpRequest), snapshot.id)
|
api.cinder.volume_snapshot_delete(IsA(http.HttpRequest), snapshot.id)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
formData = {'action':
|
formData = {'action': 'volume_snapshots__delete__%s' % snapshot.id}
|
||||||
'volume_snapshots__delete__%s' % snapshot.id}
|
res = self.client.post(INDEX_URL, formData)
|
||||||
res = self.client.post(VOLUME_SNAPSHOTS_TAB_URL, formData)
|
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, VOLUME_SNAPSHOTS_TAB_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
self.assertMessageCount(success=1)
|
self.assertMessageCount(success=1)
|
||||||
|
|
||||||
@test.create_stubs({api.cinder: ('volume_snapshot_get', 'volume_get')})
|
@test.create_stubs({api.cinder: ('volume_snapshot_get', 'volume_get')})
|
||||||
@ -145,7 +232,7 @@ class VolumeSnapshotsViewTests(test.TestCase):
|
|||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
url = reverse('horizon:project:volumes:snapshots:detail',
|
url = reverse('horizon:project:snapshots:detail',
|
||||||
args=[snapshot.id])
|
args=[snapshot.id])
|
||||||
res = self.client.get(url)
|
res = self.client.get(url)
|
||||||
|
|
||||||
@ -161,7 +248,7 @@ class VolumeSnapshotsViewTests(test.TestCase):
|
|||||||
AndRaise(self.exceptions.cinder)
|
AndRaise(self.exceptions.cinder)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
url = reverse('horizon:project:volumes:snapshots:detail',
|
url = reverse('horizon:project:snapshots:detail',
|
||||||
args=[snapshot.id])
|
args=[snapshot.id])
|
||||||
res = self.client.get(url)
|
res = self.client.get(url)
|
||||||
|
|
||||||
@ -180,7 +267,7 @@ class VolumeSnapshotsViewTests(test.TestCase):
|
|||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
url = reverse('horizon:project:volumes:snapshots:detail',
|
url = reverse('horizon:project:snapshots:detail',
|
||||||
args=[snapshot.id])
|
args=[snapshot.id])
|
||||||
res = self.client.get(url)
|
res = self.client.get(url)
|
||||||
|
|
||||||
@ -203,7 +290,7 @@ class VolumeSnapshotsViewTests(test.TestCase):
|
|||||||
formData = {'method': 'UpdateSnapshotForm',
|
formData = {'method': 'UpdateSnapshotForm',
|
||||||
'name': snapshot.name,
|
'name': snapshot.name,
|
||||||
'description': snapshot.description}
|
'description': snapshot.description}
|
||||||
url = reverse(('horizon:project:volumes:snapshots:update'),
|
url = reverse(('horizon:project:snapshots:update'),
|
||||||
args=[snapshot.id])
|
args=[snapshot.id])
|
||||||
res = self.client.post(url, formData)
|
res = self.client.post(url, formData)
|
||||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
@ -12,10 +12,11 @@
|
|||||||
|
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from openstack_dashboard.dashboards.project.volumes.snapshots import views
|
from openstack_dashboard.dashboards.project.snapshots import views
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
url(r'^$', views.SnapshotsView.as_view(), name='index'),
|
||||||
url(r'^(?P<snapshot_id>[^/]+)$',
|
url(r'^(?P<snapshot_id>[^/]+)$',
|
||||||
views.DetailView.as_view(),
|
views.DetailView.as_view(),
|
||||||
name='detail'),
|
name='detail'),
|
@ -16,26 +16,55 @@ 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 \
|
from openstack_dashboard.dashboards.project.snapshots \
|
||||||
.snapshots import forms as vol_snapshot_forms
|
import forms as vol_snapshot_forms
|
||||||
from openstack_dashboard.dashboards.project.volumes \
|
from openstack_dashboard.dashboards.project.snapshots \
|
||||||
.snapshots import tables as vol_snapshot_tables
|
import tables as vol_snapshot_tables
|
||||||
from openstack_dashboard.dashboards.project.volumes \
|
from openstack_dashboard.dashboards.project.snapshots \
|
||||||
.snapshots import tabs as vol_snapshot_tabs
|
import tabs as vol_snapshot_tabs
|
||||||
|
|
||||||
|
|
||||||
|
class SnapshotsView(tables.DataTableView, tables.PagedTableMixin):
|
||||||
|
table_class = vol_snapshot_tables.VolumeSnapshotsTable
|
||||||
|
page_title = _("Volume Snapshots")
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
snapshots = []
|
||||||
|
volumes = {}
|
||||||
|
if api.base.is_service_enabled(self.request, 'volumev2'):
|
||||||
|
try:
|
||||||
|
marker, sort_dir = self._get_marker()
|
||||||
|
snapshots, self._has_more_data, self._has_prev_data = \
|
||||||
|
api.cinder.volume_snapshot_list_paged(
|
||||||
|
self.request, paginate=True, marker=marker,
|
||||||
|
sort_dir=sort_dir)
|
||||||
|
volumes = api.cinder.volume_list(self.request)
|
||||||
|
volumes = dict((v.id, v) for v in volumes)
|
||||||
|
except Exception:
|
||||||
|
raise
|
||||||
|
exceptions.handle(self.request, _("Unable to retrieve "
|
||||||
|
"volume snapshots."))
|
||||||
|
|
||||||
|
for snapshot in snapshots:
|
||||||
|
volume = volumes.get(snapshot.volume_id)
|
||||||
|
setattr(snapshot, '_volume', volume)
|
||||||
|
|
||||||
|
return snapshots
|
||||||
|
|
||||||
|
|
||||||
class UpdateView(forms.ModalFormView):
|
class UpdateView(forms.ModalFormView):
|
||||||
form_class = vol_snapshot_forms.UpdateForm
|
form_class = vol_snapshot_forms.UpdateForm
|
||||||
form_id = "update_snapshot_form"
|
form_id = "update_snapshot_form"
|
||||||
template_name = 'project/volumes/snapshots/update.html'
|
template_name = 'project/snapshots/update.html'
|
||||||
submit_label = _("Save Changes")
|
submit_label = _("Save Changes")
|
||||||
submit_url = "horizon:project:volumes:snapshots:update"
|
submit_url = "horizon:project:snapshots:update"
|
||||||
success_url = reverse_lazy("horizon:project:volumes:index")
|
success_url = reverse_lazy("horizon:project:snapshots:index")
|
||||||
page_title = _("Edit Snapshot")
|
page_title = _("Edit Snapshot")
|
||||||
|
|
||||||
@memoized.memoized_method
|
@memoized.memoized_method
|
||||||
@ -46,7 +75,7 @@ class UpdateView(forms.ModalFormView):
|
|||||||
snap_id)
|
snap_id)
|
||||||
except Exception:
|
except Exception:
|
||||||
msg = _('Unable to retrieve volume snapshot.')
|
msg = _('Unable to retrieve volume snapshot.')
|
||||||
url = reverse('horizon:project:volumes:index')
|
url = reverse('horizon:project:snapshots:index')
|
||||||
exceptions.handle(self.request, msg, redirect=url)
|
exceptions.handle(self.request, msg, redirect=url)
|
||||||
return self._object
|
return self._object
|
||||||
|
|
||||||
@ -96,7 +125,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:snapshots:index')
|
||||||
|
|
||||||
def get_tabs(self, request, *args, **kwargs):
|
def get_tabs(self, request, *args, **kwargs):
|
||||||
snapshot = self.get_data()
|
snapshot = self.get_data()
|
@ -17,6 +17,7 @@ from collections import OrderedDict
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
|
from horizon.tables import PagedTableMixin
|
||||||
from horizon import tabs
|
from horizon import tabs
|
||||||
|
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
@ -28,8 +29,6 @@ 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 \
|
||||||
import tables as cgroup_tables
|
import tables as cgroup_tables
|
||||||
from openstack_dashboard.dashboards.project.volumes.snapshots \
|
|
||||||
import tables as vol_snapshot_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
|
||||||
|
|
||||||
@ -110,30 +109,6 @@ class VolumeTableMixIn(object):
|
|||||||
att['instance'] = instances.get(server_id, None)
|
att['instance'] = instances.get(server_id, None)
|
||||||
|
|
||||||
|
|
||||||
class PagedTableMixin(object):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(PagedTableMixin, self).__init__(*args, **kwargs)
|
|
||||||
self._has_prev_data = False
|
|
||||||
self._has_more_data = False
|
|
||||||
|
|
||||||
def has_prev_data(self, table):
|
|
||||||
return self._has_prev_data
|
|
||||||
|
|
||||||
def has_more_data(self, table):
|
|
||||||
return self._has_more_data
|
|
||||||
|
|
||||||
def _get_marker(self):
|
|
||||||
meta = self.table_classes[0]._meta
|
|
||||||
prev_marker = self.request.GET.get(meta.prev_pagination_param, None)
|
|
||||||
if prev_marker:
|
|
||||||
return prev_marker, "asc"
|
|
||||||
else:
|
|
||||||
marker = self.request.GET.get(meta.pagination_param, None)
|
|
||||||
if marker:
|
|
||||||
return marker, "desc"
|
|
||||||
return None, "desc"
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeTab(PagedTableMixin, tabs.TableTab, VolumeTableMixIn):
|
class VolumeTab(PagedTableMixin, tabs.TableTab, VolumeTableMixIn):
|
||||||
table_classes = (volume_tables.VolumesTable,)
|
table_classes = (volume_tables.VolumesTable,)
|
||||||
name = _("Volumes")
|
name = _("Volumes")
|
||||||
@ -151,36 +126,6 @@ class VolumeTab(PagedTableMixin, tabs.TableTab, VolumeTableMixIn):
|
|||||||
return volumes
|
return volumes
|
||||||
|
|
||||||
|
|
||||||
class SnapshotTab(PagedTableMixin, tabs.TableTab):
|
|
||||||
table_classes = (vol_snapshot_tables.VolumeSnapshotsTable,)
|
|
||||||
name = _("Volume Snapshots")
|
|
||||||
slug = "snapshots_tab"
|
|
||||||
template_name = ("horizon/common/_detail_table.html")
|
|
||||||
preload = False
|
|
||||||
|
|
||||||
def get_volume_snapshots_data(self):
|
|
||||||
snapshots = []
|
|
||||||
volumes = {}
|
|
||||||
if api.base.is_service_enabled(self.request, 'volumev2'):
|
|
||||||
try:
|
|
||||||
marker, sort_dir = self._get_marker()
|
|
||||||
snapshots, self._has_more_data, self._has_prev_data = \
|
|
||||||
api.cinder.volume_snapshot_list_paged(
|
|
||||||
self.request, paginate=True, marker=marker,
|
|
||||||
sort_dir=sort_dir)
|
|
||||||
volumes = api.cinder.volume_list(self.request)
|
|
||||||
volumes = dict((v.id, v) for v in volumes)
|
|
||||||
except Exception:
|
|
||||||
exceptions.handle(self.request, _("Unable to retrieve "
|
|
||||||
"volume snapshots."))
|
|
||||||
|
|
||||||
for snapshot in snapshots:
|
|
||||||
volume = volumes.get(snapshot.volume_id)
|
|
||||||
setattr(snapshot, '_volume', volume)
|
|
||||||
|
|
||||||
return snapshots
|
|
||||||
|
|
||||||
|
|
||||||
class BackupsTab(PagedTableMixin, tabs.TableTab, VolumeTableMixIn):
|
class BackupsTab(PagedTableMixin, tabs.TableTab, VolumeTableMixIn):
|
||||||
table_classes = (backups_tables.BackupsTable,)
|
table_classes = (backups_tables.BackupsTable,)
|
||||||
name = _("Volume Backups")
|
name = _("Volume Backups")
|
||||||
@ -261,5 +206,5 @@ class CGSnapshotsTab(tabs.TableTab):
|
|||||||
|
|
||||||
class VolumeAndSnapshotTabs(tabs.TabGroup):
|
class VolumeAndSnapshotTabs(tabs.TabGroup):
|
||||||
slug = "volumes_and_snapshots"
|
slug = "volumes_and_snapshots"
|
||||||
tabs = (VolumeTab, SnapshotTab, BackupsTab, CGroupsTab, CGSnapshotsTab)
|
tabs = (VolumeTab, BackupsTab, CGroupsTab, CGSnapshotsTab)
|
||||||
sticky = True
|
sticky = True
|
||||||
|
@ -25,16 +25,12 @@ from mox3.mox import IsA # noqa
|
|||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard.dashboards.project.volumes.backups \
|
from openstack_dashboard.dashboards.project.volumes.backups \
|
||||||
import tables as backup_tables
|
import tables as backup_tables
|
||||||
from openstack_dashboard.dashboards.project.volumes.snapshots \
|
|
||||||
import tables as snapshot_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_SNAPSHOTS_TAB_URL = urlunquote(reverse(
|
|
||||||
'horizon:project:volumes:snapshots_tab'))
|
|
||||||
VOLUME_BACKUPS_TAB_URL = urlunquote(reverse(
|
VOLUME_BACKUPS_TAB_URL = urlunquote(reverse(
|
||||||
'horizon:project:volumes:backups_tab'))
|
'horizon:project:volumes:backups_tab'))
|
||||||
|
|
||||||
@ -44,7 +40,6 @@ class VolumeAndSnapshotsAndBackupsTests(test.TestCase):
|
|||||||
'volume_list',
|
'volume_list',
|
||||||
'volume_list_paged',
|
'volume_list_paged',
|
||||||
'volume_snapshot_list',
|
'volume_snapshot_list',
|
||||||
'volume_snapshot_list_paged',
|
|
||||||
'volume_backup_supported',
|
'volume_backup_supported',
|
||||||
'volume_backup_list_paged',
|
'volume_backup_list_paged',
|
||||||
),
|
),
|
||||||
@ -67,13 +62,9 @@ class VolumeAndSnapshotsAndBackupsTests(test.TestCase):
|
|||||||
api.nova.server_list(IsA(http.HttpRequest), search_opts=None,
|
api.nova.server_list(IsA(http.HttpRequest), search_opts=None,
|
||||||
detailed=False).\
|
detailed=False).\
|
||||||
AndReturn([self.servers.list(), False])
|
AndReturn([self.servers.list(), False])
|
||||||
api.cinder.volume_snapshot_list(IsA(http.HttpRequest)).\
|
api.cinder.volume_snapshot_list(IsA(http.HttpRequest)).\
|
||||||
AndReturn(vol_snaps)
|
AndReturn(vol_snaps)
|
||||||
|
|
||||||
api.cinder.volume_snapshot_list_paged(
|
|
||||||
IsA(http.HttpRequest), paginate=True, marker=None,
|
|
||||||
sort_dir='desc').AndReturn([vol_snaps, False, False])
|
|
||||||
api.cinder.volume_list(IsA(http.HttpRequest)).AndReturn(volumes)
|
|
||||||
if backup_supported:
|
if backup_supported:
|
||||||
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',
|
||||||
@ -87,12 +78,6 @@ 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')
|
||||||
|
|
||||||
# Explicitly load the other tabs. If this doesn't work the test
|
|
||||||
# will fail due to "Expected methods never called."
|
|
||||||
res = self.client.get(VOLUME_SNAPSHOTS_TAB_URL)
|
|
||||||
self.assertEqual(res.status_code, 200)
|
|
||||||
self.assertTemplateUsed(res, 'project/volumes/index.html')
|
|
||||||
|
|
||||||
if backup_supported:
|
if backup_supported:
|
||||||
res = self.client.get(VOLUME_BACKUPS_TAB_URL)
|
res = self.client.get(VOLUME_BACKUPS_TAB_URL)
|
||||||
self.assertTemplateUsed(res, 'project/volumes/index.html')
|
self.assertTemplateUsed(res, 'project/volumes/index.html')
|
||||||
@ -209,95 +194,6 @@ class VolumeAndSnapshotsAndBackupsTests(test.TestCase):
|
|||||||
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_snapshot_list_paged',
|
|
||||||
'volume_list',
|
|
||||||
'volume_backup_supported',
|
|
||||||
),
|
|
||||||
api.nova: ('server_list',)})
|
|
||||||
def _test_snapshots_index_paginated(self, marker, sort_dir, snapshots, url,
|
|
||||||
has_more, has_prev):
|
|
||||||
backup_supported = True
|
|
||||||
|
|
||||||
api.cinder.volume_backup_supported(IsA(http.HttpRequest)).\
|
|
||||||
MultipleTimes().AndReturn(backup_supported)
|
|
||||||
api.cinder.volume_snapshot_list_paged(
|
|
||||||
IsA(http.HttpRequest), marker=marker, sort_dir=sort_dir,
|
|
||||||
paginate=True).AndReturn([snapshots, has_more, has_prev])
|
|
||||||
api.cinder.volume_list(IsA(http.HttpRequest)).AndReturn(
|
|
||||||
self.cinder_volumes.list())
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
res = self.client.get(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_snapshots_index_paginated(self):
|
|
||||||
mox_snapshots = self.cinder_volume_snapshots.list()
|
|
||||||
size = settings.API_RESULT_PAGE_SIZE
|
|
||||||
base_url = reverse('horizon:project:volumes:snapshots_tab')
|
|
||||||
next = snapshot_tables.VolumeSnapshotsTable._meta.pagination_param
|
|
||||||
|
|
||||||
# get first page
|
|
||||||
expected_snapshots = mox_snapshots[:size]
|
|
||||||
res = self._test_snapshots_index_paginated(
|
|
||||||
marker=None, sort_dir="desc", snapshots=expected_snapshots,
|
|
||||||
url=base_url, has_more=True, has_prev=False)
|
|
||||||
snapshots = res.context['volume_snapshots_table'].data
|
|
||||||
self.assertItemsEqual(snapshots, expected_snapshots)
|
|
||||||
|
|
||||||
# get second page
|
|
||||||
expected_snapshots = mox_snapshots[size:2 * size]
|
|
||||||
marker = expected_snapshots[0].id
|
|
||||||
|
|
||||||
url = "&".join([base_url, "=".join([next, marker])])
|
|
||||||
res = self._test_snapshots_index_paginated(
|
|
||||||
marker=marker, sort_dir="desc", snapshots=expected_snapshots,
|
|
||||||
url=url, has_more=True, has_prev=True)
|
|
||||||
snapshots = res.context['volume_snapshots_table'].data
|
|
||||||
self.assertItemsEqual(snapshots, expected_snapshots)
|
|
||||||
|
|
||||||
# get last page
|
|
||||||
expected_snapshots = mox_snapshots[-size:]
|
|
||||||
marker = expected_snapshots[0].id
|
|
||||||
url = "&".join([base_url, "=".join([next, marker])])
|
|
||||||
res = self._test_snapshots_index_paginated(
|
|
||||||
marker=marker, sort_dir="desc", snapshots=expected_snapshots,
|
|
||||||
url=url, has_more=False, has_prev=True)
|
|
||||||
snapshots = res.context['volume_snapshots_table'].data
|
|
||||||
self.assertItemsEqual(snapshots, expected_snapshots)
|
|
||||||
|
|
||||||
@override_settings(API_RESULT_PAGE_SIZE=1)
|
|
||||||
def test_snapshots_index_paginated_prev_page(self):
|
|
||||||
mox_snapshots = self.cinder_volume_snapshots.list()
|
|
||||||
size = settings.API_RESULT_PAGE_SIZE
|
|
||||||
base_url = reverse('horizon:project:volumes:snapshots_tab')
|
|
||||||
prev = snapshot_tables.VolumeSnapshotsTable._meta.prev_pagination_param
|
|
||||||
|
|
||||||
# prev from some page
|
|
||||||
expected_snapshots = mox_snapshots[size:2 * size]
|
|
||||||
marker = expected_snapshots[0].id
|
|
||||||
url = "&".join([base_url, "=".join([prev, marker])])
|
|
||||||
res = self._test_snapshots_index_paginated(
|
|
||||||
marker=marker, sort_dir="asc", snapshots=expected_snapshots,
|
|
||||||
url=url, has_more=True, has_prev=True)
|
|
||||||
snapshots = res.context['volume_snapshots_table'].data
|
|
||||||
self.assertItemsEqual(snapshots, expected_snapshots)
|
|
||||||
|
|
||||||
# back to first page
|
|
||||||
expected_snapshots = mox_snapshots[:size]
|
|
||||||
marker = expected_snapshots[0].id
|
|
||||||
url = "&".join([base_url, "=".join([prev, marker])])
|
|
||||||
res = self._test_snapshots_index_paginated(
|
|
||||||
marker=marker, sort_dir="asc", snapshots=expected_snapshots,
|
|
||||||
url=url, has_more=True, has_prev=False)
|
|
||||||
snapshots = res.context['volume_snapshots_table'].data
|
|
||||||
self.assertItemsEqual(snapshots, expected_snapshots)
|
|
||||||
|
|
||||||
@test.create_stubs({api.cinder: ('tenant_absolute_limits',
|
@test.create_stubs({api.cinder: ('tenant_absolute_limits',
|
||||||
'volume_backup_list_paged',
|
'volume_backup_list_paged',
|
||||||
'volume_list',
|
'volume_list',
|
||||||
|
@ -21,16 +21,12 @@ 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 \
|
||||||
import urls as cgroup_urls
|
import urls as cgroup_urls
|
||||||
from openstack_dashboard.dashboards.project.volumes.snapshots \
|
|
||||||
import urls as snapshot_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
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||||
url(r'^\?tab=volumes_and_snapshots__snapshots_tab$',
|
|
||||||
views.IndexView.as_view(), name='snapshots_tab'),
|
|
||||||
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$',
|
url(r'^\?tab=volumes_and_snapshots__backups_tab$',
|
||||||
@ -45,9 +41,6 @@ urlpatterns = [
|
|||||||
url(r'backups/', include(
|
url(r'backups/', include(
|
||||||
backups_urls,
|
backups_urls,
|
||||||
namespace='backups')),
|
namespace='backups')),
|
||||||
url(r'snapshots/', include(
|
|
||||||
snapshot_urls,
|
|
||||||
namespace='snapshots')),
|
|
||||||
url(r'cgroups/', include(
|
url(r'cgroups/', include(
|
||||||
cgroup_urls,
|
cgroup_urls,
|
||||||
namespace='cgroups')),
|
namespace='cgroups')),
|
||||||
|
@ -191,7 +191,7 @@ class CreateSnapshotView(forms.ModalFormView):
|
|||||||
form_class = project_forms.CreateSnapshotForm
|
form_class = project_forms.CreateSnapshotForm
|
||||||
template_name = 'project/volumes/volumes/create_snapshot.html'
|
template_name = 'project/volumes/volumes/create_snapshot.html'
|
||||||
submit_url = "horizon:project:volumes:volumes:create_snapshot"
|
submit_url = "horizon:project:volumes:volumes:create_snapshot"
|
||||||
success_url = reverse_lazy('horizon:project:volumes:snapshots_tab')
|
success_url = reverse_lazy('horizon:project:snapshots:index')
|
||||||
page_title = _("Create Volume Snapshot")
|
page_title = _("Create Volume Snapshot")
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
8
openstack_dashboard/enabled/_1310_volumes_panel_group.py
Normal file
8
openstack_dashboard/enabled/_1310_volumes_panel_group.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
# The slug of the panel group to be added to HORIZON_CONFIG. Required.
|
||||||
|
PANEL_GROUP = 'volumes'
|
||||||
|
# The display name of the PANEL_GROUP. Required.
|
||||||
|
PANEL_GROUP_NAME = _('Volumes')
|
||||||
|
# The slug of the dashboard the PANEL_GROUP associated with. Required.
|
||||||
|
PANEL_GROUP_DASHBOARD = 'project'
|
@ -0,0 +1,9 @@
|
|||||||
|
# The slug of the panel to be added to HORIZON_CONFIG. Required.
|
||||||
|
PANEL = '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.snapshots.panel.Snapshots'
|
Loading…
Reference in New Issue
Block a user