Merge "Move image conversion to privsep."
This commit is contained in:
commit
6cab2f9ef4
59
nova/privsep/qemu.py
Normal file
59
nova/privsep/qemu.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# Copyright 2018 Michael Still and Aptira
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Helpers for qemu tasks.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from oslo_concurrency import processutils
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
import nova.privsep.utils
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||||
|
def convert_image(source, dest, in_format, out_format, instances_path):
|
||||||
|
unprivileged_convert_image(source, dest, in_format, out_format,
|
||||||
|
instances_path)
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE(mikal): this method is deliberately not wrapped in a privsep entrypoint
|
||||||
|
def unprivileged_convert_image(source, dest, in_format, out_format,
|
||||||
|
instances_path):
|
||||||
|
# NOTE(mdbooth): qemu-img convert defaults to cache=unsafe, which means
|
||||||
|
# that data is not synced to disk at completion. We explicitly use
|
||||||
|
# cache=none here to (1) ensure that we don't interfere with other
|
||||||
|
# applications using the host's io cache, and (2) ensure that the data is
|
||||||
|
# on persistent storage when the command exits. Without (2), a host crash
|
||||||
|
# may leave a corrupt image in the image cache, which Nova cannot recover
|
||||||
|
# automatically.
|
||||||
|
# NOTE(zigo): we cannot use -t none if the instances dir is mounted on a
|
||||||
|
# filesystem that doesn't have support for O_DIRECT, which is the case
|
||||||
|
# for example with tmpfs. This simply crashes "openstack server create"
|
||||||
|
# in environments like live distributions. In such case, the best choice
|
||||||
|
# is writethrough, which is power-failure safe, but still faster than
|
||||||
|
# writeback.
|
||||||
|
if nova.privsep.utils.supports_direct_io(instances_path):
|
||||||
|
cache_mode = 'none'
|
||||||
|
else:
|
||||||
|
cache_mode = 'writethrough'
|
||||||
|
cmd = ('qemu-img', 'convert', '-t', cache_mode, '-O', out_format)
|
||||||
|
|
||||||
|
if in_format is not None:
|
||||||
|
cmd = cmd + ('-f', in_format)
|
||||||
|
cmd = cmd + (source, dest)
|
||||||
|
processutils.execute(*cmd)
|
78
nova/privsep/utils.py
Normal file
78
nova/privsep/utils.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# Copyright 2010 United States Government as represented by the
|
||||||
|
# Administrator of the National Aeronautics and Space Administration.
|
||||||
|
# Copyright 2011 Justin Santa Barbara
|
||||||
|
# Copyright 2018 Michael Still and Aptira
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
# This module is utility methods that privsep depends on. Privsep isn't allowed
|
||||||
|
# to depend on anything outside the privsep directory, so these need to be
|
||||||
|
# here. That said, other parts of nova can call into these utilities if
|
||||||
|
# needed.
|
||||||
|
|
||||||
|
import errno
|
||||||
|
import mmap
|
||||||
|
import os
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
from oslo_utils import excutils
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def supports_direct_io(dirpath):
|
||||||
|
|
||||||
|
if not hasattr(os, 'O_DIRECT'):
|
||||||
|
LOG.debug("This python runtime does not support direct I/O")
|
||||||
|
return False
|
||||||
|
|
||||||
|
testfile = os.path.join(dirpath, ".directio.test")
|
||||||
|
|
||||||
|
hasDirectIO = True
|
||||||
|
fd = None
|
||||||
|
try:
|
||||||
|
fd = os.open(testfile, os.O_CREAT | os.O_WRONLY | os.O_DIRECT)
|
||||||
|
# Check is the write allowed with 512 byte alignment
|
||||||
|
align_size = 512
|
||||||
|
m = mmap.mmap(-1, align_size)
|
||||||
|
m.write(b"x" * align_size)
|
||||||
|
os.write(fd, m)
|
||||||
|
LOG.debug("Path '%(path)s' supports direct I/O",
|
||||||
|
{'path': dirpath})
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno == errno.EINVAL:
|
||||||
|
LOG.debug("Path '%(path)s' does not support direct I/O: "
|
||||||
|
"'%(ex)s'", {'path': dirpath, 'ex': e})
|
||||||
|
hasDirectIO = False
|
||||||
|
else:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.error("Error on '%(path)s' while checking "
|
||||||
|
"direct I/O: '%(ex)s'",
|
||||||
|
{'path': dirpath, 'ex': e})
|
||||||
|
except Exception as e:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.error("Error on '%(path)s' while checking direct I/O: "
|
||||||
|
"'%(ex)s'", {'path': dirpath, 'ex': e})
|
||||||
|
finally:
|
||||||
|
# ensure unlink(filepath) will actually remove the file by deleting
|
||||||
|
# the remaining link to it in close(fd)
|
||||||
|
if fd is not None:
|
||||||
|
os.close(fd)
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.unlink(testfile)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return hasDirectIO
|
111
nova/tests/unit/privsep/test_utils.py
Normal file
111
nova/tests/unit/privsep/test_utils.py
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
# Copyright 2011 Justin Santa Barbara
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import errno
|
||||||
|
import mock
|
||||||
|
import os
|
||||||
|
|
||||||
|
import nova.privsep.utils
|
||||||
|
from nova import test
|
||||||
|
|
||||||
|
|
||||||
|
class SupportDirectIOTestCase(test.NoDBTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(SupportDirectIOTestCase, self).setUp()
|
||||||
|
# O_DIRECT is not supported on all Python runtimes, so on platforms
|
||||||
|
# where it's not supported (e.g. Mac), we can still test the code-path
|
||||||
|
# by stubbing out the value.
|
||||||
|
if not hasattr(os, 'O_DIRECT'):
|
||||||
|
# `mock` seems to have trouble stubbing an attr that doesn't
|
||||||
|
# originally exist, so falling back to stubbing out the attribute
|
||||||
|
# directly.
|
||||||
|
os.O_DIRECT = 16384
|
||||||
|
self.addCleanup(delattr, os, 'O_DIRECT')
|
||||||
|
self.einval = OSError()
|
||||||
|
self.einval.errno = errno.EINVAL
|
||||||
|
self.test_path = os.path.join('.', '.directio.test')
|
||||||
|
self.io_flags = os.O_CREAT | os.O_WRONLY | os.O_DIRECT
|
||||||
|
|
||||||
|
open_patcher = mock.patch('os.open')
|
||||||
|
write_patcher = mock.patch('os.write')
|
||||||
|
close_patcher = mock.patch('os.close')
|
||||||
|
unlink_patcher = mock.patch('os.unlink')
|
||||||
|
self.addCleanup(open_patcher.stop)
|
||||||
|
self.addCleanup(write_patcher.stop)
|
||||||
|
self.addCleanup(close_patcher.stop)
|
||||||
|
self.addCleanup(unlink_patcher.stop)
|
||||||
|
self.mock_open = open_patcher.start()
|
||||||
|
self.mock_write = write_patcher.start()
|
||||||
|
self.mock_close = close_patcher.start()
|
||||||
|
self.mock_unlink = unlink_patcher.start()
|
||||||
|
|
||||||
|
def test_supports_direct_io(self):
|
||||||
|
self.mock_open.return_value = 3
|
||||||
|
|
||||||
|
self.assertTrue(nova.privsep.utils.supports_direct_io('.'))
|
||||||
|
|
||||||
|
self.mock_open.assert_called_once_with(self.test_path, self.io_flags)
|
||||||
|
self.mock_write.assert_called_once_with(3, mock.ANY)
|
||||||
|
# ensure unlink(filepath) will actually remove the file by deleting
|
||||||
|
# the remaining link to it in close(fd)
|
||||||
|
self.mock_close.assert_called_once_with(3)
|
||||||
|
self.mock_unlink.assert_called_once_with(self.test_path)
|
||||||
|
|
||||||
|
def test_supports_direct_io_with_exception_in_write(self):
|
||||||
|
self.mock_open.return_value = 3
|
||||||
|
self.mock_write.side_effect = ValueError()
|
||||||
|
|
||||||
|
self.assertRaises(ValueError, nova.privsep.utils.supports_direct_io,
|
||||||
|
'.')
|
||||||
|
|
||||||
|
self.mock_open.assert_called_once_with(self.test_path, self.io_flags)
|
||||||
|
self.mock_write.assert_called_once_with(3, mock.ANY)
|
||||||
|
# ensure unlink(filepath) will actually remove the file by deleting
|
||||||
|
# the remaining link to it in close(fd)
|
||||||
|
self.mock_close.assert_called_once_with(3)
|
||||||
|
self.mock_unlink.assert_called_once_with(self.test_path)
|
||||||
|
|
||||||
|
def test_supports_direct_io_with_exception_in_open(self):
|
||||||
|
self.mock_open.side_effect = ValueError()
|
||||||
|
|
||||||
|
self.assertRaises(ValueError, nova.privsep.utils.supports_direct_io,
|
||||||
|
'.')
|
||||||
|
|
||||||
|
self.mock_open.assert_called_once_with(self.test_path, self.io_flags)
|
||||||
|
self.mock_write.assert_not_called()
|
||||||
|
self.mock_close.assert_not_called()
|
||||||
|
self.mock_unlink.assert_called_once_with(self.test_path)
|
||||||
|
|
||||||
|
def test_supports_direct_io_with_oserror_in_write(self):
|
||||||
|
self.mock_open.return_value = 3
|
||||||
|
self.mock_write.side_effect = self.einval
|
||||||
|
|
||||||
|
self.assertFalse(nova.privsep.utils.supports_direct_io('.'))
|
||||||
|
|
||||||
|
self.mock_open.assert_called_once_with(self.test_path, self.io_flags)
|
||||||
|
self.mock_write.assert_called_once_with(3, mock.ANY)
|
||||||
|
# ensure unlink(filepath) will actually remove the file by deleting
|
||||||
|
# the remaining link to it in close(fd)
|
||||||
|
self.mock_close.assert_called_once_with(3)
|
||||||
|
self.mock_unlink.assert_called_once_with(self.test_path)
|
||||||
|
|
||||||
|
def test_supports_direct_io_with_oserror_in_open(self):
|
||||||
|
self.mock_open.side_effect = self.einval
|
||||||
|
|
||||||
|
self.assertFalse(nova.privsep.utils.supports_direct_io('.'))
|
||||||
|
|
||||||
|
self.mock_open.assert_called_once_with(self.test_path, self.io_flags)
|
||||||
|
self.mock_write.assert_not_called()
|
||||||
|
self.mock_close.assert_not_called()
|
||||||
|
self.mock_unlink.assert_called_once_with(self.test_path)
|
@ -13,7 +13,6 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import errno
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import importlib
|
import importlib
|
||||||
import os
|
import os
|
||||||
@ -1392,93 +1391,3 @@ class GetEndpointTestCase(test.NoDBTestCase):
|
|||||||
self.adap.get_endpoint_data.assert_not_called()
|
self.adap.get_endpoint_data.assert_not_called()
|
||||||
self.assertEqual(3, self.adap.get_endpoint.call_count)
|
self.assertEqual(3, self.adap.get_endpoint.call_count)
|
||||||
self.assertEqual('public', self.adap.interface)
|
self.assertEqual('public', self.adap.interface)
|
||||||
|
|
||||||
|
|
||||||
class SupportDirectIOTestCase(test.NoDBTestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(SupportDirectIOTestCase, self).setUp()
|
|
||||||
# O_DIRECT is not supported on all Python runtimes, so on platforms
|
|
||||||
# where it's not supported (e.g. Mac), we can still test the code-path
|
|
||||||
# by stubbing out the value.
|
|
||||||
if not hasattr(os, 'O_DIRECT'):
|
|
||||||
# `mock` seems to have trouble stubbing an attr that doesn't
|
|
||||||
# originally exist, so falling back to stubbing out the attribute
|
|
||||||
# directly.
|
|
||||||
os.O_DIRECT = 16384
|
|
||||||
self.addCleanup(delattr, os, 'O_DIRECT')
|
|
||||||
self.einval = OSError()
|
|
||||||
self.einval.errno = errno.EINVAL
|
|
||||||
self.test_path = os.path.join('.', '.directio.test')
|
|
||||||
self.io_flags = os.O_CREAT | os.O_WRONLY | os.O_DIRECT
|
|
||||||
|
|
||||||
open_patcher = mock.patch('os.open')
|
|
||||||
write_patcher = mock.patch('os.write')
|
|
||||||
close_patcher = mock.patch('os.close')
|
|
||||||
unlink_patcher = mock.patch('os.unlink')
|
|
||||||
self.addCleanup(open_patcher.stop)
|
|
||||||
self.addCleanup(write_patcher.stop)
|
|
||||||
self.addCleanup(close_patcher.stop)
|
|
||||||
self.addCleanup(unlink_patcher.stop)
|
|
||||||
self.mock_open = open_patcher.start()
|
|
||||||
self.mock_write = write_patcher.start()
|
|
||||||
self.mock_close = close_patcher.start()
|
|
||||||
self.mock_unlink = unlink_patcher.start()
|
|
||||||
|
|
||||||
def test_supports_direct_io(self):
|
|
||||||
self.mock_open.return_value = 3
|
|
||||||
|
|
||||||
self.assertTrue(utils.supports_direct_io('.'))
|
|
||||||
|
|
||||||
self.mock_open.assert_called_once_with(self.test_path, self.io_flags)
|
|
||||||
self.mock_write.assert_called_once_with(3, mock.ANY)
|
|
||||||
# ensure unlink(filepath) will actually remove the file by deleting
|
|
||||||
# the remaining link to it in close(fd)
|
|
||||||
self.mock_close.assert_called_once_with(3)
|
|
||||||
self.mock_unlink.assert_called_once_with(self.test_path)
|
|
||||||
|
|
||||||
def test_supports_direct_io_with_exception_in_write(self):
|
|
||||||
self.mock_open.return_value = 3
|
|
||||||
self.mock_write.side_effect = ValueError()
|
|
||||||
|
|
||||||
self.assertRaises(ValueError, utils.supports_direct_io, '.')
|
|
||||||
|
|
||||||
self.mock_open.assert_called_once_with(self.test_path, self.io_flags)
|
|
||||||
self.mock_write.assert_called_once_with(3, mock.ANY)
|
|
||||||
# ensure unlink(filepath) will actually remove the file by deleting
|
|
||||||
# the remaining link to it in close(fd)
|
|
||||||
self.mock_close.assert_called_once_with(3)
|
|
||||||
self.mock_unlink.assert_called_once_with(self.test_path)
|
|
||||||
|
|
||||||
def test_supports_direct_io_with_exception_in_open(self):
|
|
||||||
self.mock_open.side_effect = ValueError()
|
|
||||||
|
|
||||||
self.assertRaises(ValueError, utils.supports_direct_io, '.')
|
|
||||||
|
|
||||||
self.mock_open.assert_called_once_with(self.test_path, self.io_flags)
|
|
||||||
self.mock_write.assert_not_called()
|
|
||||||
self.mock_close.assert_not_called()
|
|
||||||
self.mock_unlink.assert_called_once_with(self.test_path)
|
|
||||||
|
|
||||||
def test_supports_direct_io_with_oserror_in_write(self):
|
|
||||||
self.mock_open.return_value = 3
|
|
||||||
self.mock_write.side_effect = self.einval
|
|
||||||
|
|
||||||
self.assertFalse(utils.supports_direct_io('.'))
|
|
||||||
|
|
||||||
self.mock_open.assert_called_once_with(self.test_path, self.io_flags)
|
|
||||||
self.mock_write.assert_called_once_with(3, mock.ANY)
|
|
||||||
# ensure unlink(filepath) will actually remove the file by deleting
|
|
||||||
# the remaining link to it in close(fd)
|
|
||||||
self.mock_close.assert_called_once_with(3)
|
|
||||||
self.mock_unlink.assert_called_once_with(self.test_path)
|
|
||||||
|
|
||||||
def test_supports_direct_io_with_oserror_in_open(self):
|
|
||||||
self.mock_open.side_effect = self.einval
|
|
||||||
|
|
||||||
self.assertFalse(utils.supports_direct_io('.'))
|
|
||||||
|
|
||||||
self.mock_open.assert_called_once_with(self.test_path, self.io_flags)
|
|
||||||
self.mock_write.assert_not_called()
|
|
||||||
self.mock_close.assert_not_called()
|
|
||||||
self.mock_unlink.assert_called_once_with(self.test_path)
|
|
||||||
|
@ -7988,7 +7988,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
|||||||
def connection_supports_direct_io_stub(dirpath):
|
def connection_supports_direct_io_stub(dirpath):
|
||||||
return directio_supported
|
return directio_supported
|
||||||
|
|
||||||
self.stub_out('nova.utils.supports_direct_io',
|
self.stub_out('nova.privsep.utils.supports_direct_io',
|
||||||
connection_supports_direct_io_stub)
|
connection_supports_direct_io_stub)
|
||||||
|
|
||||||
instance_ref = objects.Instance(**self.test_instance)
|
instance_ref = objects.Instance(**self.test_instance)
|
||||||
@ -17851,7 +17851,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
|
|||||||
# get_guest_xml().
|
# get_guest_xml().
|
||||||
@mock.patch.object(libvirt_driver.LibvirtDriver, '_set_host_enabled')
|
@mock.patch.object(libvirt_driver.LibvirtDriver, '_set_host_enabled')
|
||||||
@mock.patch.object(libvirt_driver.LibvirtDriver, '_build_device_metadata')
|
@mock.patch.object(libvirt_driver.LibvirtDriver, '_build_device_metadata')
|
||||||
@mock.patch('nova.utils.supports_direct_io')
|
@mock.patch('nova.privsep.utils.supports_direct_io')
|
||||||
@mock.patch('nova.api.metadata.base.InstanceMetadata')
|
@mock.patch('nova.api.metadata.base.InstanceMetadata')
|
||||||
def _test_finish_migration(self, mock_instance_metadata,
|
def _test_finish_migration(self, mock_instance_metadata,
|
||||||
mock_supports_direct_io,
|
mock_supports_direct_io,
|
||||||
@ -18795,7 +18795,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
|
|||||||
# get_guest_xml().
|
# get_guest_xml().
|
||||||
@mock.patch.object(libvirt_driver.LibvirtDriver, '_set_host_enabled')
|
@mock.patch.object(libvirt_driver.LibvirtDriver, '_set_host_enabled')
|
||||||
@mock.patch.object(libvirt_driver.LibvirtDriver, '_build_device_metadata')
|
@mock.patch.object(libvirt_driver.LibvirtDriver, '_build_device_metadata')
|
||||||
@mock.patch('nova.utils.supports_direct_io')
|
@mock.patch('nova.privsep.utils.supports_direct_io')
|
||||||
@mock.patch('nova.api.metadata.base.InstanceMetadata')
|
@mock.patch('nova.api.metadata.base.InstanceMetadata')
|
||||||
def _test_rescue(self, instance,
|
def _test_rescue(self, instance,
|
||||||
mock_instance_metadata, mock_supports_direct_io,
|
mock_instance_metadata, mock_supports_direct_io,
|
||||||
|
@ -670,16 +670,14 @@ class LvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
|||||||
self.LV = '%s_%s' % (self.INSTANCE['uuid'], self.NAME)
|
self.LV = '%s_%s' % (self.INSTANCE['uuid'], self.NAME)
|
||||||
self.PATH = os.path.join('/dev', self.VG, self.LV)
|
self.PATH = os.path.join('/dev', self.VG, self.LV)
|
||||||
|
|
||||||
@mock.patch('nova.utils.supports_direct_io', return_value=True)
|
@mock.patch('nova.privsep.utils.supports_direct_io', return_value=True)
|
||||||
@mock.patch.object(imagebackend.lvm, 'create_volume')
|
@mock.patch.object(imagebackend.lvm, 'create_volume')
|
||||||
@mock.patch.object(imagebackend.disk, 'get_disk_size',
|
@mock.patch.object(imagebackend.disk, 'get_disk_size',
|
||||||
return_value=TEMPLATE_SIZE)
|
return_value=TEMPLATE_SIZE)
|
||||||
@mock.patch.object(imagebackend.utils, 'execute')
|
@mock.patch('nova.privsep.qemu.convert_image')
|
||||||
def _create_image(self, sparse, mock_execute, mock_get, mock_create,
|
def _create_image(self, sparse, mock_convert_image, mock_get, mock_create,
|
||||||
mock_ignored):
|
mock_ignored):
|
||||||
fn = mock.MagicMock()
|
fn = mock.MagicMock()
|
||||||
cmd = ('qemu-img', 'convert', '-t', 'none', '-O', 'raw',
|
|
||||||
self.TEMPLATE_PATH, self.PATH)
|
|
||||||
|
|
||||||
image = self.image_class(self.INSTANCE, self.NAME)
|
image = self.image_class(self.INSTANCE, self.NAME)
|
||||||
|
|
||||||
@ -691,7 +689,9 @@ class LvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
|||||||
|
|
||||||
fn.assert_called_once_with(target=self.TEMPLATE_PATH)
|
fn.assert_called_once_with(target=self.TEMPLATE_PATH)
|
||||||
mock_get.assert_called_once_with(self.TEMPLATE_PATH)
|
mock_get.assert_called_once_with(self.TEMPLATE_PATH)
|
||||||
mock_execute.assert_called_once_with(*cmd, run_as_root=True)
|
path = '/dev/%s/%s_%s' % (self.VG, self.INSTANCE.uuid, self.NAME)
|
||||||
|
mock_convert_image.assert_called_once_with(
|
||||||
|
self.TEMPLATE_PATH, path, None, 'raw', CONF.instances_path)
|
||||||
|
|
||||||
@mock.patch.object(imagebackend.lvm, 'create_volume')
|
@mock.patch.object(imagebackend.lvm, 'create_volume')
|
||||||
def _create_image_generated(self, sparse, mock_create):
|
def _create_image_generated(self, sparse, mock_create):
|
||||||
@ -705,25 +705,25 @@ class LvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
|||||||
self.SIZE, sparse=sparse)
|
self.SIZE, sparse=sparse)
|
||||||
fn.assert_called_once_with(target=self.PATH, ephemeral_size=None)
|
fn.assert_called_once_with(target=self.PATH, ephemeral_size=None)
|
||||||
|
|
||||||
@mock.patch('nova.utils.supports_direct_io', return_value=True)
|
@mock.patch('nova.privsep.utils.supports_direct_io', return_value=True)
|
||||||
@mock.patch.object(imagebackend.disk, 'resize2fs')
|
@mock.patch.object(imagebackend.disk, 'resize2fs')
|
||||||
@mock.patch.object(imagebackend.lvm, 'create_volume')
|
@mock.patch.object(imagebackend.lvm, 'create_volume')
|
||||||
@mock.patch.object(imagebackend.disk, 'get_disk_size',
|
@mock.patch.object(imagebackend.disk, 'get_disk_size',
|
||||||
return_value=TEMPLATE_SIZE)
|
return_value=TEMPLATE_SIZE)
|
||||||
@mock.patch.object(imagebackend.utils, 'execute')
|
@mock.patch('nova.privsep.qemu.convert_image')
|
||||||
def _create_image_resize(self, sparse, mock_execute, mock_get,
|
def _create_image_resize(self, sparse, mock_convert_image, mock_get,
|
||||||
mock_create, mock_resize, mock_ignored):
|
mock_create, mock_resize, mock_ignored):
|
||||||
fn = mock.MagicMock()
|
fn = mock.MagicMock()
|
||||||
fn(target=self.TEMPLATE_PATH)
|
fn(target=self.TEMPLATE_PATH)
|
||||||
cmd = ('qemu-img', 'convert', '-t', 'none', '-O', 'raw',
|
|
||||||
self.TEMPLATE_PATH, self.PATH)
|
|
||||||
image = self.image_class(self.INSTANCE, self.NAME)
|
image = self.image_class(self.INSTANCE, self.NAME)
|
||||||
image.create_image(fn, self.TEMPLATE_PATH, self.SIZE)
|
image.create_image(fn, self.TEMPLATE_PATH, self.SIZE)
|
||||||
|
|
||||||
mock_create.assert_called_once_with(self.VG, self.LV,
|
mock_create.assert_called_once_with(self.VG, self.LV,
|
||||||
self.SIZE, sparse=sparse)
|
self.SIZE, sparse=sparse)
|
||||||
mock_get.assert_called_once_with(self.TEMPLATE_PATH)
|
mock_get.assert_called_once_with(self.TEMPLATE_PATH)
|
||||||
mock_execute.assert_called_once_with(*cmd, run_as_root=True)
|
mock_convert_image.assert_called_once_with(
|
||||||
|
self.TEMPLATE_PATH, self.PATH, None, 'raw',
|
||||||
|
CONF.instances_path)
|
||||||
mock_resize.assert_called_once_with(self.PATH, run_as_root=True)
|
mock_resize.assert_called_once_with(self.PATH, run_as_root=True)
|
||||||
|
|
||||||
@mock.patch.object(imagebackend.fileutils, 'ensure_tree')
|
@mock.patch.object(imagebackend.fileutils, 'ensure_tree')
|
||||||
@ -928,7 +928,8 @@ class EncryptedLvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
|||||||
|
|
||||||
def _create_image(self, sparse):
|
def _create_image(self, sparse):
|
||||||
with test.nested(
|
with test.nested(
|
||||||
mock.patch('nova.utils.supports_direct_io', return_value=True),
|
mock.patch('nova.privsep.utils.supports_direct_io',
|
||||||
|
return_value=True),
|
||||||
mock.patch.object(self.lvm, 'create_volume', mock.Mock()),
|
mock.patch.object(self.lvm, 'create_volume', mock.Mock()),
|
||||||
mock.patch.object(self.lvm, 'remove_volumes', mock.Mock()),
|
mock.patch.object(self.lvm, 'remove_volumes', mock.Mock()),
|
||||||
mock.patch.object(self.disk, 'resize2fs', mock.Mock()),
|
mock.patch.object(self.disk, 'resize2fs', mock.Mock()),
|
||||||
@ -941,7 +942,7 @@ class EncryptedLvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
|||||||
mock.Mock()),
|
mock.Mock()),
|
||||||
mock.patch.object(self.libvirt_utils, 'remove_logical_volumes',
|
mock.patch.object(self.libvirt_utils, 'remove_logical_volumes',
|
||||||
mock.Mock()),
|
mock.Mock()),
|
||||||
mock.patch.object(self.utils, 'execute', mock.Mock())):
|
mock.patch('nova.privsep.qemu.convert_image')):
|
||||||
fn = mock.Mock()
|
fn = mock.Mock()
|
||||||
|
|
||||||
image = self.image_class(self.INSTANCE, self.NAME)
|
image = self.image_class(self.INSTANCE, self.NAME)
|
||||||
@ -960,14 +961,9 @@ class EncryptedLvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
|||||||
CONF.ephemeral_storage_encryption.cipher,
|
CONF.ephemeral_storage_encryption.cipher,
|
||||||
CONF.ephemeral_storage_encryption.key_size,
|
CONF.ephemeral_storage_encryption.key_size,
|
||||||
self.KEY)
|
self.KEY)
|
||||||
cmd = ('qemu-img',
|
nova.privsep.qemu.convert_image.assert_called_with(
|
||||||
'convert',
|
self.TEMPLATE_PATH, self.PATH, None, 'raw',
|
||||||
'-t', 'none',
|
CONF.instances_path)
|
||||||
'-O',
|
|
||||||
'raw',
|
|
||||||
self.TEMPLATE_PATH,
|
|
||||||
self.PATH)
|
|
||||||
self.utils.execute.assert_called_with(*cmd, run_as_root=True)
|
|
||||||
|
|
||||||
def _create_image_generated(self, sparse):
|
def _create_image_generated(self, sparse):
|
||||||
with test.nested(
|
with test.nested(
|
||||||
@ -983,7 +979,7 @@ class EncryptedLvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
|||||||
mock.Mock()),
|
mock.Mock()),
|
||||||
mock.patch.object(self.libvirt_utils, 'remove_logical_volumes',
|
mock.patch.object(self.libvirt_utils, 'remove_logical_volumes',
|
||||||
mock.Mock()),
|
mock.Mock()),
|
||||||
mock.patch.object(self.utils, 'execute', mock.Mock())):
|
mock.patch('nova.privsep.qemu.convert_image')):
|
||||||
fn = mock.Mock()
|
fn = mock.Mock()
|
||||||
|
|
||||||
image = self.image_class(self.INSTANCE, self.NAME)
|
image = self.image_class(self.INSTANCE, self.NAME)
|
||||||
@ -1008,7 +1004,8 @@ class EncryptedLvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
|||||||
|
|
||||||
def _create_image_resize(self, sparse):
|
def _create_image_resize(self, sparse):
|
||||||
with test.nested(
|
with test.nested(
|
||||||
mock.patch('nova.utils.supports_direct_io', return_value=True),
|
mock.patch('nova.privsep.utils.supports_direct_io',
|
||||||
|
return_value=True),
|
||||||
mock.patch.object(self.lvm, 'create_volume', mock.Mock()),
|
mock.patch.object(self.lvm, 'create_volume', mock.Mock()),
|
||||||
mock.patch.object(self.lvm, 'remove_volumes', mock.Mock()),
|
mock.patch.object(self.lvm, 'remove_volumes', mock.Mock()),
|
||||||
mock.patch.object(self.disk, 'resize2fs', mock.Mock()),
|
mock.patch.object(self.disk, 'resize2fs', mock.Mock()),
|
||||||
@ -1021,7 +1018,7 @@ class EncryptedLvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
|||||||
mock.Mock()),
|
mock.Mock()),
|
||||||
mock.patch.object(self.libvirt_utils, 'remove_logical_volumes',
|
mock.patch.object(self.libvirt_utils, 'remove_logical_volumes',
|
||||||
mock.Mock()),
|
mock.Mock()),
|
||||||
mock.patch.object(self.utils, 'execute', mock.Mock())):
|
mock.patch('nova.privsep.qemu.convert_image')):
|
||||||
fn = mock.Mock()
|
fn = mock.Mock()
|
||||||
|
|
||||||
image = self.image_class(self.INSTANCE, self.NAME)
|
image = self.image_class(self.INSTANCE, self.NAME)
|
||||||
@ -1042,14 +1039,9 @@ class EncryptedLvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
|||||||
CONF.ephemeral_storage_encryption.cipher,
|
CONF.ephemeral_storage_encryption.cipher,
|
||||||
CONF.ephemeral_storage_encryption.key_size,
|
CONF.ephemeral_storage_encryption.key_size,
|
||||||
self.KEY)
|
self.KEY)
|
||||||
cmd = ('qemu-img',
|
nova.privsep.qemu.convert_image.assert_called_with(
|
||||||
'convert',
|
self.TEMPLATE_PATH, self.PATH, None, 'raw',
|
||||||
'-t', 'none',
|
CONF.instances_path)
|
||||||
'-O',
|
|
||||||
'raw',
|
|
||||||
self.TEMPLATE_PATH,
|
|
||||||
self.PATH)
|
|
||||||
self.utils.execute.assert_called_with(*cmd, run_as_root=True)
|
|
||||||
self.disk.resize2fs.assert_called_with(self.PATH, run_as_root=True)
|
self.disk.resize2fs.assert_called_with(self.PATH, run_as_root=True)
|
||||||
|
|
||||||
def test_create_image(self):
|
def test_create_image(self):
|
||||||
|
@ -629,12 +629,9 @@ disk size: 4.4M
|
|||||||
mock_images.assert_called_once_with(
|
mock_images.assert_called_once_with(
|
||||||
_context, image_id, target)
|
_context, image_id, target)
|
||||||
|
|
||||||
@mock.patch('nova.utils.supports_direct_io', return_value=True)
|
@mock.patch('nova.privsep.utils.supports_direct_io', return_value=True)
|
||||||
def test_fetch_raw_image(self, mock_direct_io):
|
@mock.patch('nova.privsep.qemu.unprivileged_convert_image')
|
||||||
|
def test_fetch_raw_image(self, mock_convert_image, mock_direct_io):
|
||||||
def fake_execute(*cmd, **kwargs):
|
|
||||||
self.executes.append(cmd)
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
def fake_rename(old, new):
|
def fake_rename(old, new):
|
||||||
self.executes.append(('mv', old, new))
|
self.executes.append(('mv', old, new))
|
||||||
@ -666,7 +663,6 @@ disk size: 4.4M
|
|||||||
|
|
||||||
return FakeImgInfo()
|
return FakeImgInfo()
|
||||||
|
|
||||||
self.stub_out('nova.utils.execute', fake_execute)
|
|
||||||
self.stub_out('os.rename', fake_rename)
|
self.stub_out('os.rename', fake_rename)
|
||||||
self.stub_out('os.unlink', fake_unlink)
|
self.stub_out('os.unlink', fake_unlink)
|
||||||
self.stub_out('nova.virt.images.fetch', lambda *_, **__: None)
|
self.stub_out('nova.virt.images.fetch', lambda *_, **__: None)
|
||||||
@ -686,19 +682,21 @@ disk size: 4.4M
|
|||||||
|
|
||||||
target = 't.qcow2'
|
target = 't.qcow2'
|
||||||
self.executes = []
|
self.executes = []
|
||||||
expected_commands = [('qemu-img', 'convert', '-t', 'none',
|
expected_commands = [('rm', 't.qcow2.part'),
|
||||||
'-O', 'raw', '-f', 'qcow2',
|
|
||||||
't.qcow2.part', 't.qcow2.converted'),
|
|
||||||
('rm', 't.qcow2.part'),
|
|
||||||
('mv', 't.qcow2.converted', 't.qcow2')]
|
('mv', 't.qcow2.converted', 't.qcow2')]
|
||||||
images.fetch_to_raw(context, image_id, target)
|
images.fetch_to_raw(context, image_id, target)
|
||||||
self.assertEqual(self.executes, expected_commands)
|
self.assertEqual(self.executes, expected_commands)
|
||||||
|
mock_convert_image.assert_called_with(
|
||||||
|
't.qcow2.part', 't.qcow2.converted', 'qcow2', 'raw',
|
||||||
|
CONF.instances_path)
|
||||||
|
mock_convert_image.reset_mock()
|
||||||
|
|
||||||
target = 't.raw'
|
target = 't.raw'
|
||||||
self.executes = []
|
self.executes = []
|
||||||
expected_commands = [('mv', 't.raw.part', 't.raw')]
|
expected_commands = [('mv', 't.raw.part', 't.raw')]
|
||||||
images.fetch_to_raw(context, image_id, target)
|
images.fetch_to_raw(context, image_id, target)
|
||||||
self.assertEqual(self.executes, expected_commands)
|
self.assertEqual(self.executes, expected_commands)
|
||||||
|
mock_convert_image.assert_not_called()
|
||||||
|
|
||||||
target = 'backing.qcow2'
|
target = 'backing.qcow2'
|
||||||
self.executes = []
|
self.executes = []
|
||||||
@ -706,6 +704,7 @@ disk size: 4.4M
|
|||||||
self.assertRaises(exception.ImageUnacceptable,
|
self.assertRaises(exception.ImageUnacceptable,
|
||||||
images.fetch_to_raw, context, image_id, target)
|
images.fetch_to_raw, context, image_id, target)
|
||||||
self.assertEqual(self.executes, expected_commands)
|
self.assertEqual(self.executes, expected_commands)
|
||||||
|
mock_convert_image.assert_not_called()
|
||||||
|
|
||||||
del self.executes
|
del self.executes
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ class QemuTestCase(test.NoDBTestCase):
|
|||||||
self.assertTrue(image_info)
|
self.assertTrue(image_info)
|
||||||
self.assertTrue(str(image_info))
|
self.assertTrue(str(image_info))
|
||||||
|
|
||||||
@mock.patch('nova.utils.supports_direct_io', return_value=True)
|
@mock.patch('nova.privsep.utils.supports_direct_io', return_value=True)
|
||||||
@mock.patch.object(utils, 'execute',
|
@mock.patch.object(utils, 'execute',
|
||||||
side_effect=processutils.ProcessExecutionError)
|
side_effect=processutils.ProcessExecutionError)
|
||||||
def test_convert_image_with_errors(self, mocked_execute, mock_direct_io):
|
def test_convert_image_with_errors(self, mocked_execute, mock_direct_io):
|
||||||
@ -101,8 +101,8 @@ class QemuTestCase(test.NoDBTestCase):
|
|||||||
images.fetch_to_raw,
|
images.fetch_to_raw,
|
||||||
None, 'href123', '/no/path')
|
None, 'href123', '/no/path')
|
||||||
|
|
||||||
@mock.patch('nova.utils.supports_direct_io', return_value=True)
|
@mock.patch('nova.privsep.utils.supports_direct_io', return_value=True)
|
||||||
@mock.patch('nova.utils.execute')
|
@mock.patch('oslo_concurrency.processutils.execute')
|
||||||
def test_convert_image_with_direct_io_support(self, mock_execute,
|
def test_convert_image_with_direct_io_support(self, mock_execute,
|
||||||
mock_direct_io):
|
mock_direct_io):
|
||||||
images._convert_image('source', 'dest', 'in_format', 'out_format',
|
images._convert_image('source', 'dest', 'in_format', 'out_format',
|
||||||
@ -111,8 +111,8 @@ class QemuTestCase(test.NoDBTestCase):
|
|||||||
'-f', 'in_format', 'source', 'dest')
|
'-f', 'in_format', 'source', 'dest')
|
||||||
self.assertTupleEqual(expected, mock_execute.call_args[0])
|
self.assertTupleEqual(expected, mock_execute.call_args[0])
|
||||||
|
|
||||||
@mock.patch('nova.utils.supports_direct_io', return_value=False)
|
@mock.patch('nova.privsep.utils.supports_direct_io', return_value=False)
|
||||||
@mock.patch('nova.utils.execute')
|
@mock.patch('oslo_concurrency.processutils.execute')
|
||||||
def test_convert_image_without_direct_io_support(self, mock_execute,
|
def test_convert_image_without_direct_io_support(self, mock_execute,
|
||||||
mock_direct_io):
|
mock_direct_io):
|
||||||
images._convert_image('source', 'dest', 'in_format', 'out_format',
|
images._convert_image('source', 'dest', 'in_format', 'out_format',
|
||||||
|
@ -20,11 +20,9 @@
|
|||||||
import contextlib
|
import contextlib
|
||||||
import copy
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
import errno
|
|
||||||
import functools
|
import functools
|
||||||
import hashlib
|
import hashlib
|
||||||
import inspect
|
import inspect
|
||||||
import mmap
|
|
||||||
import os
|
import os
|
||||||
import pyclbr
|
import pyclbr
|
||||||
import random
|
import random
|
||||||
@ -1341,53 +1339,6 @@ def get_endpoint(ksa_adapter):
|
|||||||
"interfaces: %s" % interfaces)
|
"interfaces: %s" % interfaces)
|
||||||
|
|
||||||
|
|
||||||
def supports_direct_io(dirpath):
|
|
||||||
|
|
||||||
if not hasattr(os, 'O_DIRECT'):
|
|
||||||
LOG.debug("This python runtime does not support direct I/O")
|
|
||||||
return False
|
|
||||||
|
|
||||||
testfile = os.path.join(dirpath, ".directio.test")
|
|
||||||
|
|
||||||
hasDirectIO = True
|
|
||||||
fd = None
|
|
||||||
try:
|
|
||||||
fd = os.open(testfile, os.O_CREAT | os.O_WRONLY | os.O_DIRECT)
|
|
||||||
# Check is the write allowed with 512 byte alignment
|
|
||||||
align_size = 512
|
|
||||||
m = mmap.mmap(-1, align_size)
|
|
||||||
m.write(b"x" * align_size)
|
|
||||||
os.write(fd, m)
|
|
||||||
LOG.debug("Path '%(path)s' supports direct I/O",
|
|
||||||
{'path': dirpath})
|
|
||||||
except OSError as e:
|
|
||||||
if e.errno == errno.EINVAL:
|
|
||||||
LOG.debug("Path '%(path)s' does not support direct I/O: "
|
|
||||||
"'%(ex)s'", {'path': dirpath, 'ex': e})
|
|
||||||
hasDirectIO = False
|
|
||||||
else:
|
|
||||||
with excutils.save_and_reraise_exception():
|
|
||||||
LOG.error("Error on '%(path)s' while checking "
|
|
||||||
"direct I/O: '%(ex)s'",
|
|
||||||
{'path': dirpath, 'ex': e})
|
|
||||||
except Exception as e:
|
|
||||||
with excutils.save_and_reraise_exception():
|
|
||||||
LOG.error("Error on '%(path)s' while checking direct I/O: "
|
|
||||||
"'%(ex)s'", {'path': dirpath, 'ex': e})
|
|
||||||
finally:
|
|
||||||
# ensure unlink(filepath) will actually remove the file by deleting
|
|
||||||
# the remaining link to it in close(fd)
|
|
||||||
if fd is not None:
|
|
||||||
os.close(fd)
|
|
||||||
|
|
||||||
try:
|
|
||||||
os.unlink(testfile)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return hasDirectIO
|
|
||||||
|
|
||||||
|
|
||||||
def generate_hostid(host, project_id):
|
def generate_hostid(host, project_id):
|
||||||
"""Generate an obfuscated host id representing the host.
|
"""Generate an obfuscated host id representing the host.
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ import nova.conf
|
|||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova import image
|
from nova import image
|
||||||
|
import nova.privsep.qemu
|
||||||
from nova import utils
|
from nova import utils
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -116,30 +117,14 @@ def convert_image_unsafe(source, dest, out_format, run_as_root=False):
|
|||||||
|
|
||||||
|
|
||||||
def _convert_image(source, dest, in_format, out_format, run_as_root):
|
def _convert_image(source, dest, in_format, out_format, run_as_root):
|
||||||
# NOTE(mdbooth): qemu-img convert defaults to cache=unsafe, which means
|
|
||||||
# that data is not synced to disk at completion. We explicitly use
|
|
||||||
# cache=none here to (1) ensure that we don't interfere with other
|
|
||||||
# applications using the host's io cache, and (2) ensure that the data is
|
|
||||||
# on persistent storage when the command exits. Without (2), a host crash
|
|
||||||
# may leave a corrupt image in the image cache, which Nova cannot recover
|
|
||||||
# automatically.
|
|
||||||
# NOTE(zigo): we cannot use -t none if the instances dir is mounted on a
|
|
||||||
# filesystem that doesn't have support for O_DIRECT, which is the case
|
|
||||||
# for example with tmpfs. This simply crashes "openstack server create"
|
|
||||||
# in environments like live distributions. In such case, the best choice
|
|
||||||
# is writethrough, which is power-failure safe, but still faster than
|
|
||||||
# writeback.
|
|
||||||
if utils.supports_direct_io(CONF.instances_path):
|
|
||||||
cache_mode = 'none'
|
|
||||||
else:
|
|
||||||
cache_mode = 'writethrough'
|
|
||||||
cmd = ('qemu-img', 'convert', '-t', cache_mode, '-O', out_format)
|
|
||||||
|
|
||||||
if in_format is not None:
|
|
||||||
cmd = cmd + ('-f', in_format)
|
|
||||||
cmd = cmd + (source, dest)
|
|
||||||
try:
|
try:
|
||||||
utils.execute(*cmd, run_as_root=run_as_root)
|
if not run_as_root:
|
||||||
|
nova.privsep.qemu.unprivileged_convert_image(
|
||||||
|
source, dest, in_format, out_format, CONF.instances_path)
|
||||||
|
else:
|
||||||
|
nova.privsep.qemu.convert_image(
|
||||||
|
source, dest, in_format, out_format, CONF.instances_path)
|
||||||
|
|
||||||
except processutils.ProcessExecutionError as exp:
|
except processutils.ProcessExecutionError as exp:
|
||||||
msg = (_("Unable to convert image to %(format)s: %(exp)s") %
|
msg = (_("Unable to convert image to %(format)s: %(exp)s") %
|
||||||
{'format': out_format, 'exp': exp})
|
{'format': out_format, 'exp': exp})
|
||||||
|
@ -90,6 +90,7 @@ from nova.pci import manager as pci_manager
|
|||||||
from nova.pci import utils as pci_utils
|
from nova.pci import utils as pci_utils
|
||||||
import nova.privsep.libvirt
|
import nova.privsep.libvirt
|
||||||
import nova.privsep.path
|
import nova.privsep.path
|
||||||
|
import nova.privsep.utils
|
||||||
from nova import rc_fields
|
from nova import rc_fields
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova import version
|
from nova import version
|
||||||
@ -405,7 +406,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
# is safe for migration provided the filesystem is cache coherent
|
# is safe for migration provided the filesystem is cache coherent
|
||||||
# (cluster filesystems typically are, but things like NFS are not).
|
# (cluster filesystems typically are, but things like NFS are not).
|
||||||
self._disk_cachemode = "none"
|
self._disk_cachemode = "none"
|
||||||
if not utils.supports_direct_io(CONF.instances_path):
|
if not nova.privsep.utils.supports_direct_io(CONF.instances_path):
|
||||||
self._disk_cachemode = "writethrough"
|
self._disk_cachemode = "writethrough"
|
||||||
return self._disk_cachemode
|
return self._disk_cachemode
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user