diff --git a/doc/source/devref/share_back_ends_feature_support_mapping.rst b/doc/source/devref/share_back_ends_feature_support_mapping.rst index 3ab154b144..cddec61975 100644 --- a/doc/source/devref/share_back_ends_feature_support_mapping.rst +++ b/doc/source/devref/share_back_ends_feature_support_mapping.rst @@ -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 | \- | +----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+ diff --git a/etc/manila/rootwrap.d/share.filters b/etc/manila/rootwrap.d/share.filters index 0941275749..6c5229a6fe 100644 --- a/etc/manila/rootwrap.d/share.filters +++ b/etc/manila/rootwrap.d/share.filters @@ -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 diff --git a/manila/share/drivers/ibm/gpfs.py b/manila/share/drivers/ibm/gpfs.py index 621e767c09..13a48ffb5a 100644 --- a/manila/share/drivers/ibm/gpfs.py +++ b/manila/share/drivers/ibm/gpfs.py @@ -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, diff --git a/manila/tests/share/drivers/ibm/test_gpfs.py b/manila/tests/share/drivers/ibm/test_gpfs.py index 0c4b1f429c..728a1d3155 100644 --- a/manila/tests/share/drivers/ibm/test_gpfs.py +++ b/manila/tests/share/drivers/ibm/test_gpfs.py @@ -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 ', 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'), diff --git a/releasenotes/notes/ibm-gpfs-manage-support-c110120c350728e3.yaml b/releasenotes/notes/ibm-gpfs-manage-support-c110120c350728e3.yaml new file mode 100644 index 0000000000..086cd07434 --- /dev/null +++ b/releasenotes/notes/ibm-gpfs-manage-support-c110120c350728e3.yaml @@ -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.