/*
* Copyright (c) 2008-2012 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.db.client.model;
import static com.emc.storageos.db.client.util.CommonTransformerFunctions.collectionString;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.emc.storageos.db.client.URIUtil;
import com.emc.storageos.db.client.util.StringSetUtil;
import com.emc.storageos.model.valid.EnumType;
/**
* Export Group
*/
@Cf("ExportGroup")
public class ExportGroup extends DataObject implements ProjectResource {
private NamedURI _project;
private URI _virtualArray;
private StringMap _volumes; // volume/snapshot uri -> lun mapping
// This mapping is what the user requested.
// The exportMasks will contain the mappings
// of volume to LUNs that gets pushed to
// the device.
private StringSet _snapshots; // Keep track of snapshots used in the exports
// The main reason for having this seemingly
// redundant container is that we need some way
// for the DependencyTracker to know that
// BlockSnapshots can belong to ExportGroups
private StringSet _initiators;
private StringSet _hosts;
private StringSet _clusters;
private NamedURI _tenant;
private StringSet _exportMasks; // export-mask uris for all exports that
// are associated with an ExportGroup. The
// ExportGroup is a Bourne-level
// abstraction, the ExportMask represents a
// lower-level array masking component.
private String _generatedName;
private Integer _numPaths; // number of paths for each initiator
private String _exportGroupType; // instance of #ExportGroupType
private Boolean _zoneAllInitiators = Boolean.FALSE; // if true all initiators are zoned.
private StringMap _altVirtualArrays; // alternate virtual arrays in this ExportGroup (VPlex)
// map from BlockObject id to ExportPathParam id for over-ridden path parameters
private StringMap _pathParameters;
public static final int LUN_UNASSIGNED = -1;
public static final String LUN_UNASSIGNED_STR = Integer.toHexString(LUN_UNASSIGNED);
public static final String LUN_UNASSIGNED_DECIMAL_STR = Integer.toString(LUN_UNASSIGNED);
@NamedRelationIndex(cf = "NamedRelation", type = Project.class)
@Name("project")
public NamedURI getProject() {
return _project;
}
public void setProject(NamedURI project) {
_project = project;
setChanged("project");
}
@Name("varray")
@AlternateId("AltIdIndex")
public URI getVirtualArray() {
return _virtualArray;
}
public void setVirtualArray(URI virtualArray) {
_virtualArray = virtualArray;
setChanged("varray");
}
@RelationIndex(cf = "RelationIndex", type = Volume.class)
@IndexByKey
@Name("volumes")
public StringMap getVolumes() {
return _volumes;
}
public void setVolumes(StringMap volumes) {
_volumes = volumes;
}
public void addVolume(URI volume, Integer lun) {
if (getVolumes() == null) {
setVolumes(new StringMap());
}
if (lun == null) {
lun = LUN_UNASSIGNED;
}
// Set volume's LUN only if it doesn't already exist
// or if currentLUN is unassigned
String lunString = lun.toString();
String currentLUN = getVolumes().get(volume.toString());
if (currentLUN == null || currentLUN.equals(LUN_UNASSIGNED_DECIMAL_STR)) {
getVolumes().put(volume.toString(), lunString);
}
if (URIUtil.isType(volume, BlockSnapshot.class)) {
if (_snapshots == null) {
_snapshots = new StringSet();
}
_snapshots.add(volume.toString());
}
}
public void removeVolume(URI volume) {
if (_volumes != null) {
getVolumes().remove(volume.toString());
if (URIUtil.isType(volume, BlockSnapshot.class)) {
if (_snapshots != null) {
_snapshots.remove(volume.toString());
}
}
}
}
public void removeVolumes(List<URI> volumes) {
if (_volumes != null && volumes != null) {
for (URI uri : volumes) {
_volumes.remove(uri.toString());
if (URIUtil.isType(uri, BlockSnapshot.class)) {
if (_snapshots != null) {
_snapshots.remove(uri.toString());
}
}
}
}
}
public void removeVolumes(Set<String> volumeURIStrings) {
if (_volumes != null) {
for (String uriString : volumeURIStrings) {
_volumes.remove(uriString);
if (URIUtil.isType(URI.create(uriString), BlockSnapshot.class)) {
if (_snapshots != null) {
_snapshots.remove(uriString);
}
}
}
}
}
@RelationIndex(cf = "RelationIndex", type = BlockSnapshot.class)
@IndexByKey
@Name("snapshots")
public StringSet getSnapshots() {
return _snapshots;
}
public void setSnapshots(StringSet set) {
_snapshots = set;
}
@Name("initiators")
@AlternateId("ExportGroupInitiators")
public StringSet getInitiators() {
return _initiators;
}
public void setInitiators(StringSet initiators) {
_initiators = initiators;
}
public boolean hasInitiators() {
return _initiators != null && !_initiators.isEmpty();
}
public void addInitiators(List<URI> initiators) {
if (getInitiators() == null) {
setInitiators(new StringSet());
}
getInitiators().addAll(StringSetUtil.uriListToStringSet(initiators));
}
public void addInitiators(Set<URI> initiators) {
if (getInitiators() == null) {
setInitiators(new StringSet());
}
getInitiators().addAll(StringSetUtil.uriSetToStringSet(initiators));
}
public void addHosts(List<URI> hosts) {
if (getHosts() == null) {
setHosts(new StringSet());
}
getHosts().addAll(StringSetUtil.uriListToStringSet(hosts));
}
public void addHosts(Set<URI> hosts) {
if (getHosts() == null) {
setHosts(new StringSet());
}
getHosts().addAll(StringSetUtil.uriSetToStringSet(hosts));
}
public void removeHosts(List<URI> hostsToRemove) {
StringSet hosts = getHosts();
if (hosts != null) {
hosts.removeAll(StringSetUtil.uriListToStringSet(hostsToRemove));
}
}
public void removeHosts(Set<URI> hostsToRemove) {
StringSet hosts = getHosts();
if (hosts != null) {
hosts.removeAll(StringSetUtil.uriSetToStringSet(hostsToRemove));
}
}
public void addClusters(List<URI> clusters) {
if (getClusters() == null) {
setClusters(new StringSet());
}
getClusters().addAll(StringSetUtil.uriListToStringSet(clusters));
}
public void addClusters(Set<URI> clusters) {
if (getClusters() == null) {
setClusters(new StringSet());
}
getClusters().addAll(StringSetUtil.uriSetToStringSet(clusters));
}
public void removeClusters(List<URI> clustersToRemove) {
StringSet clusters = getClusters();
if (clusters != null) {
clusters.removeAll(StringSetUtil.uriListToStringSet(clustersToRemove));
}
}
public void removeClusters(Set<URI> clustersToRemove) {
StringSet clusters = getClusters();
if (clusters != null) {
clusters.removeAll(StringSetUtil.uriSetToStringSet(clustersToRemove));
}
}
public void addVolumes(Map<URI, Integer> volumes) {
if (volumes != null) {
for (Map.Entry<URI, Integer> entry : volumes.entrySet()) {
addVolume(entry.getKey(), entry.getValue());
}
}
}
public void removeVolumes(Map<URI, Integer> volumesToRemove) {
if (volumesToRemove != null) {
for (Map.Entry<URI, Integer> entry : volumesToRemove.entrySet()) {
removeVolume(entry.getKey());
}
}
}
public void addInitiator(Initiator initiator) {
if (getInitiators() == null) {
setInitiators(new StringSet());
}
getInitiators().add(initiator.getId().toString());
}
public void removeInitiator(Initiator initiator) {
StringSet initiators = getInitiators();
if (initiators != null) {
initiators.remove(initiator.getId().toString());
}
}
public void removeInitiators(List<URI> initiatorsToRemove) {
StringSet initiators = getInitiators();
if (initiators != null) {
initiators.removeAll(StringSetUtil.uriListToStringSet(initiatorsToRemove));
}
}
public void removeInitiators(Set<URI> initiatorsToRemove) {
StringSet initiators = getInitiators();
if (initiators != null) {
initiators.removeAll(StringSetUtil.uriSetToStringSet(initiatorsToRemove));
}
}
public void removeInitiator(URI id) {
StringSet initiators = getInitiators();
if (initiators != null) {
initiators.remove(id.toString());
}
}
@Name("hosts")
@AlternateId("ExportGroupHosts")
public StringSet getHosts() {
return _hosts;
}
public void setHosts(StringSet hosts) {
_hosts = hosts;
}
public void addHost(Host host) {
if (getHosts() == null) {
setHosts(new StringSet());
}
getHosts().add(host.getId().toString());
}
public void removeHost(Host host) {
StringSet hosts = getHosts();
if (hosts != null) {
hosts.remove(host.getId().toString());
}
}
@Name("clusters")
@AlternateId("ExportGroupClusters")
public StringSet getClusters() {
return _clusters;
}
public void setClusters(StringSet clusters) {
_clusters = clusters;
}
public void addCluster(Cluster cluster) {
if (getClusters() == null) {
setClusters(new StringSet());
}
getClusters().add(cluster.getId().toString());
}
public void removeCluster(Cluster cluster) {
StringSet clusters = getClusters();
if (clusters != null) {
clusters.remove(cluster.getId().toString());
}
}
@NamedRelationIndex(cf = "NamedRelation")
@Name("tenant")
public NamedURI getTenant() {
return _tenant;
}
public void setTenant(NamedURI tenant) {
_tenant = tenant;
setChanged("tenant");
}
public boolean hasMask(URI id) {
return _exportMasks != null && _exportMasks.contains(id.toString());
}
@RelationIndex(cf = "RelationIndex", type = ExportMask.class)
@IndexByKey
@Name("exportMasks")
public StringSet getExportMasks() {
return _exportMasks;
}
public void setExportMasks(StringSet exportMasks) {
_exportMasks = exportMasks;
}
public void addExportMask(URI maskUri) {
addExportMask(maskUri.toString());
}
public void addExportMask(String maskUriStr) {
if (_exportMasks == null) {
_exportMasks = new StringSet();
}
if (!_exportMasks.contains(maskUriStr)) {
_exportMasks.add(maskUriStr);
}
}
public void removeExportMask(URI maskUri) {
removeExportMask(maskUri.toString());
}
public void removeExportMasks(List<URI> masksToRemove) {
StringSet exportMasks = getExportMasks();
if (exportMasks != null) {
exportMasks.removeAll(StringSetUtil.uriListToStringSet(masksToRemove));
}
}
public void removeExportMask(String maskUriStr) {
if (_exportMasks != null) {
_exportMasks.remove(maskUriStr);
}
}
@Name("generatedName")
public String getGeneratedName() {
return (_generatedName != null) ? _generatedName : "";
}
public void setGeneratedName(String generatedName) {
_generatedName = generatedName;
setChanged("generatedName");
}
@Name("numPaths")
public Integer getNumPaths() {
if (_numPaths == null) {
return 0;
}
return _numPaths;
}
/**
* Use this value ONLY to over-ride the automatic calculation of NumPaths
* in the BlockStorageScheduler which is based on finding the maximum num_path
* value in the VPools associated with each volume in one particular Export Mask.
* One place it is used is to over-ride is for VPLEX to back-end volumes,
* where we want the number of paths to be set up once for all volumes between
* a VPLEX and one particular back-end array.
*
* @param numPaths
*/
public void setNumPaths(Integer numPaths) {
this._numPaths = numPaths;
setChanged("numPaths");
}
@EnumType(ExportGroupType.class)
@Name("type")
public String getType() {
return _exportGroupType;
}
public void setType(String exportGroupType) {
_exportGroupType = exportGroupType;
setChanged("type");
}
@Name("zoneAllInitiators")
public Boolean getZoneAllInitiators() {
return _zoneAllInitiators;
}
/**
* Use this value ONLY when sure that a full zone map is needed.
* Normally it is not. Currently only needed by RecoverPoint.
*
* @param zoneAllInitiators
*/
public void setZoneAllInitiators(Boolean zoneAllInitiators) {
_zoneAllInitiators = zoneAllInitiators;
setChanged("zoneAllInitiators");
}
public boolean hasBlockObject(URI blockObjectId) {
if ((getSnapshots() != null && getSnapshots().contains(blockObjectId.toString()) || (getVolumes() != null && getVolumes()
.containsKey(blockObjectId.toString())))) {
return true;
}
return false;
}
/**
* The type of export is used to decide how masking views and initiator groups
* should be created for the export group and what updates are allowable. Types
* can be used as follow:
* <ol>
* <li>For creating an initiator type export group where only one host can have access to the volumes use
* {@link ExportGroupType#Initiator}. In this mode it is expected that the user wants to use specific initiators.</li>
* <li>For creating shared-access to same volumes by independent hosts use {@link ExportGroupType#Host}. All the host's initiators that
* are connected to the volumes will be used including those used for initiator type export groups.</li>
* <li>For creating shared-access to same volumes and at the same time manage the hosts as cluster where all hosts would have identical
* exports use {@link ExportGroupType#Cluster}</li>
* </ol>
*
*/
public enum ExportGroupType {
Initiator, Host, Cluster, Exclusive
}
/**
* If true, it means the ExportGroup.Type == ExportGroupType.Cluster
*
* @return
*/
public boolean forCluster() {
if (_exportGroupType != null) {
return _exportGroupType.equals(ExportGroupType.Cluster.name());
}
return false;
}
public boolean forHost() {
return _exportGroupType != null &&
_exportGroupType.equals(ExportGroupType.Host.name());
}
public boolean forInitiator() {
return _exportGroupType != null &&
_exportGroupType.equals(ExportGroupType.Initiator.name());
}
@Name("altVirtualArrays")
public StringMap getAltVirtualArrays() {
return _altVirtualArrays;
}
public void setAltVirtualArrays(StringMap altVirtualArrays) {
_altVirtualArrays = altVirtualArrays;
setChanged("altVirtualArrays");
}
public boolean hasAltVirtualArray(String storageId) {
return (_altVirtualArrays != null && _altVirtualArrays.containsKey(storageId));
}
public void putAltVirtualArray(String storageId, String altVirtualArray) {
if (_altVirtualArrays == null) {
_altVirtualArrays = new StringMap();
}
_altVirtualArrays.put(storageId, altVirtualArray);
setChanged("altVirtualArrays");
}
public void removeAltVirtualArray(String storageId) {
if (_altVirtualArrays != null && _altVirtualArrays.containsKey(storageId)) {
_altVirtualArrays.remove(storageId);
setChanged("altVirtualArrays");
}
}
public boolean hasHost(URI id) {
return _hosts != null && _hosts.contains(id.toString());
}
public boolean hasCluster(URI id) {
return _clusters != null && _clusters.contains(id.toString());
}
public boolean hasInitiator(Initiator initiator) {
return (_initiators != null && _initiators.contains(initiator.getId().toString()));
}
@Override
public String toString() {
return String.format("ExportGroup %s (%s)\n" +
"\tInactive : %s\n" +
"\tType : %s\n" +
"\tVolumes : %s\n" +
"\tClusters : %s\n" +
"\tHosts : %s\n" +
"\tInitiators : %s\n" +
"\tExportMasks : %s\n",
getLabel(), getId(),
getInactive(), getType(),
collectionString(getVolumes()),
collectionString(getClusters()),
collectionString(getHosts()),
collectionString(getInitiators()),
collectionString(getExportMasks()));
}
@Name("pathParam")
public StringMap getPathParameters() {
if (_pathParameters == null) {
return new StringMap();
}
return _pathParameters;
}
public void setPathParameters(StringMap pathParameters) {
this._pathParameters = pathParameters;
setChanged("pathParam");
}
public void addToPathParameters(URI key, URI value) {
if (_pathParameters == null) {
setPathParameters(new StringMap());
}
getPathParameters().put(key.toString(), value.toString());
}
public void removeFromPathParameters(URI key) {
getPathParameters().remove(key.toString());
}
}