From 7b5356f32de2a0cd9bde411c008ec612df5c0f99 Mon Sep 17 00:00:00 2001 From: Anvi Joshi Date: Wed, 12 Jul 2023 21:38:51 +0000 Subject: [PATCH] Support manage/unmanage shares with manila Change-Id: I95b72434a8a44f737f5ee9a44b6ecbf8f4162a7a --- doc/source/user/guides/shared_file_system.rst | 18 ++++++ .../user/proxies/shared_file_system.rst | 3 +- examples/shared_file_system/shares.py | 23 ++++++++ openstack/shared_file_system/v2/_proxy.py | 36 ++++++++++++ openstack/shared_file_system/v2/share.py | 51 +++++++++++++++++ .../shared_file_system/test_share.py | 57 +++++++++++++++++++ .../unit/shared_file_system/v2/test_share.py | 47 +++++++++++++++ ...anage-unmanage-share-830e313f96e5fd2b.yaml | 5 ++ 8 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-shared-file-system-manage-unmanage-share-830e313f96e5fd2b.yaml diff --git a/doc/source/user/guides/shared_file_system.rst b/doc/source/user/guides/shared_file_system.rst index a2e300869..dc6b5d021 100644 --- a/doc/source/user/guides/shared_file_system.rst +++ b/doc/source/user/guides/shared_file_system.rst @@ -180,3 +180,21 @@ specify multiple keys to be deleted. .. literalinclude:: ../examples/shared_file_system/share_metadata.py :pyobject: delete_share_metadata + + +Manage Share +------------ + +Manage a share with Manila. + +.. literalinclude:: ../examples/shared_file_system/shares.py + :pyobject: manage_share + + +Unmanage Share +-------------- + +Unmanage a share from Manila. + +.. literalinclude:: ../examples/shared_file_system/shares.py + :pyobject: unmanage_share diff --git a/doc/source/user/proxies/shared_file_system.rst b/doc/source/user/proxies/shared_file_system.rst index 5cdaab1af..facbe03b2 100644 --- a/doc/source/user/proxies/shared_file_system.rst +++ b/doc/source/user/proxies/shared_file_system.rst @@ -33,7 +33,8 @@ service. .. autoclass:: openstack.shared_file_system.v2._proxy.Proxy :noindex: :members: shares, get_share, delete_share, update_share, create_share, - revert_share_to_snapshot, resize_share, find_share + revert_share_to_snapshot, resize_share, find_share, manage_share, + unmanage_share Shared File System Storage Pools diff --git a/examples/shared_file_system/shares.py b/examples/shared_file_system/shares.py index 9328b5048..5f274ed7d 100644 --- a/examples/shared_file_system/shares.py +++ b/examples/shared_file_system/shares.py @@ -31,3 +31,26 @@ def resize_shares_without_shrink(conn, min_size): # Extend shares smaller than min_size to min_size, # but don't shrink shares larger than min_size. conn.share.resize_share(share.id, min_size, no_shrink=True) + + +def manage_share(conn, protocol, export_path, service_host, **params): + # Manage a share with the given protocol, export path, service host, and + # optional additional parameters + managed_share = conn.share.manage_share( + protocol, export_path, service_host, **params + ) + + # Can get the ID of the share, which is now being managed with Manila + managed_share_id = managed_share.id + print("The ID of the share which was managed: %s", managed_share_id) + + +def unmanage_share(conn, share_id): + # Unmanage the share with the given share ID + conn.share.unmanage_share(share_id) + + try: + # Getting the share will raise an exception as it has been unmanaged + conn.share.get_share(share_id) + except Exception: + pass diff --git a/openstack/shared_file_system/v2/_proxy.py b/openstack/shared_file_system/v2/_proxy.py index af6f1e663..76eae7557 100644 --- a/openstack/shared_file_system/v2/_proxy.py +++ b/openstack/shared_file_system/v2/_proxy.py @@ -187,6 +187,42 @@ class Proxy(proxy.Proxy): res = self._get(_share.Share, share_id) res.revert_to_snapshot(self, snapshot_id) + def manage_share(self, protocol, export_path, service_host, **params): + """Manage a share. + + :param str protocol: The shared file systems protocol of this share. + :param str export_path: The export path formatted according to the + protocol. + :param str service_host: The manage-share service host. + :param kwargs params: Optional parameters to be sent. Available + parameters include: + * name: The user defined name of the resource. + * share_type: The name or ID of the share type to be used to create + the resource. + * driver_options: A set of one or more key and value pairs, as a + dictionary of strings, that describe driver options. + * is_public: The level of visibility for the share. + * description: The user defiend description of the resource. + * share_server_id: The UUID of the share server. + + :returns: The share that was managed. + """ + + share = _share.Share() + return share.manage( + self, protocol, export_path, service_host, **params + ) + + def unmanage_share(self, share_id): + """Unmanage the share with the given share ID. + + :param share_id: The ID of the share to unmanage. + :returns: ``None`` + """ + + share_to_unmanage = self._get(_share.Share, share_id) + share_to_unmanage.unmanage(self) + def resize_share( self, share_id, new_size, no_shrink=False, no_extend=False, force=False ): diff --git a/openstack/shared_file_system/v2/share.py b/openstack/shared_file_system/v2/share.py index ebe3adfb4..621ea729d 100644 --- a/openstack/shared_file_system/v2/share.py +++ b/openstack/shared_file_system/v2/share.py @@ -160,3 +160,54 @@ class Share(resource.Resource, metadata.MetadataMixin): """ body = {"revert": {"snapshot_id": snapshot_id}} self._action(session, body) + + def manage(self, session, protocol, export_path, service_host, **params): + """Manage a share. + + :param session: A session object used for sending request. + :param str protocol: The shared file systems protocol of this share. + :param str export_path: The export path formatted according to the + protocol. + :param str service_host: The manage-share service host. + :param kwargs params: Optional parameters to be sent. Available + parameters include: + + * name: The user defined name of the resource. + * share_type: The name or ID of the share type to be used to create + the resource. + * driver_options: A set of one or more key and value pairs, as a + dictionary of strings, that describe driver options. + * is_public: The level of visibility for the share. + * description: The user defiend description of the resource. + * share_server_id: The UUID of the share server. + + :returns: The share that was managed. + """ + + path = 'manage' + attrs = { + 'share': { + 'protocol': protocol, + 'export_path': export_path, + 'service_host': service_host, + } + } + + attrs['share'].update(params) + + url = utils.urljoin(self.base_path, path) + resp = session.post(url, json=attrs) + + self._translate_response(resp) + return self + + def unmanage(self, session): + """Unmanage a share. + + :param session: A session object used for sending request. + :returns: ``None`` + """ + + body = {'unmanage': None} + + self._action(session, body) diff --git a/openstack/tests/functional/shared_file_system/test_share.py b/openstack/tests/functional/shared_file_system/test_share.py index 8112b5ea7..c79c6978f 100644 --- a/openstack/tests/functional/shared_file_system/test_share.py +++ b/openstack/tests/functional/shared_file_system/test_share.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from openstack import exceptions from openstack.shared_file_system.v2 import share as _share from openstack.tests.functional.shared_file_system import base @@ -158,3 +159,59 @@ class ShareTest(base.BaseSharedFileSystemTest): wait=self._wait_for_timeout, ) self.assertEqual(larger_size, get_resized_share.size) + + +class ManageUnmanageShareTest(base.BaseSharedFileSystemTest): + def setUp(self): + super(ManageUnmanageShareTest, self).setUp() + + self.NEW_SHARE = self.create_share( + share_proto="NFS", + name="accounting_p8787", + size=2, + ) + self.SHARE_ID = self.NEW_SHARE.id + + self.export_locations = self.operator_cloud.share.export_locations( + self.SHARE_ID + ) + export_paths = [export['path'] for export in self.export_locations] + self.export_path = export_paths[0] + + self.share_host = self.operator_cloud.share.get_share(self.SHARE_ID)[ + 'host' + ] + + def test_manage_and_unmanage_share(self): + self.operator_cloud.share.unmanage_share(self.SHARE_ID) + + self.operator_cloud.shared_file_system.wait_for_delete( + self.NEW_SHARE, interval=2, wait=self._wait_for_timeout + ) + + try: + self.operator_cloud.share.get_share(self.SHARE_ID) + except exceptions.ResourceNotFound: + pass + + managed_share = self.operator_cloud.share.manage_share( + self.NEW_SHARE.share_protocol, self.export_path, self.share_host + ) + + self.operator_cloud.share.wait_for_status( + managed_share, + status='available', + failures=['error'], + interval=5, + wait=self._wait_for_timeout, + ) + + self.assertEqual( + self.NEW_SHARE.share_protocol, managed_share.share_protocol + ) + + managed_host = self.operator_cloud.share.get_share(managed_share.id)[ + 'host' + ] + + self.assertEqual(self.share_host, managed_host) diff --git a/openstack/tests/unit/shared_file_system/v2/test_share.py b/openstack/tests/unit/shared_file_system/v2/test_share.py index 3ddea2061..f45bc9c6c 100644 --- a/openstack/tests/unit/shared_file_system/v2/test_share.py +++ b/openstack/tests/unit/shared_file_system/v2/test_share.py @@ -183,3 +183,50 @@ class TestShareActions(TestShares): self.sess.post.assert_called_with( url, json=body, headers=headers, microversion=microversion ) + + def test_manage_share(self): + sot = share.Share() + + self.resp.headers = {} + self.resp.json = mock.Mock( + return_value={"share": {"name": "test_share", "size": 1}} + ) + + export_path = ( + "10.254.0 .5:/shares/share-42033c24-0261-424f-abda-4fef2f6dbfd5." + ) + params = {"name": "test_share"} + res = sot.manage( + self.sess, + sot["share_protocol"], + export_path, + sot["host"], + **params, + ) + + self.assertEqual(res.name, "test_share") + self.assertEqual(res.size, 1) + + jsonDict = { + "share": { + "protocol": sot["share_protocol"], + "export_path": export_path, + "service_host": sot["host"], + "name": "test_share", + } + } + + self.sess.post.assert_called_once_with("shares/manage", json=jsonDict) + + def test_unmanage_share(self): + sot = share.Share(**EXAMPLE) + microversion = sot._get_microversion(self.sess, action='patch') + + self.assertIsNone(sot.unmanage(self.sess)) + + url = 'shares/%s/action' % IDENTIFIER + body = {'unmanage': None} + + self.sess.post.assert_called_with( + url, json=body, headers={'Accept': ''}, microversion=microversion + ) diff --git a/releasenotes/notes/add-shared-file-system-manage-unmanage-share-830e313f96e5fd2b.yaml b/releasenotes/notes/add-shared-file-system-manage-unmanage-share-830e313f96e5fd2b.yaml new file mode 100644 index 000000000..d349d6c90 --- /dev/null +++ b/releasenotes/notes/add-shared-file-system-manage-unmanage-share-830e313f96e5fd2b.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Added support to manage and unmanage shares + from the shared file system service. \ No newline at end of file