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:
parent
5bf1bb47c7
commit
abe566df40
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 = ""
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue