Support qemu >= 2.10

qemu 2.10 enables automatic write locking of underlying disks for VM's;
ensure that calls to 'qemu-img info' are made using the --force-share
flag for qemu >= 2.10.0 allowing the libvirt driver to query the
underlying disk file when the instance is running.

Note that not all calls to 'qemu-img info' need to be made with this
flag; only calls that might occur when the instance is running will
fail to obtain the automatic write lock.

Change-Id: I97082895adcf97ba6700c88d28960b0e09001aca
Closes-Bug: 1718295
This commit is contained in:
James Page 2017-09-20 17:47:48 +01:00
parent 5bf1bb47c7
commit abe566df40
7 changed files with 65 additions and 30 deletions

View File

@ -30,7 +30,7 @@ def create_image(disk_format, path, size):
pass
def create_cow_image(backing_file, path):
def create_cow_image(backing_file, path, force_share=False):
pass
@ -38,11 +38,11 @@ def create_ploop_image(disk_format, path, size, fs_type):
pass
def get_disk_size(path, format=None):
def get_disk_size(path, format=None, force_share=False):
return 0
def get_disk_backing_file(path, basename=True, format=None):
def get_disk_backing_file(path, basename=True, format=None, force_share=False):
backing_file = disk_backing_files.get(path, None)
if backing_file and basename:
backing_file = os.path.basename(backing_file)

View File

@ -15165,10 +15165,13 @@ class LibvirtConnTestCase(test.NoDBTestCase,
fakelibvirt.VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT |
fakelibvirt.VIR_DOMAIN_BLOCK_REBASE_SHALLOW))
mock_size.assert_called_once_with(srcfile, format="qcow2")
mock_size.assert_called_once_with(srcfile, format="qcow2",
force_share=False)
mock_backing.assert_called_once_with(srcfile, basename=False,
format="qcow2")
mock_create_cow.assert_called_once_with(bckfile, dltfile, 1004009)
format="qcow2",
force_share=False)
mock_create_cow.assert_called_once_with(bckfile, dltfile, 1004009,
force_share=False)
mock_chown.assert_called_once_with(dltfile, uid=os.getuid())
mock_snapshot.assert_called_once_with(dltfile, "qcow2",
dstfile, "qcow2")

View File

@ -391,7 +391,7 @@ class FlatTestCase(_ImageTestCase, test.NoDBTestCase):
mock_extend.assert_called_once_with(
imgmodel.LocalFileImage(self.PATH, imgmodel.FORMAT_RAW),
self.SIZE)
mock_qemu.assert_called_once_with(self.TEMPLATE_PATH)
mock_qemu.assert_called_once_with(self.TEMPLATE_PATH, force_share=False)
mock_utime.assert_called()
@mock.patch.object(os.path, 'exists')

View File

@ -136,14 +136,14 @@ def resize2fs(image, check_exit_code=False, run_as_root=False):
run_as_root=run_as_root)
def get_disk_size(path):
def get_disk_size(path, force_share=False):
"""Get the (virtual) size of a disk image
:param path: Path to the disk image
:returns: Size (in bytes) of the given disk image as it would be seen
by a virtual machine.
"""
return images.qemu_img_info(path).virtual_size
return images.qemu_img_info(path, force_share=force_share).virtual_size
def extend(image, size):

View File

@ -43,7 +43,7 @@ QEMU_IMG_LIMITS = processutils.ProcessLimits(
address_space=1 * units.Gi)
def qemu_img_info(path, format=None):
def qemu_img_info(path, format=None, force_share=False):
"""Return an object containing the parsed output from qemu-img info."""
# TODO(mikal): this code should not be referring to a libvirt specific
# flag.
@ -58,6 +58,8 @@ def qemu_img_info(path, format=None):
path = os.path.join(path, "root.hds")
cmd = ('env', 'LC_ALL=C', 'LANG=C', 'qemu-img', 'info', path)
if force_share:
cmd = cmd + ('--force-share',)
if format is not None:
cmd = cmd + ('-f', format)
out, err = utils.execute(*cmd, prlimit=QEMU_IMG_LIMITS)

View File

