package org.ovirt.engine.core.bll.pm;
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.VdsCommand;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.hostedengine.PreviousHostedEngineHost;
import org.ovirt.engine.core.bll.interfaces.BackendInternal;
import org.ovirt.engine.core.bll.validator.FenceValidator;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.FenceVdsActionParameters;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.businessentities.FencingPolicy;
import org.ovirt.engine.core.common.businessentities.VDS;
import org.ovirt.engine.core.common.businessentities.VDSStatus;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VdsDynamic;
import org.ovirt.engine.core.common.businessentities.pm.FenceActionType;
import org.ovirt.engine.core.common.businessentities.pm.FenceOperationResult;
import org.ovirt.engine.core.common.businessentities.pm.FenceOperationResult.Status;
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.locks.LockingGroup;
import org.ovirt.engine.core.common.utils.Pair;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dao.AuditLogDao;
import org.ovirt.engine.core.dao.VdsDynamicDao;
import org.ovirt.engine.core.dao.VmDao;
public abstract class FenceVdsBaseCommand<T extends FenceVdsActionParameters> extends VdsCommand<T> {
protected FenceValidator fenceValidator;
@Inject
private PreviousHostedEngineHost previousHostedEngineHost;
@Inject
private AuditLogDao auditLogDao;
@Inject
private VdsDynamicDao vdsDynamicDao;
@Inject
private VmDao vmDao;
/**
* Constructor for command creation when compensation is applied on startup
*/
protected FenceVdsBaseCommand(Guid commandId) {
super(commandId);
fenceValidator = new FenceValidator();
}
public FenceVdsBaseCommand(T parameters, CommandContext commandContext) {
super(parameters, commandContext);
fenceValidator = new FenceValidator();
}
@Override
protected boolean validate() {
List<String> messages = getReturnValue().getValidationMessages();
boolean valid =
fenceValidator.isHostExists(getVds(), messages)
&& fenceValidator.isPowerManagementEnabledAndLegal(getVds(), getCluster(), messages)
&& (previousHostedEngineHost.isPreviousHostId(getVds().getId())
|| fenceValidator.isStartupTimeoutPassed(messages))
&& isQuietTimeFromLastActionPassed()
&& fenceValidator.isProxyHostAvailable(getVds(), messages);
if (!valid) {
handleError();
}
getReturnValue().setSucceeded(valid);
return valid;
}
private boolean isQuietTimeFromLastActionPassed() {
// Check Quiet time between PM operations, this is done only if command is not internal and parent
// command is not <Restart>
int secondsLeftToNextPmOp =
(isInternalExecution() || (getParameters().getParentCommand() == VdcActionType.RestartVds))
?
0
:
auditLogDao.getTimeToWaitForNextPmOp(getVds().getName(), getRequestedAuditEvent());
if (secondsLeftToNextPmOp > 0) {
addValidationMessage(EngineMessage.VDS_FENCE_DISABLED_AT_QUIET_TIME);
addValidationMessageVariable("seconds", secondsLeftToNextPmOp);
return false;
} else {
return true;
}
}
@Override
protected void executeCommand() {
log.info("Power-Management: {} of host '{}' initiated.", getAction(), getVdsName());
audit(AuditLogType.FENCE_OPERATION_STARTED);
VDSStatus lastStatus = getVds().getStatus();
FenceOperationResult result = null;
try {
setup();
result = createHostFenceActionExecutor(
getVds(),
getParameters().getFencingPolicy()
).fence(getAction());
handleResult(result);
if (getSucceeded()) {
log.info("Power-Management: {} host '{}' succeeded.", getAction(), getVdsName());
audit(AuditLogType.FENCE_OPERATION_SUCCEEDED);
} else {
log.info("Power-Management: {} host '{}' failed.", getAction(), getVdsName());
audit(AuditLogType.FENCE_OPERATION_FAILED);
}
} finally {
if (!getSucceeded()) {
setStatus(lastStatus);
if (result != null && result.getStatus() != Status.SKIPPED_DUE_TO_POLICY) {
// show alert only if command was not skipped due to fencing policy
alertIfPowerManagementOperationFailed();
}
throw new EngineException(EngineError.VDS_FENCE_OPERATION_FAILED);
}
else {
teardown();
}
}
}
protected HostFenceActionExecutor createHostFenceActionExecutor(VDS fencedHost, FencingPolicy fencingPolicy) {
return new HostFenceActionExecutor(fencedHost, fencingPolicy);
}
private void audit(AuditLogType auditMessage) {
addCustomValue("Action", getAction().name().toLowerCase());
addCustomValue("VdsName", getVds().getName());
auditLogDirector.log(this, auditMessage);
}
private void handleResult(FenceOperationResult result) {
switch (result.getStatus()) {
case SKIPPED_DUE_TO_POLICY:
// when fencing is skipped due to policy we want to suppress command result logging, because
// we fire an alert in VdsNotRespondingTreatment
setCommandShouldBeLogged(false);
setSucceeded(false);
break;
case SUCCESS:
handleSpecificCommandActions();
setSucceeded(true);
break;
default:
setSucceeded(false);
}
setActionReturnValue(result);
}
protected void setStatus() {
setVdsStatus(VDSStatus.Reboot);
runSleepOnReboot();
}
protected void setStatus(VDSStatus status) {
// we need to load current status from db
VdsDynamic currentHost = vdsDynamicDao.get(getVds().getId());
if (currentHost != null && currentHost.getStatus() != status) {
setVdsStatus(status);
}
}
@Override
protected Map<String, Pair<String, String>> getExclusiveLocks() {
return createFenceExclusiveLocksMap(getVdsId());
}
public static Map<String, Pair<String, String>> createFenceExclusiveLocksMap(Guid vdsId) {
return Collections.singletonMap(vdsId.toString(), LockMessagesMatchUtil.makeLockingPair(
LockingGroup.VDS_FENCE,
EngineMessage.POWER_MANAGEMENT_ACTION_ON_ENTITY_ALREADY_IN_PROGRESS));
}
protected List<VM> getVmList() {
return vmDao.getAllRunningForVds(getVdsId());
}
// Method needs to be overridden to allow mocking in bll.pm subpackage
@Override
protected BackendInternal getBackend() {
return super.getBackend();
}
/**
* get the event to look for in validate() , if we requested to start Host then we should look when we stopped it
* and vice
*/
protected abstract String getRequestedAuditEvent();
protected abstract void handleError();
protected abstract void setup();
protected abstract void teardown();
protected abstract void handleSpecificCommandActions();
/**
* Get the fence action
*/
protected abstract FenceActionType getAction();
@Override
public Map<String, String> getJobMessageProperties() {
Map<String, String> map = super.getJobMessageProperties();
map.put(VdcObjectType.Cluster.name(), getClusterName());
return map;
}
}