Merge "Add volume type encryption update"
This commit is contained in:
commit
91e1b6dfbc
@ -562,6 +562,11 @@ def volume_encryption_type_list(request):
|
|||||||
return cinderclient(request).volume_encryption_types.list()
|
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):
|
def volume_type_extra_get(request, type_id, raw=False):
|
||||||
vol_type = volume_type_get(request, type_id)
|
vol_type = volume_type_get(request, type_id)
|
||||||
extras = vol_type.get_keys()
|
extras = vol_type.get_keys()
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
{% extends "horizon/common/_modal_form.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block modal-body-right %}
|
||||||
|
<h3>{% trans "Description:" %}</h3>
|
||||||
|
<p>
|
||||||
|
{% trans "Encryption information cannot be updated for a volume type if volumes are currently in use with the volume type." %}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{% blocktrans %}The <strong>Provider</strong> is the class providing encryption support (e.g., LuksEncryptor).{% endblocktrans %}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{% blocktrans %}The <strong>Control Location</strong> is the notional service where encryption is performed (e.g., front-end=Nova). The default value is 'front-end.'{% endblocktrans %}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{% blocktrans %}The <strong>Cipher</strong> 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 %}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{% blocktrans %}The <strong>Key Size</strong> 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 %}
|
||||||
|
</p>
|
||||||
|
{% endblock %}
|
@ -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 %}
|
@ -104,7 +104,7 @@ class CreateVolumeTypeEncryption(forms.SelfHandlingForm):
|
|||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
try:
|
try:
|
||||||
# Set Cipher to None if empty
|
# Set Cipher to None if empty
|
||||||
if data['cipher'] is u'':
|
if data['cipher'] == u'':
|
||||||
data['cipher'] = None
|
data['cipher'] = None
|
||||||
|
|
||||||
# Create encryption for the volume type
|
# Create encryption for the volume type
|
||||||
@ -122,6 +122,34 @@ class CreateVolumeTypeEncryption(forms.SelfHandlingForm):
|
|||||||
redirect=redirect)
|
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):
|
class ManageQosSpecAssociation(forms.SelfHandlingForm):
|
||||||
qos_spec_choice = forms.ChoiceField(
|
qos_spec_choice = forms.ChoiceField(
|
||||||
label=_("QoS Spec to be associated"),
|
label=_("QoS Spec to be associated"),
|
||||||
|
@ -94,11 +94,21 @@ class CreateVolumeTypeEncryption(tables.LinkAction):
|
|||||||
policy_rules = (("volume", "volume_extension:volume_type_encryption"),)
|
policy_rules = (("volume", "volume_extension:volume_type_encryption"),)
|
||||||
|
|
||||||
def allowed(self, request, volume_type):
|
def allowed(self, request, volume_type):
|
||||||
if _is_vol_type_enc_possible(request):
|
return (_is_vol_type_enc_possible(request) and
|
||||||
return (hasattr(volume_type, 'encryption')
|
not _does_vol_type_enc_exist(volume_type))
|
||||||
and not hasattr(volume_type.encryption, 'provider'))
|
|
||||||
else:
|
|
||||||
return False
|
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):
|
class DeleteVolumeTypeEncryption(tables.DeleteAction):
|
||||||
@ -127,7 +137,13 @@ class DeleteVolumeTypeEncryption(tables.DeleteAction):
|
|||||||
|
|
||||||
def allowed(self, request, volume_type=None):
|
def allowed(self, request, volume_type=None):
|
||||||
return (_is_vol_type_enc_possible(request) and
|
return (_is_vol_type_enc_possible(request) and
|
||||||
hasattr(volume_type, 'encryption') and
|
_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'))
|
hasattr(volume_type.encryption, 'provider'))
|
||||||
|
|
||||||
|
|
||||||
@ -240,6 +256,7 @@ class VolumeTypesTable(tables.DataTable):
|
|||||||
ViewVolumeTypeExtras,
|
ViewVolumeTypeExtras,
|
||||||
ManageQosSpecAssociation,
|
ManageQosSpecAssociation,
|
||||||
EditVolumeType,
|
EditVolumeType,
|
||||||
|
UpdateVolumeTypeEncryption,
|
||||||
DeleteVolumeTypeEncryption,
|
DeleteVolumeTypeEncryption,
|
||||||
DeleteVolumeType,)
|
DeleteVolumeType,)
|
||||||
row_class = UpdateRow
|
row_class = UpdateRow
|
||||||
|
@ -261,3 +261,39 @@ class VolumeTypeTests(test.BaseAdminViewTests):
|
|||||||
redirect = reverse('horizon:admin:volumes:volume_types_tab')
|
redirect = reverse('horizon:admin:volumes:volume_types_tab')
|
||||||
self.assertNoFormErrors(res)
|
self.assertNoFormErrors(res)
|
||||||
self.assertRedirectsNoFollow(res, redirect)
|
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')
|
||||||
|
@ -43,6 +43,9 @@ urlpatterns = patterns(
|
|||||||
url(r'^(?P<volume_type_id>[^/]+)/create_type_encryption/$',
|
url(r'^(?P<volume_type_id>[^/]+)/create_type_encryption/$',
|
||||||
views.CreateVolumeTypeEncryptionView.as_view(),
|
views.CreateVolumeTypeEncryptionView.as_view(),
|
||||||
name='create_type_encryption'),
|
name='create_type_encryption'),
|
||||||
|
url(r'^(?P<volume_type_id>[^/]+)/update_type_encryption/$',
|
||||||
|
views.UpdateVolumeTypeEncryptionView.as_view(),
|
||||||
|
name='update_type_encryption'),
|
||||||
url(r'^(?P<volume_type_id>[^/]+)/type_encryption_detail/$',
|
url(r'^(?P<volume_type_id>[^/]+)/type_encryption_detail/$',
|
||||||
views.VolumeTypeEncryptionDetailView.as_view(),
|
views.VolumeTypeEncryptionDetailView.as_view(),
|
||||||
name='type_encryption_detail'),
|
name='type_encryption_detail'),
|
||||||
|
@ -86,15 +86,8 @@ class CreateVolumeTypeEncryptionView(forms.ModalFormView):
|
|||||||
|
|
||||||
@memoized.memoized_method
|
@memoized.memoized_method
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
try:
|
if not hasattr(self, "name"):
|
||||||
volume_type_list = api.cinder.volume_type_list(self.request)
|
self.name = _get_volume_type_name(self.request, self.kwargs)
|
||||||
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)
|
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
@ -147,6 +140,67 @@ class EditVolumeTypeView(forms.ModalFormView):
|
|||||||
'description': getattr(volume_type, 'description', "")}
|
'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):
|
class CreateQosSpecView(forms.ModalFormView):
|
||||||
form_class = volume_types_forms.CreateQosSpec
|
form_class = volume_types_forms.CreateQosSpec
|
||||||
modal_header = _("Create QoS Spec")
|
modal_header = _("Create QoS Spec")
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Added the Update Encryption action for encrypted volume types.
|
Loading…
Reference in New Issue
Block a user