From 948e90010fa9acfb1ba8d44fc42d12b0ff375046 Mon Sep 17 00:00:00 2001 From: wanghao Date: Tue, 24 Nov 2015 17:13:19 +0800 Subject: [PATCH] support for snapshot management Add support to manage/unmanage snapshot cinder snapshot-manage volume identifier option args is --id-type, --name, --description, --metadata. cinder snapshot-unmanage snapshot DocImpact Implements: blueprint support-for-snapshot-management Change-Id: Id4d73fa0ff0c0c05c0c69924968aa2154da64118 --- cinderclient/tests/unit/v2/fakes.py | 7 +++ cinderclient/tests/unit/v2/test_shell.py | 18 ++++++ .../tests/unit/v2/test_snapshot_actions.py | 6 ++ cinderclient/tests/unit/v2/test_volumes.py | 6 ++ cinderclient/v2/shell.py | 60 +++++++++++++++++++ cinderclient/v2/volume_snapshots.py | 26 ++++++++ 6 files changed, 123 insertions(+) diff --git a/cinderclient/tests/unit/v2/fakes.py b/cinderclient/tests/unit/v2/fakes.py index b0c78b96e..654525f4a 100644 --- a/cinderclient/tests/unit/v2/fakes.py +++ b/cinderclient/tests/unit/v2/fakes.py @@ -346,6 +346,8 @@ class FakeHTTPClient(base_client.HTTPClient): assert 'status' in body['os-reset_status'] elif action == 'os-update_snapshot_status': assert 'status' in body['os-update_snapshot_status'] + elif action == 'os-unmanage': + assert body[action] is None else: raise AssertionError('Unexpected action: %s' % action) return (resp, {}, _body) @@ -1074,6 +1076,11 @@ class FakeHTTPClient(base_client.HTTPClient): volume.update(kw['body']['volume']) return (202, {}, {'volume': volume}) + def post_os_snapshot_manage(self, **kw): + snapshot = _stub_snapshot(id='1234', volume_id='volume_id1') + snapshot.update(kw['body']['snapshot']) + return (202, {}, {'snapshot': snapshot}) + def post_os_promote_replica_1234(self, **kw): return (202, {}, {}) diff --git a/cinderclient/tests/unit/v2/test_shell.py b/cinderclient/tests/unit/v2/test_shell.py index 1c140cb5f..3613b5445 100644 --- a/cinderclient/tests/unit/v2/test_shell.py +++ b/cinderclient/tests/unit/v2/test_shell.py @@ -1151,3 +1151,21 @@ class ShellTest(utils.TestCase): pass expected = {"os-show_image_metadata": None} self.assert_called('POST', '/volumes/1234/action', body=expected) + + def test_snapshot_manage(self): + self.run_command('snapshot-manage 1234 some_fake_name ' + '--name foo --description bar ' + '--metadata k1=v1 k2=v2') + expected = {'snapshot': {'volume_id': 1234, + 'ref': {'source-name': 'some_fake_name'}, + 'name': 'foo', + 'description': 'bar', + 'metadata': {'k1': 'v1', 'k2': 'v2'} + }} + self.assert_called_anytime('POST', '/os-snapshot-manage', + body=expected) + + def test_snapshot_unmanage(self): + self.run_command('snapshot-unmanage 1234') + self.assert_called('POST', '/snapshots/1234/action', + body={'os-unmanage': None}) diff --git a/cinderclient/tests/unit/v2/test_snapshot_actions.py b/cinderclient/tests/unit/v2/test_snapshot_actions.py index cc84885c1..a8b226c1f 100644 --- a/cinderclient/tests/unit/v2/test_snapshot_actions.py +++ b/cinderclient/tests/unit/v2/test_snapshot_actions.py @@ -42,3 +42,9 @@ class SnapshotActionsTest(utils.FixturedTestCase): def test_list_snapshots_with_sort(self): self.cs.volume_snapshots.list(sort="id") self.assert_called('GET', '/snapshots/detail?sort=id') + + def test_snapshot_unmanage(self): + s = self.cs.volume_snapshots.get('1234') + self.cs.volume_snapshots.unmanage(s) + self.assert_called('POST', '/snapshots/1234/action', + {'os-unmanage': None}) diff --git a/cinderclient/tests/unit/v2/test_volumes.py b/cinderclient/tests/unit/v2/test_volumes.py index 526023871..3c59bf065 100644 --- a/cinderclient/tests/unit/v2/test_volumes.py +++ b/cinderclient/tests/unit/v2/test_volumes.py @@ -227,6 +227,12 @@ class VolumesTest(utils.TestCase): cs.volumes.unmanage(v) cs.assert_called('POST', '/volumes/1234/action', {'os-unmanage': None}) + def test_snapshot_manage(self): + cs.volume_snapshots.manage('volume_id1', {'k': 'v'}) + expected = {'volume_id': 'volume_id1', 'name': None, + 'description': None, 'metadata': None, 'ref': {'k': 'v'}} + cs.assert_called('POST', '/os-snapshot-manage', {'snapshot': expected}) + def test_replication_promote(self): v = cs.volumes.get('1234') cs.volumes.promote(v) diff --git a/cinderclient/v2/shell.py b/cinderclient/v2/shell.py index eb489a9b5..5e4b0df65 100644 --- a/cinderclient/v2/shell.py +++ b/cinderclient/v2/shell.py @@ -2535,3 +2535,63 @@ def do_get_capabilities(cs, args): prop = infos.pop('properties', None) utils.print_dict(infos, "Volume stats") utils.print_dict(prop, "Backend properties") + + +@utils.arg('volume', + metavar='', + help='Cinder volume already exists in volume backend') +@utils.arg('identifier', + metavar='', + help='Name or other Identifier for existing snapshot') +@utils.arg('--id-type', + metavar='', + default='source-name', + help='Type of backend device identifier provided, ' + 'typically source-name or source-id (Default=source-name)') +@utils.arg('--name', + metavar='', + help='Snapshot name (Default=None)') +@utils.arg('--description', + metavar='', + help='Snapshot description (Default=None)') +@utils.arg('--metadata', + type=str, + nargs='*', + metavar='', + help='Metadata key=value pairs (Default=None)') +@utils.service_type('volumev2') +def do_snapshot_manage(cs, args): + """Manage an existing snapshot.""" + snapshot_metadata = None + if args.metadata is not None: + snapshot_metadata = _extract_metadata(args) + + # Build a dictionary of key/value pairs to pass to the API. + ref_dict = {args.id_type: args.identifier} + + if hasattr(args, 'source_name') and args.source_name is not None: + ref_dict['source-name'] = args.source_name + if hasattr(args, 'source_id') and args.source_id is not None: + ref_dict['source-id'] = args.source_id + + volume = utils.find_volume(cs, args.volume) + snapshot = cs.volume_snapshots.manage(volume_id=volume.id, + ref=ref_dict, + name=args.name, + description=args.description, + metadata=snapshot_metadata) + + info = {} + snapshot = cs.volume_snapshots.get(snapshot.id) + info.update(snapshot._info) + info.pop('links', None) + utils.print_dict(info) + + +@utils.arg('snapshot', metavar='', + help='Name or ID of the snapshot to unmanage.') +@utils.service_type('volumev2') +def do_snapshot_unmanage(cs, args): + """Stop managing a snapshot.""" + snapshot = _find_volume_snapshot(cs, args.snapshot) + cs.volume_snapshots.unmanage(snapshot.id) diff --git a/cinderclient/v2/volume_snapshots.py b/cinderclient/v2/volume_snapshots.py index c08ab3938..1d5ca408e 100644 --- a/cinderclient/v2/volume_snapshots.py +++ b/cinderclient/v2/volume_snapshots.py @@ -56,6 +56,16 @@ class Snapshot(base.Resource): """Update_all metadata of this snapshot.""" return self.manager.update_all_metadata(self, metadata) + def manage(self, volume_id, ref, name=None, description=None, + metadata=None): + """Manage an existing snapshot.""" + self.manager.manage(volume_id=volume_id, ref=ref, name=name, + description=description, metadata=metadata) + + def unmanage(self, snapshot): + """Unmanage a snapshot.""" + self.manager.unmanage(snapshot) + class SnapshotManager(base.ManagerWithFind): """Manage :class:`Snapshot` resources.""" @@ -170,3 +180,19 @@ class SnapshotManager(base.ManagerWithFind): body = {'metadata': metadata} return self._update("/snapshots/%s/metadata" % base.getid(snapshot), body) + + def manage(self, volume_id, ref, name=None, description=None, + metadata=None): + """Manage an existing snapshot.""" + body = {'snapshot': {'volume_id': volume_id, + 'ref': ref, + 'name': name, + 'description': description, + 'metadata': metadata + } + } + return self._create('/os-snapshot-manage', body, 'snapshot') + + def unmanage(self, snapshot): + """Unmanage a snapshot.""" + return self._action('os-unmanage', snapshot, None)