Merge "type-create should support specifying extra-specs"
This commit is contained in:
commit
ca9bd141d8
|
@ -163,8 +163,9 @@ class BaseTestCase(base.ClientTestBase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_share_type(cls, name=None, driver_handles_share_servers=True,
|
def create_share_type(cls, name=None, driver_handles_share_servers=True,
|
||||||
snapshot_support=True, is_public=True, client=None,
|
snapshot_support=None, is_public=True, client=None,
|
||||||
cleanup_in_class=True, microversion=None):
|
cleanup_in_class=True, microversion=None,
|
||||||
|
extra_specs=None):
|
||||||
if client is None:
|
if client is None:
|
||||||
client = cls.get_admin_client()
|
client = cls.get_admin_client()
|
||||||
share_type = client.create_share_type(
|
share_type = client.create_share_type(
|
||||||
|
@ -173,6 +174,7 @@ class BaseTestCase(base.ClientTestBase):
|
||||||
snapshot_support=snapshot_support,
|
snapshot_support=snapshot_support,
|
||||||
is_public=is_public,
|
is_public=is_public,
|
||||||
microversion=microversion,
|
microversion=microversion,
|
||||||
|
extra_specs=extra_specs,
|
||||||
)
|
)
|
||||||
resource = {
|
resource = {
|
||||||
"type": "share_type",
|
"type": "share_type",
|
||||||
|
|
|
@ -144,8 +144,8 @@ class ManilaCLIClient(base.CLIClient):
|
||||||
# Share types
|
# Share types
|
||||||
|
|
||||||
def create_share_type(self, name=None, driver_handles_share_servers=True,
|
def create_share_type(self, name=None, driver_handles_share_servers=True,
|
||||||
snapshot_support=True, is_public=True,
|
snapshot_support=None, is_public=True,
|
||||||
microversion=None):
|
microversion=None, extra_specs=None):
|
||||||
"""Creates share type.
|
"""Creates share type.
|
||||||
|
|
||||||
:param name: text -- name of share type to use, if not set then
|
:param name: text -- name of share type to use, if not set then
|
||||||
|
@ -156,22 +156,34 @@ class ManilaCLIClient(base.CLIClient):
|
||||||
string alias. Default is True.
|
string alias. Default is True.
|
||||||
:param is_public: bool/str -- boolean or its string alias. Default is
|
:param is_public: bool/str -- boolean or its string alias. Default is
|
||||||
True.
|
True.
|
||||||
|
:param extra_specs: -- dictionary of extra specs Default is None.
|
||||||
"""
|
"""
|
||||||
if name is None:
|
if name is None:
|
||||||
name = data_utils.rand_name('manilaclient_functional_test')
|
name = data_utils.rand_name('manilaclient_functional_test')
|
||||||
dhss = driver_handles_share_servers
|
dhss = driver_handles_share_servers
|
||||||
if not isinstance(dhss, six.string_types):
|
if not isinstance(dhss, six.string_types):
|
||||||
dhss = six.text_type(dhss)
|
dhss = six.text_type(dhss)
|
||||||
if not isinstance(snapshot_support, six.string_types):
|
|
||||||
snapshot_support = six.text_type(snapshot_support)
|
|
||||||
if not isinstance(is_public, six.string_types):
|
if not isinstance(is_public, six.string_types):
|
||||||
is_public = six.text_type(is_public)
|
is_public = six.text_type(is_public)
|
||||||
cmd = ('type-create %(name)s %(dhss)s --is-public %(is_public)s '
|
|
||||||
'--snapshot-support %(snapshot_support)s') % {
|
cmd = ('type-create %(name)s %(dhss)s --is-public %(is_public)s ') % {
|
||||||
'name': name, 'dhss': dhss, 'is_public': is_public,
|
'name': name, 'dhss': dhss, 'is_public': is_public}
|
||||||
'snapshot_support': snapshot_support}
|
|
||||||
|
if snapshot_support is not None:
|
||||||
|
if not isinstance(snapshot_support, six.string_types):
|
||||||
|
snapshot_support = six.text_type(snapshot_support)
|
||||||
|
cmd += " --snapshot-support " + snapshot_support
|
||||||
|
|
||||||
|
if extra_specs is not None:
|
||||||
|
extra_spec_str = ''
|
||||||
|
for k, v in extra_specs.items():
|
||||||
|
if not isinstance(v, six.string_types):
|
||||||
|
extra_specs[k] = six.text_type(v)
|
||||||
|
extra_spec_str += "{}='{}' ".format(k, v)
|
||||||
|
cmd += " --extra_specs " + extra_spec_str
|
||||||
|
|
||||||
share_type_raw = self.manila(cmd, microversion=microversion)
|
share_type_raw = self.manila(cmd, microversion=microversion)
|
||||||
share_type = output_parser.details(share_type_raw)
|
share_type = utils.details(share_type_raw)
|
||||||
return share_type
|
return share_type
|
||||||
|
|
||||||
@not_found_wrapper
|
@not_found_wrapper
|
||||||
|
|
|
@ -81,42 +81,76 @@ class ShareTypesReadWriteTest(base.BaseTestCase):
|
||||||
microversion=microversion))
|
microversion=microversion))
|
||||||
|
|
||||||
@ddt.data(
|
@ddt.data(
|
||||||
(True, False),
|
(True, False, None),
|
||||||
(True, True),
|
(True, True, None),
|
||||||
(False, True),
|
(False, True, None),
|
||||||
(False, False),
|
(False, False, None),
|
||||||
(False, True, False, "2.6"),
|
|
||||||
(False, False, True, "2.7"),
|
(False, True, None, {'snapshot_support': False,
|
||||||
|
'replication_type': 'fake_repl_type1'}, "2.6"),
|
||||||
|
(False, False, None, {'snapshot_support': True,
|
||||||
|
'replication_type': 'fake_repl_type2'}, "2.7"),
|
||||||
|
(False, False, None, {'snapshot_support': False,
|
||||||
|
'replication_type': 'fake_repl_type3',
|
||||||
|
'foo': 'bar'}),
|
||||||
|
(False, False, None, {'replication_type': 'fake_repl_type4',
|
||||||
|
'foo': 'bar',
|
||||||
|
'foo2': 'abcd'}),
|
||||||
|
(False, True, True, {'foo2': 'abcd'}),
|
||||||
|
(False, True, False, {'foo2': 'abcd'}),
|
||||||
|
(False, True, False, {'capabilities:dedupe': '<is> True'}),
|
||||||
)
|
)
|
||||||
@ddt.unpack
|
@ddt.unpack
|
||||||
def test_create_delete_share_type(self, is_public, dhss,
|
def test_create_delete_share_type(self, is_public, dhss,
|
||||||
snapshot_support=True,
|
spec_snapshot_support=None,
|
||||||
microversion=None):
|
extra_specs=None, microversion=None):
|
||||||
if microversion:
|
if microversion:
|
||||||
self.skip_if_microversion_not_supported(microversion)
|
self.skip_if_microversion_not_supported(microversion)
|
||||||
|
|
||||||
share_type_name = data_utils.rand_name('manilaclient_functional_test')
|
share_type_name = data_utils.rand_name('manilaclient_functional_test')
|
||||||
dhss_expected = 'driver_handles_share_servers : %s' % dhss
|
dhss_expected = 'driver_handles_share_servers : %s' % dhss
|
||||||
snapshot_support_expected = 'snapshot_support : %s' % snapshot_support
|
|
||||||
|
if extra_specs is None:
|
||||||
|
extra_specs = {}
|
||||||
|
|
||||||
|
expected_extra_specs = []
|
||||||
|
for key, val in extra_specs.items():
|
||||||
|
expected_extra_specs.append(('{} : {}'.format(key, val)).strip())
|
||||||
|
|
||||||
|
if 'snapshot_support' not in extra_specs:
|
||||||
|
if spec_snapshot_support is None:
|
||||||
|
expected_extra_specs.append(
|
||||||
|
('{} : {}'.format('snapshot_support', True)).strip())
|
||||||
|
else:
|
||||||
|
expected_extra_specs.append(
|
||||||
|
('{} : {}'.format(
|
||||||
|
'snapshot_support',
|
||||||
|
spec_snapshot_support)).strip())
|
||||||
|
|
||||||
# Create share type
|
# Create share type
|
||||||
share_type = self.create_share_type(
|
share_type = self.create_share_type(
|
||||||
name=share_type_name,
|
name=share_type_name,
|
||||||
driver_handles_share_servers=dhss,
|
driver_handles_share_servers=dhss,
|
||||||
snapshot_support=snapshot_support,
|
snapshot_support=spec_snapshot_support,
|
||||||
is_public=is_public,
|
is_public=is_public,
|
||||||
microversion=microversion,
|
microversion=microversion,
|
||||||
|
extra_specs=extra_specs,
|
||||||
)
|
)
|
||||||
|
|
||||||
st_id = share_type['ID']
|
st_id = share_type['ID']
|
||||||
|
optional_extra_specs = share_type['optional_extra_specs']
|
||||||
# Verify response body
|
# Verify response body
|
||||||
for key in self.create_keys:
|
for key in self.create_keys:
|
||||||
self.assertIn(key, share_type)
|
self.assertIn(key, share_type)
|
||||||
|
|
||||||
self.assertEqual(share_type_name, share_type['Name'])
|
self.assertEqual(share_type_name, share_type['Name'])
|
||||||
self.assertEqual(dhss_expected, share_type['required_extra_specs'])
|
self.assertEqual(dhss_expected, share_type['required_extra_specs'])
|
||||||
self.assertEqual(
|
if not isinstance(optional_extra_specs, list):
|
||||||
snapshot_support_expected, share_type['optional_extra_specs'])
|
optional_extra_specs = [optional_extra_specs]
|
||||||
|
self.assertEqual(len(expected_extra_specs),
|
||||||
|
len(optional_extra_specs))
|
||||||
|
for e in optional_extra_specs:
|
||||||
|
self.assertIn(e.strip(), expected_extra_specs)
|
||||||
|
|
||||||
self.assertEqual('public' if is_public else 'private',
|
self.assertEqual('public' if is_public else 'private',
|
||||||
share_type['Visibility'].lower())
|
share_type['Visibility'].lower())
|
||||||
self.assertEqual('-', share_type['is_default'])
|
self.assertEqual('-', share_type['is_default'])
|
||||||
|
|
|
@ -93,6 +93,15 @@ def listing(output_lines):
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
|
||||||
|
def details(output_lines):
|
||||||
|
"""Returns dict parsed from CLI output."""
|
||||||
|
result = listing(output_lines)
|
||||||
|
d = {}
|
||||||
|
for item in result:
|
||||||
|
d.update({item['Property']: item['Value']})
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
def is_microversion_supported(microversion):
|
def is_microversion_supported(microversion):
|
||||||
return (
|
return (
|
||||||
api_versions.APIVersion(CONF.min_api_microversion) <=
|
api_versions.APIVersion(CONF.min_api_microversion) <=
|
||||||
|
|
|
@ -685,11 +685,11 @@ class FakeHTTPClient(fakes.FakeHTTPClient):
|
||||||
return (200, {}, {
|
return (200, {}, {
|
||||||
'share_types': [{'id': 1,
|
'share_types': [{'id': 1,
|
||||||
'name': 'test-type-1',
|
'name': 'test-type-1',
|
||||||
'extra_specs': {'test': 'test'},
|
'extra_specs': {'test1': 'test1'},
|
||||||
'required_extra_specs': {'test': 'test'}},
|
'required_extra_specs': {'test': 'test'}},
|
||||||
{'id': 2,
|
{'id': 2,
|
||||||
'name': 'test-type-2',
|
'name': 'test-type-2',
|
||||||
'extra_specs': {'test': 'test'},
|
'extra_specs': {'test1': 'test1'},
|
||||||
'required_extra_specs': {'test': 'test'}}]})
|
'required_extra_specs': {'test': 'test'}}]})
|
||||||
|
|
||||||
def get_types_1(self, **kw):
|
def get_types_1(self, **kw):
|
||||||
|
@ -728,12 +728,16 @@ class FakeHTTPClient(fakes.FakeHTTPClient):
|
||||||
|
|
||||||
def post_types(self, body, **kw):
|
def post_types(self, body, **kw):
|
||||||
share_type = body['share_type']
|
share_type = body['share_type']
|
||||||
|
required_extra_specs = {
|
||||||
|
"driver_handles_share_servers": share_type[
|
||||||
|
'extra_specs']['driver_handles_share_servers'],
|
||||||
|
}
|
||||||
return (202, {}, {
|
return (202, {}, {
|
||||||
'share_type': {
|
'share_type': {
|
||||||
'id': 3,
|
'id': 3,
|
||||||
'name': 'test-type-3',
|
'name': 'test-type-3',
|
||||||
'extra_specs': share_type['extra_specs'],
|
'extra_specs': share_type['extra_specs'],
|
||||||
'required_extra_specs': share_type['extra_specs'],
|
'required_extra_specs': required_extra_specs,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -756,6 +756,32 @@ class ShellTest(test_utils.TestCase):
|
||||||
'type-create test ' + value,
|
'type-create test ' + value,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ddt.data('True', 'False')
|
||||||
|
def test_type_create_duplicate_dhss(self, value):
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.CommandError,
|
||||||
|
self.run_command,
|
||||||
|
'type-create test ' + value +
|
||||||
|
' --extra-specs driver_handles_share_servers=' + value,
|
||||||
|
)
|
||||||
|
|
||||||
|
@ddt.data('True', 'False')
|
||||||
|
def test_type_create_duplicate_snapshot_support(self, value):
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.CommandError,
|
||||||
|
self.run_command,
|
||||||
|
'type-create test True --snapshot-support ' + value +
|
||||||
|
' --extra-specs snapshot_support=' + value,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_type_create_duplicate_extra_spec_key(self):
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.CommandError,
|
||||||
|
self.run_command,
|
||||||
|
'type-create test True --extra-specs'
|
||||||
|
' a=foo1 a=foo2',
|
||||||
|
)
|
||||||
|
|
||||||
@ddt.unpack
|
@ddt.unpack
|
||||||
@ddt.data({'expected_bool': True, 'text': 'true'},
|
@ddt.data({'expected_bool': True, 'text': 'true'},
|
||||||
{'expected_bool': True, 'text': '1'},
|
{'expected_bool': True, 'text': '1'},
|
||||||
|
@ -785,6 +811,29 @@ class ShellTest(test_utils.TestCase):
|
||||||
for v in ('false', 'False', '0', 'FALSE', 'fAlSe')])
|
for v in ('false', 'False', '0', 'FALSE', 'fAlSe')])
|
||||||
)
|
)
|
||||||
def test_create_with_snapshot_support(self, expected_bool, text):
|
def test_create_with_snapshot_support(self, expected_bool, text):
|
||||||
|
expected = {
|
||||||
|
"share_type": {
|
||||||
|
"name": "test",
|
||||||
|
"share_type_access:is_public": True,
|
||||||
|
"extra_specs": {
|
||||||
|
"snapshot_support": expected_bool,
|
||||||
|
"driver_handles_share_servers": False,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.run_command('type-create test false --snapshot-support ' + text)
|
||||||
|
|
||||||
|
self.assert_called('POST', '/types', body=expected)
|
||||||
|
|
||||||
|
@ddt.unpack
|
||||||
|
@ddt.data({'expected_bool': True,
|
||||||
|
'snapshot_text': 'true',
|
||||||
|
'replication_type': 'readable'},
|
||||||
|
{'expected_bool': False,
|
||||||
|
'snapshot_text': 'false',
|
||||||
|
'replication_type': 'writable'})
|
||||||
|
def test_create_with_extra_specs(self, expected_bool, snapshot_text,
|
||||||
|
replication_type):
|
||||||
expected = {
|
expected = {
|
||||||
"share_type": {
|
"share_type": {
|
||||||
"name": "test",
|
"name": "test",
|
||||||
|
@ -792,11 +841,14 @@ class ShellTest(test_utils.TestCase):
|
||||||
"extra_specs": {
|
"extra_specs": {
|
||||||
"driver_handles_share_servers": False,
|
"driver_handles_share_servers": False,
|
||||||
"snapshot_support": expected_bool,
|
"snapshot_support": expected_bool,
|
||||||
|
"replication_type": replication_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.run_command('type-create test false --snapshot-support ' + text)
|
self.run_command('type-create test false --extra-specs'
|
||||||
|
' snapshot_support=' + snapshot_text +
|
||||||
|
' replication_type=' + replication_type)
|
||||||
|
|
||||||
self.assert_called('POST', '/types', body=expected)
|
self.assert_called('POST', '/types', body=expected)
|
||||||
|
|
||||||
|
@ -808,6 +860,14 @@ class ShellTest(test_utils.TestCase):
|
||||||
'type-create test false --snapshot-support ' + value,
|
'type-create test false --snapshot-support ' + value,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ddt.data('fake', 'FFFalse', 'trueee')
|
||||||
|
def test_type_create_invalid_snapshot_support_value2(self, value):
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.CommandError,
|
||||||
|
self.run_command,
|
||||||
|
'type-create test false --extra-specs snapshot_support=' + value,
|
||||||
|
)
|
||||||
|
|
||||||
@ddt.data('--is-public', '--is_public')
|
@ddt.data('--is-public', '--is_public')
|
||||||
def test_update(self, alias):
|
def test_update(self, alias):
|
||||||
# basic rename with positional arguments
|
# basic rename with positional arguments
|
||||||
|
|
|
@ -15,6 +15,7 @@ import ddt
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
from manilaclient import api_versions
|
from manilaclient import api_versions
|
||||||
|
from manilaclient import exceptions
|
||||||
from manilaclient.tests.unit import utils
|
from manilaclient.tests.unit import utils
|
||||||
from manilaclient.tests.unit.v2 import fakes
|
from manilaclient.tests.unit.v2 import fakes
|
||||||
from manilaclient.v2 import share_types
|
from manilaclient.v2 import share_types
|
||||||
|
@ -35,7 +36,7 @@ class TypesTest(utils.TestCase):
|
||||||
{'snapshot_support': 'False', 'foo': 'bar'},
|
{'snapshot_support': 'False', 'foo': 'bar'},
|
||||||
)
|
)
|
||||||
def test_init(self, extra_specs):
|
def test_init(self, extra_specs):
|
||||||
info = {'extra_specs': {'snapshot_support': 'False'}}
|
info = {'extra_specs': extra_specs}
|
||||||
|
|
||||||
share_type = share_types.ShareType(share_types.ShareTypeManager, info)
|
share_type = share_types.ShareType(share_types.ShareTypeManager, info)
|
||||||
|
|
||||||
|
@ -43,8 +44,7 @@ class TypesTest(utils.TestCase):
|
||||||
self.assertTrue(hasattr(share_type, '_optional_extra_specs'))
|
self.assertTrue(hasattr(share_type, '_optional_extra_specs'))
|
||||||
self.assertIsInstance(share_type._required_extra_specs, dict)
|
self.assertIsInstance(share_type._required_extra_specs, dict)
|
||||||
self.assertIsInstance(share_type._optional_extra_specs, dict)
|
self.assertIsInstance(share_type._optional_extra_specs, dict)
|
||||||
self.assertEqual(
|
self.assertEqual(extra_specs, share_type.get_optional_keys())
|
||||||
{'snapshot_support': 'False'}, share_type.get_optional_keys())
|
|
||||||
|
|
||||||
def test_list_types(self):
|
def test_list_types(self):
|
||||||
tl = cs.share_types.list()
|
tl = cs.share_types.list()
|
||||||
|
@ -54,46 +54,110 @@ class TypesTest(utils.TestCase):
|
||||||
self.assertTrue(callable(getattr(t, 'get_required_keys', '')))
|
self.assertTrue(callable(getattr(t, 'get_required_keys', '')))
|
||||||
self.assertTrue(callable(getattr(t, 'get_optional_keys', '')))
|
self.assertTrue(callable(getattr(t, 'get_optional_keys', '')))
|
||||||
self.assertEqual({'test': 'test'}, t.get_required_keys())
|
self.assertEqual({'test': 'test'}, t.get_required_keys())
|
||||||
self.assertEqual(
|
self.assertEqual({'test1': 'test1'}, t.get_optional_keys())
|
||||||
{'snapshot_support': 'unknown'}, t.get_optional_keys())
|
|
||||||
|
|
||||||
def test_list_types_only_public(self):
|
def test_list_types_only_public(self):
|
||||||
cs.share_types.list(show_all=False)
|
cs.share_types.list(show_all=False)
|
||||||
cs.assert_called('GET', '/types')
|
cs.assert_called('GET', '/types')
|
||||||
|
|
||||||
@ddt.data(
|
@ddt.data(
|
||||||
(True, True, True),
|
(True, True, None, {'snapshot_support': True}),
|
||||||
(True, True, False),
|
(True, True, None, {'snapshot_support': True,
|
||||||
(True, False, True),
|
'replication_type': 'fake_repl_type',
|
||||||
(False, True, True),
|
'foo': 'bar'}),
|
||||||
(True, False, False),
|
(True, True, None, {'snapshot_support': False,
|
||||||
(False, False, True),
|
'replication_type': 'fake_repl_type',
|
||||||
(False, True, False),
|
'foo': 'abc'}),
|
||||||
(False, False, False),
|
(True, False, None, {'snapshot_support': True,
|
||||||
|
'replication_type': 'fake_repl_type'}),
|
||||||
|
(False, True, None, {'snapshot_support': True,
|
||||||
|
'replication_type': 'fake_repl_type'}),
|
||||||
|
(True, False, None, {'snapshot_support': False,
|
||||||
|
'replication_type': 'fake_repl_type'}),
|
||||||
|
(False, False, None, {'snapshot_support': True,
|
||||||
|
'replication_type': 'fake_repl_type'}),
|
||||||
|
(False, True, None, {'snapshot_support': False,
|
||||||
|
'replication_type': 'fake_repl_type'}),
|
||||||
|
(False, False, None, {'snapshot_support': False,
|
||||||
|
'replication_type': 'fake_repl_type'}),
|
||||||
|
|
||||||
|
(False, False, None, {'replication_type': 'fake_repl_type'}),
|
||||||
|
(False, False, True, {'replication_type': 'fake_repl_type'}),
|
||||||
|
(False, False, False, {'replication_type': 'fake_repl_type'}),
|
||||||
|
|
||||||
|
(False, False, False, None),
|
||||||
|
(False, False, True, None),
|
||||||
|
(False, False, None, None),
|
||||||
)
|
)
|
||||||
@ddt.unpack
|
@ddt.unpack
|
||||||
def test_create(self, is_public, dhss, snapshot_support):
|
def test_create(self, is_public, dhss, spec_snapshot_support, extra_specs):
|
||||||
manager = self._get_share_types_manager("2.7")
|
manager = self._get_share_types_manager("2.7")
|
||||||
|
|
||||||
|
if extra_specs is None:
|
||||||
|
extra_specs = {}
|
||||||
|
|
||||||
|
expected_extra_specs = dict(extra_specs)
|
||||||
|
|
||||||
expected_body = {
|
expected_body = {
|
||||||
"share_type": {
|
"share_type": {
|
||||||
"name": 'test-type-3',
|
"name": 'test-type-3',
|
||||||
'share_type_access:is_public': is_public,
|
'share_type_access:is_public': is_public,
|
||||||
"extra_specs": {
|
"extra_specs": expected_extra_specs,
|
||||||
"driver_handles_share_servers": dhss,
|
|
||||||
"snapshot_support": snapshot_support,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expected_body["share_type"]["extra_specs"][
|
||||||
|
"driver_handles_share_servers"] = dhss
|
||||||
|
|
||||||
|
if spec_snapshot_support is None:
|
||||||
|
if 'snapshot_support' not in extra_specs:
|
||||||
|
expected_body["share_type"]["extra_specs"][
|
||||||
|
'snapshot_support'] = True
|
||||||
|
else:
|
||||||
|
expected_body["share_type"]["extra_specs"][
|
||||||
|
'snapshot_support'] = spec_snapshot_support
|
||||||
|
|
||||||
with mock.patch.object(manager, '_create',
|
with mock.patch.object(manager, '_create',
|
||||||
mock.Mock(return_value="fake")):
|
mock.Mock(return_value="fake")):
|
||||||
result = manager.create(
|
result = manager.create(
|
||||||
'test-type-3', dhss, snapshot_support, is_public=is_public)
|
'test-type-3', spec_driver_handles_share_servers=dhss,
|
||||||
|
spec_snapshot_support=spec_snapshot_support,
|
||||||
|
extra_specs=extra_specs, is_public=is_public)
|
||||||
|
|
||||||
manager._create.assert_called_once_with(
|
manager._create.assert_called_once_with(
|
||||||
"/types", expected_body, "share_type")
|
"/types", expected_body, "share_type")
|
||||||
self.assertEqual("fake", result)
|
self.assertEqual("fake", result)
|
||||||
|
|
||||||
|
@ddt.data(
|
||||||
|
(False, False, True, {'snapshot_support': True,
|
||||||
|
'replication_type': 'fake_repl_type'}),
|
||||||
|
(False, False, False, {'snapshot_support': False,
|
||||||
|
'replication_type': 'fake_repl_type'}),
|
||||||
|
(False, False, True, {'snapshot_support': False,
|
||||||
|
'replication_type': 'fake_repl_type'}),
|
||||||
|
(False, False, False, {'snapshot_support': True,
|
||||||
|
'replication_type': 'fake_repl_type'}),
|
||||||
|
|
||||||
|
(False, True, None, {'driver_handles_share_servers': True}),
|
||||||
|
(False, False, None, {'driver_handles_share_servers': True}),
|
||||||
|
(False, None, None, {'driver_handles_share_servers': True}),
|
||||||
|
(False, None, None, {'driver_handles_share_servers': None}),
|
||||||
|
)
|
||||||
|
@ddt.unpack
|
||||||
|
def test_create_command_error(self, is_public, dhss, spec_snapshot_support,
|
||||||
|
extra_specs):
|
||||||
|
manager = self._get_share_types_manager("2.7")
|
||||||
|
with mock.patch.object(manager, '_create',
|
||||||
|
mock.Mock(return_value="fake")):
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.CommandError,
|
||||||
|
manager.create,
|
||||||
|
'test-type-3',
|
||||||
|
spec_driver_handles_share_servers=dhss,
|
||||||
|
spec_snapshot_support=spec_snapshot_support,
|
||||||
|
extra_specs=extra_specs,
|
||||||
|
is_public=is_public)
|
||||||
|
|
||||||
@ddt.data(
|
@ddt.data(
|
||||||
("2.6", True),
|
("2.6", True),
|
||||||
("2.7", True),
|
("2.7", True),
|
||||||
|
|
|
@ -20,6 +20,7 @@ Share Type interface.
|
||||||
|
|
||||||
from manilaclient import api_versions
|
from manilaclient import api_versions
|
||||||
from manilaclient import base
|
from manilaclient import base
|
||||||
|
from manilaclient import exceptions
|
||||||
from manilaclient.openstack.common.apiclient import base as common_base
|
from manilaclient.openstack.common.apiclient import base as common_base
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,10 +30,9 @@ class ShareType(common_base.Resource):
|
||||||
def __init__(self, manager, info, loaded=False):
|
def __init__(self, manager, info, loaded=False):
|
||||||
super(ShareType, self).__init__(manager, info, loaded)
|
super(ShareType, self).__init__(manager, info, loaded)
|
||||||
self._required_extra_specs = info.get('required_extra_specs', {})
|
self._required_extra_specs = info.get('required_extra_specs', {})
|
||||||
self._optional_extra_specs = {
|
self._optional_extra_specs = info.get('extra_specs', {}).copy()
|
||||||
'snapshot_support': info.get('extra_specs', {}).get(
|
for key in self._required_extra_specs.keys():
|
||||||
'snapshot_support', 'unknown'),
|
self._optional_extra_specs.pop(key, None)
|
||||||
}
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<ShareType: %s>" % self.name
|
return "<ShareType: %s>" % self.name
|
||||||
|
@ -134,23 +134,45 @@ class ShareTypeManager(base.ManagerWithFind):
|
||||||
self._delete("/types/%s" % common_base.getid(share_type))
|
self._delete("/types/%s" % common_base.getid(share_type))
|
||||||
|
|
||||||
def _do_create(self, name, spec_driver_handles_share_servers,
|
def _do_create(self, name, spec_driver_handles_share_servers,
|
||||||
spec_snapshot_support=True, is_public=True,
|
spec_snapshot_support, is_public=True,
|
||||||
is_public_keyname="os-share-type-access:is_public"):
|
is_public_keyname="os-share-type-access:is_public",
|
||||||
|
optional_extra_specs=None):
|
||||||
"""Create a share type.
|
"""Create a share type.
|
||||||
|
|
||||||
:param name: Descriptive name of the share type
|
:param name: Descriptive name of the share type
|
||||||
:rtype: :class:`ShareType`
|
:rtype: :class:`ShareType`
|
||||||
"""
|
"""
|
||||||
|
if optional_extra_specs is None:
|
||||||
|
optional_extra_specs = {}
|
||||||
|
|
||||||
|
if spec_snapshot_support is not None:
|
||||||
|
if 'snapshot_support' in optional_extra_specs:
|
||||||
|
msg = "'snapshot_support' extra spec is provided twice."
|
||||||
|
raise exceptions.CommandError(msg)
|
||||||
|
else:
|
||||||
|
optional_extra_specs['snapshot_support'] = (
|
||||||
|
spec_snapshot_support)
|
||||||
|
elif 'snapshot_support' not in optional_extra_specs:
|
||||||
|
optional_extra_specs['snapshot_support'] = True
|
||||||
|
|
||||||
|
if spec_driver_handles_share_servers is not None:
|
||||||
|
if 'driver_handles_share_servers' in optional_extra_specs:
|
||||||
|
msg = ("'driver_handles_share_servers' is already set via "
|
||||||
|
"positional argument.")
|
||||||
|
raise exceptions.CommandError(msg)
|
||||||
|
else:
|
||||||
|
optional_extra_specs['driver_handles_share_servers'] = (
|
||||||
|
spec_driver_handles_share_servers)
|
||||||
|
else:
|
||||||
|
msg = ("'driver_handles_share_servers' is not set via "
|
||||||
|
"positional argument.")
|
||||||
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
body = {
|
body = {
|
||||||
"share_type": {
|
"share_type": {
|
||||||
"name": name,
|
"name": name,
|
||||||
is_public_keyname: is_public,
|
is_public_keyname: is_public,
|
||||||
"extra_specs": {
|
"extra_specs": optional_extra_specs,
|
||||||
"driver_handles_share_servers":
|
|
||||||
spec_driver_handles_share_servers,
|
|
||||||
"snapshot_support": spec_snapshot_support,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,20 +180,26 @@ class ShareTypeManager(base.ManagerWithFind):
|
||||||
|
|
||||||
@api_versions.wraps("1.0", "2.6")
|
@api_versions.wraps("1.0", "2.6")
|
||||||
def create(self, name, spec_driver_handles_share_servers,
|
def create(self, name, spec_driver_handles_share_servers,
|
||||||
spec_snapshot_support=True, is_public=True):
|
spec_snapshot_support=None, is_public=True,
|
||||||
|
extra_specs=None):
|
||||||
|
|
||||||
return self._do_create(
|
return self._do_create(
|
||||||
name,
|
name,
|
||||||
spec_driver_handles_share_servers,
|
spec_driver_handles_share_servers,
|
||||||
spec_snapshot_support,
|
spec_snapshot_support,
|
||||||
is_public,
|
is_public,
|
||||||
"os-share-type-access:is_public")
|
"os-share-type-access:is_public",
|
||||||
|
optional_extra_specs=extra_specs)
|
||||||
|
|
||||||
@api_versions.wraps("2.7") # noqa
|
@api_versions.wraps("2.7") # noqa
|
||||||
def create(self, name, spec_driver_handles_share_servers,
|
def create(self, name, spec_driver_handles_share_servers,
|
||||||
spec_snapshot_support=True, is_public=True):
|
spec_snapshot_support=None, is_public=True,
|
||||||
|
extra_specs=None):
|
||||||
|
|
||||||
return self._do_create(
|
return self._do_create(
|
||||||
name,
|
name,
|
||||||
spec_driver_handles_share_servers,
|
spec_driver_handles_share_servers,
|
||||||
spec_snapshot_support,
|
spec_snapshot_support,
|
||||||
is_public,
|
is_public,
|
||||||
"share_type_access:is_public")
|
"share_type_access:is_public",
|
||||||
|
optional_extra_specs=extra_specs)
|
||||||
|
|
|
@ -254,6 +254,8 @@ def _extract_extra_specs(args):
|
||||||
|
|
||||||
def _extract_key_value_options(args, option_name):
|
def _extract_key_value_options(args, option_name):
|
||||||
result_dict = {}
|
result_dict = {}
|
||||||
|
duplicate_options = []
|
||||||
|
|
||||||
options = getattr(args, option_name, None)
|
options = getattr(args, option_name, None)
|
||||||
|
|
||||||
if options:
|
if options:
|
||||||
|
@ -265,7 +267,15 @@ def _extract_key_value_options(args, option_name):
|
||||||
key = option
|
key = option
|
||||||
value = None
|
value = None
|
||||||
|
|
||||||
result_dict[key] = value
|
if key not in result_dict:
|
||||||
|
result_dict[key] = value
|
||||||
|
else:
|
||||||
|
duplicate_options.append(key)
|
||||||
|
|
||||||
|
if len(duplicate_options) > 0:
|
||||||
|
duplicate_str = ', '.join(duplicate_options)
|
||||||
|
msg = "Following options were duplicated: %s" % duplicate_str
|
||||||
|
raise exceptions.CommandError(msg)
|
||||||
return result_dict
|
return result_dict
|
||||||
|
|
||||||
|
|
||||||
|
@ -2769,6 +2779,18 @@ def do_extra_specs_list(cs, args):
|
||||||
action='single_alias',
|
action='single_alias',
|
||||||
help="Boolean extra spec that used for filtering of back ends by their "
|
help="Boolean extra spec that used for filtering of back ends by their "
|
||||||
"capability to create share snapshots. (Default is True).")
|
"capability to create share snapshots. (Default is True).")
|
||||||
|
@cliutils.arg(
|
||||||
|
'--extra-specs',
|
||||||
|
'--extra_specs', # alias
|
||||||
|
type=str,
|
||||||
|
nargs='*',
|
||||||
|
metavar='<key=value>',
|
||||||
|
action='single_alias',
|
||||||
|
help="Extra specs key and value of share type that will be"
|
||||||
|
" used for share type creation. OPTIONAL: Default=None."
|
||||||
|
" e.g --extra-specs thin_provisioning='<is> True', "
|
||||||
|
"replication_type=readable.",
|
||||||
|
default=None)
|
||||||
@cliutils.arg(
|
@cliutils.arg(
|
||||||
'--is_public',
|
'--is_public',
|
||||||
'--is-public',
|
'--is-public',
|
||||||
|
@ -2789,10 +2811,26 @@ def do_type_create(cs, args):
|
||||||
msg = ("Argument spec_driver_handles_share_servers "
|
msg = ("Argument spec_driver_handles_share_servers "
|
||||||
"argument is not valid: %s" % six.text_type(e))
|
"argument is not valid: %s" % six.text_type(e))
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
kwargs['extra_specs'] = _extract_extra_specs(args)
|
||||||
|
|
||||||
|
if 'driver_handles_share_servers' in kwargs['extra_specs']:
|
||||||
|
msg = ("Argument 'driver_handles_share_servers' is already "
|
||||||
|
"set via positional argument.")
|
||||||
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
if args.snapshot_support and 'snapshot_support' in kwargs['extra_specs']:
|
||||||
|
msg = ("Argument 'snapshot_support' value specified twice.")
|
||||||
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if args.snapshot_support:
|
if args.snapshot_support:
|
||||||
kwargs['spec_snapshot_support'] = strutils.bool_from_string(
|
kwargs['spec_snapshot_support'] = strutils.bool_from_string(
|
||||||
args.snapshot_support, strict=True)
|
args.snapshot_support, strict=True)
|
||||||
|
elif 'snapshot_support' in kwargs['extra_specs']:
|
||||||
|
kwargs['extra_specs']['snapshot_support'] = (
|
||||||
|
strutils.bool_from_string(
|
||||||
|
kwargs['extra_specs']['snapshot_support'], strict=True))
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
msg = ("Argument 'snapshot_support' is of boolean type and has "
|
msg = ("Argument 'snapshot_support' is of boolean type and has "
|
||||||
"invalid value: %s" % six.text_type(e))
|
"invalid value: %s" % six.text_type(e))
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- User can specify any number of extra-specs in type_create.
|
Loading…
Reference in New Issue