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:
parent
f478fa6967
commit
2ccbfd8f52
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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',
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user