From 7dfbb239de0dd150a20835441b16ff61d3ed6edb Mon Sep 17 00:00:00 2001 From: TommyLike Date: Tue, 16 May 2017 09:52:08 +0800 Subject: [PATCH] Support revert to snapshot in client This patch added revert to snapshot support in cinder client, also fix two pylint errors. Change-Id: I20d8df8d7bcf763f6651f44901a98f6d853b27ce Partial-Implements: blueprint revert-volume-to-snapshot Depends-On: cca9e1ac54d123da8859ff918b2355606bfa474e --- cinderclient/api_versions.py | 2 +- cinderclient/tests/unit/v2/fakes.py | 2 ++ cinderclient/tests/unit/v3/test_shell.py | 13 ++++++++++ cinderclient/tests/unit/v3/test_volumes.py | 24 +++++++++++++++++++ cinderclient/v3/shell.py | 13 ++++++++++ cinderclient/v3/volumes.py | 17 +++++++++++++ .../revert-to-snapshot-er4598df88aq5918.yaml | 3 +++ 7 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/revert-to-snapshot-er4598df88aq5918.yaml diff --git a/cinderclient/api_versions.py b/cinderclient/api_versions.py index d1bb1e16c..dd7da6536 100644 --- a/cinderclient/api_versions.py +++ b/cinderclient/api_versions.py @@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__) # key is a deprecated version and value is an alternative version. DEPRECATED_VERSIONS = {"1": "2"} DEPRECATED_VERSION = "2.0" -MAX_VERSION = "3.33" +MAX_VERSION = "3.40" MIN_VERSION = "3.0" _SUBSTITUTIONS = {} diff --git a/cinderclient/tests/unit/v2/fakes.py b/cinderclient/tests/unit/v2/fakes.py index a21b7b24e..900a4f2cc 100644 --- a/cinderclient/tests/unit/v2/fakes.py +++ b/cinderclient/tests/unit/v2/fakes.py @@ -535,6 +535,8 @@ class FakeHTTPClient(base_client.HTTPClient): elif action == 'os-volume_upload_image': assert 'image_name' in body[action] _body = body + elif action == 'revert': + assert 'snapshot_id' in body[action] else: raise AssertionError("Unexpected action: %s" % action) return (resp, {}, _body) diff --git a/cinderclient/tests/unit/v3/test_shell.py b/cinderclient/tests/unit/v3/test_shell.py index a6e9c1e73..977754866 100644 --- a/cinderclient/tests/unit/v3/test_shell.py +++ b/cinderclient/tests/unit/v3/test_shell.py @@ -23,6 +23,7 @@ from cinderclient import client from cinderclient import exceptions from cinderclient import shell from cinderclient.v3 import volumes +from cinderclient.v3 import volume_snapshots from cinderclient.tests.unit import utils from cinderclient.tests.unit.v3 import fakes from cinderclient.tests.unit.fixture_data import keystone_client @@ -278,6 +279,18 @@ class ShellTest(utils.TestCase): self.run_command(command) self.assert_called('GET', '/attachments%s' % expected) + @mock.patch('cinderclient.shell_utils.find_volume_snapshot') + def test_revert_to_snapshot(self, mock_snapshot): + + mock_snapshot.return_value = volume_snapshots.Snapshot( + self, {'id': '5678', 'volume_id': '1234'}) + + self.run_command( + '--os-volume-api-version 3.40 revert-to-snapshot 5678') + + self.assert_called('POST', '/volumes/1234/action', + body={'revert': {'snapshot_id': '5678'}}) + def test_attachment_show(self): self.run_command('--os-volume-api-version 3.27 attachment-show 1234') self.assert_called('GET', '/attachments/1234') diff --git a/cinderclient/tests/unit/v3/test_volumes.py b/cinderclient/tests/unit/v3/test_volumes.py index 3a84bf34b..5eb97507e 100644 --- a/cinderclient/tests/unit/v3/test_volumes.py +++ b/cinderclient/tests/unit/v3/test_volumes.py @@ -18,9 +18,11 @@ import ddt from cinderclient import api_versions +from cinderclient import exceptions from cinderclient.tests.unit import utils from cinderclient.tests.unit.v3 import fakes from cinderclient.v3 import volumes +from cinderclient.v3 import volume_snapshots from six.moves.urllib import parse @@ -48,6 +50,28 @@ class VolumesTest(utils.TestCase): visibility='public', protected=True) cs.assert_called_anytime('POST', '/volumes/1234/action', body=expected) + @ddt.data('3.39', '3.40') + def test_revert_to_snapshot(self, version): + + api_version = api_versions.APIVersion(version) + cs = fakes.FakeClient(api_version) + manager = volumes.VolumeManager(cs) + fake_snapshot = volume_snapshots.Snapshot( + manager, {'id': 12345, 'name': 'fake-snapshot'}, loaded=True) + fake_volume = volumes.Volume(manager, + {'id': 1234, 'name': 'sample-volume'}, + loaded=True) + expected = {'revert': {'snapshot_id': 12345}} + + if version == '3.40': + fake_volume.revert_to_snapshot(fake_snapshot) + + cs.assert_called_anytime('POST', '/volumes/1234/action', + body=expected) + else: + self.assertRaises(exceptions.VersionNotFoundForAPIMethod, + fake_volume.revert_to_snapshot, fake_snapshot) + def test_create_volume(self): vol = cs.volumes.create(1, group_id='1234', volume_type='5678') expected = {'volume': {'status': 'creating', diff --git a/cinderclient/v3/shell.py b/cinderclient/v3/shell.py index f8c057061..70b94da50 100644 --- a/cinderclient/v3/shell.py +++ b/cinderclient/v3/shell.py @@ -1371,6 +1371,19 @@ def do_api_version(cs, args): utils.print_list(response, columns) +@api_versions.wraps("3.40") +@utils.arg( + 'snapshot', + metavar='', + help='Name or ID of the snapshot to restore. The snapshot must be the ' + 'most recent one known to cinder.') +def do_revert_to_snapshot(cs, args): + """Revert a volume to the specified snapshot.""" + snapshot = shell_utils.find_volume_snapshot(cs, args.snapshot) + volume = utils.find_volume(cs, snapshot.volume_id) + volume.revert_to_snapshot(snapshot) + + @api_versions.wraps("3.3") @utils.arg('--marker', metavar='', diff --git a/cinderclient/v3/volumes.py b/cinderclient/v3/volumes.py index 222448941..ebe4ed2c7 100644 --- a/cinderclient/v3/volumes.py +++ b/cinderclient/v3/volumes.py @@ -45,6 +45,10 @@ class Volume(volumes.Volume): return self.manager.upload_to_image(self, force, image_name, container_format, disk_format) + def revert_to_snapshot(self, snapshot): + """Revert a volume to a snapshot.""" + self.manager.revert_to_snapshot(self, snapshot) + class VolumeManager(volumes.VolumeManager): resource_class = Volume @@ -109,6 +113,17 @@ class VolumeManager(volumes.VolumeManager): return self._create('/volumes', body, 'volume') + @api_versions.wraps('3.40') + def revert_to_snapshot(self, volume, snapshot): + """Revert a volume to a snapshot. + + The snapshot must be the most recent one known to cinder. + :param volume: volume object. + :param snapshot: snapshot object. + """ + return self._action('revert', volume, + info={'snapshot_id': base.getid(snapshot.id)}) + @api_versions.wraps("3.0") def delete_metadata(self, volume, keys): """Delete specified keys from volumes metadata. @@ -131,6 +146,7 @@ class VolumeManager(volumes.VolumeManager): :param volume: The :class:`Volume`. :param keys: A list of keys to be removed. """ + # pylint: disable=function-redefined data = self._get("/volumes/%s/metadata" % base.getid(volume)) metadata = data._info.get("metadata", {}) if set(keys).issubset(metadata.keys()): @@ -160,6 +176,7 @@ class VolumeManager(volumes.VolumeManager): """Upload volume to image service as image. :param volume: The :class:`Volume` to upload. """ + # pylint: disable=function-redefined return self._action('os-volume_upload_image', volume, {'force': force, diff --git a/releasenotes/notes/revert-to-snapshot-er4598df88aq5918.yaml b/releasenotes/notes/revert-to-snapshot-er4598df88aq5918.yaml new file mode 100644 index 000000000..395fab30f --- /dev/null +++ b/releasenotes/notes/revert-to-snapshot-er4598df88aq5918.yaml @@ -0,0 +1,3 @@ +--- +features: + - Added support for the revert-to-snapshot feature.