From db1e0f49dc06d16218ce61d9ef6a025075710d8e Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Fri, 19 Feb 2021 09:36:47 +0100 Subject: [PATCH] kvm: set Backing Format of qcow2 images in vm start and migration (#4656) In previous cloudstack versions, qcow2 image does not have a backing file format. however, it is required in newer qemu versions, for example qemu 4.2 on ubuntu 20.04. steps to reproduce the issue (1) install cloudstack 4.14 or previous version, and ubuntu 19.04 or 18.04/16.04 LTS. (2) create vms. (3) upgrade to 4.15, upgrade os to ubuntu 20.04 , or install a new server with ubuntu 20.04. (4) migrate vm from old ubuntu version to ubuntu 20.04, failed with exception below ``` 2021-02-04 13:43:07,397 DEBUG [resource.wrapper.LibvirtMigrateCommandWrapper] (agentRequest-Handler-1:null) (logid:93da9385) ExecutionException : org.libvirt.LibvirtException: Requested operation is not valid: format of backing image '/mnt/03b6f487-9eaf-38bf-ad2d-d985423b832f/66990fcc-fd98-4932-9649-989bf6583d59' of image '/mnt/03b6f487-9eaf-38bf-ad2d-d985423b832f/a3dd1f0f-2557-4e07-951c-e4eb7b3f38b2' was not specified in the image metadata (See https://libvirt.org/kbase/backing_chains.html for troubleshooting) ``` (5)stop vm, and start it on ubuntu 20.04 server. failed with exception below ``` 2021-02-04 13:46:29,766 WARN [resource.wrapper.LibvirtStartCommandWrapper] (agentRequest-Handler-5:null) (logid:b54745a7) LibvirtException org.libvirt.LibvirtException: Requested operation is not valid: format of backing image '/mnt/03b6f487-9eaf-38bf-ad2d-d985423b832f/66990fcc-fd98-4932-9649-989bf6583d59' of image '/mnt/03b6f487-9eaf-38bf-ad2d-d985423b832f/a3dd1f0f-2557-4e07-951c-e4eb7b3f38b2' was not specified in the image metadata (See https://libvirt.org/kbase/backing_chains.html for troubleshooting) ``` To make testing easier, step 1 and 2 can be replaced by ``` qemu-img create -f qcow2 -b ``` so qcow2 image does not have a backing file format. --- .../resource/LibvirtComputingResource.java | 33 +++++++++++++++++++ .../wrapper/LibvirtMigrateCommandWrapper.java | 8 +++++ .../apache/cloudstack/utils/qemu/QemuImg.java | 23 ++++++++++++- 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 074d0af1ad3..4cb356c8023 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -56,6 +56,9 @@ import org.apache.cloudstack.utils.hypervisor.HypervisorUtils; import org.apache.cloudstack.utils.linux.CPUStat; import org.apache.cloudstack.utils.linux.KVMHostInfo; import org.apache.cloudstack.utils.linux.MemStat; +import org.apache.cloudstack.utils.qemu.QemuImg; +import org.apache.cloudstack.utils.qemu.QemuImgException; +import org.apache.cloudstack.utils.qemu.QemuImgFile; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import org.apache.cloudstack.utils.security.KeyStoreUtils; import org.apache.commons.collections.MapUtils; @@ -2528,6 +2531,15 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv volPath = physicalDisk.getPath(); } + if (volume.getType() != Volume.Type.ISO + && physicalDisk != null && physicalDisk.getFormat() == PhysicalDiskFormat.QCOW2 + && (pool.getType() == StoragePoolType.NetworkFilesystem + || pool.getType() == StoragePoolType.SharedMountPoint + || pool.getType() == StoragePoolType.Filesystem + || pool.getType() == StoragePoolType.Gluster)) { + setBackingFileFormat(physicalDisk.getPath()); + } + // check for disk activity, if detected we should exit because vm is running elsewhere if (_diskActivityCheckEnabled && physicalDisk != null && physicalDisk.getFormat() == PhysicalDiskFormat.QCOW2) { s_logger.debug("Checking physical disk file at path " + volPath + " for disk activity to ensure vm is not running elsewhere"); @@ -4245,4 +4257,25 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv cmd.setTopology(numCoresPerSocket, vcpus / numCoresPerSocket); } } + + public void setBackingFileFormat(String volPath) { + final int timeout = 0; + QemuImgFile file = new QemuImgFile(volPath); + QemuImg qemu = new QemuImg(timeout); + try{ + Map info = qemu.info(file); + String backingFilePath = info.get(new String("backing_file")); + String backingFileFormat = info.get(new String("backing_file_format")); + if (org.apache.commons.lang.StringUtils.isEmpty(backingFileFormat)) { + s_logger.info("Setting backing file format of " + volPath); + QemuImgFile backingFile = new QemuImgFile(backingFilePath); + Map backingFileinfo = qemu.info(backingFile); + String backingFileFmt = backingFileinfo.get(new String("file_format")); + qemu.rebase(file, backingFile, backingFileFmt, false); + } + } catch (QemuImgException e) { + s_logger.error("Failed to set backing file format of " + volPath + " due to : " + e.getMessage()); + } + } + } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java index 0524be79617..841eadf13c2 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java @@ -155,6 +155,14 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper mapMigrateStorage = command.getMigrateStorage(); // migrateStorage is declared as final because the replaceStorage method may mutate mapMigrateStorage, but // migrateStorage's value should always only be associated with the initial state of mapMigrateStorage. diff --git a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuImg.java b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuImg.java index e2c03672ccb..3006d256697 100644 --- a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuImg.java +++ b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuImg.java @@ -379,8 +379,29 @@ public class QemuImg { } /* Changes the backing file of an image */ - public void rebase() throws QemuImgException { + public void rebase(final QemuImgFile file, final QemuImgFile backingFile, final String backingFileFormat, final boolean secure) throws QemuImgException { + if (backingFile == null) { + throw new QemuImgException("No backing file was passed"); + } + final Script s = new Script(_qemuImgPath, timeout); + s.add("rebase"); + if (! secure) { + s.add("-u"); + } + s.add("-F"); + if (backingFileFormat != null) { + s.add(backingFileFormat); + } else { + s.add(backingFile.getFormat().toString()); + } + s.add("-b"); + s.add(backingFile.getFileName()); + s.add(file.getFileName()); + final String result = s.execute(); + if (result != null) { + throw new QemuImgException(result); + } } /**