diff --git a/doc/source/configuration/shared-file-systems/drivers/emc-isilon-driver.rst b/doc/source/configuration/shared-file-systems/drivers/emc-isilon-driver.rst index fb99936bd0..b103f3bac7 100644 --- a/doc/source/configuration/shared-file-systems/drivers/emc-isilon-driver.rst +++ b/doc/source/configuration/shared-file-systems/drivers/emc-isilon-driver.rst @@ -46,6 +46,8 @@ The following operations are supported: - Create a share from a snapshot. +- Ensure shares. + Back end configuration ~~~~~~~~~~~~~~~~~~~~~~ diff --git a/manila/share/drivers/dell_emc/driver.py b/manila/share/drivers/dell_emc/driver.py index 3d6aed437e..be0745892f 100644 --- a/manila/share/drivers/dell_emc/driver.py +++ b/manila/share/drivers/dell_emc/driver.py @@ -293,3 +293,15 @@ class EMCShareDriver(driver.ShareDriver): if hasattr(self.plugin, 'get_default_filter_function'): return self.plugin.get_default_filter_function() return None + + def get_backend_info(self, context): + """Get driver and array configuration parameters.""" + if hasattr(self.plugin, 'get_backend_info'): + return self.plugin.get_backend_info(context) + raise NotImplementedError() + + def ensure_shares(self, context, shares): + """Invoked to ensure that shares are exported.""" + if hasattr(self.plugin, 'ensure_shares'): + return self.plugin.ensure_shares(context, shares) + raise NotImplementedError() diff --git a/manila/share/drivers/dell_emc/plugins/isilon/isilon.py b/manila/share/drivers/dell_emc/plugins/isilon/isilon.py index 80dd1012ae..6367191cb1 100644 --- a/manila/share/drivers/dell_emc/plugins/isilon/isilon.py +++ b/manila/share/drivers/dell_emc/plugins/isilon/isilon.py @@ -32,8 +32,9 @@ from manila.share.drivers.dell_emc.plugins.isilon import isilon_api 0.1.0 - Initial version 1.0.0 - Fix Http auth issue, SSL verification error and etc 1.0.1 - Add support for update share stats + 1.0.2 - Add support for ensure shares """ -VERSION = "1.0.1" +VERSION = "1.0.2" CONF = cfg.CONF @@ -265,6 +266,7 @@ class IsilonStorageConnection(base.StorageConnection): def ensure_share(self, context, share, share_server): """Invoked to ensure that share is exported.""" + raise NotImplementedError() def extend_share(self, share, new_size, share_server=None): """Extends a share.""" @@ -504,6 +506,59 @@ class IsilonStorageConnection(base.StorageConnection): rule_state_map.update({rule['access_id']: {'state': 'error'}}) return cifs_user_permissions + def get_backend_info(self, context): + """Get driver and array configuration parameters. + + :returns: A dictionary containing driver-specific info. + """ + LOG.debug("Retrieving PowerScale backend info.") + cluster_version = self._isilon_api.get_cluster_version() + return {'driver_version': VERSION, + 'cluster_version': cluster_version, + 'rest_server': self._server, + 'rest_port': self._port} + + def ensure_shares(self, context, shares): + """Invoked to ensure that shares are exported. + + :shares: A list of all shares for updates. + :returns: None or a dictionary of updates in the format. + """ + LOG.debug("Ensuring PowerScale shares.") + updates = {} + for share in shares: + if share['share_proto'] == 'NFS': + container_path = self._get_container_path(share) + share_id = self._isilon_api.lookup_nfs_export(container_path) + if share_id: + location = self._format_nfs_path(container_path) + updates[share['id']] = { + 'export_locations': [location], + 'status': 'available', + 'reapply_access_rules': True, + } + else: + LOG.warning(f'NFS Share {share["name"]} is not found.') + elif share['share_proto'] == 'CIFS': + smb_share = self._isilon_api.lookup_smb_share(share['name']) + if smb_share: + location = self._format_smb_path(share['name']) + updates[share['id']] = { + 'export_locations': [location], + 'status': 'available', + 'reapply_access_rules': True, + } + else: + LOG.warning(f'CIFS Share {share["name"]} is not found.') + + if share['id'] not in updates: + updates[share['id']] = { + 'export_locations': [], + 'status': 'error', + 'reapply_access_rules': False, + } + return updates + def _format_smb_path(self, share_name): return '\\\\{0}\\{1}'.format(self._server, share_name) diff --git a/manila/share/drivers/dell_emc/plugins/isilon/isilon_api.py b/manila/share/drivers/dell_emc/plugins/isilon/isilon_api.py index 9895387f7d..5cef65bea3 100644 --- a/manila/share/drivers/dell_emc/plugins/isilon/isilon_api.py +++ b/manila/share/drivers/dell_emc/plugins/isilon/isilon_api.py @@ -393,6 +393,15 @@ class IsilonApi(object): spaces['used'] = stat['value'] return spaces + def get_cluster_version(self): + url = '{0}/platform/12/cluster/version'.format(self.host_url) + r = self.send_get_request(url) + if r.status_code != 200: + raise exception.ShareBackendException( + msg=_('Failed to get cluster version from PowerScale.') + ) + return r.json()['nodes'][0]['release'] + def request(self, method, url, headers=None, data=None, params=None): if data is not None: data = jsonutils.dumps(data) diff --git a/manila/tests/share/drivers/dell_emc/plugins/isilon/test_isilon.py b/manila/tests/share/drivers/dell_emc/plugins/isilon/test_isilon.py index 21518694c5..347fe0fd7c 100644 --- a/manila/tests/share/drivers/dell_emc/plugins/isilon/test_isilon.py +++ b/manila/tests/share/drivers/dell_emc/plugins/isilon/test_isilon.py @@ -454,7 +454,9 @@ class IsilonTest(test.TestCase): def test_ensure_share(self): share = {"name": self.SHARE_NAME, "share_proto": 'CIFS'} - self.storage_connection.ensure_share(self.mock_context, share, None) + self.assertRaises(NotImplementedError, + self.storage_connection.ensure_share, + self.mock_context, share, None) @mock.patch( 'manila.share.drivers.dell_emc.plugins.isilon.isilon.isilon_api.' @@ -980,3 +982,89 @@ class IsilonTest(test.TestCase): self.storage_connection._delete_directory(path) self._mock_isilon_api.is_path_existent.assert_called_with(path) self._mock_isilon_api.delete_path.assert_not_called() + + def test_get_backend_info(self): + self._mock_isilon_api.get_cluster_version.return_value = '1.0' + result = self.storage_connection.get_backend_info(None) + expected_info = { + 'driver_version': isilon.VERSION, + 'cluster_version': '1.0', + 'rest_server': self.ISILON_ADDR, + 'rest_port': '8080', + } + self.assertEqual(expected_info, result) + + def test_ensure_shares_nfs_share_exists(self): + share = { + 'id': '123', + 'share_proto': 'NFS', + 'name': 'my_share', + } + container_path = '/ifs/my_share' + location = '10.0.0.1:/ifs/my_share' + self.storage_connection._get_container_path = mock.MagicMock( + return_value=container_path) + self._mock_isilon_api.lookup_nfs_export.return_value = '123' + + result = self.storage_connection.ensure_shares(None, [share]) + expected_result = { + '123': { + 'export_locations': [location], + 'status': 'available', + 'reapply_access_rules': True, + } + } + self.assertEqual(result, expected_result) + + def test_ensure_shares_cifs_share_exists(self): + share = { + 'id': '123', + 'share_proto': 'CIFS', + 'name': 'my_share', + } + location = '\\\\10.0.0.1\\my_share' + self._mock_isilon_api.lookup_smb_share.return_value = share + + result = self.storage_connection.ensure_shares(None, [share]) + expected_result = { + '123': { + 'export_locations': [location], + 'status': 'available', + 'reapply_access_rules': True, + } + } + self.assertEqual(result, expected_result) + + def test_ensure_shares_nfs_share_does_not_exist(self): + share = { + 'id': '123', + 'share_proto': 'NFS', + 'name': 'my_share', + } + self._mock_isilon_api.lookup_nfs_export.return_value = None + result = self.storage_connection.ensure_shares(None, [share]) + expected_result = { + '123': { + 'export_locations': [], + 'status': 'error', + 'reapply_access_rules': False, + } + } + self.assertEqual(result, expected_result) + + def test_ensure_shares_cifs_share_does_not_exist(self): + share = { + 'id': '123', + 'share_proto': 'CIFS', + 'name': 'my_share', + } + self._mock_isilon_api.lookup_smb_share.return_value = None + result = self.storage_connection.ensure_shares(None, [share]) + expected_result = { + '123': { + 'export_locations': [], + 'status': 'error', + 'reapply_access_rules': False, + } + } + self.assertEqual(result, expected_result) diff --git a/manila/tests/share/drivers/dell_emc/plugins/isilon/test_isilon_api.py b/manila/tests/share/drivers/dell_emc/plugins/isilon/test_isilon_api.py index 4984ff3efc..b147b90900 100644 --- a/manila/tests/share/drivers/dell_emc/plugins/isilon/test_isilon_api.py +++ b/manila/tests/share/drivers/dell_emc/plugins/isilon/test_isilon_api.py @@ -886,6 +886,29 @@ class IsilonApiTest(test.TestCase): self.assertRaises(exception.ShareBackendException, self.isilon_api.get_space_stats) + def test_get_cluster_version_success(self): + self.isilon_api.send_get_request = mock.MagicMock() + self.isilon_api.send_get_request.return_value.status_code = 200 + self.isilon_api.send_get_request.return_value.json.return_value = { + 'nodes': [{'release': '1.0'}]} + + version = self.isilon_api.get_cluster_version() + self.assertEqual(version, '1.0') + self.isilon_api.send_get_request.assert_called_once_with( + '{0}/platform/12/cluster/version'.format(self.isilon_api.host_url) + ) + + def test_get_cluster_version_failure(self): + self.isilon_api.send_get_request = mock.MagicMock() + self.isilon_api.send_get_request.return_value.status_code = 404 + + self.assertRaises(exception.ShareBackendException, + self.isilon_api.get_cluster_version) + + self.isilon_api.send_get_request.assert_called_once_with( + '{0}/platform/12/cluster/version'.format(self.isilon_api.host_url) + ) + def test_modify_smb_share_access_with_host_acl_and_smb_permission(self): self.isilon_api.send_put_request = mock.MagicMock() share_name = 'my_share' diff --git a/manila/tests/share/drivers/dell_emc/test_driver.py b/manila/tests/share/drivers/dell_emc/test_driver.py index 87cd5e9d8e..4023e2d56f 100644 --- a/manila/tests/share/drivers/dell_emc/test_driver.py +++ b/manila/tests/share/drivers/dell_emc/test_driver.py @@ -78,6 +78,12 @@ class FakeConnection(base.StorageConnection): def teardown_server(self, server_details, security_services=None): """Teardown share server.""" + def get_backend_info(self, context): + """Get driver and array configuration parameters.""" + + def ensure_shares(self, context, shares): + """Invoked to ensure that shares are exported.""" + class FakeConnection_powermax(FakeConnection): def __init__(self, *args, **kwargs): @@ -228,6 +234,8 @@ class EMCShareFrameworkTestCase(test.TestCase): self.driver.delete_share(context, share, share_server) self.driver.delete_snapshot(context, snapshot, share_server) self.driver.ensure_share(context, share, share_server) + self.driver.get_backend_info(context) + self.driver.ensure_shares(context, [share]) access = mock.Mock() self.driver.allow_access(context, share, access, share_server) self.driver.deny_access(context, share, access, share_server) diff --git a/releasenotes/notes/bp-dell-powerscale-ensure-shares-f2634d498a679d23.yaml b/releasenotes/notes/bp-dell-powerscale-ensure-shares-f2634d498a679d23.yaml new file mode 100644 index 0000000000..222f995d1a --- /dev/null +++ b/releasenotes/notes/bp-dell-powerscale-ensure-shares-f2634d498a679d23.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Dell PowerScale Driver: Added support for ensure shares.