package org.ovirt.engine.core.bll.storage;
import org.ovirt.engine.core.bll.Backend;
import org.ovirt.engine.core.bll.InternalCommandAttribute;
import org.ovirt.engine.core.bll.LockIdNameAttribute;
import org.ovirt.engine.core.common.action.StorageDomainPoolParametersBase;
import org.ovirt.engine.core.common.action.StoragePoolWithStoragesParameter;
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.StorageFormatType;
import org.ovirt.engine.core.common.businessentities.StoragePoolIsoMapId;
import org.ovirt.engine.core.common.businessentities.StoragePoolStatus;
import org.ovirt.engine.core.common.businessentities.VDS;
import org.ovirt.engine.core.common.businessentities.storage_domains;
import org.ovirt.engine.core.common.businessentities.storage_pool;
import org.ovirt.engine.core.common.businessentities.storage_pool_iso_map;
import org.ovirt.engine.core.common.errors.VdcBLLException;
import org.ovirt.engine.core.common.errors.VdcBllErrors;
import org.ovirt.engine.core.common.vdscommands.CreateStoragePoolVDSCommandParameters;
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.LogCompat;
import org.ovirt.engine.core.compat.LogFactoryCompat;
import org.ovirt.engine.core.compat.TransactionScopeOption;
import org.ovirt.engine.core.dal.VdcBllMessages;
import org.ovirt.engine.core.dal.dbbroker.DbFacade;
import org.ovirt.engine.core.utils.transaction.TransactionMethod;
import org.ovirt.engine.core.utils.transaction.TransactionSupport;
@InternalCommandAttribute
@LockIdNameAttribute(fieldName = "StoragePoolId")
public class AddStoragePoolWithStoragesCommand<T extends StoragePoolWithStoragesParameter> extends
UpdateStoragePoolCommand<T> {
public AddStoragePoolWithStoragesCommand(T parameters) {
super(parameters);
}
/**
* Constructor for command creation when compensation is applied on startup
*
* @param commandId
*/
protected AddStoragePoolWithStoragesCommand(Guid commandId) {
super(commandId);
}
private storage_domains masterStorageDomain = null;
VDSReturnValue retVal;
@Override
protected void executeCommand() {
TransactionSupport.executeInNewTransaction(new TransactionMethod<Object>() {
@Override
public Object runInTransaction() {
if (UpdateStorageDomainsInDb()) {
// setting storage pool status to maintenance
storage_pool storagePool = getStoragePool();
getCompensationContext().snapshotEntity(storagePool);
TransactionSupport.executeInNewTransaction(new TransactionMethod<Object>() {
@Override
public Object runInTransaction() {
getStoragePool().setstatus(StoragePoolStatus.Maintanance);
getStoragePool().setStoragePoolFormatType(masterStorageDomain.getStorageFormat());
DbFacade.getInstance().getStoragePoolDAO().update(getStoragePool());
getCompensationContext().stateChanged();
StoragePoolStatusHandler.PoolStatusChanged(getStoragePool().getId(),
getStoragePool().getstatus());
return null;
}
});
// Following code performs only read operations, therefore no need for new transaction
boolean returnValue =
TransactionSupport.executeInScope(TransactionScopeOption.Required,
new TransactionMethod<Boolean>() {
@Override
public Boolean runInTransaction() {
boolean result = false;
retVal = null;
for (VDS vds : getAllRunningVdssInPool()) {
setVds(vds);
for (Guid storageDomainId : getParameters().getStorages()) {
// now the domain should have the mapping
// with the pool in db
storage_domains storageDomain =
DbFacade.getInstance()
.getStorageDomainDAO()
.getForStoragePool(storageDomainId,
getStoragePool().getId());
StorageHelperDirector.getInstance()
.getItem(storageDomain.getstorage_type())
.ConnectStorageToDomainByVdsId(storageDomain,
getVds().getvds_id());
}
retVal = AddStoragePoolInIrs();
if (!retVal.getSucceeded()
&& retVal.getVdsError().getCode() == VdcBllErrors.StorageDomainAccessError) {
log.warnFormat("Error creating storage pool on vds {0} - continuing",
vds.getvds_name());
continue;
} else {
// storage pool creation succeeded or failed
// but didn't throw exception
result = retVal.getSucceeded();
break;
}
}
return result;
}
});
setSucceeded(returnValue);
if (!returnValue) {
if (retVal != null && retVal.getVdsError().getCode() != null) {
throw new VdcBLLException(retVal.getVdsError().getCode(), retVal.getVdsError().getMessage());
} else {
// throw exception to cause rollback and stop the
// command
throw new VdcBLLException(VdcBllErrors.ENGINE_ERROR_CREATING_STORAGE_POOL);
}
}
}
return null;
}
});
// Create pool phase completed, no rollback is needed here, so compensation information needs to be cleared!
TransactionSupport.executeInNewTransaction(new TransactionMethod<Void>() {
@Override
public Void runInTransaction() {
getCompensationContext().resetCompensation();
return null;
}
});
freeLock();
// if create succeeded activate
if (getSucceeded()) {
ActivateStorageDomains();
}
}
private boolean UpdateStorageDomainsInDb() {
boolean result = TransactionSupport.executeInNewTransaction(new TransactionMethod<Boolean>() {
@Override
public Boolean runInTransaction() {
for (Guid storageDomainId : getParameters().getStorages()) {
storage_domains storageDomain = DbFacade.getInstance().getStorageDomainDAO().get(
storageDomainId);
if (storageDomain != null) {
storage_pool_iso_map mapFromDB =
DbFacade.getInstance()
.getStoragePoolIsoMapDAO()
.get(new StoragePoolIsoMapId(storageDomain.getid(), getStoragePool().getId()));
boolean existingInDb = mapFromDB != null;
if (existingInDb) {
getCompensationContext().snapshotEntity(mapFromDB);
}
storageDomain.setstorage_pool_id(getStoragePool().getId());
if (masterStorageDomain == null
&& storageDomain.getstorage_domain_type() == StorageDomainType.Data) {
// increase master domain version - no need to snapshot, as we would like
// the master domain version to grow monotonously even if the wrapping transaction fails
getStoragePool().setmaster_domain_version(getStoragePool().getmaster_domain_version() + 1);
getCompensationContext().snapshotEntity(storageDomain.getStorageStaticData());
storageDomain.setstorage_domain_type(StorageDomainType.Master);
DbFacade.getInstance().getStorageDomainStaticDAO().update(storageDomain.getStorageStaticData());
masterStorageDomain = storageDomain;
// The update of storage pool should be without compensation,
// this is why we run it in a different SUPRESS transaction.
updateStoragePoolInDiffTransaction();
}
storageDomain.setstatus(StorageDomainStatus.Locked);
if (existingInDb) {
DbFacade.getInstance()
.getStoragePoolIsoMapDAO()
.update(storageDomain.getStoragePoolIsoMapData());
} else {
DbFacade.getInstance()
.getStoragePoolIsoMapDAO()
.save(storageDomain.getStoragePoolIsoMapData());
getCompensationContext().snapshotNewEntity(storageDomain.getStoragePoolIsoMapData());
}
} else {
return false;
}
}
getCompensationContext().stateChanged();
return true;
}
});
return result && masterStorageDomain != null;
}
/**
* Save the master version out of the transaction
*/
private VDSReturnValue AddStoragePoolInIrs() {
return Backend
.getInstance()
.getResourceManager()
.RunVdsCommand(
VDSCommandType.CreateStoragePool,
new CreateStoragePoolVDSCommandParameters(getVds().getvds_id(), getStoragePool()
.getstorage_pool_type(), getStoragePool().getId(), getStoragePool().getname(),
masterStorageDomain.getid(), getParameters().getStorages(), getStoragePool()
.getmaster_domain_version()));
}
private boolean ActivateStorageDomains() {
boolean returnValue = true;
for (final Guid storageDomainId : getParameters().getStorages()) {
StorageDomainPoolParametersBase activateParameters = new StorageDomainPoolParametersBase(storageDomainId,
getStoragePool().getId());
activateParameters.setSessionId(getParameters().getSessionId());
activateParameters.setTransactionScopeOption(TransactionScopeOption.RequiresNew);
returnValue = Backend.getInstance()
.runInternalAction(VdcActionType.ActivateStorageDomain, activateParameters).getSucceeded();
// if activate domain failed then set domain status to inactive
if (!returnValue) {
TransactionSupport.executeInNewTransaction(new TransactionMethod<Void>() {
@Override
public Void runInTransaction() {
DbFacade.getInstance()
.getStoragePoolIsoMapDAO()
.updateStatus(
new StoragePoolIsoMapId(storageDomainId, getStoragePool().getId()),
StorageDomainStatus.InActive);
return null;
}
});
}
}
return returnValue;
}
private boolean checkStorageDomainsInPool() {
if (!getParameters().getIsInternal()) {
boolean _hasData = false;
StorageFormatType storageFormat = null;
for (Guid storageDomainId : getParameters().getStorages()) {
storage_domains domain = DbFacade.getInstance().getStorageDomainDAO().get(storageDomainId);
if (isStorageDomainNotNull(domain) && checkDomainCanBeAttached(domain)) {
if (domain.getstorage_domain_type() == StorageDomainType.Data) {
_hasData = true;
if (storageFormat == null) {
storageFormat = domain.getStorageFormat();
} else if (storageFormat != domain.getStorageFormat()) {
addCanDoActionMessage(VdcBllMessages.ERROR_CANNOT_ADD_STORAGE_POOL_WITH_DIFFERENT_STORAGE_FORMAT);
return false;
}
}
} else {
return false;
}
}
if (!_hasData) {
addCanDoActionMessage(VdcBllMessages.ERROR_CANNOT_ADD_STORAGE_POOL_WITHOUT_DATA_AND_ISO_DOMAINS);
return false;
}
}
return true;
}
@Override
protected boolean canDoAction() {
boolean returnValue = super.canDoAction() && CheckStoragePool()
&& CheckStoragePoolStatus(StoragePoolStatus.Uninitialized) && InitializeVds()
&& checkStorageDomainsInPool();
return returnValue;
}
@Override
public void Rollback() {
super.Rollback();
// try to set status of all domains in the pool that are locked back to inactive
for (Guid storageDomainId : getParameters().getStorages()) {
storage_pool_iso_map domainPoolMap =
DbFacade.getInstance()
.getStoragePoolIsoMapDAO()
.get(new StoragePoolIsoMapId(storageDomainId,
getStoragePoolId().getValue()));
if (domainPoolMap != null && domainPoolMap.getstatus() == StorageDomainStatus.Locked) {
try {
domainPoolMap.setstatus(StorageDomainStatus.InActive);
DbFacade.getInstance()
.getStoragePoolIsoMapDAO()
.updateStatus(domainPoolMap.getId(), domainPoolMap.getstatus());
} catch (Exception e) {
log.warnFormat("Could not set domain {0} status to inactive in pool {1} during rollback: {2}",
storageDomainId,
getStoragePoolId().getValue(),
e.getMessage());
}
}
}
}
private static LogCompat log = LogFactoryCompat.getLog(AddStoragePoolWithStoragesCommand.class);
}