package org.ovirt.engine.core.bll.storage.domain;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import org.ovirt.engine.core.bll.LockMessagesMatchUtil;
import org.ovirt.engine.core.bll.NonTransactiveCommandAttribute;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.storage.connection.CINDERStorageHelper;
import org.ovirt.engine.core.bll.utils.PermissionSubject;
import org.ovirt.engine.core.bll.validator.storage.StorageDomainToPoolRelationValidator;
import org.ovirt.engine.core.bll.validator.storage.StorageDomainValidator;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.AttachStorageDomainToPoolParameters;
import org.ovirt.engine.core.common.action.LockProperties;
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.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.businessentities.OvfEntityData;
import org.ovirt.engine.core.common.businessentities.StorageDomainStatic;
import org.ovirt.engine.core.common.businessentities.StorageDomainStatus;
import org.ovirt.engine.core.common.businessentities.StorageDomainType;
import org.ovirt.engine.core.common.businessentities.StoragePoolIsoMap;
import org.ovirt.engine.core.common.businessentities.StoragePoolIsoMapId;
import org.ovirt.engine.core.common.businessentities.StoragePoolStatus;
import org.ovirt.engine.core.common.businessentities.storage.DiskImage;
import org.ovirt.engine.core.common.errors.EngineException;
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.common.vdscommands.AttachStorageDomainVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.DetachStorageDomainVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.HSMGetStorageDomainInfoVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.TransactionScopeOption;
import org.ovirt.engine.core.dao.StoragePoolIsoMapDao;
import org.ovirt.engine.core.dao.UnregisteredOVFDataDao;
import org.ovirt.engine.core.dao.profiles.DiskProfileDao;
@NonTransactiveCommandAttribute(forceCompensation = true)
public class AttachStorageDomainToPoolCommand<T extends AttachStorageDomainToPoolParameters> extends
StorageDomainCommandBase<T> {
@Inject
private DiskProfileDao diskProfileDao;
private StoragePoolIsoMap map;
@Inject
private StoragePoolIsoMapDao storagePoolIsoMapDao;
@Inject
private UnregisteredOVFDataDao unregisteredOVFDataDao;
public AttachStorageDomainToPoolCommand(T parameters, CommandContext commandContext) {
super(parameters, commandContext);
}
/**
* Constructor for command creation when compensation is applied on startup
*/
public AttachStorageDomainToPoolCommand(Guid commandId) {
super(commandId);
}
@Override
protected List<DiskImage> getAllOVFDisks(Guid storageDomainId, Guid storagePoolId) {
return super.getAllOVFDisks(storageDomainId, storagePoolId);
}
@Override
protected List<OvfEntityData> getEntitiesFromStorageOvfDisk(Guid storageDomainId, Guid storagePoolId) {
return super.getEntitiesFromStorageOvfDisk(storageDomainId, storagePoolId);
}
@Override
protected LockProperties applyLockProperties(LockProperties lockProperties) {
return lockProperties.withScope(LockProperties.Scope.Execution);
}
@Override
protected void executeCommand() {
if (isCinderStorageDomain()) {
handleCinderDomain();
return;
}
if (getStoragePool().getStatus() == StoragePoolStatus.Uninitialized) {
StoragePoolWithStoragesParameter parameters = new StoragePoolWithStoragesParameter(getStoragePool(),
Collections.singletonList(getStorageDomain().getId()),
getParameters().getSessionId());
parameters.setIsInternal(true);
parameters.setTransactionScopeOption(TransactionScopeOption.Suppress);
VdcReturnValueBase returnValue = runInternalAction(
VdcActionType.AddStoragePoolWithStorages,
parameters,
getContext().clone().withoutCompensationContext());
setSucceeded(returnValue.getSucceeded());
if (!returnValue.getSucceeded()) {
getReturnValue().setFault(returnValue.getFault());
}
} else {
map = storagePoolIsoMapDao.get(new StoragePoolIsoMapId(getStorageDomain().getId(),
getParameters().getStoragePoolId()));
if (map == null) {
executeInNewTransaction(() -> {
map = new StoragePoolIsoMap(getStorageDomain().getId(), getParameters()
.getStoragePoolId(), StorageDomainStatus.Locked);
storagePoolIsoMapDao.save(map);
getCompensationContext().snapshotNewEntity(map);
getCompensationContext().stateChanged();
return null;
});
List<Pair<Guid, Boolean>> hostsConnectionResults = connectHostsInUpToDomainStorageServer();
if (isAllHostConnectionFailed(hostsConnectionResults)) {
log.error("Cannot connect storage connection server, aborting attach storage domain operation.");
setSucceeded(false);
return;
}
// Forcibly detach only data storage domains.
if (getStorageDomain().getStorageDomainType() == StorageDomainType.Data) {
@SuppressWarnings("unchecked")
Pair<StorageDomainStatic, Guid> domainFromIrs =
(Pair<StorageDomainStatic, Guid>) runVdsCommand(
VDSCommandType.HSMGetStorageDomainInfo,
new HSMGetStorageDomainInfoVDSCommandParameters(getVdsId(),
getParameters().getStorageDomainId())
).getReturnValue();
// If the storage domain is already related to another Storage Pool, detach it by force.
Guid storagePoolId = domainFromIrs.getSecond();
if (storagePoolId != null) {
// Master domain version is not relevant since force remove at
// DetachStorageDomainVdsCommand does not use it.
// Storage pool id can be empty
DetachStorageDomainVDSCommandParameters detachParams =
new DetachStorageDomainVDSCommandParameters(getStoragePoolIdFromVds(),
getParameters().getStorageDomainId(),
Guid.Empty,
0);
detachParams.setForce(true);
detachParams.setDetachFromOldStoragePool(true);
try {
runVdsCommand(VDSCommandType.DetachStorageDomain, detachParams);
} catch (EngineException e) {
log.warn("Detaching Storage Domain '{}' from it's previous storage pool '{}'"
+ " has failed. The meta data of the Storage Domain might still"
+ " indicate that it is attached to a different Storage Pool.",
getParameters().getStorageDomainId(),
Guid.Empty,
0);
throw e;
}
}
if (diskProfileDao.getAllForStorageDomain(getStorageDomain().getId()).isEmpty()) {
createDefaultDiskProfile();
}
}
runVdsCommand(VDSCommandType.AttachStorageDomain,
new AttachStorageDomainVDSCommandParameters(getParameters().getStoragePoolId(),
getParameters().getStorageDomainId()));
final List<OvfEntityData> unregisteredEntitiesFromOvfDisk = new ArrayList<>();
if (getStorageDomain().getStorageDomainType().isDataDomain()) {
List<OvfEntityData> returnValueFromStorageOvfDisk = getEntitiesFromStorageOvfDisk(
getParameters().getStorageDomainId(), getStoragePoolIdFromVds());
unregisteredEntitiesFromOvfDisk.addAll(returnValueFromStorageOvfDisk);
}
executeInNewTransaction(() -> {
final StorageDomainType sdType = getStorageDomain().getStorageDomainType();
map.setStatus(StorageDomainStatus.Maintenance);
storagePoolIsoMapDao.updateStatus(map.getId(), map.getStatus());
if (sdType == StorageDomainType.Master) {
calcStoragePoolStatusByDomainsStatus();
}
// upgrade the domain format to the storage pool format
updateStorageDomainFormatIfNeeded(getStorageDomain());
List<DiskImage> ovfStoreDiskImages =
getAllOVFDisks(getParameters().getStorageDomainId(), getStoragePoolIdFromVds());
registerAllOvfDisks(ovfStoreDiskImages, getParameters().getStorageDomainId());
// Update unregistered entities
for (OvfEntityData ovf : unregisteredEntitiesFromOvfDisk) {
unregisteredOVFDataDao.removeEntity(ovf.getEntityId(),
getParameters().getStorageDomainId());
unregisteredOVFDataDao.saveOVFData(ovf);
log.info("Adding OVF data of entity id '{}' and entity name '{}'",
ovf.getEntityId(),
ovf.getEntityName());
}
initUnregisteredDisksToDB(getParameters().getStorageDomainId());
return null;
});
if (getParameters().getActivate()) {
attemptToActivateDomain();
}
setSucceeded(true);
}
}
}
private boolean isAllHostConnectionFailed(List<Pair<Guid, Boolean>> hostsConnectionResults) {
return hostsConnectionResults.stream().map(Pair::getSecond).noneMatch(Boolean.TRUE::equals);
}
private void handleCinderDomain() {
CINDERStorageHelper CINDERStorageHelper = new CINDERStorageHelper();
CINDERStorageHelper.attachCinderDomainToPool(getStorageDomain().getId(),
getParameters().getStoragePoolId());
if (getParameters().getActivate()) {
attemptToActivateCinderDomain();
}
setSucceeded(true);
}
@Override
protected Map<String, Pair<String, String>> getExclusiveLocks() {
return Collections.singletonMap(getParameters().getStorageDomainId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.STORAGE, EngineMessage.ACTION_TYPE_FAILED_OBJECT_LOCKED));
}
protected void attemptToActivateDomain() {
StorageDomainPoolParametersBase activateParameters = new StorageDomainPoolParametersBase(getStorageDomain().getId(),
getStoragePool().getId());
getBackend()
.runInternalAction(VdcActionType.ActivateStorageDomain,
activateParameters,
cloneContext().withoutCompensationContext().withoutExecutionContext());
}
protected void attemptToActivateCinderDomain() {
try {
CINDERStorageHelper CINDERStorageHelper = new CINDERStorageHelper();
CINDERStorageHelper.activateCinderDomain(
getParameters().getStorageDomainId(), getParameters().getStoragePoolId());
} catch (RuntimeException e) {
auditLogDirector.log(this, AuditLogType.USER_ACTIVATE_STORAGE_DOMAIN_FAILED);
}
}
@Override
public AuditLogType getAuditLogTypeValue() {
return getSucceeded() ? AuditLogType.USER_ATTACH_STORAGE_DOMAIN_TO_POOL
: AuditLogType.USER_ATTACH_STORAGE_DOMAIN_TO_POOL_FAILED;
}
@Override
protected boolean validate() {
if (!checkStoragePool()
|| !initializeVds()
|| !checkStorageDomain()) {
return false;
}
StorageDomainToPoolRelationValidator
storageDomainToPoolRelationValidator = new StorageDomainToPoolRelationValidator(getStorageDomain().getStorageStaticData(), getStoragePool());
if (!validate(storageDomainToPoolRelationValidator.validateDomainCanBeAttachedToPool())) {
return false;
}
if (getStoragePool().getStatus() == StoragePoolStatus.Uninitialized
&& getStorageDomain().getStorageDomainType() != StorageDomainType.Data) {
return failValidation(EngineMessage.ERROR_CANNOT_ADD_STORAGE_POOL_WITHOUT_DATA_DOMAIN);
}
StorageDomainValidator storageDomainValidator = new StorageDomainValidator(getStorageDomain());
if (!validate(storageDomainValidator.isDiscardAfterDeleteSupportedByDcVersion(getStoragePool().getCompatibilityVersion()))) {
return false;
}
if (getStoragePool().getStatus() != StoragePoolStatus.Uninitialized) {
return checkMasterDomainIsUp();
}
return true;
}
@Override
protected void setActionMessageParameters() {
addValidationMessage(EngineMessage.VAR__TYPE__STORAGE__DOMAIN);
addValidationMessage(EngineMessage.VAR__ACTION__ATTACH);
}
@Override
public List<PermissionSubject> getPermissionCheckSubjects() {
List<PermissionSubject> permissionList = super.getPermissionCheckSubjects();
permissionList.add(new PermissionSubject(getStoragePoolId(),
VdcObjectType.StoragePool,
getActionType().getActionGroup()));
return permissionList;
}
protected Guid getStoragePoolIdFromVds() {
return getVds().getStoragePoolId();
}
}