package org.ovirt.engine.core.bll.gluster;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import javax.inject.Inject;
import org.apache.commons.lang.StringUtils;
import org.ovirt.engine.core.bll.NonTransactiveCommandAttribute;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.action.LockProperties;
import org.ovirt.engine.core.common.action.LockProperties.Scope;
import org.ovirt.engine.core.common.action.gluster.GlusterHookManageParameters;
import org.ovirt.engine.core.common.businessentities.VDS;
import org.ovirt.engine.core.common.businessentities.VDSStatus;
import org.ovirt.engine.core.common.businessentities.gluster.GlusterHookContentType;
import org.ovirt.engine.core.common.businessentities.gluster.GlusterHookStatus;
import org.ovirt.engine.core.common.businessentities.gluster.GlusterServerHook;
import org.ovirt.engine.core.common.constants.gluster.GlusterConstants;
import org.ovirt.engine.core.common.errors.EngineException;
import org.ovirt.engine.core.common.errors.EngineMessage;
import org.ovirt.engine.core.common.utils.Pair;
import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
import org.ovirt.engine.core.common.vdscommands.VDSReturnValue;
import org.ovirt.engine.core.common.vdscommands.gluster.GlusterHookVDSParameters;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dao.VdsDao;
import org.ovirt.engine.core.dao.gluster.GlusterHooksDao;
import org.ovirt.engine.core.utils.threadpool.ThreadPoolUtil;
/**
* BLL command to update gluster hook on servers where there's a content conflict
*/
@NonTransactiveCommandAttribute
public class UpdateGlusterHookCommand extends GlusterHookCommandBase<GlusterHookManageParameters> {
@Inject
private VdsDao vdsDao;
@Inject
private GlusterHooksDao glusterHooksDao;
protected List<String> errors = new ArrayList<>();
public UpdateGlusterHookCommand(GlusterHookManageParameters params, CommandContext commandContext) {
super(params, commandContext);
}
@Override
protected LockProperties applyLockProperties(LockProperties lockProperties) {
return lockProperties.withScope(Scope.Execution).withWait(true);
}
@Override
protected void setActionMessageParameters() {
addValidationMessage(EngineMessage.VAR__ACTION__UPDATE);
addValidationMessage(EngineMessage.VAR__TYPE__GLUSTER_HOOK);
}
private List<GlusterServerHook> getContentConflictServerHooks() {
//get all destination servers - only serverhooks where content is in conflict
List<GlusterServerHook> serverHooks = new ArrayList<>();
for (GlusterServerHook serverHook: getGlusterHook().getServerHooks()) {
if (!serverHook.getStatus().equals(GlusterHookStatus.MISSING) && !serverHook.getChecksum().equals(getGlusterHook().getChecksum())) {
serverHooks.add(serverHook);
}
}
return serverHooks;
}
@Override
protected boolean validate() {
if (!super.validate()) {
return false;
}
if (getContentConflictServerHooks().isEmpty()) {
addValidationMessage(EngineMessage.ACTION_TYPE_FAILED_GLUSTER_HOOK_NO_CONFLICT_SERVERS);
return false;
}
for (GlusterServerHook serverHook: getContentConflictServerHooks()) {
VDS vds = vdsDao.get(serverHook.getServerId());
if (vds == null || vds.getStatus() != VDSStatus.Up) {
String vdsName = vds != null ? vds.getName() : GlusterConstants.NO_SERVER;
setVdsName(vdsName);
addValidationMessage(EngineMessage.ACTION_TYPE_FAILED_SERVER_STATUS_NOT_UP);
addValidationMessage(String.format("$%1$s %2$s", "VdsName", vdsName));
return false;
}
}
return true;
}
@Override
protected void executeCommand() {
//check source to copy hook from - if engine copy or server copy
final boolean copyfromEngine = getParameters().getSourceServerId() == null;
entity = getGlusterHook();
addCustomValue(GlusterConstants.HOOK_NAME, entity.getName());
final String hookContent;
final String hookChecksum;
final GlusterHookContentType hookContentType;
if (copyfromEngine) {
hookContent = entity.getContent();
hookChecksum = entity.getChecksum();
hookContentType = entity.getContentType();
} else {
//use a server's copy
GlusterServerHook sourceServerHook = glusterHooksDao.getGlusterServerHook(entity.getId(), getParameters().getSourceServerId());
VDSReturnValue retValue = runVdsCommand(VDSCommandType.GetGlusterHookContent,
new GlusterHookVDSParameters(getParameters().getSourceServerId(),
entity.getGlusterCommand(),
entity.getStage(),
entity.getName()));
if (!retValue.getSucceeded()) {
// throw exception as we cannot continue without content
log.error("Failed to get content from server with id '{}': {}", getParameters().getSourceServerId(), retValue.getExceptionString());
throw new EngineException(retValue.getVdsError().getCode(), retValue.getVdsError().getMessage());
}
hookContent = (String) retValue.getReturnValue();
hookChecksum = sourceServerHook.getChecksum();
hookContentType = sourceServerHook.getContentType();
}
List<Callable<Pair<Guid, VDSReturnValue>>> taskList = new ArrayList<>();
List<Guid> serverIdsToUpdate = new ArrayList<>();
if (copyfromEngine) {
for (final GlusterServerHook serverHook : getContentConflictServerHooks()) {
serverIdsToUpdate.add(serverHook.getServerId());
}
} else {
// if copying from one of the servers, all servers other than source server
// need to be updated with hook content
for (final VDS server : glusterUtil.getAllUpServers(entity.getClusterId())) {
if (!server.getId().equals(getParameters().getSourceServerId())) {
serverIdsToUpdate.add(server.getId());
}
}
}
for (final Guid serverId : serverIdsToUpdate) {
taskList.add(() -> {
VDSReturnValue returnValue;
returnValue =
runVdsCommand(
VDSCommandType.UpdateGlusterHook,
new GlusterHookVDSParameters(serverId,
entity.getGlusterCommand(),
entity.getStage(),
entity.getName(),
hookContent,
hookChecksum));
return new Pair<>(serverId, returnValue);
});
}
setSucceeded(true);
if (!taskList.isEmpty()) {
List<Pair<Guid, VDSReturnValue>> pairResults = ThreadPoolUtil.invokeAll(taskList);
for (Pair<Guid, VDSReturnValue> pairResult : pairResults) {
VDSReturnValue retValue = pairResult.getSecond();
if (!retValue.getSucceeded() ) {
errors.add(retValue.getVdsError().getMessage());
}
}
} else {
setSucceeded(false);
}
if (errors.size() > 0) {
setSucceeded(false);
errorType = AuditLogType.GLUSTER_HOOK_UPDATE_FAILED;
handleVdsErrors(getAuditLogTypeValue(), errors);
addCustomValue(GlusterConstants.FAILURE_MESSAGE , StringUtils.join(errors, System.lineSeparator()));
}
if (getSucceeded() && !copyfromEngine) {
//update server's content copy
entity.setChecksum(hookChecksum);
entity.setContent(hookContent);
entity.setContentType(hookContentType);
}
if (getSucceeded()) {
entity.removeContentConflict();
updateGlusterHook(entity);
}
}
@Override
public Map<String, String> getJobMessageProperties() {
if (jobProperties == null) {
jobProperties = super.getJobMessageProperties();
if (getGlusterHook() != null) {
jobProperties.put(GlusterConstants.HOOK_NAME, getGlusterHook().getName());
}
}
return jobProperties;
}
@Override
public AuditLogType getAuditLogTypeValue() {
return getSucceeded() ? AuditLogType.GLUSTER_HOOK_UPDATED : errorType;
}
}