From a4feb32ac4e06b75bb7151e21d9663a198791065 Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Fri, 15 Sep 2023 01:34:07 +0530 Subject: [PATCH] Add volume manage support Add support for volume manage operation. Change-Id: Ic31e92a501721b5ecbf56d44188d9dad95cd1ac3 --- doc/source/user/proxies/block_storage_v3.rst | 1 + openstack/block_storage/v3/_proxy.py | 15 ++++- openstack/block_storage/v3/volume.py | 38 +++++++++++ .../unit/block_storage/v3/test_volume.py | 63 +++++++++++++++++++ ...anage-volume-support-a4fd90e3ff2fa0d0.yaml | 4 ++ 5 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-manage-volume-support-a4fd90e3ff2fa0d0.yaml diff --git a/doc/source/user/proxies/block_storage_v3.rst b/doc/source/user/proxies/block_storage_v3.rst index c7cc4b085..d8c101703 100644 --- a/doc/source/user/proxies/block_storage_v3.rst +++ b/doc/source/user/proxies/block_storage_v3.rst @@ -26,6 +26,7 @@ Volume Operations upload_volume_to_image, reserve_volume, unreserve_volume, begin_volume_detaching, abort_volume_detaching, init_volume_attachment, terminate_volume_attachment, + manage_volume, Backend Pools Operations ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/openstack/block_storage/v3/_proxy.py b/openstack/block_storage/v3/_proxy.py index 90d0008eb..15bf11080 100644 --- a/openstack/block_storage/v3/_proxy.py +++ b/openstack/block_storage/v3/_proxy.py @@ -806,6 +806,18 @@ class Proxy(_base_proxy.BaseBlockStorageProxy): volume = self._get_resource(_volume.Volume, volume) volume.detach(self, attachment, force, connector) + def manage_volume(self, **attrs): + """Creates a volume by using existing storage rather than + allocating new storage. + + :param dict attrs: Keyword arguments which will be used to create + a :class:`~openstack.block_storage.v3.volume.Volume`, + comprised of the properties on the Volume class. + :returns: The results of volume creation + :rtype: :class:`~openstack.block_storage.v3.volume.Volume` + """ + return _volume.Volume.manage(self, **attrs) + def unmanage_volume(self, volume): """Removes a volume from Block Storage management without removing the back-end storage object that is associated with it. @@ -813,7 +825,8 @@ class Proxy(_base_proxy.BaseBlockStorageProxy): :param volume: The value can be either the ID of a volume or a :class:`~openstack.block_storage.v3.volume.Volume` instance. - :returns: None""" + :returns: None + """ volume = self._get_resource(_volume.Volume, volume) volume.unmanage(self) diff --git a/openstack/block_storage/v3/volume.py b/openstack/block_storage/v3/volume.py index eb412c40e..0b6a69b48 100644 --- a/openstack/block_storage/v3/volume.py +++ b/openstack/block_storage/v3/volume.py @@ -179,6 +179,44 @@ class Volume(resource.Resource, metadata.MetadataMixin): self._action(session, body) + @classmethod + def manage( + cls, + session, + host, + ref, + name=None, + description=None, + volume_type=None, + availability_zone=None, + metadata=None, + bootable=False, + cluster=None, + ): + """Manage an existing volume.""" + url = '/manageable_volumes' + if not utils.supports_microversion(session, '3.8'): + url = '/os-volume-manage' + body = { + 'volume': { + 'host': host, + 'ref': ref, + 'name': name, + 'description': description, + 'volume_type': volume_type, + 'availability_zone': availability_zone, + 'metadata': metadata, + 'bootable': bootable, + } + } + if cluster is not None: + body['volume']['cluster'] = cluster + resp = session.post(url, json=body, microversion=cls._max_microversion) + exceptions.raise_from_response(resp) + volume = Volume() + volume._translate_response(resp) + return volume + def unmanage(self, session): """Unmanage volume""" body = {'os-unmanage': {}} diff --git a/openstack/tests/unit/block_storage/v3/test_volume.py b/openstack/tests/unit/block_storage/v3/test_volume.py index 7e162252c..27a9d6d99 100644 --- a/openstack/tests/unit/block_storage/v3/test_volume.py +++ b/openstack/tests/unit/block_storage/v3/test_volume.py @@ -31,6 +31,7 @@ IMAGE_METADATA = { u'size': '13167616', } +FAKE_HOST = "fake_host@fake_backend#fake_pool" VOLUME = { "status": "creating", "name": "my_volume", @@ -598,3 +599,65 @@ class TestVolumeActions(TestVolume): headers={}, params={}, ) + + @mock.patch( + 'openstack.utils.supports_microversion', + autospec=True, + return_value=True, + ) + def test_manage(self, mock_mv): + resp = mock.Mock() + resp.body = {'volume': copy.deepcopy(VOLUME)} + resp.json = mock.Mock(return_value=resp.body) + resp.headers = {} + resp.status_code = 202 + self.sess.post = mock.Mock(return_value=resp) + sot = volume.Volume.manage(self.sess, host=FAKE_HOST, ref=FAKE_ID) + self.assertIsNotNone(sot) + url = '/manageable_volumes' + body = { + 'volume': { + 'host': FAKE_HOST, + 'ref': FAKE_ID, + 'name': None, + 'description': None, + 'volume_type': None, + 'availability_zone': None, + 'metadata': None, + 'bootable': False, + } + } + self.sess.post.assert_called_with( + url, json=body, microversion=sot._max_microversion + ) + + @mock.patch( + 'openstack.utils.supports_microversion', + autospec=True, + return_value=False, + ) + def test_manage_pre_38(self, mock_mv): + resp = mock.Mock() + resp.body = {'volume': copy.deepcopy(VOLUME)} + resp.json = mock.Mock(return_value=resp.body) + resp.headers = {} + resp.status_code = 202 + self.sess.post = mock.Mock(return_value=resp) + sot = volume.Volume.manage(self.sess, host=FAKE_HOST, ref=FAKE_ID) + self.assertIsNotNone(sot) + url = '/os-volume-manage' + body = { + 'volume': { + 'host': FAKE_HOST, + 'ref': FAKE_ID, + 'name': None, + 'description': None, + 'volume_type': None, + 'availability_zone': None, + 'metadata': None, + 'bootable': False, + } + } + self.sess.post.assert_called_with( + url, json=body, microversion=sot._max_microversion + ) diff --git a/releasenotes/notes/add-manage-volume-support-a4fd90e3ff2fa0d0.yaml b/releasenotes/notes/add-manage-volume-support-a4fd90e3ff2fa0d0.yaml new file mode 100644 index 000000000..6d0a0aedf --- /dev/null +++ b/releasenotes/notes/add-manage-volume-support-a4fd90e3ff2fa0d0.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Added support for manage volume operation.