From: Ben Swartzlander Date: Wed, 28 Aug 2013 04:43:20 +0000 (-0700) Subject: Add NFS/GlusterFS support to brick library X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=7523489e856e637dc6af23638568f4700980360f;p=openstack-build%2Fcinder-build.git Add NFS/GlusterFS support to brick library Refactor NFS driver to use the brick library to perform mounts rather than duplicating the mount code. Some of the NFS unit test code was rewritten to test the brick implementation because the old mount code was moved. The Gluster driver was slightly affected in the refactoring, but does not yet use brick to perform attaches. bug 1214748 Change-Id: Ib29f83fabe407f9591191e9fd19e6306ca9960cf --- diff --git a/cinder/brick/initiator/connector.py b/cinder/brick/initiator/connector.py index 256e95f13..ae16f2c53 100644 --- a/cinder/brick/initiator/connector.py +++ b/cinder/brick/initiator/connector.py @@ -26,6 +26,7 @@ from cinder.brick import executor from cinder.brick.initiator import host_driver from cinder.brick.initiator import linuxfc from cinder.brick.initiator import linuxscsi +from cinder.brick.remotefs import remotefs from cinder.openstack.common.gettextutils import _ from cinder.openstack.common import lockutils from cinder.openstack.common import log as logging @@ -104,6 +105,11 @@ class InitiatorConnector(executor.Executor): return AoEConnector(execute=execute, driver=driver, root_helper=root_helper) + elif protocol == "NFS" or protocol == "GLUSTERFS": + return RemoteFsConnector(mount_type=protocol.lower(), + execute=execute, + driver=driver, + root_helper=root_helper) else: msg = (_("Invalid InitiatorConnector protocol " "specified %(protocol)s") % @@ -761,3 +767,45 @@ class AoEConnector(InitiatorConnector): check_exit_code=0) LOG.debug(_('aoe-flush %(dev)s: stdout=%(out)s stderr%(err)s') % {'dev': aoe_device, 'out': out, 'err': err}) + + +class RemoteFsConnector(InitiatorConnector): + """Connector class to attach/detach NFS and GlusterFS volumes.""" + + def __init__(self, mount_type, root_helper, driver=None, + execute=putils.execute, *args, **kwargs): + self._remotefsclient = remotefs.RemoteFsClient(mount_type, + execute, root_helper) + super(RemoteFsConnector, self).__init__(driver, execute, root_helper, + *args, **kwargs) + + def set_execute(self, execute): + super(RemoteFsConnector, self).set_execute(execute) + self._remotefsclient.set_execute(execute) + + def connect_volume(self, connection_properties): + """Ensure that the filesystem containing the volume is mounted. + + connection_properties must include: + export - remote filesystem device (e.g. '172.18.194.100:/var/nfs') + name - file name within the filesystem + + connection_properties may optionally include: + options - options to pass to mount + """ + + mnt_flags = [] + if 'options' in connection_properties: + mnt_flags = connection_properties['options'].split() + + nfs_share = connection_properties['export'] + self._remotefsclient.mount(nfs_share, mnt_flags) + mount_point = self._remotefsclient.get_mount_point(nfs_share) + + path = mount_point + '/' + connection_properties['name'] + + return {'path': path} + + def disconnect_volume(self, connection_properties, device_info): + """No need to do anything to disconnect a volume in a filesystem.""" + pass diff --git a/cinder/brick/remotefs/__init__.py b/cinder/brick/remotefs/__init__.py new file mode 100644 index 000000000..d04ba7089 --- /dev/null +++ b/cinder/brick/remotefs/__init__.py @@ -0,0 +1,16 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 OpenStack Foundation +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. diff --git a/cinder/brick/remotefs/remotefs.py b/cinder/brick/remotefs/remotefs.py new file mode 100644 index 000000000..5cbc016a7 --- /dev/null +++ b/cinder/brick/remotefs/remotefs.py @@ -0,0 +1,112 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 OpenStack Foundation +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""Remote filesystem client utilities.""" + +import hashlib +import os + +from oslo.config import cfg + +from cinder import exception +from cinder.openstack.common.gettextutils import _ +from cinder.openstack.common import log as logging +from cinder.openstack.common import processutils as putils + +LOG = logging.getLogger(__name__) + +remotefs_client_opts = [ + cfg.StrOpt('nfs_mount_point_base', + default='$state_path/mnt', + help='Base dir containing mount points for nfs shares'), + cfg.StrOpt('nfs_mount_options', + default=None, + help='Mount options passed to the nfs client. See section ' + 'of the nfs man page for details'), + cfg.StrOpt('glusterfs_mount_point_base', + default='$state_path/mnt', + help='Base dir containing mount points for gluster shares'), +] + +CONF = cfg.CONF +CONF.register_opts(remotefs_client_opts) + + +class RemoteFsClient(object): + + def __init__(self, mount_type, execute=putils.execute, + root_helper="sudo", *args, **kwargs): + + self._mount_type = mount_type + if mount_type == "nfs": + self._mount_base = CONF.nfs_mount_point_base + self._mount_options = CONF.nfs_mount_options + elif mount_type == "glusterfs": + self._mount_base = CONF.glusterfs_mount_point_base + self._mount_options = None + else: + raise exception.ProtocolNotSupported(protocol=mount_type) + self.root_helper = root_helper + self.set_execute(execute) + + def set_execute(self, execute): + self._execute = execute + + def _get_hash_str(self, base_str): + """Return a string that represents hash of base_str + (in a hex format). + """ + return hashlib.md5(base_str).hexdigest() + + def get_mount_point(self, device_name): + """ + :param device_name: example 172.18.194.100:/var/nfs + """ + return os.path.join(self._mount_base, + self._get_hash_str(device_name)) + + def _read_mounts(self): + (out, err) = self._execute('mount', check_exit_code=0) + lines = out.split('\n') + mounts = {} + for line in lines: + tokens = line.split() + if 2 < len(tokens): + device = tokens[0] + mnt_point = tokens[2] + mounts[mnt_point] = device + return mounts + + def mount(self, nfs_share, flags=None): + """Mount NFS share.""" + mount_path = self.get_mount_point(nfs_share) + + if mount_path in self._read_mounts(): + LOG.info(_('Already mounted: %s') % mount_path) + return + + self._execute('mkdir', '-p', mount_path, check_exit_code=0) + + mnt_cmd = ['mount', '-t', self._mount_type] + if self._mount_options is not None: + mnt_cmd.extend(['-o', self._mount_options]) + if flags is not None: + mnt_cmd.extend(flags) + mnt_cmd.extend([nfs_share, mount_path]) + + self._execute(*mnt_cmd, root_helper=self.root_helper, + run_as_root=True, check_exit_code=0) diff --git a/cinder/tests/brick/test_brick_connector.py b/cinder/tests/brick/test_brick_connector.py index 95f916967..291896906 100644 --- a/cinder/tests/brick/test_brick_connector.py +++ b/cinder/tests/brick/test_brick_connector.py @@ -25,6 +25,7 @@ from cinder.brick.initiator import connector from cinder.brick.initiator import host_driver from cinder.brick.initiator import linuxfc from cinder.brick.initiator import linuxscsi +from cinder.brick.remotefs import remotefs from cinder.openstack.common import log as logging from cinder.openstack.common import loopingcall from cinder.openstack.common import processutils as putils @@ -67,6 +68,14 @@ class ConnectorTestCase(test.TestCase): self.assertTrue(obj.__class__.__name__, "AoEConnector") + obj = connector.InitiatorConnector.factory('nfs', None) + self.assertTrue(obj.__class__.__name__, + "RemoteFsConnector") + + obj = connector.InitiatorConnector.factory('glusterfs', None) + self.assertTrue(obj.__class__.__name__, + "RemoteFsConnector") + self.assertRaises(ValueError, connector.InitiatorConnector.factory, "bogus", None) @@ -543,3 +552,44 @@ class AoEConnectorTestCase(ConnectorTestCase): self.mox.ReplayAll() self.connector.disconnect_volume(self.connection_properties, {}) + + +class RemoteFsConnectorTestCase(ConnectorTestCase): + """Test cases for Remote FS initiator class.""" + TEST_DEV = '172.18.194.100:/var/nfs' + TEST_PATH = '/mnt/test/df0808229363aad55c27da50c38d6328' + + def setUp(self): + super(RemoteFsConnectorTestCase, self).setUp() + self.mox = mox.Mox() + self.connection_properties = { + 'export': self.TEST_DEV, + 'name': '9c592d52-ce47-4263-8c21-4ecf3c029cdb'} + self.connector = connector.RemoteFsConnector('nfs', root_helper='sudo') + self.connector._remotefsclient._mount_options = None + self.connector._remotefsclient._mount_base = '/mnt/test' + + def tearDown(self): + self.mox.VerifyAll() + self.mox.UnsetStubs() + super(RemoteFsConnectorTestCase, self).tearDown() + + def test_connect_volume(self): + """Test the basic connect volume case.""" + client = self.connector._remotefsclient + self.mox.StubOutWithMock(client, '_execute') + client._execute('mount', + check_exit_code=0).AndReturn(("", "")) + client._execute('mkdir', '-p', self.TEST_PATH, + check_exit_code=0).AndReturn(("", "")) + client._execute('mount', '-t', 'nfs', + self.TEST_DEV, self.TEST_PATH, + root_helper='sudo', run_as_root=True, + check_exit_code=0).AndReturn(("", "")) + self.mox.ReplayAll() + + self.connector.connect_volume(self.connection_properties) + + def test_disconnect_volume(self): + """Nothing should happen here -- make sure it doesn't blow up.""" + self.connector.disconnect_volume(self.connection_properties, {}) diff --git a/cinder/tests/brick/test_brick_remotefs.py b/cinder/tests/brick/test_brick_remotefs.py new file mode 100644 index 000000000..88d0243ab --- /dev/null +++ b/cinder/tests/brick/test_brick_remotefs.py @@ -0,0 +1,82 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# (c) Copyright 2013 OpenStack Foundation +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mox + +from cinder.brick.remotefs import remotefs +from cinder.openstack.common import log as logging +from cinder.openstack.common import processutils as putils +from cinder import test + +LOG = logging.getLogger(__name__) + + +class BrickRemoteFsTestCase(test.TestCase): + TEST_EXPORT = '1.2.3.4/export1' + TEST_MNT_BASE = '/mnt/test' + TEST_HASH = '4d664fd43b6ff86d80a4ea969c07b3b9' + TEST_MNT_POINT = TEST_MNT_BASE + '/' + TEST_HASH + + def setUp(self): + super(BrickRemoteFsTestCase, self).setUp() + self._mox = mox.Mox() + self._nfsclient = remotefs.RemoteFsClient('nfs') + self._nfsclient._mount_options = None + self._nfsclient._mount_base = self.TEST_MNT_BASE + self.addCleanup(self._mox.UnsetStubs) + + def test_get_hash_str(self): + """_get_hash_str should calculation correct value.""" + + self.assertEqual(self.TEST_HASH, + self._nfsclient._get_hash_str(self.TEST_EXPORT)) + + def test_get_mount_point(self): + mnt_point = self._nfsclient.get_mount_point(self.TEST_EXPORT) + self.assertEqual(mnt_point, self.TEST_MNT_POINT) + + def test_mount_nfs_should_mount_correctly(self): + mox = self._mox + client = self._nfsclient + + mox.StubOutWithMock(client, '_execute') + client._execute('mount', check_exit_code=0).AndReturn(("", "")) + client._execute('mkdir', '-p', self.TEST_MNT_POINT, + check_exit_code=0).AndReturn(("", "")) + client._execute('mount', '-t', 'nfs', self.TEST_EXPORT, + self.TEST_MNT_POINT, + root_helper='sudo', run_as_root=True, + check_exit_code=0).AndReturn(("", "")) + mox.ReplayAll() + + client.mount(self.TEST_EXPORT) + + mox.VerifyAll() + + def test_mount_nfs_should_not_remount(self): + mox = self._mox + client = self._nfsclient + + line = "%s on %s type nfs (rw)\n" % (self.TEST_EXPORT, + self.TEST_MNT_POINT) + mox.StubOutWithMock(client, '_execute') + client._execute('mount', check_exit_code=0).AndReturn((line, "")) + mox.ReplayAll() + + client.mount(self.TEST_EXPORT) + + mox.VerifyAll() diff --git a/cinder/tests/test_nfs.py b/cinder/tests/test_nfs.py index 332a13a49..8d8488304 100644 --- a/cinder/tests/test_nfs.py +++ b/cinder/tests/test_nfs.py @@ -47,7 +47,6 @@ class DumbVolume(object): class RemoteFsDriverTestCase(test.TestCase): - TEST_EXPORT = '1.2.3.4/export1' TEST_FILE_NAME = 'test.txt' def setUp(self): @@ -110,13 +109,6 @@ class RemoteFsDriverTestCase(test.TestCase): mox.VerifyAll() - def test_get_hash_str(self): - """_get_hash_str should calculation correct value.""" - drv = self._driver - - self.assertEqual('4d664fd43b6ff86d80a4ea969c07b3b9', - drv._get_hash_str(self.TEST_EXPORT)) - class NfsDriverTestCase(test.TestCase): """Test case for NFS driver.""" @@ -140,13 +132,13 @@ class NfsDriverTestCase(test.TestCase): self.configuration = mox_lib.MockObject(conf.Configuration) self.configuration.append_config_values(mox_lib.IgnoreArg()) self.configuration.nfs_shares_config = None - self.configuration.nfs_mount_options = None - self.configuration.nfs_mount_point_base = '$state_path/mnt' self.configuration.nfs_sparsed_volumes = True self.configuration.nfs_used_ratio = 0.95 self.configuration.nfs_oversub_ratio = 1.0 self._driver = nfs.NfsDriver(configuration=self.configuration) self._driver.shares = {} + self._driver._remotefsclient._mount_options = None + self._driver._remotefsclient._mount_base = self.TEST_MNT_POINT_BASE self.addCleanup(self.stubs.UnsetAll) self.addCleanup(self._mox.UnsetStubs) @@ -168,22 +160,6 @@ class NfsDriverTestCase(test.TestCase): '/mnt/test/2f4f60214cf43c595666dd815f0360a4/volume-123', drv.local_path(volume)) - def test_mount_nfs_should_mount_correctly(self): - """_mount_nfs common case usage.""" - mox = self._mox - drv = self._driver - - mox.StubOutWithMock(drv, '_execute') - drv._execute('mkdir', '-p', self.TEST_MNT_POINT) - drv._execute('mount', '-t', 'nfs', self.TEST_NFS_EXPORT1, - self.TEST_MNT_POINT, run_as_root=True) - - mox.ReplayAll() - - drv._mount_nfs(self.TEST_NFS_EXPORT1, self.TEST_MNT_POINT) - - mox.VerifyAll() - def test_copy_image_to_volume(self): """resize_image common case usage.""" mox = self._mox @@ -215,86 +191,6 @@ class NfsDriverTestCase(test.TestCase): mox.VerifyAll() - def test_mount_nfs_should_suppress_already_mounted_error(self): - """_mount_nfs should suppress already mounted error if ensure=True - """ - mox = self._mox - drv = self._driver - - mox.StubOutWithMock(drv, '_execute') - drv._execute('mkdir', '-p', self.TEST_MNT_POINT) - drv._execute('mount', '-t', 'nfs', self.TEST_NFS_EXPORT1, - self.TEST_MNT_POINT, run_as_root=True).\ - AndRaise(putils.ProcessExecutionError( - stderr='is busy or already mounted')) - - mox.ReplayAll() - - drv._mount_nfs(self.TEST_NFS_EXPORT1, self.TEST_MNT_POINT, ensure=True) - - mox.VerifyAll() - - def test_mount_nfs_should_reraise_already_mounted_error(self): - """_mount_nfs should not suppress already mounted error if ensure=False - """ - mox = self._mox - drv = self._driver - - mox.StubOutWithMock(drv, '_execute') - drv._execute('mkdir', '-p', self.TEST_MNT_POINT) - drv._execute( - 'mount', - '-t', - 'nfs', - self.TEST_NFS_EXPORT1, self.TEST_MNT_POINT, run_as_root=True).\ - AndRaise(putils.ProcessExecutionError(stderr='is busy or ' - 'already mounted')) - - mox.ReplayAll() - - self.assertRaises(putils.ProcessExecutionError, drv._mount_nfs, - self.TEST_NFS_EXPORT1, self.TEST_MNT_POINT, - ensure=False) - - mox.VerifyAll() - - def test_mount_nfs_should_create_mountpoint_if_not_yet(self): - """_mount_nfs should create mountpoint if it doesn't exist.""" - mox = self._mox - drv = self._driver - - mox.StubOutWithMock(drv, '_execute') - drv._execute('mkdir', '-p', self.TEST_MNT_POINT) - drv._execute(*([IgnoreArg()] * 5), run_as_root=IgnoreArg()) - - mox.ReplayAll() - - drv._mount_nfs(self.TEST_NFS_EXPORT1, self.TEST_MNT_POINT) - - mox.VerifyAll() - - def test_mount_nfs_should_not_create_mountpoint_if_already(self): - """_mount_nfs should not create mountpoint if it already exists.""" - mox = self._mox - drv = self._driver - - mox.StubOutWithMock(drv, '_execute') - drv._execute('mkdir', '-p', self.TEST_MNT_POINT) - drv._execute(*([IgnoreArg()] * 5), run_as_root=IgnoreArg()) - - mox.ReplayAll() - - drv._mount_nfs(self.TEST_NFS_EXPORT1, self.TEST_MNT_POINT) - - mox.VerifyAll() - - def test_get_hash_str(self): - """_get_hash_str should calculation correct value.""" - drv = self._driver - - self.assertEqual('2f4f60214cf43c595666dd815f0360a4', - drv._get_hash_str(self.TEST_NFS_EXPORT1)) - def test_get_mount_point_for_share(self): """_get_mount_point_for_share should calculate correct value.""" drv = self._driver @@ -398,24 +294,6 @@ class NfsDriverTestCase(test.TestCase): mox.VerifyAll() - def test_ensure_share_mounted(self): - """_ensure_share_mounted simple use case.""" - mox = self._mox - drv = self._driver - - mox.StubOutWithMock(drv, '_get_mount_point_for_share') - drv._get_mount_point_for_share(self.TEST_NFS_EXPORT1).\ - AndReturn(self.TEST_MNT_POINT) - - mox.StubOutWithMock(drv, '_mount_nfs') - drv._mount_nfs(self.TEST_NFS_EXPORT1, self.TEST_MNT_POINT, ensure=True) - - mox.ReplayAll() - - drv._ensure_share_mounted(self.TEST_NFS_EXPORT1) - - mox.VerifyAll() - def test_ensure_shares_mounted_should_save_mounting_successfully(self): """_ensure_shares_mounted should save share if mounted with success.""" mox = self._mox diff --git a/cinder/volume/drivers/glusterfs.py b/cinder/volume/drivers/glusterfs.py index ddc4fdb40..5b3470c7e 100644 --- a/cinder/volume/drivers/glusterfs.py +++ b/cinder/volume/drivers/glusterfs.py @@ -16,12 +16,14 @@ # under the License. import errno +import hashlib import json import os import re from oslo.config import cfg +from cinder.brick.remotefs import remotefs from cinder import db from cinder import exception from cinder.image import image_utils @@ -34,9 +36,6 @@ volume_opts = [ cfg.StrOpt('glusterfs_shares_config', default='/etc/cinder/glusterfs_shares', help='File with the list of available gluster shares'), - cfg.StrOpt('glusterfs_mount_point_base', - default='$state_path/mnt', - help='Base dir containing mount points for gluster shares'), cfg.StrOpt('glusterfs_disk_util', default='df', help='Use du or df for free space calculation'), @@ -68,6 +67,7 @@ class GlusterfsDriver(nfs.RemoteFsDriver): def __init__(self, *args, **kwargs): super(GlusterfsDriver, self).__init__(*args, **kwargs) self.configuration.append_config_values(volume_opts) + self.configuration.append_config_values(remotefs.remotefs_client_opts) def do_setup(self, context): """Any initialization the volume driver does while starting.""" @@ -795,6 +795,12 @@ class GlusterfsDriver(nfs.RemoteFsDriver): volume_size=volume_size_for) return greatest_share + def _get_hash_str(self, base_str): + """Return a string that represents hash of base_str + (in a hex format). + """ + return hashlib.md5(base_str).hexdigest() + def _get_mount_point_for_share(self, glusterfs_share): """Return mount point for share. :param glusterfs_share: example 172.18.194.100:/var/glusterfs diff --git a/cinder/volume/drivers/nfs.py b/cinder/volume/drivers/nfs.py index 1fd65a8a6..13e108a92 100644 --- a/cinder/volume/drivers/nfs.py +++ b/cinder/volume/drivers/nfs.py @@ -16,15 +16,15 @@ # under the License. import errno -import hashlib import os from oslo.config import cfg +from cinder.brick.remotefs import remotefs from cinder import exception from cinder.image import image_utils from cinder.openstack.common import log as logging -from cinder.openstack.common import processutils +from cinder.openstack.common import processutils as putils from cinder import units from cinder.volume import driver @@ -36,18 +36,11 @@ volume_opts = [ cfg.StrOpt('nfs_shares_config', default='/etc/cinder/nfs_shares', help='File with the list of available nfs shares'), - cfg.StrOpt('nfs_mount_point_base', - default='$state_path/mnt', - help='Base dir containing mount points for nfs shares'), cfg.BoolOpt('nfs_sparsed_volumes', default=True, help=('Create volumes as sparsed files which take no space.' 'If set to False volume is created as regular file.' 'In such case volume creation takes a lot of time.')), - cfg.StrOpt('nfs_mount_options', - default=None, - help='Mount options passed to the nfs client. See section ' - 'of the nfs man page for details'), cfg.FloatOpt('nfs_used_ratio', default=0.95, help=('Percent of ACTUAL usage of the underlying volume ' @@ -216,12 +209,6 @@ class RemoteFsDriver(driver.VolumeDriver): return os.path.join(self._get_mount_point_for_share(nfs_share), volume['name']) - def _get_hash_str(self, base_str): - """returns string that represents hash of base_str - (in a hex format). - """ - return hashlib.md5(base_str).hexdigest() - def copy_image_to_volume(self, context, volume, image_service, image_id): """Fetch the image from image_service and write it to the volume.""" image_utils.fetch_to_raw(context, @@ -334,7 +321,7 @@ class RemoteFsDriver(driver.VolumeDriver): """ try: self._execute(*cmd, run_as_root=True) - except processutils.ProcessExecutionError as exc: + except putils.ProcessExecutionError as exc: if ensure and 'already mounted' in exc.stderr: LOG.warn(_("%s is already mounted"), share) else: @@ -368,9 +355,16 @@ class NfsDriver(RemoteFsDriver): volume_backend_name = 'Generic_NFS' VERSION = VERSION - def __init__(self, *args, **kwargs): + def __init__(self, execute=putils.execute, *args, **kwargs): + self._remotefsclient = None super(NfsDriver, self).__init__(*args, **kwargs) self.configuration.append_config_values(volume_opts) + self._remotefsclient = remotefs.RemoteFsClient('nfs', execute) + + def set_execute(self, execute): + super(NfsDriver, self).set_execute(execute) + if self._remotefsclient: + self._remotefsclient.set_execute(execute) def do_setup(self, context): """Any initialization the volume driver does while starting""" @@ -412,8 +406,10 @@ class NfsDriver(RemoteFsDriver): raise def _ensure_share_mounted(self, nfs_share): - mount_path = self._get_mount_point_for_share(nfs_share) - self._mount_nfs(nfs_share, mount_path, ensure=True) + mnt_flags = [] + if self.shares.get(nfs_share) is not None: + mnt_flags = self.shares[nfs_share].split() + self._remotefsclient.mount(nfs_share, mnt_flags) def _find_share(self, volume_size_in_gib): """Choose NFS share among available ones for given volume size. @@ -482,11 +478,8 @@ class NfsDriver(RemoteFsDriver): return target_share def _get_mount_point_for_share(self, nfs_share): - """ - :param nfs_share: example 172.18.194.100:/var/nfs - """ - return os.path.join(self.configuration.nfs_mount_point_base, - self._get_hash_str(nfs_share)) + """Needed by parent class.""" + return self._remotefsclient.get_mount_point(nfs_share) def _get_capacity_info(self, nfs_share): """Calculate available space on the NFS share. @@ -504,17 +497,3 @@ class NfsDriver(RemoteFsDriver): '*snapshot*', mount_point, run_as_root=True) total_allocated = float(du.split()[0]) return total_size, total_available, total_allocated - - def _mount_nfs(self, nfs_share, mount_path, ensure=False): - """Mount NFS share to mount path.""" - self._execute('mkdir', '-p', mount_path) - - # Construct the NFS mount command. - nfs_cmd = ['mount', '-t', 'nfs'] - if self.configuration.nfs_mount_options is not None: - nfs_cmd.extend(['-o', self.configuration.nfs_mount_options]) - if self.shares.get(nfs_share) is not None: - nfs_cmd.extend(self.shares[nfs_share].split()) - nfs_cmd.extend([nfs_share, mount_path]) - - self._do_mount(nfs_cmd, ensure, nfs_share) diff --git a/etc/cinder/cinder.conf.sample b/etc/cinder/cinder.conf.sample index 2214d0104..263405300 100644 --- a/etc/cinder/cinder.conf.sample +++ b/etc/cinder/cinder.conf.sample @@ -273,6 +273,23 @@ #volumes_dir=$state_path/volumes +# +# Options defined in cinder.brick.remotefs.remotefs +# + +# Base dir containing mount points for nfs shares (string +# value) +#nfs_mount_point_base=$state_path/mnt + +# Mount options passed to the nfs client. See section of the +# nfs man page for details (string value) +#nfs_mount_options= + +# Base dir containing mount points for gluster shares (string +# value) +#glusterfs_mount_point_base=$state_path/mnt + + # # Options defined in cinder.common.config # @@ -1103,10 +1120,6 @@ # value) #glusterfs_shares_config=/etc/cinder/glusterfs_shares -# Base dir containing mount points for gluster shares (string -# value) -#glusterfs_mount_point_base=$state_path/mnt - # Use du or df for free space calculation (string value) #glusterfs_disk_util=df @@ -1266,19 +1279,11 @@ # File with the list of available nfs shares (string value) #nfs_shares_config=/etc/cinder/nfs_shares -# Base dir containing mount points for nfs shares (string -# value) -#nfs_mount_point_base=$state_path/mnt - # Create volumes as sparsed files which take no space.If set # to False volume is created as regular file.In such case # volume creation takes a lot of time. (boolean value) #nfs_sparsed_volumes=true -# Mount options passed to the nfs client. See section of the -# nfs man page for details (string value) -#nfs_mount_options= - # Percent of ACTUAL usage of the underlying volume before no # new volumes can be allocated to the volume destination. # (floating point value)