OpenStack Dashboard (Horizon)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

views.py 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. # Copyright 2012 Nebula, Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  4. # not use this file except in compliance with the License. You may obtain
  5. # a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. # License for the specific language governing permissions and limitations
  13. # under the License.
  14. """
  15. Views for managing volumes.
  16. """
  17. from collections import OrderedDict
  18. import json
  19. from django import shortcuts
  20. from django.template.defaultfilters import slugify
  21. from django.urls import reverse
  22. from django.urls import reverse_lazy
  23. from django.utils.decorators import method_decorator
  24. from django.utils import encoding
  25. from django.utils.translation import ugettext_lazy as _
  26. from django.views.decorators.cache import cache_control
  27. from django.views.decorators.cache import never_cache
  28. from django.views import generic
  29. from horizon import exceptions
  30. from horizon import forms
  31. from horizon import tables
  32. from horizon import tabs
  33. from horizon.utils import memoized
  34. from openstack_dashboard.api import cinder
  35. from openstack_dashboard.api import nova
  36. from openstack_dashboard import exceptions as dashboard_exception
  37. from openstack_dashboard.usage import quotas
  38. from openstack_dashboard.utils import filters
  39. from openstack_dashboard.utils import futurist_utils
  40. from openstack_dashboard.dashboards.project.volumes \
  41. import forms as volume_forms
  42. from openstack_dashboard.dashboards.project.volumes \
  43. import tables as volume_tables
  44. from openstack_dashboard.dashboards.project.volumes \
  45. import tabs as project_tabs
  46. class VolumeTableMixIn(object):
  47. _has_more_data = False
  48. _has_prev_data = False
  49. def _get_volumes(self, search_opts=None):
  50. try:
  51. marker, sort_dir = self._get_marker()
  52. volumes, self._has_more_data, self._has_prev_data = \
  53. cinder.volume_list_paged(self.request, marker=marker,
  54. search_opts=search_opts,
  55. sort_dir=sort_dir, paginate=True)
  56. return volumes
  57. except Exception:
  58. exceptions.handle(self.request,
  59. _('Unable to retrieve volume list.'))
  60. return []
  61. def _get_instances(self, search_opts=None):
  62. try:
  63. # TODO(tsufiev): we should pass attached_instance_ids to
  64. # nova.server_list as soon as Nova API allows for this
  65. instances, has_more = nova.server_list(self.request,
  66. search_opts=search_opts)
  67. return instances
  68. except Exception:
  69. exceptions.handle(self.request,
  70. _("Unable to retrieve volume/instance "
  71. "attachment information"))
  72. return []
  73. def _get_volumes_ids_with_snapshots(self, search_opts=None):
  74. try:
  75. volume_ids = []
  76. snapshots = cinder.volume_snapshot_list(
  77. self.request, search_opts=search_opts)
  78. if snapshots:
  79. # extract out the volume ids
  80. volume_ids = set([(s.volume_id) for s in snapshots])
  81. except Exception:
  82. exceptions.handle(self.request,
  83. _("Unable to retrieve snapshot list."))
  84. return volume_ids
  85. def _get_attached_instance_ids(self, volumes):
  86. attached_instance_ids = []
  87. for volume in volumes:
  88. for att in volume.attachments:
  89. server_id = att.get('server_id', None)
  90. if server_id is not None:
  91. attached_instance_ids.append(server_id)
  92. return attached_instance_ids
  93. def _get_groups(self, volumes):
  94. needs_group = False
  95. if volumes and hasattr(volumes[0], 'group_id'):
  96. needs_group = True
  97. if needs_group:
  98. try:
  99. groups_list = cinder.group_list(self.request)
  100. groups = dict((g.id, g) for g in groups_list)
  101. except Exception:
  102. groups = {}
  103. exceptions.handle(self.request,
  104. _("Unable to retrieve volume groups"))
  105. for volume in volumes:
  106. if needs_group:
  107. volume.group = groups.get(volume.group_id)
  108. else:
  109. volume.group = None
  110. # set attachment string and if volume has snapshots
  111. def _set_volume_attributes(self,
  112. volumes,
  113. instances,
  114. volume_ids_with_snapshots):
  115. instances = OrderedDict([(inst.id, inst) for inst in instances])
  116. for volume in volumes:
  117. if volume_ids_with_snapshots:
  118. if volume.id in volume_ids_with_snapshots:
  119. setattr(volume, 'has_snapshot', True)
  120. if instances:
  121. for att in volume.attachments:
  122. server_id = att.get('server_id', None)
  123. att['instance'] = instances.get(server_id, None)
  124. class VolumesView(tables.PagedTableMixin, VolumeTableMixIn,
  125. tables.DataTableView):
  126. table_class = volume_tables.VolumesTable
  127. page_title = _("Volumes")
  128. def get_data(self):
  129. volumes = []
  130. attached_instance_ids = []
  131. instances = []
  132. volume_ids_with_snapshots = []
  133. def _task_get_volumes():
  134. volumes.extend(self._get_volumes())
  135. attached_instance_ids.extend(
  136. self._get_attached_instance_ids(volumes))
  137. def _task_get_instances():
  138. # As long as Nova API does not allow passing attached_instance_ids
  139. # to nova.server_list, this call can be forged to pass anything
  140. # != None
  141. instances.extend(self._get_instances())
  142. # In volumes tab we don't need to know about the assignment
  143. # instance-image, therefore fixing it to an empty value
  144. for instance in instances:
  145. if hasattr(instance, 'image'):
  146. if isinstance(instance.image, dict):
  147. instance.image['name'] = "-"
  148. def _task_get_volumes_snapshots():
  149. volume_ids_with_snapshots.extend(
  150. self._get_volumes_ids_with_snapshots())
  151. futurist_utils.call_functions_parallel(
  152. _task_get_volumes,
  153. _task_get_instances,
  154. _task_get_volumes_snapshots)
  155. self._set_volume_attributes(
  156. volumes, instances, volume_ids_with_snapshots)
  157. self._get_groups(volumes)
  158. return volumes
  159. class DetailView(tabs.TabbedTableView):
  160. tab_group_class = project_tabs.VolumeDetailTabs
  161. template_name = 'horizon/common/_detail.html'
  162. page_title = "{{ volume.name|default:volume.id }}"
  163. def get_context_data(self, **kwargs):
  164. context = super(DetailView, self).get_context_data(**kwargs)
  165. volume, snapshots = self.get_data()
  166. table = volume_tables.VolumesTable(self.request)
  167. context["volume"] = volume
  168. context["url"] = self.get_redirect_url()
  169. context["actions"] = table.render_row_actions(volume)
  170. choices = volume_tables.VolumesTableBase.STATUS_DISPLAY_CHOICES
  171. volume.status_label = filters.get_display_label(choices, volume.status)
  172. return context
  173. def get_search_opts(self, volume):
  174. return {'volume_id': volume.id}
  175. @memoized.memoized_method
  176. def get_data(self):
  177. try:
  178. volume_id = self.kwargs['volume_id']
  179. volume = cinder.volume_get(self.request, volume_id)
  180. search_opts = self.get_search_opts(volume)
  181. snapshots = cinder.volume_snapshot_list(
  182. self.request, search_opts=search_opts)
  183. if snapshots:
  184. setattr(volume, 'has_snapshot', True)
  185. for att in volume.attachments:
  186. att['instance'] = nova.server_get(self.request,
  187. att['server_id'])
  188. if getattr(volume, 'group_id', None):
  189. volume.group = cinder.group_get(self.request, volume.group_id)
  190. else:
  191. volume.group = None
  192. except Exception:
  193. redirect = self.get_redirect_url()
  194. exceptions.handle(self.request,
  195. _('Unable to retrieve volume details.'),
  196. redirect=redirect)
  197. try:
  198. volume.messages = cinder.message_list(
  199. self.request,
  200. {'resource_type': 'volume', 'resource_uuid': volume.id},
  201. )
  202. except Exception:
  203. volume.messages = []
  204. exceptions.handle(
  205. self.request,
  206. _('Unable to retrieve volume messages.'),
  207. ignore=True,
  208. )
  209. return volume, snapshots
  210. def get_redirect_url(self):
  211. return reverse('horizon:project:volumes:index')
  212. def get_tabs(self, request, *args, **kwargs):
  213. volume, snapshots = self.get_data()
  214. return self.tab_group_class(
  215. request, volume=volume, snapshots=snapshots, **kwargs)
  216. class CreateView(forms.ModalFormView):
  217. form_class = volume_forms.CreateForm
  218. template_name = 'project/volumes/create.html'
  219. submit_label = _("Create Volume")
  220. submit_url = reverse_lazy("horizon:project:volumes:create")
  221. success_url = reverse_lazy('horizon:project:volumes:index')
  222. page_title = _("Create Volume")
  223. def get_initial(self):
  224. initial = super(CreateView, self).get_initial()
  225. self.default_vol_type = None
  226. try:
  227. self.default_vol_type = cinder.volume_type_default(self.request)
  228. initial['type'] = self.default_vol_type.name
  229. except dashboard_exception.NOT_FOUND:
  230. pass
  231. return initial
  232. def get_context_data(self, **kwargs):
  233. context = super(CreateView, self).get_context_data(**kwargs)
  234. try:
  235. context['usages'] = quotas.tenant_quota_usages(
  236. self.request, targets=('volumes', 'gigabytes'))
  237. context['volume_types'] = self._get_volume_types()
  238. except Exception:
  239. exceptions.handle(self.request)
  240. return context
  241. def _get_volume_types(self):
  242. volume_types = []
  243. try:
  244. volume_types = cinder.volume_type_list(self.request)
  245. except Exception:
  246. exceptions.handle(self.request,
  247. _('Unable to retrieve volume type list.'))
  248. # check if we have default volume type so we can present the
  249. # description of no volume type differently
  250. no_type_description = None
  251. if self.default_vol_type is None:
  252. message = \
  253. _("If \"No volume type\" is selected, the volume will be "
  254. "created without a volume type.")
  255. no_type_description = encoding.force_text(message)
  256. type_descriptions = [{'name': '',
  257. 'description': no_type_description}] + \
  258. [{'name': type.name,
  259. 'description': getattr(type, "description", "")}
  260. for type in volume_types]
  261. return json.dumps(type_descriptions)
  262. class ExtendView(forms.ModalFormView):
  263. form_class = volume_forms.ExtendForm
  264. template_name = 'project/volumes/extend.html'
  265. submit_label = _("Extend Volume")
  266. submit_url = "horizon:project:volumes:extend"
  267. success_url = reverse_lazy("horizon:project:volumes:index")
  268. page_title = _("Extend Volume")
  269. def get_object(self):
  270. if not hasattr(self, "_object"):
  271. volume_id = self.kwargs['volume_id']
  272. try:
  273. self._object = cinder.volume_get(self.request, volume_id)
  274. except Exception:
  275. self._object = None
  276. exceptions.handle(self.request,
  277. _('Unable to retrieve volume information.'))
  278. return self._object
  279. def get_context_data(self, **kwargs):
  280. context = super(ExtendView, self).get_context_data(**kwargs)
  281. context['volume'] = self.get_object()
  282. args = (self.kwargs['volume_id'],)
  283. context['submit_url'] = reverse(self.submit_url, args=args)
  284. try:
  285. usages = quotas.tenant_quota_usages(self.request,
  286. targets=('gigabytes',))
  287. usages.tally('gigabytes', - context['volume'].size)
  288. context['usages'] = usages
  289. except Exception:
  290. exceptions.handle(self.request)
  291. return context
  292. def get_initial(self):
  293. volume = self.get_object()
  294. return {'id': self.kwargs['volume_id'],
  295. 'name': volume.name,
  296. 'orig_size': volume.size}
  297. class CreateSnapshotView(forms.ModalFormView):
  298. form_class = volume_forms.CreateSnapshotForm
  299. template_name = 'project/volumes/create_snapshot.html'
  300. submit_url = "horizon:project:volumes:create_snapshot"
  301. success_url = reverse_lazy('horizon:project:snapshots:index')
  302. page_title = _("Create Volume Snapshot")
  303. def get_context_data(self, **kwargs):
  304. context = super(CreateSnapshotView, self).get_context_data(**kwargs)
  305. context['volume_id'] = self.kwargs['volume_id']
  306. args = (self.kwargs['volume_id'],)
  307. context['submit_url'] = reverse(self.submit_url, args=args)
  308. try:
  309. volume = cinder.volume_get(self.request, context['volume_id'])
  310. if (volume.status == 'in-use'):
  311. context['attached'] = True
  312. context['form'].set_warning(_("This volume is currently "
  313. "attached to an instance. "
  314. "In some cases, creating a "
  315. "snapshot from an attached "
  316. "volume can result in a "
  317. "corrupted snapshot."))
  318. context['usages'] = quotas.tenant_quota_usages(
  319. self.request, targets=('snapshots', 'gigabytes'))
  320. except Exception:
  321. exceptions.handle(self.request,
  322. _('Unable to retrieve volume information.'))
  323. return context
  324. def get_initial(self):
  325. return {'volume_id': self.kwargs["volume_id"]}
  326. class UploadToImageView(forms.ModalFormView):
  327. form_class = volume_forms.UploadToImageForm
  328. template_name = 'project/volumes/upload_to_image.html'
  329. submit_label = _("Upload")
  330. submit_url = "horizon:project:volumes:upload_to_image"
  331. success_url = reverse_lazy("horizon:project:volumes:index")
  332. page_title = _("Upload Volume to Image")
  333. @memoized.memoized_method
  334. def get_data(self):
  335. try:
  336. volume_id = self.kwargs['volume_id']
  337. volume = cinder.volume_get(self.request, volume_id)
  338. except Exception:
  339. error_message = _(
  340. 'Unable to retrieve volume information for volume: "%s"') \
  341. % volume_id
  342. exceptions.handle(self.request,
  343. error_message,
  344. redirect=self.success_url)
  345. return volume
  346. def get_context_data(self, **kwargs):
  347. context = super(UploadToImageView, self).get_context_data(**kwargs)
  348. context['volume'] = self.get_data()
  349. args = (self.kwargs['volume_id'],)
  350. context['submit_url'] = reverse(self.submit_url, args=args)
  351. return context
  352. def get_initial(self):
  353. volume = self.get_data()
  354. return {'id': self.kwargs['volume_id'],
  355. 'name': volume.name,
  356. 'status': volume.status}
  357. class CreateTransferView(forms.ModalFormView):
  358. form_class = volume_forms.CreateTransferForm
  359. template_name = 'project/volumes/create_transfer.html'
  360. success_url = reverse_lazy('horizon:project:volumes:index')
  361. modal_id = "create_volume_transfer_modal"
  362. submit_label = _("Create Volume Transfer")
  363. submit_url = "horizon:project:volumes:create_transfer"
  364. page_title = _("Create Volume Transfer")
  365. def get_context_data(self, *args, **kwargs):
  366. context = super(CreateTransferView, self).get_context_data(**kwargs)
  367. volume_id = self.kwargs['volume_id']
  368. context['volume_id'] = volume_id
  369. context['submit_url'] = reverse(self.submit_url, args=[volume_id])
  370. return context
  371. def get_initial(self):
  372. return {'volume_id': self.kwargs["volume_id"]}
  373. def get_form_kwargs(self):
  374. kwargs = super(CreateTransferView, self).get_form_kwargs()
  375. kwargs['next_view'] = ShowTransferView
  376. return kwargs
  377. class AcceptTransferView(forms.ModalFormView):
  378. form_class = volume_forms.AcceptTransferForm
  379. template_name = 'project/volumes/accept_transfer.html'
  380. success_url = reverse_lazy('horizon:project:volumes:index')
  381. modal_id = "accept_volume_transfer_modal"
  382. submit_label = _("Accept Volume Transfer")
  383. submit_url = reverse_lazy(
  384. "horizon:project:volumes:accept_transfer")
  385. page_title = _("Accept Volume Transfer")
  386. class ShowTransferView(forms.ModalFormView):
  387. form_class = volume_forms.ShowTransferForm
  388. template_name = 'project/volumes/show_transfer.html'
  389. success_url = reverse_lazy('horizon:project:volumes:index')
  390. modal_id = "show_volume_transfer_modal"
  391. modal_header = _("Volume Transfer")
  392. submit_url = "horizon:project:volumes:show_transfer"
  393. cancel_label = _("Close")
  394. download_label = _("Download transfer credentials")
  395. page_title = _("Volume Transfer Details")
  396. def get_object(self):
  397. try:
  398. return self._object
  399. except AttributeError:
  400. transfer_id = self.kwargs['transfer_id']
  401. try:
  402. self._object = cinder.transfer_get(self.request, transfer_id)
  403. return self._object
  404. except Exception:
  405. exceptions.handle(self.request,
  406. _('Unable to retrieve volume transfer.'))
  407. def get_context_data(self, **kwargs):
  408. context = super(ShowTransferView, self).get_context_data(**kwargs)
  409. context['transfer_id'] = self.kwargs['transfer_id']
  410. context['auth_key'] = self.kwargs['auth_key']
  411. context['download_label'] = self.download_label
  412. context['download_url'] = reverse(
  413. 'horizon:project:volumes:download_transfer_creds',
  414. args=[context['transfer_id'], context['auth_key']]
  415. )
  416. return context
  417. def get_initial(self):
  418. transfer = self.get_object()
  419. return {'id': transfer.id,
  420. 'name': transfer.name,
  421. 'auth_key': self.kwargs['auth_key']}
  422. class UpdateView(forms.ModalFormView):
  423. form_class = volume_forms.UpdateForm
  424. modal_id = "update_volume_modal"
  425. template_name = 'project/volumes/update.html'
  426. submit_url = "horizon:project:volumes:update"
  427. success_url = reverse_lazy("horizon:project:volumes:index")
  428. page_title = _("Edit Volume")
  429. def get_object(self):
  430. if not hasattr(self, "_object"):
  431. vol_id = self.kwargs['volume_id']
  432. try:
  433. self._object = cinder.volume_get(self.request, vol_id)
  434. except Exception:
  435. msg = _('Unable to retrieve volume.')
  436. url = reverse('horizon:project:volumes:index')
  437. exceptions.handle(self.request, msg, redirect=url)
  438. return self._object
  439. def get_context_data(self, **kwargs):
  440. context = super(UpdateView, self).get_context_data(**kwargs)
  441. context['volume'] = self.get_object()
  442. args = (self.kwargs['volume_id'],)
  443. context['submit_url'] = reverse(self.submit_url, args=args)
  444. return context
  445. def get_initial(self):
  446. volume = self.get_object()
  447. return {'volume_id': self.kwargs["volume_id"],
  448. 'name': volume.name,
  449. 'description': volume.description,
  450. 'bootable': volume.is_bootable}
  451. class EditAttachmentsView(tables.DataTableView, forms.ModalFormView):
  452. table_class = volume_tables.AttachmentsTable
  453. form_class = volume_forms.AttachForm
  454. form_id = "attach_volume_form"
  455. modal_id = "attach_volume_modal"
  456. template_name = 'project/volumes/attach.html'
  457. submit_url = "horizon:project:volumes:attach"
  458. success_url = reverse_lazy("horizon:project:volumes:index")
  459. page_title = _("Manage Volume Attachments")
  460. @memoized.memoized_method
  461. def get_object(self):
  462. volume_id = self.kwargs['volume_id']
  463. try:
  464. return cinder.volume_get(self.request, volume_id)
  465. except Exception:
  466. self._object = None
  467. exceptions.handle(self.request,
  468. _('Unable to retrieve volume information.'))
  469. def get_data(self):
  470. attachments = []
  471. volume = self.get_object()
  472. if volume is not None:
  473. for att in volume.attachments:
  474. att['volume_name'] = getattr(volume, 'name', att['device'])
  475. attachments.append(att)
  476. return attachments
  477. def get_initial(self):
  478. try:
  479. instances, has_more = nova.server_list(self.request)
  480. except Exception:
  481. instances = []
  482. exceptions.handle(self.request,
  483. _("Unable to retrieve attachment information."))
  484. return {'volume': self.get_object(),
  485. 'instances': instances}
  486. @memoized.memoized_method
  487. def get_form(self, **kwargs):
  488. form_class = kwargs.get('form_class', self.get_form_class())
  489. return super(EditAttachmentsView, self).get_form(form_class)
  490. def get_context_data(self, **kwargs):
  491. context = super(EditAttachmentsView, self).get_context_data(**kwargs)
  492. context['form'] = self.get_form()
  493. volume = self.get_object()
  494. args = (self.kwargs['volume_id'],)
  495. context['submit_url'] = reverse(self.submit_url, args=args)
  496. if volume and volume.status == 'available':
  497. context['show_attach'] = True
  498. else:
  499. context['show_attach'] = False
  500. context['volume'] = volume
  501. if self.request.is_ajax():
  502. context['hide'] = True
  503. return context
  504. def get(self, request, *args, **kwargs):
  505. # Table action handling
  506. handled = self.construct_tables()
  507. if handled:
  508. return handled
  509. return self.render_to_response(self.get_context_data(**kwargs))
  510. def post(self, request, *args, **kwargs):
  511. form = self.get_form()
  512. if form.is_valid():
  513. return self.form_valid(form)
  514. else:
  515. return self.get(request, *args, **kwargs)
  516. class RetypeView(forms.ModalFormView):
  517. form_class = volume_forms.RetypeForm
  518. modal_id = "retype_volume_modal"
  519. template_name = 'project/volumes/retype.html'
  520. submit_label = _("Change Volume Type")
  521. submit_url = "horizon:project:volumes:retype"
  522. success_url = reverse_lazy("horizon:project:volumes:index")
  523. page_title = _("Change Volume Type")
  524. @memoized.memoized_method
  525. def get_data(self):
  526. try:
  527. volume_id = self.kwargs['volume_id']
  528. volume = cinder.volume_get(self.request, volume_id)
  529. except Exception:
  530. error_message = _(
  531. 'Unable to retrieve volume information for volume: "%s"') \
  532. % volume_id
  533. exceptions.handle(self.request,
  534. error_message,
  535. redirect=self.success_url)
  536. return volume
  537. def get_context_data(self, **kwargs):
  538. context = super(RetypeView, self).get_context_data(**kwargs)
  539. context['volume'] = self.get_data()
  540. args = (self.kwargs['volume_id'],)
  541. context['submit_url'] = reverse(self.submit_url, args=args)
  542. return context
  543. def get_initial(self):
  544. volume = self.get_data()
  545. return {'id': self.kwargs['volume_id'],
  546. 'name': volume.name,
  547. 'volume_type': volume.volume_type}
  548. class EncryptionDetailView(generic.TemplateView):
  549. template_name = 'project/volumes/encryption_detail.html'
  550. page_title = _("Volume Encryption Details: {{ volume.name }}")
  551. def get_context_data(self, **kwargs):
  552. context = super(EncryptionDetailView, self).get_context_data(**kwargs)
  553. volume = self.get_volume_data()
  554. context["encryption_metadata"] = self.get_encryption_data()
  555. context["volume"] = volume
  556. context["page_title"] = _("Volume Encryption Details: "
  557. "%(volume_name)s") % {'volume_name':
  558. volume.name}
  559. return context
  560. @memoized.memoized_method
  561. def get_encryption_data(self):
  562. try:
  563. volume_id = self.kwargs['volume_id']
  564. self._encryption_metadata = \
  565. cinder.volume_get_encryption_metadata(self.request,
  566. volume_id)
  567. except Exception:
  568. redirect = self.get_redirect_url()
  569. exceptions.handle(self.request,
  570. _('Unable to retrieve volume encryption '
  571. 'details.'),
  572. redirect=redirect)
  573. return self._encryption_metadata
  574. @memoized.memoized_method
  575. def get_volume_data(self):
  576. try:
  577. volume_id = self.kwargs['volume_id']
  578. volume = cinder.volume_get(self.request, volume_id)
  579. except Exception:
  580. redirect = self.get_redirect_url()
  581. exceptions.handle(self.request,
  582. _('Unable to retrieve volume details.'),
  583. redirect=redirect)
  584. return volume
  585. def get_redirect_url(self):
  586. return reverse('horizon:project:volumes:index')
  587. class DownloadTransferCreds(generic.View):
  588. # TODO(Itxaka): Remove cache_control in django >= 1.9
  589. # https://code.djangoproject.com/ticket/13008
  590. @method_decorator(cache_control(max_age=0, no_cache=True,
  591. no_store=True, must_revalidate=True))
  592. @method_decorator(never_cache)
  593. def get(self, request, transfer_id, auth_key):
  594. try:
  595. transfer = cinder.transfer_get(self.request, transfer_id)
  596. except Exception:
  597. transfer = None
  598. context = {'transfer': {
  599. 'name': getattr(transfer, 'name', ''),
  600. 'id': transfer_id,
  601. 'auth_key': auth_key,
  602. }}
  603. response = shortcuts.render_to_response(
  604. 'project/volumes/download_transfer_creds.html',
  605. context, content_type='application/text')
  606. response['Content-Disposition'] = (
  607. 'attachment; filename=%s.txt' % slugify(transfer_id))
  608. return response