[OSC] Add share type encryption

Implemented share type encryption create/get/set/delete osc client
calls.

partially-implements: blueprint share-encryption
Change-Id: I630f95a3a7e4909465b1190bbe5cd3ac51352b27
This commit is contained in:
Kiran Pawar 2024-04-20 16:24:18 +00:00
parent 9e81815674
commit b01b675266
10 changed files with 474 additions and 21 deletions

View File

@ -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 = {}

View File

@ -32,7 +32,8 @@ ATTRIBUTES = [
'is_default',
'required_extra_specs',
'optional_extra_specs',
'description'
'description',
'encryption',
]
@ -56,6 +57,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 +66,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 +147,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,6 +220,30 @@ 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)
@ -287,6 +335,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 +391,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 +435,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."""
@ -352,6 +464,14 @@ class UnsetShareType(command.Command):
nargs='+',
help=_('Remove extra_specs from this share type'),
)
parser.add_argument(
"--encryption-type",
metavar="<encryption-type>",
default=False,
help=_("Boolean encryption-type used for removing "
"encryption of share type. (Default is False). "
"Available with API microversion >= 2.86"),
)
return parser
def take_action(self, parsed_args):
@ -367,6 +487,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 to share type "
"is only available with API microversion >= 2.86")
class ListShareType(command.Lister):
"""List Share Types."""
@ -391,6 +523,14 @@ class ListShareType(command.Lister):
'Available only for API microversion >= 2.43. '
'OPTIONAL: Default=None.'),
)
parser.add_argument(
"--encryption-type",
metavar="<encryption-type>",
default=False,
help=_("Boolean encryption-type used for listing "
"encryption of share type. (Default is False). "
"Available with API microversion >= 2.86"),
)
return parser
def take_action(self, parsed_args):
@ -409,6 +549,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({'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)

View File

@ -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} '

View File

@ -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)

View File

@ -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)

View File

@ -31,7 +31,8 @@ COLUMNS = [
'is_default',
'required_extra_specs',
'optional_extra_specs',
'description'
'description',
'encryption',
]
@ -69,6 +70,9 @@ class TestShareTypeCreate(TestShareType):
'create_share_from_snapshot_support : True\n'
'snapshot_support : True'),
self.new_share_type.description,
('cipher : test_cipher\n'
'provider : test_provider\n'
'key_size : test_key_size')
]
self.raw_data = [
@ -83,6 +87,9 @@ class TestShareTypeCreate(TestShareType):
'create_share_from_snapshot_support': True,
'snapshot_support': True},
self.new_share_type.description,
{'cipher': 'test_cipher',
'provider': 'test_provider',
'key_size': 'test_key_size'}
]
def test_share_type_create_required_args(self):
@ -105,7 +112,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 +140,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 +180,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 +207,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 +282,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, columns)
self.assertCountEqual(self.data, 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 +413,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 +515,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 +570,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 +605,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', 'True'
]
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 +732,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 +749,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):

View File

@ -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',

View File

@ -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)

View File

@ -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,

View File

@ -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.