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

Reply via email to