diff --git a/cinderclient/tests/v1/fakes.py b/cinderclient/tests/v1/fakes.py index c83f5adcd..88e58ea19 100644 --- a/cinderclient/tests/v1/fakes.py +++ b/cinderclient/tests/v1/fakes.py @@ -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 # diff --git a/cinderclient/tests/v1/test_shell.py b/cinderclient/tests/v1/test_shell.py index 71d78963e..014df945f 100644 --- a/cinderclient/tests/v1/test_shell.py +++ b/cinderclient/tests/v1/test_shell.py @@ -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") diff --git a/cinderclient/tests/v1/test_volume_encryption_types.py b/cinderclient/tests/v1/test_volume_encryption_types.py new file mode 100644 index 000000000..d9af7d80c --- /dev/null +++ b/cinderclient/tests/v1/test_volume_encryption_types.py @@ -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") diff --git a/cinderclient/tests/v1/test_volume_transfers.py b/cinderclient/tests/v1/test_volume_transfers.py index 40fb09bed..47656d794 100644 --- a/cinderclient/tests/v1/test_volume_transfers.py +++ b/cinderclient/tests/v1/test_volume_transfers.py @@ -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') diff --git a/cinderclient/tests/v1/test_volumes.py b/cinderclient/tests/v1/test_volumes.py index 0da88e28f..2da750947 100644 --- a/cinderclient/tests/v1/test_volumes.py +++ b/cinderclient/tests/v1/test_volumes.py @@ -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') diff --git a/cinderclient/tests/v2/fakes.py b/cinderclient/tests/v2/fakes.py index 8f70e0926..f9a20a8e7 100644 --- a/cinderclient/tests/v2/fakes.py +++ b/cinderclient/tests/v2/fakes.py @@ -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 # diff --git a/cinderclient/tests/v2/test_shell.py b/cinderclient/tests/v2/test_shell.py index 2405192eb..6f9a53fa2 100644 --- a/cinderclient/tests/v2/test_shell.py +++ b/cinderclient/tests/v2/test_shell.py @@ -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") diff --git a/cinderclient/tests/v2/test_volume_encryption_types.py b/cinderclient/tests/v2/test_volume_encryption_types.py new file mode 100644 index 000000000..96a0c02a4 --- /dev/null +++ b/cinderclient/tests/v2/test_volume_encryption_types.py @@ -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") diff --git a/cinderclient/tests/v2/test_volume_transfers.py b/cinderclient/tests/v2/test_volume_transfers.py index 40fb09bed..47656d794 100644 --- a/cinderclient/tests/v2/test_volume_transfers.py +++ b/cinderclient/tests/v2/test_volume_transfers.py @@ -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') diff --git a/cinderclient/tests/v2/test_volumes.py b/cinderclient/tests/v2/test_volumes.py index 8a2560dc9..594bba4e9 100644 --- a/cinderclient/tests/v2/test_volumes.py +++ b/cinderclient/tests/v2/test_volumes.py @@ -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') diff --git a/cinderclient/v1/client.py b/cinderclient/v1/client.py index 1272c4e8b..60376ab8e 100644 --- a/cinderclient/v1/client.py +++ b/cinderclient/v1/client.py @@ -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) diff --git a/cinderclient/v1/shell.py b/cinderclient/v1/shell.py index 6c5d26690..88e0ec2e3 100644 --- a/cinderclient/v1/shell.py +++ b/cinderclient/v1/shell.py @@ -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='', + 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='', + type=str, + help="Name or ID of the volume type") +@utils.arg('provider', + metavar='', + type=str, + help="Class providing encryption support (e.g. LuksEncryptor)") +@utils.arg('--cipher', + metavar='', + 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='', + 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='', + 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]) diff --git a/cinderclient/v1/volume_encryption_types.py b/cinderclient/v1/volume_encryption_types.py new file mode 100644 index 000000000..b97c6f02a --- /dev/null +++ b/cinderclient/v1/volume_encryption_types.py @@ -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 "" % 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() diff --git a/cinderclient/v1/volume_types.py b/cinderclient/v1/volume_types.py index 93eabd303..12c4612d7 100644 --- a/cinderclient/v1/volume_types.py +++ b/cinderclient/v1/volume_types.py @@ -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 diff --git a/cinderclient/v1/volumes.py b/cinderclient/v1/volumes.py index 9c870cb30..6d63e723e 100644 --- a/cinderclient/v1/volumes.py +++ b/cinderclient/v1/volumes.py @@ -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 diff --git a/cinderclient/v2/client.py b/cinderclient/v2/client.py index 31781a80f..2f73ed69b 100644 --- a/cinderclient/v2/client.py +++ b/cinderclient/v2/client.py @@ -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) diff --git a/cinderclient/v2/shell.py b/cinderclient/v2/shell.py index a1c1f222d..8a1900c87 100644 --- a/cinderclient/v2/shell.py +++ b/cinderclient/v2/shell.py @@ -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='', + 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='', + type=str, + help="Name or ID of the volume type") +@utils.arg('provider', + metavar='', + type=str, + help="Class providing encryption support (e.g. LuksEncryptor)") +@utils.arg('--cipher', + metavar='', + 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='', + 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='', + 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]) diff --git a/cinderclient/v2/volume_encryption_types.py b/cinderclient/v2/volume_encryption_types.py new file mode 100644 index 000000000..b97c6f02a --- /dev/null +++ b/cinderclient/v2/volume_encryption_types.py @@ -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 "" % 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() diff --git a/cinderclient/v2/volumes.py b/cinderclient/v2/volumes.py index 14535afc9..be4a9e662 100644 --- a/cinderclient/v2/volumes.py +++ b/cinderclient/v2/volumes.py @@ -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