package org.ovirt.engine.core.bll.storage.utils; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.stream.Collectors; import org.ovirt.engine.core.bll.Backend; import org.ovirt.engine.core.bll.CommandBase; import org.ovirt.engine.core.bll.VdsHandler; import org.ovirt.engine.core.bll.interfaces.BackendInternal; import org.ovirt.engine.core.common.VdcObjectType; 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.job.Step; import org.ovirt.engine.core.common.job.StepEnum; import org.ovirt.engine.core.common.job.StepSubjectEntity; import org.ovirt.engine.core.common.vdscommands.VDSCommandType; import org.ovirt.engine.core.common.vdscommands.VDSReturnValue; import org.ovirt.engine.core.common.vdscommands.VdsIdVDSCommandParametersBase; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dal.dbbroker.DbFacade; import org.ovirt.engine.core.dal.job.ExecutionMessageDirector; import org.ovirt.engine.core.dao.StepDao; import org.ovirt.engine.core.dao.StepSubjectEntityDao; import org.ovirt.engine.core.dao.VdsDao; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class VdsCommandsHelper { private static final Logger log = LoggerFactory.getLogger(VdsCommandsHelper.class); private VdsCommandsHelper() { } public static VDSReturnValue runVdsCommandWithFailover(VDSCommandType vdsCommandType, VdsIdVDSCommandParametersBase params, Guid storagePoolId, CommandBase<?> cmd) { return runVdsCommand(vdsCommandType, params, storagePoolId, cmd, true); } public static VDSReturnValue runVdsCommandWithoutFailover(VDSCommandType vdsCommandType, VdsIdVDSCommandParametersBase parametersBase, Guid storagePoolId, CommandBase<?> cmd) { return runVdsCommand(vdsCommandType, parametersBase, storagePoolId, cmd, false); } private static VDSReturnValue runVdsCommand(VDSCommandType vdsCommandType, VdsIdVDSCommandParametersBase params, Guid storagePoolId, CommandBase<?> cmd, boolean performFailover) { Set<Guid> executedHosts = new HashSet<>(); VDSReturnValue returnValue = null; if (params.getVdsId() == null) { chooseHostForExecution(params, storagePoolId, cmd, Collections.emptyList()); if (params.getVdsId() == null) { throw new EngineException(EngineError.RESOURCE_MANAGER_VDS_NOT_FOUND, "No host was found to perform the operation"); } } int attempts = 0; while (attempts <= Config.<Integer>getValue(ConfigValues.HsmCommandFailOverRetries)) { try { attempts++; returnValue = getBackend().getResourceManager().runVdsCommand(vdsCommandType, params); if (returnValue != null && returnValue.getSucceeded()) { return returnValue; } } catch (EngineException e) { returnValue = e.getVdsReturnValue(); } executedHosts.add(params.getVdsId()); if (!performFailover || (returnValue != null && !returnValue.isCanTryOnDifferentVds())) { break; } chooseHostForExecution(params, storagePoolId, cmd, executedHosts); if (params.getVdsId() == null) { break; } } return VdsHandler.handleVdsResult(returnValue); } private static void chooseHostForExecution(VdsIdVDSCommandParametersBase parametersBase, Guid storagePoolId, CommandBase<?> cmd, Collection<Guid> executedHosts) { Guid vdsForExecution = getHostForExecution(storagePoolId, executedHosts); parametersBase.setVdsId(vdsForExecution); if (cmd != null) { if (cmd.getCommandStep() != null && cmd.getExecutionContext().getStep() != null) { Guid stepId = cmd.getExecutionContext().getStep().getId(); if (cmd.getParameters().getVdsRunningOn() != null) { getStepSubjectEntityDao().remove(cmd.getParameters().getVdsRunningOn(), stepId); } if (vdsForExecution != null) { getStepSubjectEntityDao().saveAll(Collections.singletonList( new StepSubjectEntity(stepId, VdcObjectType.EXECUTION_HOST, vdsForExecution))); updateStepMessage(cmd, vdsForExecution); } } cmd.getParameters().setVdsRunningOn(vdsForExecution); cmd.persistCommand(cmd.getParameters().getParentCommand(), cmd .getCallback() != null); } } public static Guid getHostForExecution(Guid poolId, Collection<Guid> hostsToFilter) { List<Guid> hostsForExecution = getVdsDao() .getAllForStoragePoolAndStatus(poolId, VDSStatus.Up).stream() .filter(x -> !hostsToFilter.contains(x.getId())) .map(x -> x.getId()).collect(Collectors.toList()); if (hostsForExecution.isEmpty()) { return null; } return hostsForExecution.get(new Random().nextInt(hostsForExecution.size())); } protected static BackendInternal getBackend() { return Backend.getInstance(); } protected static VdsDao getVdsDao() { return DbFacade.getInstance().getVdsDao(); } protected static StepSubjectEntityDao getStepSubjectEntityDao() { return DbFacade.getInstance().getStepSubjectEntityDao(); } protected static StepDao getStepDao() { return DbFacade.getInstance().getStepDao(); } private static void updateStepMessage(CommandBase<?> cmd, Guid vdsForExecution) { // As an HSM job can run on any host, we want to display the host running the job when it is // chosen. To do so, we look for a corresponding command step with an "_ON_HOST" suffix that // is supposed to contain a "vds" placeholder. StepEnum stepEnum = null; try { stepEnum = StepEnum.valueOf(getStepWithHostname(cmd)); } catch (IllegalArgumentException e) { // Ignore this exception and do nothing as no corresponding step_ON_HOST found. log.debug("No StepEnum found for " + getStepWithHostname(cmd)); return; } Step step = cmd.getExecutionContext().getStep(); Map<String, String> jobProperties = cmd.getJobMessageProperties(); jobProperties.put(VdcObjectType.VDS.name().toLowerCase(), getVdsDao().get(vdsForExecution).getName()); step.setDescription(ExecutionMessageDirector.resolveStepMessage(stepEnum, jobProperties)); getStepDao().update(step); } private static String getStepWithHostname(CommandBase<?> cmd) { return cmd.getCommandStep().toString() + "_ON_HOST"; } }