Add support for manage/unmanage in GPFS driver
Added support for manage/unmanage in GPFS NFS driver. This patch added functions that allow share on Spectrum Scale node to be managed by OpenStack if existing fileset is an independent fileset and doesn't have any NFS export over the fileset path. Also, share can be unmanaged from OpenStack but still left in Spectrum Scale cluster. Implements: blueprint gpfs-manage-support Change-Id: I9134408b59c30ac4bc593f287294741f6e996136
This commit is contained in:
parent
f427dfe00b
commit
fb4b0b86e9
@ -61,7 +61,7 @@ Mapping of share drivers and share features support
|
||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
||||
| Huawei | K | L | L | L | K | M | \- |
|
||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
||||
| IBM GPFS | K | \- | L | \- | K | K | \- |
|
||||
| IBM GPFS | K | O | L | \- | K | K | \- |
|
||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
||||
| LVM | M | \- | M | \- | M | M | \- |
|
||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
||||
|
@ -119,6 +119,12 @@ df: CommandFilter, df, root
|
||||
chmod: CommandFilter, chmod, root
|
||||
# manila/share/drivers/ibm/gpfs.py: 'mmnfs', 'export', '%s', '%s'
|
||||
mmnfs: CommandFilter, mmnfs, root
|
||||
# manila/share/drivers/ibm/gpfs.py: 'mmlsfileset', '%s', '-J', '%s', '-L'
|
||||
mmlsfileset: CommandFilter, mmlsfileset, root
|
||||
# manila/share/drivers/ibm/gpfs.py: 'mmchfileset', '%s', '-J', '%s', '-j', '%s'
|
||||
mmchfileset: CommandFilter, mmchfileset, root
|
||||
# manila/share/drivers/ibm/gpfs.py: 'mmlsquota', '-j', '-J', '%s', '%s'
|
||||
mmlsquota: CommandFilter, mmlsquota, root
|
||||
|
||||
# manila/share/drivers/ganesha/manager.py: 'mv', '%s', '%s'
|
||||
mv: CommandFilter, mv, root
|
||||
|
@ -44,7 +44,7 @@ import six
|
||||
|
||||
from manila.common import constants
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila.i18n import _, _LI
|
||||
from manila.share import driver
|
||||
from manila.share.drivers.helpers import NFSHelper
|
||||
from manila.share import share_types
|
||||
@ -564,6 +564,178 @@ class GPFSShareDriver(driver.ExecuteMixin, driver.GaneshaMixin,
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidParameterValue(err=msg)
|
||||
|
||||
def _is_share_valid(self, fsdev, location):
|
||||
try:
|
||||
out, __ = self._gpfs_execute('mmlsfileset', fsdev, '-J',
|
||||
location, '-L', '-Y')
|
||||
except exception.ProcessExecutionError:
|
||||
msg = (_('Given share path %(share_path)s does not exist at '
|
||||
'mount point %(mount_point)s.')
|
||||
% {'share_path': location, 'mount_point': fsdev})
|
||||
LOG.exception(msg)
|
||||
raise exception.ManageInvalidShare(reason=msg)
|
||||
|
||||
lines = out.splitlines()
|
||||
try:
|
||||
validation_token = lines[0].split(':').index('allocInodes')
|
||||
alloc_inodes = lines[1].split(':')[validation_token]
|
||||
except (IndexError, ValueError):
|
||||
msg = (_('Failed to check share at %s.') % location)
|
||||
LOG.exception(msg)
|
||||
raise exception.GPFSException(msg)
|
||||
|
||||
return alloc_inodes != '0'
|
||||
|
||||
def _get_share_name(self, fsdev, location):
|
||||
try:
|
||||
out, __ = self._gpfs_execute('mmlsfileset', fsdev, '-J',
|
||||
location, '-L', '-Y')
|
||||
except exception.ProcessExecutionError:
|
||||
msg = (_('Given share path %(share_path)s does not exist at '
|
||||
'mount point %(mount_point)s.')
|
||||
% {'share_path': location, 'mount_point': fsdev})
|
||||
LOG.exception(msg)
|
||||
raise exception.ManageInvalidShare(reason=msg)
|
||||
|
||||
lines = out.splitlines()
|
||||
try:
|
||||
validation_token = lines[0].split(':').index('filesetName')
|
||||
share_name = lines[1].split(':')[validation_token]
|
||||
except (IndexError, ValueError):
|
||||
msg = (_('Failed to check share at %s.') % location)
|
||||
LOG.exception(msg)
|
||||
raise exception.GPFSException(msg)
|
||||
|
||||
return share_name
|
||||
|
||||
def _manage_existing(self, fsdev, share, old_share_name):
|
||||
new_share_name = share['name']
|
||||
new_export_location = self._local_path(new_share_name)
|
||||
try:
|
||||
self._gpfs_execute('mmunlinkfileset', fsdev, old_share_name, '-f')
|
||||
except exception.ProcessExecutionError:
|
||||
msg = _('Failed to unlink fileset for share %s.') % new_share_name
|
||||
LOG.exception(msg)
|
||||
raise exception.GPFSException(msg)
|
||||
LOG.debug('Unlinked the fileset of share %s.', old_share_name)
|
||||
|
||||
try:
|
||||
self._gpfs_execute('mmchfileset', fsdev, old_share_name,
|
||||
'-j', new_share_name)
|
||||
except exception.ProcessExecutionError:
|
||||
msg = _('Failed to rename fileset for share %s.') % new_share_name
|
||||
LOG.exception(msg)
|
||||
raise exception.GPFSException(msg)
|
||||
LOG.debug('Renamed the fileset from %(old_share)s to %(new_share)s.',
|
||||
{'old_share': old_share_name, 'new_share': new_share_name})
|
||||
|
||||
try:
|
||||
self._gpfs_execute('mmlinkfileset', fsdev, new_share_name, '-J',
|
||||
new_export_location)
|
||||
except exception.ProcessExecutionError:
|
||||
msg = _('Failed to link fileset for the share %s.'
|
||||
) % new_share_name
|
||||
LOG.exception(msg)
|
||||
raise exception.GPFSException(msg)
|
||||
LOG.debug('Linked the fileset of share %(share_name)s at location '
|
||||
'%(export_location)s.',
|
||||
{'share_name': new_share_name,
|
||||
'export_location': new_export_location})
|
||||
|
||||
try:
|
||||
self._gpfs_execute('chmod', '777', new_export_location)
|
||||
except exception.ProcessExecutionError:
|
||||
msg = _('Failed to set permissions for share %s.') % new_share_name
|
||||
LOG.exception(msg)
|
||||
raise exception.GPFSException(msg)
|
||||
LOG.debug('Changed the permission of share %s.', new_share_name)
|
||||
|
||||
try:
|
||||
out, __ = self._gpfs_execute('mmlsquota', '-j', new_share_name,
|
||||
'-Y', fsdev)
|
||||
except exception.ProcessExecutionError:
|
||||
msg = _('Failed to check size for share %s.') % new_share_name
|
||||
LOG.exception(msg)
|
||||
raise exception.GPFSException(msg)
|
||||
|
||||
lines = out.splitlines()
|
||||
try:
|
||||
quota_limit = lines[0].split(':').index('blockLimit')
|
||||
quota_status = lines[1].split(':')[quota_limit]
|
||||
except (IndexError, ValueError):
|
||||
msg = _('Failed to check quota for share %s.') % new_share_name
|
||||
LOG.exception(msg)
|
||||
raise exception.GPFSException(msg)
|
||||
|
||||
share_size = int(quota_status)
|
||||
# Note: since share_size returns integer value in KB,
|
||||
# we are checking whether share is less than 1GiB.
|
||||
# (units.Mi * KB = 1GB)
|
||||
if share_size < units.Mi:
|
||||
try:
|
||||
self._gpfs_execute('mmsetquota', fsdev + ':' + new_share_name,
|
||||
'--block', '0:1G')
|
||||
except exception.ProcessExecutionError:
|
||||
msg = _('Failed to set quota for share %s.') % new_share_name
|
||||
LOG.exception(msg)
|
||||
raise exception.GPFSException(msg)
|
||||
LOG.info(_LI('Existing share %(shr)s has size %(size)s KB '
|
||||
'which is below 1GiB, so extended it to 1GiB.') %
|
||||
{'shr': new_share_name, 'size': share_size})
|
||||
share_size = 1
|
||||
else:
|
||||
orig_share_size = share_size
|
||||
share_size = int(math.ceil(float(share_size) / units.Mi))
|
||||
if orig_share_size != share_size * units.Mi:
|
||||
try:
|
||||
self._gpfs_execute('mmsetquota', fsdev + ':' +
|
||||
new_share_name, '--block', '0:' +
|
||||
str(share_size) + 'G')
|
||||
except exception.ProcessExecutionError:
|
||||
msg = _('Failed to set quota for share %s.'
|
||||
) % new_share_name
|
||||
LOG.exception(msg)
|
||||
raise exception.GPFSException(msg)
|
||||
|
||||
new_export_location = self._get_helper(share).create_export(
|
||||
new_export_location)
|
||||
return share_size, new_export_location
|
||||
|
||||
def manage_existing(self, share, driver_options):
|
||||
|
||||
old_export = share['export_location'].split(':')
|
||||
try:
|
||||
ces_ip = old_export[0]
|
||||
old_export_location = old_export[1]
|
||||
except IndexError:
|
||||
msg = _('Incorrect export path. Expected format: '
|
||||
'IP:/gpfs_mount_point_base/share_id.')
|
||||
LOG.exception(msg)
|
||||
raise exception.ShareBackendException(msg=msg)
|
||||
|
||||
if ces_ip not in self.configuration.gpfs_nfs_server_list:
|
||||
msg = _('The CES IP %s is not present in the '
|
||||
'configuration option "gpfs_nfs_server_list".') % ces_ip
|
||||
raise exception.ShareBackendException(msg=msg)
|
||||
|
||||
fsdev = self._get_gpfs_device()
|
||||
if not self._is_share_valid(fsdev, old_export_location):
|
||||
err_msg = _('Given share path %s does not have a valid '
|
||||
'share.') % old_export_location
|
||||
raise exception.ManageInvalidShare(reason=err_msg)
|
||||
|
||||
share_name = self._get_share_name(fsdev, old_export_location)
|
||||
|
||||
out = self._get_helper(share)._has_client_access(old_export_location)
|
||||
if out:
|
||||
err_msg = _('Clients have access to %s share currently. Evict any '
|
||||
'clients before trying again.') % share_name
|
||||
raise exception.ManageInvalidShare(reason=err_msg)
|
||||
|
||||
share_size, new_export_location = self._manage_existing(
|
||||
fsdev, share, share_name)
|
||||
return {"size": share_size, "export_locations": new_export_location}
|
||||
|
||||
def _update_share_stats(self):
|
||||
"""Retrieve stats info from share volume group."""
|
||||
|
||||
@ -679,6 +851,23 @@ class KNFSHelper(NASHelperBase):
|
||||
LOG.error(msg)
|
||||
raise exception.GPFSException(msg)
|
||||
|
||||
def _has_client_access(self, local_path, access_to=None):
|
||||
try:
|
||||
out, __ = self._execute('exportfs', run_as_root=True)
|
||||
except exception.ProcessExecutionError:
|
||||
msg = _('Failed to check exports on the systems.')
|
||||
LOG.exception(msg)
|
||||
raise exception.GPFSException(msg)
|
||||
|
||||
if access_to:
|
||||
if (re.search(re.escape(local_path) + '[\s\n]*'
|
||||
+ re.escape(access_to), out)):
|
||||
return True
|
||||
else:
|
||||
if re.findall(local_path + '\\b', ''.join(out)):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _publish_access(self, *cmd, **kwargs):
|
||||
check_exit_code = kwargs.get('check_exit_code', True)
|
||||
|
||||
@ -739,19 +928,9 @@ class KNFSHelper(NASHelperBase):
|
||||
raise exception.InvalidShareAccess(reason='Only ip access type '
|
||||
'supported.')
|
||||
|
||||
# check if present in export
|
||||
try:
|
||||
out, __ = self._execute('exportfs', run_as_root=True)
|
||||
except exception.ProcessExecutionError as e:
|
||||
msg = (_('Failed to check exports on the systems. '
|
||||
' Error: %s.') % e)
|
||||
LOG.error(msg)
|
||||
raise exception.GPFSException(msg)
|
||||
out = self._has_client_access(local_path, access['access_to'])
|
||||
|
||||
out = re.search(re.escape(local_path) + '[\s\n]*'
|
||||
+ re.escape(access['access_to']), out)
|
||||
|
||||
if out is not None:
|
||||
if out:
|
||||
access_type = access['access_type']
|
||||
access_to = access['access_to']
|
||||
raise exception.ShareAccessExists(access_type=access_type,
|
||||
|
@ -45,6 +45,7 @@ class GPFSShareDriverTestCase(test.TestCase):
|
||||
|
||||
self._helper_fake = mock.Mock()
|
||||
CONF.set_default('driver_handles_share_servers', False)
|
||||
CONF.set_default('share_backend_name', 'GPFS')
|
||||
self.fake_conf = config.Configuration(None)
|
||||
self._driver = gpfs.GPFSShareDriver(execute=self._gpfs_execute,
|
||||
configuration=self.fake_conf)
|
||||
@ -55,6 +56,7 @@ class GPFSShareDriverTestCase(test.TestCase):
|
||||
self.fakedev = "/dev/gpfs0"
|
||||
self.fakefspath = "/gpfs0"
|
||||
self.fakesharepath = "/gpfs0/share-fakeid"
|
||||
self.fakeexistingshare = "existingshare"
|
||||
self.fakesnapshotpath = "/gpfs0/.snapshots/snapshot-fakesnapshotid"
|
||||
|
||||
self.fake_ces_exports = """
|
||||
@ -74,7 +76,8 @@ mmcesnfslsexport:nfsexports:HEADER:version:reserved:reserved:Path:Delegations:Cl
|
||||
self._driver._helpers = {
|
||||
'KNFS': self._helper_fake
|
||||
}
|
||||
self.share = fake_share.fake_share(share_proto='NFS')
|
||||
self.share = fake_share.fake_share(share_proto='NFS',
|
||||
host='fakehost@fakehost#GPFS')
|
||||
self.server = {
|
||||
'backend_details': {
|
||||
'ip': '1.2.3.4',
|
||||
@ -86,7 +89,8 @@ mmcesnfslsexport:nfsexports:HEADER:version:reserved:reserved:Path:Delegations:Cl
|
||||
self.local_ip = "192.11.22.1"
|
||||
self.remote_ip = "192.11.22.2"
|
||||
self.remote_ip2 = "2.2.2.2"
|
||||
gpfs_nfs_server_list = [self.remote_ip, self.local_ip, self.remote_ip2]
|
||||
gpfs_nfs_server_list = [self.remote_ip, self.local_ip, self.remote_ip2,
|
||||
"fake_location"]
|
||||
self._knfs_helper.configuration.gpfs_nfs_server_list = \
|
||||
gpfs_nfs_server_list
|
||||
self._ces_helper.configuration.gpfs_nfs_server_list = \
|
||||
@ -671,6 +675,333 @@ mmcesnfslsexport:nfsexports:HEADER:version:reserved:reserved:Path:Delegations:Cl
|
||||
'rsync', '-rp', self.fakesnapshotpath + '/', self.fakesharepath
|
||||
)
|
||||
|
||||
@ddt.data("mmlsfileset::allocInodes:\nmmlsfileset::100096:",
|
||||
"mmlsfileset::allocInodes:\nmmlsfileset::0:")
|
||||
def test__is_share_valid_with_quota(self, fakeout):
|
||||
self._driver._gpfs_execute = mock.Mock(return_value=(fakeout, ''))
|
||||
|
||||
result = self._driver._is_share_valid(self.fakedev, self.fakesharepath)
|
||||
|
||||
self._driver._gpfs_execute.assert_called_once_with(
|
||||
'mmlsfileset', self.fakedev, '-J', self.fakesharepath, '-L', '-Y')
|
||||
if fakeout == "mmlsfileset::allocInodes:\nmmlsfileset::100096:":
|
||||
self.assertTrue(result)
|
||||
else:
|
||||
self.assertFalse(result)
|
||||
|
||||
def test__is_share_valid_exception(self):
|
||||
self._driver._gpfs_execute = mock.Mock(
|
||||
side_effect=exception.ProcessExecutionError)
|
||||
|
||||
self.assertRaises(exception.ManageInvalidShare,
|
||||
self._driver._is_share_valid, self.fakedev,
|
||||
self.fakesharepath)
|
||||
|
||||
self._driver._gpfs_execute.assert_called_once_with(
|
||||
'mmlsfileset', self.fakedev, '-J', self.fakesharepath, '-L', '-Y')
|
||||
|
||||
def test__is_share_valid_no_share_exist_exception(self):
|
||||
fakeout = "mmlsfileset::allocInodes:"
|
||||
self._driver._gpfs_execute = mock.Mock(return_value=(fakeout, ''))
|
||||
|
||||
self.assertRaises(exception.GPFSException,
|
||||
self._driver._is_share_valid, self.fakedev,
|
||||
self.fakesharepath)
|
||||
|
||||
self._driver._gpfs_execute.assert_called_once_with(
|
||||
'mmlsfileset', self.fakedev, '-J', self.fakesharepath, '-L', '-Y')
|
||||
|
||||
def test__get_share_name(self):
|
||||
fakeout = "mmlsfileset::filesetName:\nmmlsfileset::existingshare:"
|
||||
self._driver._gpfs_execute = mock.Mock(return_value=(fakeout, ''))
|
||||
|
||||
result = self._driver._get_share_name(self.fakedev, self.fakesharepath)
|
||||
|
||||
self.assertEqual('existingshare', result)
|
||||
|
||||
def test__get_share_name_exception(self):
|
||||
self._driver._gpfs_execute = mock.Mock(
|
||||
side_effect=exception.ProcessExecutionError)
|
||||
|
||||
self.assertRaises(exception.ManageInvalidShare,
|
||||
self._driver._get_share_name, self.fakedev,
|
||||
self.fakesharepath)
|
||||
|
||||
self._driver._gpfs_execute.assert_called_once_with(
|
||||
'mmlsfileset', self.fakedev, '-J', self.fakesharepath, '-L', '-Y')
|
||||
|
||||
def test__get_share_name_no_share_exist_exception(self):
|
||||
fakeout = "mmlsfileset::filesetName:"
|
||||
self._driver._gpfs_execute = mock.Mock(return_value=(fakeout, ''))
|
||||
|
||||
self.assertRaises(exception.GPFSException,
|
||||
self._driver._get_share_name, self.fakedev,
|
||||
self.fakesharepath)
|
||||
|
||||
self._driver._gpfs_execute.assert_called_once_with(
|
||||
'mmlsfileset', self.fakedev, '-J', self.fakesharepath, '-L', '-Y')
|
||||
|
||||
@ddt.data("mmlsquota::blockLimit:\nmmlsquota::1048577",
|
||||
"mmlsquota::blockLimit:\nmmlsquota::1048576",
|
||||
"mmlsquota::blockLimit:\nmmlsquota::0")
|
||||
def test__manage_existing(self, fakeout):
|
||||
self._driver._gpfs_execute = mock.Mock(return_value=(fakeout, ''))
|
||||
self._helper_fake.create_export.return_value = 'fakelocation'
|
||||
self._driver._local_path = mock.Mock(return_value=self.fakesharepath)
|
||||
|
||||
actual_size, actual_path = self._driver._manage_existing(
|
||||
self.fakedev, self.share, self.fakeexistingshare)
|
||||
|
||||
self._driver._gpfs_execute.assert_any_call('mmunlinkfileset',
|
||||
self.fakedev,
|
||||
self.fakeexistingshare,
|
||||
'-f')
|
||||
self._driver._gpfs_execute.assert_any_call('mmchfileset',
|
||||
self.fakedev,
|
||||
self.fakeexistingshare,
|
||||
'-j', self.share['name'])
|
||||
self._driver._gpfs_execute.assert_any_call('mmlinkfileset',
|
||||
self.fakedev,
|
||||
self.share['name'],
|
||||
'-J', self.fakesharepath)
|
||||
self._driver._gpfs_execute.assert_any_call('chmod',
|
||||
'777',
|
||||
self.fakesharepath)
|
||||
if fakeout == "mmlsquota::blockLimit:\nmmlsquota::1048577":
|
||||
self._driver._gpfs_execute.assert_called_with('mmsetquota',
|
||||
self.fakedev + ':' +
|
||||
self.share['name'],
|
||||
'--block',
|
||||
'0:2G')
|
||||
self.assertEqual(2, actual_size)
|
||||
self.assertEqual('fakelocation', actual_path)
|
||||
elif fakeout == "mmlsquota::blockLimit:\nmmlsquota::0":
|
||||
self._driver._gpfs_execute.assert_called_with('mmsetquota',
|
||||
self.fakedev + ':' +
|
||||
self.share['name'],
|
||||
'--block',
|
||||
'0:1G')
|
||||
self.assertEqual(1, actual_size)
|
||||
self.assertEqual('fakelocation', actual_path)
|
||||
else:
|
||||
self.assertEqual(1, actual_size)
|
||||
self.assertEqual('fakelocation', actual_path)
|
||||
|
||||
def test__manage_existing_fileset_unlink_exception(self):
|
||||
self._driver._local_path = mock.Mock(return_value=self.fakesharepath)
|
||||
self._driver._gpfs_execute = mock.Mock(
|
||||
side_effect=exception.ProcessExecutionError)
|
||||
|
||||
self.assertRaises(exception.GPFSException,
|
||||
self._driver._manage_existing, self.fakedev,
|
||||
self.share, self.fakeexistingshare)
|
||||
|
||||
self._driver._local_path.assert_called_once_with(self.share['name'])
|
||||
self._driver._gpfs_execute.assert_called_once_with(
|
||||
'mmunlinkfileset', self.fakedev, self.fakeexistingshare, '-f')
|
||||
|
||||
def test__manage_existing_fileset_creation_exception(self):
|
||||
self._driver._local_path = mock.Mock(return_value=self.fakesharepath)
|
||||
self.mock_object(self._driver, '_gpfs_execute', mock.Mock(
|
||||
side_effect=['', exception.ProcessExecutionError]))
|
||||
|
||||
self.assertRaises(exception.GPFSException,
|
||||
self._driver._manage_existing, self.fakedev,
|
||||
self.share, self.fakeexistingshare)
|
||||
|
||||
self._driver._local_path.assert_any_call(self.share['name'])
|
||||
self._driver._gpfs_execute.assert_has_calls([
|
||||
mock.call('mmunlinkfileset', self.fakedev, self.fakeexistingshare,
|
||||
'-f'),
|
||||
mock.call('mmchfileset', self.fakedev, self.fakeexistingshare,
|
||||
'-j', self.share['name'])])
|
||||
|
||||
def test__manage_existing_fileset_relink_exception(self):
|
||||
self._driver._local_path = mock.Mock(return_value=self.fakesharepath)
|
||||
self.mock_object(self._driver, '_gpfs_execute', mock.Mock(
|
||||
side_effect=['', '', exception.ProcessExecutionError]))
|
||||
|
||||
self.assertRaises(exception.GPFSException,
|
||||
self._driver._manage_existing, self.fakedev,
|
||||
self.share, self.fakeexistingshare)
|
||||
|
||||
self._driver._local_path.assert_any_call(self.share['name'])
|
||||
self._driver._gpfs_execute.assert_has_calls([
|
||||
mock.call('mmunlinkfileset', self.fakedev, self.fakeexistingshare,
|
||||
'-f'),
|
||||
mock.call('mmchfileset', self.fakedev, self.fakeexistingshare,
|
||||
'-j', self.share['name']),
|
||||
mock.call('mmlinkfileset', self.fakedev, self.share['name'], '-J',
|
||||
self.fakesharepath)])
|
||||
|
||||
def test__manage_existing_permission_change_exception(self):
|
||||
self._driver._local_path = mock.Mock(return_value=self.fakesharepath)
|
||||
self.mock_object(self._driver, '_gpfs_execute', mock.Mock(
|
||||
side_effect=['', '', '', exception.ProcessExecutionError]))
|
||||
|
||||
self.assertRaises(exception.GPFSException,
|
||||
self._driver._manage_existing, self.fakedev,
|
||||
self.share, self.fakeexistingshare)
|
||||
|
||||
self._driver._local_path.assert_any_call(self.share['name'])
|
||||
self._driver._gpfs_execute.assert_has_calls([
|
||||
mock.call('mmunlinkfileset', self.fakedev, self.fakeexistingshare,
|
||||
'-f'),
|
||||
mock.call('mmchfileset', self.fakedev, self.fakeexistingshare,
|
||||
'-j', self.share['name']),
|
||||
mock.call('mmlinkfileset', self.fakedev, self.share['name'], '-J',
|
||||
self.fakesharepath),
|
||||
mock.call('chmod', '777', self.fakesharepath)])
|
||||
|
||||
def test__manage_existing_checking_quota_of_fileset_exception(self):
|
||||
self._driver._local_path = mock.Mock(return_value=self.fakesharepath)
|
||||
self.mock_object(self._driver, '_gpfs_execute', mock.Mock(
|
||||
side_effect=['', '', '', '', exception.ProcessExecutionError]))
|
||||
|
||||
self.assertRaises(exception.GPFSException,
|
||||
self._driver._manage_existing, self.fakedev,
|
||||
self.share, self.fakeexistingshare)
|
||||
|
||||
self._driver._local_path.assert_any_call(self.share['name'])
|
||||
self._driver._gpfs_execute.assert_has_calls([
|
||||
mock.call('mmunlinkfileset', self.fakedev, self.fakeexistingshare,
|
||||
'-f'),
|
||||
mock.call('mmchfileset', self.fakedev, self.fakeexistingshare,
|
||||
'-j', self.share['name']),
|
||||
mock.call('mmlinkfileset', self.fakedev, self.share['name'], '-J',
|
||||
self.fakesharepath),
|
||||
mock.call('chmod', '777', self.fakesharepath),
|
||||
mock.call('mmlsquota', '-j', self.share['name'], '-Y',
|
||||
self.fakedev)])
|
||||
|
||||
def test__manage_existing_unable_to_get_quota_of_fileset_exception(self):
|
||||
fakeout = "mmlsquota::blockLimit:"
|
||||
self._driver._local_path = mock.Mock(return_value=self.fakesharepath)
|
||||
self._driver._gpfs_execute = mock.Mock(return_value=(fakeout, ''))
|
||||
|
||||
self.assertRaises(exception.GPFSException,
|
||||
self._driver._manage_existing, self.fakedev,
|
||||
self.share, self.fakeexistingshare)
|
||||
|
||||
self._driver._local_path.assert_any_call(self.share['name'])
|
||||
self._driver._gpfs_execute.assert_any_call('mmunlinkfileset',
|
||||
self.fakedev,
|
||||
self.fakeexistingshare,
|
||||
'-f')
|
||||
self._driver._gpfs_execute.assert_any_call('mmchfileset',
|
||||
self.fakedev,
|
||||
self.fakeexistingshare,
|
||||
'-j', self.share['name'])
|
||||
self._driver._gpfs_execute.assert_any_call('mmlinkfileset',
|
||||
self.fakedev,
|
||||
self.share['name'],
|
||||
'-J', self.fakesharepath)
|
||||
self._driver._gpfs_execute.assert_any_call('chmod',
|
||||
'777',
|
||||
self.fakesharepath)
|
||||
self._driver._gpfs_execute.assert_called_with(
|
||||
'mmlsquota', '-j', self.share['name'], '-Y', self.fakedev)
|
||||
|
||||
def test__manage_existing_set_quota_of_fileset_less_than_1G_exception(
|
||||
self):
|
||||
sizestr = '1G'
|
||||
mock_out = "mmlsquota::blockLimit:\nmmlsquota::0:", None
|
||||
self._driver._local_path = mock.Mock(return_value=self.fakesharepath)
|
||||
self.mock_object(self._driver, '_gpfs_execute', mock.Mock(
|
||||
side_effect=['', '', '', '', mock_out,
|
||||
exception.ProcessExecutionError]))
|
||||
|
||||
self.assertRaises(exception.GPFSException,
|
||||
self._driver._manage_existing, self.fakedev,
|
||||
self.share, self.fakeexistingshare)
|
||||
|
||||
self._driver._local_path.assert_any_call(self.share['name'])
|
||||
self._driver._gpfs_execute.assert_has_calls([
|
||||
mock.call('mmunlinkfileset', self.fakedev, self.fakeexistingshare,
|
||||
'-f'),
|
||||
mock.call('mmchfileset', self.fakedev, self.fakeexistingshare,
|
||||
'-j', self.share['name']),
|
||||
mock.call('mmlinkfileset', self.fakedev, self.share['name'], '-J',
|
||||
self.fakesharepath),
|
||||
mock.call('chmod', '777', self.fakesharepath),
|
||||
mock.call('mmlsquota', '-j', self.share['name'], '-Y',
|
||||
self.fakedev),
|
||||
mock.call('mmsetquota', self.fakedev + ':' + self.share['name'],
|
||||
'--block', '0:' + sizestr)])
|
||||
|
||||
def test__manage_existing_set_quota_of_fileset_grater_than_1G_exception(
|
||||
self):
|
||||
sizestr = '2G'
|
||||
mock_out = "mmlsquota::blockLimit:\nmmlsquota::1048577:", None
|
||||
self._driver._local_path = mock.Mock(return_value=self.fakesharepath)
|
||||
self.mock_object(self._driver, '_gpfs_execute', mock.Mock(
|
||||
side_effect=['', '', '', '', mock_out,
|
||||
exception.ProcessExecutionError]))
|
||||
|
||||
self.assertRaises(exception.GPFSException,
|
||||
self._driver._manage_existing, self.fakedev,
|
||||
self.share, self.fakeexistingshare)
|
||||
|
||||
self._driver._local_path.assert_any_call(self.share['name'])
|
||||
self._driver._gpfs_execute.assert_has_calls([
|
||||
mock.call('mmunlinkfileset', self.fakedev, self.fakeexistingshare,
|
||||
'-f'),
|
||||
mock.call('mmchfileset', self.fakedev, self.fakeexistingshare,
|
||||
'-j', self.share['name']),
|
||||
mock.call('mmlinkfileset', self.fakedev, self.share['name'], '-J',
|
||||
self.fakesharepath),
|
||||
mock.call('chmod', '777', self.fakesharepath),
|
||||
mock.call('mmlsquota', '-j', self.share['name'], '-Y',
|
||||
self.fakedev),
|
||||
mock.call('mmsetquota', self.fakedev + ':' + self.share['name'],
|
||||
'--block', '0:' + sizestr)])
|
||||
|
||||
def test_manage_existing(self):
|
||||
self._driver._manage_existing = mock.Mock(return_value=('1',
|
||||
'fakelocation'))
|
||||
self._driver._get_gpfs_device = mock.Mock(return_value=self.fakedev)
|
||||
self._driver._is_share_valid = mock.Mock(return_value=True)
|
||||
self._driver._get_share_name = mock.Mock(return_value=self.
|
||||
fakeexistingshare)
|
||||
self._helper_fake._has_client_access = mock.Mock(return_value=[])
|
||||
|
||||
result = self._driver.manage_existing(self.share, {})
|
||||
|
||||
self.assertEqual('1', result['size'])
|
||||
self.assertEqual('fakelocation', result['export_locations'])
|
||||
|
||||
def test_manage_existing_incorrect_path_exception(self):
|
||||
share = fake_share.fake_share(export_location="wrong_ip::wrong_path")
|
||||
|
||||
self.assertRaises(exception.ShareBackendException,
|
||||
self._driver.manage_existing, share, {})
|
||||
|
||||
def test_manage_existing_incorrect_ip_exception(self):
|
||||
share = fake_share.fake_share(export_location="wrong_ip:wrong_path")
|
||||
|
||||
self.assertRaises(exception.ShareBackendException,
|
||||
self._driver.manage_existing, share, {})
|
||||
|
||||
def test__manage_existing_invalid_export_exception(self):
|
||||
share = fake_share.fake_share(export_location="wrong_ip/wrong_path")
|
||||
|
||||
self.assertRaises(exception.ShareBackendException,
|
||||
self._driver.manage_existing, share, {})
|
||||
|
||||
@ddt.data(True, False)
|
||||
def test_manage_existing_invalid_share_exception(self, valid_share):
|
||||
self._driver._get_gpfs_device = mock.Mock(return_value=self.fakedev)
|
||||
self._driver._is_share_valid = mock.Mock(return_value=valid_share)
|
||||
if valid_share:
|
||||
self._driver._get_share_name = mock.Mock(return_value=self.
|
||||
fakeexistingshare)
|
||||
self._helper_fake._has_client_access = mock.Mock()
|
||||
else:
|
||||
self.assertFalse(self._helper_fake._has_client_access.called)
|
||||
|
||||
self.assertRaises(exception.ManageInvalidShare,
|
||||
self._driver.manage_existing, self.share, {})
|
||||
|
||||
def test__gpfs_local_execute(self):
|
||||
self.mock_object(utils, 'execute', mock.Mock(return_value=True))
|
||||
cmd = "testcmd"
|
||||
@ -730,6 +1061,28 @@ mmcesnfslsexport:nfsexports:HEADER:version:reserved:reserved:Path:Delegations:Cl
|
||||
self._knfs_helper.get_export_options,
|
||||
share, access, 'KNFS', options_not_allowed)
|
||||
|
||||
@ddt.data(("/gpfs0/share-fakeid\t10.0.0.1", None),
|
||||
("", None),
|
||||
("/gpfs0/share-fakeid\t10.0.0.1", "10.0.0.1"),
|
||||
("/gpfs0/share-fakeid\t10.0.0.1", "10.0.0.2"))
|
||||
@ddt.unpack
|
||||
def test_knfs__has_client_access(self, mock_out, access_to):
|
||||
self._knfs_helper._execute = mock.Mock(return_value=[mock_out, 0])
|
||||
|
||||
result = self._knfs_helper._has_client_access(self.fakesharepath,
|
||||
access_to)
|
||||
|
||||
self._ces_helper._execute.assert_called_once_with('exportfs',
|
||||
check_exit_code=True,
|
||||
run_as_root=True)
|
||||
if mock_out == "/gpfs0/share-fakeid\t10.0.0.1":
|
||||
if access_to in (None, "10.0.0.1"):
|
||||
self.assertTrue(result)
|
||||
else:
|
||||
self.assertFalse(result)
|
||||
else:
|
||||
self.assertFalse(result)
|
||||
|
||||
def test_knfs_allow_access(self):
|
||||
self._knfs_helper._execute = mock.Mock(
|
||||
return_value=['/fs0 <world>', 0]
|
||||
@ -1041,7 +1394,8 @@ mmcesnfslsexport:nfsexports:HEADER:version:reserved:reserved:Path:Delegations:Cl
|
||||
access = self.access
|
||||
local_path = self.fakesharepath
|
||||
|
||||
self._ces_helper.allow_access(local_path, self.share, access)
|
||||
self._ces_helper.allow_access(self.fakesharepath, self.share,
|
||||
self.access)
|
||||
|
||||
self._ces_helper._execute.assert_has_calls([
|
||||
mock.call('mmnfs', 'export', 'list', '-n', local_path, '-Y'),
|
||||
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
features:
|
||||
- Added manila manage/unmanage feature support for
|
||||
GPFS driver.
|
||||
The existing fileset should be an independent fileset
|
||||
and should not have any NFS export over the fileset
|
||||
path. With this prerequisite existing GPFS filesets
|
||||
can be brought under Manila management.
|
Loading…
Reference in New Issue
Block a user