Arik Hadas has uploaded a new change for review. Change subject: core: handling memory snapshots on export/import vm ......................................................................
core: handling memory snapshots on export/import vm Export the memory volumes to the export domain when exporting VM that has snapshots that contain memory, and importing the memory volumes when importing VM that has snapshots that contain memory. Change-Id: Ifae3d5dd6bdeca04d5b5c9eee2dbe93e3eb03cea Bug-Url: https://bugzilla.redhat.com/960931 Signed-off-by: Arik Hadas <aha...@redhat.com> --- M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/ExportVmCommand.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/HibernateVmCommand.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/ImagesHandler.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/ImportVmCommand.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/MoveOrCopyImageGroupCommand.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/VmCommand.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/VmHandler.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/memory/MemoryImageRemover.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VM.java M backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/ovf/OvfVmReader.java M backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/ovf/OvfVmWriter.java 11 files changed, 555 insertions(+), 97 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/83/15683/1 diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/ExportVmCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/ExportVmCommand.java index a66588b..2c5cc1e 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/ExportVmCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/ExportVmCommand.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -9,6 +10,7 @@ import org.apache.commons.lang.StringUtils; import org.ovirt.engine.core.bll.job.ExecutionHandler; +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.ClusterUtils; @@ -34,6 +36,7 @@ import org.ovirt.engine.core.common.businessentities.VM; import org.ovirt.engine.core.common.businessentities.VmTemplate; import org.ovirt.engine.core.common.businessentities.VolumeFormat; +import org.ovirt.engine.core.common.businessentities.VolumeType; import org.ovirt.engine.core.common.businessentities.network.VmNetworkInterface; import org.ovirt.engine.core.common.errors.VdcBLLException; import org.ovirt.engine.core.common.errors.VdcBllMessages; @@ -49,6 +52,7 @@ import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.compat.KeyValuePairCompat; import org.ovirt.engine.core.dal.dbbroker.DbFacade; +import org.ovirt.engine.core.utils.GuidUtils; import org.ovirt.engine.core.utils.linq.LinqUtils; import org.ovirt.engine.core.utils.linq.Predicate; import org.ovirt.engine.core.utils.ovf.OvfManager; @@ -61,6 +65,7 @@ public class ExportVmCommand<T extends MoveVmParameters> extends MoveOrCopyTemplateCommand<T> { private List<DiskImage> disksImages; + private Collection<Snapshot> snapshotsWithMemory; /** * Constructor for command creation when compensation is applied on startup @@ -135,8 +140,7 @@ if (getParameters().getCopyCollapse()) { for (DiskImage img : getDisksBasedOnImage()) { if (images.containsKey(img.getId())) { - // check that no RAW format exists (we are in collapse - // mode) + // check that no RAW format exists (we are in collapse mode) if (((DiskImage) images.get(img.getId())).getVolumeFormat() == VolumeFormat.RAW && img.getVolumeFormat() != VolumeFormat.RAW) { addCanDoActionMessage(VdcBllMessages.VM_CANNOT_EXPORT_RAW_FORMAT); @@ -151,8 +155,12 @@ addCanDoActionMessage(String.format("$storageDomainName %1$s", getStorageDomainName())); return failCanDoAction(VdcBllMessages.ACTION_TYPE_FAILED_SPECIFY_DOMAIN_IS_NOT_EXPORT_DOMAIN); } + + // get the snapshot that are going to be exported and have memory + snapshotsWithMemory = getSnapshotsToBeExportedWithMemory(); + // check destination storage have free space - int sizeInGB = (int) getVm().getActualDiskWithSnapshotsSize(); + int sizeInGB = (int) getVm().getActualDiskWithSnapshotsSize() + getTotalMemoryStatesSizeGb(); if (!doesStorageDomainhaveSpaceForRequest(getStorageDomain(), sizeInGB)) { return false; } @@ -169,6 +177,42 @@ } return true; + } + + private Collection<Snapshot> getSnapshotsToBeExportedWithMemory() { + if (getParameters().getCopyCollapse()) { + Snapshot activeSnapshot = getSnapshotDao().get(getVmId(), SnapshotType.ACTIVE); + return !activeSnapshot.getMemoryVolume().isEmpty() ? + Collections.<Snapshot>singleton(activeSnapshot) : Collections.<Snapshot>emptyList(); + } + else { + Map<String, Snapshot> memory2snapshot = new HashMap<String, Snapshot>(); + for (Snapshot snapshot : getSnapshotDao().getAll(getVmId())) { + memory2snapshot.put(snapshot.getMemoryVolume(), snapshot); + } + memory2snapshot.remove(StringUtils.EMPTY); + return memory2snapshot.values(); + } + } + + private int getTotalMemoryStatesSizeGb() { + long sizeInBytes = 0; + SnapshotsManager snapshotsManager = new SnapshotsManager(); + for (Snapshot snapshot : snapshotsWithMemory) { + String vmConfiguration = snapshot.getVmConfiguration(); + VM vm; + if (vmConfiguration == null) { + vm = getVm(); + } + else { + vm = new VM(); + if (!snapshotsManager.updateVmFromConfiguration(vm, snapshot.getVmConfiguration())) { + // TODO + } + } + sizeInBytes += vm.getTotalMemorySizeInBytes() + HibernateVmCommand.META_DATA_SIZE_IN_BYTES; + } + return (int) Math.ceil(1.0 * sizeInBytes / BYTES_IN_GB); } @Override @@ -188,7 +232,6 @@ endSuccessfullySynchronous(); } else { TransactionSupport.executeInNewTransaction(new TransactionMethod<Void>() { - @Override public Void runInTransaction() { @@ -265,7 +308,10 @@ @Override protected void moveOrCopyAllImageGroups() { + // Disks moveOrCopyAllImageGroups(getVm().getId(), getDisksBasedOnImage()); + // Memory volumes + copyAllMemoryImages(getVm().getId()); } private List<DiskImage> getDisksBasedOnImage() { @@ -275,25 +321,72 @@ return disksImages; } + protected void copyAllMemoryImages(Guid containerID) { + for (Snapshot snapshot : snapshotsWithMemory) { + List<Guid> guids = GuidUtils.getGuidListFromString(snapshot.getMemoryVolume()); + + // copy the memory dump image + VdcReturnValueBase vdcRetValue = Backend.getInstance().runInternalAction( + VdcActionType.MoveOrCopyImageGroup, + buildMoveOrCopyImageGroupParametersForMemoryDumpImage( + containerID, guids.get(0), guids.get(2), guids.get(3)), + ExecutionHandler.createDefaultContexForTasks(getExecutionContext())); + if (!vdcRetValue.getSucceeded()) { + throw new VdcBLLException(vdcRetValue.getFault().getError(), "Failed during ExportVmCommand"); + } + getReturnValue().getTaskIdList().addAll(vdcRetValue.getInternalTaskIdList()); + + // copy the memory configuration (of the VM) image + vdcRetValue = Backend.getInstance().runInternalAction( + VdcActionType.MoveOrCopyImageGroup, + buildMoveOrCopyImageGroupParametersForMemoryConfImage( + containerID, guids.get(0), guids.get(4), guids.get(5)), + ExecutionHandler.createDefaultContexForTasks(getExecutionContext())); + if (!vdcRetValue.getSucceeded()) { + throw new VdcBLLException(vdcRetValue.getFault().getError(), "Failed during ExportVmCommand"); + } + getReturnValue().getTaskIdList().addAll(vdcRetValue.getInternalTaskIdList()); + } + } + + private MoveOrCopyImageGroupParameters buildMoveOrCopyImageGroupParametersForMemoryDumpImage( + Guid containerID, Guid storageDomainId, Guid imageId, Guid volumeId) { + MoveOrCopyImageGroupParameters params = new MoveOrCopyImageGroupParameters(containerID, imageId, + volumeId, getParameters().getStorageDomainId(), getMoveOrCopyImageOperation()); + params.setParentCommand(getActionType()); + params.setEntityId(getParameters().getEntityId()); + params.setUseCopyCollapse(getParameters().getCopyCollapse()); + params.setVolumeFormat(VolumeFormat.RAW); + params.setVolumeType(VolumeType.Sparse); + params.setCopyVolumeType(CopyVolumeType.LeafVol); + params.setForceOverride(getParameters().getForceOverride()); + params.setParentParameters(getParameters()); + params.setSourceDomainId(storageDomainId); + return params; + } + + private MoveOrCopyImageGroupParameters buildMoveOrCopyImageGroupParametersForMemoryConfImage( + Guid containerID, Guid storageDomainId, Guid imageId, Guid volumeId) { + MoveOrCopyImageGroupParameters params = new MoveOrCopyImageGroupParameters(containerID, imageId, + volumeId, getParameters().getStorageDomainId(), getMoveOrCopyImageOperation()); + params.setParentCommand(getActionType()); + params.setEntityId(getParameters().getEntityId()); + params.setUseCopyCollapse(getParameters().getCopyCollapse()); + params.setVolumeFormat(VolumeFormat.COW); + params.setVolumeType(VolumeType.Sparse); + params.setCopyVolumeType(CopyVolumeType.LeafVol); + params.setForceOverride(getParameters().getForceOverride()); + params.setParentParameters(getParameters()); + params.setSourceDomainId(storageDomainId); + return params; + } + @Override protected void moveOrCopyAllImageGroups(Guid containerID, Iterable<DiskImage> disks) { for (DiskImage disk : disks) { - MoveOrCopyImageGroupParameters tempVar = new MoveOrCopyImageGroupParameters(containerID, disk - .getId(), disk.getImageId(), getParameters().getStorageDomainId(), - getMoveOrCopyImageOperation()); - tempVar.setParentCommand(getActionType()); - tempVar.setEntityId(getParameters().getEntityId()); - tempVar.setUseCopyCollapse(getParameters().getCopyCollapse()); - DiskImage diskForVolumeInfo = getDiskForVolumeInfo(disk); - tempVar.setVolumeFormat(diskForVolumeInfo.getVolumeFormat()); - tempVar.setVolumeType(diskForVolumeInfo.getVolumeType()); - tempVar.setCopyVolumeType(CopyVolumeType.LeafVol); - tempVar.setForceOverride(getParameters().getForceOverride()); - MoveOrCopyImageGroupParameters p = tempVar; - p.setParentParameters(getParameters()); VdcReturnValueBase vdcRetValue = Backend.getInstance().runInternalAction( VdcActionType.MoveOrCopyImageGroup, - p, + buildMoveOrCopyImageGroupParametersForDisk(containerID, disk), ExecutionHandler.createDefaultContexForTasks(getExecutionContext())); if (!vdcRetValue.getSucceeded()) { throw new VdcBLLException(vdcRetValue.getFault().getError(), "Failed during ExportVmCommand"); @@ -301,6 +394,21 @@ getReturnValue().getTaskIdList().addAll(vdcRetValue.getInternalTaskIdList()); } + } + + private MoveOrCopyImageGroupParameters buildMoveOrCopyImageGroupParametersForDisk(Guid containerID, DiskImage disk) { + MoveOrCopyImageGroupParameters params = new MoveOrCopyImageGroupParameters(containerID, disk.getId(), + disk.getImageId(), getParameters().getStorageDomainId(), getMoveOrCopyImageOperation()); + params.setParentCommand(getActionType()); + params.setEntityId(getParameters().getEntityId()); + params.setUseCopyCollapse(getParameters().getCopyCollapse()); + DiskImage diskForVolumeInfo = getDiskForVolumeInfo(disk); + params.setVolumeFormat(diskForVolumeInfo.getVolumeFormat()); + params.setVolumeType(diskForVolumeInfo.getVolumeType()); + params.setCopyVolumeType(CopyVolumeType.LeafVol); + params.setForceOverride(getParameters().getForceOverride()); + params.setParentParameters(getParameters()); + return params; } /** @@ -431,7 +539,7 @@ vm.setVmtName(null); Snapshot activeSnapshot = DbFacade.getInstance().getSnapshotDao().get( DbFacade.getInstance().getSnapshotDao().getId(vm.getId(), SnapshotType.ACTIVE)); - vm.setSnapshots(Arrays.asList(activeSnapshot)); + vm.setSnapshots(Collections.singletonList(activeSnapshot)); updateCopyVmInSpm(getVm().getStoragePoolId(), vm, getParameters() .getStorageDomainId()); diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/HibernateVmCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/HibernateVmCommand.java index c3214c0..bd2db4e 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/HibernateVmCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/HibernateVmCommand.java @@ -12,6 +12,7 @@ import org.ovirt.engine.core.common.asynctasks.AsyncTaskType; import org.ovirt.engine.core.common.businessentities.Snapshot.SnapshotType; import org.ovirt.engine.core.common.businessentities.StorageDomain; +import org.ovirt.engine.core.common.businessentities.StoragePool; import org.ovirt.engine.core.common.businessentities.VMStatus; import org.ovirt.engine.core.common.businessentities.VolumeFormat; import org.ovirt.engine.core.common.businessentities.VolumeType; @@ -364,15 +365,19 @@ } } + private VolumeType getVolumeType() { + return getMemoryDumpVolumeType(getStoragePool()); + } + /** * Returns whether to use Sparse or Preallocation. If the storage type is file system devices ,it would be more * efficient to use Sparse allocation. Otherwise for block devices we should use Preallocated for faster allocation. * * @return - VolumeType of allocation type to use. */ - private VolumeType getVolumeType() { - return (getStoragePool().getstorage_pool_type().isFileDomain()) ? VolumeType.Sparse - : VolumeType.Preallocated; + static VolumeType getMemoryDumpVolumeType(StoragePool storagePool) { + return storagePool.getstorage_pool_type().isFileDomain() ? + VolumeType.Sparse : VolumeType.Preallocated; } private static Log log = LogFactory.getLog(HibernateVmCommand.class); diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/ImagesHandler.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/ImagesHandler.java index 97f925a..d6106b6 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/ImagesHandler.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/ImagesHandler.java @@ -420,16 +420,19 @@ public static boolean CheckImageConfiguration(StorageDomainStatic storageDomain, DiskImageBase diskInfo, List<String> messages) { - boolean result = true; - if ((diskInfo.getVolumeType() == VolumeType.Preallocated && diskInfo.getVolumeFormat() == VolumeFormat.COW) - || (storageDomain.getStorageType().isBlockDomain() && diskInfo.getVolumeType() == VolumeType.Sparse && diskInfo.getVolumeFormat() == VolumeFormat.RAW) - || diskInfo.getVolumeFormat() == VolumeFormat.Unassigned - || diskInfo.getVolumeType() == VolumeType.Unassigned) { + if (!checkImageConfiguration(storageDomain, diskInfo.getVolumeType(), diskInfo.getVolumeFormat())) { // not supported - result = false; messages.add(VdcBllMessages.ACTION_TYPE_FAILED_DISK_CONFIGURATION_NOT_SUPPORTED.toString()); + return false; } - return result; + return true; + } + + public static boolean checkImageConfiguration(StorageDomainStatic storageDomain, VolumeType volumeType, VolumeFormat volumeFormat) { + return !((volumeType == VolumeType.Preallocated && volumeFormat == VolumeFormat.COW) + || (storageDomain.getStorageType().isBlockDomain() && volumeType == VolumeType.Sparse && volumeFormat == VolumeFormat.RAW) + || volumeFormat == VolumeFormat.Unassigned + || volumeType == VolumeType.Unassigned); } public static boolean CheckImagesConfiguration(Guid storageDomainId, diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/ImportVmCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/ImportVmCommand.java index 01ef1c9..f70e482 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/ImportVmCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/ImportVmCommand.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -11,13 +12,16 @@ import java.util.Set; import org.apache.commons.lang.StringUtils; +import org.ovirt.engine.core.bll.job.ExecutionContext; import org.ovirt.engine.core.bll.job.ExecutionHandler; +import org.ovirt.engine.core.bll.memory.MemoryImageRemover; import org.ovirt.engine.core.bll.network.MacPoolManager; import org.ovirt.engine.core.bll.network.VmInterfaceManager; 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.tasks.TaskHandlerCommand; import org.ovirt.engine.core.bll.utils.PermissionSubject; import org.ovirt.engine.core.bll.utils.VmDeviceUtils; import org.ovirt.engine.core.bll.validator.DiskImagesValidator; @@ -29,6 +33,7 @@ import org.ovirt.engine.core.common.action.VdcActionParametersBase; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VdcReturnValueBase; +import org.ovirt.engine.core.common.asynctasks.AsyncTaskCreationInfo; import org.ovirt.engine.core.common.businessentities.ActionGroup; import org.ovirt.engine.core.common.businessentities.CopyVolumeType; import org.ovirt.engine.core.common.businessentities.Disk; @@ -50,11 +55,13 @@ import org.ovirt.engine.core.common.businessentities.VmStatistics; import org.ovirt.engine.core.common.businessentities.VmTemplate; import org.ovirt.engine.core.common.businessentities.VmTemplateStatus; +import org.ovirt.engine.core.common.businessentities.VolumeFormat; +import org.ovirt.engine.core.common.businessentities.VolumeType; import org.ovirt.engine.core.common.businessentities.network.Network; import org.ovirt.engine.core.common.businessentities.network.VmNetworkInterface; import org.ovirt.engine.core.common.errors.VdcBLLException; -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.queries.GetAllFromExportDomainQueryParameters; import org.ovirt.engine.core.common.queries.IdQueryParameters; import org.ovirt.engine.core.common.queries.VdcQueryReturnValue; @@ -66,8 +73,10 @@ 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.compat.NotImplementedException; import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogDirector; import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogableBase; +import org.ovirt.engine.core.utils.GuidUtils; import org.ovirt.engine.core.utils.collections.MultiValueMapUtils; import org.ovirt.engine.core.utils.linq.Function; import org.ovirt.engine.core.utils.linq.LinqUtils; @@ -83,7 +92,7 @@ @NonTransactiveCommandAttribute(forceCompensation = true) @LockIdNameAttribute public class ImportVmCommand extends MoveOrCopyTemplateCommand<ImportVmParameters> - implements QuotaStorageDependent { + implements QuotaStorageDependent, TaskHandlerCommand<ImportVmParameters> { private static final Log log = LogFactory.getLog(ImportVmCommand.class); private static VmStatic vmStaticForDefaultValues = new VmStatic(); @@ -128,6 +137,7 @@ } return super.getVmId(); } + @Override public VM getVm() { if (getParameters().isImportAsNewEntity()) { @@ -347,6 +357,8 @@ Map<StorageDomain, Integer> domainMap = getSpaceRequirementsForStorageDomains(imageList); + setDomainsForMemoryImages(domainMap); + for (Map.Entry<StorageDomain, Integer> entry : domainMap.entrySet()) { if (!doesStorageDomainhaveSpaceForRequest(entry.getKey(), entry.getValue())) { return false; @@ -361,6 +373,68 @@ return false; } + return true; + } + + private Collection<Snapshot> getSnapshotsToBeImportedWithMemory() { + if (getParameters().getCopyCollapse()) { + Snapshot activeSnapshot = getSnapshotDao().get(getVmId(), SnapshotType.ACTIVE); + return !activeSnapshot.getMemoryVolume().isEmpty() ? + Collections.<Snapshot>singleton(activeSnapshot) : Collections.<Snapshot>emptyList(); + } + else { + Map<String, Snapshot> memory2snapshot = new HashMap<String, Snapshot>(); + for (Snapshot snapshot : getSnapshotDao().getAll(getVmId())) { + memory2snapshot.put(snapshot.getMemoryVolume(), snapshot); + } + memory2snapshot.remove(StringUtils.EMPTY); + return memory2snapshot.values(); + } + } + + private boolean setDomainsForMemoryImages(Map<StorageDomain, Integer> domain2requiredSize) { + SnapshotsManager snapshotsManager = new SnapshotsManager(); + Map<String, String> handledMemoryVolumes = new HashMap<String, String>(); + for (Snapshot snapshot : getVm().getSnapshots()) { + String memoryVolume = snapshot.getMemoryVolume(); + if (memoryVolume.isEmpty()) { + continue; + } + + if (handledMemoryVolumes.containsKey(memoryVolume)) { + snapshot.setMemoryVolume(handledMemoryVolumes.get(memoryVolume)); + continue; + } + + String vmConfiguration = snapshot.getVmConfiguration(); + VM vm; + // active/stateless + if (vmConfiguration == null) { + vm = getVm(); + } + else { + vm = new VM(); + if (!snapshotsManager.updateVmFromConfiguration(vm, snapshot.getVmConfiguration())) { + // TODO + } + } + + int requiredSizeForMemory = (int) Math.ceil((vm.getTotalMemorySizeInBytes() + + HibernateVmCommand.META_DATA_SIZE_IN_BYTES) * 1.0 / BYTES_IN_GB); + StorageDomain storageDomain = VmHandler.findStorageDomainForMemory( + getParameters().getStoragePoolId(),requiredSizeForMemory, domain2requiredSize); + if (storageDomain == null) { + // TODO: add can do action message + return false; + } + domain2requiredSize.put(storageDomain, + domain2requiredSize.get(storageDomain) + requiredSizeForMemory); + List<Guid> guids = GuidUtils.getGuidListFromString(memoryVolume); + snapshot.setMemoryVolume(String.format("%1$s,%2$s,%3$s,%4$s,%5$s,%6$s", + storageDomain.getId().toString(), getParameters().getStoragePoolId().toString(), + guids.get(2), guids.get(3), guids.get(4), guids.get(5))); + handledMemoryVolumes.put(memoryVolume, snapshot.getMemoryVolume()); + } return true; } @@ -511,7 +585,7 @@ VM vm = getVm(); // if there aren't any images- we can just perform the end // vm related ops - if (vm.getImages().isEmpty()) { + if (vm.getImages().isEmpty()) { // TODO: add memory endVmRelatedOps(); } else { processImages(); @@ -564,39 +638,96 @@ protected void moveOrCopyAllImageGroups() { moveOrCopyAllImageGroups(getVm().getId(), ImagesHandler.filterImageDisks(getVm().getDiskMap().values(), false, false)); + copyAllMemoryImages(getVm().getId()); + } + + private void copyAllMemoryImages(Guid containerId) { + Set<String> handledMemoryVolumes = new HashSet<String>(); + for (Snapshot snapshot : getVm().getSnapshots()) { + String memoryVolume = snapshot.getMemoryVolume(); + if (!memoryVolume.isEmpty() && !handledMemoryVolumes.contains(memoryVolume)) { + List<Guid> guids = GuidUtils.getGuidListFromString(memoryVolume); + + // copy the memory dump image + VdcReturnValueBase vdcRetValue = Backend.getInstance().runInternalAction( + VdcActionType.MoveOrCopyImageGroup, + buildMoveOrCopyImageGroupParametersForMemoryDumpImage( + containerId, guids.get(0), guids.get(2), guids.get(3)), + ExecutionHandler.createDefaultContexForTasks(getExecutionContext())); + if (!vdcRetValue.getSucceeded()) { + throw new VdcBLLException(vdcRetValue.getFault().getError(), "Failed during ExportVmCommand"); + } + getReturnValue().getTaskIdList().addAll(vdcRetValue.getInternalTaskIdList()); + + // copy the memory configuration (of the VM) image + vdcRetValue = Backend.getInstance().runInternalAction( + VdcActionType.MoveOrCopyImageGroup, + buildMoveOrCopyImageGroupParametersForVmConfImage( + containerId, guids.get(0), guids.get(4), guids.get(5)), + ExecutionHandler.createDefaultContexForTasks(getExecutionContext())); + if (!vdcRetValue.getSucceeded()) { + throw new VdcBLLException(vdcRetValue.getFault().getError(), "Failed during ExportVmCommand"); + } + getReturnValue().getTaskIdList().addAll(vdcRetValue.getInternalTaskIdList()); + + handledMemoryVolumes.add(memoryVolume); + } + } + } + + private MoveOrCopyImageGroupParameters buildMoveOrCopyImageGroupParametersForMemoryDumpImage(Guid containerID, + Guid storageId, Guid imageId, Guid volumeId) { + MoveOrCopyImageGroupParameters params = new MoveOrCopyImageGroupParameters(containerID, + imageId, + volumeId, + imageId, + volumeId, + storageId, + getMoveOrCopyImageOperation()); + params.setParentCommand(getActionType()); + params.setUseCopyCollapse(getParameters().getCopyCollapse()); + params.setCopyVolumeType(CopyVolumeType.LeafVol); + params.setForceOverride(getParameters().getForceOverride()); + params.setSourceDomainId(getParameters().getSourceDomainId()); + params.setStoragePoolId(getParameters().getStoragePoolId()); + params.setImportEntity(true); + params.setEntityId(getVm().getId()); + params.setVolumeType(VolumeType.Preallocated); + params.setVolumeFormat(VolumeFormat.RAW); + params.setParentParameters(getParameters()); + return params; + } + + private MoveOrCopyImageGroupParameters buildMoveOrCopyImageGroupParametersForVmConfImage(Guid containerID, + Guid storageId, Guid imageId, Guid volumeId) { + MoveOrCopyImageGroupParameters params = new MoveOrCopyImageGroupParameters(containerID, + imageId, + volumeId, + imageId, + volumeId, + storageId, + getMoveOrCopyImageOperation()); + params.setParentCommand(getActionType()); + params.setUseCopyCollapse(getParameters().getCopyCollapse()); + params.setCopyVolumeType(CopyVolumeType.LeafVol); + params.setForceOverride(getParameters().getForceOverride()); + params.setSourceDomainId(getParameters().getSourceDomainId()); + params.setStoragePoolId(getParameters().getStoragePoolId()); + params.setImportEntity(true); + params.setEntityId(getVm().getId()); + params.setVolumeType(VolumeType.Sparse); + params.setVolumeFormat(VolumeFormat.COW); + params.setParentParameters(getParameters()); + return params; } @Override protected void moveOrCopyAllImageGroups(Guid containerID, Iterable<DiskImage> disks) { int i = 0; for (DiskImage disk : disks) { - Guid destinationDomain = imageToDestinationDomainMap.get(diskGuidList.get(i)); - MoveOrCopyImageGroupParameters p = new MoveOrCopyImageGroupParameters(containerID, - diskGuidList.get(i), - imageGuidList.get(i), - disk.getId(), - disk.getImageId(), - destinationDomain, getMoveOrCopyImageOperation()); - p.setParentCommand(getActionType()); - p.setUseCopyCollapse(getParameters().getCopyCollapse()); - p.setCopyVolumeType(CopyVolumeType.LeafVol); - p.setForceOverride(getParameters().getForceOverride()); - p.setSourceDomainId(getParameters().getSourceDomainId()); - p.setStoragePoolId(getParameters().getStoragePoolId()); - p.setImportEntity(true); - p.setEntityId(getVm().getId()); - p.setQuotaId(disk.getQuotaId() != null ? disk.getQuotaId() : getParameters().getQuotaId()); - if (getParameters().getVm().getDiskMap() != null - && getParameters().getVm().getDiskMap().containsKey(diskGuidList.get(i))) { - DiskImageBase diskImageBase = - (DiskImageBase) getParameters().getVm().getDiskMap().get(diskGuidList.get(i)); - p.setVolumeType(diskImageBase.getVolumeType()); - p.setVolumeFormat(diskImageBase.getVolumeFormat()); - } - p.setParentParameters(getParameters()); VdcReturnValueBase vdcRetValue = Backend.getInstance().runInternalAction( VdcActionType.MoveOrCopyImageGroup, - p, + buildMoveOrCopyImageGroupParametersForDisk(disk, containerID, i++), ExecutionHandler.createDefaultContexForTasks(getExecutionContext())); if (!vdcRetValue.getSucceeded()) { throw new VdcBLLException(vdcRetValue.getFault().getError(), @@ -604,8 +735,35 @@ } getReturnValue().getTaskIdList().addAll(vdcRetValue.getInternalTaskIdList()); - i++; } + } + + private MoveOrCopyImageGroupParameters buildMoveOrCopyImageGroupParametersForDisk(DiskImage disk, Guid containerID, int i) { + Guid destinationDomain = imageToDestinationDomainMap.get(diskGuidList.get(i)); + MoveOrCopyImageGroupParameters params = new MoveOrCopyImageGroupParameters(containerID, + diskGuidList.get(i), + imageGuidList.get(i), + disk.getId(), + disk.getImageId(), + destinationDomain, getMoveOrCopyImageOperation()); + params.setParentCommand(getActionType()); + params.setUseCopyCollapse(getParameters().getCopyCollapse()); + params.setCopyVolumeType(CopyVolumeType.LeafVol); + params.setForceOverride(getParameters().getForceOverride()); + params.setSourceDomainId(getParameters().getSourceDomainId()); + params.setStoragePoolId(getParameters().getStoragePoolId()); + params.setImportEntity(true); + params.setEntityId(getVm().getId()); + params.setQuotaId(disk.getQuotaId() != null ? disk.getQuotaId() : getParameters().getQuotaId()); + if (getParameters().getVm().getDiskMap() != null + && getParameters().getVm().getDiskMap().containsKey(diskGuidList.get(i))) { + DiskImageBase diskImageBase = + (DiskImageBase) getParameters().getVm().getDiskMap().get(diskGuidList.get(i)); + params.setVolumeType(diskImageBase.getVolumeType()); + params.setVolumeFormat(diskImageBase.getVolumeFormat()); + } + params.setParentParameters(getParameters()); + return params; } protected void addVmImagesAndSnapshots() { @@ -648,8 +806,7 @@ } Snapshot snapshot = addActiveSnapshot(snapshotId); - getVm().getSnapshots().clear(); - getVm().getSnapshots().add(snapshot); + getVm().setSnapshots(Collections.singletonList(snapshot)); } else { Guid snapshotId = null; for (DiskImage disk : getVm().getImages()) { @@ -967,16 +1124,27 @@ if (getVm() != null) { endActionOnAllImageGroups(); removeVmNetworkInterfaces(); - new SnapshotsManager().removeSnapshots(getVm().getId()); + removeVmSnapshot(getVm()); getVmDynamicDAO().remove(getVmId()); getVmStatisticsDAO().remove(getVmId()); - new SnapshotsManager().removeSnapshots(getVmId()); + if (getParameters().isImportAsNewEntity()) { + removeVmSnapshot(getParameters().getVm()); + } getVmStaticDAO().remove(getVmId()); setSucceeded(true); } else { setVm(vmFromParams); // Setting VM from params, for logging purposes // No point in trying to end action again, as the imported VM does not exist in the DB. getReturnValue().setEndActionTryAgain(false); + } + } + + private void removeVmSnapshot(VM vm) { + MemoryImageRemover memoryImageRemover = new MemoryImageRemover(vm, this); + Collection<String> memoriesOfRemovedSnapshots = + new SnapshotsManager().removeSnapshots(vm.getId()); + for (String memoryVolumes : memoriesOfRemovedSnapshots) { + memoryImageRemover.removeMemoryVolume(memoryVolumes); } } @@ -1072,4 +1240,45 @@ } return list; } + + public ImportVmParameters getParameters() { + return super.getParameters(); + } + + public VdcActionType getActionType() { + return super.getActionType(); + } + + public VdcReturnValueBase getReturnValue() { + return super.getReturnValue(); + } + + public ExecutionContext getExecutionContext() { + return super.getExecutionContext(); + } + + public void setExecutionContext(ExecutionContext executionContext) { + super.setExecutionContext(executionContext); + } + + public Guid createTask(AsyncTaskCreationInfo asyncTaskCreationInfo, + VdcActionType parentCommand, + VdcObjectType entityType, + Guid... entityIds) { + return super.createTask(asyncTaskCreationInfo, parentCommand, + entityType, entityIds); + } + + public Guid createTask(AsyncTaskCreationInfo asyncTaskCreationInfo, + VdcActionType parentCommand) { + return super.createTask(asyncTaskCreationInfo, parentCommand); + } + + public ArrayList<Guid> getTaskIdList() { + return super.getTaskIdList(); + } + + public void preventRollback() { + throw new NotImplementedException(); + } } diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/MoveOrCopyImageGroupCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/MoveOrCopyImageGroupCommand.java index b1ca1c4..faf38ab 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/MoveOrCopyImageGroupCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/MoveOrCopyImageGroupCommand.java @@ -55,48 +55,42 @@ lockImage(); VDSReturnValue vdsReturnValue = null; - Guid sourceDomainId = getParameters().getSourceDomainId() != null ? getParameters().getSourceDomainId() - .getValue() + Guid sourceDomainId = getParameters().getSourceDomainId() != null ? + getParameters().getSourceDomainId().getValue() : getDiskImage().getStorageIds().get(0); if (getParameters().getUseCopyCollapse()) { vdsReturnValue = runVdsCommand( VDSCommandType.CopyImage, - new CopyImageVDSCommandParameters(getStorageDomain().getStoragePoolId() - .getValue(), + new CopyImageVDSCommandParameters( + getStorageDomain().getStoragePoolId().getValue(), sourceDomainId, - getParameters() - .getContainerId(), + getParameters().getContainerId(), getParameters().getImageGroupID(), - getParameters() - .getImageId(), + getParameters().getImageId(), getParameters().getDestImageGroupId(), getParameters().getDestinationImageId(), "", getParameters().getStorageDomainId(), - getParameters() - .getCopyVolumeType(), + getParameters().getCopyVolumeType(), getParameters().getVolumeFormat(), - getParameters() - .getVolumeType(), + getParameters().getVolumeType(), isWipeAfterDelete(), - getParameters() - .getForceOverride(), + getParameters().getForceOverride(), getStoragePool().getcompatibility_version().toString())); } else { vdsReturnValue = runVdsCommand( VDSCommandType.MoveImageGroup, - new MoveImageGroupVDSCommandParameters(getDiskImage().getStoragePoolId() - .getValue(), + new MoveImageGroupVDSCommandParameters( + getDiskImage() != null ? getDiskImage().getStoragePoolId() + .getValue() : getStorageDomain().getStoragePoolId().getValue(), sourceDomainId, - getDiskImage() - .getId(), + getDiskImage() != null ? getDiskImage().getId() : getParameters().getImageGroupID(), getParameters().getStorageDomainId(), getParameters().getContainerId(), getParameters().getOperation(), isWipeAfterDelete(), getParameters().getForceOverride(), - getStoragePool() - .getcompatibility_version().toString())); + getStoragePool().getcompatibility_version().toString())); } if (vdsReturnValue.getSucceeded()) { 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 b9f4080..d873cb3 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 @@ -38,6 +38,7 @@ import org.ovirt.engine.core.dao.VmDeviceDAO; import org.ovirt.engine.core.dao.VmDynamicDAO; import org.ovirt.engine.core.dao.network.VmNetworkInterfaceDao; +import org.ovirt.engine.core.utils.GuidUtils; import org.ovirt.engine.core.utils.customprop.ValidationError; import org.ovirt.engine.core.utils.customprop.VmPropertiesUtils; import org.ovirt.engine.core.utils.linq.LinqUtils; @@ -254,13 +255,9 @@ protected boolean removeMemoryVolumes(String memVols, VdcActionType parentCommand, boolean startPollingTasks) { // this is temp code until it will be implemented in SPM - String[] strings = memVols.split(","); - Guid[] guids = new Guid[strings.length]; - for (int i=0; i<strings.length; ++i) { - guids[i] = new Guid(strings[i]); - } + List<Guid> guids = GuidUtils.getGuidListFromString(memVols); - if (guids.length == 6) { + if (guids.size() == 6) { // get all vm disks in order to check post zero - if one of the // disks is marked with wipe_after_delete boolean postZero = @@ -274,34 +271,34 @@ // delete first image // the next 'DeleteImageGroup' command should also take care of the image removal: - VDSReturnValue vdsRetValue1 = runVdsCommand( + VDSReturnValue vdsRetValue = runVdsCommand( VDSCommandType.DeleteImageGroup, - new DeleteImageGroupVDSCommandParameters(guids[1], guids[0], guids[2], + new DeleteImageGroupVDSCommandParameters(guids.get(1), guids.get(0), guids.get(2), postZero, false, getVm().getVdsGroupCompatibilityVersion().toString())); - if (!vdsRetValue1.getSucceeded()) { + if (!vdsRetValue.getSucceeded()) { return false; } Guid guid1 = - createTask(vdsRetValue1.getCreationInfo(), parentCommand, VdcObjectType.Storage, guids[0]); + createTask(vdsRetValue.getCreationInfo(), parentCommand, VdcObjectType.Storage, guids.get(0)); getTaskIdList().add(guid1); // delete second image // the next 'DeleteImageGroup' command should also take care of the image removal: - VDSReturnValue vdsRetValue2 = runVdsCommand( + vdsRetValue = runVdsCommand( VDSCommandType.DeleteImageGroup, - new DeleteImageGroupVDSCommandParameters(guids[1], guids[0], guids[4], + new DeleteImageGroupVDSCommandParameters(guids.get(1), guids.get(0), guids.get(4), postZero, false, getVm().getVdsGroupCompatibilityVersion().toString())); - if (!vdsRetValue2.getSucceeded()) { + if (!vdsRetValue.getSucceeded()) { if (startPollingTasks) { AsyncTaskManager.getInstance().StartPollingTask(guid1); } return false; } - Guid guid2 = createTask(vdsRetValue2.getCreationInfo(), parentCommand); + Guid guid2 = createTask(vdsRetValue.getCreationInfo(), parentCommand); getTaskIdList().add(guid2); if (startPollingTasks) { diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/VmHandler.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/VmHandler.java index 12d5c90..592944b 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/VmHandler.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/VmHandler.java @@ -511,6 +511,23 @@ return null; } + public static StorageDomain findStorageDomainForMemory(Guid storagePoolId, long sizeRequested, + Map<StorageDomain, Integer> domain2reservedSpaceInDomain) { + List<StorageDomain> domainsInPool = DbFacade.getInstance().getStorageDomainDao().getAllForStoragePool(storagePoolId); + for (StorageDomain currDomain : domainsInPool) { + long reservedSizeForDisks = domain2reservedSpaceInDomain.containsKey(currDomain) ? + domain2reservedSpaceInDomain.get(currDomain) : 0; + long sizeNeeded = sizeRequested + reservedSizeForDisks; + if ((currDomain.getStorageDomainType().equals(StorageDomainType.Master) + || currDomain.getStorageDomainType().equals(StorageDomainType.Data)) + && currDomain.getStatus() == StorageDomainStatus.Active + && doesStorageDomainHaveSpaceForRequest(currDomain, sizeNeeded)) { + return currDomain; + } + } + return null; + } + protected static boolean doesStorageDomainHaveSpaceForRequest(StorageDomain storageDomain, long sizeRequested) { // not calling validate in order not to add the messages per domain return (new StorageDomainValidator(storageDomain).isDomainHasSpaceForRequest(sizeRequested)).isValid(); diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/memory/MemoryImageRemover.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/memory/MemoryImageRemover.java new file mode 100644 index 0000000..e46fcd4 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/memory/MemoryImageRemover.java @@ -0,0 +1,120 @@ +package org.ovirt.engine.core.bll.memory; + +import java.util.List; + +import org.ovirt.engine.core.bll.AsyncTaskManager; +import org.ovirt.engine.core.bll.Backend; +import org.ovirt.engine.core.bll.tasks.TaskHandlerCommand; +import org.ovirt.engine.core.common.VdcObjectType; +import org.ovirt.engine.core.common.businessentities.Disk; +import org.ovirt.engine.core.common.businessentities.VM; +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.dal.dbbroker.DbFacade; +import org.ovirt.engine.core.utils.GuidUtils; +import org.ovirt.engine.core.utils.linq.LinqUtils; +import org.ovirt.engine.core.utils.linq.Predicate; + +public class MemoryImageRemover { + + private TaskHandlerCommand<?> enclosingCommand; + private VM vm; + + public MemoryImageRemover(VM vm, TaskHandlerCommand<?> enclosingCommand) { + this.enclosingCommand = enclosingCommand; + this.vm = vm; + } + + public boolean removeMemoryVolume(String memoryVolumes) { + if (!shouldRemoveMemorySnapshotVolumes(memoryVolumes)) { + return false; + } + return removeMemoryVolumes(memoryVolumes, false); + } + + protected DbFacade getDbFacade() { + return DbFacade.getInstance(); + } + + /** + * 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 boolean removeMemoryVolumes(String memVols, boolean startPollingTasks) { + // this is temp code until it will be implemented in SPM + List<Guid> guids = GuidUtils.getGuidListFromString(memVols); + + if (guids.size() == 6) { + // 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( + getDbFacade().getDiskDao().getAllForVm(vm.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 = + Backend + .getInstance() + .getResourceManager() + .RunVdsCommand( + VDSCommandType.DeleteImageGroup, + new DeleteImageGroupVDSCommandParameters(guids.get(1), guids.get(0), + guids.get(2), postZero, false, + vm.getVdsGroupCompatibilityVersion().toString())); + + if (!vdsRetValue.getSucceeded()) { + return false; + } + + Guid guid1 = + enclosingCommand.createTask(vdsRetValue.getCreationInfo(), + enclosingCommand.getActionType(), VdcObjectType.Storage, guids.get(0)); + enclosingCommand.getTaskIdList().add(guid1); + + // delete second image + // the next 'DeleteImageGroup' command should also take care of the image removal: + vdsRetValue = + Backend + .getInstance() + .getResourceManager() + .RunVdsCommand( + VDSCommandType.DeleteImageGroup, + new DeleteImageGroupVDSCommandParameters(guids.get(1), guids.get(0), + guids.get(4), postZero, false, + vm.getVdsGroupCompatibilityVersion().toString())); + + if (!vdsRetValue.getSucceeded()) { + if (startPollingTasks) { + AsyncTaskManager.getInstance().StartPollingTask(guid1); + } + return false; + } + + Guid guid2 = enclosingCommand.createTask(vdsRetValue.getCreationInfo(), + enclosingCommand.getActionType()); + enclosingCommand.getTaskIdList().add(guid2); + + if (startPollingTasks) { + AsyncTaskManager.getInstance().StartPollingTask(guid1); + AsyncTaskManager.getInstance().StartPollingTask(guid2); + } + } + + return true; + } + +} diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VM.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VM.java index a236412..84710ab 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VM.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VM.java @@ -1459,7 +1459,7 @@ */ @JsonIgnore public long getTotalMemorySizeInBytes() { - return (long) (getVmMemSizeMb() + 200 + (64 * getNumOfMonitors())) * 1024 * 1024; + return (getVmMemSizeMb() + 200 + (64 * getNumOfMonitors())) * 1024 * 1024; } /////////////////////////////////////////////// diff --git a/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/ovf/OvfVmReader.java b/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/ovf/OvfVmReader.java index 89b98b6..03d1a50 100644 --- a/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/ovf/OvfVmReader.java +++ b/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/ovf/OvfVmReader.java @@ -255,6 +255,10 @@ snapshot.setType(SnapshotType.valueOf(node.SelectSingleNode("Type", _xmlNS).InnerText)); snapshot.setStatus(SnapshotStatus.OK); snapshot.setDescription(node.SelectSingleNode("Description", _xmlNS).InnerText); + XmlNode memory = node.SelectSingleNode("Memory", _xmlNS); + if (memory != null) { + snapshot.setMemoryVolume(memory.InnerText); + } final Date creationDate = OvfParser.UtcDateStringToLocaDate(node.SelectSingleNode("CreationDate", _xmlNS).InnerText); if (creationDate != null) { diff --git a/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/ovf/OvfVmWriter.java b/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/ovf/OvfVmWriter.java index 54caed0..7ad1cd0 100644 --- a/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/ovf/OvfVmWriter.java +++ b/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/ovf/OvfVmWriter.java @@ -325,6 +325,7 @@ _writer.writeElement("Type", snapshot.getType().name()); _writer.writeElement("Description", snapshot.getDescription()); _writer.writeElement("CreationDate", OvfParser.LocalDateToUtcDateString(snapshot.getCreationDate())); + _writer.writeElement("Memory", snapshot.getMemoryVolume()); if (snapshot.getAppList() != null) { _writer.writeElement("ApplicationList", snapshot.getAppList()); } -- To view, visit http://gerrit.ovirt.org/15683 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Ifae3d5dd6bdeca04d5b5c9eee2dbe93e3eb03cea 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