package org.ovirt.engine.core.common.businessentities.gluster;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import javax.validation.constraints.NotNull;
import org.ovirt.engine.core.common.asynctasks.gluster.GlusterAsyncTask;
import org.ovirt.engine.core.common.businessentities.BusinessEntityWithStatus;
import org.ovirt.engine.core.common.businessentities.IVdcQueryable;
import org.ovirt.engine.core.common.businessentities.Nameable;
import org.ovirt.engine.core.common.config.Config;
import org.ovirt.engine.core.common.config.ConfigValues;
import org.ovirt.engine.core.common.validation.group.CreateEntity;
import org.ovirt.engine.core.common.validation.group.gluster.AddBrick;
import org.ovirt.engine.core.common.validation.group.gluster.RemoveBrick;
import org.ovirt.engine.core.compat.Guid;
/**
* Brick is the building block of a Gluster Volume. It represents a directory on one of the servers of the cluster, and
* is typically represented in the form <b>serverName:brickDirectory</b><br>
* It also has a status (ONLINE / OFFLINE) which represents the status of the brick process that runs on the server to
* which the brick belongs.
*
* @see GlusterVolumeEntity
* @see GlusterBrickStatus
*/
public class GlusterBrickEntity implements IVdcQueryable, BusinessEntityWithStatus<Guid, GlusterStatus>, GlusterTaskSupport, Nameable {
private static final long serialVersionUID = 7119439284741452278L;
@NotNull(message = "VALIDATION_GLUSTER_BRICK_ID_NOT_NULL", groups = { RemoveBrick.class })
private Guid id;
@NotNull(message = "VALIDATION_GLUSTER_VOLUME_ID_NOT_NULL", groups = { AddBrick.class })
private Guid volumeId;
private String volumeName;
@NotNull(message = "VALIDATION_GLUSTER_VOLUME_BRICK_SERVER_ID_NOT_NULL", groups = { CreateEntity.class })
private Guid serverId;
private Guid networkId;
private String networkAddress;
private String serverName;
@NotNull(message = "VALIDATION_GLUSTER_VOLUME_BRICK_BRICK_DIR_NOT_NULL", groups = { CreateEntity.class })
private String brickDirectory;
private GlusterStatus status;
private Integer brickOrder;
private BrickDetails brickDetails;
private GlusterAsyncTask asyncTask;
private Integer unSyncedEntries;
private List<Integer> unSyncedEntriesTrend;
private Double selfHealEta;
private Boolean isArbiter;
public GlusterBrickEntity() {
status = GlusterStatus.DOWN;
asyncTask = new GlusterAsyncTask();
unSyncedEntriesTrend = Collections.emptyList();
selfHealEta = 0D;
isArbiter = Boolean.FALSE;
}
public Guid getVolumeId() {
return volumeId;
}
public void setVolumeId(Guid volumeId) {
this.volumeId = volumeId;
}
public Guid getServerId() {
return serverId;
}
public void setServerId(Guid serverId) {
this.serverId = serverId;
}
public String getServerName() {
return serverName;
}
public void setServerName(String serverName) {
this.serverName = serverName;
}
public void setBrickDirectory(String brickDirectory) {
this.brickDirectory = brickDirectory;
}
public String getBrickDirectory() {
return brickDirectory;
}
public Guid getNetworkId() {
return networkId;
}
public void setNetworkId(Guid networkId) {
this.networkId = networkId;
}
public String getNetworkAddress() {
return networkAddress;
}
public void setNetworkAddress(String networkAddress) {
this.networkAddress = networkAddress;
}
@Override
public GlusterStatus getStatus() {
return status;
}
@Override
public void setStatus(GlusterStatus status) {
this.status = status;
}
public boolean isOnline() {
return status == GlusterStatus.UP;
}
public String getQualifiedName() {
if (networkId != null && networkAddress != null && !networkAddress.isEmpty()) {
return networkAddress + ":" + brickDirectory;
}
return serverName + ":" + brickDirectory;
}
@Override
public String toString() {
return getQualifiedName() + "(" + serverName + ")";
}
@Override
public int hashCode() {
return Objects.hash(
id,
volumeId,
serverId,
brickDirectory,
brickOrder,
status,
asyncTask,
unSyncedEntries,
unSyncedEntriesTrend,
selfHealEta,
isArbiter
);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof GlusterBrickEntity)) {
return false;
}
GlusterBrickEntity other = (GlusterBrickEntity) obj;
return Objects.equals(id, other.id)
&& Objects.equals(volumeId, other.volumeId)
&& Objects.equals(serverId, other.serverId)
&& Objects.equals(brickDirectory, other.brickDirectory)
&& Objects.equals(brickOrder, other.brickOrder)
&& Objects.equals(asyncTask, other.asyncTask)
&& Objects.equals(unSyncedEntries, other.unSyncedEntries)
&& Objects.equals(unSyncedEntriesTrend, other.unSyncedEntriesTrend)
&& Objects.equals(selfHealEta, other.selfHealEta)
&& status == other.status
&& Objects.equals(isArbiter, other.isArbiter);
}
public void copyFrom(GlusterBrickEntity brick) {
setId(brick.getId());
setVolumeId(brick.getVolumeId());
setServerId(brick.getServerId());
setServerName(brick.getServerName());
setBrickDirectory(brick.getBrickDirectory());
setBrickOrder(brick.getBrickOrder());
setUnSyncedEntries(brick.unSyncedEntries);
setUnSyncedEntriesTrend(brick.getUnSyncedEntriesTrend());
setStatus(brick.getStatus());
setIsArbiter(brick.getIsArbiter());
}
/**
* Generates the id if not present. Volume brick doesn't have an id in
* GlusterFS, and hence is generated on the backend side.
* @return id of the brick
*/
@Override
public Guid getId() {
return getId(true);
}
public Guid getId(boolean generateIfNull) {
if(id == null && generateIfNull) {
id = Guid.newGuid();
}
return id;
}
@Override
public void setId(Guid id) {
this.id = id;
}
@Override
public Object getQueryableId() {
return getId();
}
public Integer getBrickOrder() {
return brickOrder;
}
public void setBrickOrder(Integer brickOrder) {
this.brickOrder = brickOrder;
}
public BrickDetails getBrickDetails() {
return brickDetails;
}
public void setBrickDetails(BrickDetails brickDetails) {
this.brickDetails = brickDetails;
}
public BrickProperties getBrickProperties() {
if (brickDetails != null) {
return brickDetails.getBrickProperties();
}
return null;
}
@Override
public GlusterAsyncTask getAsyncTask() {
return asyncTask;
}
@Override
public void setAsyncTask(GlusterAsyncTask asyncTask) {
this.asyncTask = asyncTask;
}
public String getVolumeName() {
return volumeName;
}
public void setVolumeName(String volumeName) {
this.volumeName = volumeName;
}
@Override
public String getName() {
return getQualifiedName();
}
public List<Integer> getUnSyncedEntriesTrend() {
return unSyncedEntriesTrend;
}
public void setUnSyncedEntriesTrend(List<Integer> unSyncedEntriesTrend) {
this.unSyncedEntriesTrend = unSyncedEntriesTrend;
setSelfHealEta(calculateSelfHealEta());
}
public Integer getUnSyncedEntries() {
return unSyncedEntries;
}
public void setUnSyncedEntries(Integer unSyncedEntries) {
this.unSyncedEntries = unSyncedEntries;
}
public Double getSelfHealEta() {
return selfHealEta;
}
private void setSelfHealEta(Double value) {
this.selfHealEta = value;
}
private Double calculateSelfHealEta() {
if (this.getUnSyncedEntries() == null || this.getUnSyncedEntriesTrend() == null
|| this.getUnSyncedEntriesTrend().size() < 2) {
return 0D;
}
// Calculate Heal rate between each entries in the unsynced entries list and calculate the average heal rate.
List<Double> healRates = new ArrayList<>();
for (int index = 0; index < this.getUnSyncedEntriesTrend().size() - 1; index++) {
Integer entries = this.unSyncedEntriesTrend.get(index);
Integer entriesRemaining = this.unSyncedEntriesTrend.get(index + 1);
// -1 is added when fetching heal info fails. We can ignore them for heal rate calculation.
if (entries >= 0 && entriesRemaining >= 0) {
double healRate = ((double) entries - entriesRemaining)
/ (Config.<Integer> getValue(ConfigValues.GlusterRefreshRateHealInfo));
if (healRate > 0) {
healRates.add(healRate);
}
}
}
if (healRates.isEmpty()) {
return 0D;
}
Double healRateSum = 0D;
for (Double healRate : healRates) {
healRateSum += healRate;
}
Double healRateAvg = healRateSum / healRates.size();
return this.getUnSyncedEntries() / healRateAvg;
}
public Boolean getIsArbiter() {
return isArbiter;
}
public void setIsArbiter(Boolean isArbiter) {
this.isArbiter = isArbiter;
}
}