Tomas Jelinek has uploaded a new change for review. Change subject: core: WIP RFE clone VM ......................................................................
core: WIP RFE clone VM Do not review - only WIP Change-Id: I3038638314e399e9d2390e7204ce4cc00e85e6ae Signed-off-by: Tomas Jelinek <[email protected]> --- A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/CloneVmCommand.java A backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/CloneVmParameters.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/VdcActionType.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/job/StepEnum.java M backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/SnapshotDao.java M backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/SnapshotDaoDbFacadeImpl.java M backend/manager/modules/dal/src/main/resources/bundles/ExecutionMessages.properties M packaging/dbscripts/snapshots_sp.sql 8 files changed, 333 insertions(+), 1 deletion(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/05/23805/1 diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/CloneVmCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/CloneVmCommand.java new file mode 100644 index 0000000..448410f --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/CloneVmCommand.java @@ -0,0 +1,235 @@ +package org.ovirt.engine.core.bll; + +import org.ovirt.engine.core.bll.context.CommandContext; +import org.ovirt.engine.core.bll.job.ExecutionContext; +import org.ovirt.engine.core.bll.utils.PermissionSubject; +import org.ovirt.engine.core.common.VdcObjectType; +import org.ovirt.engine.core.common.action.AddVmFromSnapshotParameters; +import org.ovirt.engine.core.common.action.CloneVmParameters; +import org.ovirt.engine.core.common.action.CreateAllSnapshotsFromVmParameters; +import org.ovirt.engine.core.common.action.RemoveSnapshotParameters; +import org.ovirt.engine.core.common.action.VdcActionType; +import org.ovirt.engine.core.common.action.VdcReturnValueBase; +import org.ovirt.engine.core.common.action.VmOperationParameterBase; +import org.ovirt.engine.core.common.businessentities.Snapshot; +import org.ovirt.engine.core.common.errors.VdcBLLException; +import org.ovirt.engine.core.common.errors.VdcBllErrors; +import org.ovirt.engine.core.common.errors.VdcBllMessages; +import org.ovirt.engine.core.common.job.Step; +import org.ovirt.engine.core.common.job.StepEnum; +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.core.dal.dbbroker.DbFacade; +import org.ovirt.engine.core.utils.RandomUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +@NonTransactiveCommandAttribute +public class CloneVmCommand<T extends CloneVmParameters> extends AddVmCommand<T> { + + public static final String CLONE_SNAPSHOT_DESCRIPTION = "clone snapshot"; + + public CloneVmCommand(T parameters) { + super(parameters); + setVmId((parameters.getVmId().equals(Guid.Empty)) ? Guid.newGuid() : parameters.getVmId()); + parameters.getVm().setName(parameters.getNewName() + RandomUtils.instance().nextInt()); + setVmName(parameters.getVm().getName()); + } + + @Override + protected void executeCommand() { + if (hasStatelessSnapshot()) { + cloneVmFromSnapshot(); + } else { + createSnapshot(); + } + } + + @Override + protected void endSuccessfully() { + StepEnum stepType = getExecutionContext().getStep().getStepType(); + + if (stepType == StepEnum.CREATING_SNAPSHOTS) { + VdcReturnValueBase res = getBackend().endAction(VdcActionType.CreateAllSnapshotsFromVm, buildCreateSnapshotParametersForEndAction()); + getParameters().setShouldBeLogged(false); + + setSucceeded(res.getSucceeded()); + // only if success does make sense to continue + if (res.getSucceeded()) { + cloneVmFromSnapshot(); + } + + } else if (stepType == StepEnum.CLONING_VM_FROM_SNAPSHOT) { + VdcReturnValueBase res = getBackend().endAction(VdcActionType.AddVmFromSnapshot, buildAddVmFromSnapshotParameters()); + setSucceeded(res.getSucceeded()); + // try to clean up even the clone VM from it did not succeeded + removeSnapshot(); + } else if (stepType == StepEnum.REMOVING_SNAPSHOT) { + VdcReturnValueBase res = getBackend().endAction(VdcActionType.RemoveSnapshot, buildRemoveSnapshotParameters()); + setSucceeded(res.getSucceeded()); + } + } + + private void createSnapshot() { + VdcReturnValueBase vdcReturnValue = getBackend().runInternalAction( + VdcActionType.CreateAllSnapshotsFromVm, + buildCreateSnapshotParameters(), + createContext(StepEnum.CREATING_SNAPSHOTS)); + + boolean callNextExplicitly = actionPerformed(vdcReturnValue, VdcActionType.CreateAllSnapshotsFromVm, buildCreateSnapshotParametersForEndAction()); + // try to clone it only if the create snapshot has been successful + if (callNextExplicitly && getSucceeded()) { + cloneVmFromSnapshot(); + } + } + + private void cloneVmFromSnapshot() { + VdcReturnValueBase vdcReturnValue = getBackend().runInternalAction( + VdcActionType.AddVmFromSnapshot, + buildAddVmFromSnapshotParameters(), + createContext(StepEnum.CLONING_VM_FROM_SNAPSHOT)); + + boolean callNextExplicitly = actionPerformed(vdcReturnValue, VdcActionType.AddVmFromSnapshot, buildAddVmFromSnapshotParameters()); + + // try to clean up even the clone VM from it did not succeeded + if (callNextExplicitly) { + removeSnapshot(); + } + } + + private void removeSnapshot() { + // nothing to do, no snapshot has been taken + if (hasStatelessSnapshot()) { + return; + } + VdcReturnValueBase vdcReturnValue = getBackend().runInternalAction( + VdcActionType.RemoveSnapshot, + buildRemoveSnapshotParameters(), + createContext(StepEnum.REMOVING_SNAPSHOT)); + + // no subsequent actions needed, not checking if perform something next + actionPerformed(vdcReturnValue, VdcActionType.RemoveSnapshot, buildRemoveSnapshotParameters()); + } + + private boolean actionPerformed(VdcReturnValueBase vdcReturnValue, VdcActionType actionType, VmOperationParameterBase params) { + setSucceeded(vdcReturnValue.getSucceeded()); + + if (vdcReturnValue.getSucceeded()) { + List<Guid> internalVdsmTaskIdList = vdcReturnValue.getInternalVdsmTaskIdList(); + if (internalVdsmTaskIdList == null || internalVdsmTaskIdList.size() == 0) { + getBackend().endAction(actionType, params); + return true; + } else { + getReturnValue().getVdsmTaskIdList().addAll(internalVdsmTaskIdList); + } + } else { + if (areDisksLocked(vdcReturnValue)) { + throw new VdcBLLException(VdcBllErrors.IRS_IMAGE_STATUS_ILLEGAL); + } + getReturnValue().setFault(vdcReturnValue.getFault()); + log.errorFormat("Failed to execute action {0} for VM {1} ({2})", + actionType, getVm().getName(), getVm().getId()); + } + + return false; + } + + private CreateAllSnapshotsFromVmParameters buildCreateSnapshotParameters() { + CreateAllSnapshotsFromVmParameters parameters = new CreateAllSnapshotsFromVmParameters(getVmId(), + CLONE_SNAPSHOT_DESCRIPTION, + getParameters().getIncludeMemory(), + getParameters().getDiskImages()); + + parameters.setShouldBeLogged(false); + parameters.setParentCommand(getActionType()); + parameters.setEntityInfo(getParameters().getEntityInfo()); + parameters.setSnapshotType(Snapshot.SnapshotType.REGULAR); + parameters.setParentParameters(getParameters()); + parameters.setSessionId(getParameters().getSessionId()); + parameters.setQuotaId(getVm().getQuotaId()); + + return parameters; + } + + private CreateAllSnapshotsFromVmParameters buildCreateSnapshotParametersForEndAction() { + CreateAllSnapshotsFromVmParameters parameters = buildCreateSnapshotParameters(); + parameters.setImagesParameters(getParameters().getImagesParameters()); + + return parameters; + } + + private AddVmFromSnapshotParameters buildAddVmFromSnapshotParameters() { + Guid snapshotsId = DbFacade.getInstance().getSnapshotDao().getNewestId(getVmId(), hasStatelessSnapshot() ? Snapshot.SnapshotType.STATELESS : Snapshot.SnapshotType.REGULAR); + + getVm().getStaticData().setName(getParameters().getNewName()); + + AddVmFromSnapshotParameters parameters = + new AddVmFromSnapshotParameters(getVm().getStaticData(), snapshotsId); + parameters.setDiskInfoDestinationMap(getParameters().getDiskInfoDestinationMap()); + parameters.setSessionId(getParameters().getSessionId()); + parameters.setParentParameters(getParameters()); + parameters.setMakeCreatorExplicitOwner(getParameters().isMakeCreatorExplicitOwner()); + + return parameters; + } + + private RemoveSnapshotParameters buildRemoveSnapshotParameters() { + Guid snapshotsId = DbFacade.getInstance().getSnapshotDao().getNewestId(getVmId(), Snapshot.SnapshotType.REGULAR); + RemoveSnapshotParameters parameters = new RemoveSnapshotParameters(snapshotsId, getVmId()); + + parameters.setParentParameters(getParameters()); + parameters.setSessionId(getParameters().getSessionId()); + + return parameters; + } + + private boolean hasStatelessSnapshot() { + return DbFacade.getInstance().getSnapshotDao().exists(getVmId(), Snapshot.SnapshotType.STATELESS); + } + + private CommandContext createContext(StepEnum substepName) { + Map<String, String> values = getVmValuesForMsgResolving(); + Step createSnapshotsStep = addSubStep(StepEnum.EXECUTING, substepName, values); + + ExecutionContext createSnapshotsCtx = new ExecutionContext(); + createSnapshotsCtx.setMonitored(true); + createSnapshotsCtx.setStep(createSnapshotsStep); + return new CommandContext(createSnapshotsCtx, getCompensationContext(), getLock()); + } + + private boolean areDisksLocked(VdcReturnValueBase vdcReturnValue) { + return vdcReturnValue.getCanDoActionMessages().contains( + VdcBllMessages.ACTION_TYPE_FAILED_DISKS_LOCKED.name()); + } + + protected Map<String, String> getVmValuesForMsgResolving() { + return Collections.singletonMap(VdcObjectType.VM.name().toLowerCase(), getVmName()); + } + + @Override + public List<PermissionSubject> getPermissionCheckSubjects() { + // subcommands are run as internal, so checking the permissions here + List<PermissionSubject> permissionList = new ArrayList<>(); + + permissionList.add(new PermissionSubject(getParameters().getVmId(), + VdcObjectType.VM, + getActionType().getActionGroup())); + + permissionList.add(new PermissionSubject(getVdsGroupId(), + VdcObjectType.VdsGroups, + getActionType().getActionGroup())); + addPermissionSubjectForAdminLevelProperties(permissionList); + + addPermissionSubjectForAdminLevelProperties(permissionList); + + return permissionList; + } + + @Override + protected boolean canDoAction() { + // leaving to the subcommands' canDoActions + return true; + } +} diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/CloneVmParameters.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/CloneVmParameters.java new file mode 100644 index 0000000..a7acb8a --- /dev/null +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/CloneVmParameters.java @@ -0,0 +1,51 @@ +package org.ovirt.engine.core.common.action; + +import org.ovirt.engine.core.common.businessentities.DiskImage; +import org.ovirt.engine.core.common.businessentities.VM; + +import java.util.List; + +public class CloneVmParameters extends VmManagementParametersBase { + + public CloneVmParameters() { + + } + + public CloneVmParameters(VM vm, String newName, List<DiskImage> diskImages, boolean includeMemory) { + super(vm); + this.newName = newName; + this.diskImages = diskImages; + this.includeMemory = includeMemory; + } + + private List<DiskImage> diskImages; + + private String newName; + + private boolean includeMemory; + + public String getNewName() { + return newName; + } + + public void setNewName(String newName) { + this.newName = newName; + } + + public boolean getIncludeMemory() { + return includeMemory; + } + + public void setIncludeMemory(boolean includeMemory) { + this.includeMemory = includeMemory; + } + + public List<DiskImage> getDiskImages() { + return diskImages; + } + + public void setDiskImages(List<DiskImage> diskImages) { + this.diskImages = diskImages; + } + +} diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/VdcActionType.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/VdcActionType.java index a635343..a379724 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/VdcActionType.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/VdcActionType.java @@ -51,6 +51,8 @@ CancelMigrateVm(41, ActionGroup.MIGRATE_VM, false, QuotaDependency.NONE), ActivateDeactivateVmNic(42, QuotaDependency.NONE), AddVmFromSnapshot(52, ActionGroup.CREATE_VM, QuotaDependency.BOTH), + // the sub commands take care of it + CloneVm(53, ActionGroup.CREATE_VM, QuotaDependency.NONE), ImportVmFromConfiguration(43, ActionGroup.IMPORT_EXPORT_VM, QuotaDependency.NONE), UpdateVmVersion(44, QuotaDependency.NONE), // VdsCommands diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/job/StepEnum.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/job/StepEnum.java index c13c17a..778ae75 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/job/StepEnum.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/job/StepEnum.java @@ -18,6 +18,8 @@ MIGRATE_VM, CREATING_SNAPSHOTS, RUN_STATELESS_VM, + CLONING_VM_FROM_SNAPSHOT, + REMOVING_SNAPSHOT, TAKING_VM_FROM_POOL, // Gluster diff --git a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/SnapshotDao.java b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/SnapshotDao.java index ff5a87e..dbdd9a4 100644 --- a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/SnapshotDao.java +++ b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/SnapshotDao.java @@ -50,6 +50,19 @@ /** * Return the {@link Snapshot} <b>first</b> id that matches the given parameters.<br> + * <b>Note:</b> If more than one snapshot answers to the parameters, only the last will be returned (newest by + * creation date). + * + * @param vmId + * The id of the VM to check for. + * @param type + * The type of snapshot. + * @return The ID of the snapshot, or <code>null</code> if it doesn't exist. + */ + Guid getNewestId(Guid vmId, SnapshotType type); + + /** + * Return the {@link Snapshot} <b>first</b> id that matches the given parameters.<br> * <b>Note:</b> If more than one snapshot answers to the parameters, only the first will be returned (oldest by * creation date). * diff --git a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/SnapshotDaoDbFacadeImpl.java b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/SnapshotDaoDbFacadeImpl.java index 4abefb1..1d25af8 100644 --- a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/SnapshotDaoDbFacadeImpl.java +++ b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/SnapshotDaoDbFacadeImpl.java @@ -64,11 +64,20 @@ @Override public Guid getId(Guid vmId, SnapshotType type) { + return getIdCommon(vmId, type, "GetSnapshotIdsByVmIdAndType"); + } + + @Override + public Guid getNewestId(Guid vmId, SnapshotType type) { + return getIdCommon(vmId, type, "GetNewestSnapshotIdsByVmIdAndType"); + } + + private Guid getIdCommon(Guid vmId, SnapshotType type, String procedure) { MapSqlParameterSource parameterSource = getCustomMapSqlParameterSource() .addValue("vm_id", vmId) .addValue("snapshot_type", EnumUtils.nameOrNull(type)); - return getCallsHandler().executeRead("GetSnapshotIdsByVmIdAndType", + return getCallsHandler().executeRead(procedure, createGuidMapper(), parameterSource); } diff --git a/backend/manager/modules/dal/src/main/resources/bundles/ExecutionMessages.properties b/backend/manager/modules/dal/src/main/resources/bundles/ExecutionMessages.properties index 4c2e1b8..299e53d 100644 --- a/backend/manager/modules/dal/src/main/resources/bundles/ExecutionMessages.properties +++ b/backend/manager/modules/dal/src/main/resources/bundles/ExecutionMessages.properties @@ -127,6 +127,8 @@ step.ADD_VM_TO_POOL=Creating VM ${VM} for VM Pool step.MIGRATE_VM=Migrating VM ${VM} from Host ${VDS} step.CREATING_SNAPSHOTS=Creating snapshots for VM ${VM} +step.CLONING_VM_FROM_SNAPSHOT=Cloning VM from snapshot taken from VM ${VM} +step.REMOVING_SNAPSHOT=Removing snapshots for VM ${VM} step.RUN_STATELESS_VM=Running stateless VM ${VM} step.TAKING_VM_FROM_POOL=Taking VM ${VM} from VM pool in order to run it step.CLONE_IMAGE_STRUCTURE=Cloning image's structure diff --git a/packaging/dbscripts/snapshots_sp.sql b/packaging/dbscripts/snapshots_sp.sql index a270966..c5ffcdb 100644 --- a/packaging/dbscripts/snapshots_sp.sql +++ b/packaging/dbscripts/snapshots_sp.sql @@ -243,6 +243,24 @@ +Create or replace FUNCTION GetNewestSnapshotIdsByVmIdAndType( + v_vm_id UUID, + v_snapshot_type VARCHAR(32)) +RETURNS SETOF idUuidType STABLE +AS $procedure$ +BEGIN + RETURN QUERY + SELECT snapshot_id + FROM snapshots + WHERE vm_id = v_vm_id + AND snapshot_type = v_snapshot_type + ORDER BY creation_date DESC; +END; $procedure$ +LANGUAGE plpgsql; + + + + Create or replace FUNCTION GetSnapshotIdsByVmIdAndTypeAndStatus( v_vm_id UUID, v_snapshot_type VARCHAR(32), -- To view, visit http://gerrit.ovirt.org/23805 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I3038638314e399e9d2390e7204ce4cc00e85e6ae Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine Gerrit-Branch: master Gerrit-Owner: Tomas Jelinek <[email protected]> _______________________________________________ Engine-patches mailing list [email protected] http://lists.ovirt.org/mailman/listinfo/engine-patches
