Add a volume driver in Nova for Scality SOFS

Scality SOFS is a network filesystem mounted with FUSE, with most
options given in a configuration file. Given a mount point and a SOFS
configuration file as driver options, the Scality volume driver mounts
SOFS, and then creates, accesses and deletes volumes as regular
(sparse) files on SOFS.

Change-Id: I84bf268e5a2c5c33b8706830e8067914fae44aed
Implements: blueprint scality-volume-driver
This commit is contained in:
Jean-Marc Saffroy 2013-01-15 00:27:42 +01:00 committed by Vishvananda Ishaya
parent f478fa6967
commit 2ccbfd8f52
4 changed files with 124 additions and 1 deletions

View File

@ -14,6 +14,7 @@ tune2fs: CommandFilter, /sbin/tune2fs, root
# nova/virt/disk/api.py: 'mount', '-o', 'bind', src, target
# nova/virt/xenapi/vm_utils.py: 'mount', '-t', 'ext2,ext3,ext4,reiserfs'..
# nova/virt/configdrive.py: 'mount', device, mountdir
# nova/virt/libvirt/volume.py: 'mount', '-t', 'sofs' ...
mount: CommandFilter, /bin/mount, root
# nova/virt/disk/mount/api.py: 'umount', mapped_device

View File

@ -15,6 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import fixtures
import os
from oslo.config import cfg
@ -547,3 +548,43 @@ class LibvirtVolumeTestCase(test.TestCase):
"/0000:05:00.3/0000:06:00.6/host2/fc_host/host2"}
pci_num = libvirt_driver._get_pci_num(hba)
self.assertEqual("0000:06:00.6", pci_num)
def test_libvirt_scality_driver(self):
tempdir = self.useFixture(fixtures.TempDir()).path
TEST_MOUNT = os.path.join(tempdir, 'fake_mount')
TEST_CONFIG = os.path.join(tempdir, 'fake_config')
TEST_VOLDIR = 'volumes'
TEST_VOLNAME = 'volume_name'
TEST_CONN_INFO = {
'data': {
'sofs_path': os.path.join(TEST_VOLDIR, TEST_VOLNAME)
}
}
TEST_VOLPATH = os.path.join(TEST_MOUNT,
TEST_VOLDIR,
TEST_VOLNAME)
TEST_DISK_INFO = {
"bus": "virtio",
"dev": "vde",
"type": "disk",
}
open(TEST_CONFIG, "w+").close()
os.makedirs(os.path.join(TEST_MOUNT, 'sys'))
def _access_wrapper(path, flags):
if path == '/sbin/mount.sofs':
return True
else:
return os.access(path, flags)
self.stubs.Set(os, 'access', _access_wrapper)
self.flags(scality_sofs_config=TEST_CONFIG,
scality_sofs_mount_point=TEST_MOUNT)
driver = volume.LibvirtScalityVolumeDriver(self.fake_conn)
conf = driver.connect_volume(TEST_CONN_INFO, TEST_DISK_INFO)
tree = conf.format_dom()
self.assertEqual(tree.get('type'), 'file')
self.assertEqual(tree.find('./source').get('file'), TEST_VOLPATH)

View File

@ -166,7 +166,9 @@ libvirt_opts = [
'glusterfs='
'nova.virt.libvirt.volume.LibvirtGlusterfsVolumeDriver',
'fibre_channel=nova.virt.libvirt.volume.'
'LibvirtFibreChannelVolumeDriver'
'LibvirtFibreChannelVolumeDriver',
'scality='
'nova.virt.libvirt.volume.LibvirtScalityVolumeDriver',
],
help='Libvirt handlers for remote volumes.'),
cfg.StrOpt('libvirt_disk_prefix',

View File

@ -21,6 +21,8 @@
import hashlib
import os
import time
import urllib2
import urlparse
from oslo.config import cfg
@ -63,6 +65,12 @@ volume_opts = [
cfg.BoolOpt('libvirt_iscsi_use_multipath',
default=False,
help='use multipath connection of the iSCSI volume'),
cfg.StrOpt('scality_sofs_config',
default=None,
help='Path or URL to Scality SOFS configuration file'),
cfg.StrOpt('scality_sofs_mount_point',
default='$state_path/scality',
help='Base dir where Scality SOFS shall be mounted'),
]
CONF = cfg.CONF
@ -749,3 +757,74 @@ class LibvirtFibreChannelVolumeDriver(LibvirtBaseVolumeDriver):
# all of them
for device in devices:
linuxscsi.remove_device(device)
class LibvirtScalityVolumeDriver(LibvirtBaseVolumeDriver):
"""Scality SOFS Nova driver. Provide hypervisors with access
to sparse files on SOFS. """
def __init__(self, connection):
"""Create back-end to SOFS and check connection."""
super(LibvirtScalityVolumeDriver,
self).__init__(connection, is_block_dev=False)
def connect_volume(self, connection_info, disk_info):
"""Connect the volume. Returns xml for libvirt."""
self._check_prerequisites()
self._mount_sofs()
conf = super(LibvirtScalityVolumeDriver,
self).connect_volume(connection_info, disk_info)
path = os.path.join(CONF.scality_sofs_mount_point,
connection_info['data']['sofs_path'])
conf.source_type = 'file'
conf.source_path = path
# The default driver cache policy is 'none', and this causes
# qemu/kvm to open the volume file with O_DIRECT, which is
# rejected by FUSE (on kernels older than 3.3). Scality SOFS
# is FUSE based, so we must provide a more sensible default.
conf.driver_cache = 'writethrough'
return conf
def _check_prerequisites(self):
"""Sanity checks before attempting to mount SOFS."""
# config is mandatory
config = CONF.scality_sofs_config
if not config:
msg = _("Value required for 'scality_sofs_config'")
LOG.warn(msg)
raise exception.NovaException(msg)
# config can be a file path or a URL, check it
if urlparse.urlparse(config).scheme == '':
# turn local path into URL
config = 'file://%s' % config
try:
urllib2.urlopen(config, timeout=5).close()
except urllib2.URLError as e:
msg = _("Cannot access 'scality_sofs_config': %s") % e
LOG.warn(msg)
raise exception.NovaException(msg)
# mount.sofs must be installed
if not os.access('/sbin/mount.sofs', os.X_OK):
msg = _("Cannot execute /sbin/mount.sofs")
LOG.warn(msg)
raise exception.NovaException(msg)
def _mount_sofs(self):
config = CONF.scality_sofs_config
mount_path = CONF.scality_sofs_mount_point
sysdir = os.path.join(mount_path, 'sys')
if not os.path.isdir(mount_path):
utils.execute('mkdir', '-p', mount_path)
if not os.path.isdir(sysdir):
utils.execute('mount', '-t', 'sofs', config, mount_path,
run_as_root=True)
if not os.path.isdir(sysdir):
msg = _("Cannot mount Scality SOFS, check syslog for errors")
LOG.warn(msg)
raise exception.NovaException(msg)