/*
* Copyright (c) 2008-2011 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.db.client.model;
import java.net.URI;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.bind.annotation.XmlTransient;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.URIUtil;
import com.emc.storageos.db.client.constraint.AlternateIdConstraint;
import com.emc.storageos.db.client.constraint.ContainmentConstraint;
import com.emc.storageos.db.client.constraint.URIQueryResultList;
import com.emc.storageos.db.client.model.BlockSnapshot.TechnologyType;
import com.emc.storageos.db.client.model.util.TagUtils;
import com.emc.storageos.db.client.util.NullColumnValueGetter;
/**
* Block volume data object
*/
@Cf("Volume")
public class Volume extends BlockObject implements ProjectResource {
// project this volume is associated with
private NamedURI _project;
// total capacity in bytes
private Long _capacity;
// thinVolumePreAllocate size in bytes
private Long _thinVolumePreAllocationSize;
// thin or thick volume type
Boolean _thinlyProvisioned = false;
// class of service for this volume
private URI _virtualPool;
// Setting pool so that the it is available for the delete method
private URI _pool;
// Tenant who owns this volume
private NamedURI _tenant;
// Logical size of a storage volume on array which is volume.ConsumableBlocks *
// volume.BlockSize.
private Long _provisionedCapacity;
// Total amount of storage space consumed within the StoragePool which is SpaceConsumed of
// CIM_AllocatedFromStoragePool.
private Long _allocatedCapacity;
// Associated volumes. In the case that this volume is a virtual volume, this
// member captures the backend volume(s) that provide the actual storage.
private StringSet _associatedVolumes;
// In case when the volume is a meta volume, metaVolumeMembers have list of meta volume members (meta volume head is
// not included).
private StringSet _metaVolumeMembers;
// Basic volume personality type (source, target, metadata) [determined internally]
private String _personality;
// Protection URI
private NamedURI _protectionSet;
// volume-unique RP property
private String _rSetName;
// volume-unique RP property
private String _rpCopyName;
// corresponding RP journal volume
private URI rpJournalVolume;
// The secondary RP journal volume in the case of MetroPoint.
private URI secondaryRpJournalVolume;
// volume-unique RP property
private String _internalSiteName;
// Is composite volume (meta)
private Boolean _isComposite = false;
// Meta volume type. Used for create meta volume and expand regular volume as meta.
private String _compositionType;
// Size of meta meta members (the same for all members)
private Long _metaMemberSize;
// Count of meta meta members (including meta head)
private Integer _metaMemberCount;
// Target BlockMirror volume IDs that act as a mirror for this Volume
private StringSet _mirrors;
// Target RP volume IDs where this volume is a source
private StringSet _rpTargets;
// Target SRDF volume IDs that are a remote copy for this volume
private StringSet _srdfTargets;
// Parent source volume for SRDF targets
private NamedURI _srdfParent;
// SRDF RDF Group
private URI _srdfGroup;
// SRDF copy policy
private String _srdfCopyMode;
// SRDF/RP link Status
private String _linkStatus;
// volume access state from array; can be overridden by protection (RP)
private String _accessState;
// volume group that the volume belongs to
private StringSet volumeGroupIds;
// Compression ratio of the volume if it is compressed
private String _compressedRatio = null;
// VPLEX virtual volumes only: Name reference of replication group
// that this Volume's backing Volumes belong to.
private String _backingReplicationGroupInstance;
// The value alignments 0-4 correspond to SMIS values. Other storage types must map to these values.
public static enum VolumeAccessState {
UNKNOWN("0"), READABLE("1"), WRITEABLE("2"), READWRITE("3"), WRITEONCE("4"), NOT_READY("5"); // Not part of SMIS
// "Access" field,
// gathered from
// StatusDescriptions
private final String state;
VolumeAccessState(String state) {
this.state = state;
}
public String getState() {
return state;
}
private static final VolumeAccessState[] copyOfValues = values();
public static String getVolumeAccessStateDisplayName(String state) {
for (VolumeAccessState stateValue : copyOfValues) {
if (state != null && stateValue.getState().contains(state)) {
return stateValue.name();
}
}
return VolumeAccessState.UNKNOWN.name();
}
public static VolumeAccessState getVolumeAccessState(String state) {
for (VolumeAccessState stateValue : copyOfValues) {
if (state != null && stateValue.name().equalsIgnoreCase(state)) {
return stateValue;
}
}
return VolumeAccessState.UNKNOWN;
}
}
public enum LinkStatus {
FAILED_OVER("6014"), IN_SYNC("6002 6015"), SUSPENDED("6013"), CONSISTENT("6111"), SPLIT(""), SWAPPED(""), DETACHED(""), OTHER("");
private final String status;
LinkStatus(String status) {
this.status = status;
}
public String getStatus() {
return status;
}
private static final LinkStatus[] copyOfValues = values();
public static String getLinkStatusDisplayName(String status) {
for (LinkStatus statusValue : copyOfValues) {
if (statusValue.getStatus().contains(status)) {
return statusValue.name();
}
}
return LinkStatus.OTHER.name();
}
}
public static enum CompositionType {
STRIPED, CONCATENATED
}
// Total capacity in bytes of all meta members
private Long _totalMetaMemberCapacity = 0L;
// Auto Tiering policy Uri, this volume is associated with.
private URI _autoTieringPolicyUri;
public static enum PersonalityTypes {
SOURCE, // Source Volume, direct result of provision request
TARGET, // Target Volume, side-effect of getting protection
METADATA // Metadata Volume, Protection "hidden" volume not as important to end-user, like
// an RP Journal volume
}
/*
* When the volume is the target of a full copy operation, this flag is used to determine if the
* synchronization has been activated or not. Activation means that the source and target
* synchronization has been initialized (Data may not have been written to the target, however).
*/
private Boolean isSyncActive;
// Contains a list of URI's for any full copies that were created from this volume.
private StringSet fullCopies;
// When a volume is created as a full copy of another source volume, the source volume URI is
// set here.
private URI associatedSourceVolume;
// Full copy set name which user provided while creating full copies for volumes in an Application.
// There could be multiple array groups within an Application.
private String fullCopySetName;
/*
* when this is a full copy, this specifies the current relationship state with its source volume.
*/
private String replicaState;
/**
* Indicates if volume is deduplicated.
*/
private Boolean isDeduplicated = false;
@Name("isSyncActive")
public Boolean getSyncActive() {
return (isSyncActive != null) ? isSyncActive : Boolean.FALSE;
}
public void setSyncActive(Boolean syncActive) {
isSyncActive = syncActive;
setChanged("isSyncActive");
}
@Name("fullCopies")
public StringSet getFullCopies() {
return fullCopies;
}
public void setFullCopies(StringSet fullCopies) {
this.fullCopies = fullCopies;
setChanged("fullCopies");
}
@RelationIndex(cf = "associatedSourceVolumeIndex", type = Volume.class)
@Name("associatedSourceVolume")
public URI getAssociatedSourceVolume() {
return associatedSourceVolume;
}
public void setAssociatedSourceVolume(URI associatedSourceVolume) {
this.associatedSourceVolume = associatedSourceVolume;
setChanged("associatedSourceVolume");
}
@Override
@NamedRelationIndex(cf = "NamedRelation", type = Project.class)
@Name("project")
public NamedURI getProject() {
return _project;
}
public void setProject(NamedURI project) {
_project = project;
setChanged("project");
}
@NamedRelationIndex(cf = "NamedRelation", type = ProtectionSet.class)
@Name("protectionSet")
public NamedURI getProtectionSet() {
return _protectionSet;
}
public void setProtectionSet(NamedURI protectionSet) {
_protectionSet = protectionSet;
setChanged("protectionSet");
}
@Override
@XmlTransient
@NamedRelationIndex(cf = "NamedRelation")
@Name("tenant")
public NamedURI getTenant() {
return _tenant;
}
public void setTenant(NamedURI tenant) {
_tenant = tenant;
setChanged("tenant");
}
@AggregatedIndex(cf = "AggregatedIndex", classGlobal = true)
@Name("capacity")
public Long getCapacity() {
return (null == _capacity) ? 0L : _capacity;
}
public void setCapacity(Long capacity) {
if (_capacity == null || !_capacity.equals(capacity)) {
_capacity = capacity;
setChanged("capacity");
}
}
@Name("thinVolumePreAllocationSize")
public Long getThinVolumePreAllocationSize() {
return (null == _thinVolumePreAllocationSize) ? 0L : _thinVolumePreAllocationSize;
}
public void setThinVolumePreAllocationSize(Long thinVolumePreAllocationSize) {
_thinVolumePreAllocationSize = thinVolumePreAllocationSize;
setChanged("thinVolumePreAllocationSize");
}
@Name("thinlyProvisioned")
public Boolean getThinlyProvisioned() {
return _thinlyProvisioned;
}
public void setThinlyProvisioned(Boolean thinlyProvisioned) {
_thinlyProvisioned = thinlyProvisioned;
setChanged("thinlyProvisioned");
}
@RelationIndex(cf = "RelationIndex", type = VirtualPool.class)
@Name("virtualPool")
public URI getVirtualPool() {
return _virtualPool;
}
public void setVirtualPool(URI virtualPool) {
_virtualPool = virtualPool;
setChanged("virtualPool");
}
@RelationIndex(cf = "RelationIndex", type = StoragePool.class)
@Name("pool")
public URI getPool() {
return _pool;
}
public void setPool(URI pool) {
_pool = pool;
setChanged("pool");
}
@AggregatedIndex(cf = "AggregatedIndex", groupBy = "virtualPool,project", classGlobal = true)
@Name("provisionedCapacity")
public Long getProvisionedCapacity() {
return (null == _provisionedCapacity) ? 0L : _provisionedCapacity;
}
public void setProvisionedCapacity(Long provisionedCapacity) {
if (_provisionedCapacity == null || !_provisionedCapacity.equals(provisionedCapacity)) {
_provisionedCapacity = provisionedCapacity;
setChanged("provisionedCapacity");
}
}
@AggregatedIndex(cf = "AggregatedIndex", classGlobal = true)
@Name("allocatedCapacity")
public Long getAllocatedCapacity() {
return (null == _allocatedCapacity) ? 0L : _allocatedCapacity;
}
public void setAllocatedCapacity(Long allocatedCapacity) {
if (_allocatedCapacity == null || !_allocatedCapacity.equals(allocatedCapacity)) {
_allocatedCapacity = allocatedCapacity;
setChanged("allocatedCapacity");
}
}
/**
* Getter for the ids of the backend volumes that provide the actual storage for a virtual
* volume.
*
* @return The set of ids of the backend volumes that provide the actual storage for a virtual
* volume.
*/
@Name("associatedVolumes")
@AlternateId("AssocVolumes")
public StringSet getAssociatedVolumes() {
return _associatedVolumes;
}
/**
* Setter for the ids of the backend volumes that provide the actual storage for a virtual
* volume.
*
* @param volumes
* The ids of the backend volumes that provide the actual storage for a virtual
* volume.
*/
public void setAssociatedVolumes(StringSet volumes) {
_associatedVolumes = volumes;
setChanged("associatedVolumes");
}
/**
* Getter for the device ids of the meta volume members volumes.
*
* @return The set of device ids of the meta volume member volumes.
*/
@Name("metaVolumeMembers")
public StringSet getMetaVolumeMembers() {
return _metaVolumeMembers;
}
/**
* Setter for the ids of the meta volume members volumes.
*
* @param volumes
*/
public void setMetaVolumeMembers(StringSet volumes) {
_metaVolumeMembers = volumes;
setChanged("metaVolumeMembers");
}
/**
* Getter for the ids of the BlockMirror volumes that act as a mirror for this volume.
*
* @return The set of ids for the BlockMirror objects
*/
@Name("mirrors")
public StringSet getMirrors() {
return _mirrors;
}
/**
* Setter for the ids of the BlockMirror volumes that act as a mirror for this volume.
*
* @param mirrors
* The set of ids for the BlockMirror objects
*/
public void setMirrors(StringSet mirrors) {
_mirrors = mirrors;
setChanged("mirrors");
}
@Name("personality")
@AlternateId("AltIdIndex")
public String getPersonality() {
return _personality;
}
public void setPersonality(String personality) {
this._personality = personality;
setChanged("personality");
}
@Name("replication_set")
public String getRSetName() {
return _rSetName;
}
public void setRSetName(String _rSetName) {
this._rSetName = _rSetName;
setChanged("replication_set");
}
@Name("isComposite")
public Boolean getIsComposite() {
return _isComposite;
}
public void setIsComposite(Boolean isComposite) {
this._isComposite = isComposite;
setChanged("isComposite");
}
@Name("metaMemberSize")
public Long getMetaMemberSize() {
return _metaMemberSize;
}
public void setMetaMemberSize(Long metaMemberSize) {
this._metaMemberSize = metaMemberSize;
setChanged("metaMemberSize");
}
@Name("metaMemberCount")
public Integer getMetaMemberCount() {
return _metaMemberCount;
}
public void setMetaMemberCount(Integer metaMemberCount) {
this._metaMemberCount = metaMemberCount;
setChanged("metaMemberCount");
}
@Name("compositionType")
public String getCompositionType() {
return _compositionType;
}
public void setCompositionType(String compositionType) {
this._compositionType = compositionType;
setChanged("compositionType");
}
@Name("totalMetaMemberCapacity")
public Long getTotalMetaMemberCapacity() {
return _totalMetaMemberCapacity;
}
public void setTotalMetaMemberCapacity(Long totalMetaMemberCapacity) {
this._totalMetaMemberCapacity = totalMetaMemberCapacity;
setChanged("totalMetaMemberCapacity");
}
@Name("autoTieringPolicyUri")
public URI getAutoTieringPolicyUri() {
return _autoTieringPolicyUri;
}
public void setAutoTieringPolicyUri(URI autoTieringPolicyUri) {
_autoTieringPolicyUri = autoTieringPolicyUri;
setChanged("autoTieringPolicyUri");
}
@Name("internalSiteName")
public String getInternalSiteName() {
return _internalSiteName;
}
public void setInternalSiteName(String internalSiteName) {
this._internalSiteName = internalSiteName;
setChanged("internalSiteName");
}
@Name("rpTargets")
@AlternateId("RpTargetsAltIdIndex")
public StringSet getRpTargets() {
return _rpTargets;
}
public void setRpTargets(StringSet rpTargets) {
this._rpTargets = rpTargets;
setChanged("rpTargets");
}
@Name("rpCopyName")
public String getRpCopyName() {
return _rpCopyName;
}
public void setRpCopyName(String rpCopyName) {
this._rpCopyName = rpCopyName;
setChanged("rpCopyName");
}
@Name("srdfTargets")
public StringSet getSrdfTargets() {
return _srdfTargets;
}
public void setSrdfTargets(StringSet srdfTargets) {
this._srdfTargets = srdfTargets;
setChanged("srdfTargets");
}
@Name("srdfParent")
public NamedURI getSrdfParent() {
return _srdfParent;
}
public void setSrdfParent(NamedURI srdfParent) {
_srdfParent = srdfParent;
setChanged("srdfParent");
}
@Name("srdfGroup")
public URI getSrdfGroup() {
return _srdfGroup;
}
public void setSrdfGroup(URI srdfGroup) {
this._srdfGroup = srdfGroup;
setChanged("srdfGroup");
}
@Name("srdfCopyMode")
public String getSrdfCopyMode() {
return _srdfCopyMode;
}
public void setSrdfCopyMode(String srdfCopyMode) {
this._srdfCopyMode = srdfCopyMode;
setChanged("srdfCopyMode");
}
/**
* Uses a field in the volume to determine if the volume is an SRDF volume. Best to use a field
* that is set during placement/scheduling of the volume, during ViPR (cassandra) volume
* creation.
*
* @return true if the volume is used by SRDF
*/
public static boolean checkForSRDF(DbClient dbClient, URI blockURI) {
if (URIUtil.isType(blockURI, Volume.class)) {
Volume volume = dbClient.queryObject(Volume.class, blockURI);
if (volume != null) {
return volume.checkForSRDF();
}
}
return false;
}
/**
* Uses a field in the volume to determine if the volume is an SRDF volume. Best to use a field
* that is set during placement/scheduling of the volume, during ViPR (cassandra) volume
* creation.
*
* @return true if the volume is used by SRDF
*/
public boolean checkForSRDF() {
if (getSrdfTargets() != null && !getSrdfTargets().isEmpty()) {
return true;
}
// If the SRDF parent is set, this is an SRDF device
return getSrdfParent() != null;
}
/**
* Checks whether the volume is a SRDF source volume or not
*
* @return true if the volume is a SRDF source volume
*/
public boolean isSRDFSource() {
return (getSrdfTargets() != null && !getSrdfTargets().isEmpty());
}
/**
* Get all of the volumes in this SRDF set; the source and all of its targets. For a
* multi-volume SRDF, it only returns the targets (and source) associated with this one volume.
*
* @param dbClient
* db object to read from database
* @param volumeURI
* volume object
* @return list of volume URIs
*/
public static List<URI> fetchSRDFVolumes(DbClient dbClient, URI volumeURI) {
List<URI> volumeIDs = new ArrayList<URI>();
Volume volume = dbClient.queryObject(Volume.class, volumeURI);
if (volume.getSrdfTargets() != null && !volume.getSrdfTargets().isEmpty()) {
volumeIDs.add(volume.getId());
for (String volumeID : volume.getSrdfTargets()) {
volumeIDs.add(URI.create(volumeID));
}
}
// If the SRDF parent is set, this is an SRDF device
if (volume.getSrdfParent() != null) {
Volume parentVol = dbClient.queryObject(Volume.class, volume.getSrdfParent().getURI());
if (parentVol != null) {
volumeIDs.addAll(Volume.fetchSRDFVolumes(dbClient, parentVol.getId()));
}
}
return volumeIDs;
}
/**
* Uses a field in the volume to determine if the volume is an RP volume. Best to use a field
* that is set during placement/scheduling of the volume, during ViPR (cassandra) volume
* creation.
*
* @return true if the volume is used by RP
*/
public boolean checkForRp() {
return NullColumnValueGetter.isNotNullValue(getRpCopyName());
}
/**
* Utility function that retrieves the block object associated with the URI that is passed in.
* If the block object URI is volume, then that is returned. If the block object URI is an RP snapshot
* then the parent volume object of the snapshot is returned. If the block object URI is a regular snapshot,
* then the snapshot object is returned.
*
* This utility function is called from various places in the controller code when it is necessary to determine
* if the operation needs to be performed on the actual block object or its parent. In the case of RP snapshots,
* operations such as export/unexport of RP type snapshots needs to be performed on the parent of the snapshot
* rather
* than the snapshot object itself.
*
* @param dbClient
* [in] - DbClient object to read from database
* @param blockURI
* [in] - URI of BlockObject
* @return Volume associated with the blockURI for RP, otherwise the object associated with the ID
*/
public static BlockObject fetchExportMaskBlockObject(DbClient dbClient, URI blockURI) {
BlockObject bo = null;
if (URIUtil.isType(blockURI, Volume.class)) {
bo = dbClient.queryObject(Volume.class, blockURI);
} else if (URIUtil.isType(blockURI, BlockSnapshot.class)) {
BlockSnapshot snapshot = dbClient.queryObject(BlockSnapshot.class, blockURI);
if (snapshot != null && snapshot.getParent() != null &&
NullColumnValueGetter.isNotNullValue(snapshot.getTechnologyType()) &&
snapshot.getTechnologyType().equals(TechnologyType.RP.name())) {
// Process RP snapshots. At this point, if we don't find the target RP volume,
// we need to return null.
Volume parent = dbClient.queryObject(Volume.class, snapshot.getParent().getURI());
if (parent.getRpTargets() != null) {
for (String targetIdStr : parent.getRpTargets()) {
Volume targetVolume = dbClient.queryObject(Volume.class, URI.create(targetIdStr));
if (targetVolume != null && targetVolume.getVirtualArray().equals(snapshot.getVirtualArray())) {
// Always return the BlockSnapshot here. ExportMasks reference the RP BlockSnapshot, not
// Volume, when a RP BlockSnapshot export is performed.
return snapshot;
}
}
}
// The snapshot is an RP snapshot, and the RP target couldn't be found. Return null.
return null;
}
// It's a snapshot that can be exported directly.
// Local array snapshots of RP or RP+VPlex target volumes also apply to this case.
bo = snapshot;
} else if (URIUtil.isType(blockURI, BlockMirror.class)) {
BlockMirror mirror = dbClient.queryObject(BlockMirror.class, blockURI);
bo = mirror;
}
return bo;
}
@Name("rpJournalVolume")
@RelationIndex(cf = "JournalVolumeIndex", type = Volume.class)
public URI getRpJournalVolume() {
return rpJournalVolume;
}
public void setRpJournalVolume(URI rpJournalVolume) {
this.rpJournalVolume = rpJournalVolume;
setChanged("rpJournalVolume");
}
/**
* Getter for the secondary RecoverPoint journal volume. This
* will only ever be used in the case of MetroPoint.
*
* @return The secondary RP journal volume URI.
*/
@Name("secondaryRpJournalVolume")
@RelationIndex(cf = "SecondaryJournalVolumeIndex", type = Volume.class)
public URI getSecondaryRpJournalVolume() {
return secondaryRpJournalVolume;
}
/**
* Setter for the secondary RecoverPoint journal volume.
*
* @param secondaryRpJournalVolumes
* The secondary journal volume.
*/
public void setSecondaryRpJournalVolume(URI secondaryRpJournalVolume) {
this.secondaryRpJournalVolume = secondaryRpJournalVolume;
setChanged("secondaryRpJournalVolume");
}
@Name("srdfLinkStatus")
public String getLinkStatus() {
return _linkStatus;
}
public void setLinkStatus(String linkStatus) {
this._linkStatus = linkStatus;
setChanged("srdfLinkStatus");
}
@Name("accessState")
public String getAccessState() {
return _accessState;
}
public void setAccessState(String accessState) {
this._accessState = accessState;
setChanged("accessState");
}
@Name("replicaState")
public String getReplicaState() {
return replicaState;
}
public void setReplicaState(String state) {
this.replicaState = state;
setChanged("replicaState");
}
/**
* Returns true if the volume is of the personality of the passed in param.
*
* @param personality
* to check
*
* @return true if the volume is of that personality, false otherwise
*/
public boolean checkPersonality(String personality) {
if (NullColumnValueGetter.isNotNullValue(this.getPersonality()) && this.getPersonality().equalsIgnoreCase(personality)) {
return true;
}
return false;
}
/**
* Returns true if the volume is of the personality of the passed in param.
*
* @param personality to check
*
* @return true if the volume is of that personality, false otherwise
*/
public boolean checkPersonality(Volume.PersonalityTypes personality) {
return checkPersonality(personality.name());
}
@AlternateId("AltIdIndex")
@Name("fullCopySetName")
public String getFullCopySetName() {
return fullCopySetName;
}
public void setFullCopySetName(String fullCopySetName) {
this.fullCopySetName = fullCopySetName;
setChanged("fullCopySetName");
}
@Name("isDeduplicated")
public Boolean getIsDeduplicated() {
return isDeduplicated;
}
public void setIsDeduplicated(Boolean isDeduplicated) {
if (isDeduplicated == null) {
this.isDeduplicated = false;
} else {
this.isDeduplicated = isDeduplicated;
}
setChanged("isDeduplicated");
}
/**
* Returns true if the passed volume is in an export group, false otherwise.
*
* @param dbClient
* A reference to a DbClient.
*
* @return true if the passed volume is in an export group, false otherwise.
*/
public boolean isVolumeExported(DbClient dbClient) {
URIQueryResultList exportGroupURIs = new URIQueryResultList();
dbClient.queryByConstraint(ContainmentConstraint.Factory.getBlockObjectExportGroupConstraint(getId()), exportGroupURIs);
return exportGroupURIs.iterator().hasNext();
}
/**
* Returns true if the passed volume is in an export group, false otherwise.
*
* @param dbClient
* A reference to a DbClient.
* @param ignoreRPExports
* If true, ignore if this volume has been exported to RP
* @return true if the passed volume is in an export group, false otherwise.
*/
public boolean isVolumeExported(DbClient dbClient, boolean ignoreRPExports, boolean ignoreVPlexExports) {
boolean isExported = false;
URIQueryResultList exportGroupURIs = new URIQueryResultList();
dbClient.queryByConstraint(ContainmentConstraint.Factory.getBlockObjectExportGroupConstraint(getId()), exportGroupURIs);
if (ignoreRPExports) {
while (exportGroupURIs.iterator().hasNext()) {
URI exportGroupURI = exportGroupURIs.iterator().next();
if (exportGroupURI != null) {
ExportGroup exportGroup = dbClient.queryObject(ExportGroup.class, exportGroupURI);
if (!exportGroup.checkInternalFlags(Flag.RECOVERPOINT)) {
isExported = true;
break;
}
}
}
} else {
isExported = exportGroupURIs.iterator().hasNext();
}
return isExported;
}
/**
* Returns true if the passed volume is in an export group that isn't associated with RP.
*
* @param dbClient
* A reference to a DbClient
*
* @return true if the passed volume is in an export group that isn't associated with RP, false otherwise
*/
public boolean isExportedNonRP(DbClient dbClient) {
URIQueryResultList exportGroupURIs = new URIQueryResultList();
dbClient.queryByConstraint(ContainmentConstraint.Factory.getBlockObjectExportGroupConstraint(getId()), exportGroupURIs);
Iterator<URI> exportGroupURIIter = exportGroupURIs.iterator();
while (exportGroupURIIter.hasNext()) {
URI exportGroupURI = exportGroupURIIter.next();
ExportGroup exportGroup = dbClient.queryObject(ExportGroup.class, exportGroupURI);
if (!exportGroup.checkInternalFlags(Flag.RECOVERPOINT)) {
return true;
}
}
return false;
}
/**
* Return whether or not a volume in ViPR was created outside
* of ViPR and ingested virtual-volume-only (with no backend volumes).
*
* @param volume
* A reference to a volume.
*
* @return true if the volume was ingested without backend volumes, else false.
*/
public boolean isIngestedVolumeWithoutBackend(DbClient dbClient) {
URI systemURI = getStorageController();
if (systemURI != null) {
StorageSystem system = dbClient.queryObject(StorageSystem.class, systemURI);
if (system != null) {
if (system.getSystemType().equals(DiscoveredSystemObject.Type.vplex.name())) {
StringSet associatedVolumeIds = getAssociatedVolumes();
if ((associatedVolumeIds == null) || (associatedVolumeIds.isEmpty())) {
return true;
}
}
}
}
return false;
}
/**
* Utility function that tells if the passed in volume is a back-end volume of a VPLEX virtual volume.
*
* @param dbClient
* @param volume
* @return
*/
public static boolean checkForVplexBackEndVolume(DbClient dbClient, Volume volume) {
URIQueryResultList queryResults = new URIQueryResultList();
dbClient.queryByConstraint(AlternateIdConstraint.Factory
.getVolumeByAssociatedVolumesConstraint(volume.getId().toString()),
queryResults);
if (queryResults.iterator().hasNext()) {
return true;
}
return false;
}
/**
* Given a volume, this is an utility method that returns the VPLEX virtual volume that this volume is associated
* with.
*
* @param dbClient
* @param volume
* @return
*/
public static Volume fetchVplexVolume(DbClient dbClient, Volume volume) {
URI vplexVolumeURI = fetchVplexVolume(dbClient, volume.getId());
if (vplexVolumeURI != null) {
return dbClient.queryObject(Volume.class, vplexVolumeURI);
}
return null;
}
/**
* Given a volume, this is an utility method that returns the VPLEX virtual volume ID that this volume is associated
* with. It is a more efficient implementation comparing with the other fetchVplexVOlume. Sometimes we need volume URI only
*
* @param dbClient
* @param volumeUri
* @return
*/
public static URI fetchVplexVolume(DbClient dbClient, URI volumeUri) {
URIQueryResultList queryResults = new URIQueryResultList();
dbClient.queryByConstraint(AlternateIdConstraint.Factory
.getVolumeByAssociatedVolumesConstraint(volumeUri.toString()),
queryResults);
if (queryResults.iterator().hasNext()) {
while (queryResults.iterator().hasNext()) {
URI vplexVolumeURI = queryResults.iterator().next();
if (vplexVolumeURI != null) {
return vplexVolumeURI;
}
}
}
return null;
}
/**
* Check if the volume is a VPLEX volume.
*
* @param dbClient
* the db client
* @return true or false
*/
public boolean isVPlexVolume(DbClient dbClient) {
StorageSystem storage = dbClient.queryObject(StorageSystem.class, getStorageController());
return DiscoveredDataObject.Type.vplex.name().equals(storage.getSystemType());
}
/**
* Check whether the given volume is vmax3 volume
*
* @param volume
* @return {@link Boolean}
*/
public boolean isVmax3Volume(DbClient dbClient) {
StorageSystem storage = dbClient.queryObject(StorageSystem.class, this.getStorageController());
return (storage != null && storage.checkIfVmax3());
}
/**
* Check whether the given volume is SRDF volume or not
*
* @param volume
* @return boolean
*/
public static boolean isSRDFProtectedVolume(Volume volume) {
return (!NullColumnValueGetter.isNullNamedURI(volume.getSrdfParent()) || null != volume.getSrdfTargets());
}
/**
* Utility function that tells if the passed in volume is a back-end volume of a protected VPLEX virtual volume.
* For now the only supported protection type for VPLEX virtual volumes is RecoverPoint, if additional protection
* types are added in the future we can add checks for them as they are introduced.
*
* @param dbClient
* @param volume
* @return
*/
public static boolean checkForProtectedVplexBackendVolume(DbClient dbClient, Volume volume) {
if (checkForVplexBackEndVolume(dbClient, volume)) {
Volume vplexVolume = fetchVplexVolume(dbClient, volume);
if (vplexVolume != null && vplexVolume.checkForRp()) {
return true;
}
}
return false;
}
public static enum ReplicationState {
UNKNOWN(0), SYNCHRONIZED(1), CREATED(2), RESYNCED(3), INACTIVE(4), DETACHED(5), RESTORED(6);
private static final Map<String, ReplicationState> states = new HashMap<String, ReplicationState>();
static {
for (ReplicationState state : EnumSet.allOf(ReplicationState.class)) {
states.put(state.name(), state);
}
}
private final int value;
ReplicationState(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static ReplicationState getEnumValue(String state) {
return states.get(state);
}
}
/**
* Uses a field in the volume to determine if the volume is part of a CG.
*
* @return true if the volume is part of a CG
*/
public boolean isInCG() {
return !NullColumnValueGetter.isNullURI(getConsistencyGroup());
}
/**
* returns the VolumeGroup object that represents the application for this Volume
*
* @param dbClient
* dbclient for querying
* @return a VolumeGroup object or null if this volume isn't associated with an application
*/
public VolumeGroup getApplication(DbClient dbClient) {
if (this.getVolumeGroupIds() != null) {
for (String volumeGroupId : this.getVolumeGroupIds()) {
VolumeGroup volumeGroup = dbClient.queryObject(VolumeGroup.class, URI.create(volumeGroupId));
if (volumeGroup.getRoles().contains(VolumeGroup.VolumeGroupRole.COPY.toString())) {
return volumeGroup;
}
}
}
return null;
}
/**
* Getter for the ids of the volume groups
*
* @return The set of application ids
*/
@Name("volumeGroupIds")
@AlternateId("VolumeGroups")
public StringSet getVolumeGroupIds() {
if (volumeGroupIds == null) {
volumeGroupIds = new StringSet();
}
return volumeGroupIds;
}
/**
* Setter for the volume group ids
*/
public void setVolumeGroupIds(StringSet applicationIds) {
this.volumeGroupIds = applicationIds;
setChanged("applicationIds");
}
/**
* Checks if the volume is in volume group.
*
* @return true, if this volume is in volume group
*/
public boolean isInVolumeGroup() {
return !getVolumeGroupIds().isEmpty();
}
/**
* Convenience method to retrieve all of the CGs associated with the volume IDs
*
* @param dbClient
* dbClient
* @param volURIs
* volume IDs
* @return list of CG IDs
*/
public static List<URI> fetchCgIds(DbClient dbClient, List<URI> volURIs) {
List<URI> cgIds = new ArrayList<>();
if (volURIs != null) {
for (URI volumeId : volURIs) {
Volume volume = dbClient.queryObject(Volume.class, volumeId);
if (volume != null && volume.getConsistencyGroup() != null && !cgIds.contains(volume.getConsistencyGroup())) {
cgIds.add(volume.getConsistencyGroup());
}
}
}
return cgIds;
}
@Name("compressionRatio")
public String getCompressionRatio() {
return _compressedRatio;
}
public void setCompressionRatio(String compressionRatio) {
this._compressedRatio = compressionRatio;
setChanged("compressionRatio");
}
@Name("backingReplicationGroupInstance")
public String getBackingReplicationGroupInstance() {
return _backingReplicationGroupInstance;
}
public void setBackingReplicationGroupInstance(String backingReplicationGroupInstance) {
_backingReplicationGroupInstance = backingReplicationGroupInstance;
setChanged("backingReplicationGroupInstance");
}
/**
* Return the volume's boot volume tag, if available.
*
* @return usually the Host URI of the volume, but it's really a string. Let the caller cast it.
*/
public String bootVolumeTagValue() {
return TagUtils.getBlockVolumeBootVolume(this);
}
}