From 4a7014a46e2cbcbb9e3ae075eca0de0fca9e0f28 Mon Sep 17 00:00:00 2001 From: Subhadeep De Date: Fri, 23 May 2014 05:03:24 -0700 Subject: [PATCH] Ability to pass metadata during snapshot create Metadata as a parameter is supported by the underlying cinder driver. This is not exposed by the python client. It is helpful to store additional information for snapshots and can be used for decision within cinder driver during snapshot creation. Similar functionality exists for volume creation in cinder client and cinder driver. Updating cli(shell) to accomodate the new metadata parameter Adding above change for v2 Change-Id: I002221efea1b9994da4a8d200544a88b0ed26277 Closes-Bug: #1341424 --- cinderclient/tests/v2/fakes.py | 7 +++++++ cinderclient/tests/v2/test_shell.py | 13 +++++++++++++ cinderclient/v2/shell.py | 13 ++++++++++++- cinderclient/v2/volume_snapshots.py | 12 ++++++++++-- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/cinderclient/tests/v2/fakes.py b/cinderclient/tests/v2/fakes.py index 5439efbb6..462a70693 100644 --- a/cinderclient/tests/v2/fakes.py +++ b/cinderclient/tests/v2/fakes.py @@ -270,6 +270,13 @@ class FakeHTTPClient(base_client.HTTPClient): def get_snapshots_5678(self, **kw): return (200, {}, {'snapshot': _stub_snapshot(id='5678')}) + def post_snapshots(self, **kw): + metadata = kw['body']['snapshot'].get('metadata', None) + snapshot = _stub_snapshot(id='1234', volume_id='1234') + if snapshot is not None: + snapshot.update({'metadata': metadata}) + return (202, {}, {'snapshot': snapshot}) + def put_snapshots_1234(self, **kw): snapshot = _stub_snapshot(id='1234') snapshot.update(kw['body']['snapshot']) diff --git a/cinderclient/tests/v2/test_shell.py b/cinderclient/tests/v2/test_shell.py index 696d627db..e962f6e41 100644 --- a/cinderclient/tests/v2/test_shell.py +++ b/cinderclient/tests/v2/test_shell.py @@ -540,3 +540,16 @@ class ShellTest(utils.TestCase): self.run_command('replication-reenable 1234') self.assert_called('POST', '/volumes/1234/action', body={'os-reenable-replica': None}) + + def test_create_snapshot_from_volume_with_metadata(self): + """ + Tests create snapshot with --metadata parameter. + + Checks metadata params are set during create snapshot + when metadata is passed + """ + expected = {'snapshot': {'volume_id': 1234, + 'metadata': {'k1': 'v1', + 'k2': 'v2'}}} + self.run_command('snapshot-create 1234 --metadata k1=v1 k2=v2') + self.assert_called_anytime('POST', '/snapshots', partial_body=expected) diff --git a/cinderclient/v2/shell.py b/cinderclient/v2/shell.py index 2405dd5b1..9c2baafd1 100644 --- a/cinderclient/v2/shell.py +++ b/cinderclient/v2/shell.py @@ -562,6 +562,12 @@ def do_snapshot_show(cs, args): help=argparse.SUPPRESS) @utils.arg('--display_description', help=argparse.SUPPRESS) +@utils.arg('--metadata', + type=str, + nargs='*', + metavar='', + help='Snapshot metadata key and value pairs. Default=None.', + default=None) @utils.service_type('volumev2') def do_snapshot_create(cs, args): """Creates a snapshot.""" @@ -571,11 +577,16 @@ def do_snapshot_create(cs, args): if args.display_description is not None: args.description = args.display_description + snapshot_metadata = None + if args.metadata is not None: + snapshot_metadata = _extract_metadata(args) + volume = utils.find_volume(cs, args.volume) snapshot = cs.volume_snapshots.create(volume.id, args.force, args.name, - args.description) + args.description, + metadata=snapshot_metadata) _print_volume_snapshot(snapshot) diff --git a/cinderclient/v2/volume_snapshots.py b/cinderclient/v2/volume_snapshots.py index a398b0eb9..51b611db6 100644 --- a/cinderclient/v2/volume_snapshots.py +++ b/cinderclient/v2/volume_snapshots.py @@ -67,7 +67,7 @@ class SnapshotManager(base.ManagerWithFind): resource_class = Snapshot def create(self, volume_id, force=False, - name=None, description=None): + name=None, description=None, metadata=None): """Creates a snapshot of the given volume. @@ -76,12 +76,20 @@ class SnapshotManager(base.ManagerWithFind): attached to an instance. Default is False. :param name: Name of the snapshot :param description: Description of the snapshot + :param metadata: Metadata of the snapshot :rtype: :class:`Snapshot` """ + + if metadata is None: + snapshot_metadata = {} + else: + snapshot_metadata = metadata + body = {'snapshot': {'volume_id': volume_id, 'force': force, 'name': name, - 'description': description}} + 'description': description, + 'metadata': snapshot_metadata}} return self._create('/snapshots', body, 'snapshot') def get(self, snapshot_id):