package org.ovirt.engine.core.bll.pm; import java.util.Calendar; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import javax.inject.Inject; import org.ovirt.engine.core.bll.NonTransactiveCommandAttribute; import org.ovirt.engine.core.bll.RestartVdsVmsOperation; import org.ovirt.engine.core.bll.VdsCommand; import org.ovirt.engine.core.bll.context.CommandContext; import org.ovirt.engine.core.common.AuditLogType; import org.ovirt.engine.core.common.ExternalVariable; import org.ovirt.engine.core.common.action.FenceVdsManualyParameters; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VdsActionParameters; import org.ovirt.engine.core.common.businessentities.KdumpFlowStatus; import org.ovirt.engine.core.common.businessentities.KdumpStatus; import org.ovirt.engine.core.common.businessentities.VDSStatus; import org.ovirt.engine.core.common.businessentities.VM; import org.ovirt.engine.core.common.businessentities.VdsKdumpStatus; import org.ovirt.engine.core.common.config.Config; import org.ovirt.engine.core.common.config.ConfigValues; import org.ovirt.engine.core.common.errors.EngineMessage; import org.ovirt.engine.core.common.utils.Pair; import org.ovirt.engine.core.common.vdscommands.UpdateVdsVMsClearedVDSCommandParameters; import org.ovirt.engine.core.common.vdscommands.VDSCommandType; import org.ovirt.engine.core.dao.ExternalVariableDao; import org.ovirt.engine.core.dao.VdsKdumpStatusDao; import org.ovirt.engine.core.dao.VmDao; import org.ovirt.engine.core.utils.ThreadUtils; /** * Tries to detect if host is kdumping. */ @NonTransactiveCommandAttribute public class VdsKdumpDetectionCommand<T extends VdsActionParameters> extends VdsCommand<T> { @Inject private VdsKdumpStatusDao vdsKdumpStatusDao; @Inject private VmDao vmDao; /** * Name of external variable to store fence_kdump listener heartbeat */ private static final String LISTENER_HEARTBEAT = "fence-kdump-listener-heartbeat"; /** * Listener timeout interval in milliseconds */ private final long listenerTimeoutInterval; /** * Kdump detection results */ private enum KdumpDetectionResult { LISTENER_NOT_ALIVE, KDUMP_NOT_DETECTED, KDUMP_FINISHED } /** * Kdump detection result for specified host */ private KdumpDetectionResult kdumpDetectionResult; @Inject private ExternalVariableDao externalVariableDao; public VdsKdumpDetectionCommand(T parameters, CommandContext commandContext) { super(parameters, commandContext); listenerTimeoutInterval = Config.<Integer>getValue(ConfigValues.FenceKdumpListenerTimeout) * 1000; kdumpDetectionResult = null; } private boolean isListenerAlive() { ExternalVariable fkAlive = externalVariableDao.get(LISTENER_HEARTBEAT); return fkAlive != null && fkAlive.getUpdateDate().getTime() + listenerTimeoutInterval >= System.currentTimeMillis(); } private void restartVdsVms() { List<VM> vms = vmDao.getAllRunningForVds(getVdsId()); if (!vms.isEmpty()) { RestartVdsVmsOperation restartVmsOper = new RestartVdsVmsOperation( getContext(), getVds() ); restartVmsOper.restartVms(vms); runVdsCommand(VDSCommandType.UpdateVdsVMsCleared, new UpdateVdsVMsClearedVDSCommandParameters(getVds().getId())); } } @Override protected Map<String, Pair<String, String>> getExclusiveLocks() { return FenceVdsBaseCommand.createFenceExclusiveLocksMap(getVdsId()); } private void executeFenceVdsManuallyAction() { FenceVdsManualyParameters fenceVdsManuallyParams = new FenceVdsManualyParameters(false); fenceVdsManuallyParams.setStoragePoolId(getVds().getStoragePoolId()); fenceVdsManuallyParams.setVdsId(getVdsId()); fenceVdsManuallyParams.setSessionId(getParameters().getSessionId()); fenceVdsManuallyParams.setParentCommand(VdcActionType.RestartVds); // if fencing succeeded, call to reset irs in order to try select new spm runInternalAction( VdcActionType.FenceVdsManualy, fenceVdsManuallyParams, getContext()); } private KdumpDetectionResult detectHostKdumping() { VdsKdumpStatus kdumpStatus; long messageInterval = TimeUnit.SECONDS.toMillis( Config.<Integer>getValue(ConfigValues.FenceKdumpMessageInterval)); Calendar cal = Calendar.getInstance(); cal.add(Calendar.SECOND, Config.<Integer>getValue(ConfigValues.KdumpStartedTimeout)); long timeout = cal.getTimeInMillis(); boolean kdumpDetected = false; while (true) { if (!isListenerAlive()) { // fence_kdump listener is down, continue with hard fencing return KdumpDetectionResult.LISTENER_NOT_ALIVE; } kdumpStatus = vdsKdumpStatusDao.get(getVdsId()); if (kdumpStatus == null) { // host kdump flow hasn't started yet if (timeout < System.currentTimeMillis()) { // host kdump flow not detected until timeout, continue with hard fencing return KdumpDetectionResult.KDUMP_NOT_DETECTED; } } else { if (!kdumpDetected) { // host kdump status detected kdumpDetected = true; auditLogDirector.log(this, AuditLogType.KDUMP_FLOW_DETECTED_ON_VDS); // set status to Kdumping to prevent Host Monitoring errors and wait until kdump finishes setVdsStatus(VDSStatus.Kdumping); // restart VMs running on Vds restartVdsVms(); // execute all actions needed to manual fence the host (without PM fencing) executeFenceVdsManuallyAction(); } if (kdumpStatus.getStatus() == KdumpFlowStatus.FINISHED) { // host finished its kdump flow, set status to Non Responsive setVdsStatus(VDSStatus.NonResponsive); return KdumpDetectionResult.KDUMP_FINISHED; } } ThreadUtils.sleep(messageInterval); } } @Override protected boolean validate() { if (getVds().getKdumpStatus() != KdumpStatus.ENABLED) { addValidationMessage(EngineMessage.KDUMP_DETECTION_NOT_CONFIGURED_ON_VDS); return false; } boolean detectionEnabled = getVds().isPmKdumpDetection(); if (!detectionEnabled) { addValidationMessage(EngineMessage.KDUMP_DETECTION_NOT_ENABLED_FOR_VDS); } return detectionEnabled; } /** * If the VDS is not responding, it tries to detect if VDS is kdumping or not. */ @Override protected void executeCommand() { setVds(null); if (getVds() == null) { setCommandShouldBeLogged(false); log.info("Kdump detection will not be executed on host '{}' ({}) since it doesn't exist anymore.", getVdsName(), getVdsId() ); getReturnValue().setSucceeded(false); return; } kdumpDetectionResult = detectHostKdumping(); getReturnValue().setSucceeded(kdumpDetectionResult == KdumpDetectionResult.KDUMP_FINISHED); } @Override public AuditLogType getAuditLogTypeValue() { switch (kdumpDetectionResult) { case LISTENER_NOT_ALIVE: return AuditLogType.FENCE_KDUMP_LISTENER_IS_NOT_ALIVE; case KDUMP_NOT_DETECTED: return AuditLogType.KDUMP_FLOW_NOT_DETECTED_ON_VDS; case KDUMP_FINISHED: return AuditLogType.KDUMP_FLOW_FINISHED_ON_VDS; default: return null; } } }