diff --git a/openstack_dashboard/api/cinder.py b/openstack_dashboard/api/cinder.py index 2c5ca36b86..847f621c48 100644 --- a/openstack_dashboard/api/cinder.py +++ b/openstack_dashboard/api/cinder.py @@ -562,6 +562,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 0000000000..187ff3cead --- /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 0000000000..82841630fa --- /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 99b180827b..4934027589 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 00e1e9e0e5..4fa63bf2d2 100644 --- a/openstack_dashboard/dashboards/admin/volumes/volume_types/tables.py +++ b/openstack_dashboard/dashboards/admin/volumes/volume_types/tables.py @@ -94,11 +94,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): @@ -127,8 +137,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): @@ -240,6 +256,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 88411ea71c..5d2397a6b3 100644 --- a/openstack_dashboard/dashboards/admin/volumes/volume_types/tests.py +++ b/openstack_dashboard/dashboards/admin/volumes/volume_types/tests.py @@ -261,3 +261,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 65dff3e30b..3b3b8aac86 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 2215f57b0b..3d9b209ce4 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 0000000000..dbb9fb74ae --- /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.