package org.ovirt.engine.core.bll.storage.pool; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import javax.inject.Inject; import org.ovirt.engine.core.bll.NonTransactiveCommandAttribute; import org.ovirt.engine.core.bll.context.CommandContext; import org.ovirt.engine.core.bll.storage.connection.StorageHelperDirector; import org.ovirt.engine.core.bll.storage.domain.DeactivateStorageDomainCommand; import org.ovirt.engine.core.bll.validator.storage.StorageDomainValidator; import org.ovirt.engine.core.bll.validator.storage.StoragePoolValidator; import org.ovirt.engine.core.common.AuditLogType; import org.ovirt.engine.core.common.action.LockProperties; import org.ovirt.engine.core.common.action.ReconstructMasterParameters; import org.ovirt.engine.core.common.businessentities.StorageDomain; import org.ovirt.engine.core.common.businessentities.StorageDomainStatus; import org.ovirt.engine.core.common.businessentities.StoragePoolIsoMap; import org.ovirt.engine.core.common.businessentities.VDS; import org.ovirt.engine.core.common.businessentities.VDSStatus; import org.ovirt.engine.core.common.config.Config; import org.ovirt.engine.core.common.config.ConfigValues; import org.ovirt.engine.core.common.errors.EngineError; import org.ovirt.engine.core.common.errors.EngineException; import org.ovirt.engine.core.common.errors.EngineMessage; import org.ovirt.engine.core.common.vdscommands.ConnectStoragePoolVDSCommandParameters; import org.ovirt.engine.core.common.vdscommands.DisconnectStoragePoolVDSCommandParameters; import org.ovirt.engine.core.common.vdscommands.IrsBaseVDSCommandParameters; import org.ovirt.engine.core.common.vdscommands.ReconstructMasterVDSCommandParameters; import org.ovirt.engine.core.common.vdscommands.ResetIrsVDSCommandParameters; 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.dao.StoragePoolIsoMapDao; import org.ovirt.engine.core.dao.VdsDao; import org.ovirt.engine.core.utils.threadpool.ThreadPoolUtil; @NonTransactiveCommandAttribute(forceCompensation = true) public class ReconstructMasterDomainCommand<T extends ReconstructMasterParameters> extends DeactivateStorageDomainCommand<T> { @Inject private StoragePoolIsoMapDao storagePoolIsoMapDao; @Inject private VdsDao vdsDao; protected StorageDomain newMasterStorageDomain; protected Guid newMasterStorageDomainId; protected boolean isLastMaster; /** * Constructor for command creation when compensation is applied on startup */ public ReconstructMasterDomainCommand(Guid commandId) { super(commandId); } public ReconstructMasterDomainCommand(T parameters, CommandContext commandContext) { super(parameters, commandContext); } @Override protected boolean initializeVds() { return super.initializeVds(); } @Override protected LockProperties applyLockProperties(LockProperties lockProperties) { return lockProperties; } protected StorageDomain getNewMasterStorageDomain() { if (newMasterStorageDomainId == null) { getNewMasterStorageDomainId(); } return newMasterStorageDomain; } protected Guid getNewMasterStorageDomainId() { if (newMasterStorageDomainId == null) { newMasterStorageDomain = electNewMaster(true, getParameters().isCanChooseInactiveDomainAsMaster(), getParameters().isCanChooseCurrentMasterAsNewMaster()); newMasterStorageDomainId = newMasterStorageDomain != null ? newMasterStorageDomain.getId() : Guid.Empty; } return newMasterStorageDomainId; } protected StorageDomainValidator createStorageDomainValidator() { return new StorageDomainValidator(getStorageDomain()); } protected StoragePoolValidator createStoragePoolValidator() { return new StoragePoolValidator(getStoragePool()); } @Override protected boolean validate() { // This check is done here to handle a race in which the returned domain from // getStorageDomains() is with LOCKED status. Having this domain with LOCKED status might // cause to the command to apply the compensation data and leave the domain as LOCKED. if (!validate(createStorageDomainValidator().isInProcess())) { return false; } if (!validate(createStoragePoolValidator().isAnyDomainInProcess())) { return false; } return initializeVds(); } @Override protected void setActionMessageParameters() { addValidationMessage(EngineMessage.VAR__ACTION__RECONSTRUCT_MASTER); addValidationMessage(EngineMessage.VAR__TYPE__STORAGE__DOMAIN); } protected boolean reconstructMaster() { isLastMaster = proceedStorageDomainTreatmentByDomainType(getNewMasterStorageDomain(), false); // To issue a reconstructMaster you need to set the domain inactive unless the selected domain is the current master if (getParameters().isInactive() && !getStorageDomain().getId().equals(getNewMasterStorageDomainId())) { executeInNewTransaction(() -> { setStorageDomainStatus(StorageDomainStatus.Inactive, getCompensationContext()); calcStoragePoolStatusByDomainsStatus(); getCompensationContext().stateChanged(); return null; }); } if (isLastMaster) { return stopSpm(); } boolean commandSucceeded = stopSpm(); final List<String> disconnectPoolFormats = Config.getValue(ConfigValues.DisconnectPoolOnReconstruct); if (commandSucceeded && disconnectPoolFormats.contains(getNewMasterStorageDomain().getStorageFormat().getValue())) { commandSucceeded = runVdsCommand( VDSCommandType.DisconnectStoragePool, new DisconnectStoragePoolVDSCommandParameters(getVds().getId(), getStoragePool().getId(), getVds().getVdsSpmId()) ).getSucceeded(); } if (!commandSucceeded) { return false; } List<StoragePoolIsoMap> domains = storagePoolIsoMapDao.getAllForStoragePool(getStoragePool().getId()); // set to true here in case of failure in executing/getting answer from the reconstruct vds command, // unless we know that the command failed we assume that it succeeded (use by RecoveryStoragePool command in // order to avoid detaching domain that is already part of the pool in vdsm). setActionReturnValue(true); return runVdsCommand(VDSCommandType.ReconstructMaster, new ReconstructMasterVDSCommandParameters(getVds().getId(), getVds().getVdsSpmId(), getStoragePool().getId(), getStoragePool().getName(), getNewMasterStorageDomainId(), domains, getStoragePool().getMasterDomainVersion())).getSucceeded(); } @Override protected void executeCommand() { boolean reconstructOpSucceeded = reconstructMaster(); setActionReturnValue(reconstructOpSucceeded); connectAndRefreshAllUpHosts(reconstructOpSucceeded); if (isLastMaster) { getCompensationContext().cleanupCompensationDataAfterSuccessfulCommand(); } setSucceeded(!isLastMaster && reconstructOpSucceeded); if (getSucceeded()) { runVdsCommand(VDSCommandType.MarkPoolInReconstructMode, new IrsBaseVDSCommandParameters(getStoragePoolId())); } } protected boolean stopSpm() { boolean commandSucceeded = true; if (getStoragePool().getSpmVdsId() != null) { // if spm host id is different from selected host get the spm // in order to try and perform stop spm VDS spm; if (getStoragePool().getSpmVdsId().equals(getVds().getId())) { spm = getVds(); } else { spm = vdsDao.get(getStoragePool().getSpmVdsId()); } if (spm != null) { ResetIrsVDSCommandParameters tempVar2 = new ResetIrsVDSCommandParameters( getStoragePool().getId(), spm.getId()); tempVar2.setIgnoreStopFailed(true); commandSucceeded = runVdsCommand(VDSCommandType.ResetIrs, tempVar2).getSucceeded(); // if spm host is up switch to use it in the following logic if (spm.getStatus() == VDSStatus.Up) { setVdsId(spm.getId()); setVds(spm); } } } return commandSucceeded; } /** * performs any connect related operations if needed before attempting * to connect/refresh pool information. */ private boolean connectVdsToNewMaster(VDS vds) { StorageDomain masterDomain = getNewMasterStorageDomain(); if (vds.getId().equals(getVds().getId()) || StorageHelperDirector.getInstance().getItem(masterDomain.getStorageType()) .connectStorageToDomainByVdsId(masterDomain, vds.getId())) { return true; } log.error("Error while trying connect host {} to the needed storage server during the reinitialization" + " of Data Center '{}'", vds.getId(), getStoragePool().getId()); return false; } @Override protected List<VDS> getAllRunningVdssInPool() { return vdsDao.getAllForStoragePoolAndStatus(getStoragePool().getId(), VDSStatus.Up); } private void connectAndRefreshAllUpHosts(final boolean commandSucceeded) { if (isLastMaster || !commandSucceeded) { log.warn("skipping connect and refresh for all hosts, last master '{}', command status '{}'", isLastMaster, commandSucceeded); return; } List<Callable<Void>> tasks = new ArrayList<>(); for (final VDS vds : getAllRunningVdssInPool()) { tasks.add(() -> { try { if (!connectVdsToNewMaster(vds)) { log.warn("failed to connect vds '{}' to the new master '{}'", vds.getId(), getNewMasterStorageDomainId()); return null; } List<StoragePoolIsoMap> storagePoolIsoMap = storagePoolIsoMapDao.getAllForStoragePool(getStoragePool().getId()); try { runVdsCommand( VDSCommandType.ConnectStoragePool, new ConnectStoragePoolVDSCommandParameters(vds, getStoragePool(), getNewMasterStorageDomainId(), storagePoolIsoMap, true)); } catch (EngineException ex) { if (EngineError.StoragePoolUnknown == ex.getVdsError().getCode()) { VDSReturnValue returnVal = runVdsCommand( VDSCommandType.ConnectStoragePool, new ConnectStoragePoolVDSCommandParameters(vds, getStoragePool(), getNewMasterStorageDomainId(), storagePoolIsoMap)); if (!returnVal.getSucceeded()) { log.error("Post reconstruct actions (connectPool) did not complete on host '{}' in the pool. error {}", vds.getId(), returnVal.getVdsError().getMessage()); } } else { log.error("Post reconstruct actions (refreshPool)" + " did not complete on host '{}' in the pool. error {}", vds.getId(), ex.getMessage()); } } } catch (Exception e) { log.error("Post reconstruct actions (connectPool,refreshPool,disconnect storage)" + " did not complete on host '{}' in the pool: {}", vds.getId(), e.getMessage()); log.debug("Exception", e); } return null; }); } ThreadPoolUtil.invokeAll(tasks); } @Override public AuditLogType getAuditLogTypeValue() { return getSucceeded() ? isLastMaster ? AuditLogType.RECONSTRUCT_MASTER_FAILED_NO_MASTER : AuditLogType.RECONSTRUCT_MASTER_DONE : AuditLogType.RECONSTRUCT_MASTER_FAILED; } }