Merge "Add the Snapshots tab on the Volume Details page"
This commit is contained in:
commit
cc1196588d
@ -110,6 +110,11 @@ class EditVolumeSnapshot(policy.PolicyTargetMixin, tables.LinkAction):
|
||||
def allowed(self, request, snapshot=None):
|
||||
return snapshot.status == "available"
|
||||
|
||||
def get_link_url(self, datum):
|
||||
params = urlencode({"success_url": self.table.get_full_url()})
|
||||
snapshot_id = self.table.get_object_id(datum)
|
||||
return "?".join([reverse(self.url, args=(snapshot_id,)), params])
|
||||
|
||||
|
||||
class CreateVolumeFromSnapshot(tables.LinkAction):
|
||||
name = "create_from_snapshot"
|
||||
@ -178,15 +183,11 @@ class VolumeSnapshotsFilterAction(tables.FilterAction):
|
||||
if query in snapshot.name.lower()]
|
||||
|
||||
|
||||
class VolumeSnapshotsTable(volume_tables.VolumesTableBase):
|
||||
class VolumeDetailsSnapshotsTable(volume_tables.VolumesTableBase):
|
||||
name = tables.WrappingColumn(
|
||||
"name",
|
||||
verbose_name=_("Name"),
|
||||
link="horizon:project:snapshots:detail")
|
||||
volume_name = SnapshotVolumeNameColumn(
|
||||
"name",
|
||||
verbose_name=_("Volume Name"),
|
||||
link="horizon:project:volumes:detail")
|
||||
|
||||
class Meta(object):
|
||||
name = "volume_snapshots"
|
||||
@ -209,3 +210,13 @@ class VolumeSnapshotsTable(volume_tables.VolumesTableBase):
|
||||
permissions = [
|
||||
('openstack.services.volume', 'openstack.services.volumev2'),
|
||||
]
|
||||
|
||||
|
||||
class VolumeSnapshotsTable(VolumeDetailsSnapshotsTable):
|
||||
volume_name = SnapshotVolumeNameColumn(
|
||||
"name",
|
||||
verbose_name=_("Volume Name"),
|
||||
link="horizon:project:volumes:detail")
|
||||
|
||||
class Meta(VolumeDetailsSnapshotsTable.Meta):
|
||||
pass
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.utils.http import urlencode
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
@ -81,8 +82,11 @@ class UpdateView(forms.ModalFormView):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UpdateView, self).get_context_data(**kwargs)
|
||||
context['snapshot'] = self.get_object()
|
||||
success_url = self.request.GET.get('success_url', "")
|
||||
args = (self.kwargs['snapshot_id'],)
|
||||
context['submit_url'] = reverse(self.submit_url, args=args)
|
||||
params = urlencode({"success_url": success_url})
|
||||
context['submit_url'] = "?".join([reverse(self.submit_url, args=args),
|
||||
params])
|
||||
return context
|
||||
|
||||
def get_initial(self):
|
||||
@ -91,6 +95,12 @@ class UpdateView(forms.ModalFormView):
|
||||
'name': snapshot.name,
|
||||
'description': snapshot.description}
|
||||
|
||||
def get_success_url(self):
|
||||
success_url = self.request.GET.get(
|
||||
"success_url",
|
||||
reverse_lazy("horizon:project:snapshots:index"))
|
||||
return success_url
|
||||
|
||||
|
||||
class DetailView(tabs.TabView):
|
||||
tab_group_class = vol_snapshot_tabs.SnapshotDetailTabs
|
||||
|
@ -34,7 +34,6 @@ from openstack_dashboard import api
|
||||
from openstack_dashboard.api import cinder
|
||||
from openstack_dashboard import policy
|
||||
|
||||
|
||||
DELETABLE_STATES = ("available", "error", "error_extending")
|
||||
|
||||
|
||||
|
@ -14,8 +14,12 @@
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
|
||||
from openstack_dashboard.api import cinder
|
||||
from openstack_dashboard.dashboards.project.snapshots import tables
|
||||
|
||||
|
||||
class OverviewTab(tabs.Tab):
|
||||
name = _("Overview")
|
||||
@ -26,6 +30,31 @@ class OverviewTab(tabs.Tab):
|
||||
return {"volume": self.tab_group.kwargs['volume']}
|
||||
|
||||
|
||||
class VolumeDetailTabs(tabs.TabGroup):
|
||||
class SnapshotTab(tabs.TableTab):
|
||||
table_classes = (tables.VolumeDetailsSnapshotsTable,)
|
||||
name = _("Snapshots")
|
||||
slug = "snapshots_tab"
|
||||
template_name = "horizon/common/_detail_table.html"
|
||||
preload = False
|
||||
|
||||
def get_volume_snapshots_data(self):
|
||||
volume_id = self.tab_group.kwargs['volume_id']
|
||||
try:
|
||||
snapshots = cinder.volume_snapshot_list(
|
||||
self.request, search_opts={'volume_id': volume_id})
|
||||
volume = cinder.volume_get(self.request, volume_id)
|
||||
except Exception:
|
||||
snapshots = []
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to retrieve volume snapshots for "
|
||||
"volume %s.") % volume_id)
|
||||
|
||||
for snapshot in snapshots:
|
||||
snapshot._volume = volume
|
||||
|
||||
return snapshots
|
||||
|
||||
|
||||
class VolumeDetailTabs(tabs.DetailTabsGroup):
|
||||
slug = "volume_details"
|
||||
tabs = (OverviewTab,)
|
||||
tabs = (OverviewTab, SnapshotTab)
|
||||
|
@ -34,6 +34,7 @@ from openstack_dashboard.test import helpers as test
|
||||
from openstack_dashboard.usage import quotas
|
||||
|
||||
|
||||
DETAIL_URL = ('horizon:project:volumes:detail')
|
||||
INDEX_URL = reverse('horizon:project:volumes:index')
|
||||
SEARCH_OPTS = dict(status=api.cinder.VOLUME_STATE_AVAILABLE)
|
||||
|
||||
@ -1490,6 +1491,53 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase):
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(volume.name, volume.id)
|
||||
|
||||
@test.create_stubs({cinder: ('tenant_absolute_limits',
|
||||
'volume_get',
|
||||
'volume_snapshot_list',
|
||||
'message_list'),
|
||||
api.nova: ('server_get',)})
|
||||
def test_detail_view_snapshot_tab(self):
|
||||
volume = self.cinder_volumes.first()
|
||||
server = self.servers.first()
|
||||
snapshots = self.cinder_volume_snapshots.list()
|
||||
this_volume_snapshots = [snapshot for snapshot in snapshots
|
||||
if snapshot.volume_id == volume.id]
|
||||
volume.attachments = [{"server_id": server.id}]
|
||||
|
||||
# Expected api calls for the Overview tab.
|
||||
cinder.volume_get(IsA(http.HttpRequest), volume.id).AndReturn(volume)
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest),
|
||||
search_opts={'volume_id': volume.id})\
|
||||
.AndReturn(snapshots)
|
||||
api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
||||
cinder.tenant_absolute_limits(IsA(http.HttpRequest))\
|
||||
.AndReturn(self.cinder_limits['absolute'])
|
||||
cinder.message_list(
|
||||
IsA(http.HttpRequest),
|
||||
{
|
||||
'resource_uuid': volume.id,
|
||||
'resource_type': 'volume'
|
||||
}
|
||||
).AndReturn([])
|
||||
|
||||
# Expected api calls for the Snapshots tab.
|
||||
cinder.volume_get(IsA(http.HttpRequest), volume.id).AndReturn(volume)
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest),
|
||||
search_opts={'volume_id': volume.id})\
|
||||
.AndReturn(this_volume_snapshots)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = '?'.join([reverse(DETAIL_URL, args=[volume.id]),
|
||||
'='.join(['tab', 'volume_details__snapshots_tab'])])
|
||||
res = self.client.get(url)
|
||||
|
||||
self.assertTemplateUsed(res, 'horizon/common/_detail.html')
|
||||
self.assertEqual(res.context['volume'].id, volume.id)
|
||||
self.assertEqual(len(res.context['table'].data),
|
||||
len(this_volume_snapshots))
|
||||
self.assertNoMessages()
|
||||
|
||||
@test.create_stubs({cinder: ('volume_get',)})
|
||||
def test_detail_view_with_exception(self):
|
||||
volume = self.cinder_volumes.first()
|
||||
|
@ -45,6 +45,9 @@ urlpatterns = [
|
||||
url(r'^(?P<volume_id>[^/]+)/$',
|
||||
views.DetailView.as_view(),
|
||||
name='detail'),
|
||||
url(r'^(?P<volume_id>[^/]+)/\?tab=volume_details__snapshots_tab$',
|
||||
views.DetailView.as_view(),
|
||||
name='snapshots_tab'),
|
||||
url(r'^(?P<volume_id>[^/]+)/upload_to_image/$',
|
||||
views.UploadToImageView.as_view(),
|
||||
name='upload_to_image'),
|
||||
|
@ -140,7 +140,7 @@ class VolumesView(tables.PagedTableMixin, VolumeTableMixIn,
|
||||
return volumes
|
||||
|
||||
|
||||
class DetailView(tabs.TabView):
|
||||
class DetailView(tabs.TabbedTableView):
|
||||
tab_group_class = project_tabs.VolumeDetailTabs
|
||||
template_name = 'horizon/common/_detail.html'
|
||||
page_title = "{{ volume.name|default:volume.id }}"
|
||||
|
Loading…
Reference in New Issue
Block a user