Arik Hadas has uploaded a new change for review.

Change subject: core: remove memory snapshot when its not needed
......................................................................

core: remove memory snapshot when its not needed

This patch adds the removal of memory snapshot volumes in case it's not
needed anymore:
1. when removing VM that has snapshots with memory
2. when snapshots with memory are removed because we revert (commit) to
previous snapshot
3. on run VM when the VM reach to a point where the disks might not be
synchronous with the memory state that was restored anymore (and the
active snapshot is the only one that points to its memory)

This patch also treat the restoration of snapshot's memory when running
a VM that is previewing/being committed to a snapshot with memory in
stateless mode.

Change-Id: Ic087580475cd4fcfac969ddf0f69929fa247f86d
Bug-Url: https://bugzilla.redhat.com/960931
Signed-off-by: Arik Hadas <aha...@redhat.com>
---
M backend/manager/dbscripts/snapshots_sp.sql
M 
backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/AddVmCommand.java
M 
backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/CreateAllSnapshotsFromVmCommand.java
M 
backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RemoveSnapshotCommand.java
M 
backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RemoveVmCommand.java
M 
backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RestoreAllSnapshotsCommand.java
M 
backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RunVmCommand.java
M 
backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RunVmCommandBase.java
M 
backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RunVmOnceCommand.java
M 
backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/StopVmCommandBase.java
M 
backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/VmCommand.java
R 
backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/memory/LiveSnapshotMemoryImageBuilder.java
A 
backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/memory/StatelessSnapshotMemoryImageBuilder.java
M 
backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/snapshots/SnapshotsManager.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
16 files changed, 251 insertions(+), 126 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/82/15682/1

diff --git a/backend/manager/dbscripts/snapshots_sp.sql 
b/backend/manager/dbscripts/snapshots_sp.sql
index 30026b2..6a5878b 100644
--- a/backend/manager/dbscripts/snapshots_sp.sql
+++ b/backend/manager/dbscripts/snapshots_sp.sql
@@ -327,3 +327,15 @@
 END; $procedure$
 LANGUAGE plpgsql;
 
+Create or replace FUNCTION UpdateMemoryOfSnapshot(
+    v_snapshot_id UUID,
+    v_memory_volume VARCHAR(255))
+RETURNS VOID
+AS $procedure$
+BEGIN
+    UPDATE snapshots
+    SET    memory_volume = v_memory_volume
+    WHERE  snapshot_id = v_snapshot_id;
+END; $procedure$
+LANGUAGE plpgsql;
+
diff --git 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/AddVmCommand.java
 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/AddVmCommand.java
index c89f5b1..c70a1a8 100644
--- 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/AddVmCommand.java
+++ 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/AddVmCommand.java
@@ -716,7 +716,7 @@
     protected void removeVmRelatedEntitiesFromDb() {
         removeVmUsers();
         removeVmNetwork();
-        new SnapshotsManager().removeSnapshots(getVmId());
+        removeVmSnapshots();
         removeVmStatic();
     }
 
diff --git 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/CreateAllSnapshotsFromVmCommand.java
 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/CreateAllSnapshotsFromVmCommand.java
index 10f6832..9a7031e 100644
--- 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/CreateAllSnapshotsFromVmCommand.java
+++ 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/CreateAllSnapshotsFromVmCommand.java
@@ -9,9 +9,10 @@
 import org.apache.commons.lang.exception.ExceptionUtils;
 import org.ovirt.engine.core.bll.job.ExecutionContext;
 import org.ovirt.engine.core.bll.job.ExecutionHandler;
-import org.ovirt.engine.core.bll.memory.DefaultMemoryImageBuilder;
+import org.ovirt.engine.core.bll.memory.LiveSnapshotMemoryImageBuilder;
 import org.ovirt.engine.core.bll.memory.MemoryImageBuilder;
 import org.ovirt.engine.core.bll.memory.NullableMemoryImageBuilder;
+import org.ovirt.engine.core.bll.memory.StatelessSnapshotMemoryImageBuilder;
 import org.ovirt.engine.core.bll.quota.QuotaConsumptionParameter;
 import org.ovirt.engine.core.bll.quota.QuotaStorageConsumptionParameter;
 import org.ovirt.engine.core.bll.quota.QuotaStorageDependent;
