Dell PowerScale: add support for ensure shares
Implements: blueprint dell-powerscale-ensure-shares Change-Id: I957505bb4bce57f45c4caa44b1ac1eb7d7ef766c Signed-off-by: Yian Zong <yian.zong@dell.com>
This commit is contained in:
@@ -46,6 +46,8 @@ The following operations are supported:
|
||||
|
||||
- Create a share from a snapshot.
|
||||
|
||||
- Ensure shares.
|
||||
|
||||
Back end configuration
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@@ -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()
|
||||
|
@@ -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)
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
@@ -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'
|
||||
|
@@ -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)
|
||||
|
@@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Dell PowerScale Driver: Added support for ensure shares.
|
Reference in New Issue
Block a user