diff options
-rw-r--r-- | etc/nova/rootwrap.d/compute.filters | 1 | ||||
-rw-r--r-- | nova/tests/test_libvirt_volume.py | 41 | ||||
-rwxr-xr-x | nova/virt/libvirt/driver.py | 4 | ||||
-rw-r--r-- | nova/virt/libvirt/volume.py | 79 |
4 files changed, 124 insertions, 1 deletions
diff --git a/etc/nova/rootwrap.d/compute.filters b/etc/nova/rootwrap.d/compute.filters index 00b73d831..f0d9c6fee 100644 --- a/etc/nova/rootwrap.d/compute.filters +++ b/etc/nova/rootwrap.d/compute.filters @@ -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 diff --git a/nova/tests/test_libvirt_volume.py b/nova/tests/test_libvirt_volume.py index 7945329f8..28d0c8088 100644 --- a/nova/tests/test_libvirt_volume.py +++ b/nova/tests/test_libvirt_volume.py @@ -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) diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 6f1cfef6f..57e364115 100755 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -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', diff --git a/nova/virt/libvirt/volume.py b/nova/virt/libvirt/volume.py index a5bd2dd43..577cfee2c 100644 --- a/nova/virt/libvirt/volume.py +++ b/nova/virt/libvirt/volume.py @@ -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) |