package org.ovirt.engine.core.bll.storage; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.ovirt.engine.core.bll.Backend; import org.ovirt.engine.core.bll.NonTransactiveCommandAttribute; import org.ovirt.engine.core.common.AuditLogType; import org.ovirt.engine.core.common.action.DetachStorageDomainFromPoolParameters; import org.ovirt.engine.core.common.action.RemoveStorageDomainParameters; import org.ovirt.engine.core.common.action.StoragePoolParametersBase; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.businessentities.StorageDomainStatus; import org.ovirt.engine.core.common.businessentities.StorageDomainType; import org.ovirt.engine.core.common.businessentities.StoragePoolStatus; import org.ovirt.engine.core.common.businessentities.StorageType; import org.ovirt.engine.core.common.businessentities.VDS; import org.ovirt.engine.core.common.businessentities.VmStatic; import org.ovirt.engine.core.common.businessentities.network; import org.ovirt.engine.core.common.businessentities.storage_domains; import org.ovirt.engine.core.common.businessentities.storage_pool; import org.ovirt.engine.core.common.errors.VdcBLLException; import org.ovirt.engine.core.common.vdscommands.FormatStorageDomainVDSCommandParameters; import org.ovirt.engine.core.common.vdscommands.IrsBaseVDSCommandParameters; import org.ovirt.engine.core.common.vdscommands.VDSCommandType; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.compat.LogCompat; import org.ovirt.engine.core.compat.LogFactoryCompat; import org.ovirt.engine.core.dal.VdcBllMessages; import org.ovirt.engine.core.dal.dbbroker.DbFacade; import org.ovirt.engine.core.utils.ISingleAsyncOperation; import org.ovirt.engine.core.utils.SyncronizeNumberOfAsyncOperations; 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; @NonTransactiveCommandAttribute(forceCompensation = true) public class RemoveStoragePoolCommand<T extends StoragePoolParametersBase> extends StorageHandlingCommandBase<T> { private static LogCompat log = LogFactoryCompat.getLog(RemoveStoragePoolCommand.class); public RemoveStoragePoolCommand(T parameters) { super(parameters); } protected RemoveStoragePoolCommand(Guid commandId) { super(commandId); } @Override protected void executeCommand() { removeNetworks(); /** * Detach master storage domain last. */ List<storage_domains> storageDomains = DbFacade.getInstance().getStorageDomainDAO().getAllForStoragePool( getStoragePool().getId()); Collections.sort(storageDomains, new Comparator<storage_domains>() { @Override public int compare(storage_domains o1, storage_domains o2) { return o1.getstorage_domain_type().compareTo(o2.getstorage_domain_type()); } }); if (storageDomains.size() > 0) { if (!getParameters().getForceDelete() && getAllRunningVdssInPool().size() > 0) { if(!regularRemoveStorageDomains(storageDomains)) { setSucceeded(false); return; } } else if (getParameters().getForceDelete()) { forceRemoveStorageDomains(storageDomains); } else { return; } } TransactionSupport.executeInNewTransaction(new TransactionMethod<Void>() { @Override public Void runInTransaction() { getCompensationContext().snapshotEntity(getStoragePool()); DbFacade.getInstance().getStoragePoolDAO().remove(getStoragePool().getId()); getCompensationContext().stateChanged(); return null; } }); setSucceeded(true); } private void removeNetworks() { TransactionSupport.executeInNewTransaction(new TransactionMethod<Void>() { @Override public Void runInTransaction() { final List<network> networks = DbFacade.getInstance().getNetworkDAO() .getAllForDataCenter(getStoragePoolId().getValue()); for (final network net : networks) { getCompensationContext().snapshotEntity(net); DbFacade.getInstance().getNetworkDAO().remove(net.getId()); } getCompensationContext().stateChanged(); return null; } }); } private void forceRemoveStorageDomains(List<storage_domains> storageDomains) { storage_domains masterDomain = null; for (storage_domains storageDomain : storageDomains) { if (storageDomain.getstorage_domain_type() != StorageDomainType.Master) { if (storageDomain.getstorage_domain_type() != StorageDomainType.ISO) { removeDomainFromDb(storageDomain); } } else { masterDomain = storageDomain; } } if (masterDomain != null) { removeDomainFromDb(masterDomain); } } private boolean regularRemoveStorageDomains(List<storage_domains> storageDomains) { boolean retVal = true; List<storage_domains> temp = LinqUtils.filter(storageDomains, new Predicate<storage_domains>() { @Override public boolean eval(storage_domains storage_domain) { return storage_domain.getstorage_domain_type() == StorageDomainType.Master; } }); final storage_domains masterDomain = LinqUtils.first(temp); TransactionSupport.executeInNewTransaction(new TransactionMethod<Void>() { @Override public Void runInTransaction() { getCompensationContext().snapshotEntity(masterDomain.getStoragePoolIsoMapData()); masterDomain.setstatus(StorageDomainStatus.Locked); DbFacade.getInstance() .getStoragePoolIsoMapDAO() .update(masterDomain.getStoragePoolIsoMapData()); getCompensationContext().stateChanged(); return null; } }); // destroying a pool is an SPM action. We need to connect all hosts // to the pool. Later on, during spm election, one of the hosts will // lock the pool // and the spm status will be FREE. Only then we can invoke the // destroy verb. connectAllHostToPoolAndDomain(masterDomain); List<VDS> vdss = getAllRunningVdssInPool(); for (storage_domains storageDomain : storageDomains) { if (storageDomain.getstorage_domain_type() != StorageDomainType.Master) { if (!removeDomainFromPool(storageDomain, vdss.get(0))) { log.errorFormat("Unable to detach storage domain {0} {1}", storageDomain.getstorage_name(), storageDomain.getid()); retVal = false; } } } TransactionSupport.executeInNewTransaction(new TransactionMethod<Void>() { @Override public Void runInTransaction() { getCompensationContext().snapshotEntity(masterDomain.getStorageStaticData()); masterDomain.setstorage_domain_type(StorageDomainType.Data); DbFacade.getInstance().getStorageDomainStaticDAO().update(masterDomain.getStorageStaticData()); getCompensationContext().stateChanged(); return null; } }); TransactionSupport.executeInNewTransaction(new TransactionMethod<Void>() { @Override public Void runInTransaction() { getCompensationContext().snapshotEntity(getStoragePool()); handleDestroyStoragePoolCommand(); getCompensationContext().stateChanged(); return null; } }); setSucceeded(true); TransactionSupport.executeInNewTransaction(new TransactionMethod<Void>() { @Override public Void runInTransaction() { getCompensationContext().snapshotEntity(masterDomain.getStoragePoolIsoMapData()); DbFacade.getInstance() .getStoragePoolIsoMapDAO() .remove(masterDomain.getStoragePoolIsoMapData().getId()); getCompensationContext().stateChanged(); return null; } }); if (getStoragePool().getstorage_pool_type() != StorageType.LOCALFS) { for (VDS vds : vdss) { StorageHelperDirector.getInstance().getItem(getStoragePool().getstorage_pool_type()) .DisconnectStorageFromDomainByVdsId(masterDomain, vds.getvds_id()); } } else { try { Backend .getInstance() .getResourceManager() .RunVdsCommand(VDSCommandType.FormatStorageDomain, new FormatStorageDomainVDSCommandParameters(vdss.get(0).getvds_id(), masterDomain.getid())); } catch (VdcBLLException e) { // Do nothing, exception already printed at logs } StorageHelperDirector.getInstance().getItem(getStoragePool().getstorage_pool_type()) .DisconnectStorageFromDomainByVdsId(masterDomain, vdss.get(0).getvds_id()); removeDomainFromDb(masterDomain); } runSynchronizeOperation(new DisconnectStoragePoolAsyncOperationFactory()); return retVal; } private void handleDestroyStoragePoolCommand() { try { Backend .getInstance() .getResourceManager() .RunVdsCommand(VDSCommandType.DestroyStoragePool, new IrsBaseVDSCommandParameters(getStoragePool().getId())); } catch (VdcBLLException e) { try { TransactionSupport.executeInNewTransaction(new TransactionMethod<Void>() { @Override public Void runInTransaction() { Backend.getInstance() .getResourceManager() .RunVdsCommand(VDSCommandType.SpmStopOnIrs, new IrsBaseVDSCommandParameters(getStoragePool().getId())); return null; } }); } catch (Exception e1) { log.errorFormat("Failed destroy storage pool with id {0} and after that failed to stop spm because of {1}", getStoragePoolId(), e1); } throw e; } } private void removeDomainFromDb(final storage_domains domain) { TransactionSupport.executeInNewTransaction(new TransactionMethod<Void>() { @Override public Void runInTransaction() { // Not compensation for remove domain as we don't want // to rollback a deleted domain - it will only cause more // problems if a domain got deleted in VDSM and not in backend // as it will be impossible to remove it. StorageHelperDirector.getInstance().getItem(domain.getstorage_type()) .StorageDomainRemoved(domain.getStorageStaticData()); DbFacade.getInstance().getStorageDomainDAO().remove(domain.getid()); return null; } }); } protected boolean removeDomainFromPool(storage_domains storageDomain, VDS vds) { if (storageDomain.getstorage_type() != StorageType.LOCALFS) { DetachStorageDomainFromPoolParameters tempVar = new DetachStorageDomainFromPoolParameters( storageDomain.getid(), getStoragePool().getId()); tempVar.setRemoveLast(true); tempVar.setDestroyingPool(true); // Compensation context is not passed, as we do not want to compensate in case of failure // in detach of one of storage domains if (!Backend.getInstance() .runInternalAction(VdcActionType.DetachStorageDomainFromPool, tempVar) .getSucceeded()) { return false; } } else { RemoveStorageDomainParameters tempVar = new RemoveStorageDomainParameters(storageDomain.getid()); tempVar.setDestroyingPool(true); tempVar.setDoFormat(true); tempVar.setVdsId(vds.getvds_id()); if (!Backend.getInstance() .runInternalAction(VdcActionType.RemoveStorageDomain, tempVar, getCompensationContext()) .getSucceeded()) { return false; } } return true; } @Override protected boolean canDoAction() { boolean returnValue = super.canDoAction() && CheckStoragePool() && CheckStoragePoolStatusNotEqual(StoragePoolStatus.Up, VdcBllMessages.ERROR_CANNOT_REMOVE_ACTIVE_STORAGE_POOL); addCanDoActionMessage(VdcBllMessages.VAR__TYPE__STORAGE__POOL); addCanDoActionMessage(VdcBllMessages.VAR__ACTION__REMOVE); if (returnValue) { if (getStoragePool().getstatus() != StoragePoolStatus.Uninitialized && !getParameters().getForceDelete()) { returnValue = InitializeVds(); } List<storage_domains> poolDomains = DbFacade.getInstance().getStorageDomainDAO().getAllForStoragePool( getStoragePool().getId()); List<storage_domains> domainsList = poolDomains; if (returnValue) { domainsList = getActiveOrLockedDomainList(domainsList); if (!domainsList.isEmpty()) { returnValue = false; addCanDoActionMessage(VdcBllMessages.ERROR_CANNOT_REMOVE_POOL_WITH_ACTIVE_DOMAINS); } } if (returnValue && !getParameters().getForceDelete()) { for (storage_domains domain : poolDomains) { // check that there are no images on data domains if ((domain.getstorage_domain_type() == StorageDomainType.Data || domain.getstorage_domain_type() == StorageDomainType.Master) && DbFacade.getInstance() .getDiskImageDAO() .getAllSnapshotsForStorageDomain(domain.getid()) .size() != 0) { returnValue = false; addCanDoActionMessage(VdcBllMessages.ERROR_CANNOT_REMOVE_STORAGE_POOL_WITH_IMAGES); break; } } } // Check that there are no VMs with if (returnValue && !getParameters().getForceDelete()) { List<VmStatic> vms = DbFacade.getInstance().getVmStaticDAO().getAllByStoragePoolId(getStoragePool().getId()); if (vms.size() > 0) { returnValue = false; addCanDoActionMessage(VdcBllMessages.ERROR_CANNOT_REMOVE_STORAGE_POOL_WITH_VMS); } } } return returnValue; } protected List<storage_domains> getActiveOrLockedDomainList(List<storage_domains> domainsList) { domainsList = LinqUtils.filter(domainsList, new Predicate<storage_domains>() { @Override public boolean eval(storage_domains dom) { return (dom.getstatus() == StorageDomainStatus.Active || dom.getstatus() == StorageDomainStatus.Locked); } }); return domainsList; } @Override public AuditLogType getAuditLogTypeValue() { return getSucceeded() ? AuditLogType.USER_REMOVE_STORAGE_POOL : AuditLogType.USER_REMOVE_STORAGE_POOL_FAILED; } /** * @param masterDomain * Connect all hosts to the pool and to the domains */ protected void connectAllHostToPoolAndDomain(final storage_domains masterDomain) { final List<VDS> vdsList = getAllRunningVdssInPool(); final storage_pool storagePool = getStoragePool(); SyncronizeNumberOfAsyncOperations sync = new SyncronizeNumberOfAsyncOperations(vdsList.size(), null, new ActivateDeactivateSingleAsyncOperationFactory() { @Override public ISingleAsyncOperation CreateSingleAsyncOperation() { return new ConntectVDSToPoolAndDomains((ArrayList<VDS>) vdsList, masterDomain, storagePool); } @Override public void Initialize(ArrayList parameters) { // no need to initilalize params } }); sync.Execute(); } }