/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.constraint.ContainmentConstraint;
import com.emc.storageos.db.client.constraint.URIQueryResultList;
import com.emc.storageos.db.client.model.BlockConsistencyGroup;
import com.emc.storageos.db.client.model.ExportGroup;
import com.emc.storageos.db.client.model.Host;
import com.emc.storageos.db.client.model.Initiator;
import com.emc.storageos.db.client.model.ProtectionSystem;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.db.client.util.NullColumnValueGetter;
public class ControllerLockingUtil {
private static final Logger log = LoggerFactory.getLogger(ControllerLockingUtil.class);
private static final String DELIMITER = "::";
/**
* Returns a list of lock keys for export of Hosts to StorageSystems.
* This is constructed from a list of Initiators.
* All the host URIs are collected from the Initiators.
* If an Initiator does not have a host URI, its hostName is converted to a URI and used.
* iF export type is Cluster, the clusters are looked up, and all hosts in the
* cluster are used.
* The keys are constructed from a concatenation of the host URI and the storage system URI.
*
* @param dbClient
* @param type
* ExportGroup.ExportGroupType -- used to determine if cluster export
* @param initiatorURIs
* -- set of Initiators to consider
* @param storageURI
* -- URI of storage system
* (could be a Protection System or null in which case only host in key)
* @return List<String> where each item in list is a lockKey
*/
static public List<String> getHostStorageLockKeys(DbClient dbClient, ExportGroup.ExportGroupType type,
Collection<URI> initiatorURIs, URI storageURI) {
String storageKey = getStorageKey(dbClient, storageURI);
List<String> lockKeys = new ArrayList<String>();
// Collect the needed hosts, which can be specified either by URI or string name.
Set<URI> hostURIs = new HashSet<URI>();
Set<String> hostNames = new HashSet<String>();
for (URI initiatorURI : initiatorURIs) {
Initiator initiator = dbClient.queryObject(Initiator.class, initiatorURI);
if (initiator == null || initiator.getInactive()) {
continue;
}
hostURIs.add(initiator.getHost());
hostNames.add(initiator.getHostName());
}
// If the export is type cluster, we want to add ALL the hosts in the clusters.
if (type.equals(ExportGroup.ExportGroupType.Cluster)) {
Set<URI> clusterURIs = new HashSet<URI>();
// First determine the clusters for each real host.
for (URI hostURI : hostURIs) {
Host host = dbClient.queryObject(Host.class, hostURI);
if (host == null || host.getInactive()) {
continue;
}
if (!NullColumnValueGetter.isNullURI(host.getCluster())) {
clusterURIs.add(host.getCluster());
}
}
// Now, for each cluster, look up all the hosts in the cluster and
// add those hosts if not already present.
for (URI clusterURI : clusterURIs) {
URIQueryResultList result = new URIQueryResultList();
dbClient.queryByConstraint(ContainmentConstraint.Factory.getContainedObjectsConstraint(clusterURI, Host.class, "cluster"),
result);
Iterator<URI> iter = result.iterator();
while (iter.hasNext()) {
Host host = dbClient.queryObject(Host.class, iter.next());
if (host == null || host.getInactive()) {
continue;
}
hostNames.add(host.getHostName());
}
}
}
// Now make a key for every host / storage pair
for (String hostName : hostNames) {
String key = hostName + DELIMITER + storageKey;
key = key.replaceAll("\\s", "");
if (!lockKeys.contains(key)) {
lockKeys.add(key);
}
}
log.info("Lock keys: " + lockKeys.toString());
return lockKeys;
}
/**
* This method is only invoked by RecoverPoint controller at this point as RP systems are treated as clusters for
* export,
* but they do not have a real host object associated with each initiator.
*
* Returns a list of lock keys for export of Hosts to StorageSystems.
* This is constructed from a list of Initiators.
* All the host names are collected from the Initiators.
* The keys are constructed from a concatenation of the host URI and the storage system URI.
*
* @param dbClient
* @param initiatorURIs
* -- set of Initiators to consider
* @param storageURI
* --
* (could be a Protection System or null in which case only host in key)
* @return List<String> where each item in list is a lockKey
*/
static public List<String> getStorageLockKeysForRecoverPoint(DbClient dbClient,
Collection<URI> initiatorURIs, URI storageURI) {
String storageKey = getStorageKey(dbClient, storageURI);
List<String> lockKeys = new ArrayList<String>();
// Collect the needed hosts, which can be specified either by URI or string name.
Set<String> hostNames = new HashSet<String>();
for (URI initiatorURI : initiatorURIs) {
Initiator initiator = dbClient.queryObject(Initiator.class, initiatorURI);
if (initiator == null || initiator.getInactive()) {
continue;
}
hostNames.add(initiator.getClusterName());
}
// Now make a key for every host / storage pair
for (String hostName : hostNames) {
String key = hostName + DELIMITER + storageKey;
key = key.replaceAll("\\s", ""); // remove any spaces as ZK has an issue with space in lockKeys
if (!lockKeys.contains(key)) {
lockKeys.add(key);
}
}
log.info("Lock keys: " + lockKeys.toString());
return lockKeys;
}
/**
* Make a consistencyGroup / storageSystem duple key.
*
* @param cgURI
* -- consistencyGroup may not exist but cgURI must be non NULL
* @param storageURI
* (could be a Protection System or null)
* @return
*/
static public String getConsistencyGroupStorageKey(DbClient dbClient, URI cgURI, URI storageURI) {
BlockConsistencyGroup consistencyGroup = dbClient.queryObject(BlockConsistencyGroup.class, cgURI);
String storageKey = getStorageKey(dbClient, storageURI);
if (consistencyGroup != null) {
return consistencyGroup.getLabel().replaceAll("\\s", "") + DELIMITER + storageKey;
} else {
return cgURI.toString() + DELIMITER + storageKey;
}
}
/**
* Make a replicationGroupInstance / storageSystem duple key.
*
* @param rgName
* -- replicationGroupInstance
* @param storageURI
* (could be a Protection System or null)
* @return
*/
static public String getReplicationGroupStorageKey(DbClient dbClient, String rgName, URI storageURI) {
return rgName.replaceAll("\\s", "") + DELIMITER + getStorageKey(dbClient, storageURI);
}
/**
* Returns a string identifier for the Storage or Protection System
*
* @param dbClient
* -- DbClient to access database
* @param storageURI
* -- URI, StorageSystem, ProtectionSystem, or random URI, or null
* @return
*/
static private String getStorageKey(DbClient dbClient, URI storageURI) {
if (storageURI == null) {
// Return an empty string if no storageURI supplied
return "";
}
StorageSystem storage = dbClient.queryObject(StorageSystem.class, storageURI);
if (storage != null) {
return storage.getNativeGuid();
}
ProtectionSystem protection = dbClient.queryObject(ProtectionSystem.class, storageURI);
if (protection != null) {
return protection.getNativeGuid();
}
return storageURI.toString();
}
static public String getMountHostKey(DbClient dbClient, URI hostURI) {
if (hostURI == null) {
// Return an empty string if no hostURI supplied
return "";
}
return hostURI.toString();
}
}