summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/tests/test_libvirt.py26
-rw-r--r--nova/tests/test_virt_drivers.py5
-rw-r--r--nova/virt/libvirt.xml.template14
-rw-r--r--nova/virt/libvirt/connection.py47
4 files changed, 85 insertions, 7 deletions
diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py
index d388a1d4f..11aadc896 100644
--- a/nova/tests/test_libvirt.py
+++ b/nova/tests/test_libvirt.py
@@ -15,6 +15,7 @@
# under the License.
import copy
+import errno
import eventlet
import mox
import os
@@ -800,6 +801,19 @@ class LibvirtConnTestCase(test.TestCase):
(check(tree), expected_result, i))
def _check_xml_and_disk_driver(self, image_meta):
+ os_open = os.open
+ directio_supported = True
+
+ def os_open_stub(path, flags, *args, **kwargs):
+ if flags & os.O_DIRECT:
+ if not directio_supported:
+ raise OSError(errno.EINVAL,
+ '%s: %s' % (os.strerror(errno.EINVAL), path))
+ flags &= ~os.O_DIRECT
+ return os_open(path, flags, *args, **kwargs)
+
+ self.stubs.Set(os, 'open', os_open_stub)
+
user_context = context.RequestContext(self.user_id, self.project_id)
instance_ref = db.instance_create(user_context, self.test_instance)
network_info = _fake_network_info(self.stubs, 1)
@@ -812,6 +826,18 @@ class LibvirtConnTestCase(test.TestCase):
for disk in disks:
self.assertEqual(disk.get("cache"), "none")
+ directio_supported = False
+
+ # The O_DIRECT availability is cached on first use in
+ # LibvirtConnection, hence we re-create it here
+ xml = connection.LibvirtConnection(True).to_xml(instance_ref,
+ network_info,
+ image_meta)
+ tree = ElementTree.fromstring(xml)
+ disks = tree.findall('./devices/disk/driver')
+ for disk in disks:
+ self.assertEqual(disk.get("cache"), "writethrough")
+
def _check_xml_and_disk_bus(self, image_meta, device_type, bus):
user_context = context.RequestContext(self.user_id, self.project_id)
instance_ref = db.instance_create(user_context, self.test_instance)
diff --git a/nova/tests/test_virt_drivers.py b/nova/tests/test_virt_drivers.py
index 8018008a6..592926597 100644
--- a/nova/tests/test_virt_drivers.py
+++ b/nova/tests/test_virt_drivers.py
@@ -451,6 +451,11 @@ class LibvirtConnTestCase(_VirtDriverTestCase):
nova.virt.libvirt.connection.libvirt_utils = fake_libvirt_utils
nova.virt.libvirt.firewall.libvirt = fakelibvirt
+ # So that the _supports_direct_io does the test based
+ # on the current working directory, instead of the
+ # default instances_path which doesn't exist
+ FLAGS.instances_path = ''
+
# Point _VirtDriverTestCase at the right module
self.driver_module = nova.virt.libvirt.connection
super(LibvirtConnTestCase, self).setUp()
diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template
index b3cc75580..225133eb5 100644
--- a/nova/virt/libvirt.xml.template
+++ b/nova/virt/libvirt.xml.template
@@ -66,40 +66,40 @@
#else
#if $getVar('rescue', False)
<disk type='file'>
- <driver type='${driver_type}' cache='none'/>
+ <driver type='${driver_type}' cache='${cachemode}'/>
<source file='${basepath}/disk.rescue'/>
<target dev='${disk_prefix}a' bus='${ephemeral_disk_bus}'/>
</disk>
<disk type='file'>
- <driver type='${driver_type}' cache='none'/>
+ <driver type='${driver_type}' cache='${cachemode}'/>
<source file='${basepath}/disk'/>
<target dev='${disk_prefix}b' bus='${ephemeral_disk_bus}'/>
</disk>
#else
#if not ($getVar('ebs_root', False))
<disk type='file' device='${root_device_type}'>
- <driver type='${driver_type}' cache='none'/>
+ <driver type='${driver_type}' cache='${cachemode}'/>
<source file='${basepath}/disk'/>
<target dev='${root_device}' bus='${root_disk_bus}'/>
</disk>
#end if
#if $getVar('ephemeral_device', False)
<disk type='file'>
- <driver type='${driver_type}' cache='none'/>
+ <driver type='${driver_type}' cache='${cachemode}'/>
<source file='${basepath}/disk.local'/>
<target dev='${ephemeral_device}' bus='${ephemeral_disk_bus}'/>
</disk>
#end if
#for $eph in $ephemerals
<disk type='block'>
- <driver type='${driver_type}' cache='none'/>
+ <driver type='${driver_type}' cache='${cachemode}'/>
<source dev='${basepath}/${eph.device_path}'/>
<target dev='${eph.device}' bus='${ephemeral_disk_bus}'/>
</disk>
#end for
#if $getVar('swap_device', False)
<disk type='file'>
- <driver type='${driver_type}' cache='none'/>
+ <driver type='${driver_type}' cache='${cachemode}'/>
<source file='${basepath}/disk.swap'/>
<target dev='${swap_device}' bus='${ephemeral_disk_bus}'/>
</disk>
@@ -110,7 +110,7 @@
#end if
#if $getVar('config_drive', False)
<disk type='file'>
- <driver type='raw' />
+ <driver type='raw' cache='${cachemode}'/>
<source file='${basepath}/disk.config' />
<target dev='${disk_prefix}z' bus='${ephemeral_disk_bus}' />
</disk>
diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py
index 3aabfde13..f1e04d3f9 100644
--- a/nova/virt/libvirt/connection.py
+++ b/nova/virt/libvirt/connection.py
@@ -225,9 +225,25 @@ class LibvirtConnection(driver.ComputeDriver):
self.default_second_device = self._disk_prefix + 'b'
self.default_third_device = self._disk_prefix + 'c'
+ self._disk_cachemode = None
self.image_cache_manager = imagecache.ImageCacheManager()
@property
+ def disk_cachemode(self):
+ if self._disk_cachemode is None:
+ # We prefer 'none' for consistent performance, host crash
+ # safety & migration correctness by avoiding host page cache.
+ # Some filesystems (eg GlusterFS via FUSE) don't support
+ # O_DIRECT though. For those we fallback to 'writethrough'
+ # which gives host crash safety, and is safe for migration
+ # provided the filesystem is cache coherant (cluster filesystems
+ # typically are, but things like NFS are not).
+ self._disk_cachemode = "none"
+ if not self._supports_direct_io(FLAGS.instances_path):
+ self._disk_cachemode = "writethrough"
+ return self._disk_cachemode
+
+ @property
def host_state(self):
if not self._host_state:
self._host_state = HostState(self.read_only)
@@ -996,6 +1012,36 @@ class LibvirtConnection(driver.ComputeDriver):
return {'host': host, 'port': port, 'internal_access_path': None}
@staticmethod
+ def _supports_direct_io(dirpath):
+ testfile = os.path.join(dirpath, ".directio.test")
+ hasDirectIO = True
+ try:
+ f = os.open(testfile, os.O_CREAT | os.O_WRONLY | os.O_DIRECT)
+ os.close(f)
+ LOG.debug(_("Path '%(path)s' supports direct I/O") %
+ {'path': dirpath})
+ except OSError, e:
+ if e.errno == errno.EINVAL:
+ LOG.debug(_("Path '%(path)s' does not support direct I/O: "
+ "'%(ex)s'") % {'path': dirpath, 'ex': str(e)})
+ hasDirectIO = False
+ else:
+ LOG.error(_("Error on '%(path)s' while checking direct I/O: "
+ "'%(ex)s'") % {'path': dirpath, 'ex': str(e)})
+ raise e
+ except Exception, e:
+ LOG.error(_("Error on '%(path)s' while checking direct I/O: "
+ "'%(ex)s'") % {'path': dirpath, 'ex': str(e)})
+ raise e
+ finally:
+ try:
+ os.unlink(testfile)
+ except:
+ pass
+
+ return hasDirectIO
+
+ @staticmethod
def _cache_image(fn, target, fname, cow=False, size=None, *args, **kwargs):
"""Wrapper for a method that creates an image that caches the image.
@@ -1374,6 +1420,7 @@ class LibvirtConnection(driver.ComputeDriver):
xml_info = {'type': FLAGS.libvirt_type,
'name': instance['name'],
'uuid': instance['uuid'],
+ 'cachemode': self.disk_cachemode,
'basepath': os.path.join(FLAGS.instances_path,
instance['name']),
'memory_kb': inst_type['memory_mb'] * 1024,