package org.ovirt.engine.core.bll.storage.disk.cinder; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import javax.inject.Inject; import org.ovirt.engine.core.bll.InternalCommandAttribute; import org.ovirt.engine.core.bll.LockMessagesMatchUtil; import org.ovirt.engine.core.bll.SerialChildCommandsExecutionCallback; import org.ovirt.engine.core.bll.context.CommandContext; import org.ovirt.engine.core.bll.storage.disk.image.ImagesHandler; import org.ovirt.engine.core.bll.tasks.interfaces.CommandCallback; import org.ovirt.engine.core.common.AuditLogType; import org.ovirt.engine.core.common.VdcObjectType; import org.ovirt.engine.core.common.action.RemoveCinderDiskParameters; import org.ovirt.engine.core.common.businessentities.Snapshot; import org.ovirt.engine.core.common.businessentities.SubjectEntity; import org.ovirt.engine.core.common.businessentities.VM; import org.ovirt.engine.core.common.businessentities.VmDeviceId; 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.ImageStatus; import org.ovirt.engine.core.common.errors.EngineMessage; import org.ovirt.engine.core.common.locks.LockingGroup; import org.ovirt.engine.core.common.utils.Pair; import org.ovirt.engine.core.compat.CommandStatus; import org.ovirt.engine.core.compat.TransactionScopeOption; import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogDirector; import org.ovirt.engine.core.dao.BaseDiskDao; import org.ovirt.engine.core.dao.DiskDao; import org.ovirt.engine.core.dao.DiskImageDao; import org.ovirt.engine.core.dao.ImageDao; import org.ovirt.engine.core.dao.VmDeviceDao; import org.ovirt.engine.core.utils.lock.EngineLock; import org.ovirt.engine.core.utils.transaction.TransactionSupport; @InternalCommandAttribute public class RemoveCinderDiskCommand<T extends RemoveCinderDiskParameters> extends RemoveCinderVolumeParentCommand<T> { @Inject private ImageDao imageDao; @Inject private VmDeviceDao vmDeviceDao; @Inject private BaseDiskDao baseDiskDao; @Inject private DiskDao diskDao; private CinderDisk cinderDisk; @Inject private DiskImageDao diskImageDao; public RemoveCinderDiskCommand(T parameters, CommandContext commandContext) { super(parameters, commandContext); } @Override public void executeCommand() { CinderDisk disk = getDisk(); lockDisk(); VM vm = getVmForNonShareableDiskImage(disk); // if the disk is not part of a vm (floating), there are no snapshots to update // so no lock is required. if (getParameters().isLockVM() && vm != null) { getParameters().setVmId(vm.getId()); lockVmSnapshotsWithWait(vm); } getParameters().setRemovedVolume(getDisk()); getParameters().setStorageDomainId(getDisk().getStorageIds().get(0)); getParameters().setUpdateSnapshot(true); if (getDisk().getImageStatus() == ImageStatus.ILLEGAL) { handleRemoveCinderVolumesForIllegal(); setCommandStatus(CommandStatus.SUCCEEDED); setSucceeded(true); return; } // Get the first volume to delete from. CinderDisk parentVolume = (CinderDisk) diskImageDao.getSnapshotById(disk.getId()); initCinderDiskVolumesParametersList(parentVolume); if (!removeCinderVolume(0, getDisk().getStorageIds().get(0))) { imageDao.updateStatusOfImagesByImageGroupId(getDisk().getId(), ImageStatus.ILLEGAL); setSucceeded(false); return; } persistCommand(getParameters().getParentCommand(), true); getReturnValue().setActionReturnValue(disk.getId()); setSucceeded(true); } private void handleRemoveCinderVolumesForIllegal() { final List<DiskImage> diskSnapshots = diskImageDao.getAllSnapshotsForImageGroup(cinderDisk.getId()); ImagesHandler.sortImageList(diskSnapshots); TransactionSupport.executeInScope(TransactionScopeOption.Required, () -> { int indCinderVolumeToDelete = diskSnapshots.size() - 1; while (indCinderVolumeToDelete >= 0) { CinderDisk cinderVolume = (CinderDisk) diskSnapshots.get(indCinderVolumeToDelete); Snapshot updated = getSnapshotWithoutCinderVolume(cinderVolume); removeDiskFromDb(cinderVolume, updated); indCinderVolumeToDelete--; } return null; }); } @Override protected void endSuccessfully() { freeVmSnapshotsWithWait(); vmDeviceDao.remove(new VmDeviceId(getParameters().getRemovedVolume().getId(), null)); baseDiskDao.remove(getParameters().getRemovedVolume().getId()); if (getParameters().getShouldBeLogged()) { new AuditLogDirector().log(this, AuditLogType.USER_FINISHED_REMOVE_DISK); } setSucceeded(true); } @Override protected void endWithFailure() { freeVmSnapshotsWithWait(); int removedVolumeIndex = getParameters().getRemovedVolumeIndex(); final CinderDisk cinderVolume = getParameters().getChildCommandsParameters().get(removedVolumeIndex).getRemovedVolume(); imageDao.updateStatusOfImagesByImageGroupId(getDisk().getId(), ImageStatus.ILLEGAL); addCustomValue("imageId", cinderVolume.getImageId().toString()); new AuditLogDirector().log(this, AuditLogType.USER_FINISHED_FAILED_REMOVE_CINDER_DISK); setSucceeded(true); } @Override public Map<String, String> getJobMessageProperties() { if (jobProperties == null) { jobProperties = super.getJobMessageProperties(); jobProperties.put("diskalias", getDiskAlias()); } return jobProperties; } public String getDiskAlias() { return getParameters().getRemovedVolume().getDiskAlias(); } private void lockDisk() { ImagesHandler.updateAllDiskImageSnapshotsStatusWithCompensation(getDisk().getId(), ImageStatus.LOCKED, ImageStatus.ILLEGAL, getCompensationContext()); } protected CinderDisk getDisk() { if (cinderDisk == null) { cinderDisk = (CinderDisk) diskDao.get(getParameters().getDiskId()); } return cinderDisk; } private void freeVmSnapshotsWithWait() { if (getParameters().getVmId() != null) { EngineLock snapshotsEngineLock = new EngineLock(); Map<String, Pair<String, String>> snapshotsExclusiveLockMap = Collections.singletonMap(getParameters().getVmId().toString(), LockMessagesMatchUtil.makeLockingPair(LockingGroup.VM_SNAPSHOTS, EngineMessage.ACTION_TYPE_FAILED_OBJECT_LOCKED)); snapshotsEngineLock.setExclusiveLocks(snapshotsExclusiveLockMap); lockManager.releaseLock(snapshotsEngineLock); } } @Override public CommandCallback getCallback() { return new SerialChildCommandsExecutionCallback(); } @Override protected Collection<SubjectEntity> getSubjectEntities() { return Collections.singleton(new SubjectEntity(VdcObjectType.Storage, getDisk().getStorageIds().get(0))); } }