diff options
| author | Jenkins <jenkins@review.openstack.org> | 2012-08-15 07:55:57 +0000 |
|---|---|---|
| committer | Gerrit Code Review <review@openstack.org> | 2012-08-15 07:55:57 +0000 |
| commit | efdc248d15cf2a0308b215dccb90d0b7da6926b7 (patch) | |
| tree | 0faad2bacdfd36ecfd3577f51597e3155c0c4131 | |
| parent | 9f9f1a7aa46fad83fdda36be15d648dfa21cba2f (diff) | |
| parent | 6972cd1e7eb1ec51f9ab4d94a842ebc45b655cc5 (diff) | |
| download | nova-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.py | 26 | ||||
| -rw-r--r-- | nova/virt/libvirt/volume_nfs.py | 96 |
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 |
