Support share revert to snapshot in Manila client
This commit adds support to Manila client for the share-revert-to-snapshot feature. Implements: blueprint manila-share-revert-to-snapshot Depends-On: Id497e13070e0003db2db951526a52de6c2182cca Change-Id: Ia9c63ac416db47baae78b02ffcab5a60892cbf5c
This commit is contained in:
parent
c91deb8498
commit
d736acc442
@ -27,7 +27,7 @@ from manilaclient import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
MAX_VERSION = '2.26'
|
||||
MAX_VERSION = '2.27'
|
||||
MIN_VERSION = '2.0'
|
||||
DEPRECATED_VERSION = '1.0'
|
||||
_VERSIONED_METHOD_MAP = {}
|
||||
|
@ -172,7 +172,8 @@ class BaseTestCase(base.ClientTestBase):
|
||||
@classmethod
|
||||
def create_share_type(cls, name=None, driver_handles_share_servers=True,
|
||||
snapshot_support=None,
|
||||
create_share_from_snapshot=None, is_public=True,
|
||||
create_share_from_snapshot=None,
|
||||
revert_to_snapshot=None, is_public=True,
|
||||
client=None, cleanup_in_class=True,
|
||||
microversion=None, extra_specs=None):
|
||||
if client is None:
|
||||
@ -185,6 +186,7 @@ class BaseTestCase(base.ClientTestBase):
|
||||
microversion=microversion,
|
||||
extra_specs=extra_specs,
|
||||
create_share_from_snapshot=create_share_from_snapshot,
|
||||
revert_to_snapshot=revert_to_snapshot
|
||||
)
|
||||
resource = {
|
||||
"type": "share_type",
|
||||
|
@ -164,7 +164,8 @@ class ManilaCLIClient(base.CLIClient):
|
||||
|
||||
def create_share_type(self, name=None, driver_handles_share_servers=True,
|
||||
snapshot_support=None,
|
||||
create_share_from_snapshot=None, is_public=True,
|
||||
create_share_from_snapshot=None,
|
||||
revert_to_snapshot=None, is_public=True,
|
||||
microversion=None, extra_specs=None):
|
||||
"""Creates share type.
|
||||
|
||||
@ -179,6 +180,8 @@ class ManilaCLIClient(base.CLIClient):
|
||||
:param extra_specs: -- dictionary of extra specs Default is None.
|
||||
:param create_share_from_snapshot: -- boolean or its string
|
||||
alias. Default is None.
|
||||
:param revert_to_snapshot: -- boolean or its string alias. Default is
|
||||
None.
|
||||
"""
|
||||
if name is None:
|
||||
name = data_utils.rand_name('manilaclient_functional_test')
|
||||
@ -194,14 +197,20 @@ class ManilaCLIClient(base.CLIClient):
|
||||
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
|
||||
cmd += " --snapshot-support " + snapshot_support
|
||||
|
||||
if create_share_from_snapshot is not None:
|
||||
if not isinstance(create_share_from_snapshot, six.string_types):
|
||||
create_share_from_snapshot = six.text_type(
|
||||
create_share_from_snapshot)
|
||||
cmd += (" --create-share-from-snapshot-support " +
|
||||
create_share_from_snapshot)
|
||||
cmd += (" --create-share-from-snapshot-support " +
|
||||
create_share_from_snapshot)
|
||||
|
||||
if revert_to_snapshot is not None:
|
||||
if not isinstance(revert_to_snapshot, six.string_types):
|
||||
revert_to_snapshot = six.text_type(
|
||||
revert_to_snapshot)
|
||||
cmd += (" --revert-to-snapshot-support " + revert_to_snapshot)
|
||||
|
||||
if extra_specs is not None:
|
||||
extra_spec_str = ''
|
||||
|
@ -90,7 +90,7 @@ class ShareTypesReadWriteTest(base.BaseTestCase):
|
||||
self.skip_if_microversion_not_supported('2.0')
|
||||
self._test_create_delete_share_type(
|
||||
'2.0', is_public, dhss, spec_snapshot_support,
|
||||
None, extra_specs)
|
||||
None, None, extra_specs)
|
||||
|
||||
@ddt.data(*unit_test_types.get_valid_type_create_data_2_24())
|
||||
@ddt.unpack
|
||||
@ -101,11 +101,25 @@ class ShareTypesReadWriteTest(base.BaseTestCase):
|
||||
self.skip_if_microversion_not_supported('2.24')
|
||||
self._test_create_delete_share_type(
|
||||
'2.24', is_public, dhss, spec_snapshot_support,
|
||||
spec_create_share_from_snapshot, extra_specs)
|
||||
spec_create_share_from_snapshot, None, extra_specs)
|
||||
|
||||
@ddt.data(*unit_test_types.get_valid_type_create_data_2_27())
|
||||
@ddt.unpack
|
||||
def test_create_delete_share_type_2_27(
|
||||
self, is_public, dhss, spec_snapshot_support,
|
||||
spec_create_share_from_snapshot, spec_revert_to_snapshot_support,
|
||||
extra_specs):
|
||||
|
||||
self.skip_if_microversion_not_supported('2.27')
|
||||
self._test_create_delete_share_type(
|
||||
'2.27', is_public, dhss, spec_snapshot_support,
|
||||
spec_create_share_from_snapshot, spec_revert_to_snapshot_support,
|
||||
extra_specs)
|
||||
|
||||
def _test_create_delete_share_type(self, microversion, is_public, dhss,
|
||||
spec_snapshot_support,
|
||||
spec_create_share_from_snapshot,
|
||||
spec_revert_to_snapshot_support,
|
||||
extra_specs):
|
||||
|
||||
share_type_name = data_utils.rand_name('manilaclient_functional_test')
|
||||
@ -119,6 +133,7 @@ class ShareTypesReadWriteTest(base.BaseTestCase):
|
||||
driver_handles_share_servers=dhss,
|
||||
snapshot_support=spec_snapshot_support,
|
||||
create_share_from_snapshot=spec_create_share_from_snapshot,
|
||||
revert_to_snapshot=spec_revert_to_snapshot_support,
|
||||
is_public=is_public,
|
||||
microversion=microversion,
|
||||
extra_specs=extra_specs)
|
||||
@ -163,6 +178,11 @@ class ShareTypesReadWriteTest(base.BaseTestCase):
|
||||
('{} : {}'.format(
|
||||
'create_share_from_snapshot_support',
|
||||
spec_create_share_from_snapshot)).strip())
|
||||
if spec_revert_to_snapshot_support is not None:
|
||||
expected_extra_specs.append(
|
||||
('{} : {}'.format(
|
||||
'revert_to_snapshot_support',
|
||||
spec_revert_to_snapshot_support)).strip())
|
||||
|
||||
# Verify optional extra specs
|
||||
optional_extra_specs = share_type['optional_extra_specs']
|
||||
|
@ -402,6 +402,9 @@ class FakeHTTPClient(fakes.FakeHTTPClient):
|
||||
assert body[action]['new_size'] is not None
|
||||
elif action in ('unmanage', ):
|
||||
assert body[action] is None
|
||||
elif action in ('revert', ):
|
||||
assert body[action] is not None
|
||||
assert body[action]['snapshot_id'] is not None
|
||||
elif action in (
|
||||
'migration_cancel', 'migration_complete',
|
||||
'migration_get_progress'):
|
||||
|
@ -19,6 +19,7 @@ import ddt
|
||||
import mock
|
||||
|
||||
from manilaclient import api_versions
|
||||
from manilaclient.common.apiclient import exceptions as client_exceptions
|
||||
from manilaclient import exceptions
|
||||
from manilaclient import extension
|
||||
from manilaclient.tests.unit import utils
|
||||
@ -239,6 +240,35 @@ class SharesTest(utils.TestCase):
|
||||
manager._action.assert_called_once_with("unmanage", share)
|
||||
self.assertEqual("fake", result)
|
||||
|
||||
def test_revert_to_snapshot(self):
|
||||
|
||||
share = 'fake_share'
|
||||
snapshot = 'fake_snapshot'
|
||||
version = api_versions.APIVersion("2.27")
|
||||
mock_microversion = mock.Mock(api_version=version)
|
||||
manager = shares.ShareManager(api=mock_microversion)
|
||||
mock_action = self.mock_object(
|
||||
manager, '_action', mock.Mock(return_value='fake'))
|
||||
|
||||
result = manager.revert_to_snapshot(share, snapshot)
|
||||
|
||||
self.assertEqual('fake', result)
|
||||
mock_action.assert_called_once_with(
|
||||
'revert', 'fake_share', info={'snapshot_id': 'fake_snapshot'})
|
||||
|
||||
def test_revert_to_snapshot_not_supported(self):
|
||||
|
||||
share = 'fake_share'
|
||||
snapshot = 'fake_snapshot'
|
||||
version = api_versions.APIVersion("2.26")
|
||||
mock_microversion = mock.Mock(api_version=version)
|
||||
manager = shares.ShareManager(api=mock_microversion)
|
||||
|
||||
self.assertRaises(client_exceptions.UnsupportedVersion,
|
||||
manager.revert_to_snapshot,
|
||||
share,
|
||||
snapshot)
|
||||
|
||||
@ddt.data(
|
||||
("2.6", "os-force_delete"),
|
||||
("2.7", "force_delete"),
|
||||
|
@ -455,6 +455,7 @@ class ShellTest(test_utils.TestCase):
|
||||
'driver_handles_share_servers': False,
|
||||
'snapshot_support': True,
|
||||
'create_share_from_snapshot_support': True,
|
||||
'revert_to_snapshot_support': False,
|
||||
},
|
||||
'share_type_access:is_public': public
|
||||
}
|
||||
@ -618,6 +619,19 @@ class ShellTest(test_utils.TestCase):
|
||||
self.assert_called('POST', '/snapshots/1234/action',
|
||||
body={'unmanage': None})
|
||||
|
||||
def test_revert_to_snapshot(self):
|
||||
|
||||
fake_share_snapshot = type(
|
||||
'FakeShareSnapshot', (object,), {'id': '5678', 'share_id': '1234'})
|
||||
self.mock_object(
|
||||
shell_v2, '_find_share_snapshot',
|
||||
mock.Mock(return_value=fake_share_snapshot))
|
||||
|
||||
self.run_command('revert-to-snapshot 5678')
|
||||
|
||||
self.assert_called('POST', '/shares/1234/action',
|
||||
body={'revert': {'snapshot_id': '5678'}})
|
||||
|
||||
def test_delete(self):
|
||||
self.run_command('delete 1234')
|
||||
self.assert_called('DELETE', '/shares/1234')
|
||||
@ -823,6 +837,7 @@ class ShellTest(test_utils.TestCase):
|
||||
"driver_handles_share_servers": expected_bool,
|
||||
"snapshot_support": True,
|
||||
"create_share_from_snapshot_support": True,
|
||||
"revert_to_snapshot_support": False,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -870,6 +885,7 @@ class ShellTest(test_utils.TestCase):
|
||||
"driver_handles_share_servers": False,
|
||||
"snapshot_support": expected_bool,
|
||||
"create_share_from_snapshot_support": True,
|
||||
"revert_to_snapshot_support": False,
|
||||
"replication_type": replication_type,
|
||||
}
|
||||
}
|
||||
@ -898,6 +914,7 @@ class ShellTest(test_utils.TestCase):
|
||||
"driver_handles_share_servers": False,
|
||||
"snapshot_support": True,
|
||||
"create_share_from_snapshot_support": expected_bool,
|
||||
"revert_to_snapshot_support": False,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -923,6 +940,41 @@ class ShellTest(test_utils.TestCase):
|
||||
'type-create test false --extra-specs %s=fake' % value,
|
||||
)
|
||||
|
||||
@ddt.unpack
|
||||
@ddt.data(
|
||||
*([{'expected_bool': True, 'text': v}
|
||||
for v in ('true', 'True', '1', 'TRUE', 'tRuE')] +
|
||||
[{'expected_bool': False, 'text': v}
|
||||
for v in ('false', 'False', '0', 'FALSE', 'fAlSe')])
|
||||
)
|
||||
def test_type_create_with_revert_to_snapshot_support(
|
||||
self, expected_bool, text):
|
||||
expected = {
|
||||
"share_type": {
|
||||
"name": "test",
|
||||
"share_type_access:is_public": True,
|
||||
"extra_specs": {
|
||||
"driver_handles_share_servers": False,
|
||||
"snapshot_support": True,
|
||||
"create_share_from_snapshot_support": True,
|
||||
"revert_to_snapshot_support": expected_bool,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.run_command('type-create test false --snapshot-support true '
|
||||
'--revert-to-snapshot-support ' + text)
|
||||
|
||||
self.assert_called('POST', '/types', body=expected)
|
||||
|
||||
@ddt.data('fake', 'FFFalse', 'trueee')
|
||||
def test_type_create_invalid_revert_to_snapshot_support_value(self, value):
|
||||
self.assertRaises(
|
||||
exceptions.CommandError,
|
||||
self.run_command,
|
||||
'type-create test false --revert-to-snapshot-support ' + value,
|
||||
)
|
||||
|
||||
@ddt.data('--is-public', '--is_public')
|
||||
def test_update(self, alias):
|
||||
# basic rename with positional arguments
|
||||
|
@ -72,6 +72,47 @@ def get_valid_type_create_data_2_24():
|
||||
return snapshot_none_combos + snapshot_true_combos + snapshot_false_combos
|
||||
|
||||
|
||||
def get_valid_type_create_data_2_27():
|
||||
|
||||
public = [True, False]
|
||||
dhss = [True, False]
|
||||
snapshot = [None]
|
||||
create_from_snapshot = [None]
|
||||
revert_to_snapshot = [None]
|
||||
extra_specs = [None, {'replication_type': 'writable', 'foo': 'bar'}]
|
||||
|
||||
snapshot_none_combos = list(itertools.product(public, dhss, snapshot,
|
||||
create_from_snapshot,
|
||||
revert_to_snapshot,
|
||||
extra_specs))
|
||||
|
||||
public = [True, False]
|
||||
dhss = [True, False]
|
||||
snapshot = [True]
|
||||
create_from_snapshot = [True, False, None]
|
||||
revert_to_snapshot = [True, False, None]
|
||||
extra_specs = [None, {'replication_type': 'readable', 'foo': 'bar'}]
|
||||
|
||||
snapshot_true_combos = list(itertools.product(public, dhss, snapshot,
|
||||
create_from_snapshot,
|
||||
revert_to_snapshot,
|
||||
extra_specs))
|
||||
|
||||
public = [True, False]
|
||||
dhss = [True, False]
|
||||
snapshot = [False]
|
||||
create_from_snapshot = [False, None]
|
||||
revert_to_snapshot = [False, None]
|
||||
extra_specs = [None, {'replication_type': 'dr', 'foo': 'bar'}]
|
||||
|
||||
snapshot_false_combos = list(itertools.product(public, dhss, snapshot,
|
||||
create_from_snapshot,
|
||||
revert_to_snapshot,
|
||||
extra_specs))
|
||||
|
||||
return snapshot_none_combos + snapshot_true_combos + snapshot_false_combos
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TypesTest(utils.TestCase):
|
||||
|
||||
@ -146,9 +187,12 @@ class TypesTest(utils.TestCase):
|
||||
self.assertEqual("fake", result)
|
||||
|
||||
def _add_standard_extra_specs_to_dict(self, extra_specs,
|
||||
create_from_snapshot=None):
|
||||
create_from_snapshot=None,
|
||||
revert_to_snapshot=None):
|
||||
|
||||
if all(spec is None for spec in [create_from_snapshot]):
|
||||
# Short-circuit checks to allow for extra specs to be (and remain) None
|
||||
if all(spec is None for spec in [
|
||||
create_from_snapshot, revert_to_snapshot]):
|
||||
return extra_specs
|
||||
|
||||
extra_specs = extra_specs or {}
|
||||
@ -156,6 +200,9 @@ class TypesTest(utils.TestCase):
|
||||
if create_from_snapshot is not None:
|
||||
extra_specs['create_share_from_snapshot_support'] = (
|
||||
create_from_snapshot)
|
||||
if revert_to_snapshot is not None:
|
||||
extra_specs['revert_to_snapshot_support'] = (
|
||||
revert_to_snapshot)
|
||||
|
||||
return extra_specs
|
||||
|
||||
@ -203,6 +250,57 @@ class TypesTest(utils.TestCase):
|
||||
"/types", expected_body, "share_type")
|
||||
self.assertEqual("fake", result)
|
||||
|
||||
@ddt.data(*get_valid_type_create_data_2_27())
|
||||
@ddt.unpack
|
||||
def test_create_2_27(self, is_public, dhss, snapshot, create_from_snapshot,
|
||||
revert_to_snapshot, extra_specs):
|
||||
|
||||
extra_specs = copy.copy(extra_specs)
|
||||
extra_specs = self._add_standard_extra_specs_to_dict(
|
||||
extra_specs, create_from_snapshot=create_from_snapshot,
|
||||
revert_to_snapshot=revert_to_snapshot)
|
||||
|
||||
manager = self._get_share_types_manager("2.27")
|
||||
self.mock_object(manager, '_create', mock.Mock(return_value="fake"))
|
||||
|
||||
result = manager.create(
|
||||
'test-type-3', spec_driver_handles_share_servers=dhss,
|
||||
spec_snapshot_support=snapshot,
|
||||
extra_specs=extra_specs, is_public=is_public)
|
||||
|
||||
expected_extra_specs = dict(extra_specs or {})
|
||||
expected_extra_specs["driver_handles_share_servers"] = dhss
|
||||
|
||||
if snapshot is None:
|
||||
expected_extra_specs.pop("snapshot_support", None)
|
||||
else:
|
||||
expected_extra_specs["snapshot_support"] = snapshot
|
||||
|
||||
if create_from_snapshot is None:
|
||||
expected_extra_specs.pop("create_share_from_snapshot_support",
|
||||
None)
|
||||
else:
|
||||
expected_extra_specs["create_share_from_snapshot_support"] = (
|
||||
create_from_snapshot)
|
||||
|
||||
if revert_to_snapshot is None:
|
||||
expected_extra_specs.pop("revert_to_snapshot_support", None)
|
||||
else:
|
||||
expected_extra_specs["revert_to_snapshot_support"] = (
|
||||
revert_to_snapshot)
|
||||
|
||||
expected_body = {
|
||||
"share_type": {
|
||||
"name": 'test-type-3',
|
||||
'share_type_access:is_public': is_public,
|
||||
"extra_specs": expected_extra_specs,
|
||||
}
|
||||
}
|
||||
|
||||
manager._create.assert_called_once_with(
|
||||
"/types", expected_body, "share_type")
|
||||
self.assertEqual("fake", result)
|
||||
|
||||
@ddt.data(
|
||||
(False, False, True, {'snapshot_support': True,
|
||||
'replication_type': 'fake_repl_type'}),
|
||||
|
@ -109,6 +109,10 @@ class Share(common_base.Resource):
|
||||
"""List instances of the specified share."""
|
||||
self.manager.list_instances(self)
|
||||
|
||||
def revert_to_snapshot(self, snapshot):
|
||||
"""Reverts a share (in place) to a snapshot."""
|
||||
self.manager.revert_to_snapshot(self, snapshot)
|
||||
|
||||
|
||||
class ShareManager(base.ManagerWithFind):
|
||||
"""Manage :class:`Share` resources."""
|
||||
@ -276,6 +280,19 @@ class ShareManager(base.ManagerWithFind):
|
||||
"""
|
||||
return self._action("unmanage", share)
|
||||
|
||||
@api_versions.wraps("2.27")
|
||||
def revert_to_snapshot(self, share, snapshot):
|
||||
"""Reverts a share (in place) to a snapshot.
|
||||
|
||||
The snapshot must be the most recent one known to manila.
|
||||
:param share: either share object or text with its ID.
|
||||
:param snapshot: either snapshot object or text with its ID.
|
||||
"""
|
||||
|
||||
snapshot_id = common_base.getid(snapshot)
|
||||
info = {'snapshot_id': snapshot_id}
|
||||
return self._action('revert', share, info=info)
|
||||
|
||||
def get(self, share):
|
||||
"""Get a share.
|
||||
|
||||
|
@ -1034,6 +1034,19 @@ def do_snapshot_unmanage(cs, args):
|
||||
"specified snapshots.")
|
||||
|
||||
|
||||
@api_versions.wraps("2.27")
|
||||
@cliutils.arg(
|
||||
'snapshot',
|
||||
metavar='<snapshot>',
|
||||
help='Name or ID of the snapshot to restore. The snapshot must be the '
|
||||
'most recent one known to manila.')
|
||||
def do_revert_to_snapshot(cs, args):
|
||||
"""Revert a share to the specified snapshot."""
|
||||
snapshot = _find_share_snapshot(cs, args.snapshot)
|
||||
share = _find_share(cs, snapshot.share_id)
|
||||
share.revert_to_snapshot(snapshot)
|
||||
|
||||
|
||||
@cliutils.arg(
|
||||
'share',
|
||||
metavar='<share>',
|
||||
@ -3117,6 +3130,13 @@ def do_extra_specs_list(cs, args):
|
||||
action='single_alias',
|
||||
help="Boolean extra spec used for filtering of back ends by their "
|
||||
"capability to create shares from snapshots.")
|
||||
@cliutils.arg(
|
||||
'--revert_to_snapshot_support',
|
||||
'--revert-to-snapshot-support',
|
||||
metavar='<revert_to_snapshot_support>',
|
||||
action='single_alias',
|
||||
help="Boolean extra spec used for filtering of back ends by their "
|
||||
"capability to revert shares to snapshots. (Default is False).")
|
||||
@cliutils.arg(
|
||||
'--extra-specs',
|
||||
'--extra_specs', # alias
|
||||
@ -3157,7 +3177,11 @@ def do_type_create(cs, args):
|
||||
"set via positional argument.")
|
||||
raise exceptions.CommandError(msg)
|
||||
|
||||
boolean_keys = ('snapshot_support', 'create_share_from_snapshot_support')
|
||||
boolean_keys = (
|
||||
'snapshot_support',
|
||||
'create_share_from_snapshot_support',
|
||||
'revert_to_snapshot_support',
|
||||
)
|
||||
for key in boolean_keys:
|
||||
value = getattr(args, key)
|
||||
|
||||
|
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Added support for the revert-to-snapshot feature.
|
Loading…
Reference in New Issue
Block a user