From b560b1777ca11f605eb794ece3d765de0f47519e Mon Sep 17 00:00:00 2001 From: Ankit Agrawal Date: Sun, 13 Dec 2015 23:43:16 -0800 Subject: [PATCH] Add request_ids attribute to resource objects Added request_ids attribute to resource object for all the volume, volume_types, volume_type_access and volume_snapshots APIs by updating following APIs: volumes: delete, update, force_delete, reset_state, extend, migrate_volume, retype, update_readonly_flag, manage, unmanage, promote, reenable, get_pools, initialize_connection, terminate_connection, get_encryption_metadata volume_types: delete volume_type_access: add_project_access remove_project_access volume_snapshots: delete and update Returning list with request_ids as attribute in case of 'delete_metadata' and 'delete_image_metadata' APIs. These changes are required to return 'request_id' from client to log request_id mappings of cross projects. For more details on how request_id will be returned to the caller, please refer to the approved blueprint [1] discussed with the cross-project team. [1] http://specs.openstack.org/openstack/openstack-specs/specs/return-request-id.html DocImpact 'request-ids' will be returned as an attribute with response object. User can access it using 'res.request_ids' where 'res' is a response object. Change-Id: Icc4565291220278a65e6910a840fba623b750cc4 Partial-Implements: blueprint return-request-id-to-caller --- .../tests/unit/fixture_data/snapshots.py | 33 +++-- cinderclient/tests/unit/v2/fakes.py | 2 +- .../tests/unit/v2/test_snapshot_actions.py | 20 ++- .../tests/unit/v2/test_type_access.py | 7 +- cinderclient/tests/unit/v2/test_types.py | 14 +- cinderclient/tests/unit/v2/test_volumes.py | 124 +++++++++++++----- cinderclient/v2/volume_snapshots.py | 21 ++- cinderclient/v2/volume_type_access.py | 8 +- cinderclient/v2/volume_types.py | 2 +- cinderclient/v2/volumes.py | 77 ++++++----- 10 files changed, 211 insertions(+), 97 deletions(-) diff --git a/cinderclient/tests/unit/fixture_data/snapshots.py b/cinderclient/tests/unit/fixture_data/snapshots.py index e9f77cad8..6fccf796c 100644 --- a/cinderclient/tests/unit/fixture_data/snapshots.py +++ b/cinderclient/tests/unit/fixture_data/snapshots.py @@ -15,6 +15,9 @@ import json from cinderclient.tests.unit.fixture_data import base +REQUEST_ID = 'req-test-request-id' + + def _stub_snapshot(**kwargs): snapshot = { "created_at": "2012-08-28T16:30:31.000000", @@ -37,8 +40,11 @@ class Fixture(base.Fixture): super(Fixture, self).setUp() snapshot_1234 = _stub_snapshot(id='1234') - self.requests.register_uri('GET', self.url('1234'), - json={'snapshot': snapshot_1234}) + self.requests.register_uri( + 'GET', self.url('1234'), + json={'snapshot': snapshot_1234}, + headers={'x-openstack-request-id': REQUEST_ID} + ) def action_1234(request, context): return '' @@ -53,13 +59,20 @@ class Fixture(base.Fixture): raise AssertionError("Unexpected action: %s" % action) return '' - self.requests.register_uri('POST', self.url('1234', 'action'), - text=action_1234, status_code=202) + self.requests.register_uri( + 'POST', self.url('1234', 'action'), + text=action_1234, status_code=202, + headers={'x-openstack-request-id': REQUEST_ID} + ) - self.requests.register_uri('GET', - self.url('detail?limit=2&marker=1234'), - status_code=200, json={'snapshots': []}) + self.requests.register_uri( + 'GET', self.url('detail?limit=2&marker=1234'), + status_code=200, json={'snapshots': []}, + headers={'x-openstack-request-id': REQUEST_ID} + ) - self.requests.register_uri('GET', - self.url('detail?sort=id'), - status_code=200, json={'snapshots': []}) + self.requests.register_uri( + 'GET', self.url('detail?sort=id'), + status_code=200, json={'snapshots': []}, + headers={'x-openstack-request-id': REQUEST_ID} + ) diff --git a/cinderclient/tests/unit/v2/fakes.py b/cinderclient/tests/unit/v2/fakes.py index a11eb7c06..97bac04be 100644 --- a/cinderclient/tests/unit/v2/fakes.py +++ b/cinderclient/tests/unit/v2/fakes.py @@ -433,7 +433,7 @@ class FakeHTTPClient(base_client.HTTPClient): assert body[action] is None elif action == 'os-initialize_connection': assert list(body[action]) == ['connector'] - return (202, {}, {'connection_info': 'foos'}) + return (202, {}, {'connection_info': {'foos': 'bars'}}) elif action == 'os-terminate_connection': assert list(body[action]) == ['connector'] elif action == 'os-begin_detaching': diff --git a/cinderclient/tests/unit/v2/test_snapshot_actions.py b/cinderclient/tests/unit/v2/test_snapshot_actions.py index a8b226c1f..5fa3bd4f7 100644 --- a/cinderclient/tests/unit/v2/test_snapshot_actions.py +++ b/cinderclient/tests/unit/v2/test_snapshot_actions.py @@ -24,27 +24,35 @@ class SnapshotActionsTest(utils.FixturedTestCase): data_fixture_class = snapshots.Fixture def test_update_snapshot_status(self): - s = self.cs.volume_snapshots.get('1234') + snap = self.cs.volume_snapshots.get('1234') + self._assert_request_id(snap) stat = {'status': 'available'} - self.cs.volume_snapshots.update_snapshot_status(s, stat) + stats = self.cs.volume_snapshots.update_snapshot_status(snap, stat) self.assert_called('POST', '/snapshots/1234/action') + self._assert_request_id(stats) def test_update_snapshot_status_with_progress(self): s = self.cs.volume_snapshots.get('1234') + self._assert_request_id(s) stat = {'status': 'available', 'progress': '73%'} - self.cs.volume_snapshots.update_snapshot_status(s, stat) + stats = self.cs.volume_snapshots.update_snapshot_status(s, stat) self.assert_called('POST', '/snapshots/1234/action') + self._assert_request_id(stats) def test_list_snapshots_with_marker_limit(self): - self.cs.volume_snapshots.list(marker=1234, limit=2) + lst = self.cs.volume_snapshots.list(marker=1234, limit=2) self.assert_called('GET', '/snapshots/detail?limit=2&marker=1234') + self._assert_request_id(lst) def test_list_snapshots_with_sort(self): - self.cs.volume_snapshots.list(sort="id") + lst = self.cs.volume_snapshots.list(sort="id") self.assert_called('GET', '/snapshots/detail?sort=id') + self._assert_request_id(lst) def test_snapshot_unmanage(self): s = self.cs.volume_snapshots.get('1234') - self.cs.volume_snapshots.unmanage(s) + self._assert_request_id(s) + snap = self.cs.volume_snapshots.unmanage(s) self.assert_called('POST', '/snapshots/1234/action', {'os-unmanage': None}) + self._assert_request_id(snap) diff --git a/cinderclient/tests/unit/v2/test_type_access.py b/cinderclient/tests/unit/v2/test_type_access.py index 7e41b3ee3..1df9e7212 100644 --- a/cinderclient/tests/unit/v2/test_type_access.py +++ b/cinderclient/tests/unit/v2/test_type_access.py @@ -28,15 +28,18 @@ class TypeAccessTest(utils.TestCase): def test_list(self): access = cs.volume_type_access.list(volume_type='3') cs.assert_called('GET', '/types/3/os-volume-type-access') + self._assert_request_id(access) for a in access: self.assertTrue(isinstance(a, volume_type_access.VolumeTypeAccess)) def test_add_project_access(self): - cs.volume_type_access.add_project_access('3', PROJECT_UUID) + access = cs.volume_type_access.add_project_access('3', PROJECT_UUID) cs.assert_called('POST', '/types/3/action', {'addProjectAccess': {'project': PROJECT_UUID}}) + self._assert_request_id(access) def test_remove_project_access(self): - cs.volume_type_access.remove_project_access('3', PROJECT_UUID) + access = cs.volume_type_access.remove_project_access('3', PROJECT_UUID) cs.assert_called('POST', '/types/3/action', {'removeProjectAccess': {'project': PROJECT_UUID}}) + self._assert_request_id(access) diff --git a/cinderclient/tests/unit/v2/test_types.py b/cinderclient/tests/unit/v2/test_types.py index 2a24caf34..b12611174 100644 --- a/cinderclient/tests/unit/v2/test_types.py +++ b/cinderclient/tests/unit/v2/test_types.py @@ -26,12 +26,14 @@ class TypesTest(utils.TestCase): def test_list_types(self): tl = cs.volume_types.list() cs.assert_called('GET', '/types?is_public=None') + self._assert_request_id(tl) for t in tl: self.assertIsInstance(t, volume_types.VolumeType) def test_list_types_not_public(self): - cs.volume_types.list(is_public=None) + t1 = cs.volume_types.list(is_public=None) cs.assert_called('GET', '/types?is_public=None') + self._assert_request_id(t1) def test_create(self): t = cs.volume_types.create('test-type-3', 'test-type-3-desc') @@ -42,6 +44,7 @@ class TypesTest(utils.TestCase): 'os-volume-type-access:is_public': True }}) self.assertIsInstance(t, volume_types.VolumeType) + self._assert_request_id(t) def test_create_non_public(self): t = cs.volume_types.create('test-type-3', 'test-type-3-desc', False) @@ -52,6 +55,7 @@ class TypesTest(utils.TestCase): 'os-volume-type-access:is_public': False }}) self.assertIsInstance(t, volume_types.VolumeType) + self._assert_request_id(t) def test_update(self): t = cs.volume_types.update('1', 'test_type_1', 'test_desc_1', False) @@ -61,16 +65,19 @@ class TypesTest(utils.TestCase): 'description': 'test_desc_1', 'is_public': False}}) self.assertIsInstance(t, volume_types.VolumeType) + self._assert_request_id(t) def test_get(self): t = cs.volume_types.get('1') cs.assert_called('GET', '/types/1') self.assertIsInstance(t, volume_types.VolumeType) + self._assert_request_id(t) def test_default(self): t = cs.volume_types.default() cs.assert_called('GET', '/types/default') self.assertIsInstance(t, volume_types.VolumeType) + self._assert_request_id(t) def test_set_key(self): t = cs.volume_types.get(1) @@ -78,12 +85,15 @@ class TypesTest(utils.TestCase): cs.assert_called('POST', '/types/1/extra_specs', {'extra_specs': {'k': 'v'}}) + self._assert_request_id(t) def test_unsset_keys(self): t = cs.volume_types.get(1) t.unset_keys(['k']) cs.assert_called('DELETE', '/types/1/extra_specs/k') + self._assert_request_id(t) def test_delete(self): - cs.volume_types.delete(1) + t = cs.volume_types.delete(1) cs.assert_called('DELETE', '/types/1') + self._assert_request_id(t) diff --git a/cinderclient/tests/unit/v2/test_volumes.py b/cinderclient/tests/unit/v2/test_volumes.py index 3c59bf065..11fc40994 100644 --- a/cinderclient/tests/unit/v2/test_volumes.py +++ b/cinderclient/tests/unit/v2/test_volumes.py @@ -24,12 +24,14 @@ cs = fakes.FakeClient() class VolumesTest(utils.TestCase): def test_list_volumes_with_marker_limit(self): - cs.volumes.list(marker=1234, limit=2) + lst = cs.volumes.list(marker=1234, limit=2) cs.assert_called('GET', '/volumes/detail?limit=2&marker=1234') + self._assert_request_id(lst) def test_list_volumes_with_sort_key_dir(self): - cs.volumes.list(sort_key='id', sort_dir='asc') + lst = cs.volumes.list(sort_key='id', sort_dir='asc') cs.assert_called('GET', '/volumes/detail?sort_dir=asc&sort_key=id') + self._assert_request_id(lst) def test_list_volumes_with_invalid_sort_key(self): self.assertRaises(ValueError, @@ -54,6 +56,7 @@ class VolumesTest(utils.TestCase): # osapi_max_limit is 1000 by default. If limit is less than # osapi_max_limit, we can get 2 volumes back. volumes = cs.volumes._list(url, response_key, limit=limit) + self._assert_request_id(volumes) cs.assert_called('GET', url) self.assertEqual(fake_volumes, volumes) @@ -64,23 +67,28 @@ class VolumesTest(utils.TestCase): cs.client.osapi_max_limit = 1 volumes = cs.volumes._list(url, response_key, limit=limit) self.assertEqual(fake_volumes, volumes) + self._assert_request_id(volumes) cs.client.osapi_max_limit = 1000 def test_delete_volume(self): v = cs.volumes.list()[0] - v.delete() + del_v = v.delete() cs.assert_called('DELETE', '/volumes/1234') - cs.volumes.delete('1234') + self._assert_request_id(del_v) + del_v = cs.volumes.delete('1234') cs.assert_called('DELETE', '/volumes/1234') - cs.volumes.delete(v) + self._assert_request_id(del_v) + del_v = cs.volumes.delete(v) cs.assert_called('DELETE', '/volumes/1234') + self._assert_request_id(del_v) def test_create_volume(self): - cs.volumes.create(1) + vol = cs.volumes.create(1) cs.assert_called('POST', '/volumes') + self._assert_request_id(vol) def test_create_volume_with_hint(self): - cs.volumes.create(1, scheduler_hints='uuid') + vol = cs.volumes.create(1, scheduler_hints='uuid') expected = {'volume': {'status': 'creating', 'description': None, 'availability_zone': None, @@ -99,31 +107,42 @@ class VolumesTest(utils.TestCase): 'multiattach': False}, 'OS-SCH-HNT:scheduler_hints': 'uuid'} cs.assert_called('POST', '/volumes', body=expected) + self._assert_request_id(vol) def test_attach(self): v = cs.volumes.get('1234') - cs.volumes.attach(v, 1, '/dev/vdc', mode='ro') + self._assert_request_id(v) + vol = cs.volumes.attach(v, 1, '/dev/vdc', mode='ro') cs.assert_called('POST', '/volumes/1234/action') + self._assert_request_id(vol) def test_attach_to_host(self): v = cs.volumes.get('1234') - cs.volumes.attach(v, None, None, host_name='test', mode='rw') + self._assert_request_id(v) + vol = cs.volumes.attach(v, None, None, host_name='test', mode='rw') cs.assert_called('POST', '/volumes/1234/action') + self._assert_request_id(vol) def test_detach(self): v = cs.volumes.get('1234') - cs.volumes.detach(v, 'abc123') + self._assert_request_id(v) + vol = cs.volumes.detach(v, 'abc123') cs.assert_called('POST', '/volumes/1234/action') + self._assert_request_id(vol) def test_reserve(self): v = cs.volumes.get('1234') - cs.volumes.reserve(v) + self._assert_request_id(v) + vol = cs.volumes.reserve(v) cs.assert_called('POST', '/volumes/1234/action') + self._assert_request_id(vol) def test_unreserve(self): v = cs.volumes.get('1234') - cs.volumes.unreserve(v) + self._assert_request_id(v) + vol = cs.volumes.unreserve(v) cs.assert_called('POST', '/volumes/1234/action') + self._assert_request_id(vol) def test_begin_detaching(self): v = cs.volumes.get('1234') @@ -132,126 +151,161 @@ class VolumesTest(utils.TestCase): def test_roll_detaching(self): v = cs.volumes.get('1234') - cs.volumes.roll_detaching(v) + self._assert_request_id(v) + vol = cs.volumes.roll_detaching(v) cs.assert_called('POST', '/volumes/1234/action') + self._assert_request_id(vol) def test_initialize_connection(self): v = cs.volumes.get('1234') - cs.volumes.initialize_connection(v, {}) + self._assert_request_id(v) + vol = cs.volumes.initialize_connection(v, {}) cs.assert_called('POST', '/volumes/1234/action') + self._assert_request_id(vol) def test_terminate_connection(self): v = cs.volumes.get('1234') - cs.volumes.terminate_connection(v, {}) + self._assert_request_id(v) + vol = cs.volumes.terminate_connection(v, {}) cs.assert_called('POST', '/volumes/1234/action') + self._assert_request_id(vol) def test_set_metadata(self): - cs.volumes.set_metadata(1234, {'k1': 'v2'}) + vol = cs.volumes.set_metadata(1234, {'k1': 'v2'}) cs.assert_called('POST', '/volumes/1234/metadata', {'metadata': {'k1': 'v2'}}) + self._assert_request_id(vol) def test_delete_metadata(self): keys = ['key1'] - cs.volumes.delete_metadata(1234, keys) + vol = cs.volumes.delete_metadata(1234, keys) cs.assert_called('DELETE', '/volumes/1234/metadata/key1') + self._assert_request_id(vol) def test_extend(self): v = cs.volumes.get('1234') - cs.volumes.extend(v, 2) + self._assert_request_id(v) + vol = cs.volumes.extend(v, 2) cs.assert_called('POST', '/volumes/1234/action') + self._assert_request_id(vol) def test_reset_state(self): v = cs.volumes.get('1234') - cs.volumes.reset_state(v, 'in-use', attach_status='detached', - migration_status='none') + self._assert_request_id(v) + vol = cs.volumes.reset_state(v, 'in-use', attach_status='detached', + migration_status='none') cs.assert_called('POST', '/volumes/1234/action') + self._assert_request_id(vol) def test_get_encryption_metadata(self): - cs.volumes.get_encryption_metadata('1234') + vol = cs.volumes.get_encryption_metadata('1234') cs.assert_called('GET', '/volumes/1234/encryption') + self._assert_request_id(vol) def test_migrate(self): v = cs.volumes.get('1234') - cs.volumes.migrate_volume(v, 'dest', False, False) + self._assert_request_id(v) + vol = cs.volumes.migrate_volume(v, 'dest', False, False) cs.assert_called('POST', '/volumes/1234/action', {'os-migrate_volume': {'host': 'dest', 'force_host_copy': False, 'lock_volume': False}}) + self._assert_request_id(vol) def test_migrate_with_lock_volume(self): v = cs.volumes.get('1234') - cs.volumes.migrate_volume(v, 'dest', False, True) + self._assert_request_id(v) + vol = cs.volumes.migrate_volume(v, 'dest', False, True) cs.assert_called('POST', '/volumes/1234/action', {'os-migrate_volume': {'host': 'dest', 'force_host_copy': False, 'lock_volume': True}}) + self._assert_request_id(vol) def test_metadata_update_all(self): - cs.volumes.update_all_metadata(1234, {'k1': 'v1'}) + vol = cs.volumes.update_all_metadata(1234, {'k1': 'v1'}) cs.assert_called('PUT', '/volumes/1234/metadata', {'metadata': {'k1': 'v1'}}) + self._assert_request_id(vol) def test_readonly_mode_update(self): v = cs.volumes.get('1234') - cs.volumes.update_readonly_flag(v, True) + self._assert_request_id(v) + vol = cs.volumes.update_readonly_flag(v, True) cs.assert_called('POST', '/volumes/1234/action') + self._assert_request_id(vol) def test_retype(self): v = cs.volumes.get('1234') - cs.volumes.retype(v, 'foo', 'on-demand') + self._assert_request_id(v) + vol = cs.volumes.retype(v, 'foo', 'on-demand') cs.assert_called('POST', '/volumes/1234/action', {'os-retype': {'new_type': 'foo', 'migration_policy': 'on-demand'}}) + self._assert_request_id(vol) def test_set_bootable(self): v = cs.volumes.get('1234') - cs.volumes.set_bootable(v, True) + self._assert_request_id(v) + vol = cs.volumes.set_bootable(v, True) cs.assert_called('POST', '/volumes/1234/action') + self._assert_request_id(vol) def test_volume_manage(self): - cs.volumes.manage('host1', {'k': 'v'}) + vol = cs.volumes.manage('host1', {'k': 'v'}) expected = {'host': 'host1', 'name': None, 'availability_zone': None, 'description': None, 'metadata': None, 'ref': {'k': 'v'}, 'volume_type': None, 'bootable': False} cs.assert_called('POST', '/os-volume-manage', {'volume': expected}) + self._assert_request_id(vol) def test_volume_manage_bootable(self): - cs.volumes.manage('host1', {'k': 'v'}, bootable=True) + vol = cs.volumes.manage('host1', {'k': 'v'}, bootable=True) expected = {'host': 'host1', 'name': None, 'availability_zone': None, 'description': None, 'metadata': None, 'ref': {'k': 'v'}, 'volume_type': None, 'bootable': True} cs.assert_called('POST', '/os-volume-manage', {'volume': expected}) + self._assert_request_id(vol) def test_volume_unmanage(self): v = cs.volumes.get('1234') - cs.volumes.unmanage(v) + self._assert_request_id(v) + vol = cs.volumes.unmanage(v) cs.assert_called('POST', '/volumes/1234/action', {'os-unmanage': None}) + self._assert_request_id(vol) def test_snapshot_manage(self): - cs.volume_snapshots.manage('volume_id1', {'k': 'v'}) + vol = 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}) + self._assert_request_id(vol) def test_replication_promote(self): v = cs.volumes.get('1234') - cs.volumes.promote(v) + self._assert_request_id(v) + vol = cs.volumes.promote(v) cs.assert_called('POST', '/volumes/1234/action', {'os-promote-replica': None}) + self._assert_request_id(vol) def test_replication_reenable(self): v = cs.volumes.get('1234') - cs.volumes.reenable(v) + self._assert_request_id(v) + vol = cs.volumes.reenable(v) cs.assert_called('POST', '/volumes/1234/action', {'os-reenable-replica': None}) + self._assert_request_id(vol) def test_get_pools(self): - cs.volumes.get_pools('') + vol = cs.volumes.get_pools('') cs.assert_called('GET', '/scheduler-stats/get_pools') + self._assert_request_id(vol) def test_get_pools_detail(self): - cs.volumes.get_pools('--detail') + vol = cs.volumes.get_pools('--detail') cs.assert_called('GET', '/scheduler-stats/get_pools?detail=True') + self._assert_request_id(vol) class FormatSortParamTestCase(utils.TestCase): diff --git a/cinderclient/v2/volume_snapshots.py b/cinderclient/v2/volume_snapshots.py index 1d5ca408e..46ae2bb03 100644 --- a/cinderclient/v2/volume_snapshots.py +++ b/cinderclient/v2/volume_snapshots.py @@ -16,6 +16,7 @@ """Volume snapshot interface (1.1 extension).""" from cinderclient import base +from cinderclient.openstack.common.apiclient import base as common_base class Snapshot(base.Resource): @@ -26,11 +27,11 @@ class Snapshot(base.Resource): def delete(self): """Delete this snapshot.""" - self.manager.delete(self) + return self.manager.delete(self) def update(self, **kwargs): """Update the name or description for this snapshot.""" - self.manager.update(self, **kwargs) + return self.manager.update(self, **kwargs) @property def progress(self): @@ -42,7 +43,7 @@ class Snapshot(base.Resource): def reset_state(self, state): """Update the snapshot with the provided state.""" - self.manager.reset_state(self, state) + return self.manager.reset_state(self, state) def set_metadata(self, metadata): """Set metadata of this snapshot.""" @@ -122,7 +123,7 @@ class SnapshotManager(base.ManagerWithFind): :param snapshot: The :class:`Snapshot` to delete. """ - self._delete("/snapshots/%s" % base.getid(snapshot)) + return self._delete("/snapshots/%s" % base.getid(snapshot)) def update(self, snapshot, **kwargs): """Update the name or description for a snapshot. @@ -134,7 +135,7 @@ class SnapshotManager(base.ManagerWithFind): body = {"snapshot": kwargs} - self._update("/snapshots/%s" % base.getid(snapshot), body) + return self._update("/snapshots/%s" % base.getid(snapshot), body) def reset_state(self, snapshot, state): """Update the specified snapshot with the provided state.""" @@ -145,7 +146,8 @@ class SnapshotManager(base.ManagerWithFind): body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) url = '/snapshots/%s/action' % base.getid(snapshot) - return self.api.client.post(url, body=body) + resp, body = self.api.client.post(url, body=body) + return common_base.TupleWithMeta((resp, body), resp) def update_snapshot_status(self, snapshot, update_dict): return self._action('os-update_snapshot_status', @@ -167,9 +169,14 @@ class SnapshotManager(base.ManagerWithFind): :param snapshot: The :class:`Snapshot`. :param keys: A list of keys to be removed. """ + response_list = [] snapshot_id = base.getid(snapshot) for k in keys: - self._delete("/snapshots/%s/metadata/%s" % (snapshot_id, k)) + resp, body = self._delete("/snapshots/%s/metadata/%s" % + (snapshot_id, k)) + response_list.append(resp) + + return common_base.ListWithMeta([], response_list) def update_all_metadata(self, snapshot, metadata): """Update_all snapshot metadata. diff --git a/cinderclient/v2/volume_type_access.py b/cinderclient/v2/volume_type_access.py index d604041a5..abdfa8658 100644 --- a/cinderclient/v2/volume_type_access.py +++ b/cinderclient/v2/volume_type_access.py @@ -15,6 +15,7 @@ """Volume type access interface.""" from cinderclient import base +from cinderclient.openstack.common.apiclient import base as common_base class VolumeTypeAccess(base.Resource): @@ -36,16 +37,17 @@ class VolumeTypeAccessManager(base.ManagerWithFind): def add_project_access(self, volume_type, project): """Add a project to the given volume type access list.""" info = {'project': project} - self._action('addProjectAccess', volume_type, info) + return self._action('addProjectAccess', volume_type, info) def remove_project_access(self, volume_type, project): """Remove a project from the given volume type access list.""" info = {'project': project} - self._action('removeProjectAccess', volume_type, info) + return self._action('removeProjectAccess', volume_type, info) def _action(self, action, volume_type, info, **kwargs): """Perform a volume type action.""" body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) url = '/types/%s/action' % base.getid(volume_type) - return self.api.client.post(url, body=body) + resp, body = self.api.client.post(url, body=body) + return common_base.TupleWithMeta((resp, body), resp) diff --git a/cinderclient/v2/volume_types.py b/cinderclient/v2/volume_types.py index e9936e567..251e75b9f 100644 --- a/cinderclient/v2/volume_types.py +++ b/cinderclient/v2/volume_types.py @@ -108,7 +108,7 @@ class VolumeTypeManager(base.ManagerWithFind): :param volume_type: The name or ID of the :class:`VolumeType` to get. """ - self._delete("/types/%s" % base.getid(volume_type)) + return self._delete("/types/%s" % base.getid(volume_type)) def create(self, name, description=None, is_public=True): """Creates a volume type. diff --git a/cinderclient/v2/volumes.py b/cinderclient/v2/volumes.py index 19bec81cb..e3c11ab52 100644 --- a/cinderclient/v2/volumes.py +++ b/cinderclient/v2/volumes.py @@ -16,6 +16,7 @@ """Volume interface (v2 extension).""" from cinderclient import base +from cinderclient.openstack.common.apiclient import base as common_base class Volume(base.Resource): @@ -25,11 +26,11 @@ class Volume(base.Resource): def delete(self): """Delete this volume.""" - self.manager.delete(self) + return self.manager.delete(self) def update(self, **kwargs): """Update the name or description for this volume.""" - self.manager.update(self, **kwargs) + return self.manager.update(self, **kwargs) def attach(self, instance_uuid, mountpoint, mode='rw', host_name=None): """Set attachment metadata. @@ -119,7 +120,7 @@ class Volume(base.Resource): :param volume: The UUID of the volume to force-delete. """ - self.manager.force_delete(self) + return self.manager.force_delete(self) def reset_state(self, state, attach_status=None, migration_status=None): """Update the volume with the provided state. @@ -130,7 +131,8 @@ class Volume(base.Resource): :param migration_status: The migration_status of the volume to be set, or None to keep the current status. """ - self.manager.reset_state(self, state, attach_status, migration_status) + return self.manager.reset_state(self, state, attach_status, + migration_status) def extend(self, volume, new_size): """Extend the size of the specified volume. @@ -138,11 +140,12 @@ class Volume(base.Resource): :param volume: The UUID of the volume to extend :param new_size: The desired size to extend volume to. """ - self.manager.extend(self, new_size) + return self.manager.extend(self, new_size) def migrate_volume(self, host, force_host_copy, lock_volume): """Migrate the volume to a new host.""" - self.manager.migrate_volume(self, host, force_host_copy, lock_volume) + return self.manager.migrate_volume(self, host, force_host_copy, + lock_volume) def replication_enable(self, volume): """Enables volume replication on a given volume.""" @@ -162,7 +165,7 @@ class Volume(base.Resource): def retype(self, volume_type, policy): """Change a volume's type.""" - self.manager.retype(self, volume_type, policy) + return self.manager.retype(self, volume_type, policy) def update_all_metadata(self, metadata): """Update all metadata of this volume.""" @@ -175,32 +178,33 @@ class Volume(base.Resource): :param read_only: The value to indicate whether to update volume to read-only access mode. """ - self.manager.update_readonly_flag(self, read_only) + return self.manager.update_readonly_flag(self, read_only) def manage(self, host, ref, name=None, description=None, volume_type=None, availability_zone=None, metadata=None, bootable=False): """Manage an existing volume.""" - self.manager.manage(host=host, ref=ref, name=name, - description=description, volume_type=volume_type, - availability_zone=availability_zone, - metadata=metadata, bootable=bootable) + return self.manager.manage(host=host, ref=ref, name=name, + description=description, + volume_type=volume_type, + availability_zone=availability_zone, + metadata=metadata, bootable=bootable) def unmanage(self, volume): """Unmanage a volume.""" - self.manager.unmanage(volume) + return self.manager.unmanage(volume) def promote(self, volume): """Promote secondary to be primary in relationship.""" - self.manager.promote(volume) + return self.manager.promote(volume) def reenable(self, volume): """Sync the secondary volume with primary for a relationship.""" - self.manager.reenable(volume) + return self.manager.reenable(volume) def get_pools(self, detail): """Show pool information for backends.""" - self.manager.get_pools(detail) + return self.manager.get_pools(detail) class VolumeManager(base.ManagerWithFind): @@ -298,7 +302,7 @@ class VolumeManager(base.ManagerWithFind): :param volume: The :class:`Volume` to delete. """ - self._delete("/volumes/%s" % base.getid(volume)) + return self._delete("/volumes/%s" % base.getid(volume)) def update(self, volume, **kwargs): """Update the name or description for a volume. @@ -310,7 +314,7 @@ class VolumeManager(base.ManagerWithFind): body = {"volume": kwargs} - self._update("/volumes/%s" % base.getid(volume), body) + return self._update("/volumes/%s" % base.getid(volume), body) def _action(self, action, volume, info=None, **kwargs): """Perform a volume "action." @@ -318,7 +322,8 @@ class VolumeManager(base.ManagerWithFind): body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) url = '/volumes/%s/action' % base.getid(volume) - return self.api.client.post(url, body=body) + resp, body = self.api.client.post(url, body=body) + return common_base.TupleWithMeta((resp, body), resp) def attach(self, volume, instance_uuid, mountpoint, mode='rw', host_name=None): @@ -386,8 +391,9 @@ class VolumeManager(base.ManagerWithFind): :param volume: The :class:`Volume` (or its ID). :param connector: connector dict from nova. """ - return self._action('os-initialize_connection', volume, - {'connector': connector})[1]['connection_info'] + resp, body = self._action('os-initialize_connection', volume, + {'connector': connector}) + return common_base.DictWithMeta(body['connection_info'], resp) def terminate_connection(self, volume, connector): """Terminate a volume connection. @@ -395,8 +401,8 @@ class VolumeManager(base.ManagerWithFind): :param volume: The :class:`Volume` (or its ID). :param connector: connector dict from nova. """ - self._action('os-terminate_connection', volume, - {'connector': connector}) + return self._action('os-terminate_connection', volume, + {'connector': connector}) def set_metadata(self, volume, metadata): """Update/Set a volumes metadata. @@ -414,8 +420,13 @@ class VolumeManager(base.ManagerWithFind): :param volume: The :class:`Volume`. :param keys: A list of keys to be removed. """ + response_list = [] for k in keys: - self._delete("/volumes/%s/metadata/%s" % (base.getid(volume), k)) + resp, body = self._delete("/volumes/%s/metadata/%s" % + (base.getid(volume), k)) + response_list.append(resp) + + return common_base.ListWithMeta([], response_list) def set_image_metadata(self, volume, metadata): """Set a volume's image metadata. @@ -433,9 +444,13 @@ class VolumeManager(base.ManagerWithFind): :param volume: The :class:`Volume`. :param keys: A list of keys to be removed. """ + response_list = [] for key in keys: - self._action("os-unset_image_metadata", volume, - {'key': key}) + resp, body = self._action("os-unset_image_metadata", volume, + {'key': key}) + response_list.append(resp) + + return common_base.ListWithMeta([], response_list) def show_image_metadata(self, volume): """Show a volume's image metadata. @@ -500,7 +515,8 @@ class VolumeManager(base.ManagerWithFind): :param volume_id: the id of the volume to query :return: a dictionary of volume encryption metadata """ - return self._get("/volumes/%s/encryption" % volume_id)._info + metadata = self._get("/volumes/%s/encryption" % volume_id) + return common_base.DictWithMeta(metadata._info, metadata.request_ids) def migrate_volume(self, volume, host, force_host_copy, lock_volume): """Migrate volume to new host. @@ -524,9 +540,10 @@ class VolumeManager(base.ManagerWithFind): :param error: Inform of an error to cause migration cleanup """ new_volume_id = base.getid(new_volume) - return self._action('os-migrate_volume_completion', - old_volume, - {'new_volume': new_volume_id, 'error': error})[1] + resp, body = self._action('os-migrate_volume_completion', old_volume, + {'new_volume': new_volume_id, + 'error': error}) + return common_base.DictWithMeta(body, resp) def replication_enable(self, volume_id): """