diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/storage/backup/DeleteAllVmCheckpointsCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/storage/backup/DeleteAllVmCheckpointsCommand.java index b8bc45fe3a2..75d8b499df8 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/storage/backup/DeleteAllVmCheckpointsCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/storage/backup/DeleteAllVmCheckpointsCommand.java @@ -139,4 +139,9 @@ protected Map> getSharedLocks() { return Collections.singletonMap(getParameters().getVmId().toString(), LockMessagesMatchUtil.makeLockingPair(LockingGroup.VM, EngineMessage.ACTION_TYPE_FAILED_VM_IS_LOCKED)); } + + @Override + public boolean ignoreChildCommandFailure() { + return getParameters().isForce(); + } } diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/storage/backup/RedefineVmCheckpointCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/storage/backup/RedefineVmCheckpointCommand.java index 489b733ff99..af82127feaa 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/storage/backup/RedefineVmCheckpointCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/storage/backup/RedefineVmCheckpointCommand.java @@ -1,5 +1,6 @@ package org.ovirt.engine.core.bll.storage.backup; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; @@ -15,6 +16,9 @@ import org.ovirt.engine.core.bll.utils.PermissionSubject; import org.ovirt.engine.core.common.FeatureSupported; import org.ovirt.engine.core.common.VdcObjectType; +import org.ovirt.engine.core.common.action.ActionParametersBase.EndProcedure; +import org.ovirt.engine.core.common.action.ActionType; +import org.ovirt.engine.core.common.action.DeleteAllVmCheckpointsParameters; import org.ovirt.engine.core.common.action.VmBackupParameters; import org.ovirt.engine.core.common.businessentities.ActionGroup; import org.ovirt.engine.core.common.businessentities.VmBackup; @@ -25,10 +29,8 @@ import org.ovirt.engine.core.common.vdscommands.VDSReturnValue; import org.ovirt.engine.core.common.vdscommands.VdsAndVmIDVDSParametersBase; import org.ovirt.engine.core.common.vdscommands.VmBackupVDSParameters; -import org.ovirt.engine.core.common.vdscommands.VmCheckpointsVDSParameters; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dao.VmCheckpointDao; -import org.ovirt.engine.core.utils.transaction.TransactionSupport; import org.ovirt.engine.core.vdsbroker.irsbroker.VmCheckpointIds; @NonTransactiveCommandAttribute @@ -220,20 +222,21 @@ private VDSReturnValue performVmCheckpointsOperation(VDSCommandType vdsCommandTy } private void removeCheckpointChain(List definedCheckpointsIds) { - for (Guid checkpointId : definedCheckpointsIds) { - // Best effort to remove all checkpoints in the chain from libvirt, - // starting from the oldest checkpoint to the leaf. - VmCheckpoint vmCheckpoint = new VmCheckpoint(); - vmCheckpoint.setId(checkpointId); - performVmCheckpointsOperation(VDSCommandType.DeleteVmCheckpoints, - new VmCheckpointsVDSParameters(getVdsId(), getVmId(), List.of(vmCheckpoint))); - } - - // Removing all the checkpoints from the Engine database - TransactionSupport.executeInNewTransaction(() -> { - vmCheckpointDao.removeAllCheckpointsByVmId(getVmId()); - return null; - }); + // Collect all the images that were part of a backup. + List imagesWithCheckpoints = definedCheckpointsIds.stream() + .map(vmCheckpoint -> vmCheckpointDao.getDisksByCheckpointId(vmCheckpoint)) + .flatMap(List::stream) + .distinct() + .collect(Collectors.toCollection(ArrayList::new)); + + DeleteAllVmCheckpointsParameters deleteAllVmCheckpointsParameters = + new DeleteAllVmCheckpointsParameters(getVmId(), imagesWithCheckpoints); + deleteAllVmCheckpointsParameters.setParentCommand(getActionType()); + deleteAllVmCheckpointsParameters.setParentParameters(getParameters()); + deleteAllVmCheckpointsParameters.setEndProcedure(EndProcedure.COMMAND_MANAGED); + deleteAllVmCheckpointsParameters.setForce(true); + + runInternalAction(ActionType.DeleteAllVmCheckpoints, deleteAllVmCheckpointsParameters); } @Override diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/storage/backup/StartVmBackupCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/storage/backup/StartVmBackupCommand.java index 60fc7fdfd2f..2f4a70640d7 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/storage/backup/StartVmBackupCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/storage/backup/StartVmBackupCommand.java @@ -39,8 +39,10 @@ import org.ovirt.engine.core.common.FeatureSupported; import org.ovirt.engine.core.common.VdcObjectType; import org.ovirt.engine.core.common.action.ActionParametersBase; +import org.ovirt.engine.core.common.action.ActionParametersBase.EndProcedure; import org.ovirt.engine.core.common.action.ActionReturnValue; import org.ovirt.engine.core.common.action.ActionType; +import org.ovirt.engine.core.common.action.DeleteAllVmCheckpointsParameters; import org.ovirt.engine.core.common.action.LockProperties; import org.ovirt.engine.core.common.action.VmBackupParameters; import org.ovirt.engine.core.common.action.VolumeBitmapCommandParameters; @@ -58,6 +60,8 @@ import org.ovirt.engine.core.common.businessentities.storage.DiskBackupMode; import org.ovirt.engine.core.common.businessentities.storage.DiskImage; import org.ovirt.engine.core.common.businessentities.storage.ImageStatus; +import org.ovirt.engine.core.common.businessentities.storage.Qcow2BitmapInfo; +import org.ovirt.engine.core.common.businessentities.storage.QemuImageInfo; import org.ovirt.engine.core.common.businessentities.storage.VmBackupType; import org.ovirt.engine.core.common.errors.EngineException; import org.ovirt.engine.core.common.errors.EngineMessage; @@ -264,6 +268,24 @@ protected void executeCommand() { Guid vmBackupId = createVmBackup(); log.info("Created VmBackup entity '{}'", vmBackupId); + // Set a VDS to be able to gather Qemu Image Info + if (getVds() == null) { + setHostForColdBackupOperation(); + } + if (getVds().isQemuImageInfoBitmaps()) { + log.info("Checking VM checkpoint '{}' for VM '{}'", vmBackup.getFromCheckpointId(), vmBackup.getVmId()); + if (!validateCheckpoint(vmBackup.getFromCheckpointId())) { + addCustomValue("backupId", vmBackupId.toString()); + auditLogDirector.log(this, AuditLogType.VM_INCREMENTAL_BACKUP_FAILED_FULL_VM_BACKUP_NEEDED); + setCommandStatus(CommandStatus.FAILED); + return; + } + log.info("Previous VM checkpoint '{}' for VM '{}' is valid", vmBackup.getFromCheckpointId(), vmBackup.getVmId()); + } else { + log.info("Could not check VM checkpoint '{}' for VM '{}' due to missing bitmap info support in vdsm", + vmBackup.getFromCheckpointId(), vmBackup.getVmId()); + } + if (isLiveBackup()) { log.info("Redefine previous VM checkpoints for VM '{}'", vmBackup.getVmId()); if (!redefineVmCheckpoints()) { @@ -368,6 +390,45 @@ public boolean performNextOperation(int completedChildCount) { return true; } + private boolean validateCheckpoint(Guid checkpointId) { + List images = vmCheckpointDao.getDisksByCheckpointId(checkpointId); + /* Check if the checkpoint is still there on each volume/image */ + for (DiskImage image : images) { + QemuImageInfo qcow2Info = imagesHandler.getQemuImageInfoFromVdsm( + getStoragePoolId(), + image.getStorageIds().get(0), + image.getId(), + image.getImageId(), + getParameters().getVdsRunningOn(), + !isLiveBackup()); + + boolean valid = false; + if (qcow2Info != null) { + List bitmaps = qcow2Info.getQcow2bitmaps(); + if (bitmaps != null) { + valid = bitmaps.stream().anyMatch(bitmap -> bitmap.getName().equals(checkpointId)); + } + } + /* Bitmap did not exist on disk -> Remove checkpoints */ + if (!valid) { + log.error("Checkpoint '{}' does not exist for disk '{}'. Removing checkpoints", + checkpointId, + image.getId()); + /* Some checkpoint corruption, remove checkpoints */ + DeleteAllVmCheckpointsParameters deleteAllVmCheckpointsParameters = + new DeleteAllVmCheckpointsParameters(getVmId(), List.of(image)); + deleteAllVmCheckpointsParameters.setParentCommand(getActionType()); + deleteAllVmCheckpointsParameters.setParentParameters(getParameters()); + deleteAllVmCheckpointsParameters.setEndProcedure(EndProcedure.COMMAND_MANAGED); + deleteAllVmCheckpointsParameters.setForce(true); + + runInternalAction(ActionType.DeleteAllVmCheckpoints, deleteAllVmCheckpointsParameters); + return false; + } + } + return true; + } + private boolean redefineVmCheckpoints() { VmBackupParameters parameters = new VmBackupParameters(getParameters().getVmBackup()); parameters.setParentCommand(getActionType()); @@ -380,8 +441,6 @@ private boolean redefineVmCheckpoints() { } private boolean startAddBitmapJobs() { - setHostForColdBackupOperation(); - VmBackup vmBackup = getParameters().getVmBackup(); if (getParameters().getVdsRunningOn() == null) { log.error("Failed to find host to run cold backup operation for VM '{}'", vmBackup.getVmId()); diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/storage/disk/image/TransferDiskImageCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/storage/disk/image/TransferDiskImageCommand.java index b8d16d1b7fe..3880759b154 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/storage/disk/image/TransferDiskImageCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/storage/disk/image/TransferDiskImageCommand.java @@ -32,9 +32,11 @@ import org.ovirt.engine.core.common.AuditLogType; import org.ovirt.engine.core.common.VdcObjectType; import org.ovirt.engine.core.common.action.ActionParametersBase; +import org.ovirt.engine.core.common.action.ActionParametersBase.EndProcedure; import org.ovirt.engine.core.common.action.ActionReturnValue; import org.ovirt.engine.core.common.action.ActionType; import org.ovirt.engine.core.common.action.AddDiskParameters; +import org.ovirt.engine.core.common.action.DeleteAllVmCheckpointsParameters; import org.ovirt.engine.core.common.action.LockProperties; import org.ovirt.engine.core.common.action.RemoveDiskParameters; import org.ovirt.engine.core.common.action.TransferDiskImageParameters; @@ -54,6 +56,8 @@ import org.ovirt.engine.core.common.businessentities.storage.ImageTransfer; import org.ovirt.engine.core.common.businessentities.storage.ImageTransferBackend; import org.ovirt.engine.core.common.businessentities.storage.ImageTransferPhase; +import org.ovirt.engine.core.common.businessentities.storage.Qcow2BitmapInfo; +import org.ovirt.engine.core.common.businessentities.storage.QemuImageInfo; import org.ovirt.engine.core.common.businessentities.storage.TimeoutPolicyType; import org.ovirt.engine.core.common.businessentities.storage.TransferType; import org.ovirt.engine.core.common.businessentities.storage.VmBackupType; @@ -259,7 +263,46 @@ private PrepareImageVDSCommandParameters getPrepareParameters(Guid vdsId) { getDiskImage().getImageId(), true); } + private boolean validateBitmap(DiskImage image, Guid checkpointId) { + QemuImageInfo qcow2Info = imagesHandler.getQemuImageInfoFromVdsm( + getStoragePoolId(), + image.getStorageIds().get(0), + image.getId(), + image.getImageId(), + getVdsId(), + !isLiveBackup()); + + boolean valid = false; + if (qcow2Info != null) { + List bitmaps = qcow2Info.getQcow2bitmaps(); + if (bitmaps != null) { + valid = bitmaps.stream().anyMatch(bitmap -> bitmap.getName().equals(checkpointId)); + } + } + /* Bitmap did not exist on disk -> Remove checkpoints */ + if (!valid) { + log.error("Checkpoint '{}' does not exist for disk '{}'. Removing checkpoints", + checkpointId, + image.getId()); + /* Some checkpoint corruption, remove checkpoints */ + DeleteAllVmCheckpointsParameters deleteAllVmCheckpointsParameters = + new DeleteAllVmCheckpointsParameters(getVmId(), List.of(image)); + deleteAllVmCheckpointsParameters.setParentCommand(getActionType()); + deleteAllVmCheckpointsParameters.setParentParameters(getParameters()); + deleteAllVmCheckpointsParameters.setEndProcedure(EndProcedure.COMMAND_MANAGED); + deleteAllVmCheckpointsParameters.setForce(true); + + runInternalAction(ActionType.DeleteAllVmCheckpoints, deleteAllVmCheckpointsParameters); + return false; + } + return true; + } + private Guid getBitmap() { + if (!validateBitmap(getDiskImage(), getBackup().getFromCheckpointId())) { + return null; + } + if (isHybridBackup() && getDiskImage().getBackupMode() == DiskBackupMode.Incremental) { return getBackup().getFromCheckpointId(); } diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/DeleteAllVmCheckpointsParameters.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/DeleteAllVmCheckpointsParameters.java index 9105aa144de..8fcdfa2481a 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/DeleteAllVmCheckpointsParameters.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/DeleteAllVmCheckpointsParameters.java @@ -16,6 +16,7 @@ public class DeleteAllVmCheckpointsParameters extends VmOperationParameterBase { @NotNull private List diskImages; private int completedDisksCount; + private boolean force; public DeleteAllVmCheckpointsParameters() { } @@ -24,6 +25,7 @@ public DeleteAllVmCheckpointsParameters(Guid vmId, List diskImages) { super(vmId); this.diskImages = diskImages; completedDisksCount = 0; + force = false; } public List getDiskImages() { @@ -41,4 +43,11 @@ public int getCompletedDisksCount() { public void setCompletedDisksCount(int completedDisksCount) { this.completedDisksCount = completedDisksCount; } + public boolean isForce() { + return force; + } + + public void setForce(boolean force) { + this.force = force; + } } diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VDS.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VDS.java index 43da486f928..632fd7cd1e3 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VDS.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VDS.java @@ -265,6 +265,7 @@ public VDS clone() { vds.setBootUuid(getBootUuid()); vds.setCdChangePdiv(isCdChangePdiv()); vds.setOvnConfigured(isOvnConfigured()); + vds.setQemuImageInfoBitmaps(isQemuImageInfoBitmaps()); return vds; } @@ -1906,4 +1907,12 @@ public String getVdsmCpusAffinity() { public void setVdsmCpusAffinity(String value) { vdsDynamic.setVdsmCpusAffinity(value); } + + public boolean isQemuImageInfoBitmaps() { + return vdsDynamic.isQemuImageInfoBitmaps(); + } + + public void setQemuImageInfoBitmaps(boolean qemuImageInfoBitmaps) { + vdsDynamic.setQemuImageInfoBitmaps(qemuImageInfoBitmaps); + } } diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VdsDynamic.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VdsDynamic.java index 3af87d982c2..302e2754914 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VdsDynamic.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VdsDynamic.java @@ -241,6 +241,8 @@ public class VdsDynamic implements BusinessEntityWithStatus { private String vdsmCpusAffinity; + private boolean qemuImageInfoBitmaps; + public VdsDynamic() { rpmVersion = new RpmVersion(); libvirtVersion = new RpmVersion(); @@ -1028,6 +1030,14 @@ public String getVdsmCpusAffinity() { return vdsmCpusAffinity; } + public boolean isQemuImageInfoBitmaps() { + return qemuImageInfoBitmaps; + } + + public void setQemuImageInfoBitmaps(boolean qemuImageInfoBitmaps) { + this.qemuImageInfoBitmaps = qemuImageInfoBitmaps; + } + @Override public int hashCode() { @@ -1113,7 +1123,8 @@ public int hashCode() { cdChangePdiv, ovnConfigured, cpuTopology, - vdsmCpusAffinity + vdsmCpusAffinity, + qemuImageInfoBitmaps ); } @@ -1209,6 +1220,7 @@ public boolean equals(Object obj) { && cdChangePdiv == other.cdChangePdiv && ovnConfigured == other.ovnConfigured && Objects.equals(cpuTopology, other.cpuTopology) - && Objects.equals(vdsmCpusAffinity, other.vdsmCpusAffinity); + && Objects.equals(vdsmCpusAffinity, other.vdsmCpusAffinity) + && qemuImageInfoBitmaps == other.qemuImageInfoBitmaps; } } diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/storage/Qcow2BitmapInfo.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/storage/Qcow2BitmapInfo.java new file mode 100644 index 00000000000..4148a5d7014 --- /dev/null +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/storage/Qcow2BitmapInfo.java @@ -0,0 +1,37 @@ +package org.ovirt.engine.core.common.businessentities.storage; + +import java.util.List; + +import org.ovirt.engine.core.compat.Guid; + +public class Qcow2BitmapInfo { + + private Guid name; + private long granularity; + private List flags; + + + public Guid getName() { + return name; + } + + public void setName(Guid name) { + this.name = name; + } + + public long getGranularity() { + return granularity; + } + + public void setGranularity(long granularity) { + this.granularity = granularity; + } + + public List getFlags() { + return flags; + } + + public void setFlags(List flags) { + this.flags = flags; + } +} diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/storage/Qcow2BitmapInfoFlags.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/storage/Qcow2BitmapInfoFlags.java new file mode 100644 index 00000000000..b6919ec85f9 --- /dev/null +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/storage/Qcow2BitmapInfoFlags.java @@ -0,0 +1,31 @@ +package org.ovirt.engine.core.common.businessentities.storage; + +import java.util.HashMap; +import java.util.Map; + +public enum Qcow2BitmapInfoFlags { + IN_USE("in-use"), + AUTO("auto"); + + private String value; + private static Map mappings; + + static { + mappings = new HashMap<>(); + for (Qcow2BitmapInfoFlags error : values()) { + mappings.put(error.getValue(), error); + } + } + + Qcow2BitmapInfoFlags(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static Qcow2BitmapInfoFlags forValue(String value) { + return mappings.get(value); + } +} diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/storage/QemuImageInfo.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/storage/QemuImageInfo.java index b9fc3a02cbc..23f50d0259d 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/storage/QemuImageInfo.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/storage/QemuImageInfo.java @@ -1,5 +1,7 @@ package org.ovirt.engine.core.common.businessentities.storage; +import java.util.List; + import org.ovirt.engine.core.compat.Guid; public class QemuImageInfo { @@ -13,6 +15,7 @@ public class QemuImageInfo { private long clusterSize; private String backingFile; private QcowCompat qcowCompat; + private List qcow2bitmaps; public Guid getImageId() { return imageId; @@ -85,4 +88,12 @@ public QcowCompat getQcowCompat() { public void setQcowCompat(QcowCompat qcowCompat) { this.qcowCompat = qcowCompat; } + + public List getQcow2bitmaps() { + return qcow2bitmaps; + } + + public void setQcow2bitmaps(List qcow2bitmaps) { + this.qcow2bitmaps = qcow2bitmaps; + } } diff --git a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/VdsDaoImpl.java b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/VdsDaoImpl.java index 0677466cf0c..0c51995ac40 100644 --- a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/VdsDaoImpl.java +++ b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/VdsDaoImpl.java @@ -421,6 +421,7 @@ public List getAllWithoutNetwork(Guid networkId) { entity.setOvnConfigured(rs.getBoolean("ovn_configured")); entity.setCpuTopology(SerializationFactory.getDeserializer().deserialize(rs.getString("cpu_topology"), ArrayList.class)); entity.setVdsmCpusAffinity(rs.getString("vdsm_cpus_affinity")); + entity.setQemuImageInfoBitmaps(rs.getBoolean("qemu_image_info_bitmaps")); return entity; }; } diff --git a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/VdsDynamicDaoImpl.java b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/VdsDynamicDaoImpl.java index 1ee852a28de..f169f796c82 100644 --- a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/VdsDynamicDaoImpl.java +++ b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/VdsDynamicDaoImpl.java @@ -142,6 +142,7 @@ public VdsDynamicDaoImpl() { entity.setOvnConfigured(rs.getBoolean("ovn_configured")); entity.setCpuTopology(SerializationFactory.getDeserializer().deserialize(rs.getString("cpu_topology"), ArrayList.class)); entity.setVdsmCpusAffinity(rs.getString("vdsm_cpus_affinity")); + entity.setQemuImageInfoBitmaps(rs.getBoolean("qemu_image_info_bitmaps")); return entity; }; @@ -352,7 +353,8 @@ protected MapSqlParameterSource createFullParametersMapper(VdsDynamic vds) { .addValue("cd_change_pdiv", vds.isCdChangePdiv()) .addValue("ovn_configured", vds.isOvnConfigured()) .addValue("cpu_topology", SerializationFactory.getSerializer().serialize(vds.getCpuTopology())) - .addValue("vdsm_cpus_affinity", vds.getVdsmCpusAffinity()); + .addValue("vdsm_cpus_affinity", vds.getVdsmCpusAffinity()) + .addValue("qemu_image_info_bitmaps", vds.isQemuImageInfoBitmaps()); } @Override diff --git a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/GetQemuImageInfoVDSCommand.java b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/GetQemuImageInfoVDSCommand.java index 83b2b907a3d..405cb71c734 100644 --- a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/GetQemuImageInfoVDSCommand.java +++ b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/GetQemuImageInfoVDSCommand.java @@ -2,6 +2,8 @@ import java.util.Map; +import javax.inject.Inject; + import org.ovirt.engine.core.common.businessentities.storage.QcowCompat; import org.ovirt.engine.core.common.businessentities.storage.QemuImageInfo; import org.ovirt.engine.core.common.businessentities.storage.QemuVolumeFormat; @@ -9,6 +11,9 @@ import org.ovirt.engine.core.common.vdscommands.GetVolumeInfoVDSCommandParameters; public class GetQemuImageInfoVDSCommand

extends VdsBrokerCommand

{ + @Inject + private VdsBrokerObjectsBuilder vdsBrokerObjectsBuilder; + private QemuImageInfoReturn result; public GetQemuImageInfoVDSCommand(P parameters) { @@ -61,6 +66,10 @@ public QemuImageInfo buildImageEntity(Map struct) { if (struct.containsKey("clustersize")) { qemuImageInfo.setClusterSize(Long.parseLong(struct.get("clustersize").toString())); } + if (struct.containsKey("bitmaps") && struct.get("bitmaps") != null) { + Object[] bitmaps = (Object[]) struct.get("bitmaps"); + qemuImageInfo.setQcow2bitmaps(vdsBrokerObjectsBuilder.buildQcow2Bitmaps(bitmaps)); + } } catch (RuntimeException ex) { log.error("Failed building Qemu image: {}", ex.getMessage()); printReturnValue(); diff --git a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsBrokerObjectsBuilder.java b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsBrokerObjectsBuilder.java index bede27c286a..9bd1effc147 100644 --- a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsBrokerObjectsBuilder.java +++ b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsBrokerObjectsBuilder.java @@ -99,6 +99,8 @@ import org.ovirt.engine.core.common.businessentities.storage.DiskVmElement; import org.ovirt.engine.core.common.businessentities.storage.LUNs; import org.ovirt.engine.core.common.businessentities.storage.LeaseJobStatus; +import org.ovirt.engine.core.common.businessentities.storage.Qcow2BitmapInfo; +import org.ovirt.engine.core.common.businessentities.storage.Qcow2BitmapInfoFlags; import org.ovirt.engine.core.common.businessentities.storage.StorageType; import org.ovirt.engine.core.common.businessentities.storage.VolumeFormat; import org.ovirt.engine.core.common.businessentities.storage.VolumeType; @@ -1040,6 +1042,7 @@ public void updateVDSDynamicData(VDS vds, Map vdsmNameMap, Map struct) { @@ -2824,4 +2827,23 @@ public LeaseStatus buildLeaseStatus(Map struct) { return leaseStatus; } + + public List buildQcow2Bitmaps(Object[] struct) { + ArrayList bitmaps = new ArrayList<>(); + + for (Map bitmap : (Map[]) struct) { + Qcow2BitmapInfo bitmapInfo = new Qcow2BitmapInfo(); + bitmapInfo.setName(Guid.createGuidFromString(bitmap.get("name").toString())); + bitmapInfo.setGranularity((Long)bitmap.get("granularity")); + + Object[] flags = (Object[]) bitmap.get("flags"); + List qcow2BitmapInfoFlags = Arrays.stream(flags) + .map(flag -> Qcow2BitmapInfoFlags.forValue(flag.toString())).collect(Collectors.toList()); + bitmapInfo.setFlags(qcow2BitmapInfoFlags); + + bitmaps.add(bitmapInfo); + } + + return bitmaps; + } } diff --git a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsProperties.java b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsProperties.java index 51915942c41..a7e8d7ce6d7 100644 --- a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsProperties.java +++ b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsProperties.java @@ -591,4 +591,6 @@ public final class VdsProperties { public static final String BOOT_UUID = "boot_uuid"; public static final String CD_CHANGE_PDIV = "cd_change_pdiv"; + + public static final String QEMU_IMAGE_INFO_BITMAPS = "qemu_image_info_bitmaps"; } diff --git a/backend/manager/modules/vdsbroker/src/test/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsBrokerObjectsBuilderTest.java b/backend/manager/modules/vdsbroker/src/test/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsBrokerObjectsBuilderTest.java index f41dc76e853..fa3b2c5f28e 100644 --- a/backend/manager/modules/vdsbroker/src/test/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsBrokerObjectsBuilderTest.java +++ b/backend/manager/modules/vdsbroker/src/test/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsBrokerObjectsBuilderTest.java @@ -16,6 +16,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import org.junit.jupiter.api.Test; import org.ovirt.engine.core.common.businessentities.LeaseStatus; @@ -25,6 +26,8 @@ import org.ovirt.engine.core.common.businessentities.network.VmInterfaceType; import org.ovirt.engine.core.common.businessentities.network.VmNetworkInterface; import org.ovirt.engine.core.common.businessentities.storage.DiskImageDynamic; +import org.ovirt.engine.core.common.businessentities.storage.Qcow2BitmapInfo; +import org.ovirt.engine.core.common.businessentities.storage.Qcow2BitmapInfoFlags; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.utils.serialization.json.JsonObjectDeserializer; @@ -481,4 +484,26 @@ private Map createCpuTopologyStruct() { new Object[] { cpuCapability1, cpuCapability2, cpuCapability3, cpuCapability4 }); return struct; } + + @Test + private static Map getBitmapAsMap(String name, Long granularity, List flags) { + Map bitmap = new HashMap<>(); + bitmap.put("name", name); + bitmap.put("granularity", granularity); + flags.stream().map(flag -> flag.getValue()).collect(Collectors.toList()).toArray(); + return bitmap; + } + + public void testBitmapList() { + Object[] bitmaps = new Object[2]; + bitmaps[0] = getBitmapAsMap("old-bitmap", 65536L, Arrays.asList(Qcow2BitmapInfoFlags.AUTO)); + bitmaps[1] = getBitmapAsMap("new-bitmap", 65536L, Arrays.asList(Qcow2BitmapInfoFlags.AUTO)); + List info = getBitmapInfo(bitmaps); + assertEquals("old-bitmap", info.get(0).getName()); + assertEquals("new-bitmap", info.get(1).getName()); + } + + private List getBitmapInfo(Object[] bitmaps) { + return vdsBrokerObjectsBuilder.buildQcow2Bitmaps(bitmaps); + } } diff --git a/packaging/dbscripts/create_views.sql b/packaging/dbscripts/create_views.sql index 0b2f5e56d2d..b0313f6f64a 100644 --- a/packaging/dbscripts/create_views.sql +++ b/packaging/dbscripts/create_views.sql @@ -1926,7 +1926,8 @@ SELECT cluster.cluster_id AS cluster_id, vds_dynamic.ovn_configured AS ovn_configured, vds_static.ssh_public_key AS ssh_public_key, vds_dynamic.cpu_topology AS cpu_topology, - vds_dynamic.vdsm_cpus_affinity AS vdsm_cpus_affinity + vds_dynamic.vdsm_cpus_affinity AS vdsm_cpus_affinity, + vds_dynamic.qemu_image_info_bitmaps AS qemu_image_info_bitmaps FROM cluster INNER JOIN vds_static ON cluster.cluster_id = vds_static.cluster_id @@ -2102,7 +2103,8 @@ SELECT cluster.cluster_id, vds_dynamic.ovn_configured AS ovn_configured, vds_static.ssh_public_key AS ssh_public_key, vds_dynamic.cpu_topology AS cpu_topology, - vds_dynamic.vdsm_cpus_affinity AS vdsm_cpus_affinity + vds_dynamic.vdsm_cpus_affinity AS vdsm_cpus_affinity, + vds_dynamic.qemu_image_info_bitmaps AS qemu_image_info_bitmaps FROM cluster INNER JOIN vds_static ON cluster.cluster_id = vds_static.cluster_id diff --git a/packaging/dbscripts/upgrade/04_05_0310_add_qemu_image_info_bitmaps_to_vdsdynamic.sql b/packaging/dbscripts/upgrade/04_05_0310_add_qemu_image_info_bitmaps_to_vdsdynamic.sql new file mode 100644 index 00000000000..9637fd331e5 --- /dev/null +++ b/packaging/dbscripts/upgrade/04_05_0310_add_qemu_image_info_bitmaps_to_vdsdynamic.sql @@ -0,0 +1 @@ +select fn_db_add_column('vds_dynamic', 'qemu_image_info_bitmaps', 'BOOLEAN NOT NULL DEFAULT FALSE'); diff --git a/packaging/dbscripts/upgrade/post_upgrade/0010_add_object_column_white_list_table.sql b/packaging/dbscripts/upgrade/post_upgrade/0010_add_object_column_white_list_table.sql index cb6ea10dfb4..d9cccee520b 100644 --- a/packaging/dbscripts/upgrade/post_upgrade/0010_add_object_column_white_list_table.sql +++ b/packaging/dbscripts/upgrade/post_upgrade/0010_add_object_column_white_list_table.sql @@ -87,3 +87,4 @@ SELECT fn_db_add_column_to_object_white_list('vds', 'backup_enabled'); SELECT fn_db_add_column_to_object_white_list('vds', 'cold_backup_enabled'); SELECT fn_db_add_column_to_object_white_list('vds', 'clear_bitmaps_enabled'); SELECT fn_db_add_column_to_object_white_list('vds', 'cd_change_pdiv'); +SELECT fn_db_add_column_to_object_white_list('vds', 'qemu_image_info_bitmaps'); diff --git a/packaging/dbscripts/vds_sp.sql b/packaging/dbscripts/vds_sp.sql index d077e3b487c..5f5eaecf53e 100644 --- a/packaging/dbscripts/vds_sp.sql +++ b/packaging/dbscripts/vds_sp.sql @@ -277,7 +277,8 @@ CREATE OR REPLACE FUNCTION InsertVdsDynamic ( v_cd_change_pdiv BOOLEAN, v_ovn_configured BOOLEAN, v_cpu_topology JSONB, - v_vdsm_cpus_affinity VARCHAR(256) + v_vdsm_cpus_affinity VARCHAR(256), + v_qemu_image_info_bitmaps BOOLEAN ) RETURNS VOID AS $FUNCTION$ BEGIN @@ -360,7 +361,8 @@ BEGIN cd_change_pdiv, ovn_configured, cpu_topology, - vdsm_cpus_affinity + vdsm_cpus_affinity, + qemu_image_info_bitmaps ) VALUES ( v_cpu_cores, @@ -440,7 +442,8 @@ BEGIN v_cd_change_pdiv, v_ovn_configured, v_cpu_topology, - v_vdsm_cpus_affinity + v_vdsm_cpus_affinity, + v_qemu_image_info_bitmaps ); END; @@ -545,7 +548,8 @@ CREATE OR REPLACE FUNCTION UpdateVdsDynamic ( v_cd_change_pdiv BOOLEAN, v_ovn_configured BOOLEAN, v_cpu_topology JSONB, - v_vdsm_cpus_affinity VARCHAR(256) + v_vdsm_cpus_affinity VARCHAR(256), + v_qemu_image_info_bitmaps BOOLEAN ) RETURNS VOID --The [vds_dynamic] table doesn't have a timestamp column. Optimistic concurrency logic cannot be generated @@ -633,7 +637,8 @@ BEGIN cd_change_pdiv = v_cd_change_pdiv, ovn_configured = v_ovn_configured, cpu_topology = v_cpu_topology, - vdsm_cpus_affinity = v_vdsm_cpus_affinity + vdsm_cpus_affinity = v_vdsm_cpus_affinity, + qemu_image_info_bitmaps = v_qemu_image_info_bitmaps WHERE vds_id = v_vds_id; END;