summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2012-08-15 07:55:57 +0000
committerGerrit Code Review <review@openstack.org>2012-08-15 07:55:57 +0000
commitefdc248d15cf2a0308b215dccb90d0b7da6926b7 (patch)
tree0faad2bacdfd36ecfd3577f51597e3155c0c4131
parent9f9f1a7aa46fad83fdda36be15d648dfa21cba2f (diff)
parent6972cd1e7eb1ec51f9ab4d94a842ebc45b655cc5 (diff)
downloadnova-efdc248d15cf2a0308b215dccb90d0b7da6926b7.tar.gz
nova-efdc248d15cf2a0308b215dccb90d0b7da6926b7.tar.xz
nova-efdc248d15cf2a0308b215dccb90d0b7da6926b7.zip
Merge "Add support for NFS-based virtual block devices"
-rw-r--r--nova/tests/test_libvirt.py26
-rw-r--r--nova/virt/libvirt/volume_nfs.py96
2 files changed, 122 insertions, 0 deletions
diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py
index 781a68033..b1359323b 100644
--- a/nova/tests/test_libvirt.py
+++ b/nova/tests/test_libvirt.py
@@ -56,6 +56,7 @@ from nova.virt.libvirt import firewall
from nova.virt.libvirt import imagebackend
from nova.virt.libvirt import utils as libvirt_utils
from nova.virt.libvirt import volume
+from nova.virt.libvirt import volume_nfs
from nova.volume import driver as volume_driver
@@ -326,6 +327,31 @@ class LibvirtVolumeTestCase(test.TestCase):
libvirt_driver.disconnect_volume(connection_info, mount_device)
connection_info = vol_driver.terminate_connection(vol, self.connr)
+ def test_libvirt_nfs_driver(self):
+ # NOTE(vish) exists is to make driver assume connecting worked
+ mnt_base = '/mnt'
+ self.flags(nfs_mount_point_base=mnt_base)
+
+ libvirt_driver = volume_nfs.NfsVolumeDriver(self.fake_conn)
+ export_string = '192.168.1.1:/nfs/share1'
+ name = 'volume-00001'
+ export_mnt_base = os.path.join(mnt_base,
+ libvirt_driver.get_hash_str(export_string))
+ file_path = os.path.join(export_mnt_base, name)
+
+ connection_info = {'data': {'export': export_string, 'name': name}}
+ mount_device = "vde"
+ conf = libvirt_driver.connect_volume(connection_info, mount_device)
+ tree = conf.format_dom()
+ self.assertEqual(tree.get('type'), 'file')
+ self.assertEqual(tree.find('./source').get('file'), file_path)
+ libvirt_driver.disconnect_volume(connection_info, mount_device)
+
+ expected_commands = [
+ ('stat', export_mnt_base),
+ ('mount', '-t', 'nfs', export_string, export_mnt_base)]
+ self.assertEqual(self.executes, expected_commands)
+
class CacheConcurrencyTestCase(test.TestCase):
def setUp(self):
diff --git a/nova/virt/libvirt/volume_nfs.py b/nova/virt/libvirt/volume_nfs.py
new file mode 100644
index 000000000..cf901b23a
--- /dev/null
+++ b/nova/virt/libvirt/volume_nfs.py
@@ -0,0 +1,96 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2012 NetApp, Inc.
+# 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.
+
+"""Volume driver for using NFS as volumes storage. Nova compute part."""
+
+import ctypes
+import os
+
+from nova import exception
+from nova import flags
+from nova.openstack.common import cfg
+from nova.openstack.common import log as logging
+from nova import utils
+from nova.virt.libvirt import volume
+
+LOG = logging.getLogger(__name__)
+
+volume_opts = [
+ cfg.StrOpt('nfs_mount_point_base',
+ default='$state_path/mnt',
+ help='Base dir where nfs expected to be mounted on compute'),
+]
+FLAGS = flags.FLAGS
+FLAGS.register_opts(volume_opts)
+
+
+class NfsVolumeDriver(volume.LibvirtVolumeDriver):
+ """ Class implements libvirt part of volume driver for NFS
+ """
+ def __init__(self, *args, **kwargs):
+ """Create back-end to nfs and check connection"""
+ super(NfsVolumeDriver, self).__init__(*args, **kwargs)
+
+ def connect_volume(self, connection_info, mount_device):
+ """Connect the volume. Returns xml for libvirt."""
+ path = self._ensure_mounted(connection_info['data']['export'])
+ path = os.path.join(path, connection_info['data']['name'])
+ connection_info['data']['device_path'] = path
+ conf = super(NfsVolumeDriver, self).connect_volume(connection_info,
+ mount_device)
+ conf.source_type = 'file'
+ return conf
+
+ def disconnect_volume(self, connection_info, mount_device):
+ """Disconnect the volume"""
+ pass
+
+ def _ensure_mounted(self, nfs_export):
+ """
+ @type nfs_export: string
+ """
+ mount_path = os.path.join(FLAGS.nfs_mount_point_base,
+ self.get_hash_str(nfs_export))
+ self._mount_nfs(mount_path, nfs_export, ensure=True)
+ return mount_path
+
+ def _mount_nfs(self, mount_path, nfs_share, ensure=False):
+ """Mount nfs export to mount path"""
+ if not self._path_exists(mount_path):
+ utils.execute('mkdir', '-p', mount_path)
+
+ try:
+ utils.execute('mount', '-t', 'nfs', nfs_share, mount_path,
+ run_as_root=True)
+ except exception.ProcessExecutionError as exc:
+ if ensure and 'already mounted' in exc.message:
+ LOG.warn(_("%s is already mounted"), nfs_share)
+ else:
+ raise
+
+ @staticmethod
+ def get_hash_str(base_str):
+ """returns string that represents hash of base_str (in a hex format)"""
+ return str(ctypes.c_uint64(hash(base_str)).value)
+
+ @staticmethod
+ def _path_exists(path):
+ """Check path """
+ try:
+ return utils.execute('stat', path, run_as_root=True)
+ except exception.ProcessExecutionError:
+ return False