Add volume encryption metadata to cinderclient

This modification adds support for volume type encryption to
python-cinderclient, supporting two Cinder API extensions.

The first support set provides accessors to python-cinderclient for
retrieving volume encryption metadata from Cinder. These changes
provide other services (e.g., Nova) access to encryption
metadata (e.g., encryption key UUIDs). See the volume encryption
key API extension in Cinder for more information.

The second support set creates a new python-cinderclient resource
manager, along with matching shell commands, that provides creation
and accessor operations for encryption type information. These
operations allow users and services to define encryption information
(e.g., cipher, key size, encryption provider) for a pre-existing
volume type. See the volume type encryption API extension in Cinder
for more information.

blueprint encrypt-cinder-volumes

Change-Id: Id4b2425d699678eb1997863362ddb9bf5ba6f033
This commit is contained in:
Peter Hamilton 2013-08-21 14:20:41 -04:00
parent 41cf3f193b
commit 109415c26d
19 changed files with 752 additions and 6 deletions

View File

@ -276,6 +276,10 @@ class FakeHTTPClient(base_client.HTTPClient):
r = {'volume': self.get_volumes_detail()[2]['volumes'][0]}
return (200, {}, r)
def get_volumes_1234_encryption(self, **kw):
r = {'encryption_key_id': 'id'}
return (200, {}, r)
def post_volumes_1234_action(self, body, **kw):
_body = None
resp = 202
@ -383,6 +387,11 @@ class FakeHTTPClient(base_client.HTTPClient):
'name': 'test-type-1',
'extra_specs': {}}})
def get_types_2(self, **kw):
return (200, {}, {'volume_type': {'id': 2,
'name': 'test-type-2',
'extra_specs': {}}})
def post_types(self, body, **kw):
return (202, {}, {'volume_type': {'id': 3,
'name': 'test-type-3',
@ -398,6 +407,23 @@ class FakeHTTPClient(base_client.HTTPClient):
def delete_types_1(self, **kw):
return (202, {}, None)
#
# VolumeEncryptionTypes
#
def get_types_1_encryption(self, **kw):
return (200, {}, {'id': 1, 'volume_type_id': 1, 'provider': 'test',
'cipher': 'test', 'key_size': 1,
'control_location': 'front'})
def get_types_2_encryption(self, **kw):
return (200, {}, {})
def post_types_2_encryption(self, body, **kw):
return (200, {}, {'encryption': {}})
def put_types_1_encryption_1(self, body, **kw):
return (200, {}, {})
#
# Set/Unset metadata
#

View File

@ -203,3 +203,60 @@ class ShellTest(utils.TestCase):
self.run_command('snapshot-reset-state --state error 1234')
expected = {'os-reset_status': {'status': 'error'}}
self.assert_called('POST', '/snapshots/1234/action', body=expected)
def test_encryption_type_list(self):
"""
Test encryption-type-list shell command.
Verify a series of GET requests are made:
- one to get the volume type list information
- one per volume type to retrieve the encryption type information
"""
self.run_command('encryption-type-list')
self.assert_called_anytime('GET', '/types')
self.assert_called_anytime('GET', '/types/1/encryption')
self.assert_called_anytime('GET', '/types/2/encryption')
def test_encryption_type_show(self):
"""
Test encryption-type-show shell command.
Verify two GET requests are made per command invocation:
- one to get the volume type information
- one to get the encryption type information
"""
self.run_command('encryption-type-show 1')
self.assert_called('GET', '/types/1/encryption')
self.assert_called_anytime('GET', '/types/1')
def test_encryption_type_create(self):
"""
Test encryption-type-create shell command.
Verify GET and POST requests are made per command invocation:
- one GET request to retrieve the relevant volume type information
- one POST request to create the new encryption type
"""
expected = {'encryption': {'cipher': None, 'key_size': None,
'provider': 'TestProvider',
'control_location': None}}
self.run_command('encryption-type-create 2 TestProvider')
self.assert_called('POST', '/types/2/encryption', body=expected)
self.assert_called_anytime('GET', '/types/2')
def test_encryption_type_update(self):
"""
Test encryption-type-update shell command.
Verify two GETs/one PUT requests are made per command invocation:
- one GET request to retrieve the relevant volume type information
- one GET request to retrieve the relevant encryption type information
- one PUT request to update the encryption type information
"""
self.skipTest("Not implemented")
def test_encryption_type_delete(self):
"""
Test encryption-type-delete shell command.
"""
self.skipTest("Not implemented")

View File

@ -0,0 +1,95 @@
# Copyright (c) 2013 The Johns Hopkins University/Applied Physics Laboratory
# All Rights Reserved.
#
# 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.
from cinderclient.v1.volume_encryption_types import VolumeEncryptionType
from cinderclient.tests import utils
from cinderclient.tests.v1 import fakes
cs = fakes.FakeClient()
class VolumeEncryptionTypesTest(utils.TestCase):
"""
Test suite for the Volume Encryption Types Resource and Manager.
"""
def test_list(self):
"""
Unit test for VolumeEncryptionTypesManager.list
Verify that a series of GET requests are made:
- one GET request for the list of volume types
- one GET request per volume type for encryption type information
Verify that all returned information is :class: VolumeEncryptionType
"""
encryption_types = cs.volume_encryption_types.list()
cs.assert_called_anytime('GET', '/types')
cs.assert_called_anytime('GET', '/types/2/encryption')
cs.assert_called_anytime('GET', '/types/1/encryption')
for encryption_type in encryption_types:
self.assertIsInstance(encryption_type, VolumeEncryptionType)
def test_get(self):
"""
Unit test for VolumeEncryptionTypesManager.get
Verify that one GET request is made for the volume type encryption
type information. Verify that returned information is :class:
VolumeEncryptionType
"""
encryption_type = cs.volume_encryption_types.get(1)
cs.assert_called('GET', '/types/1/encryption')
self.assertIsInstance(encryption_type, VolumeEncryptionType)
def test_get_no_encryption(self):
"""
Unit test for VolumeEncryptionTypesManager.get
Verify that a request on a volume type with no associated encryption
type information returns a VolumeEncryptionType with no attributes.
"""
encryption_type = cs.volume_encryption_types.get(2)
self.assertIsInstance(encryption_type, VolumeEncryptionType)
self.assertFalse(hasattr(encryption_type, 'id'),
'encryption type has an id')
def test_create(self):
"""
Unit test for VolumeEncryptionTypesManager.create
Verify that one POST request is made for the encryption type creation.
Verify that encryption type creation returns a VolumeEncryptionType.
"""
result = cs.volume_encryption_types.create(2, {'encryption':
{'provider': 'Test',
'key_size': None,
'cipher': None,
'control_location':
None}})
cs.assert_called('POST', '/types/2/encryption')
self.assertIsInstance(result, VolumeEncryptionType)
def test_update(self):
"""
Unit test for VolumeEncryptionTypesManager.update
"""
self.skipTest("Not implemented")
def test_delete(self):
"""
Unit test for VolumeEncryptionTypesManager.delete
"""
self.skipTest("Not implemented")

View File

@ -20,7 +20,7 @@ from cinderclient.tests.v1 import fakes
cs = fakes.FakeClient()
class VolumeTRansfersTest(utils.TestCase):
class VolumeTransfersTest(utils.TestCase):
def test_create(self):
cs.transfers.create('1234')

View File

@ -87,3 +87,7 @@ class VolumesTest(utils.TestCase):
v = cs.volumes.get('1234')
cs.volumes.extend(v, 2)
cs.assert_called('POST', '/volumes/1234/action')
def test_get_encryption_metadata(self):
cs.volumes.get_encryption_metadata('1234')
cs.assert_called('GET', '/volumes/1234/encryption')

View File

@ -283,6 +283,10 @@ class FakeHTTPClient(base_client.HTTPClient):
r = {'volume': self.get_volumes_detail()[2]['volumes'][0]}
return (200, {}, r)
def get_volumes_1234_encryption(self, **kw):
r = {'encryption_key_id': 'id'}
return (200, {}, r)
def post_volumes_1234_action(self, body, **kw):
_body = None
resp = 202
@ -390,6 +394,11 @@ class FakeHTTPClient(base_client.HTTPClient):
'name': 'test-type-1',
'extra_specs': {}}})
def get_types_2(self, **kw):
return (200, {}, {'volume_type': {'id': 2,
'name': 'test-type-2',
'extra_specs': {}}})
def post_types(self, body, **kw):
return (202, {}, {'volume_type': {'id': 3,
'name': 'test-type-3',
@ -405,6 +414,23 @@ class FakeHTTPClient(base_client.HTTPClient):
def delete_types_1(self, **kw):
return (202, {}, None)
#
# VolumeEncryptionTypes
#
def get_types_1_encryption(self, **kw):
return (200, {}, {'id': 1, 'volume_type_id': 1, 'provider': 'test',
'cipher': 'test', 'key_size': 1,
'control_location': 'front'})
def get_types_2_encryption(self, **kw):
return (200, {}, {})
def post_types_2_encryption(self, body, **kw):
return (200, {}, {'encryption': {}})
def put_types_1_encryption_1(self, body, **kw):
return (200, {}, {})
#
# Set/Unset metadata
#

View File

@ -181,3 +181,60 @@ class ShellTest(utils.TestCase):
self.run_command('snapshot-reset-state --state error 1234')
expected = {'os-reset_status': {'status': 'error'}}
self.assert_called('POST', '/snapshots/1234/action', body=expected)
def test_encryption_type_list(self):
"""
Test encryption-type-list shell command.
Verify a series of GET requests are made:
- one to get the volume type list information
- one per volume type to retrieve the encryption type information
"""
self.run_command('encryption-type-list')
self.assert_called_anytime('GET', '/types')
self.assert_called_anytime('GET', '/types/1/encryption')
self.assert_called_anytime('GET', '/types/2/encryption')
def test_encryption_type_show(self):
"""
Test encryption-type-show shell command.
Verify two GET requests are made per command invocation:
- one to get the volume type information
- one to get the encryption type information
"""
self.run_command('encryption-type-show 1')
self.assert_called('GET', '/types/1/encryption')
self.assert_called_anytime('GET', '/types/1')
def test_encryption_type_create(self):
"""
Test encryption-type-create shell command.
Verify GET and POST requests are made per command invocation:
- one GET request to retrieve the relevant volume type information
- one POST request to create the new encryption type
"""
expected = {'encryption': {'cipher': None, 'key_size': None,
'provider': 'TestProvider',
'control_location': None}}
self.run_command('encryption-type-create 2 TestProvider')
self.assert_called('POST', '/types/2/encryption', body=expected)
self.assert_called_anytime('GET', '/types/2')
def test_encryption_type_update(self):
"""
Test encryption-type-update shell command.
Verify two GETs/one PUT requests are made per command invocation:
- one GET request to retrieve the relevant volume type information
- one GET request to retrieve the relevant encryption type information
- one PUT request to update the encryption type information
"""
self.skipTest("Not implemented")
def test_encryption_type_delete(self):
"""
Test encryption-type-delete shell command.
"""
self.skipTest("Not implemented")

View File

@ -0,0 +1,95 @@
# Copyright (c) 2013 The Johns Hopkins University/Applied Physics Laboratory
# All Rights Reserved.
#
# 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.
from cinderclient.v2.volume_encryption_types import VolumeEncryptionType
from cinderclient.tests import utils
from cinderclient.tests.v2 import fakes
cs = fakes.FakeClient()
class VolumeEncryptionTypesTest(utils.TestCase):
"""
Test suite for the Volume Encryption Types Resource and Manager.
"""
def test_list(self):
"""
Unit test for VolumeEncryptionTypesManager.list
Verify that a series of GET requests are made:
- one GET request for the list of volume types
- one GET request per volume type for encryption type information
Verify that all returned information is :class: VolumeEncryptionType
"""
encryption_types = cs.volume_encryption_types.list()
cs.assert_called_anytime('GET', '/types')
cs.assert_called_anytime('GET', '/types/2/encryption')
cs.assert_called_anytime('GET', '/types/1/encryption')
for encryption_type in encryption_types:
self.assertIsInstance(encryption_type, VolumeEncryptionType)
def test_get(self):
"""
Unit test for VolumeEncryptionTypesManager.get
Verify that one GET request is made for the volume type encryption
type information. Verify that returned information is :class:
VolumeEncryptionType
"""
encryption_type = cs.volume_encryption_types.get(1)
cs.assert_called('GET', '/types/1/encryption')
self.assertIsInstance(encryption_type, VolumeEncryptionType)
def test_get_no_encryption(self):
"""
Unit test for VolumeEncryptionTypesManager.get
Verify that a request on a volume type with no associated encryption
type information returns a VolumeEncryptionType with no attributes.
"""
encryption_type = cs.volume_encryption_types.get(2)
self.assertIsInstance(encryption_type, VolumeEncryptionType)
self.assertFalse(hasattr(encryption_type, 'id'),
'encryption type has an id')
def test_create(self):
"""
Unit test for VolumeEncryptionTypesManager.create
Verify that one POST request is made for the encryption type creation.
Verify that encryption type creation returns a VolumeEncryptionType.
"""
result = cs.volume_encryption_types.create(2, {'encryption':
{'provider': 'Test',
'key_size': None,
'cipher': None,
'control_location':
None}})
cs.assert_called('POST', '/types/2/encryption')
self.assertIsInstance(result, VolumeEncryptionType)
def test_update(self):
"""
Unit test for VolumeEncryptionTypesManager.update
"""
self.skipTest("Not implemented")
def test_delete(self):
"""
Unit test for VolumeEncryptionTypesManager.delete
"""
self.skipTest("Not implemented")

View File

@ -20,7 +20,7 @@ from cinderclient.tests.v1 import fakes
cs = fakes.FakeClient()
class VolumeTRansfersTest(utils.TestCase):
class VolumeTransfersTest(utils.TestCase):
def test_create(self):
cs.transfers.create('1234')

View File

@ -90,3 +90,7 @@ class VolumesTest(utils.TestCase):
v = cs.volumes.get('1234')
cs.volumes.extend(v, 2)
cs.assert_called('POST', '/volumes/1234/action')
def test_get_encryption_metadata(self):
cs.volumes.get_encryption_metadata('1234')
cs.assert_called('GET', '/volumes/1234/encryption')

View File

@ -22,6 +22,7 @@ from cinderclient.v1 import services
from cinderclient.v1 import volumes
from cinderclient.v1 import volume_snapshots
from cinderclient.v1 import volume_types
from cinderclient.v1 import volume_encryption_types
from cinderclient.v1 import volume_backups
from cinderclient.v1 import volume_backups_restore
from cinderclient.v1 import volume_transfers
@ -59,6 +60,8 @@ class Client(object):
self.volumes = volumes.VolumeManager(self)
self.volume_snapshots = volume_snapshots.SnapshotManager(self)
self.volume_types = volume_types.VolumeTypeManager(self)
self.volume_encryption_types = \
volume_encryption_types.VolumeEncryptionTypeManager(self)
self.quota_classes = quota_classes.QuotaClassSetManager(self)
self.quotas = quotas.QuotaSetManager(self)
self.backups = volume_backups.VolumeBackupManager(self)

View File

@ -525,7 +525,7 @@ def do_type_delete(cs, args):
help='Extra_specs to set/unset (only key is necessary on unset)')
@utils.service_type('volume')
def do_type_key(cs, args):
"Set or unset extra_spec for a volume type."""
"""Set or unset extra_spec for a volume type."""
vtype = _find_volume_type(cs, args.vtype)
if args.metadata is not None:
@ -947,3 +947,86 @@ def do_availability_zone_list(cs, _args):
result += _treeizeAvailabilityZone(zone)
_translate_availability_zone_keys(result)
utils.print_list(result, ['Name', 'Status'])
def _print_volume_encryption_type_list(encryption_types):
"""
Display a tabularized list of volume encryption types.
:param encryption_types: a list of :class: VolumeEncryptionType instances
"""
utils.print_list(encryption_types, ['Volume Type ID', 'Provider',
'Cipher', 'Key Size',
'Control Location'])
@utils.service_type('volume')
def do_encryption_type_list(cs, args):
"""List encryption type information for all volume types (Admin Only)."""
result = cs.volume_encryption_types.list()
utils.print_list(result, ['Volume Type ID', 'Provider', 'Cipher',
'Key Size', 'Control Location'])
@utils.arg('volume_type',
metavar='<volume_type>',
type=str,
help="Name or ID of the volume type")
@utils.service_type('volume')
def do_encryption_type_show(cs, args):
"""Show the encryption type information for a volume type (Admin Only)."""
volume_type = _find_volume_type(cs, args.volume_type)
result = cs.volume_encryption_types.get(volume_type)
# Display result or an empty table if no result
if hasattr(result, 'volume_type_id'):
_print_volume_encryption_type_list([result])
else:
_print_volume_encryption_type_list([])
@utils.arg('volume_type',
metavar='<volume_type>',
type=str,
help="Name or ID of the volume type")
@utils.arg('provider',
metavar='<provider>',
type=str,
help="Class providing encryption support (e.g. LuksEncryptor)")
@utils.arg('--cipher',
metavar='<cipher>',
type=str,
required=False,
default=None,
help="Encryption algorithm/mode to use (e.g., aes-xts-plain64) "
"(Optional, Default=None)")
@utils.arg('--key_size',
metavar='<key_size>',
type=int,
required=False,
default=None,
help="Size of the encryption key, in bits (e.g., 128, 256) "
"(Optional, Default=None)")
@utils.arg('--control_location',
metavar='<control_location>',
choices=['front-end', 'back-end'],
type=str,
required=False,
default=None,
help="Notional service where encryption is performed (e.g., "
"front-end=Nova). Values: 'front-end', 'back-end' "
"(Optional, Default=None)")
@utils.service_type('volume')
def do_encryption_type_create(cs, args):
"""Create a new encryption type for a volume type (Admin Only)."""
volume_type = _find_volume_type(cs, args.volume_type)
body = {}
body['provider'] = args.provider
body['cipher'] = args.cipher
body['key_size'] = args.key_size
body['control_location'] = args.control_location
result = cs.volume_encryption_types.create(volume_type, body)
_print_volume_encryption_type_list([result])

View File

@ -0,0 +1,96 @@
# Copyright (c) 2013 The Johns Hopkins University/Applied Physics Laboratory
# All Rights Reserved.
#
# 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.
"""
Volume Encryption Type interface
"""
from cinderclient import base
class VolumeEncryptionType(base.Resource):
"""
A Volume Encryption Type is a collection of settings used to conduct
encryption for a specific volume type.
"""
def __repr__(self):
return "<VolumeEncryptionType: %s>" % self.name
class VolumeEncryptionTypeManager(base.ManagerWithFind):
"""
Manage :class: `VolumeEncryptionType` resources.
"""
resource_class = VolumeEncryptionType
def list(self):
"""
List all volume encryption types.
:param volume_types: a list of volume types
:return: a list of :class: VolumeEncryptionType instances
"""
# Since the encryption type is a volume type extension, we cannot get
# all encryption types without going through all volume types.
volume_types = self.api.volume_types.list()
encryption_types = []
for volume_type in volume_types:
encryption_type = self._get("/types/%s/encryption"
% base.getid(volume_type))
if hasattr(encryption_type, 'volume_type_id'):
encryption_types.append(encryption_type)
return encryption_types
def get(self, volume_type):
"""
Get the volume encryption type for the specified volume type.
:param volume_type: the volume type to query
:return: an instance of :class: VolumeEncryptionType
"""
return self._get("/types/%s/encryption" % base.getid(volume_type))
def create(self, volume_type, specs):
"""
Create a new encryption type for the specified volume type.
:param volume_type: the volume type on which to add an encryption type
:param specs: the encryption type specifications to add
:return: an instance of :class: VolumeEncryptionType
"""
body = {'encryption': specs}
return self._create("/types/%s/encryption" % base.getid(volume_type),
body, "encryption")
def update(self, volume_type, specs):
"""
Update the encryption type information for the specified volume type.
:param volume_type: the volume type whose encryption type information
must be updated
:param specs: the encryption type specifications to update
:return: an instance of :class: VolumeEncryptionType
"""
raise NotImplementedError()
def delete(self, volume_type):
"""
Delete the encryption type information for the specified volume type.
:param volume_type: the volume type whose encryption type information
must be deleted
"""
raise NotImplementedError()

View File

@ -55,7 +55,7 @@ class VolumeType(base.Resource):
def unset_keys(self, keys):
"""
Unset extra specs on a volue type.
Unset extra specs on a volume type.
:param type_id: The :class:`VolumeType` to unset extra spec on
:param keys: A list of keys to be unset

View File

@ -134,13 +134,13 @@ class VolumeManager(base.ManagerWithFind):
:param display_name: Name of the volume
:param display_description: Description of the volume
:param volume_type: Type of volume
:rtype: :class:`Volume`
:param user_id: User id derived from context
:param project_id: Project id derived from context
:param availability_zone: Availability Zone to use
:param metadata: Optional metadata to set on volume creation
:param imageRef: reference to an image stored in glance
:param source_volid: ID of source volume to clone from
:rtype: :class:`Volume`
"""
if metadata is None:
@ -352,3 +352,12 @@ class VolumeManager(base.ManagerWithFind):
return self._action('os-extend',
base.getid(volume),
{'new_size': new_size})
def get_encryption_metadata(self, volume_id):
"""
Retrieve the encryption metadata from the desired volume.
:param volume_id: the id of the volume to query
:return: a dictionary of volume encryption metadata
"""
return self._get("/volumes/%s/encryption" % volume_id)._info

View File

@ -22,6 +22,7 @@ from cinderclient.v2 import services
from cinderclient.v2 import volumes
from cinderclient.v2 import volume_snapshots
from cinderclient.v2 import volume_types
from cinderclient.v2 import volume_encryption_types
from cinderclient.v2 import volume_backups
from cinderclient.v2 import volume_backups_restore
from cinderclient.v1 import volume_transfers
@ -57,6 +58,8 @@ class Client(object):
self.volumes = volumes.VolumeManager(self)
self.volume_snapshots = volume_snapshots.SnapshotManager(self)
self.volume_types = volume_types.VolumeTypeManager(self)
self.volume_encryption_types = \
volume_encryption_types.VolumeEncryptionTypeManager(self)
self.quota_classes = quota_classes.QuotaClassSetManager(self)
self.quotas = quotas.QuotaSetManager(self)
self.backups = volume_backups.VolumeBackupManager(self)

View File

@ -1032,3 +1032,86 @@ def do_availability_zone_list(cs, _args):
result += _treeizeAvailabilityZone(zone)
_translate_availability_zone_keys(result)
utils.print_list(result, ['Name', 'Status'])
def _print_volume_encryption_type_list(encryption_types):
"""
Display a tabularized list of volume encryption types.
:param encryption_types: a list of :class: VolumeEncryptionType instances
"""
utils.print_list(encryption_types, ['Volume Type ID', 'Provider',
'Cipher', 'Key Size',
'Control Location'])
@utils.service_type('volumev2')
def do_encryption_type_list(cs, args):
"""List encryption type information for all volume types (Admin Only)."""
result = cs.volume_encryption_types.list()
utils.print_list(result, ['Volume Type ID', 'Provider', 'Cipher',
'Key Size', 'Control Location'])
@utils.arg('volume_type',
metavar='<volume_type>',
type=str,
help="Name or ID of the volume type")
@utils.service_type('volumev2')
def do_encryption_type_show(cs, args):
"""Show the encryption type information for a volume type (Admin Only)."""
volume_type = _find_volume_type(cs, args.volume_type)
result = cs.volume_encryption_types.get(volume_type)
# Display result or an empty table if no result
if hasattr(result, 'volume_type_id'):
_print_volume_encryption_type_list([result])
else:
_print_volume_encryption_type_list([])
@utils.arg('volume_type',
metavar='<volume_type>',
type=str,
help="Name or ID of the volume type")
@utils.arg('provider',
metavar='<provider>',
type=str,
help="Class providing encryption support (e.g. LuksEncryptor)")
@utils.arg('--cipher',
metavar='<cipher>',
type=str,
required=False,
default=None,
help="Encryption algorithm/mode to use (e.g., aes-xts-plain64) "
"(Optional, Default=None)")
@utils.arg('--key_size',
metavar='<key_size>',
type=int,
required=False,
default=None,
help="Size of the encryption key, in bits (e.g., 128, 256) "
"(Optional, Default=None)")
@utils.arg('--control_location',
metavar='<control_location>',
choices=['front-end', 'back-end'],
type=str,
required=False,
default=None,
help="Notional service where encryption is performed (e.g., "
"front-end=Nova). Values: 'front-end', 'back-end' "
"(Optional, Default=None)")
@utils.service_type('volumev2')
def do_encryption_type_create(cs, args):
"""Create a new encryption type for a volume type (Admin Only)."""
volume_type = _find_volume_type(cs, args.volume_type)
body = {}
body['provider'] = args.provider
body['cipher'] = args.cipher
body['key_size'] = args.key_size
body['control_location'] = args.control_location
result = cs.volume_encryption_types.create(volume_type, body)
_print_volume_encryption_type_list([result])

View File

@ -0,0 +1,96 @@
# Copyright (c) 2013 The Johns Hopkins University/Applied Physics Laboratory
# All Rights Reserved.
#
# 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.
"""
Volume Encryption Type interface
"""
from cinderclient import base
class VolumeEncryptionType(base.Resource):
"""
A Volume Encryption Type is a collection of settings used to conduct
encryption for a specific volume type.
"""
def __repr__(self):
return "<VolumeEncryptionType: %s>" % self.name
class VolumeEncryptionTypeManager(base.ManagerWithFind):
"""
Manage :class: `VolumeEncryptionType` resources.
"""
resource_class = VolumeEncryptionType
def list(self):
"""
List all volume encryption types.
:param volume_types: a list of volume types
:return: a list of :class: VolumeEncryptionType instances
"""
# Since the encryption type is a volume type extension, we cannot get
# all encryption types without going through all volume types.
volume_types = self.api.volume_types.list()
encryption_types = []
for volume_type in volume_types:
encryption_type = self._get("/types/%s/encryption"
% base.getid(volume_type))
if hasattr(encryption_type, 'volume_type_id'):
encryption_types.append(encryption_type)
return encryption_types
def get(self, volume_type):
"""
Get the volume encryption type for the specified volume type.
:param volume_type: the volume type to query
:return: an instance of :class: VolumeEncryptionType
"""
return self._get("/types/%s/encryption" % base.getid(volume_type))
def create(self, volume_type, specs):
"""
Create a new encryption type for the specified volume type.
:param volume_type: the volume type on which to add an encryption type
:param specs: the encryption type specifications to add
:return: an instance of :class: VolumeEncryptionType
"""
body = {'encryption': specs}
return self._create("/types/%s/encryption" % base.getid(volume_type),
body, "encryption")
def update(self, volume_type, specs):
"""
Update the encryption type information for the specified volume type.
:param volume_type: the volume type whose encryption type information
must be updated
:param specs: the encryption type specifications to update
:return: an instance of :class: VolumeEncryptionType
"""
raise NotImplementedError()
def delete(self, volume_type):
"""
Delete the encryption type information for the specified volume type.
:param volume_type: the volume type whose encryption type information
must be deleted
"""
raise NotImplementedError()

View File

@ -129,7 +129,6 @@ class VolumeManager(base.ManagerWithFind):
:param name: Name of the volume
:param description: Description of the volume
:param volume_type: Type of volume
:rtype: :class:`Volume`
:param user_id: User id derived from context
:param project_id: Project id derived from context
:param availability_zone: Availability Zone to use
@ -138,6 +137,7 @@ class VolumeManager(base.ManagerWithFind):
:param source_volid: ID of source volume to clone from
:param scheduler_hints: (optional extension) arbitrary key-value pairs
specified by the client to help boot an instance
:rtype: :class:`Volume`
"""
if metadata is None:
@ -334,3 +334,12 @@ class VolumeManager(base.ManagerWithFind):
return self._action('os-extend',
base.getid(volume),
{'new_size': new_size})
def get_encryption_metadata(self, volume_id):
"""
Retrieve the encryption metadata from the desired volume.
:param volume_id: the id of the volume to query
:return: a dictionary of volume encryption metadata
"""
return self._get("/volumes/%s/encryption" % volume_id)._info