[OSC] Add share type encryption
Implemented share type encryption create/get/set/delete osc client calls. partially-implements: blueprint share-encryption Depends-on: I4f952e953f797db48344a17f368b0cdbc1875cd1 Change-Id: I630f95a3a7e4909465b1190bbe5cd3ac51352b27
This commit is contained in:
parent
9e81815674
commit
40bd1d8fe8
|
@ -27,7 +27,7 @@ from manilaclient import utils
|
|||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
MAX_VERSION = '2.84'
|
||||
MAX_VERSION = '2.86'
|
||||
MIN_VERSION = '2.0'
|
||||
DEPRECATED_VERSION = '1.0'
|
||||
_VERSIONED_METHOD_MAP = {}
|
||||
|
|
|
@ -32,7 +32,7 @@ ATTRIBUTES = [
|
|||
'is_default',
|
||||
'required_extra_specs',
|
||||
'optional_extra_specs',
|
||||
'description'
|
||||
'description',
|
||||
]
|
||||
|
||||
|
||||
|
@ -56,6 +56,7 @@ def format_share_type(share_type, formatter='table'):
|
|||
share_type.required_extra_specs),
|
||||
'optional_extra_specs': utils.format_properties(
|
||||
optional_extra_specs),
|
||||
'encryption': utils.format_properties(share_type.encryption),
|
||||
}
|
||||
)
|
||||
else:
|
||||
|
@ -64,6 +65,7 @@ def format_share_type(share_type, formatter='table'):
|
|||
'visibility': visibility,
|
||||
'required_extra_specs': share_type.required_extra_specs,
|
||||
'optional_extra_specs': optional_extra_specs,
|
||||
'encryption': share_type.encryption
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -144,6 +146,27 @@ class CreateShareType(command.ShowOne):
|
|||
default=True,
|
||||
help=_('Make type accessible to the public (default true).')
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encryption-provider",
|
||||
metavar="<encryption_provider>",
|
||||
default=None,
|
||||
help=_("Share type encryption provider. "
|
||||
"Available only for microversion >= 2.86."),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encryption-cipher",
|
||||
metavar="<encryption_cipher>",
|
||||
default=None,
|
||||
help=_("Share type encryption cipher. "
|
||||
"Available only for microversion >= 2.86."),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encryption-key-size",
|
||||
metavar="<encryption_key_size>",
|
||||
default=None,
|
||||
help=_("Share type encryption key size. "
|
||||
"Available only for microversion >= 2.86."),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
|
@ -196,11 +219,39 @@ class CreateShareType(command.ShowOne):
|
|||
|
||||
kwargs['extra_specs'] = extra_specs
|
||||
|
||||
encryption = {}
|
||||
if (parsed_args.encryption_provider or parsed_args.encryption_key_size
|
||||
or parsed_args.encryption_cipher):
|
||||
if share_client.api_version >= api_versions.APIVersion("2.86"):
|
||||
if not (parsed_args.encryption_provider and
|
||||
parsed_args.encryption_key_size and
|
||||
parsed_args.encryption_cipher):
|
||||
raise exceptions.CommandError(
|
||||
"Please specify all encryption options i.e. provider, "
|
||||
"key_size and cipher to add encryption to share type.")
|
||||
else:
|
||||
encryption.update(
|
||||
{
|
||||
"provider": parsed_args.encryption_provider,
|
||||
"key_size": parsed_args.encryption_key_size,
|
||||
"cipher": parsed_args.encryption_cipher
|
||||
}
|
||||
)
|
||||
else:
|
||||
raise exceptions.CommandError(
|
||||
"Adding encryption to share type "
|
||||
"is only available with API microversion >= 2.86")
|
||||
kwargs['encryption'] = encryption
|
||||
|
||||
share_type = share_client.share_types.create(**kwargs)
|
||||
formatted_type = format_share_type(share_type, parsed_args.formatter)
|
||||
|
||||
return (ATTRIBUTES, oscutils.get_dict_properties(
|
||||
formatted_type._info, ATTRIBUTES))
|
||||
attrib = ATTRIBUTES
|
||||
if encryption:
|
||||
attrib.append('encryption')
|
||||
|
||||
return (attrib, oscutils.get_dict_properties(
|
||||
formatted_type._info, attrib))
|
||||
|
||||
|
||||
class DeleteShareType(command.Command):
|
||||
|
@ -287,6 +338,28 @@ class SetShareType(command.Command):
|
|||
help=_('New name of share type. '
|
||||
'Available only for microversion >= 2.50')
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encryption-provider",
|
||||
metavar="<encryption_provider>",
|
||||
default=None,
|
||||
help=_("Share type encryption provider. "
|
||||
"Available only for microversion >= 2.86."),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encryption-cipher",
|
||||
metavar="<encryption_cipher>",
|
||||
default=None,
|
||||
help=_("Share type encryption cipher. "
|
||||
"Available only for microversion >= 2.86."),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encryption-key-size",
|
||||
metavar="<encryption_key_size>",
|
||||
default=None,
|
||||
help=_("Share type encryption key size. "
|
||||
"Available only for microversion >= 2.86."),
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
|
@ -321,6 +394,37 @@ class SetShareType(command.Command):
|
|||
raise exceptions.CommandError(
|
||||
"Setting visibility to share type "
|
||||
"is only available with API microversion >= 2.50")
|
||||
|
||||
encryption = {}
|
||||
if (parsed_args.encryption_provider or parsed_args.encryption_key_size
|
||||
or parsed_args.encryption_cipher):
|
||||
if share_client.api_version >= api_versions.APIVersion("2.86"):
|
||||
fetched_encryption = share_type.get_encryption()
|
||||
if not fetched_encryption:
|
||||
# this is create request instead of update
|
||||
if not (parsed_args.encryption_provider and
|
||||
parsed_args.encryption_key_size and
|
||||
parsed_args.encryption_cipher):
|
||||
raise exceptions.CommandError(
|
||||
"Please specify all encryption options i.e. "
|
||||
"provider, key_size and cipher to set encryption "
|
||||
"for share type as encryption")
|
||||
encryption.update({'operation': 'create'})
|
||||
else:
|
||||
encryption.update({'operation': 'update'})
|
||||
if parsed_args.encryption_provider:
|
||||
encryption.update(
|
||||
{"provider": parsed_args.encryption_provider})
|
||||
if parsed_args.encryption_key_size:
|
||||
encryption.update(
|
||||
{"key_size": parsed_args.encryption_key_size})
|
||||
if parsed_args.encryption_cipher:
|
||||
encryption.update(
|
||||
{"cipher": parsed_args.encryption_cipher})
|
||||
else:
|
||||
raise exceptions.CommandError(
|
||||
"Set encryption to share type "
|
||||
"is only available with API microversion >= 2.86")
|
||||
if kwargs:
|
||||
share_type.update(**kwargs)
|
||||
|
||||
|
@ -334,6 +438,17 @@ class SetShareType(command.Command):
|
|||
raise exceptions.CommandError(
|
||||
"Failed to set share type key: %s" % e)
|
||||
|
||||
if encryption:
|
||||
try:
|
||||
operation = encryption.pop('operation')
|
||||
if operation == 'create':
|
||||
share_type.create_encryption(encryption)
|
||||
else:
|
||||
share_type.set_encryption(encryption)
|
||||
except Exception as e:
|
||||
raise exceptions.CommandError(
|
||||
"Failed to set share type encryption: %s" % e)
|
||||
|
||||
|
||||
class UnsetShareType(command.Command):
|
||||
"""Unset share type extra specs."""
|
||||
|
@ -349,9 +464,17 @@ class UnsetShareType(command.Command):
|
|||
parser.add_argument(
|
||||
'extra_specs',
|
||||
metavar='<key>',
|
||||
nargs='+',
|
||||
nargs='*',
|
||||
default=None,
|
||||
help=_('Remove extra_specs from this share type'),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encryption-type",
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=_("Remove encryption-type of share type. Available "
|
||||
"with API microversion >= 2.86"),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
|
@ -367,6 +490,18 @@ class UnsetShareType(command.Command):
|
|||
raise exceptions.CommandError(
|
||||
"Failed to remove share type extra spec: %s" % e)
|
||||
|
||||
if parsed_args.encryption_type:
|
||||
if share_client.api_version >= api_versions.APIVersion("2.86"):
|
||||
try:
|
||||
share_type.unset_encryption()
|
||||
except Exception as e:
|
||||
raise exceptions.CommandError(
|
||||
"Failed to remove share type encryption: %s" % e)
|
||||
else:
|
||||
raise exceptions.CommandError(
|
||||
"Unset encryption-type of share type "
|
||||
"is only available with API microversion >= 2.86")
|
||||
|
||||
|
||||
class ListShareType(command.Lister):
|
||||
"""List Share Types."""
|
||||
|
@ -391,6 +526,13 @@ class ListShareType(command.Lister):
|
|||
'Available only for API microversion >= 2.43. '
|
||||
'OPTIONAL: Default=None.'),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encryption-type",
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=_("List encryption-type of share type. Available "
|
||||
"with API microversion >= 2.86"),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
|
@ -409,6 +551,14 @@ class ListShareType(command.Lister):
|
|||
specs_to_add=parsed_args.extra_specs)
|
||||
}
|
||||
|
||||
if parsed_args.encryption_type:
|
||||
if share_client.api_version >= api_versions.APIVersion("2.86"):
|
||||
search_opts.update({'with_encryption': True})
|
||||
else:
|
||||
raise exceptions.CommandError(
|
||||
"List share type encryption is only available "
|
||||
"with API microversion >= 2.86")
|
||||
|
||||
share_types = share_client.share_types.list(
|
||||
search_opts=search_opts,
|
||||
show_all=parsed_args.all)
|
||||
|
@ -418,10 +568,14 @@ class ListShareType(command.Lister):
|
|||
formatted_types.append(format_share_type(share_type,
|
||||
parsed_args.formatter))
|
||||
|
||||
values = (oscutils.get_dict_properties(
|
||||
s._info, ATTRIBUTES) for s in formatted_types)
|
||||
attrib = ATTRIBUTES
|
||||
if parsed_args.encryption_type:
|
||||
attrib.append('encryption')
|
||||
|
||||
columns = utils.format_column_headers(ATTRIBUTES)
|
||||
values = (oscutils.get_dict_properties(
|
||||
s._info, attrib) for s in formatted_types)
|
||||
|
||||
columns = utils.format_column_headers(attrib)
|
||||
|
||||
return (columns, values)
|
||||
|
||||
|
@ -448,5 +602,8 @@ class ShowShareType(command.ShowOne):
|
|||
|
||||
formatted_type = format_share_type(share_type, parsed_args.formatter)
|
||||
|
||||
return (ATTRIBUTES, oscutils.get_dict_properties(
|
||||
formatted_type._info, ATTRIBUTES))
|
||||
attrib = ATTRIBUTES
|
||||
attrib.append('encryption')
|
||||
|
||||
return (attrib, oscutils.get_dict_properties(
|
||||
formatted_type._info, attrib))
|
||||
|
|
|
@ -208,8 +208,9 @@ class OSCClientTestBase(base.ClientTestBase):
|
|||
create_share_from_snapshot_support=None,
|
||||
revert_to_snapshot_support=False,
|
||||
mount_snapshot_support=False, extra_specs={},
|
||||
public=True, add_cleanup=True, client=None,
|
||||
formatter=None):
|
||||
public=True, encryption_cipher=None,
|
||||
encryption_provider=None, encryption_key_size=None,
|
||||
add_cleanup=True, client=None, formatter=None):
|
||||
|
||||
name = name or data_utils.rand_name('autotest_share_type_name')
|
||||
|
||||
|
@ -231,6 +232,12 @@ class OSCClientTestBase(base.ClientTestBase):
|
|||
for key, value in extra_specs.items():
|
||||
specs += f' {key}={value}'
|
||||
cmd += specs
|
||||
if encryption_cipher:
|
||||
cmd += f' --encryption-cipher {encryption_cipher}'
|
||||
if encryption_provider:
|
||||
cmd += f' --encryption-provider {encryption_provider}'
|
||||
if encryption_key_sizer:
|
||||
cmd += f' --encryption-key-size {encryption_key_size}'
|
||||
|
||||
if formatter == 'json':
|
||||
cmd = f'share type {cmd} -f {formatter} '
|
||||
|
|
|
@ -56,6 +56,18 @@ class ShareTypesCLITest(base.OSCClientTestBase):
|
|||
self.assertEqual("bar", optional_specs["foo"])
|
||||
self.assertEqual("zorilla", optional_specs["manila"])
|
||||
|
||||
def test_share_type_encryption(self):
|
||||
share_type = self.create_share_type(
|
||||
snapshot_support=True,
|
||||
encryption_cipher="foo",
|
||||
encryption_provider="bar",
|
||||
encryption_key_size="10",
|
||||
formatter='json')
|
||||
encryption = share_type["encryption"]
|
||||
self.assertEqual("foo", encryption['cipher'])
|
||||
self.assertEqual("bar", encryption['provider'])
|
||||
self.assertEqual("10", encryption['key_size'])
|
||||
|
||||
def test_share_type_delete(self):
|
||||
share_type_1 = self.create_share_type(add_cleanup=False)
|
||||
share_type_2 = self.create_share_type(add_cleanup=False)
|
||||
|
@ -68,11 +80,16 @@ class ShareTypesCLITest(base.OSCClientTestBase):
|
|||
self.check_object_deleted('share type', share_type_2["id"])
|
||||
|
||||
def test_share_type_set(self):
|
||||
share_type = self.create_share_type()
|
||||
share_type = self.create_share_type(
|
||||
encryption_cipher="foo",
|
||||
encryption_provider="bar",
|
||||
encryption_key_size="10",
|
||||
formatter='json')
|
||||
|
||||
self.openstack(
|
||||
f'share type set {share_type["id"]} --description Description'
|
||||
' --name Name --public false --extra-specs foo=bar'
|
||||
' --name Name --public false --extra-specs foo=bar '
|
||||
' --encryption-cipher zoo'
|
||||
)
|
||||
|
||||
share_type = json.loads(self.openstack(
|
||||
|
@ -84,15 +101,20 @@ class ShareTypesCLITest(base.OSCClientTestBase):
|
|||
self.assertEqual('private', share_type["visibility"])
|
||||
self.assertEqual(
|
||||
'bar', share_type["optional_extra_specs"]["foo"])
|
||||
self.assertEqual(
|
||||
'zoo', share_type['encryption']['cipher'])
|
||||
|
||||
def test_share_type_unset(self):
|
||||
share_type = self.create_share_type(
|
||||
snapshot_support=True,
|
||||
extra_specs={'foo': 'bar'})
|
||||
extra_specs={'foo': 'bar'},
|
||||
encryption_cipher="zoo",
|
||||
encryption_provider="bar",
|
||||
encryption_key_size="10")
|
||||
|
||||
self.openstack(
|
||||
f'share type unset {share_type["id"]} '
|
||||
'snapshot_support foo')
|
||||
'snapshot_support foo --encryption-type')
|
||||
|
||||
share_type = json.loads(self.openstack(
|
||||
f'share type show {share_type["id"]} -f json'
|
||||
|
@ -101,6 +123,7 @@ class ShareTypesCLITest(base.OSCClientTestBase):
|
|||
self.assertNotIn('foo', share_type["optional_extra_specs"])
|
||||
self.assertNotIn(
|
||||
'snapshot_support', share_type["optional_extra_specs"])
|
||||
self.assertNotIn('zoo', share_type['encryption'])
|
||||
|
||||
def test_share_type_list(self):
|
||||
share_type_1 = self.create_share_type(public=False)
|
||||
|
@ -117,14 +140,16 @@ class ShareTypesCLITest(base.OSCClientTestBase):
|
|||
'Is Default',
|
||||
'Required Extra Specs',
|
||||
'Optional Extra Specs',
|
||||
'Description'
|
||||
'Description',
|
||||
'Encryption'
|
||||
])
|
||||
id_list = [item['ID'] for item in types_list]
|
||||
self.assertIn(share_type_1['id'], id_list)
|
||||
self.assertIn(share_type_2['id'], id_list)
|
||||
|
||||
types_list = self.listing_result(
|
||||
'share type', 'list --extra-specs foo=bar')
|
||||
'share type',
|
||||
'list --extra-specs foo=bar --encryption-type')
|
||||
|
||||
id_list = [item['ID'] for item in types_list]
|
||||
self.assertNotIn(share_type_1['id'], id_list)
|
||||
|
|
|
@ -257,7 +257,12 @@ class FakeShareType(object):
|
|||
"id": 'share-type-id-' + uuid.uuid4().hex,
|
||||
"name": 'share-type-name-' + uuid.uuid4().hex,
|
||||
"is_default": False,
|
||||
"description": 'share-type-description-' + uuid.uuid4().hex
|
||||
"description": 'share-type-description-' + uuid.uuid4().hex,
|
||||
"encryption": {
|
||||
"cipher": "test_cipher",
|
||||
"provider": "test_provider",
|
||||
"key_size": "test_key_size"
|
||||
}
|
||||
}
|
||||
|
||||
share_type_info.update(attrs)
|
||||
|
|
|
@ -31,7 +31,7 @@ COLUMNS = [
|
|||
'is_default',
|
||||
'required_extra_specs',
|
||||
'optional_extra_specs',
|
||||
'description'
|
||||
'description',
|
||||
]
|
||||
|
||||
|
||||
|
@ -71,6 +71,12 @@ class TestShareTypeCreate(TestShareType):
|
|||
self.new_share_type.description,
|
||||
]
|
||||
|
||||
self.data_encrypt = self.data + [
|
||||
('cipher : test_cipher\n'
|
||||
'provider : test_provider\n'
|
||||
'key_size : test_key_size')
|
||||
]
|
||||
|
||||
self.raw_data = [
|
||||
self.new_share_type.id,
|
||||
self.new_share_type.name,
|
||||
|
@ -85,6 +91,12 @@ class TestShareTypeCreate(TestShareType):
|
|||
self.new_share_type.description,
|
||||
]
|
||||
|
||||
self.raw_data_encrypt = self.raw_data + [
|
||||
{'cipher': 'test_cipher',
|
||||
'provider': 'test_provider',
|
||||
'key_size': 'test_key_size'}
|
||||
]
|
||||
|
||||
def test_share_type_create_required_args(self):
|
||||
"""Verifies required arguments."""
|
||||
|
||||
|
@ -105,7 +117,8 @@ class TestShareTypeCreate(TestShareType):
|
|||
extra_specs={},
|
||||
is_public=True,
|
||||
name=self.new_share_type.name,
|
||||
spec_driver_handles_share_servers=True
|
||||
spec_driver_handles_share_servers=True,
|
||||
encryption={}
|
||||
)
|
||||
|
||||
self.assertCountEqual(COLUMNS, columns)
|
||||
|
@ -132,7 +145,8 @@ class TestShareTypeCreate(TestShareType):
|
|||
extra_specs={},
|
||||
is_public=True,
|
||||
name=self.new_share_type.name,
|
||||
spec_driver_handles_share_servers=True
|
||||
spec_driver_handles_share_servers=True,
|
||||
encryption={}
|
||||
)
|
||||
|
||||
self.assertCountEqual(COLUMNS, columns)
|
||||
|
@ -171,7 +185,8 @@ class TestShareTypeCreate(TestShareType):
|
|||
extra_specs={},
|
||||
is_public=False,
|
||||
name=self.new_share_type.name,
|
||||
spec_driver_handles_share_servers=True
|
||||
spec_driver_handles_share_servers=True,
|
||||
encryption={}
|
||||
)
|
||||
|
||||
self.assertCountEqual(COLUMNS, columns)
|
||||
|
@ -197,7 +212,8 @@ class TestShareTypeCreate(TestShareType):
|
|||
extra_specs={'snapshot_support': 'True'},
|
||||
is_public=True,
|
||||
name=self.new_share_type.name,
|
||||
spec_driver_handles_share_servers=True
|
||||
spec_driver_handles_share_servers=True,
|
||||
encryption={}
|
||||
)
|
||||
|
||||
self.assertCountEqual(COLUMNS, columns)
|
||||
|
@ -271,12 +287,70 @@ class TestShareTypeCreate(TestShareType):
|
|||
extra_specs={'snapshot_support': 'True'},
|
||||
is_public=True,
|
||||
name=self.new_share_type.name,
|
||||
spec_driver_handles_share_servers=True
|
||||
spec_driver_handles_share_servers=True,
|
||||
encryption={}
|
||||
)
|
||||
|
||||
self.assertCountEqual(COLUMNS, columns)
|
||||
self.assertCountEqual(self.data, data)
|
||||
|
||||
def test_share_type_create_encryption(self):
|
||||
self.app.client_manager.share.api_version = api_versions.APIVersion(
|
||||
"2.86")
|
||||
|
||||
arglist = [
|
||||
self.new_share_type.name,
|
||||
'True',
|
||||
'--encryption-cipher', 'test_cipher',
|
||||
'--encryption-provider', 'test_provider',
|
||||
'--encryption-key-size', 'test_key_size'
|
||||
]
|
||||
verifylist = [
|
||||
('name', self.new_share_type.name),
|
||||
('spec_driver_handles_share_servers', 'True'),
|
||||
('encryption_cipher', 'test_cipher'),
|
||||
('encryption_provider', 'test_provider'),
|
||||
('encryption_key_size', 'test_key_size')
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.shares_mock.create.assert_called_with(
|
||||
extra_specs={},
|
||||
is_public=True,
|
||||
name=self.new_share_type.name,
|
||||
spec_driver_handles_share_servers=True,
|
||||
encryption={
|
||||
'cipher': 'test_cipher',
|
||||
'provider': 'test_provider',
|
||||
'key_size': 'test_key_size'
|
||||
}
|
||||
)
|
||||
|
||||
self.assertCountEqual(COLUMNS + ['encryption'], columns)
|
||||
self.assertCountEqual(self.data_encrypt, data)
|
||||
|
||||
def test_share_type_create_encryption_missing_fields(self):
|
||||
self.app.client_manager.share.api_version = api_versions.APIVersion(
|
||||
"2.86")
|
||||
|
||||
arglist = [
|
||||
self.new_share_type.name,
|
||||
'True',
|
||||
'--encryption-cipher', 'test_cipher',
|
||||
]
|
||||
verifylist = [
|
||||
('name', self.new_share_type.name),
|
||||
('spec_driver_handles_share_servers', 'True'),
|
||||
('encryption_cipher', 'test_cipher'),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.assertRaises(
|
||||
exceptions.CommandError, self.cmd.take_action, parsed_args)
|
||||
|
||||
|
||||
class TestShareTypeDelete(TestShareType):
|
||||
|
||||
|
@ -344,7 +418,9 @@ class TestShareTypeSet(TestShareType):
|
|||
super(TestShareTypeSet, self).setUp()
|
||||
|
||||
self.share_type = manila_fakes.FakeShareType.create_one_sharetype(
|
||||
methods={'set_keys': None, 'update': None})
|
||||
methods={'set_keys': None, 'update': None,
|
||||
'get_encryption': None, 'set_encryption': None,
|
||||
'create_encryption': None})
|
||||
self.shares_mock.get.return_value = self.share_type
|
||||
|
||||
# Get the command object to test
|
||||
|
@ -444,6 +520,54 @@ class TestShareTypeSet(TestShareType):
|
|||
self.assertRaises(
|
||||
exceptions.CommandError, self.cmd.take_action, parsed_args)
|
||||
|
||||
def test_share_type_set_encryption_update_API(self):
|
||||
self.app.client_manager.share.api_version = api_versions.APIVersion(
|
||||
"2.86")
|
||||
|
||||
arglist = [
|
||||
self.share_type.id,
|
||||
'--encryption-cipher', 'test2_cipher',
|
||||
]
|
||||
verifylist = [
|
||||
('share_type', self.share_type.id),
|
||||
('encryption_cipher', 'test2_cipher')
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.share_type.get_encryption.return_value = {
|
||||
'provider': 'temp_provider'}
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
self.share_type.set_encryption.assert_called_with({
|
||||
'cipher': 'test2_cipher'}
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_share_type_set_encryption_create_API(self):
|
||||
self.app.client_manager.share.api_version = api_versions.APIVersion(
|
||||
"2.86")
|
||||
|
||||
arglist = [
|
||||
self.share_type.id,
|
||||
'--encryption-cipher', 'test2_cipher',
|
||||
'--encryption-provider', 'test2_provider',
|
||||
'--encryption-key-size', 'test2_key_size'
|
||||
]
|
||||
verifylist = [
|
||||
('share_type', self.share_type.id),
|
||||
('encryption_cipher', 'test2_cipher'),
|
||||
('encryption_provider', 'test2_provider'),
|
||||
('encryption_key_size', 'test2_key_size')
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
self.share_type.create_encryption.assert_called_with({
|
||||
'cipher': 'test2_cipher',
|
||||
'provider': 'test2_provider',
|
||||
'key_size': 'test2_key_size'}
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
|
||||
|
||||
class TestShareTypeUnset(TestShareType):
|
||||
|
||||
|
@ -451,7 +575,7 @@ class TestShareTypeUnset(TestShareType):
|
|||
super(TestShareTypeUnset, self).setUp()
|
||||
|
||||
self.share_type = manila_fakes.FakeShareType.create_one_sharetype(
|
||||
methods={'unset_keys': None})
|
||||
methods={'unset_keys': None, 'unset_encryption': None})
|
||||
self.shares_mock.get.return_value = self.share_type
|
||||
|
||||
# Get the command object to test
|
||||
|
@ -486,6 +610,26 @@ class TestShareTypeUnset(TestShareType):
|
|||
self.assertRaises(
|
||||
exceptions.CommandError, self.cmd.take_action, parsed_args)
|
||||
|
||||
def test_share_type_unset_encryption(self):
|
||||
self.app.client_manager.share.api_version = api_versions.APIVersion(
|
||||
"2.86")
|
||||
|
||||
arglist = [
|
||||
self.share_type.id,
|
||||
'snapshot_support',
|
||||
'--encryption-type',
|
||||
]
|
||||
verifylist = [
|
||||
('share_type', self.share_type.id),
|
||||
('extra_specs', ['snapshot_support']),
|
||||
('encryption_type', True)
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
self.share_type.unset_encryption.assert_called()
|
||||
self.assertIsNone(result)
|
||||
|
||||
|
||||
class TestShareTypeList(TestShareType):
|
||||
|
||||
|
@ -593,6 +737,9 @@ class TestShareTypeShow(TestShareType):
|
|||
'create_share_from_snapshot_support : True\n'
|
||||
'snapshot_support : True'),
|
||||
self.share_type.description,
|
||||
('cipher : test_cipher\n'
|
||||
'provider : test_provider\n'
|
||||
'key_size : test_key_size')
|
||||
]
|
||||
|
||||
self.raw_data = [
|
||||
|
@ -607,6 +754,9 @@ class TestShareTypeShow(TestShareType):
|
|||
'create_share_from_snapshot_support': True,
|
||||
'snapshot_support': True},
|
||||
self.share_type.description,
|
||||
{'cipher': 'test_cipher',
|
||||
'provider': 'test_provider',
|
||||
'key_size': 'test_key_size'}
|
||||
]
|
||||
|
||||
def test_share_type_show(self):
|
||||
|
@ -621,7 +771,7 @@ class TestShareTypeShow(TestShareType):
|
|||
columns, data = self.cmd.take_action(parsed_args)
|
||||
self.shares_mock.get.assert_called_with(self.share_type.id)
|
||||
|
||||
self.assertCountEqual(COLUMNS, columns)
|
||||
self.assertCountEqual(COLUMNS + ['encryption'], columns)
|
||||
self.assertCountEqual(self.data, data)
|
||||
|
||||
def test_share_type_show_json_format(self):
|
||||
|
@ -637,5 +787,5 @@ class TestShareTypeShow(TestShareType):
|
|||
columns, data = self.cmd.take_action(parsed_args)
|
||||
self.shares_mock.get.assert_called_with(self.share_type.id)
|
||||
|
||||
self.assertCountEqual(COLUMNS, columns)
|
||||
self.assertCountEqual(COLUMNS + ['encryption'], columns)
|
||||
self.assertCountEqual(self.raw_data, data)
|
||||
|
|
|
@ -1087,12 +1087,36 @@ class FakeHTTPClient(fakes.FakeHTTPClient):
|
|||
assert list(body) == ['extra_specs']
|
||||
return (200, {}, {'extra_specs': {'k': 'v'}})
|
||||
|
||||
def post_types_1_encryption(self, body, **kw):
|
||||
assert list(body) == ['encryption']
|
||||
return (200, {}, {
|
||||
'encryption': {'cipher': 'test_cipher',
|
||||
'provider': 'test_provider'}
|
||||
})
|
||||
|
||||
def put_types_1_encryption(self, body, **kw):
|
||||
assert list(body) == ['encryption']
|
||||
return (200, {}, {
|
||||
'encryption': {'cipher': 'test_cipher',
|
||||
'provider': 'test_provider'}
|
||||
})
|
||||
|
||||
def delete_types_1_extra_specs_k(self, **kw):
|
||||
return (204, {}, None)
|
||||
|
||||
def delete_types_1_encryption(self, **kw):
|
||||
return (204, {}, None)
|
||||
|
||||
def delete_types_1(self, **kw):
|
||||
return (202, {}, None)
|
||||
|
||||
def get_types_1_encryption(self, **kw):
|
||||
return (200, {}, {
|
||||
'encryption': {'cipher': 'test_cipher',
|
||||
'provider': 'test_provider',
|
||||
'key_size': 'test_key_size'}
|
||||
})
|
||||
|
||||
def get_types_3_os_share_type_access(self, **kw):
|
||||
return (200, {}, {'share_type_access': [
|
||||
{'share_type_id': '11111111-1111-1111-1111-111111111111',
|
||||
|
|
|
@ -436,6 +436,37 @@ class TypesTest(utils.TestCase):
|
|||
t.unset_keys(['k'])
|
||||
cs.assert_called('DELETE', '/types/1/extra_specs/k')
|
||||
|
||||
def test_create_encryption(self):
|
||||
t = cs.share_types.get(1)
|
||||
t.create_encryption({
|
||||
'cipher': 'test_cipher', 'provider': 'test_provider'})
|
||||
cs.assert_called('POST',
|
||||
'/types/1/encryption',
|
||||
{'encryption': {
|
||||
'cipher': 'test_cipher',
|
||||
'provider': 'test_provider'}})
|
||||
|
||||
def test_set_encryption(self):
|
||||
t = cs.share_types.get(1)
|
||||
t.set_encryption({
|
||||
'cipher': 'test_cipher', 'provider': 'test_provider'})
|
||||
cs.assert_called('PUT',
|
||||
'/types/1/encryption',
|
||||
{'encryption': {
|
||||
'cipher': 'test_cipher',
|
||||
'provider': 'test_provider'}})
|
||||
|
||||
def test_get_encryption(self):
|
||||
t = cs.share_types.get(1)
|
||||
t.get_encryption()
|
||||
cs.assert_called('GET',
|
||||
'/types/1/encryption')
|
||||
|
||||
def test_unset_encryption(self):
|
||||
t = cs.share_types.get(1)
|
||||
t.unset_encryption()
|
||||
cs.assert_called('DELETE', '/types/1/encryption')
|
||||
|
||||
@ddt.data(*set(('2.50', LATEST_MICROVERSION)))
|
||||
def test_update(self, microversion):
|
||||
manager = self._get_share_types_manager(microversion)
|
||||
|
|
|
@ -104,6 +104,51 @@ class ShareType(base.Resource):
|
|||
"""Update this share type."""
|
||||
return self.manager.update(self, **kwargs)
|
||||
|
||||
def create_encryption(self, encryption):
|
||||
"""Create encryption of a share type.
|
||||
|
||||
:param type : The :class:`ShareType` to set extra spec on
|
||||
:param encryption: An encryption dict of key/value pairs to be set
|
||||
"""
|
||||
body = {'encryption': encryption}
|
||||
return self.manager._create(
|
||||
"/types/%s/encryption" % base.getid(self),
|
||||
body,
|
||||
"encryption",
|
||||
return_raw=True,
|
||||
)
|
||||
|
||||
def get_encryption(self):
|
||||
"""Get encryption from a share type.
|
||||
|
||||
:return: dict with encryption
|
||||
"""
|
||||
_resp, body = self.manager.api.client.get(
|
||||
"/types/%s/encryption" % base.getid(self))
|
||||
|
||||
return body["encryption"]
|
||||
|
||||
def set_encryption(self, encryption):
|
||||
"""Set encryption on a share type.
|
||||
|
||||
:param type : The :class:`ShareType` to set extra spec on
|
||||
:param encryption: An encryption dict of key/value pairs to be set
|
||||
"""
|
||||
body = {'encryption': encryption}
|
||||
return self.manager._update(
|
||||
"/types/%s/encryption" % base.getid(self),
|
||||
body,
|
||||
"encryption",
|
||||
return_raw=True,
|
||||
)
|
||||
|
||||
def unset_encryption(self):
|
||||
"""Unset encryption on a share type.
|
||||
|
||||
:param type: The :class:`ShareType` to unset encryption on
|
||||
"""
|
||||
return self.manager._delete("/types/%s/encryption" % base.getid(self))
|
||||
|
||||
|
||||
class ShareTypeManager(base.ManagerWithFind):
|
||||
"""Manage :class:`ShareType` resources."""
|
||||
|
@ -148,7 +193,7 @@ class ShareTypeManager(base.ManagerWithFind):
|
|||
|
||||
def _do_create(self, name, extra_specs, is_public,
|
||||
is_public_keyname="share_type_access:is_public",
|
||||
description=None):
|
||||
description=None, encryption=None):
|
||||
"""Create a share type.
|
||||
|
||||
:param name: Descriptive name of the share type
|
||||
|
@ -165,6 +210,8 @@ class ShareTypeManager(base.ManagerWithFind):
|
|||
|
||||
if description:
|
||||
body["share_type"]["description"] = description
|
||||
if encryption:
|
||||
body['share_type']["encryption"] = encryption
|
||||
return self._create("/types", body, "share_type")
|
||||
|
||||
def _do_update(self, share_type, name=None, is_public=None,
|
||||
|
@ -234,7 +281,7 @@ class ShareTypeManager(base.ManagerWithFind):
|
|||
|
||||
return self._do_create(name, extra_specs, is_public)
|
||||
|
||||
@api_versions.wraps("2.41") # noqa
|
||||
@api_versions.wraps("2.41", "2.85") # noqa
|
||||
def create(self, name, spec_driver_handles_share_servers, # noqa
|
||||
spec_snapshot_support=None, is_public=True, extra_specs=None,
|
||||
description=None):
|
||||
|
@ -248,6 +295,21 @@ class ShareTypeManager(base.ManagerWithFind):
|
|||
return self._do_create(name, extra_specs, is_public,
|
||||
description=description)
|
||||
|
||||
@api_versions.wraps("2.86") # noqa
|
||||
def create(self, name, spec_driver_handles_share_servers, # noqa
|
||||
spec_snapshot_support=None, is_public=True, extra_specs=None,
|
||||
description=None, encryption=None):
|
||||
if extra_specs is None:
|
||||
extra_specs = {}
|
||||
|
||||
self._handle_spec_driver_handles_share_servers(
|
||||
extra_specs, spec_driver_handles_share_servers)
|
||||
self._handle_spec_snapshot_support(extra_specs, spec_snapshot_support)
|
||||
|
||||
return self._do_create(name, extra_specs, is_public,
|
||||
description=description,
|
||||
encryption=encryption)
|
||||
|
||||
@api_versions.wraps("2.50")
|
||||
def update(self, share_type, name=None, is_public=None, description=None):
|
||||
return self._do_update(share_type, name, is_public,
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Added support for share type encryption. Users can use openstack client to
|
||||
create/get/update/delete share type encryption. Available from microversion
|
||||
2.86.
|
Loading…
Reference in New Issue