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 <backing file> <qcow2 image>
```
so qcow2 image does not have a backing file format.
This commit is contained in:
Wei Zhou 2021-02-19 09:36:47 +01:00 committed by GitHub
parent fd49efa9c1
commit db1e0f49dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 63 additions and 1 deletions

View File

@ -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<String, String> 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<String, String> 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());
}
}
}

View File

@ -155,6 +155,14 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
// delete the metadata of vm snapshots before migration
vmsnapshots = libvirtComputingResource.cleanVMSnapshotMetadata(dm);
// Verify Format of backing file
for (DiskDef disk : disks) {
if (disk.getDeviceType() == DiskDef.DeviceType.DISK
&& disk.getDiskFormatType() == DiskDef.DiskFmtType.QCOW2) {
libvirtComputingResource.setBackingFileFormat(disk.getDiskPath());
}
}
Map<String, MigrateCommand.MigrateDiskInfo> 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.

View File

@ -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);
}
}
/**