package org.ovirt.engine.core.bll.gluster;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.commons.lang.Validate;
import org.ovirt.engine.core.common.businessentities.Cluster;
import org.ovirt.engine.core.common.businessentities.gluster.GlusterBrickEntity;
import org.ovirt.engine.core.common.businessentities.gluster.GlusterStatus;
import org.ovirt.engine.core.common.businessentities.gluster.GlusterVolumeEntity;
import org.ovirt.engine.core.common.constants.gluster.GlusterConstants;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dao.gluster.GlusterBrickDao;
import org.ovirt.engine.core.dao.gluster.GlusterVolumeDao;
@Singleton
public class GlusterHostValidator {
private final GlusterBrickDao brickDao;
private final GlusterVolumeDao volumeDao;
@Inject
public GlusterHostValidator(GlusterVolumeDao volumeDao, GlusterBrickDao brickDao){
Validate.notNull(volumeDao, "volumeDao can not be null");
Validate.notNull(brickDao, "brickDao can not be null");
this.volumeDao = volumeDao;
this.brickDao = brickDao;
}
private boolean isQuorumMet(GlusterVolumeEntity volume,
List<GlusterBrickEntity> bricksGoingToMaintenance) {
int replicaCount = volume.getReplicaCount();
List<GlusterBrickEntity> bricks = volume.getBricks();
int subVolumes = bricks.size() / replicaCount;
String quorumType = volume.getOptionValue(GlusterConstants.OPTION_QUORUM_TYPE);
int quorumCount;
// Quorum count will be directly specified in case of quorum type 'fixed'.
// Incase of quorum type 'auto', more than 50% bricks should be UP (e.g 2 out 3 bricks should be up in case of
// replica 3 volume)
if (GlusterConstants.OPTION_QUORUM_TYPE_FIXED.equals(quorumType)) {
quorumCount = Integer.parseInt(volume.getOptionValue(GlusterConstants.OPTION_QUORUM_COUNT));
} else if (GlusterConstants.OPTION_QUORUM_TYPE_AUTO.equals(quorumType)) {
quorumCount = (int) Math.ceil((double) replicaCount / 2);
} else {
return true;
}
for (int index = 0; index < subVolumes; index++) {
List<GlusterBrickEntity> bricksInSubVolume =
bricks.subList(index * replicaCount, (index * replicaCount) + replicaCount);
int bricksGoingDown = 0;
int remainingUpBricks = 0;
for (GlusterBrickEntity brick : bricksInSubVolume) {
if (GlusterStatus.UP.equals(brick.getStatus()) && bricksGoingToMaintenance.contains(brick)) {
bricksGoingDown++;
} else if (GlusterStatus.UP.equals(brick.getStatus())) {
remainingUpBricks++;
}
}
if (bricksGoingDown > 0) {
if (remainingUpBricks < quorumCount) {
return false;
}
}
}
return true;
}
public List<String> checkGlusterQuorum(Cluster cluster, Iterable<Guid> selectedHostIdsForMaintenance) {
List<String> volumesWithoutQuorum = new ArrayList<>();
if (cluster.supportsGlusterService()) {
List<GlusterBrickEntity> bricksGoingToMaintenance = new ArrayList<>();
for (Guid serverId : selectedHostIdsForMaintenance) {
bricksGoingToMaintenance.addAll(brickDao.getGlusterVolumeBricksByServerId(serverId));
}
List<GlusterVolumeEntity> volumesInCluster = volumeDao.getByClusterId(cluster.getId());
volumesWithoutQuorum = volumesInCluster.stream()
.filter(volume -> volume.getStatus() == GlusterStatus.UP
&& volume.getVolumeType().isReplicatedType()
&& !isQuorumMet(volume, bricksGoingToMaintenance))
.map(v -> v.getName())
.collect(Collectors.toList());
}
return volumesWithoutQuorum;
}
public Map<Guid, List<String>> checkUnsyncedEntries(Iterable<Guid> hostIds) {
Map<Guid, List<String>> result = new HashMap<>();
for (Guid serverId : hostIds) {
List<GlusterBrickEntity> bricks = brickDao.getGlusterVolumeBricksByServerId(serverId);
List<String> bricksWithUnsyncedEntries = bricks.stream()
.filter(brick -> brick.getStatus() == GlusterStatus.UP && brick.getUnSyncedEntries() != null
&& brick.getUnSyncedEntries() > 0)
.map(brick -> brick.getQualifiedName())
.collect(Collectors.toList());
if (!bricksWithUnsyncedEntries.isEmpty()) {
result.put(serverId, bricksWithUnsyncedEntries);
}
}
return result;
}
}