@@ -146,14 +147,20 @@
     }
 
     private MemoryImageBuilder createMemoryImageBuilder() {
-        if 
(FeatureSupported.memorySnapshot(getVm().getVdsGroupCompatibilityVersion())
-                && getParameters().isSaveMemory() && 
isLiveSnapshotApplicable()) {
-            prepareForMemorySnapshot = true;
-            return new DefaultMemoryImageBuilder(getVm(), 
getStorageDomainIdForVmMemory(), getStoragePool(), this);
-        }
-        else {
+        if 
(!FeatureSupported.memorySnapshot(getVm().getVdsGroupCompatibilityVersion())) {
             return new NullableMemoryImageBuilder();
         }
+
+        if (getParameters().getSnapshotType() == SnapshotType.STATELESS) {
+            return new StatelessSnapshotMemoryImageBuilder(getVm());
+        }
+
+        if (getParameters().isSaveMemory() && isLiveSnapshotApplicable()) {
+            prepareForMemorySnapshot = true;
+            return new LiveSnapshotMemoryImageBuilder(getVm(), 
getStorageDomainIdForVmMemory(), getStoragePool(), this);
+        }
+
+        return new NullableMemoryImageBuilder();
     }
 
     private Snapshot addSnapshotToDB(Guid snapshotId, MemoryImageBuilder 
memoryImageBuilder) {
@@ -223,10 +230,32 @@
                     if (isLiveSnapshotApplicable()) {
                         performLiveSnapshot(createdSnapshot);
                     }
-                    // TODO: else remove the memory image if exists
+                    else {
+                        // If the created snapshot contains memory, remove the 
memory volumes as
+                        // they are not in use since no live snapshot was 
created
+                        String memoryVolume = 
createdSnapshot.getMemoryVolume();
+                        if (!memoryVolume.isEmpty() &&
+                                
getSnapshotDao().getNumOfSnapshotsByMemory(memoryVolume) == 1) {
+                            boolean succeed = 
removeMemoryVolumes(memoryVolume, getActionType(), false);
+                            if (!succeed) {
+                                log.errorFormat("Failed to remove unused 
memory {0} of snapshot {1}",
+                                        memoryVolume, createdSnapshot.getId());
+                            }
+                        }
+                    }
                 } else {
                     revertToActiveSnapshot(createdSnapshot.getId());
-                    // TODO: remove the memory image if exists
+                    // If the removed snapshot contained memory, remove the 
memory volumes
+                    // Note that the memory volumes might not have been created
+                    String memoryVolume = createdSnapshot.getMemoryVolume();
+                    if (!memoryVolume.isEmpty() &&
+                            
getSnapshotDao().getNumOfSnapshotsByMemory(memoryVolume) == 1) {
+                        boolean succeed = removeMemoryVolumes(memoryVolume, 
getActionType(), false);
+                        if (!succeed) {
+                            log.warnFormat("Failed to remove memory {0} of 
snapshot {1}",
+                                    memoryVolume, createdSnapshot.getId());
+                        }
+                    }
                 }
 
                 incrementVmGeneration();
diff --git 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RemoveSnapshotCommand.java
 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RemoveSnapshotCommand.java
index f61dfac..4b7a6f9 100644
--- 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RemoveSnapshotCommand.java
+++ 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RemoveSnapshotCommand.java
@@ -23,7 +23,6 @@
 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.businessentities.Disk;
 import org.ovirt.engine.core.common.businessentities.DiskImage;
 import org.ovirt.engine.core.common.businessentities.Snapshot;
 import org.ovirt.engine.core.common.businessentities.Snapshot.SnapshotStatus;
@@ -31,18 +30,12 @@
 import org.ovirt.engine.core.common.businessentities.VMStatus;
 import org.ovirt.engine.core.common.errors.VdcBLLException;
 import org.ovirt.engine.core.common.errors.VdcBllErrors;
-import org.ovirt.engine.core.common.locks.LockingGroup;
 import org.ovirt.engine.core.common.errors.VdcBllMessages;
+import org.ovirt.engine.core.common.locks.LockingGroup;
 import org.ovirt.engine.core.common.utils.Pair;
-import 
org.ovirt.engine.core.common.vdscommands.DeleteImageGroupVDSCommandParameters;
-import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
-import org.ovirt.engine.core.common.vdscommands.VDSReturnValue;
 import org.ovirt.engine.core.compat.Guid;
 import org.ovirt.engine.core.dao.SnapshotDao;
-import org.ovirt.engine.core.utils.GuidUtils;
 import org.ovirt.engine.core.utils.collections.MultiValueMapUtils;
-import org.ovirt.engine.core.utils.linq.LinqUtils;
-import org.ovirt.engine.core.utils.linq.Predicate;
 import org.ovirt.engine.core.utils.transaction.TransactionMethod;
 import org.ovirt.engine.core.utils.transaction.TransactionSupport;
 
@@ -123,60 +116,10 @@
         setSucceeded(true);
     }
 
