package org.ovirt.engine.core.bll.gluster;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
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.LockMessagesMatchUtil;
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.GlusterServiceParameters;
import org.ovirt.engine.core.common.businessentities.VDS;
import org.ovirt.engine.core.common.businessentities.VDSStatus;
import org.ovirt.engine.core.common.businessentities.gluster.GlusterServerService;
import org.ovirt.engine.core.common.businessentities.gluster.GlusterService;
import org.ovirt.engine.core.common.businessentities.gluster.GlusterServiceStatus;
import org.ovirt.engine.core.common.businessentities.gluster.PeerStatus;
import org.ovirt.engine.core.common.businessentities.gluster.ServiceType;
import org.ovirt.engine.core.common.constants.gluster.GlusterConstants;
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.common.vdscommands.SetVdsStatusVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
import org.ovirt.engine.core.common.vdscommands.VDSReturnValue;
import org.ovirt.engine.core.common.vdscommands.gluster.GlusterServiceVDSParameters;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dao.gluster.GlusterServerDao;
import org.ovirt.engine.core.dao.gluster.GlusterServerServiceDao;
import org.ovirt.engine.core.dao.gluster.GlusterServiceDao;
import org.ovirt.engine.core.utils.threadpool.ThreadPoolUtil;
@NonTransactiveCommandAttribute
public class ManageGlusterServiceCommand extends GlusterCommandBase<GlusterServiceParameters> {
private final List<String> errors = new ArrayList<>();
@Inject
private GlusterServerServiceDao glusterServerServiceDao;
@Inject
private GlusterServiceDao glusterServiceDao;
@Inject
private GlusterServerDao glusterServerDao;
private static final Map<String, ManageActionDetail> manageActionDetailsMap = new HashMap<>();
static {
manageActionDetailsMap.put(GlusterConstants.MANAGE_GLUSTER_SERVICE_ACTION_TYPE_START,
new ManageActionDetail(EngineMessage.VAR__ACTION__START,
GlusterServiceStatus.RUNNING,
AuditLogType.GLUSTER_SERVICE_STARTED,
AuditLogType.GLUSTER_SERVICE_START_FAILED));
manageActionDetailsMap.put(GlusterConstants.MANAGE_GLUSTER_SERVICE_ACTION_TYPE_STOP,
new ManageActionDetail(EngineMessage.VAR__ACTION__STOP,
GlusterServiceStatus.STOPPED,
AuditLogType.GLUSTER_SERVICE_STOPPED,
AuditLogType.GLUSTER_SERVICE_STOP_FAILED));
manageActionDetailsMap.put(GlusterConstants.MANAGE_GLUSTER_SERVICE_ACTION_TYPE_RESTART,
new ManageActionDetail(EngineMessage.VAR__ACTION__RESTART,
GlusterServiceStatus.RUNNING,
AuditLogType.GLUSTER_SERVICE_RESTARTED,
AuditLogType.GLUSTER_SERVICE_RESTART_FAILED));
}
public ManageGlusterServiceCommand(GlusterServiceParameters params, CommandContext commandContext) {
super(params, commandContext);
setVdsId(params.getServerId());
setClusterId(params.getClusterId());
}
@Override
protected LockProperties applyLockProperties(LockProperties lockProperties) {
return lockProperties.withScope(Scope.Execution).withWait(true);
}
@Override
protected void setActionMessageParameters() {
addValidationMessage(manageActionDetailsMap.get(getParameters().getActionType()).getValidateMsg());
addValidationMessage(EngineMessage.VAR__TYPE__GLUSTER_SERVICE);
}
@Override
protected boolean validate() {
if (!manageActionDetailsMap.keySet().contains(getParameters().getActionType())) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_INVALID_ACTION_TYPE);
}
if (Guid.isNullOrEmpty(getClusterId()) && Guid.isNullOrEmpty(getParameters().getServerId())) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_CLUSTERID_AND_SERVERID_BOTH_NULL);
}
/*
* only check for upserver if this is a cluster wide service change.
* On a server level change, this validation leads to chicken-egg if glusterd is not running
*/
if (!Guid.isNullOrEmpty(getClusterId()) && Guid.isNullOrEmpty(getParameters().getServerId())
&& glusterUtil.getAllUpServers(getClusterId()).size() == 0) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_NO_SERVERS_FOR_CLUSTER);
}
return true;
}
@Override
protected void executeCommand() {
if (!Guid.isNullOrEmpty(getParameters().getServerId())) {
performActionForServicesOfServer();
} else if (!Guid.isNullOrEmpty(getClusterId())) {
performActionForServicesOfCluster();
}
addCustomValue(GlusterConstants.SERVICE_TYPE, getParameters().getServiceType().name());
}
private List<String> getServiceList() {
List<GlusterService> serviceList = glusterServiceDao.getByServiceType(getParameters().getServiceType());
List<String> serviceListStr = new ArrayList<>();
for (GlusterService srvc : serviceList) {
serviceListStr.add(srvc.getServiceName());
}
return serviceListStr;
}
private List<Callable<Pair<VDS, VDSReturnValue>>> getCallableVdsCmdList() {
List<VDS> servers = glusterUtil.getAllUpServers(getClusterId());
final List<String> serviceList = getServiceList();
List<Callable<Pair<VDS, VDSReturnValue>>> commandList = new ArrayList<>();
for (final VDS upServer : servers) {
commandList.add(() -> {
VDSReturnValue returnValue =
runVdsCommand(VDSCommandType.ManageGlusterService,
new GlusterServiceVDSParameters(
upServer.getId(), serviceList, getParameters().getActionType()));
Pair<VDS, VDSReturnValue> pairRetVal = new Pair<>(upServer, returnValue);
if (returnValue.getSucceeded()) {
updateService(upServer.getId(), (List<GlusterServerService>) returnValue.getReturnValue());
} else {
errors.add(returnValue.getVdsError().getMessage());
}
return pairRetVal;
});
}
return commandList;
}
private void invokeManageGlusterService(List<Callable<Pair<VDS, VDSReturnValue>>> commandList) {
if (commandList.isEmpty()) {
return;
}
ThreadPoolUtil.invokeAll(commandList);
if (errors.size() > 0) {
setSucceeded(false);
handleVdsErrors(getAuditLogTypeValue(), errors);
addCustomValue(GlusterConstants.FAILURE_MESSAGE, StringUtils.join(errors, System.lineSeparator()));
} else {
setSucceeded(true);
}
}
private void performActionForServicesOfCluster() {
List<Callable<Pair<VDS, VDSReturnValue>>> commandList = getCallableVdsCmdList();
if (!commandList.isEmpty()) {
invokeManageGlusterService(commandList);
} else {
setSucceeded(false);
}
}
private void performActionForServicesOfServer() {
List<String> serviceList = getServiceList();
VDSReturnValue returnValue = null;
returnValue =
runVdsCommand(VDSCommandType.ManageGlusterService,
new GlusterServiceVDSParameters(
getParameters().getServerId(), serviceList, getParameters().getActionType()));
setSucceeded(returnValue.getSucceeded());
if (!getSucceeded()) {
handleVdsError(getAuditLogTypeValue(), returnValue.getVdsError().getMessage());
} else {
updateService(getParameters().getServerId(), (List<GlusterServerService>) returnValue.getReturnValue());
//if glusterd was restarted, update peer status and host status
if (getParameters().getServiceType() == ServiceType.GLUSTER
&& (GlusterConstants.MANAGE_GLUSTER_SERVICE_ACTION_TYPE_RESTART
.equals(getParameters().getActionType())
|| GlusterConstants.MANAGE_GLUSTER_SERVICE_ACTION_TYPE_START
.equals(getParameters().getActionType()))) {
glusterServerDao.updatePeerStatus(getParameters().getServerId(), PeerStatus.CONNECTED);
//only if cluster supports only gluster service
if (!getCluster().supportsVirtService()) {
runVdsCommand(VDSCommandType.SetVdsStatus, new SetVdsStatusVDSCommandParameters(getVdsId(), VDSStatus.Initializing));
}
}
}
}
private void updateService(Guid serverId, List<GlusterServerService> fetchedServerServices) {
// form the list of service ids
List<Guid> serviceIds = new ArrayList<>();
for (GlusterService srvc : glusterServiceDao.getByServiceType(getParameters().getServiceType())) {
serviceIds.add(srvc.getId());
}
for (GlusterServerService serverService : fetchedServerServices) {
if (serviceIds.contains(serverService.getServiceId())) {
serverService.setStatus(manageActionDetailsMap.get(getParameters().getActionType()).getStatus());
glusterServerServiceDao.updateByServerIdAndServiceType(serverService);
} else {
glusterServerServiceDao.save(serverService);
}
}
}
@Override
public AuditLogType getAuditLogTypeValue() {
if (getSucceeded()) {
return manageActionDetailsMap.get(getParameters().getActionType()).getActionPerformedActionLog();
} else {
return manageActionDetailsMap.get(getParameters().getActionType()).getActionFailedActionLog();
}
}
@Override
protected Map<String, Pair<String, String>> getExclusiveLocks() {
if (!Guid.isNullOrEmpty(getParameters().getServerId())) {
return Collections.singletonMap(getParameters().getServerId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.GLUSTER,
EngineMessage.ACTION_TYPE_FAILED_OBJECT_LOCKED));
} else if (!Guid.isNullOrEmpty(getParameters().getClusterId())) {
return Collections.singletonMap(getParameters().getClusterId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.GLUSTER,
EngineMessage.ACTION_TYPE_FAILED_OBJECT_LOCKED));
}
return null;
}
/**
* An instance of this class holds various details about the managed actions
*/
static class ManageActionDetail {
private EngineMessage validateMsg;
private GlusterServiceStatus status;
private AuditLogType actionPerformedActionLog;
private AuditLogType actionFailedActionLog;
public ManageActionDetail(EngineMessage validateMsg,
GlusterServiceStatus status,
AuditLogType actionPerformedActionLog,
AuditLogType actionFailedActionLog) {
this.validateMsg = validateMsg;
this.status = status;
this.actionPerformedActionLog = actionPerformedActionLog;
this.actionFailedActionLog = actionFailedActionLog;
}
public EngineMessage getValidateMsg() {
return validateMsg;
}
public void setValidateMsg(EngineMessage validateMsg) {
this.validateMsg = validateMsg;
}
public GlusterServiceStatus getStatus() {
return status;
}
public void setStatus(GlusterServiceStatus status) {
this.status = status;
}
public AuditLogType getActionPerformedActionLog() {
return actionPerformedActionLog;
}
public void setActionPerformedActionLog(AuditLogType actionPerformedActionLog) {
this.actionPerformedActionLog = actionPerformedActionLog;
}
public AuditLogType getActionFailedActionLog() {
return actionFailedActionLog;
}
public void setActionFailedActionLog(AuditLogType actionFailedActionLog) {
this.actionFailedActionLog = actionFailedActionLog;
}
}
}