@ -250,6 +250,11 @@ MIN_LIBVIRT_PF_WITH_NO_VFS_CAP_VERSION = (1, 3, 0)
MIN_LIBVIRT_VIRTLOGD = (1, 3, 3)
MIN_QEMU_VIRTLOGD = (2, 7, 0)
# qemu >= 2.10.0
# Use '--force-share' to skip image locking during qemu-img info
# execution as running qemu process owns the write lock.
MIN_QEMU_FORCE_SHARE = (2, 10, 0)
# ppc64/ppc64le architectures with KVM
# NOTE(rfolco): Same levels for Libvirt/Qemu on Big Endian and Little
# Endian giving the nuance around guest vs host architectures
@ -385,6 +390,9 @@ class LibvirtDriver(driver.ComputeDriver):
# avoid any re-calculation when computing resources.
self._reserved_hugepages = hardware.numa_get_reserved_huge_pages()
# Assume pre 2.10 version of qemu is in use
self._force_share = False
def _get_volume_drivers(self):
driver_registry = dict()
@ -481,11 +489,15 @@ class LibvirtDriver(driver.ComputeDriver):
_('Nova requires libvirt version %s or greater.') %
self._version_to_string(MIN_LIBVIRT_VERSION))
if (CONF.libvirt.virt_type in ("qemu", "kvm") and
not self._host.has_min_version(hv_ver=MIN_QEMU_VERSION)):
raise exception.InternalError(
_('Nova requires QEMU version %s or greater.') %
self._version_to_string(MIN_QEMU_VERSION))
if CONF.libvirt.virt_type in ("qemu", "kvm"):
if self._host.has_min_version(hv_ver=MIN_QEMU_VERSION):
self._force_share = (
self._host.has_min_version(hv_ver=MIN_QEMU_FORCE_SHARE)
)
else:
raise exception.InternalError(
_('Nova requires QEMU version %s or greater.') %
self._version_to_string(MIN_QEMU_VERSION))
if CONF.libvirt.virt_type == 'parallels':
if not self._host.has_min_version(hv_ver=MIN_VIRTUOZZO_VERSION):
@ -1872,14 +1884,23 @@ class LibvirtDriver(driver.ComputeDriver):
# in QEMU 1.3. In order to do this, we need to create
# a destination image with the original backing file
# and matching size of the instance root disk.
src_disk_size = libvirt_utils.get_disk_size(disk_path,
format=source_format)
src_back_path = libvirt_utils.get_disk_backing_file(disk_path,
format=source_format,
basename=False)
src_disk_size = libvirt_utils.get_disk_size(
disk_path,
format=source_format,
force_share=self._force_share
)
src_back_path = libvirt_utils.get_disk_backing_file(
disk_path,
format=source_format,
basename=False,
force_share=self._force_share
)
disk_delta = out_path + '.delta'
libvirt_utils.create_cow_image(src_back_path, disk_delta,
src_disk_size)
libvirt_utils.create_cow_image(
src_back_path, disk_delta,
src_disk_size,
force_share=self._force_share
)
quiesced = False
try:
@ -7219,8 +7240,14 @@ class LibvirtDriver(driver.ComputeDriver):
continue
if driver_type in ("qcow2", "ploop"):
backing_file = libvirt_utils.get_disk_backing_file(path)
virt_size = disk_api.get_disk_size(path)
backing_file = libvirt_utils.get_disk_backing_file(
path,
force_share=self._force_share
)
virt_size = disk_api.get_disk_size(
path,
force_share=self._force_share
)
over_commit_size = int(virt_size) - dk_size
else:
backing_file = ""

View File

@ -60,7 +60,7 @@ def create_image(disk_format, path, size):
utils.execute('qemu-img', 'create', '-f', disk_format, path, size)
def create_cow_image(backing_file, path, size=None):
def create_cow_image(backing_file, path, size=None, force_share=False):
"""Create COW image
Creates a COW image with the given backing file
@ -72,7 +72,8 @@ def create_cow_image(backing_file, path, size=None):
cow_opts = []
if backing_file:
cow_opts += ['backing_file=%s' % backing_file]
base_details = images.qemu_img_info(backing_file)
base_details = images.qemu_img_info(backing_file,
force_share=force_share)
else:
base_details = None
# Explicitly inherit the value of 'cluster_size' property of a qcow2
@ -176,25 +177,27 @@ def pick_disk_driver_name(hypervisor_version, is_block_dev=False):
return None
def get_disk_size(path, format=None):
def get_disk_size(path, format=None, force_share=False):
"""Get the (virtual) size of a disk image
:param path: Path to the disk image
:param format: the on-disk format of path
:param force_share: Inhibit write lock during qemu-img info call
:returns: Size (in bytes) of the given disk image as it would be seen
by a virtual machine.
"""
size = images.qemu_img_info(path, format).virtual_size
size = images.qemu_img_info(path, format, force_share).virtual_size
return int(size)
def get_disk_backing_file(path, basename=True, format=None):
def get_disk_backing_file(path, basename=True, format=None, force_share=False):
"""Get the backing file of a disk image
:param path: Path to the disk image
:param force_share: Inhibit write lock during qemu-img info call
:returns: a path to the image's backing store
"""
backing_file = images.qemu_img_info(path, format).backing_file
backing_file = images.qemu_img_info(path, format, force_share).backing_file
if backing_file and basename:
backing_file = os.path.basename(backing_file)