-    /**
-     * There is a one to many relation between memory volumes and snapshots, 
so memory
-     * volumes should be removed only if the only snapshot that points to them 
is removed
-     */
-    private boolean shouldRemoveMemorySnapshotVolumes(String memoryVolume) {
-        return !memoryVolume.isEmpty() &&
-                getSnapshotDao().getNumOfSnapshotsByMemory(memoryVolume) == 1;
-    }
-
     private void removeMemory(final Snapshot snapshot) {
-        List<Guid> guids = 
GuidUtils.getGuidListFromString(snapshot.getMemoryVolume());
-
-        // get all vm disks in order to check post zero - if one of the
-        // disks is marked with wipe_after_delete
-        boolean postZero =
-                LinqUtils.filter(getDiskDao().getAllForVm(getVm().getId()),
-                        new Predicate<Disk>() {
-                    @Override
-                    public boolean eval(Disk disk) {
-                        return disk.isWipeAfterDelete();
-                    }
-                }).size() > 0;
-
-        // delete first image
-        // the next 'DeleteImageGroup' command should also take care of the
-        // image removal:
-        VDSReturnValue vdsRetValue = runVdsCommand(
-                VDSCommandType.DeleteImageGroup,
-                new DeleteImageGroupVDSCommandParameters(guids.get(1), 
guids.get(0), guids.get(2),
-                        postZero, false, 
getVm().getVdsGroupCompatibilityVersion().toString()));
-
-        if (!vdsRetValue.getSucceeded()) {
-            log.errorFormat("Cannot remove memory dump volume for snapshot 
{0}", snapshot.getId());
-        }
-        else {
-            Guid guid =
-                    createTask(vdsRetValue.getCreationInfo(), 
VdcActionType.RemoveSnapshot, VdcObjectType.Storage, guids.get(0));
-            getReturnValue().getTaskIdList().add(guid);
-        }
-
-        // delete second image
-        // the next 'DeleteImageGroup' command should also take care of the 
image removal:
-        vdsRetValue = runVdsCommand(
-                VDSCommandType.DeleteImageGroup,
-                new DeleteImageGroupVDSCommandParameters(guids.get(1), 
guids.get(0), guids.get(4),
-                        postZero, false, 
getVm().getVdsGroupCompatibilityVersion().toString()));
-
-        if (!vdsRetValue.getSucceeded()) {
-            log.errorFormat("Cannot remove memory metadata volume for snapshot 
{0}", snapshot.getId());
-        }
-        else {
-            Guid guid =
-                    createTask(vdsRetValue.getCreationInfo(), 
VdcActionType.RemoveSnapshot, VdcObjectType.Storage, guids.get(0));
-            getReturnValue().getTaskIdList().add(guid);
+        boolean success = removeMemoryVolumes(snapshot.getMemoryVolume(), 
getActionType(), false);
+        if (!success) {
+            log.errorFormat("Cannot remove memory volumes for snapshot {0}", 
snapshot.getId());
         }
     }
 
diff --git 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RemoveVmCommand.java
 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RemoveVmCommand.java
index b696746..cb6539d 100644
--- 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RemoveVmCommand.java
+++ 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RemoveVmCommand.java
@@ -12,7 +12,6 @@
 import org.ovirt.engine.core.bll.quota.QuotaConsumptionParameter;
 import org.ovirt.engine.core.bll.quota.QuotaStorageConsumptionParameter;
 import org.ovirt.engine.core.bll.quota.QuotaStorageDependent;
-import org.ovirt.engine.core.bll.snapshots.SnapshotsManager;
 import org.ovirt.engine.core.bll.snapshots.SnapshotsValidator;
 import org.ovirt.engine.core.bll.storage.StoragePoolValidator;
 import org.ovirt.engine.core.bll.utils.PermissionSubject;
@@ -249,7 +248,7 @@
         removeLunDisks();
         removeVmUsers();
         removeVmNetwork();
-        new SnapshotsManager().removeSnapshots(getVmId());
+        removeVmSnapshots();
         removeVmStatic();
     }
 
diff --git 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RestoreAllSnapshotsCommand.java
 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RestoreAllSnapshotsCommand.java
index 3a85d1a..b9f1508 100644
--- 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RestoreAllSnapshotsCommand.java
+++ 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RestoreAllSnapshotsCommand.java
@@ -125,6 +125,15 @@
 
     protected void removeSnapshotsFromDB() {
         for (Guid snapshotId : snapshotsToRemove) {
+            String memoryVolume = 
getSnapshotDao().get(snapshotId).getMemoryVolume();
+            if (!memoryVolume.isEmpty() &&
+                    getSnapshotDao().getNumOfSnapshotsByMemory(memoryVolume) 
== 1) {
+                boolean succeed = removeMemoryVolumes(memoryVolume, 
getActionType(), false);
+                if (!succeed) {
+                    log.errorFormat("Failed to remove memory {0} of snapshot 
{1}",
+                            memoryVolume, snapshotId);
+                }
+            }
             getSnapshotDao().remove(snapshotId);
         }
     }
diff --git 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RunVmCommand.java
 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RunVmCommand.java
index 1706e50..82a7301 100644
--- 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RunVmCommand.java
+++ 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RunVmCommand.java
@@ -63,8 +63,10 @@
 import org.ovirt.engine.core.common.vdscommands.VDSReturnValue;
 import org.ovirt.engine.core.compat.Guid;
 import org.ovirt.engine.core.compat.Version;
+import org.ovirt.engine.core.dal.dbbroker.DbFacade;
 import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogDirector;
 import org.ovirt.engine.core.dal.job.ExecutionMessageDirector;
+import org.ovirt.engine.core.dao.SnapshotDao;
 import org.ovirt.engine.core.utils.NetworkUtils;
 import org.ovirt.engine.core.utils.linq.LinqUtils;
 import org.ovirt.engine.core.utils.linq.Predicate;
@@ -81,10 +83,16 @@
     private String _cdImagePath = "";
     private String _floppyImagePath = "";
     private boolean mResume;
-    private boolean _isVmRunningStateless;
+    /** Note: this field should not be used directly, use {@link 
#isVmRunningStateless()} instead */
+    private Boolean cachedVmIsRunningStateless;
     private boolean isFailedStatlessSnapshot;
     /** Indicates whether restoration of memory from snapshot is supported for 
the VM */
     private boolean memorySnapshotSupported;
+    /** The memory volume which is stored in the active snapshot of the VM */
+    private String memoryVolumeFromSnapshot = StringUtils.EMPTY;
+    /** This flag is used to indicate that the disks might be dirty since the 
memory
+     *  from the active snapshot was restored so the memory should not be used 
*/
+    private boolean memoryFromSnapshotIrrelevant;
 
     protected RunVmCommand(Guid commandId) {
         super(commandId);
@@ -135,8 +143,23 @@
 
             if (getVm().getStatus() != VMStatus.Suspended) {
                 memorySnapshotSupported = 
FeatureSupported.memorySnapshot(getVm().getVdsGroupCompatibilityVersion());
+                // If the VM is not hibernated, save the hibernation volume 
from the baseline snapshot
+                memoryVolumeFromSnapshot = 
getBaselineSnapshot().getMemoryVolume();
             }
         }
+    }
+
+    /**
+     * Returns the snapshot the vm is based on - either the active snapshot 
(usually) or
+     * the stateless snapshot in case the VM is running in stateless mode
+     */
+    private Snapshot getBaselineSnapshot() {
+        return getSnapshotDao().get(getVm().getId(),
+                isVmRunningStateless() ? SnapshotType.STATELESS : 
SnapshotType.ACTIVE);
+    }
+
+    private SnapshotDao getSnapshotDao() {
+        return DbFacade.getInstance().getSnapshotDao();
     }
 
     /**
@@ -234,9 +257,8 @@
             if (status != null && (status.isRunning() || status == 
VMStatus.RestoringState)) {
                 setSucceeded(true);
             } else {
-                // Try to rerun Vm on different vds
-                // no need to log the command because it is being logged inside
-                // the rerun
+                // Try to rerun Vm on different vds no need to log the command 
because it is
+                // being logged inside the rerun
                 log.infoFormat("Failed to run desktop {0}, rerun", 
getVm().getName());
                 setCommandShouldBeLogged(false);
                 setSucceeded(true);
@@ -254,8 +276,8 @@
 
     @Override
     protected void executeVmCommand() {
-        // Before running the VM we update its devices, as they may need to be 
changed due to configuration option
-        // change
+        // Before running the VM we update its devices, as they may need to be 
changed due to
+        // configuration option change
         VmDeviceUtils.updateVmDevices(getVm().getStaticData());
         setActionReturnValue(VMStatus.Down);
         if (initVm()) {
@@ -274,7 +296,7 @@
                     }
                 } else if (!isInternalExecution() && !_isRerun
                         && getVm().getStatus() != VMStatus.Suspended
-                        && statelessSnapshotExistsForVm()
+                        && isStatelessSnapshotExistsForVm()
                         && !isVMPartOfManualPool()) {
                     removeVmStatlessImages();
                 } else {
@@ -286,8 +308,8 @@
         }
     }
 
-    private boolean statelessSnapshotExistsForVm() {
-        return getDbFacade().getSnapshotDao().exists(getVm().getId(), 
SnapshotType.STATELESS);
+    private boolean isStatelessSnapshotExistsForVm() {
+        return getSnapshotDao().exists(getVm().getId(), 
SnapshotType.STATELESS);
     }
 
     /**
@@ -324,7 +346,7 @@
     private void statelessVmTreatment() {
         warnIfNotAllDisksPermitSnapshots();
 
-        if (statelessSnapshotExistsForVm()) {
+        if (isStatelessSnapshotExistsForVm()) {
             log.errorFormat(
                     "RunVmAsStateless - {0} - found existing vm images in 
stateless_vm_image_map table - skipped creating snapshots.",
                     getVm().getName());
@@ -444,34 +466,38 @@
 
         VMStatus vmStatus = (VMStatus) getBackend()
                 .getResourceManager()
-                .RunAsyncVdsCommand(VDSCommandType.CreateVm, 
initVdsCreateVmParams(), this).getReturnValue();
+                .RunAsyncVdsCommand(VDSCommandType.CreateVm, 
initCreateVmParams(), this).getReturnValue();
+
+        // Don't use the memory from the active snapshot anymore if there's a 
chance that disks were changed
+        memoryFromSnapshotIrrelevant = vmStatus.isRunning() || vmStatus == 
VMStatus.RestoringState;
 
         // After VM was create (or not), we can remove the quota vds group 
memory.
         return vmStatus;
     }
 
+
     /**
-     * Initial the parameters for the VDSM command of VM creation
+     * Initialize the parameters for the VDSM command of VM creation
      * @return the VDS create VM parameters
      */
-    protected CreateVmVDSCommandParameters initVdsCreateVmParams() {
+    protected CreateVmVDSCommandParameters initCreateVmParams() {
         VM vmToBeCreated = getVm();
 
         if (vmToBeCreated.getStatus() == VMStatus.Suspended) {
             return new CreateVmVDSCommandParameters(getVdsId(), vmToBeCreated);
         }
 
-        if (!memorySnapshotSupported) {
+        if (!memorySnapshotSupported || memoryFromSnapshotIrrelevant) {
             vmToBeCreated.setHibernationVolHandle(StringUtils.EMPTY);
             return new CreateVmVDSCommandParameters(getVdsId(), vmToBeCreated);
         }
 
         // otherwise, use the memory that is saved on the active snapshot 
(might be empty)
-        Snapshot activeSnapshotOfVmToBeCreated =
-                getDbFacade().getSnapshotDao().get(vmToBeCreated.getId(), 
SnapshotType.ACTIVE);
-        
vmToBeCreated.setHibernationVolHandle(activeSnapshotOfVmToBeCreated.getMemoryVolume());
+        vmToBeCreated.setHibernationVolHandle(memoryVolumeFromSnapshot);
         CreateVmVDSCommandParameters parameters =
                 new CreateVmVDSCommandParameters(getVdsId(), vmToBeCreated);
+        // Mark that the hibernation volume should be cleared from the VM 
right after the sync part of
+        // the create verb is finished (unlike hibernation volume that is 
created by hibernate command)
         parameters.setClearHibernationVolumes(true);
         return parameters;
     }
@@ -487,7 +513,7 @@
                 return getSucceeded() ? AuditLogType.USER_RESUME_VM : 
AuditLogType.USER_FAILED_RESUME_VM;
             } else if (isInternalExecution()) {
                 return getSucceeded() ?
-                        (statelessSnapshotExistsForVm() ? 
AuditLogType.VDS_INITIATED_RUN_VM_AS_STATELESS
+                        (isStatelessSnapshotExistsForVm() ? 
AuditLogType.VDS_INITIATED_RUN_VM_AS_STATELESS
                                 : AuditLogType.VDS_INITIATED_RUN_VM) :
                         AuditLogType.VDS_INITIATED_RUN_VM_FAILED;
             } else {
@@ -497,7 +523,7 @@
                                         && getVm().getDedicatedVmForVds() != 
null
                                         && 
!getVm().getRunOnVds().equals(getVm().getDedicatedVmForVds()) ?
                                                 
AuditLogType.USER_RUN_VM_ON_NON_DEFAULT_VDS :
-                                                
(statelessSnapshotExistsForVm() ? AuditLogType.USER_RUN_VM_AS_STATELESS : 
AuditLogType.USER_RUN_VM)
+                                                
(isStatelessSnapshotExistsForVm() ? AuditLogType.USER_RUN_VM_AS_STATELESS : 
AuditLogType.USER_RUN_VM)
                                 : _isRerun ?
                                         AuditLogType.VDS_INITIATED_RUN_VM
                                         : getTaskIdList().size() > 0 ?
@@ -510,13 +536,13 @@
             // if not running as stateless, or if succeeded running as
             // stateless,
             // command should be with 'CommandShouldBeLogged = false':
-            return _isVmRunningStateless && !getSucceeded() ? 
AuditLogType.USER_RUN_VM_AS_STATELESS_FINISHED_FAILURE
+            return isVmRunningStateless() && !getSucceeded() ? 
AuditLogType.USER_RUN_VM_AS_STATELESS_FINISHED_FAILURE
                     : AuditLogType.UNASSIGNED;
 
         case END_FAILURE:
             // if not running as stateless, command should
             // be with 'CommandShouldBeLogged = false':
-            return _isVmRunningStateless ? 
AuditLogType.USER_RUN_VM_AS_STATELESS_FINISHED_FAILURE
+            return isVmRunningStateless() ? 
AuditLogType.USER_RUN_VM_AS_STATELESS_FINISHED_FAILURE
                     : AuditLogType.UNASSIGNED;
 
         default:
@@ -746,9 +772,7 @@
 
     @Override
     protected void endSuccessfully() {
-        setIsVmRunningStateless();
-
-        if (_isVmRunningStateless) {
+        if (isVmRunningStateless()) {
             CreateAllSnapshotsFromVmParameters createSnapshotParameters = 
buildCreateSnapshotParameters();
             
createSnapshotParameters.setImagesParameters(getParameters().getImagesParameters());
             getBackend().EndAction(VdcActionType.CreateAllSnapshotsFromVm, 
createSnapshotParameters);
@@ -781,8 +805,7 @@
                     .runInternalAction(getActionType(), getParameters(), new 
CommandContext(runStatelessVmCtx))
                     .getSucceeded());
             if (!getSucceeded()) {
-                // could not run the vm don't try to run the end action
-                // again
+                // could not run the vm don't try to run the end action again
                 log.warnFormat("Could not run the vm {0} on 
RunVm.EndSuccessfully", getVm().getName());
                 getReturnValue().setEndActionTryAgain(false);
             }
@@ -796,8 +819,7 @@
 
     @Override
     protected void endWithFailure() {
-        setIsVmRunningStateless();
-        if (_isVmRunningStateless) {
+        if (isVmRunningStateless()) {
             CreateAllSnapshotsFromVmParameters createSnapshotParameters = 
buildCreateSnapshotParameters();
             
createSnapshotParameters.setImagesParameters(getParameters().getImagesParameters());
             VdcReturnValueBase vdcReturnValue = 
getBackend().endAction(VdcActionType.CreateAllSnapshotsFromVm,
@@ -813,8 +835,37 @@
         }
     }
 
-    private void setIsVmRunningStateless() {
-        _isVmRunningStateless = statelessSnapshotExistsForVm();
+    @Override
+    public void runningSucceded() {
+        removeMemoryFromSnapshot();
+        super.runningSucceded();
+    }
+
+    @Override
+    protected void failedToRunVm() {
+        if (memoryFromSnapshotIrrelevant) {
+            removeMemoryFromSnapshot();
+        }
+        super.failedToRunVm();
+    }
+
+    private void removeMemoryFromSnapshot() {
+        if (memoryVolumeFromSnapshot.isEmpty()) {
+            return;
+        }
+
+        // If the active snapshot is the only one that points to the memory 
volume we can remove it
+        if 
(getSnapshotDao().getNumOfSnapshotsByMemory(memoryVolumeFromSnapshot) == 1) {
+            removeMemoryVolumes(memoryVolumeFromSnapshot, getActionType(), 
true);
+        }
+        
getSnapshotDao().removeMemoryFromSnapshot(getBaselineSnapshot().getId());
+    }
+
+    private boolean isVmRunningStateless() {
+        if (cachedVmIsRunningStateless == null) {
+            cachedVmIsRunningStateless = isStatelessSnapshotExistsForVm();
+        }
+        return cachedVmIsRunningStateless;
     }
 
     /**
diff --git 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RunVmCommandBase.java
 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RunVmCommandBase.java
index 62478a7..baa8fe7 100644
--- 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RunVmCommandBase.java
+++ 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RunVmCommandBase.java
@@ -9,6 +9,7 @@
 import java.util.Map;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.TimeUnit;
+
 import org.apache.commons.lang.StringUtils;
 import org.ovirt.engine.core.bll.job.ExecutionContext;
 import org.ovirt.engine.core.bll.job.ExecutionContext.ExecutionMethod;
@@ -223,7 +224,7 @@
             getVm().setLastVdsRunOn(getCurrentVdsId());
         }
         if (StringUtils.isNotEmpty(getVm().getHibernationVolHandle())) {
-            handleHibernatedVm(getActionType(), true);
+            removeMemoryVolumes(getVm().getHibernationVolHandle(), 
getActionType(), true);
             // In order to prevent a race where VdsUpdateRuntimeInfo saves the 
Vm Dynamic as UP prior to execution of
             // this method (which is a part of the cached VM command,
             // so the state this method is aware to is RESTORING, in case of 
RunVmCommand after the VM got suspended.
diff --git 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RunVmOnceCommand.java
 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RunVmOnceCommand.java
index 7abb99c..230fdb7 100644
--- 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RunVmOnceCommand.java
+++ 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RunVmOnceCommand.java
@@ -6,8 +6,8 @@
 import org.ovirt.engine.core.bll.job.ExecutionContext;
 import org.ovirt.engine.core.bll.job.ExecutionHandler;
 import org.ovirt.engine.core.bll.quota.QuotaConsumptionParameter;
-import org.ovirt.engine.core.bll.quota.QuotaStorageDependent;
 import org.ovirt.engine.core.bll.quota.QuotaStorageConsumptionParameter;
+import org.ovirt.engine.core.bll.quota.QuotaStorageDependent;
 import org.ovirt.engine.core.common.action.RunVmOnceParams;
 import org.ovirt.engine.core.common.action.RunVmParams;
 import org.ovirt.engine.core.common.action.SysPrepParams;
@@ -57,9 +57,9 @@
     }
 
     @Override
-    protected CreateVmVDSCommandParameters initVdsCreateVmParams() {
+    protected CreateVmVDSCommandParameters initCreateVmParams() {
         getVm().setRunOnce(true);
-        CreateVmVDSCommandParameters createVmParams = 
super.initVdsCreateVmParams();
+        CreateVmVDSCommandParameters createVmParams = 
super.initCreateVmParams();
         SysPrepParams sysPrepParams = new SysPrepParams();
         RunVmOnceParams runOnceParams = getParameters();
         
sysPrepParams.setSysPrepDomainName(runOnceParams.getSysPrepDomainName());
diff --git 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/StopVmCommandBase.java
 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/StopVmCommandBase.java
index a2e06e3..9462562 100644
--- 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/StopVmCommandBase.java
+++ 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/StopVmCommandBase.java
@@ -113,7 +113,7 @@
             // Set the VM to image locked to decrease race condition.
             updateVmStatus(VMStatus.ImageLocked);
             if (StringUtils.isNotEmpty(getVm().getHibernationVolHandle())
-                    && handleHibernatedVm(getActionType(), false)) {
+                    && removeMemoryVolumes(getVm().getHibernationVolHandle(), 
getActionType(), false)) {
                 returnVal = true;
             } else {
                 updateVmStatus(vmStatus);
diff --git 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/VmCommand.java
 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/VmCommand.java
index de8f451..b9f4080 100644
--- 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/VmCommand.java
+++ 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/VmCommand.java
@@ -1,10 +1,11 @@
 package org.ovirt.engine.core.bll;
 
 import java.util.ArrayList;
-import java.util.LinkedList;
+import java.util.Collection;
 import java.util.List;
 
 import org.ovirt.engine.core.bll.network.MacPoolManager;
+import org.ovirt.engine.core.bll.snapshots.SnapshotsManager;
 import org.ovirt.engine.core.bll.utils.PermissionSubject;
 import org.ovirt.engine.core.common.VdcObjectType;
 import org.ovirt.engine.core.common.action.VdcActionParametersBase;
@@ -165,6 +166,25 @@
         }
     }
 
+    protected void removeVmSnapshots() {
+        Collection<String> memoriesOfRemovedSnapshots =
+                new SnapshotsManager().removeSnapshots(getVmId());
+        for (String memoryVolumes : memoriesOfRemovedSnapshots) {
+            if (shouldRemoveMemorySnapshotVolumes(memoryVolumes)) {
+                removeMemoryVolumes(memoryVolumes, getActionType(), false);
+            }
+        }
+    }
+
+    /**
+     * There is a one to many relation between memory volumes and snapshots, 
so memory
+     * volumes should be removed only if the only snapshot that points to them 
is removed
+     */
+    protected boolean shouldRemoveMemorySnapshotVolumes(String memoryVolume) {
+        return !memoryVolume.isEmpty() &&
+                
getDbFacade().getSnapshotDao().getNumOfSnapshotsByMemory(memoryVolume) == 1;
+    }
+
     protected void removeVmUsers() {
         List<TagsVmMap> all = 
getTagDao().getTagVmMapByVmIdAndDefaultTag(getVmId());
         for (TagsVmMap tagVm : all) {
@@ -232,15 +252,15 @@
         return VdcActionType.Unknown;
     }
 
-    protected boolean handleHibernatedVm(VdcActionType parentCommand, boolean 
startPollingTasks) {
+    protected boolean removeMemoryVolumes(String memVols, VdcActionType 
parentCommand, boolean startPollingTasks) {
         // this is temp code until it will be implemented in SPM
-        String[] strings = getVm().getHibernationVolHandle().split(",");
-        List<Guid> guids = new LinkedList<Guid>();
-        for (String string : strings) {
-            guids.add(new Guid(string));
+        String[] strings = memVols.split(",");
+        Guid[] guids = new Guid[strings.length];
+        for (int i=0; i<strings.length; ++i) {
+            guids[i] = new Guid(strings[i]);
         }
-        Guid[] imagesList = guids.toArray(new Guid[0]);
-        if (imagesList.length == 6) {
+
+        if (guids.length == 6) {
             // get all vm disks in order to check post zero - if one of the
             // disks is marked with wipe_after_delete
             boolean postZero =
@@ -253,11 +273,10 @@
                             }).size() > 0;
 
             // delete first image
-            // the next 'DeleteImageGroup' command should also take care of the
-            // image removal:
+            // the next 'DeleteImageGroup' command should also take care of 
the image removal:
             VDSReturnValue vdsRetValue1 = runVdsCommand(
                     VDSCommandType.DeleteImageGroup,
-                    new DeleteImageGroupVDSCommandParameters(imagesList[1], 
imagesList[0], imagesList[2],
+                    new DeleteImageGroupVDSCommandParameters(guids[1], 
guids[0], guids[2],
                             postZero, false, 
getVm().getVdsGroupCompatibilityVersion().toString()));
 
             if (!vdsRetValue1.getSucceeded()) {
@@ -265,15 +284,14 @@
             }
 
             Guid guid1 =
-                    createTask(vdsRetValue1.getCreationInfo(), parentCommand, 
VdcObjectType.Storage, imagesList[0]);
+                    createTask(vdsRetValue1.getCreationInfo(), parentCommand, 
VdcObjectType.Storage, guids[0]);
             getTaskIdList().add(guid1);
 
             // delete second image
-            // the next 'DeleteImageGroup' command should also take care of the
-            // image removal:
+            // the next 'DeleteImageGroup' command should also take care of 
the image removal:
             VDSReturnValue vdsRetValue2 = runVdsCommand(
                     VDSCommandType.DeleteImageGroup,
-                    new DeleteImageGroupVDSCommandParameters(imagesList[1], 
imagesList[0], imagesList[4],
+                    new DeleteImageGroupVDSCommandParameters(guids[1], 
guids[0], guids[4],
                             postZero, false, 
getVm().getVdsGroupCompatibilityVersion().toString()));
 
             if (!vdsRetValue2.getSucceeded()) {
diff --git 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/memory/DefaultMemoryImageBuilder.java
 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/memory/LiveSnapshotMemoryImageBuilder.java
similarity index 96%
rename from 
backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/memory/DefaultMemoryImageBuilder.java
rename to 
backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/memory/LiveSnapshotMemoryImageBuilder.java
index add296c..eb8ff90 100644
--- 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/memory/DefaultMemoryImageBuilder.java
+++ 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/memory/LiveSnapshotMemoryImageBuilder.java
@@ -18,7 +18,7 @@
 /**
  * This builder creates the memory images for live snapshots with memory 
operation
  */
-public class DefaultMemoryImageBuilder implements MemoryImageBuilder {
+public class LiveSnapshotMemoryImageBuilder implements MemoryImageBuilder {
     private Guid storageDomainId;
     private Guid memoryDumpImageGroupId;
     private Guid memoryDumpVolumeId;
@@ -28,7 +28,7 @@
     private TaskHandlerCommand<?> enclosingCommand;
     private StoragePool storagePool;
 
-    public DefaultMemoryImageBuilder(VM vm, Guid storageDomainId,
+    public LiveSnapshotMemoryImageBuilder(VM vm, Guid storageDomainId,
             StoragePool storagePool, TaskHandlerCommand<?> enclosingCommand) {
         this.vm = vm;
         this.enclosingCommand = enclosingCommand;
diff --git 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/memory/StatelessSnapshotMemoryImageBuilder.java
 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/memory/StatelessSnapshotMemoryImageBuilder.java
new file mode 100644
index 0000000..ae349f1
--- /dev/null
+++ 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/memory/StatelessSnapshotMemoryImageBuilder.java
@@ -0,0 +1,32 @@
+package org.ovirt.engine.core.bll.memory;
+
+import org.ovirt.engine.core.common.businessentities.Snapshot.SnapshotType;
+import org.ovirt.engine.core.common.businessentities.VM;
+import org.ovirt.engine.core.dal.dbbroker.DbFacade;
+
+/**
+ * This builder is responsible to create the memory volumes for stateless 
snapshot -
+ * it just take the memory volume of the previously active snapshot
+ */
+public class StatelessSnapshotMemoryImageBuilder implements MemoryImageBuilder 
{
+
+    private final String memoryVolumesFromActiveSnapshot;
+
+    public StatelessSnapshotMemoryImageBuilder(VM vm) {
+        memoryVolumesFromActiveSnapshot =
+                
DbFacade.getInstance().getSnapshotDao().get(vm.getId(),SnapshotType.ACTIVE)
+                .getMemoryVolume();
+    }
+
+    public void build() {
+      //no op
+    }
+
+    public String getVolumeStringRepresentation() {
+        return memoryVolumesFromActiveSnapshot;
+    }
+
+    public boolean isCreateTasks() {
+        return false;
+    }
+}
diff --git 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/snapshots/SnapshotsManager.java
 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/snapshots/SnapshotsManager.java
index 9af2216..797c635 100644
--- 
a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/snapshots/SnapshotsManager.java
+++ 
b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/snapshots/SnapshotsManager.java
@@ -2,7 +2,9 @@
 
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.commons.lang.StringUtils;
 import org.ovirt.engine.core.bll.ImagesHandler;
@@ -257,11 +259,15 @@
      *
      * @param vmId
      *            The ID of the VM.
+     * @return Set of memoryVolumes of the removed snapshots
      */
-    public void removeSnapshots(Guid vmId) {
+    public Set<String> removeSnapshots(Guid vmId) {
+        Set<String> memoryVolumes = new HashSet<String>();
         for (Snapshot snapshot : getSnapshotDao().getAll(vmId)) {
+            memoryVolumes.add(snapshot.getMemoryVolume());
             getSnapshotDao().remove(snapshot.getId());
         }
+        return memoryVolumes;
     }
 
     /**
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 33bde37..74ece58 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
@@ -163,5 +163,20 @@
      */
     boolean exists(Guid vmId, Guid snapshotId);
 
+    /**
+     * Get the number of snapshots that contain the given memory
+     *
+     * @param memoryVolume
+     *           The memory that should be used to filter the snapshots
+     * @return Number of snapshots containing the given memory
+     */
     int getNumOfSnapshotsByMemory(String memoryVolume);
+
+    /**
+     * Clear the memory from the snapshot with the given id
+     *
+     * @param snapshotId
+     *          The id of the snapshot that its memory should be cleared
+     */
+    void removeMemoryFromSnapshot(Guid snapshotId);
 }
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 d3e7972..96b9987 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
@@ -223,6 +223,16 @@
                 parameterSource);
     }
 
+    @Override
+    public void removeMemoryFromSnapshot(Guid snapshotId) {
+        MapSqlParameterSource parameterSource = 
getCustomMapSqlParameterSource()
+                .addValue("snapshot_id", snapshotId)
+                .addValue("memory_volume", null);
+
+        getCallsHandler().executeModification("UpdateMemoryOfSnapshot",
+                parameterSource);
+    }
+
     private String getNullableRepresentation(String memoryVolume) {
         return memoryVolume.isEmpty() ? null : memoryVolume;
     }


-- 
To view, visit http://gerrit.ovirt.org/15682
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ic087580475cd4fcfac969ddf0f69929fa247f86d
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-engine
Gerrit-Branch: master
Gerrit-Owner: Arik Hadas <aha...@redhat.com>
_______________________________________________
Engine-patches mailing list
Engine-patches@ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to