package me.prettyprint.cassandra.connection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import me.prettyprint.cassandra.service.CassandraHost;
import me.prettyprint.cassandra.service.CassandraHostConfigurator;
import me.prettyprint.cassandra.service.ThriftCluster;
import me.prettyprint.hector.api.Cluster;
import me.prettyprint.hector.api.Keyspace;
import me.prettyprint.hector.api.ddl.KeyspaceDefinition;
import me.prettyprint.hector.api.factory.HFactory;
import org.apache.cassandra.thrift.EndpointDetails;
import org.apache.cassandra.thrift.TokenRange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
public class NodeDiscovery {
private static final Logger log = LoggerFactory.getLogger(NodeDiscovery.class);
private CassandraHostConfigurator cassandraHostConfigurator;
private HConnectionManager connectionManager;
private DataCenterValidator dataCenterValidator;
public NodeDiscovery(CassandraHostConfigurator cassandraHostConfigurator, HConnectionManager connectionManager) {
this.cassandraHostConfigurator = cassandraHostConfigurator;
this.connectionManager = connectionManager;
this.dataCenterValidator = new DataCenterValidator(cassandraHostConfigurator.getAutoDiscoveryDataCenters());
}
/**
* Find any nodes that are not already in the connection manager but are in
* the cassandra ring and add them
*/
public void doAddNodes() {
if (log.isDebugEnabled()) {
log.debug("Node discovery running...");
}
Set<CassandraHost> foundHosts = discoverNodes();
if (foundHosts != null && foundHosts.size() > 0) {
log.info("Found {} new host(s) in Ring", foundHosts.size());
for (CassandraHost cassandraHost : foundHosts) {
log.info("Addding found host {} to pool", cassandraHost);
cassandraHostConfigurator.applyConfig(cassandraHost);
connectionManager.addCassandraHost(cassandraHost);
}
}
if (log.isDebugEnabled()) {
log.debug("Node discovery run complete.");
}
}
/**
* Find any unknown nodes in the cassandra ring
*/
public Set<CassandraHost> discoverNodes() {
Cluster cluster = HFactory.getCluster(connectionManager.getClusterName());
if (cluster == null) {
return null;
}
Set<CassandraHost> existingHosts = connectionManager.getHosts();
Set<CassandraHost> foundHosts = new HashSet<CassandraHost>();
if (log.isDebugEnabled()) {
log.debug("using existing hosts {}", existingHosts);
}
try {
for (KeyspaceDefinition keyspaceDefinition : cluster.describeKeyspaces()) {
if (!keyspaceDefinition.getName().equals(Keyspace.KEYSPACE_SYSTEM)) {
List<TokenRange> tokenRanges = cluster.describeRing(keyspaceDefinition.getName());
for (TokenRange tokenRange : tokenRanges) {
for (EndpointDetails endPointDetail : tokenRange.getEndpoint_details()) {
// Check if we are allowed to include this Data
// Center.
if (dataCenterValidator == null
|| dataCenterValidator.validate(endPointDetail.getDatacenter())) {
// Maybe add this host if it's a new host.
CassandraHost foundHost = new CassandraHost(endPointDetail.getHost(),
cassandraHostConfigurator.getPort());
if (!existingHosts.contains(foundHost)) {
log.info("Found a node we don't know about {} for TokenRange {}", foundHost,
tokenRange);
foundHosts.add(foundHost);
}
}
}
}
break;
}
}
} catch (Exception e) {
log.error("Discovery Service failed attempt to connect CassandraHost", e);
}
return foundHosts;
}
/**
* Abstraction to validate that the discovered nodes belong to a specific
* datacenters.
*
* @author patricioe (Patricio Echague - patricio@datastax.com)
*
*/
class DataCenterValidator {
Set<String> dataCenters;
public DataCenterValidator(List<String> dataCenters) {
if (dataCenters != null)
this.dataCenters = new HashSet<String>(dataCenters);
}
public boolean validate(String dcName) {
// If the DC is not defined (i.e: single cluster) always returns
// TRUE.
if (dataCenters == null || dcName == null)
return true;
return dataCenters.contains(dcName);
}
}
}