]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Add NFS/GlusterFS support to brick library
authorBen Swartzlander <bswartz@netapp.com>
Wed, 28 Aug 2013 04:43:20 +0000 (21:43 -0700)
committerBen Swartzlander <bswartz@netapp.com>
Wed, 28 Aug 2013 16:25:52 +0000 (09:25 -0700)
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

cinder/brick/initiator/connector.py
cinder/brick/remotefs/__init__.py [new file with mode: 0644]
cinder/brick/remotefs/remotefs.py [new file with mode: 0644]
cinder/tests/brick/test_brick_connector.py
cinder/tests/brick/test_brick_remotefs.py [new file with mode: 0644]
cinder/tests/test_nfs.py
cinder/volume/drivers/glusterfs.py
cinder/volume/drivers/nfs.py
etc/cinder/cinder.conf.sample

index 256e95f1360b21f1c0c6adab9fa21dc6a88c44ff..ae16f2c535c05eb732764c855cfa9da5e720d0d1 100644 (file)
@@ -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 (file)
index 0000000..d04ba70
--- /dev/null
@@ -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 (file)
index 0000000..5cbc016
--- /dev/null
@@ -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)
index 95f91696714aa984d61935ea322a300ded8e3364..291896906331f16487e845edad5ff4948208811c 100644 (file)
@@ -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 (file)
index 0000000..88d0243
--- /dev/null
@@ -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()
index 332a13a49791f31fd50dcbb22a7c790cd1cc140d..8d84883047f8fd77a87f20b8bac615cff0ce4e66 100644 (file)
@@ -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
index ddc4fdb4024d7d7392b405b9722a649378bf3655..5b3470c7e240995767ae2d9ed38d713236268ab3 100644 (file)
 #    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
index 1fd65a8a62c76e805e9495f06b6926df9428cba7..13e108a92e7fdbe6ad591f7699685eb728029207 100644 (file)
 #    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)
index 2214d0104211fa7ad4d09ac4656675e56535e874..263405300377153a15dab65696bc6b16161377da 100644 (file)
 #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=<None>
+
+# Base dir containing mount points for gluster shares (string
+# value)
+#glusterfs_mount_point_base=$state_path/mnt
+
+
 #
 # Options defined in cinder.common.config
 #
 # 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
 
 # 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=<None>
-
 # Percent of ACTUAL usage of the underlying volume before no
 # new volumes can be allocated to the volume destination.
 # (floating point value)