From 8cf0178165dec3eac002159fd01af6930322bebb Mon Sep 17 00:00:00 2001 From: Eric Harney Date: Fri, 1 Feb 2013 15:08:27 -0500 Subject: Add GlusterFS libvirt volume connector This is to support GlusterFS as a storage backend for Cinder, which works similarly to the NFS backend. Change-Id: I047d58ba74d27273ba8f2639bcc77999f8be0f30 --- nova/tests/test_libvirt_volume.py | 28 ++++++++++++++++++ nova/virt/libvirt/driver.py | 4 ++- nova/virt/libvirt/volume.py | 62 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 92 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_libvirt_volume.py b/nova/tests/test_libvirt_volume.py index c0fa53506..0098215b2 100644 --- a/nova/tests/test_libvirt_volume.py +++ b/nova/tests/test_libvirt_volume.py @@ -392,3 +392,31 @@ class LibvirtVolumeTestCase(test.TestCase): self.assertEqual(tree.get('type'), 'block') self.assertEqual(tree.find('./source').get('dev'), aoedevpath) libvirt_driver.disconnect_volume(connection_info, "vde") + + def test_libvirt_glusterfs_driver(self): + mnt_base = '/mnt' + self.flags(glusterfs_mount_point_base=mnt_base) + + libvirt_driver = volume.LibvirtGlusterfsVolumeDriver(self.fake_conn) + export_string = '192.168.1.1:/volume-00001' + 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}} + disk_info = { + "bus": "virtio", + "dev": "vde", + "type": "disk", + } + conf = libvirt_driver.connect_volume(connection_info, disk_info) + 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, "vde") + + expected_commands = [ + ('stat', export_mnt_base), + ('mount', '-t', 'glusterfs', export_string, export_mnt_base)] + self.assertEqual(self.executes, expected_commands) diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index bdb875ed3..4e2fb9d39 100755 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -152,7 +152,9 @@ libvirt_opts = [ 'rbd=nova.virt.libvirt.volume.LibvirtNetVolumeDriver', 'sheepdog=nova.virt.libvirt.volume.LibvirtNetVolumeDriver', 'nfs=nova.virt.libvirt.volume.LibvirtNFSVolumeDriver', - 'aoe=nova.virt.libvirt.volume.LibvirtAOEVolumeDriver' + 'aoe=nova.virt.libvirt.volume.LibvirtAOEVolumeDriver', + 'glusterfs=' + 'nova.virt.libvirt.volume.LibvirtGlusterfsVolumeDriver' ], help='Libvirt handlers for remote volumes.'), cfg.StrOpt('libvirt_disk_prefix', diff --git a/nova/virt/libvirt/volume.py b/nova/virt/libvirt/volume.py index 9ec8f6bcb..c368f66f6 100644 --- a/nova/virt/libvirt/volume.py +++ b/nova/virt/libvirt/volume.py @@ -52,7 +52,11 @@ volume_opts = [ 'of the nfs man page for details'), cfg.StrOpt('num_aoe_discover_tries', default=3, - help='number of times to rediscover AoE target to find volume') + help='number of times to rediscover AoE target to find volume'), + cfg.StrOpt('glusterfs_mount_point_base', + default=paths.state_path_def('mnt'), + help='Dir where the glusterfs volume is mounted on the ' + 'compute node'), ] CONF = cfg.CONF @@ -390,3 +394,59 @@ class LibvirtAOEVolumeDriver(LibvirtBaseVolumeDriver): conf.source_type = "block" conf.source_path = aoedevpath return conf + + +class LibvirtGlusterfsVolumeDriver(LibvirtBaseVolumeDriver): + """Class implements libvirt part of volume driver for GlusterFS.""" + + def __init__(self, connection): + """Create back-end to glusterfs.""" + super(LibvirtGlusterfsVolumeDriver, + self).__init__(connection, is_block_dev=False) + + def connect_volume(self, connection_info, mount_device): + """Connect the volume. Returns xml for libvirt.""" + conf = super(LibvirtGlusterfsVolumeDriver, + self).connect_volume(connection_info, mount_device) + path = self._ensure_mounted(connection_info['data']['export']) + path = os.path.join(path, connection_info['data']['name']) + conf.source_type = 'file' + conf.source_path = path + return conf + + def _ensure_mounted(self, glusterfs_export): + """ + @type glusterfs_export: string + """ + mount_path = os.path.join(CONF.glusterfs_mount_point_base, + self.get_hash_str(glusterfs_export)) + self._mount_glusterfs(mount_path, glusterfs_export, ensure=True) + return mount_path + + def _mount_glusterfs(self, mount_path, glusterfs_share, ensure=False): + """Mount glusterfs export to mount path.""" + if not self._path_exists(mount_path): + utils.execute('mkdir', '-p', mount_path) + + try: + utils.execute('mount', '-t', 'glusterfs', glusterfs_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"), glusterfs_share) + else: + raise + + @staticmethod + def get_hash_str(base_str): + """returns string that represents hash of base_str (in hex format).""" + return hashlib.md5(base_str).hexdigest() + + @staticmethod + def _path_exists(path): + """Check path.""" + try: + return utils.execute('stat', path, run_as_root=True) + except exception.ProcessExecutionError: + return False -- cgit