package org.ovirt.engine.core.bll.storage.disk.image; import java.util.ArrayList; import java.util.Arrays; import javax.inject.Inject; import org.ovirt.engine.core.bll.InternalCommandAttribute; import org.ovirt.engine.core.bll.context.CommandContext; import org.ovirt.engine.core.bll.storage.domain.PostDeleteActionHandler; import org.ovirt.engine.core.common.VdcObjectType; import org.ovirt.engine.core.common.action.CreateImageTemplateParameters; import org.ovirt.engine.core.common.action.RemoveImageParameters; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VdcReturnValueBase; import org.ovirt.engine.core.common.asynctasks.AsyncTaskType; import org.ovirt.engine.core.common.asynctasks.EntityInfo; import org.ovirt.engine.core.common.businessentities.StorageDomainStatic; import org.ovirt.engine.core.common.businessentities.storage.DiskImage; import org.ovirt.engine.core.common.businessentities.storage.DiskImageDynamic; import org.ovirt.engine.core.common.businessentities.storage.VolumeFormat; import org.ovirt.engine.core.common.businessentities.storage.VolumeType; import org.ovirt.engine.core.common.vdscommands.CopyImageVDSCommandParameters; 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.BaseDiskDao; import org.ovirt.engine.core.dao.DiskImageDao; import org.ovirt.engine.core.dao.DiskImageDynamicDao; import org.ovirt.engine.core.dao.StorageDomainDao; import org.ovirt.engine.core.dao.StorageDomainStaticDao; import org.ovirt.engine.core.dao.VmTemplateDao; /** * This command responsible to create new Image Template from image. */ @InternalCommandAttribute public class CreateImageTemplateCommand<T extends CreateImageTemplateParameters> extends BaseImagesCommand<T> { @Inject private PostDeleteActionHandler postDeleteActionHandler; @Inject private BaseDiskDao baseDiskDao; @Inject private DiskImageDynamicDao diskImageDynamicDao; @Inject private StorageDomainDao storageDomainDao; @Inject private StorageDomainStaticDao storageDomainStaticDao; @Inject private DiskImageDao diskImageDao; @Inject private VmTemplateDao vmTemplateDao; public CreateImageTemplateCommand(T parameters, CommandContext cmdContext) { super(parameters, cmdContext); super.setVmTemplateId(parameters.getVmTemplateId()); super.setVmTemplateName(parameters.getVmTemplateName()); } @Override protected void executeCommand() { Guid storagePoolId = getDiskImage().getStoragePoolId() != null ? getDiskImage().getStoragePoolId() : Guid.Empty; Guid imageGroupId = getDiskImage().getId() != null ? getDiskImage().getId() : Guid.Empty; Guid snapshotId = getDiskImage().getImageId(); // Create new image group id and image id: Guid destinationImageGroupID = Guid.newGuid(); setDestinationImageId(Guid.newGuid()); DiskImage newImage = cloneDiskImage(getDestinationImageId()); if (getParameters().getVolumeFormat() == null || getParameters().getVolumeType() == null) { // At least one of the volume arguments should be copied from the ancestral image. fillVolumeInformation(newImage); } if (getParameters().getVolumeFormat() != null) { newImage.setVolumeFormat(getParameters().getVolumeFormat()); } if (getParameters().getVolumeType() != null) { newImage.setVolumeType(getParameters().getVolumeType()); } Guid taskId = persistAsyncTaskPlaceHolder(getParameters().getParentCommand()); VolumeFormat targetFormat = getTargetVolumeFormat(newImage.getVolumeFormat(), newImage.getVolumeType(), getParameters().getDestinationStorageDomainId()); newImage.setDiskAlias(getParameters().getDiskAlias() != null ? getParameters().getDiskAlias() : getDiskImage().getDiskAlias()); VDSReturnValue vdsReturnValue = runVdsCommand(VDSCommandType.CopyImage, postDeleteActionHandler.fixParameters( new CopyImageVDSCommandParameters(storagePoolId, getParameters().getStorageDomainId(), getParameters().getVmId(), imageGroupId, snapshotId, destinationImageGroupID, getDestinationImageId(), getJsonDiskDescription(newImage), getParameters().getDestinationStorageDomainId(), getParameters().getCopyVolumeType(), targetFormat, newImage.getVolumeType(), getDiskImage().isWipeAfterDelete(), storageDomainDao.get(getParameters().getDestinationStorageDomainId()) .isDiscardAfterDelete(), false))); getReturnValue().getInternalVdsmTaskIdList().add( createTask(taskId, vdsReturnValue.getCreationInfo(), getParameters().getParentCommand(), VdcObjectType.Storage, getParameters().getStorageDomainId(), getParameters().getDestinationStorageDomainId())); newImage.setId(destinationImageGroupID); newImage.setDiskDescription(getParameters().getDescription() != null ? getParameters().getDescription() : getDiskImage().getDiskDescription()); newImage.setVmSnapshotId(getParameters().getVmSnapshotId()); newImage.setQuotaId(getParameters().getQuotaId()); newImage.setDiskProfileId(getParameters().getDiskProfileId()); newImage.setParentId(Guid.Empty); newImage.setImageTemplateId(Guid.Empty); newImage.setStorageIds(new ArrayList<>(Arrays.asList(getParameters().getDestinationStorageDomainId()))); newImage.setActive(true); ImagesHandler.saveImage(newImage); baseDiskDao.save(newImage); DiskImageDynamic diskDynamic = new DiskImageDynamic(); diskDynamic.setId(newImage.getImageId()); diskDynamic.setActualSize(getDiskImage().getActualSizeInBytes()); diskImageDynamicDao.save(diskDynamic); setActionReturnValue(newImage); // set source image as locked: lockImage(); setSucceeded(true); } /** * Since we are supporting copy/move operations between different storage families (file/block) we have to * predetermine the volume format according to the destination storage type, for block domains we cannot use sparse * combined with raw so we will change the raw to cow in that case, file domains will have the original format * retained * * TODO: Extract method and unite with getVolumeFormatForDomain() in CopyImageGroupCommand */ private VolumeFormat getTargetVolumeFormat(VolumeFormat volumeFormat, VolumeType volumeType, Guid storageDomainId) { if (volumeFormat == VolumeFormat.RAW && volumeType == VolumeType.Sparse) { StorageDomainStatic destDomain = storageDomainStaticDao.get(storageDomainId); if (destDomain.getStorageType().isBlockDomain()) { return VolumeFormat.COW; } } return volumeFormat; } /** * Fill the volume information from the image ancestor (if available, if not then from the father image). * * @param disk * The disk to fill the volume details in. */ private void fillVolumeInformation(DiskImage disk) { DiskImage ancestor = diskImageDao.getAncestor(getDiskImage().getImageId()); disk.setVolumeFormat(ancestor.getVolumeFormat()); disk.setVolumeType(ancestor.getVolumeType()); } @Override protected AsyncTaskType getTaskType() { return AsyncTaskType.copyImage; } @Override protected void endWithFailure() { unLockImage(); setVmTemplate(vmTemplateDao.get(getVmTemplateId())); if (getDestinationDiskImage() != null) { revertTasks(); } setSucceeded(true); } @Override protected void revertTasks() { Guid destImageId = getDestinationDiskImage().getImageId(); RemoveImageParameters p = new RemoveImageParameters(destImageId); p.setEntityInfo(new EntityInfo(VdcObjectType.Disk, destImageId)); p.setParentParameters(p); p.setParentCommand(VdcActionType.RemoveImage); VdcReturnValueBase returnValue = checkAndPerformRollbackUsingCommand(VdcActionType.RemoveImage, p, null); if (returnValue.getSucceeded()) { startPollingAsyncTasks(returnValue.getInternalVdsmTaskIdList()); } } }