Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement checkBitmap to check bitmap status before backup #866

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,9 @@ protected Map<String, Pair<String, String>> 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();
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -220,20 +222,21 @@ private VDSReturnValue performVmCheckpointsOperation(VDSCommandType vdsCommandTy
}

private void removeCheckpointChain(List<Guid> 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<DiskImage> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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();
sandrobonazzola marked this conversation as resolved.
Show resolved Hide resolved
}
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()) {
Expand Down Expand Up @@ -368,6 +390,45 @@ public boolean performNextOperation(int completedChildCount) {
return true;
}

private boolean validateCheckpoint(Guid checkpointId) {
List<DiskImage> 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<Qcow2BitmapInfo> 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());
Expand All @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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<Qcow2BitmapInfo> 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())) {
dupondje marked this conversation as resolved.
Show resolved Hide resolved
return null;
}

if (isHybridBackup() && getDiskImage().getBackupMode() == DiskBackupMode.Incremental) {
return getBackup().getFromCheckpointId();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class DeleteAllVmCheckpointsParameters extends VmOperationParameterBase {
@NotNull
private List<DiskImage> diskImages;
private int completedDisksCount;
private boolean force;

public DeleteAllVmCheckpointsParameters() {
}
Expand All @@ -24,6 +25,7 @@ public DeleteAllVmCheckpointsParameters(Guid vmId, List<DiskImage> diskImages) {
super(vmId);
this.diskImages = diskImages;
completedDisksCount = 0;
force = false;
}

public List<DiskImage> getDiskImages() {
Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ public VDS clone() {
vds.setBootUuid(getBootUuid());
vds.setCdChangePdiv(isCdChangePdiv());
vds.setOvnConfigured(isOvnConfigured());
vds.setQemuImageInfoBitmaps(isQemuImageInfoBitmaps());
return vds;
}

Expand Down Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ public class VdsDynamic implements BusinessEntityWithStatus<Guid, VDSStatus> {

private String vdsmCpusAffinity;

private boolean qemuImageInfoBitmaps;

public VdsDynamic() {
rpmVersion = new RpmVersion();
libvirtVersion = new RpmVersion();
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -1113,7 +1123,8 @@ public int hashCode() {
cdChangePdiv,
ovnConfigured,
cpuTopology,
vdsmCpusAffinity
vdsmCpusAffinity,
qemuImageInfoBitmaps
);
}

Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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<Qcow2BitmapInfoFlags> 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<Qcow2BitmapInfoFlags> getFlags() {
return flags;
}

public void setFlags(List<Qcow2BitmapInfoFlags> flags) {
this.flags = flags;
}
}
Loading