From 4e61f626e43efbe83257fc96c6767c7bafa87be2 Mon Sep 17 00:00:00 2001 From: Brianna Poulos Date: Fri, 7 Feb 2014 18:20:32 -0500 Subject: [PATCH] Add volume type encryption update This patch adds support to horizon for volume type encryption update. The modifications are made to the Admin Volume Type table, and add the Update Encryption action to the action column. Implements: blueprint integration-with-cinder-volume-encryption Change-Id: I7b6f1db60818a07feb64eaa464e64397ea477a7b --- openstack_dashboard/api/cinder.py | 5 ++ .../_update_volume_type_encryption.html | 21 ++++++ .../update_volume_type_encryption.html | 7 ++ .../admin/volumes/volume_types/forms.py | 30 +++++++- .../admin/volumes/volume_types/tables.py | 31 ++++++-- .../admin/volumes/volume_types/tests.py | 36 ++++++++++ .../admin/volumes/volume_types/urls.py | 3 + .../admin/volumes/volume_types/views.py | 72 ++++++++++++++++--- ...er-volume-encryption-80a3fe4ff66314b2.yaml | 3 + 9 files changed, 191 insertions(+), 17 deletions(-) create mode 100644 openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_update_volume_type_encryption.html create mode 100644 openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/update_volume_type_encryption.html create mode 100644 releasenotes/notes/bp-integration-with-cinder-volume-encryption-80a3fe4ff66314b2.yaml diff --git a/openstack_dashboard/api/cinder.py b/openstack_dashboard/api/cinder.py index e4fd2062d..d6572a41e 100644 --- a/openstack_dashboard/api/cinder.py +++ b/openstack_dashboard/api/cinder.py @@ -521,6 +521,11 @@ def volume_encryption_type_list(request): return cinderclient(request).volume_encryption_types.list() +def volume_encryption_type_update(request, volume_type_id, data): + return cinderclient(request).volume_encryption_types.update(volume_type_id, + specs=data) + + def volume_type_extra_get(request, type_id, raw=False): vol_type = volume_type_get(request, type_id) extras = vol_type.get_keys() diff --git a/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_update_volume_type_encryption.html b/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_update_volume_type_encryption.html new file mode 100644 index 000000000..187ff3cea --- /dev/null +++ b/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_update_volume_type_encryption.html @@ -0,0 +1,21 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

+ {% trans "Encryption information cannot be updated for a volume type if volumes are currently in use with the volume type." %} +

+

+ {% blocktrans %}The Provider is the class providing encryption support (e.g., LuksEncryptor).{% endblocktrans %} +

+

+ {% blocktrans %}The Control Location is the notional service where encryption is performed (e.g., front-end=Nova). The default value is 'front-end.'{% endblocktrans %} +

+

+ {% blocktrans %}The Cipher is the encryption algorithm/mode to use (e.g., aes-xts-plain64). If the field is left empty, the provider default will be used.{% endblocktrans %} +

+

+ {% blocktrans %}The Key Size is the size of the encryption key, in bits (e.g., 128, 256). If the field is left empty, the provider default will be used.{% endblocktrans %} +

