package org.ovirt.engine.core.common.businessentities.gluster;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import javax.validation.Valid;
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.constants.gluster.GlusterConstants;
import org.ovirt.engine.core.common.utils.ObjectUtils;
import org.ovirt.engine.core.common.validation.group.CreateEntity;
import org.ovirt.engine.core.common.validation.group.RemoveEntity;
import org.ovirt.engine.core.common.validation.group.gluster.CreateReplicatedVolume;
import org.ovirt.engine.core.common.validation.group.gluster.CreateStripedVolume;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.StringHelper;
/**
* The gluster volume entity. This is a logical partition within the virtual storage space provided by a Gluster
* Cluster. It is made up of multiple bricks (server:brickDirectory) across multiple servers of the cluster.
*
* @see GlusterVolumeType
* @see TransportType
* @see GlusterStatus
* @see GlusterBrickEntity
* @see GlusterVolumeOptionEntity
* @see AccessProtocol
*/
public class GlusterVolumeEntity implements IVdcQueryable, BusinessEntityWithStatus<Guid, GlusterStatus>, GlusterTaskSupport, Nameable {
private static final long serialVersionUID = 2355384696827317277L;
@NotNull(message = "VALIDATION_GLUSTER_VOLUME_ID_NOT_NULL", groups = { RemoveEntity.class })
private Guid id;
@NotNull(message = "VALIDATION_GLUSTER_VOLUME_CLUSTER_ID_NOT_NULL", groups = {CreateEntity.class, CreateReplicatedVolume.class, CreateStripedVolume.class})
private Guid clusterId;
private String clusterName;
@NotNull(message = "VALIDATION_GLUSTER_VOLUME_NAME_NOT_NULL", groups = {CreateEntity.class, CreateReplicatedVolume.class, CreateStripedVolume.class})
private String name;
@NotNull(message = "VALIDATION_GLUSTER_VOLUME_TYPE_NOT_NULL", groups = {CreateEntity.class, CreateReplicatedVolume.class, CreateStripedVolume.class})
private GlusterVolumeType volumeType;
@NotNull(message = "VALIDATION_GLUSTER_VOLUME_REPLICA_COUNT_NOT_NULL", groups = { CreateReplicatedVolume.class })
private Integer replicaCount;
@NotNull(message = "VALIDATION_GLUSTER_VOLUME_STRIPE_COUNT_NOT_NULL", groups = { CreateStripedVolume.class })
private Integer stripeCount;
private Integer disperseCount;
private Integer redundancyCount;
private Boolean isArbiter;
@Valid
private Map<String, GlusterVolumeOptionEntity> options;
@NotNull(message = "VALIDATION_GLUSTER_VOLUME_BRICKS_NOT_NULL", groups = {CreateEntity.class, CreateReplicatedVolume.class, CreateStripedVolume.class})
@Valid
private List<GlusterBrickEntity> bricks;
private Integer snapshotsCount;
private Integer snapMaxLimit;
private Boolean snapshotScheduled;
private GlusterStatus status;
// Gluster and NFS are enabled by default
private Set<AccessProtocol> accessProtocols;
private Set<TransportType> transportTypes;
private GlusterAsyncTask asyncTask;
private GlusterVolumeAdvancedDetails advancedDetails;
private Boolean isGeoRepMaster;
private String geoRepMasterVolAndClusterName;
public GlusterVolumeEntity() {
options = new LinkedHashMap<>();
bricks = new ArrayList<>();
status = GlusterStatus.DOWN;
accessProtocols = new LinkedHashSet<>(Arrays.asList(AccessProtocol.GLUSTER, AccessProtocol.NFS));
transportTypes = new LinkedHashSet<>();
volumeType = GlusterVolumeType.DISTRIBUTE;
asyncTask = new GlusterAsyncTask();
advancedDetails = new GlusterVolumeAdvancedDetails();
snapshotScheduled = Boolean.valueOf(false);
isArbiter = Boolean.FALSE;
}
@Override
public Guid getId() {
return id;
}
@Override
public void setId(Guid id) {
this.id = id;
}
public Guid getClusterId() {
return clusterId;
}
public void setClusterId(Guid clusterId) {
this.clusterId = clusterId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClusterName() {
return clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public GlusterVolumeType getVolumeType() {
return volumeType;
}
public void setVolumeType(GlusterVolumeType volumeType) {
this.volumeType = volumeType;
if (!volumeType.isReplicatedType()) {
setReplicaCount(0);
}
if (!volumeType.isStripedType()) {
setStripeCount(0);
}
if (!volumeType.isDispersedType()) {
setDisperseCount(0);
setRedundancyCount(0);
}
}
public void setVolumeType(String volumeType) {
setVolumeType(GlusterVolumeType.fromValue(volumeType));
}
@Override
public GlusterStatus getStatus() {
return status;
}
@Override
public void setStatus(GlusterStatus status) {
this.status = status;
}
public boolean isOnline() {
return this.status == GlusterStatus.UP;
}
public Integer getReplicaCount() {
return replicaCount;
}
public void setReplicaCount(Integer replicaCount) {
this.replicaCount = replicaCount;
}
public Integer getStripeCount() {
return stripeCount;
}
public void setStripeCount(Integer stripeCount) {
this.stripeCount = stripeCount;
}
public Integer getDisperseCount() {
return this.disperseCount;
}
public void setDisperseCount(Integer disperseCount) {
this.disperseCount = disperseCount;
}
public Integer getRedundancyCount() {
return this.redundancyCount;
}
public void setRedundancyCount(Integer redundancyCount) {
this.redundancyCount = redundancyCount;
}
public Set<AccessProtocol> getAccessProtocols() {
return accessProtocols;
}
public void setAccessProtocols(Set<AccessProtocol> accessProtocols) {
this.accessProtocols = accessProtocols;
}
public void addAccessProtocol(AccessProtocol protocol) {
accessProtocols.add(protocol);
}
public void removeAccessProtocol(AccessProtocol protocol) {
accessProtocols.remove(protocol);
}
public Set<TransportType> getTransportTypes() {
return transportTypes;
}
public void setTransportTypes(Set<TransportType> transportTypes) {
this.transportTypes = transportTypes;
}
public void addTransportType(TransportType transportType) {
transportTypes.add(transportType);
}
public void removeTransportType(TransportType transportType) {
transportTypes.remove(transportType);
}
public String getAccessControlList() {
return getOptionValue(GlusterConstants.OPTION_AUTH_ALLOW);
}
public void setAccessControlList(String accessControlList) {
if (!StringHelper.isNullOrEmpty(accessControlList)) {
setOption(GlusterConstants.OPTION_AUTH_ALLOW, accessControlList);
}
}
public Collection<GlusterVolumeOptionEntity> getOptions() {
return options.values();
}
public GlusterVolumeOptionEntity getOption(String optionKey) {
return options.get(optionKey);
}
/**
* Returns value of given option key as set on the volume. <br>
* In case the option is not set, <code>null</code> will be returned.
*/
public String getOptionValue(String optionKey) {
GlusterVolumeOptionEntity option = options.get(optionKey);
if (option == null) {
return null;
}
return option.getValue();
}
public void setOption(GlusterVolumeOptionEntity option) {
options.put(option.getKey(), option);
}
public void setOption(String key, String value) {
if (options.containsKey(key)) {
options.get(key).setValue(value);
} else {
options.put(key, new GlusterVolumeOptionEntity(id, key, value));
}
}
/**
* Sets options from a comma separated list of key value pairs separated by = <br>
* e.g. key=val1,key2=val2,...,keyn=valn
*/
public void setOptions(String options) {
this.options.clear();
if (options == null || options.trim().isEmpty()) {
return;
}
String[] optionArr = options.split(",", -1);
for (String option : optionArr) {
String[] optionInfo = option.split("=", -1);
if (optionInfo.length == 2) {
setOption(optionInfo[0], optionInfo[1]);
}
}
}
public void setOptions(Collection<GlusterVolumeOptionEntity> options) {
this.options.clear();
for (GlusterVolumeOptionEntity option : options) {
setOption(option);
}
}
public void setOptions(Map<String, String> options) {
this.options.clear();
for (Entry<String, String> entry : options.entrySet()) {
setOption(entry.getKey(), entry.getValue());
}
}
public void removeOption(String optionKey) {
options.remove(optionKey);
}
public void addBrick(GlusterBrickEntity GlusterBrick) {
bricks.add(GlusterBrick);
}
public void addBricks(Collection<GlusterBrickEntity> bricks) {
this.bricks.addAll(bricks);
}
public void setBricks(List<GlusterBrickEntity> bricks) {
this.bricks = bricks;
}
public Integer getSnapshotsCount() {
return this.snapshotsCount;
}
public void setSnapshotsCount(Integer value) {
this.snapshotsCount = value;
}
public Integer getSnapMaxLimit() {
return this.snapMaxLimit;
}
public void setSnapMaxLimit(Integer limit) {
this.snapMaxLimit = limit;
}
public Boolean getSnapshotScheduled() {
return this.snapshotScheduled;
}
public void setSnapshotScheduled(Boolean snapshotScheduled) {
this.snapshotScheduled = snapshotScheduled;
}
public Boolean getIsGeoRepMaster() {
return isGeoRepMaster;
}
public void setIsGeoRepMaster(Boolean isGeoRepMaster) {
this.isGeoRepMaster = isGeoRepMaster;
}
public Boolean getIsGeoRepSlave() {
return getGeoRepMasterVolAndClusterName() != null;
}
public String getGeoRepMasterVolAndClusterName() {
return geoRepMasterVolAndClusterName;
}
public void setGeoRepMasterVolAndClusterName(String masterVolAndClusterName) {
this.geoRepMasterVolAndClusterName = masterVolAndClusterName;
}
public void removeBrick(GlusterBrickEntity GlusterBrick) {
bricks.remove(GlusterBrick);
}
/**
* Replaces an existing brick in the volume with the given new brick. The new brick will have same index as the
* existing one.
*
* @return Index of the brick that was replaced. Returns -1 if the {@code existingBrick} is not found in the volume, leaving the volume unchanged.
*/
public int replaceBrick(GlusterBrickEntity existingBrick, GlusterBrickEntity newBrick) {
int index = bricks.indexOf(existingBrick);
if (index != -1) {
GlusterBrickEntity brick = bricks.get(index);
brick.copyFrom(newBrick);
}
return index;
}
public List<GlusterBrickEntity> getBricks() {
return bricks;
}
public void enableNFS() {
accessProtocols.add(AccessProtocol.NFS);
setOption(GlusterConstants.OPTION_NFS_DISABLE, GlusterConstants.OFF);
}
public void disableNFS() {
accessProtocols.remove(AccessProtocol.NFS);
setOption(GlusterConstants.OPTION_NFS_DISABLE, GlusterConstants.ON);
}
public boolean isNfsEnabled() {
String nfsDisabled = getOptionValue(GlusterConstants.OPTION_NFS_DISABLE);
return nfsDisabled == null || nfsDisabled.equalsIgnoreCase(GlusterConstants.OFF);
}
public void enableCifs() {
accessProtocols.add(AccessProtocol.CIFS);
setOption(GlusterConstants.OPTION_USER_CIFS, GlusterConstants.ENABLE);
}
public void disableCifs() {
accessProtocols.remove(AccessProtocol.CIFS);
setOption(GlusterConstants.OPTION_USER_CIFS, GlusterConstants.DISABLE);
}
public boolean isCifsEnabled() {
String cifsEnabled = getOptionValue(GlusterConstants.OPTION_USER_CIFS);
return cifsEnabled == null || cifsEnabled.equalsIgnoreCase(GlusterConstants.ENABLE);
}
public List<String> getBrickDirectories() {
List<String> brickDirectories = new ArrayList<>();
for (GlusterBrickEntity brick : getBricks()) {
brickDirectories.add(brick.getQualifiedName());
}
return brickDirectories;
}
@Override
public int hashCode() {
return Objects.hash(
name,
clusterId,
volumeType,
status,
replicaCount,
stripeCount,
disperseCount,
redundancyCount,
isArbiter,
options,
accessProtocols,
transportTypes,
bricks,
asyncTask,
advancedDetails,
snapshotsCount,
snapMaxLimit,
snapshotScheduled);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof GlusterVolumeEntity)) {
return false;
}
GlusterVolumeEntity other = (GlusterVolumeEntity) obj;
return Objects.equals(name, other.getName())
&& Objects.equals(clusterId, other.clusterId)
&& volumeType == other.volumeType
&& status == other.status
&& Objects.equals(replicaCount, other.replicaCount)
&& Objects.equals(stripeCount, other.stripeCount)
&& Objects.equals(disperseCount, other.disperseCount)
&& Objects.equals(redundancyCount, other.redundancyCount)
&& Objects.equals(isArbiter, other.isArbiter)
&& ObjectUtils.haveSameElements(getOptions(), other.getOptions())
&& ObjectUtils.haveSameElements(accessProtocols, other.accessProtocols)
&& ObjectUtils.haveSameElements(transportTypes, other.transportTypes)
&& ObjectUtils.haveSameElements(bricks, other.bricks)
&& Objects.equals(asyncTask, other.asyncTask)
&& Objects.equals(advancedDetails, other.advancedDetails)
&& Objects.equals(snapshotsCount, other.snapshotsCount)
&& Objects.equals(snapMaxLimit, other.snapMaxLimit)
&& Objects.equals(snapshotScheduled, other.snapshotScheduled);
}
public GlusterBrickEntity getBrickWithId(Guid brickId) {
for(GlusterBrickEntity brick : getBricks()) {
if (brick.getId().equals(brickId)) {
return brick;
}
}
return null;
}
public GlusterBrickEntity getBrickWithQualifiedName(String qualifiedName) {
for (GlusterBrickEntity brick : getBricks()) {
if (brick.getQualifiedName().equals(qualifiedName)) {
return brick;
}
}
return null;
}
@Override
public Object getQueryableId() {
return getId();
}
@Override
public GlusterAsyncTask getAsyncTask() {
return asyncTask;
}
@Override
public void setAsyncTask(GlusterAsyncTask asyncTask) {
this.asyncTask = asyncTask;
}
public GlusterVolumeAdvancedDetails getAdvancedDetails() {
return advancedDetails;
}
public void setAdvancedDetails(GlusterVolumeAdvancedDetails advancedDetails) {
this.advancedDetails = advancedDetails;
}
public Boolean getIsArbiter() {
return isArbiter;
}
public void setIsArbiter(Boolean isArbiter) {
this.isArbiter = isArbiter;
}
}