diff --git a/cinderclient/tests/v1/fakes.py b/cinderclient/tests/v1/fakes.py index b1d482117..6520da651 100644 --- a/cinderclient/tests/v1/fakes.py +++ b/cinderclient/tests/v1/fakes.py @@ -746,3 +746,18 @@ class FakeHTTPClient(base_client.HTTPClient): }, ] }) + + def post_snapshots_1234_metadata(self, **kw): + return (200, {}, {"metadata": {"key1": "val1", "key2": "val2"}}) + + def delete_snapshots_1234_metadata_key1(self, **kw): + return (200, {}, None) + + def delete_snapshots_1234_metadata_key2(self, **kw): + return (200, {}, None) + + def put_volumes_1234_metadata(self, **kw): + return (200, {}, {"metadata": {"key1": "val1", "key2": "val2"}}) + + def put_snapshots_1234_metadata(self, **kw): + return (200, {}, {"metadata": {"key1": "val1", "key2": "val2"}}) diff --git a/cinderclient/tests/v1/test_shell.py b/cinderclient/tests/v1/test_shell.py index 740342d9c..edc5d7bf9 100644 --- a/cinderclient/tests/v1/test_shell.py +++ b/cinderclient/tests/v1/test_shell.py @@ -278,3 +278,29 @@ class ShellTest(utils.TestCase): expected = {'os-migrate_volume': {'force_host_copy': 'True', 'host': 'fakehost'}} self.assert_called('POST', '/volumes/1234/action', body=expected) + + def test_snapshot_metadata_set(self): + self.run_command('snapshot-metadata 1234 set key1=val1 key2=val2') + self.assert_called('POST', '/snapshots/1234/metadata', + {'metadata': {'key1': 'val1', 'key2': 'val2'}}) + + def test_snapshot_metadata_unset_dict(self): + self.run_command('snapshot-metadata 1234 unset key1=val1 key2=val2') + self.assert_called('DELETE', '/snapshots/1234/metadata/key1') + self.assert_called('DELETE', '/snapshots/1234/metadata/key2', pos=-2) + + def test_snapshot_metadata_unset_keys(self): + self.run_command('snapshot-metadata 1234 unset key1 key2') + self.assert_called('DELETE', '/snapshots/1234/metadata/key1') + self.assert_called('DELETE', '/snapshots/1234/metadata/key2', pos=-2) + + def test_volume_metadata_update_all(self): + self.run_command('metadata-update-all 1234 key1=val1 key2=val2') + self.assert_called('PUT', '/volumes/1234/metadata', + {'metadata': {'key1': 'val1', 'key2': 'val2'}}) + + def test_snapshot_metadata_update_all(self): + self.run_command('snapshot-metadata-update-all\ + 1234 key1=val1 key2=val2') + self.assert_called('PUT', '/snapshots/1234/metadata', + {'metadata': {'key1': 'val1', 'key2': 'val2'}}) diff --git a/cinderclient/tests/v1/test_volumes.py b/cinderclient/tests/v1/test_volumes.py index bb7343810..f38fbe6fb 100644 --- a/cinderclient/tests/v1/test_volumes.py +++ b/cinderclient/tests/v1/test_volumes.py @@ -96,3 +96,8 @@ class VolumesTest(utils.TestCase): v = cs.volumes.get('1234') cs.volumes.migrate_volume(v, 'dest', False) cs.assert_called('POST', '/volumes/1234/action') + + def test_metadata_update_all(self): + cs.volumes.update_all_metadata(1234, {'k1': 'v1'}) + cs.assert_called('PUT', '/volumes/1234/metadata', + {'metadata': {'k1': 'v1'}}) diff --git a/cinderclient/tests/v2/fakes.py b/cinderclient/tests/v2/fakes.py index 174369d0a..53bb35890 100644 --- a/cinderclient/tests/v2/fakes.py +++ b/cinderclient/tests/v2/fakes.py @@ -754,3 +754,18 @@ class FakeHTTPClient(base_client.HTTPClient): }, ] }) + + def post_snapshots_1234_metadata(self, **kw): + return (200, {}, {"metadata": {"key1": "val1", "key2": "val2"}}) + + def delete_snapshots_1234_metadata_key1(self, **kw): + return (200, {}, None) + + def delete_snapshots_1234_metadata_key2(self, **kw): + return (200, {}, None) + + def put_volumes_1234_metadata(self, **kw): + return (200, {}, {"metadata": {"key1": "val1", "key2": "val2"}}) + + def put_snapshots_1234_metadata(self, **kw): + return (200, {}, {"metadata": {"key1": "val1", "key2": "val2"}}) diff --git a/cinderclient/tests/v2/test_shell.py b/cinderclient/tests/v2/test_shell.py index 26a389c8e..58d8de3f6 100644 --- a/cinderclient/tests/v2/test_shell.py +++ b/cinderclient/tests/v2/test_shell.py @@ -256,3 +256,29 @@ class ShellTest(utils.TestCase): expected = {'os-migrate_volume': {'force_host_copy': 'True', 'host': 'fakehost'}} self.assert_called('POST', '/volumes/1234/action', body=expected) + + def test_snapshot_metadata_set(self): + self.run_command('snapshot-metadata 1234 set key1=val1 key2=val2') + self.assert_called('POST', '/snapshots/1234/metadata', + {'metadata': {'key1': 'val1', 'key2': 'val2'}}) + + def test_snapshot_metadata_unset_dict(self): + self.run_command('snapshot-metadata 1234 unset key1=val1 key2=val2') + self.assert_called('DELETE', '/snapshots/1234/metadata/key1') + self.assert_called('DELETE', '/snapshots/1234/metadata/key2', pos=-2) + + def test_snapshot_metadata_unset_keys(self): + self.run_command('snapshot-metadata 1234 unset key1 key2') + self.assert_called('DELETE', '/snapshots/1234/metadata/key1') + self.assert_called('DELETE', '/snapshots/1234/metadata/key2', pos=-2) + + def test_volume_metadata_update_all(self): + self.run_command('metadata-update-all 1234 key1=val1 key2=val2') + self.assert_called('PUT', '/volumes/1234/metadata', + {'metadata': {'key1': 'val1', 'key2': 'val2'}}) + + def test_snapshot_metadata_update_all(self): + self.run_command('snapshot-metadata-update-all\ + 1234 key1=val1 key2=val2') + self.assert_called('PUT', '/snapshots/1234/metadata', + {'metadata': {'key1': 'val1', 'key2': 'val2'}}) diff --git a/cinderclient/tests/v2/test_volumes.py b/cinderclient/tests/v2/test_volumes.py index 7b8536989..7e44f840b 100644 --- a/cinderclient/tests/v2/test_volumes.py +++ b/cinderclient/tests/v2/test_volumes.py @@ -99,3 +99,8 @@ class VolumesTest(utils.TestCase): v = cs.volumes.get('1234') cs.volumes.migrate_volume(v, 'dest', False) cs.assert_called('POST', '/volumes/1234/action') + + def test_metadata_update_all(self): + cs.volumes.update_all_metadata(1234, {'k1': 'v1'}) + cs.assert_called('PUT', '/volumes/1234/metadata', + {'metadata': {'k1': 'v1'}}) diff --git a/cinderclient/v1/shell.py b/cinderclient/v1/shell.py index 820dc87e0..3a40dbf5f 100644 --- a/cinderclient/v1/shell.py +++ b/cinderclient/v1/shell.py @@ -1252,3 +1252,80 @@ def do_qos_get_association(cs, args): """Get all associations of specific qos specs.""" associations = cs.qos_specs.get_associations(args.qos_specs) _print_associations_list(associations) + + +@utils.arg('snapshot', + metavar='', + help='ID of the snapshot to update metadata on.') +@utils.arg('action', + metavar='', + choices=['set', 'unset'], + help="Actions: 'set' or 'unset'") +@utils.arg('metadata', + metavar='', + nargs='+', + default=[], + help='Metadata to set/unset (only key is necessary on unset)') +@utils.service_type('volume') +def do_snapshot_metadata(cs, args): + """Set or Delete metadata of a snapshot.""" + snapshot = _find_volume_snapshot(cs, args.snapshot) + metadata = _extract_metadata(args) + + if args.action == 'set': + metadata = snapshot.set_metadata(metadata) + utils.print_dict(metadata._info) + elif args.action == 'unset': + snapshot.delete_metadata(list(metadata.keys())) + + +@utils.arg('snapshot', metavar='', + help='ID of snapshot') +@utils.service_type('volume') +def do_snapshot_metadata_show(cs, args): + """Show metadata of given snapshot.""" + snapshot = _find_volume_snapshot(cs, args.snapshot) + utils.print_dict(snapshot._info['metadata'], 'Metadata-property') + + +@utils.arg('volume', metavar='', + help='ID of volume') +@utils.service_type('volume') +def do_metadata_show(cs, args): + """Show metadata of given volume.""" + volume = utils.find_volume(cs, args.volume) + utils.print_dict(volume._info['metadata'], 'Metadata-property') + + +@utils.arg('volume', + metavar='', + help='ID of the volume to update metadata on.') +@utils.arg('metadata', + metavar='', + nargs='+', + default=[], + help='Metadata entry/entries to update.') +@utils.service_type('volume') +def do_metadata_update_all(cs, args): + """Update all metadata of a volume.""" + volume = utils.find_volume(cs, args.volume) + metadata = _extract_metadata(args) + metadata = volume.update_all_metadata(metadata) + utils.print_dict(metadata) + + +@utils.arg('snapshot', + metavar='', + help='ID of the snapshot to update metadata on.') +@utils.arg('metadata', + metavar='', + nargs='+', + default=[], + help='Metadata entry/entries to update.') +@utils.service_type('volume') +def do_snapshot_metadata_update_all(cs, args): + """Update all metadata of a snapshot.""" + snapshot = _find_volume_snapshot(cs, args.snapshot) + metadata = _extract_metadata(args) + metadata = snapshot.update_all_metadata(metadata) + utils.print_dict(metadata) diff --git a/cinderclient/v1/volume_snapshots.py b/cinderclient/v1/volume_snapshots.py index ea0ed4377..461d663cd 100644 --- a/cinderclient/v1/volume_snapshots.py +++ b/cinderclient/v1/volume_snapshots.py @@ -57,6 +57,18 @@ class Snapshot(base.Resource): """Update the snapshot with the privided state.""" self.manager.reset_state(self, state) + def set_metadata(self, metadata): + """Set metadata of this snapshot.""" + return self.manager.set_metadata(self, metadata) + + def delete_metadata(self, keys): + """Delete metadata of this snapshot.""" + return self.manager.delete_metadata(self, keys) + + def update_all_metadata(self, metadata): + """Update_all metadata of this snapshot.""" + return self.manager.update_all_metadata(self, metadata) + class SnapshotManager(base.ManagerWithFind): """ @@ -158,3 +170,33 @@ class SnapshotManager(base.ManagerWithFind): def update_snapshot_status(self, snapshot, update_dict): return self._action('os-update_snapshot_status', base.getid(snapshot), update_dict) + + def set_metadata(self, snapshot, metadata): + """Update/Set a snapshots metadata. + + :param snapshot: The :class:`Snapshot`. + :param metadata: A list of keys to be set. + """ + body = {'metadata': metadata} + return self._create("/snapshots/%s/metadata" % base.getid(snapshot), + body, "metadata") + + def delete_metadata(self, snapshot, keys): + """Delete specified keys from snapshot metadata. + + :param snapshot: The :class:`Snapshot`. + :param keys: A list of keys to be removed. + """ + snapshot_id = base.getid(snapshot) + for k in keys: + self._delete("/snapshots/%s/metadata/%s" % (snapshot_id, k)) + + def update_all_metadata(self, snapshot, metadata): + """Update_all snapshot metadata. + + :param snapshot: The :class:`Snapshot`. + :param metadata: A list of keys to be updated. + """ + body = {'metadata': metadata} + return self._update("/snapshots/%s/metadata" % base.getid(snapshot), + body) diff --git a/cinderclient/v1/volumes.py b/cinderclient/v1/volumes.py index 6804d59bb..36758c7b0 100644 --- a/cinderclient/v1/volumes.py +++ b/cinderclient/v1/volumes.py @@ -123,6 +123,10 @@ class Volume(base.Resource): # self.manager.migrate_volume_completion(self, old_volume, # new_volume, error) + def update_all_metadata(self, metadata): + """Update all metadata of this volume.""" + return self.manager.update_all_metadata(self, metadata) + class VolumeManager(base.ManagerWithFind): """ @@ -331,7 +335,7 @@ class VolumeManager(base.ManagerWithFind): Delete specified keys from volumes metadata. :param volume: The :class:`Volume`. - :param metadata: A list of keys to be removed. + :param keys: A list of keys to be removed. """ for k in keys: self._delete("/volumes/%s/metadata/%s" % (base.getid(volume), k)) @@ -395,3 +399,13 @@ class VolumeManager(base.ManagerWithFind): return self._action('os-migrate_volume_completion', old_volume, {'new_volume': new_volume_id, 'error': error})[1] + + def update_all_metadata(self, volume, metadata): + """Update all metadata of a volume. + + :param volume: The :class:`Volume`. + :param metadata: A list of keys to be updated. + """ + body = {'metadata': metadata} + return self._update("/volumes/%s/metadata" % base.getid(volume), + body) diff --git a/cinderclient/v2/shell.py b/cinderclient/v2/shell.py index cd23ab44d..fdec4cca9 100644 --- a/cinderclient/v2/shell.py +++ b/cinderclient/v2/shell.py @@ -1331,3 +1331,80 @@ def do_qos_get_association(cs, args): """Get all associations of specific qos specs.""" associations = cs.qos_specs.get_associations(args.qos_specs) _print_associations_list(associations) + + +@utils.arg('snapshot', + metavar='', + help='ID of the snapshot to update metadata on.') +@utils.arg('action', + metavar='', + choices=['set', 'unset'], + help="Actions: 'set' or 'unset'") +@utils.arg('metadata', + metavar='', + nargs='+', + default=[], + help='Metadata to set/unset (only key is necessary on unset)') +@utils.service_type('volumev2') +def do_snapshot_metadata(cs, args): + """Set or Delete metadata of a snapshot.""" + snapshot = _find_volume_snapshot(cs, args.snapshot) + metadata = _extract_metadata(args) + + if args.action == 'set': + metadata = snapshot.set_metadata(metadata) + utils.print_dict(metadata._info) + elif args.action == 'unset': + snapshot.delete_metadata(list(metadata.keys())) + + +@utils.arg('snapshot', metavar='', + help='ID of snapshot') +@utils.service_type('volumev2') +def do_snapshot_metadata_show(cs, args): + """Show metadata of given snapshot.""" + snapshot = _find_volume_snapshot(cs, args.snapshot) + utils.print_dict(snapshot._info['metadata'], 'Metadata-property') + + +@utils.arg('volume', metavar='', + help='ID of volume') +@utils.service_type('volumev2') +def do_metadata_show(cs, args): + """Show metadata of given volume.""" + volume = utils.find_volume(cs, args.volume) + utils.print_dict(volume._info['metadata'], 'Metadata-property') + + +@utils.arg('volume', + metavar='', + help='ID of the volume to update metadata on.') +@utils.arg('metadata', + metavar='', + nargs='+', + default=[], + help='Metadata entry/entries to update.') +@utils.service_type('volumev2') +def do_metadata_update_all(cs, args): + """Update all metadata of a volume.""" + volume = utils.find_volume(cs, args.volume) + metadata = _extract_metadata(args) + metadata = volume.update_all_metadata(metadata) + utils.print_dict(metadata) + + +@utils.arg('snapshot', + metavar='', + help='ID of the snapshot to update metadata on.') +@utils.arg('metadata', + metavar='', + nargs='+', + default=[], + help='Metadata entry/entries to update') +@utils.service_type('volumev2') +def do_snapshot_metadata_update_all(cs, args): + """Update all metadata of a snapshot.""" + snapshot = _find_volume_snapshot(cs, args.snapshot) + metadata = _extract_metadata(args) + metadata = snapshot.update_all_metadata(metadata) + utils.print_dict(metadata) diff --git a/cinderclient/v2/volume_snapshots.py b/cinderclient/v2/volume_snapshots.py index a904f9553..7e561d903 100644 --- a/cinderclient/v2/volume_snapshots.py +++ b/cinderclient/v2/volume_snapshots.py @@ -49,6 +49,18 @@ class Snapshot(base.Resource): """Update the snapshot with the provided state.""" self.manager.reset_state(self, state) + def set_metadata(self, metadata): + """Set metadata of this snapshot.""" + return self.manager.set_metadata(self, metadata) + + def delete_metadata(self, keys): + """Delete metadata of this snapshot.""" + return self.manager.delete_metadata(self, keys) + + def update_all_metadata(self, metadata): + """Update_all metadata of this snapshot.""" + return self.manager.update_all_metadata(self, metadata) + class SnapshotManager(base.ManagerWithFind): """Manage :class:`Snapshot` resources.""" @@ -143,3 +155,33 @@ class SnapshotManager(base.ManagerWithFind): def update_snapshot_status(self, snapshot, update_dict): return self._action('os-update_snapshot_status', base.getid(snapshot), update_dict) + + def set_metadata(self, snapshot, metadata): + """Update/Set a snapshots metadata. + + :param snapshot: The :class:`Snapshot`. + :param metadata: A list of keys to be set. + """ + body = {'metadata': metadata} + return self._create("/snapshots/%s/metadata" % base.getid(snapshot), + body, "metadata") + + def delete_metadata(self, snapshot, keys): + """Delete specified keys from snapshot metadata. + + :param snapshot: The :class:`Snapshot`. + :param keys: A list of keys to be removed. + """ + snapshot_id = base.getid(snapshot) + for k in keys: + self._delete("/snapshots/%s/metadata/%s" % (snapshot_id, k)) + + def update_all_metadata(self, snapshot, metadata): + """Update_all snapshot metadata. + + :param snapshot: The :class:`Snapshot`. + :param metadata: A list of keys to be updated. + """ + body = {'metadata': metadata} + return self._update("/snapshots/%s/metadata" % base.getid(snapshot), + body) diff --git a/cinderclient/v2/volumes.py b/cinderclient/v2/volumes.py index e948cff3c..e6dae9704 100644 --- a/cinderclient/v2/volumes.py +++ b/cinderclient/v2/volumes.py @@ -121,6 +121,10 @@ class Volume(base.Resource): # self.manager.migrate_volume_completion(self, old_volume, # new_volume, error) + def update_all_metadata(self, metadata): + """Update all metadata of this volume.""" + return self.manager.update_all_metadata(self, metadata) + class VolumeManager(base.ManagerWithFind): """Manage :class:`Volume` resources.""" @@ -314,7 +318,7 @@ class VolumeManager(base.ManagerWithFind): """Delete specified keys from volumes metadata. :param volume: The :class:`Volume`. - :param metadata: A list of keys to be removed. + :param keys: A list of keys to be removed. """ for k in keys: self._delete("/volumes/%s/metadata/%s" % (base.getid(volume), k)) @@ -377,3 +381,13 @@ class VolumeManager(base.ManagerWithFind): return self._action('os-migrate_volume_completion', old_volume, {'new_volume': new_volume_id, 'error': error})[1] + + def update_all_metadata(self, volume, metadata): + """Update all metadata of a volume. + + :param volume: The :class:`Volume`. + :param metadata: A list of keys to be updated. + """ + body = {'metadata': metadata} + return self._update("/volumes/%s/metadata" % base.getid(volume), + body)