+{% endblock %} diff --git a/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/update_volume_type_encryption.html b/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/update_volume_type_encryption.html new file mode 100644 index 000000000..82841630f --- /dev/null +++ b/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/update_volume_type_encryption.html @@ -0,0 +1,7 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Update Encrypted Volume Type" %}{% endblock %} + +{% block main %} + {% include 'admin/volumes/volume_types/_update_volume_type_encryption.html' %} +{% endblock %} diff --git a/openstack_dashboard/dashboards/admin/volumes/volume_types/forms.py b/openstack_dashboard/dashboards/admin/volumes/volume_types/forms.py index 99b180827..493402758 100644 --- a/openstack_dashboard/dashboards/admin/volumes/volume_types/forms.py +++ b/openstack_dashboard/dashboards/admin/volumes/volume_types/forms.py @@ -104,7 +104,7 @@ class CreateVolumeTypeEncryption(forms.SelfHandlingForm): def handle(self, request, data): try: # Set Cipher to None if empty - if data['cipher'] is u'': + if data['cipher'] == u'': data['cipher'] = None # Create encryption for the volume type @@ -122,6 +122,34 @@ class CreateVolumeTypeEncryption(forms.SelfHandlingForm): redirect=redirect) +class UpdateVolumeTypeEncryption(CreateVolumeTypeEncryption): + + def handle(self, request, data): + try: + # Set Cipher to None if empty + if data['cipher'] == u'': + data['cipher'] = None + + # Update encryption for the volume type + volume_type = cinder.\ + volume_encryption_type_update(request, + data['volume_type_id'], + data) + messages.success(request, _('Successfully updated encryption for ' + 'volume type: %s') % data['name']) + return volume_type + except NotImplementedError: + messages.error(request, _('Updating encryption is not ' + 'implemented. Unable to update ' + ' encrypted volume type.')) + except Exception: + redirect = reverse("horizon:admin:volumes:index") + exceptions.handle(request, + _('Unable to update encrypted volume type.'), + redirect=redirect) + return False + + class ManageQosSpecAssociation(forms.SelfHandlingForm): qos_spec_choice = forms.ChoiceField( label=_("QoS Spec to be associated"), diff --git a/openstack_dashboard/dashboards/admin/volumes/volume_types/tables.py b/openstack_dashboard/dashboards/admin/volumes/volume_types/tables.py index 12b2c83a5..e5757e648 100644 --- a/openstack_dashboard/dashboards/admin/volumes/volume_types/tables.py +++ b/openstack_dashboard/dashboards/admin/volumes/volume_types/tables.py @@ -89,11 +89,21 @@ class CreateVolumeTypeEncryption(tables.LinkAction): policy_rules = (("volume", "volume_extension:volume_type_encryption"),) def allowed(self, request, volume_type): - if _is_vol_type_enc_possible(request): - return (hasattr(volume_type, 'encryption') - and not hasattr(volume_type.encryption, 'provider')) - else: - return False + return (_is_vol_type_enc_possible(request) and + not _does_vol_type_enc_exist(volume_type)) + + +class UpdateVolumeTypeEncryption(tables.LinkAction): + name = "update_encryption" + verbose_name = _("Update Encryption") + url = "horizon:admin:volumes:volume_types:update_type_encryption" + classes = ("ajax-modal",) + icon = "pencil" + policy_rules = (("volume", "volume_extension:volume_type_encryption"),) + + def allowed(self, request, volume_type=None): + return (_is_vol_type_enc_possible(request) and + _does_vol_type_enc_exist(volume_type)) class DeleteVolumeTypeEncryption(tables.DeleteAction): @@ -122,8 +132,14 @@ class DeleteVolumeTypeEncryption(tables.DeleteAction): def allowed(self, request, volume_type=None): return (_is_vol_type_enc_possible(request) and - hasattr(volume_type, 'encryption') and - hasattr(volume_type.encryption, 'provider')) + _does_vol_type_enc_exist(volume_type)) + + +def _does_vol_type_enc_exist(volume_type): + # Check to see if there is an existing encryption information + # for the volume type or not + return (hasattr(volume_type, 'encryption') and + hasattr(volume_type.encryption, 'provider')) def _is_vol_type_enc_possible(request): @@ -235,6 +251,7 @@ class VolumeTypesTable(tables.DataTable): ViewVolumeTypeExtras, ManageQosSpecAssociation, EditVolumeType, + UpdateVolumeTypeEncryption, DeleteVolumeTypeEncryption, DeleteVolumeType,) row_class = UpdateRow diff --git a/openstack_dashboard/dashboards/admin/volumes/volume_types/tests.py b/openstack_dashboard/dashboards/admin/volumes/volume_types/tests.py index 68ee75784..602f48cf2 100644 --- a/openstack_dashboard/dashboards/admin/volumes/volume_types/tests.py +++ b/openstack_dashboard/dashboards/admin/volumes/volume_types/tests.py @@ -226,3 +226,39 @@ class VolumeTypeTests(test.BaseAdminViewTests): redirect = reverse('horizon:admin:volumes:volume_types_tab') self.assertNoFormErrors(res) self.assertRedirectsNoFollow(res, redirect) + + @test.create_stubs({cinder: ('volume_encryption_type_update', + 'volume_encryption_type_get', + 'volume_type_list')}) + def test_update_volume_type_encryption(self): + volume_type = self.volume_types.first() + volume_type.id = u'1' + volume_type_list = [volume_type] + formData = {'name': u'An Encrypted Volume Type', + 'provider': u'a-provider', + 'control_location': u'front-end', + 'cipher': u'a-cipher', + 'key_size': 256, + 'volume_type_id': volume_type.id} + vol_enc_type = self.cinder_volume_encryption_types.list()[0] + + cinder.volume_encryption_type_get(IsA(http.HttpRequest), + volume_type.id)\ + .AndReturn(vol_enc_type) + cinder.volume_type_list(IsA(http.HttpRequest))\ + .AndReturn(volume_type_list) + cinder.volume_encryption_type_update(IsA(http.HttpRequest), + formData['volume_type_id'], + formData) + + self.mox.ReplayAll() + + url = reverse('horizon:admin:volumes:' + 'volume_types:update_type_encryption', + args=[volume_type.id]) + res = self.client.post(url, formData) + + self.assertNoFormErrors(res) + self.assertTemplateUsed( + res, + 'admin/volumes/volume_types/update_volume_type_encryption.html') diff --git a/openstack_dashboard/dashboards/admin/volumes/volume_types/urls.py b/openstack_dashboard/dashboards/admin/volumes/volume_types/urls.py index 65dff3e30..3b3b8aac8 100644 --- a/openstack_dashboard/dashboards/admin/volumes/volume_types/urls.py +++ b/openstack_dashboard/dashboards/admin/volumes/volume_types/urls.py @@ -43,6 +43,9 @@ urlpatterns = patterns( url(r'^(?P[^/]+)/create_type_encryption/$', views.CreateVolumeTypeEncryptionView.as_view(), name='create_type_encryption'), + url(r'^(?P[^/]+)/update_type_encryption/$', + views.UpdateVolumeTypeEncryptionView.as_view(), + name='update_type_encryption'), url(r'^(?P[^/]+)/type_encryption_detail/$', views.VolumeTypeEncryptionDetailView.as_view(), name='type_encryption_detail'), diff --git a/openstack_dashboard/dashboards/admin/volumes/volume_types/views.py b/openstack_dashboard/dashboards/admin/volumes/volume_types/views.py index a6c3f3a62..3abd27e8e 100644 --- a/openstack_dashboard/dashboards/admin/volumes/volume_types/views.py +++ b/openstack_dashboard/dashboards/admin/volumes/volume_types/views.py @@ -86,15 +86,8 @@ class CreateVolumeTypeEncryptionView(forms.ModalFormView): @memoized.memoized_method def get_name(self): - try: - volume_type_list = api.cinder.volume_type_list(self.request) - for volume_type in volume_type_list: - if volume_type.id == self.kwargs['volume_type_id']: - self.name = volume_type.name - except Exception: - msg = _('Unable to retrieve volume type name.') - url = reverse('horizon:admin:volumes:index') - exceptions.handle(self.request, msg, redirect=url) + if not hasattr(self, "name"): + self.name = _get_volume_type_name(self.request, self.kwargs) return self.name def get_context_data(self, **kwargs): @@ -147,6 +140,67 @@ class EditVolumeTypeView(forms.ModalFormView): 'description': getattr(volume_type, 'description', "")} +def _get_volume_type_name(request, kwargs): + try: + volume_type_list = api.cinder.volume_type_list(request) + for volume_type in volume_type_list: + if volume_type.id == kwargs['volume_type_id']: + return volume_type.name + except Exception: + msg = _('Unable to retrieve volume type name.') + url = reverse('horizon:admin:volumes:index') + exceptions.handle(request, msg, redirect=url) + + +class UpdateVolumeTypeEncryptionView(forms.ModalFormView): + form_class = volume_types_forms.UpdateVolumeTypeEncryption + form_id = "update_volume_form" + modal_header = _("Update Volume Type Encryption") + modal_id = "update_volume_type_modal" + template_name = ("admin/volumes/volume_types/" + "update_volume_type_encryption.html") + page_title = _("Update an Encrypted Volume Type") + submit_label = _("Update Volume Type Encryption") + submit_url = "horizon:admin:volumes:volume_types:update_type_encryption" + success_url = reverse_lazy('horizon:admin:volumes:index') + + def get_object(self): + if not hasattr(self, "_object"): + try: + self._object = api.cinder.\ + volume_encryption_type_get(self.request, + self.kwargs['volume_type_id']) + except Exception: + msg = _('Unable to retrieve encryption type.') + url = reverse('horizon:admin:volumes:index') + exceptions.handle(self.request, msg, redirect=url) + return self._object + + @memoized.memoized_method + def get_name(self): + if not hasattr(self, "name"): + self.name = _get_volume_type_name(self.request, self.kwargs) + return self.name + + def get_context_data(self, **kwargs): + context = super(UpdateVolumeTypeEncryptionView, self).\ + get_context_data(**kwargs) + context['volume_type_id'] = self.kwargs['volume_type_id'] + args = (self.kwargs['volume_type_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) + return context + + def get_initial(self): + encryption_type = self.get_object() + name = self.get_name() + return {'volume_type_id': encryption_type.volume_type_id, + 'control_location': encryption_type.control_location, + 'key_size': encryption_type.key_size, + 'provider': encryption_type.provider, + 'cipher': encryption_type.cipher, + 'name': name} + + class CreateQosSpecView(forms.ModalFormView): form_class = volume_types_forms.CreateQosSpec modal_header = _("Create QoS Spec") diff --git a/releasenotes/notes/bp-integration-with-cinder-volume-encryption-80a3fe4ff66314b2.yaml b/releasenotes/notes/bp-integration-with-cinder-volume-encryption-80a3fe4ff66314b2.yaml new file mode 100644 index 000000000..dbb9fb74a --- /dev/null +++ b/releasenotes/notes/bp-integration-with-cinder-volume-encryption-80a3fe4ff66314b2.yaml @@ -0,0 +1,3 @@ +--- +features: + - Added the Update Encryption action for encrypted volume types.