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:
parent
41cf3f193b
commit
109415c26d
@ -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
|
||||
#
|
||||
|
@ -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")
|
||||
|
95
cinderclient/tests/v1/test_volume_encryption_types.py
Normal file
95
cinderclient/tests/v1/test_volume_encryption_types.py
Normal 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")
|
@ -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')
|
||||
|
@ -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')
|
||||
|
@ -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
|
||||
#
|
||||
|
@ -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")
|
||||
|
95
cinderclient/tests/v2/test_volume_encryption_types.py
Normal file
95
cinderclient/tests/v2/test_volume_encryption_types.py
Normal 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")
|
@ -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')
|
||||
|
@ -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')
|
||||
|
@ -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)
|
||||
|
@ -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])
|
||||
|
96
cinderclient/v1/volume_encryption_types.py
Normal file
96
cinderclient/v1/volume_encryption_types.py
Normal 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()
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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])
|
||||
|
96
cinderclient/v2/volume_encryption_types.py
Normal file
96
cinderclient/v2/volume_encryption_types.py
Normal 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()
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user