package org.ovirt.engine.core.bll.storage.disk.cinder; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import javax.inject.Inject; import org.ovirt.engine.core.bll.SerialChildExecutingCommand; import org.ovirt.engine.core.bll.context.CommandContext; import org.ovirt.engine.core.bll.storage.disk.image.ImagesHandler; import org.ovirt.engine.core.bll.storage.disk.image.RemoveImageCommand; import org.ovirt.engine.core.bll.tasks.CommandCoordinatorUtil; import org.ovirt.engine.core.common.action.RemoveCinderDiskParameters; import org.ovirt.engine.core.common.action.RemoveCinderDiskVolumeParameters; import org.ovirt.engine.core.common.action.VdcActionParametersBase.EndProcedure; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VdcReturnValueBase; import org.ovirt.engine.core.common.businessentities.Snapshot; import org.ovirt.engine.core.common.businessentities.storage.CinderDisk; import org.ovirt.engine.core.common.businessentities.storage.DiskImage; import org.ovirt.engine.core.common.businessentities.storage.Image; import org.ovirt.engine.core.common.errors.EngineFault; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.compat.TransactionScopeOption; import org.ovirt.engine.core.dao.DiskImageDao; import org.ovirt.engine.core.dao.DiskImageDynamicDao; import org.ovirt.engine.core.dao.ImageDao; import org.ovirt.engine.core.dao.ImageStorageDomainMapDao; import org.ovirt.engine.core.dao.SnapshotDao; import org.ovirt.engine.core.utils.transaction.TransactionSupport; public class RemoveCinderVolumeParentCommand<T extends RemoveCinderDiskParameters> extends RemoveImageCommand<T> implements SerialChildExecutingCommand { @Inject private ImageDao imageDao; @Inject private ImageStorageDomainMapDao imageStorageDomainMapDao; @Inject private DiskImageDynamicDao diskImageDynamicDao; @Inject private SnapshotDao snapshotDao; @Inject private DiskImageDao diskImageDao; public RemoveCinderVolumeParentCommand(T parameters, CommandContext cmdContext) { super(parameters, cmdContext); } /** * Return the future command to be executed from the childCommandsParameters, we pass the storage's disk as the * subject entity only in the first call, since all the other volumes should be on the same Storage Domain. * * @param removedChildCommandParametersIndex * - The index to fetch the child command parameters. * @return - The future command at the ChildCommandsParameters[removedChildCommandParametersIndex]. */ protected Future<VdcReturnValueBase> getFutureRemoveCinderDiskVolume(Guid storageId, int removedChildCommandParametersIndex) { return CommandCoordinatorUtil.executeAsyncCommand(VdcActionType.RemoveCinderDiskVolume, getParameters().getChildCommandsParameters().get(removedChildCommandParametersIndex), cloneContextAndDetachFromParent()); } public boolean removeCinderVolume(int removedVolumeIndex, Guid storageId) { RemoveCinderDiskVolumeParameters param = getParameters().getChildCommandsParameters().get(removedVolumeIndex); try { VdcReturnValueBase vdcReturnValueBase = getFutureRemoveCinderDiskVolume(storageId, removedVolumeIndex).get(); if (vdcReturnValueBase == null || !vdcReturnValueBase.getSucceeded()) { handleExecutionFailure(param.getRemovedVolume(), vdcReturnValueBase); return false; } } catch (InterruptedException | ExecutionException e) { log.error("Error removing Cinder disk volume. Exception: {}", e); return false; } return true; } protected void initCinderDiskVolumesParametersList(CinderDisk cinderDisk) { // Get all the Cinder disk volumes ordered from the leaf to the parent. getParameters().getChildCommandsParameters().add(createChildParams(cinderDisk)); List<DiskImage> dependantVolumesForSnapshot = diskImageDao.getAllSnapshotsForParent(cinderDisk.getImageId()); while (!dependantVolumesForSnapshot.isEmpty()) { cinderDisk = (CinderDisk) dependantVolumesForSnapshot.get(0); getParameters().getChildCommandsParameters().addFirst(createChildParams(cinderDisk)); dependantVolumesForSnapshot = diskImageDao.getAllSnapshotsForParent(cinderDisk.getImageId()); } } private RemoveCinderDiskVolumeParameters createChildParams(CinderDisk cinderDiskVolume) { RemoveCinderDiskVolumeParameters childParam = new RemoveCinderDiskVolumeParameters(cinderDiskVolume); childParam.setParentCommand(getActionType()); childParam.setParentParameters(getParameters()); childParam.setEndProcedure(EndProcedure.COMMAND_MANAGED); return childParam; } protected void handleExecutionFailure(CinderDisk disk, VdcReturnValueBase vdcReturnValueBase) { log.error("Failed to remove cider volume id '{}' for disk id '{}'.", disk.getImageId(), disk.getId()); EngineFault fault = vdcReturnValueBase == null ? new EngineFault() : vdcReturnValueBase.getFault(); getReturnValue().setFault(fault); } protected void removeDiskFromDb(final CinderDisk cinderVolume, Snapshot updated) { if (cinderVolume.getActive()) { // Get the base volume and set it as active, so the disk will not disappear from the disks view. Image baseVol = imageDao.get(cinderVolume.getId()); baseVol.setActive(true); imageDao.update(baseVol); } imageStorageDomainMapDao.remove(cinderVolume.getImageId()); imageDao.remove(cinderVolume.getImageId()); diskImageDynamicDao.remove(cinderVolume.getImageId()); if (updated != null) { snapshotDao.update(updated); } } protected Snapshot getSnapshotWithoutCinderVolume(CinderDisk lastCinderVolume) { Guid vmSnapshotId = lastCinderVolume.getVmSnapshotId(); Snapshot updated = null; if (vmSnapshotId != null && !Guid.Empty.equals(vmSnapshotId)) { Snapshot snapshot = snapshotDao.get(vmSnapshotId); if (snapshot != null) { updated = ImagesHandler.prepareSnapshotConfigWithoutImageSingleImage(snapshot, lastCinderVolume.getImageId(), ovfManager); } } return updated; } public void removeDiskFromDbCallBack(final CinderDisk cinderVolume) { final Snapshot updated = getParameters().isUpdateSnapshot() ? getSnapshotWithoutCinderVolume(cinderVolume) : null; TransactionSupport.executeInScope(TransactionScopeOption.Required, () -> { removeDiskFromDb(cinderVolume, updated); return null; }); } @Override public boolean performNextOperation(int completedChildCount) { CinderDisk cinderVolume = getParameters().getChildCommandsParameters().get(completedChildCount - 1).getRemovedVolume(); removeDiskFromDbCallBack(cinderVolume); if(getParameters().getChildCommandsParameters().size() == completedChildCount){ return false; } getParameters().setRemovedVolumeIndex(completedChildCount); removeCinderVolume(completedChildCount, getParameters().getRemovedVolume().getStorageIds().get(0)); return true; } }