
Note that there was state leaking from one of the existing tests that was relied upon in other tests and moving that test in the run order caused the others to fail. All related tests have been altered to not leak state. Change-Id: I972bc5650fa77044de8a027f570cf2cb41febef8 Implements: blueprint reorganise-volumes
638 lines
24 KiB
Python
638 lines
24 KiB
Python
# Copyright 2012 Nebula, 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.
|
|
|
|
"""
|
|
Views for managing volumes.
|
|
"""
|
|
|
|
from collections import OrderedDict
|
|
import json
|
|
|
|
from django.core.urlresolvers import reverse
|
|
from django.core.urlresolvers import reverse_lazy
|
|
from django import http
|
|
from django.template.defaultfilters import slugify # noqa
|
|
from django.utils.decorators import method_decorator
|
|
from django.utils import encoding
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from django.views.decorators.cache import cache_control
|
|
from django.views.decorators.cache import never_cache
|
|
from django.views import generic
|
|
|
|
from horizon import exceptions
|
|
from horizon import forms
|
|
from horizon import tables
|
|
from horizon import tabs
|
|
from horizon.utils import memoized
|
|
|
|
from openstack_dashboard.api import cinder
|
|
from openstack_dashboard.api import nova
|
|
from openstack_dashboard import exceptions as dashboard_exception
|
|
from openstack_dashboard.usage import quotas
|
|
from openstack_dashboard.utils import filters
|
|
|
|
from openstack_dashboard.dashboards.project.volumes \
|
|
import forms as volume_forms
|
|
from openstack_dashboard.dashboards.project.volumes \
|
|
import tables as volume_tables
|
|
from openstack_dashboard.dashboards.project.volumes \
|
|
import tabs as project_tabs
|
|
|
|
|
|
class VolumeTableMixIn(object):
|
|
_has_more_data = False
|
|
_has_prev_data = False
|
|
|
|
def _get_volumes(self, search_opts=None):
|
|
try:
|
|
marker, sort_dir = self._get_marker()
|
|
volumes, self._has_more_data, self._has_prev_data = \
|
|
cinder.volume_list_paged(self.request, marker=marker,
|
|
search_opts=search_opts,
|
|
sort_dir=sort_dir, paginate=True)
|
|
|
|
if sort_dir == "asc":
|
|
volumes.reverse()
|
|
|
|
return volumes
|
|
except Exception:
|
|
exceptions.handle(self.request,
|
|
_('Unable to retrieve volume list.'))
|
|
return []
|
|
|
|
def _get_instances(self, search_opts=None, instance_ids=None):
|
|
if not instance_ids:
|
|
return []
|
|
try:
|
|
# TODO(tsufiev): we should pass attached_instance_ids to
|
|
# nova.server_list as soon as Nova API allows for this
|
|
instances, has_more = nova.server_list(self.request,
|
|
search_opts=search_opts)
|
|
return instances
|
|
except Exception:
|
|
exceptions.handle(self.request,
|
|
_("Unable to retrieve volume/instance "
|
|
"attachment information"))
|
|
return []
|
|
|
|
def _get_volumes_ids_with_snapshots(self, search_opts=None):
|
|
try:
|
|
volume_ids = []
|
|
snapshots = cinder.volume_snapshot_list(
|
|
self.request, search_opts=search_opts)
|
|
if snapshots:
|
|
# extract out the volume ids
|
|
volume_ids = set([(s.volume_id) for s in snapshots])
|
|
except Exception:
|
|
exceptions.handle(self.request,
|
|
_("Unable to retrieve snapshot list."))
|
|
|
|
return volume_ids
|
|
|
|
def _get_attached_instance_ids(self, volumes):
|
|
attached_instance_ids = []
|
|
for volume in volumes:
|
|
for att in volume.attachments:
|
|
server_id = att.get('server_id', None)
|
|
if server_id is not None:
|
|
attached_instance_ids.append(server_id)
|
|
return attached_instance_ids
|
|
|
|
# set attachment string and if volume has snapshots
|
|
def _set_volume_attributes(self,
|
|
volumes,
|
|
instances,
|
|
volume_ids_with_snapshots):
|
|
instances = OrderedDict([(inst.id, inst) for inst in instances])
|
|
for volume in volumes:
|
|
if volume_ids_with_snapshots:
|
|
if volume.id in volume_ids_with_snapshots:
|
|
setattr(volume, 'has_snapshot', True)
|
|
if instances:
|
|
for att in volume.attachments:
|
|
server_id = att.get('server_id', None)
|
|
att['instance'] = instances.get(server_id, None)
|
|
|
|
|
|
class VolumesView(tables.PagedTableMixin, VolumeTableMixIn,
|
|
tables.DataTableView):
|
|
table_class = volume_tables.VolumesTable
|
|
page_title = _("Volumes")
|
|
|
|
def get_data(self):
|
|
volumes = self._get_volumes()
|
|
attached_instance_ids = self._get_attached_instance_ids(volumes)
|
|
instances = self._get_instances(instance_ids=attached_instance_ids)
|
|
volume_ids_with_snapshots = self._get_volumes_ids_with_snapshots()
|
|
self._set_volume_attributes(
|
|
volumes, instances, volume_ids_with_snapshots)
|
|
return volumes
|
|
|
|
|
|
class DetailView(tabs.TabView):
|
|
tab_group_class = project_tabs.VolumeDetailTabs
|
|
template_name = 'horizon/common/_detail.html'
|
|
page_title = "{{ volume.name|default:volume.id }}"
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(DetailView, self).get_context_data(**kwargs)
|
|
volume = self.get_data()
|
|
table = volume_tables.VolumesTable(self.request)
|
|
context["volume"] = volume
|
|
context["url"] = self.get_redirect_url()
|
|
context["actions"] = table.render_row_actions(volume)
|
|
choices = volume_tables.VolumesTableBase.STATUS_DISPLAY_CHOICES
|
|
volume.status_label = filters.get_display_label(choices, volume.status)
|
|
return context
|
|
|
|
@memoized.memoized_method
|
|
def get_data(self):
|
|
try:
|
|
volume_id = self.kwargs['volume_id']
|
|
volume = cinder.volume_get(self.request, volume_id)
|
|
snapshots = cinder.volume_snapshot_list(
|
|
self.request, search_opts={'volume_id': volume.id})
|
|
if snapshots:
|
|
setattr(volume, 'has_snapshot', True)
|
|
for att in volume.attachments:
|
|
att['instance'] = nova.server_get(self.request,
|
|
att['server_id'])
|
|
except Exception:
|
|
redirect = self.get_redirect_url()
|
|
exceptions.handle(self.request,
|
|
_('Unable to retrieve volume details.'),
|
|
redirect=redirect)
|
|
return volume
|
|
|
|
def get_redirect_url(self):
|
|
return reverse('horizon:project:volumes:index')
|
|
|
|
def get_tabs(self, request, *args, **kwargs):
|
|
volume = self.get_data()
|
|
return self.tab_group_class(request, volume=volume, **kwargs)
|
|
|
|
|
|
class CreateView(forms.ModalFormView):
|
|
form_class = volume_forms.CreateForm
|
|
template_name = 'project/volumes/create.html'
|
|
submit_label = _("Create Volume")
|
|
submit_url = reverse_lazy("horizon:project:volumes:create")
|
|
success_url = reverse_lazy('horizon:project:volumes:index')
|
|
page_title = _("Create Volume")
|
|
|
|
def get_initial(self):
|
|
initial = super(CreateView, self).get_initial()
|
|
self.default_vol_type = None
|
|
try:
|
|
self.default_vol_type = cinder.volume_type_default(self.request)
|
|
initial['type'] = self.default_vol_type.name
|
|
except dashboard_exception.NOT_FOUND:
|
|
pass
|
|
return initial
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(CreateView, self).get_context_data(**kwargs)
|
|
try:
|
|
context['usages'] = quotas.tenant_limit_usages(self.request)
|
|
context['volume_types'] = self._get_volume_types()
|
|
except Exception:
|
|
exceptions.handle(self.request)
|
|
return context
|
|
|
|
def _get_volume_types(self):
|
|
volume_types = []
|
|
try:
|
|
volume_types = cinder.volume_type_list(self.request)
|
|
except Exception:
|
|
exceptions.handle(self.request,
|
|
_('Unable to retrieve volume type list.'))
|
|
|
|
# check if we have default volume type so we can present the
|
|
# description of no volume type differently
|
|
no_type_description = None
|
|
if self.default_vol_type is None:
|
|
message = \
|
|
_("If \"No volume type\" is selected, the volume will be "
|
|
"created without a volume type.")
|
|
|
|
no_type_description = encoding.force_text(message)
|
|
|
|
type_descriptions = [{'name': '',
|
|
'description': no_type_description}] + \
|
|
[{'name': type.name,
|
|
'description': getattr(type, "description", "")}
|
|
for type in volume_types]
|
|
|
|
return json.dumps(type_descriptions)
|
|
|
|
|
|
class ExtendView(forms.ModalFormView):
|
|
form_class = volume_forms.ExtendForm
|
|
template_name = 'project/volumes/extend.html'
|
|
submit_label = _("Extend Volume")
|
|
submit_url = "horizon:project:volumes:extend"
|
|
success_url = reverse_lazy("horizon:project:volumes:index")
|
|
page_title = _("Extend Volume")
|
|
|
|
def get_object(self):
|
|
if not hasattr(self, "_object"):
|
|
volume_id = self.kwargs['volume_id']
|
|
try:
|
|
self._object = cinder.volume_get(self.request, volume_id)
|
|
except Exception:
|
|
self._object = None
|
|
exceptions.handle(self.request,
|
|
_('Unable to retrieve volume information.'))
|
|
return self._object
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(ExtendView, self).get_context_data(**kwargs)
|
|
context['volume'] = self.get_object()
|
|
args = (self.kwargs['volume_id'],)
|
|
context['submit_url'] = reverse(self.submit_url, args=args)
|
|
try:
|
|
usages = quotas.tenant_limit_usages(self.request)
|
|
usages['gigabytesUsed'] = (usages['gigabytesUsed']
|
|
- context['volume'].size)
|
|
context['usages'] = usages
|
|
except Exception:
|
|
exceptions.handle(self.request)
|
|
return context
|
|
|
|
def get_initial(self):
|
|
volume = self.get_object()
|
|
return {'id': self.kwargs['volume_id'],
|
|
'name': volume.name,
|
|
'orig_size': volume.size}
|
|
|
|
|
|
class CreateSnapshotView(forms.ModalFormView):
|
|
form_class = volume_forms.CreateSnapshotForm
|
|
template_name = 'project/volumes/create_snapshot.html'
|
|
submit_url = "horizon:project:volumes:create_snapshot"
|
|
success_url = reverse_lazy('horizon:project:snapshots:index')
|
|
page_title = _("Create Volume Snapshot")
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(CreateSnapshotView, self).get_context_data(**kwargs)
|
|
context['volume_id'] = self.kwargs['volume_id']
|
|
args = (self.kwargs['volume_id'],)
|
|
context['submit_url'] = reverse(self.submit_url, args=args)
|
|
try:
|
|
volume = cinder.volume_get(self.request, context['volume_id'])
|
|
if (volume.status == 'in-use'):
|
|
context['attached'] = True
|
|
context['form'].set_warning(_("This volume is currently "
|
|
"attached to an instance. "
|
|
"In some cases, creating a "
|
|
"snapshot from an attached "
|
|
"volume can result in a "
|
|
"corrupted snapshot."))
|
|
context['usages'] = quotas.tenant_limit_usages(self.request)
|
|
except Exception:
|
|
exceptions.handle(self.request,
|
|
_('Unable to retrieve volume information.'))
|
|
return context
|
|
|
|
def get_initial(self):
|
|
return {'volume_id': self.kwargs["volume_id"]}
|
|
|
|
|
|
class UploadToImageView(forms.ModalFormView):
|
|
form_class = volume_forms.UploadToImageForm
|
|
template_name = 'project/volumes/upload_to_image.html'
|
|
submit_label = _("Upload")
|
|
submit_url = "horizon:project:volumes:upload_to_image"
|
|
success_url = reverse_lazy("horizon:project:volumes:index")
|
|
page_title = _("Upload Volume to Image")
|
|
|
|
@memoized.memoized_method
|
|
def get_data(self):
|
|
try:
|
|
volume_id = self.kwargs['volume_id']
|
|
volume = cinder.volume_get(self.request, volume_id)
|
|
except Exception:
|
|
error_message = _(
|
|
'Unable to retrieve volume information for volume: "%s"') \
|
|
% volume_id
|
|
exceptions.handle(self.request,
|
|
error_message,
|
|
redirect=self.success_url)
|
|
|
|
return volume
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(UploadToImageView, self).get_context_data(**kwargs)
|
|
context['volume'] = self.get_data()
|
|
args = (self.kwargs['volume_id'],)
|
|
context['submit_url'] = reverse(self.submit_url, args=args)
|
|
return context
|
|
|
|
def get_initial(self):
|
|
volume = self.get_data()
|
|
|
|
return {'id': self.kwargs['volume_id'],
|
|
'name': volume.name,
|
|
'status': volume.status}
|
|
|
|
|
|
class CreateTransferView(forms.ModalFormView):
|
|
form_class = volume_forms.CreateTransferForm
|
|
template_name = 'project/volumes/create_transfer.html'
|
|
success_url = reverse_lazy('horizon:project:volumes:index')
|
|
modal_id = "create_volume_transfer_modal"
|
|
submit_label = _("Create Volume Transfer")
|
|
submit_url = "horizon:project:volumes:create_transfer"
|
|
page_title = _("Create Volume Transfer")
|
|
|
|
def get_context_data(self, *args, **kwargs):
|
|
context = super(CreateTransferView, self).get_context_data(**kwargs)
|
|
volume_id = self.kwargs['volume_id']
|
|
context['volume_id'] = volume_id
|
|
context['submit_url'] = reverse(self.submit_url, args=[volume_id])
|
|
return context
|
|
|
|
def get_initial(self):
|
|
return {'volume_id': self.kwargs["volume_id"]}
|
|
|
|
|
|
class AcceptTransferView(forms.ModalFormView):
|
|
form_class = volume_forms.AcceptTransferForm
|
|
template_name = 'project/volumes/accept_transfer.html'
|
|
success_url = reverse_lazy('horizon:project:volumes:index')
|
|
modal_id = "accept_volume_transfer_modal"
|
|
submit_label = _("Accept Volume Transfer")
|
|
submit_url = reverse_lazy(
|
|
"horizon:project:volumes:accept_transfer")
|
|
page_title = _("Accept Volume Transfer")
|
|
|
|
|
|
class ShowTransferView(forms.ModalFormView):
|
|
form_class = volume_forms.ShowTransferForm
|
|
template_name = 'project/volumes/show_transfer.html'
|
|
success_url = reverse_lazy('horizon:project:volumes:index')
|
|
modal_id = "show_volume_transfer_modal"
|
|
submit_url = "horizon:project:volumes:show_transfer"
|
|
cancel_label = _("Close")
|
|
download_label = _("Download transfer credentials")
|
|
page_title = _("Volume Transfer Details")
|
|
|
|
def get_object(self):
|
|
try:
|
|
return self._object
|
|
except AttributeError:
|
|
transfer_id = self.kwargs['transfer_id']
|
|
try:
|
|
self._object = cinder.transfer_get(self.request, transfer_id)
|
|
return self._object
|
|
except Exception:
|
|
exceptions.handle(self.request,
|
|
_('Unable to retrieve volume transfer.'))
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(ShowTransferView, self).get_context_data(**kwargs)
|
|
context['transfer_id'] = self.kwargs['transfer_id']
|
|
context['auth_key'] = self.kwargs['auth_key']
|
|
context['submit_url'] = reverse(self.submit_url, args=[
|
|
context['transfer_id'], context['auth_key']])
|
|
context['download_label'] = self.download_label
|
|
context['download_url'] = reverse(
|
|
'horizon:project:volumes:download_transfer_creds',
|
|
args=[context['transfer_id'], context['auth_key']]
|
|
)
|
|
return context
|
|
|
|
def get_initial(self):
|
|
transfer = self.get_object()
|
|
return {'id': transfer.id,
|
|
'name': transfer.name,
|
|
'auth_key': self.kwargs['auth_key']}
|
|
|
|
|
|
class UpdateView(forms.ModalFormView):
|
|
form_class = volume_forms.UpdateForm
|
|
modal_id = "update_volume_modal"
|
|
template_name = 'project/volumes/update.html'
|
|
submit_url = "horizon:project:volumes:update"
|
|
success_url = reverse_lazy("horizon:project:volumes:index")
|
|
page_title = _("Edit Volume")
|
|
|
|
def get_object(self):
|
|
if not hasattr(self, "_object"):
|
|
vol_id = self.kwargs['volume_id']
|
|
try:
|
|
self._object = cinder.volume_get(self.request, vol_id)
|
|
except Exception:
|
|
msg = _('Unable to retrieve volume.')
|
|
url = reverse('horizon:project:volumes:index')
|
|
exceptions.handle(self.request, msg, redirect=url)
|
|
return self._object
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(UpdateView, self).get_context_data(**kwargs)
|
|
context['volume'] = self.get_object()
|
|
args = (self.kwargs['volume_id'],)
|
|
context['submit_url'] = reverse(self.submit_url, args=args)
|
|
return context
|
|
|
|
def get_initial(self):
|
|
volume = self.get_object()
|
|
return {'volume_id': self.kwargs["volume_id"],
|
|
'name': volume.name,
|
|
'description': volume.description,
|
|
'bootable': volume.is_bootable}
|
|
|
|
|
|
class EditAttachmentsView(tables.DataTableView, forms.ModalFormView):
|
|
table_class = volume_tables.AttachmentsTable
|
|
form_class = volume_forms.AttachForm
|
|
form_id = "attach_volume_form"
|
|
modal_id = "attach_volume_modal"
|
|
template_name = 'project/volumes/attach.html'
|
|
submit_url = "horizon:project:volumes:attach"
|
|
success_url = reverse_lazy("horizon:project:volumes:index")
|
|
page_title = _("Manage Volume Attachments")
|
|
|
|
@memoized.memoized_method
|
|
def get_object(self):
|
|
volume_id = self.kwargs['volume_id']
|
|
try:
|
|
return cinder.volume_get(self.request, volume_id)
|
|
except Exception:
|
|
self._object = None
|
|
exceptions.handle(self.request,
|
|
_('Unable to retrieve volume information.'))
|
|
|
|
def get_data(self):
|
|
attachments = []
|
|
volume = self.get_object()
|
|
if volume is not None:
|
|
for att in volume.attachments:
|
|
att['volume_name'] = getattr(volume, 'name', att['device'])
|
|
attachments.append(att)
|
|
return attachments
|
|
|
|
def get_initial(self):
|
|
try:
|
|
instances, has_more = nova.server_list(self.request)
|
|
except Exception:
|
|
instances = []
|
|
exceptions.handle(self.request,
|
|
_("Unable to retrieve attachment information."))
|
|
return {'volume': self.get_object(),
|
|
'instances': instances}
|
|
|
|
@memoized.memoized_method
|
|
def get_form(self, **kwargs):
|
|
form_class = kwargs.get('form_class', self.get_form_class())
|
|
return super(EditAttachmentsView, self).get_form(form_class)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(EditAttachmentsView, self).get_context_data(**kwargs)
|
|
context['form'] = self.get_form()
|
|
volume = self.get_object()
|
|
args = (self.kwargs['volume_id'],)
|
|
context['submit_url'] = reverse(self.submit_url, args=args)
|
|
if volume and volume.status == 'available':
|
|
context['show_attach'] = True
|
|
else:
|
|
context['show_attach'] = False
|
|
context['volume'] = volume
|
|
if self.request.is_ajax():
|
|
context['hide'] = True
|
|
return context
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
# Table action handling
|
|
handled = self.construct_tables()
|
|
if handled:
|
|
return handled
|
|
return self.render_to_response(self.get_context_data(**kwargs))
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
form = self.get_form()
|
|
if form.is_valid():
|
|
return self.form_valid(form)
|
|
else:
|
|
return self.get(request, *args, **kwargs)
|
|
|
|
|
|
class RetypeView(forms.ModalFormView):
|
|
form_class = volume_forms.RetypeForm
|
|
modal_id = "retype_volume_modal"
|
|
template_name = 'project/volumes/retype.html'
|
|
submit_label = _("Change Volume Type")
|
|
submit_url = "horizon:project:volumes:retype"
|
|
success_url = reverse_lazy("horizon:project:volumes:index")
|
|
page_title = _("Change Volume Type")
|
|
|
|
@memoized.memoized_method
|
|
def get_data(self):
|
|
try:
|
|
volume_id = self.kwargs['volume_id']
|
|
volume = cinder.volume_get(self.request, volume_id)
|
|
except Exception:
|
|
error_message = _(
|
|
'Unable to retrieve volume information for volume: "%s"') \
|
|
% volume_id
|
|
exceptions.handle(self.request,
|
|
error_message,
|
|
redirect=self.success_url)
|
|
|
|
return volume
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(RetypeView, self).get_context_data(**kwargs)
|
|
context['volume'] = self.get_data()
|
|
args = (self.kwargs['volume_id'],)
|
|
context['submit_url'] = reverse(self.submit_url, args=args)
|
|
return context
|
|
|
|
def get_initial(self):
|
|
volume = self.get_data()
|
|
|
|
return {'id': self.kwargs['volume_id'],
|
|
'name': volume.name,
|
|
'volume_type': volume.volume_type}
|
|
|
|
|
|
class EncryptionDetailView(generic.TemplateView):
|
|
template_name = 'project/volumes/encryption_detail.html'
|
|
page_title = _("Volume Encryption Details: {{ volume.name }}")
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(EncryptionDetailView, self).get_context_data(**kwargs)
|
|
volume = self.get_volume_data()
|
|
context["encryption_metadata"] = self.get_encryption_data()
|
|
context["volume"] = volume
|
|
context["page_title"] = _("Volume Encryption Details: "
|
|
"%(volume_name)s") % {'volume_name':
|
|
volume.name}
|
|
return context
|
|
|
|
@memoized.memoized_method
|
|
def get_encryption_data(self):
|
|
try:
|
|
volume_id = self.kwargs['volume_id']
|
|
self._encryption_metadata = \
|
|
cinder.volume_get_encryption_metadata(self.request,
|
|
volume_id)
|
|
except Exception:
|
|
redirect = self.get_redirect_url()
|
|
exceptions.handle(self.request,
|
|
_('Unable to retrieve volume encryption '
|
|
'details.'),
|
|
redirect=redirect)
|
|
return self._encryption_metadata
|
|
|
|
@memoized.memoized_method
|
|
def get_volume_data(self):
|
|
try:
|
|
volume_id = self.kwargs['volume_id']
|
|
volume = cinder.volume_get(self.request, volume_id)
|
|
except Exception:
|
|
redirect = self.get_redirect_url()
|
|
exceptions.handle(self.request,
|
|
_('Unable to retrieve volume details.'),
|
|
redirect=redirect)
|
|
return volume
|
|
|
|
def get_redirect_url(self):
|
|
return reverse('horizon:project:volumes:index')
|
|
|
|
|
|
class DownloadTransferCreds(generic.View):
|
|
# TODO(Itxaka): Remove cache_control in django >= 1.9
|
|
# https://code.djangoproject.com/ticket/13008
|
|
@method_decorator(cache_control(max_age=0, no_cache=True,
|
|
no_store=True, must_revalidate=True))
|
|
@method_decorator(never_cache)
|
|
def get(self, request, transfer_id, auth_key):
|
|
try:
|
|
transfer = cinder.transfer_get(self.request, transfer_id)
|
|
except Exception:
|
|
transfer = None
|
|
response = http.HttpResponse(content_type='application/text')
|
|
response['Content-Disposition'] = \
|
|
'attachment; filename=%s.txt' % slugify(transfer_id)
|
|
response.write('%s: %s\n%s: %s\n%s: %s' % (
|
|
_("Transfer name"),
|
|
getattr(transfer, 'name', ''),
|
|
_("Transfer ID"),
|
|
transfer_id,
|
|
_("Authorization Key"),
|
|
auth_key))
|
|
response['Content-Length'] = str(len(response.content))
|
|
return response
|