Updated Branches:
  refs/heads/trunk 493fccd18 -> 81cbdc531

Add yaml network topology snitch for mixed ec2/other envs
Patch by Eric Dong, reviewed by brandonwilliams for CASSANDRA-5339


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/81cbdc53
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/81cbdc53
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/81cbdc53

Branch: refs/heads/trunk
Commit: 81cbdc531265a6279c9584719a6d2f807bc9d2c4
Parents: 493fccd
Author: Brandon Williams <brandonwilli...@apache.org>
Authored: Tue Apr 2 14:55:47 2013 -0500
Committer: Brandon Williams <brandonwilli...@apache.org>
Committed: Tue Apr 2 14:56:32 2013 -0500

----------------------------------------------------------------------
 CHANGES.txt                                        |    1 +
 conf/cassandra-topology.yaml                       |   25 +
 .../locator/YamlFileNetworkTopologySnitch.java     |  568 +++++++++++++++
 test/conf/cassandra-topology.yaml                  |   74 ++
 .../locator/YamlFileNetworkTopologySnitchTest.java |  117 +++
 5 files changed, 785 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/81cbdc53/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 7c8d80a..060561f 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 2.0
+ * Add yaml network topology snitch for mixed ec2/other envs (CASSANDRA-5339)
  * Log when a node is down longer than the hint window (CASSANDRA-4554)
  * Optimize tombstone creation for ExpiringColumns (CASSANDRA-4917)
  * Improve LeveledScanner work estimation (CASSANDRA-5250)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/81cbdc53/conf/cassandra-topology.yaml
----------------------------------------------------------------------
diff --git a/conf/cassandra-topology.yaml b/conf/cassandra-topology.yaml
new file mode 100644
index 0000000..11c37d0
--- /dev/null
+++ b/conf/cassandra-topology.yaml
@@ -0,0 +1,25 @@
+# YAML topology configuration file for Cassandra,
+# to be used with YamlFileNetworkTopologySnitch.
+
+# The topology, as a list of data centers.
+topology:
+    # Each data center has a name and a list of racks.
+    - dc_name: DC1
+      racks:
+          # Each rack has a name and a list of nodes.
+          - rack_name: c1
+            nodes:
+                # Each node has a broadcast address (required)
+                # and a data-center-local address (optional).
+                # If dc_local_address is specified, its peers
+                # in the same data center will attempt to
+                # reconnect over that address instead.
+                - broadcast_address: 1.2.3.4
+                  dc_local_address: 5.6.7.8
+
+# Default data center name for unknown nodes; defaults to "UNKNOWN"
+# default_dc_name: UNKNOWN
+
+# Default rack name for unknown nodes
+# default_rack_name: UNKNOWN; defaults to "UNKNOWN"
+

http://git-wip-us.apache.org/repos/asf/cassandra/blob/81cbdc53/src/java/org/apache/cassandra/locator/YamlFileNetworkTopologySnitch.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/locator/YamlFileNetworkTopologySnitch.java 
b/src/java/org/apache/cassandra/locator/YamlFileNetworkTopologySnitch.java
new file mode 100644
index 0000000..e1f1a95
--- /dev/null
+++ b/src/java/org/apache/cassandra/locator/YamlFileNetworkTopologySnitch.java
@@ -0,0 +1,568 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.locator;
+
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.gms.ApplicationState;
+import org.apache.cassandra.gms.EndpointState;
+import org.apache.cassandra.gms.Gossiper;
+import org.apache.cassandra.gms.IEndpointStateChangeSubscriber;
+import org.apache.cassandra.gms.VersionedValue;
+import org.apache.cassandra.net.MessagingService;
+import org.apache.cassandra.service.StorageService;
+import org.apache.cassandra.utils.FBUtilities;
+import org.apache.cassandra.utils.ResourceWatcher;
+import org.apache.cassandra.utils.WrappedRunnable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.yaml.snakeyaml.Loader;
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+
+import com.google.common.base.Objects;
+import com.google.common.net.InetAddresses;
+
+/**
+ * Network topology snitch that reads its configuration from a YAML file.
+ * <p>
+ * This snitch supports connections over preferred addresses, such as a 
data-center-local address, based on the
+ * reconnection trick used in {@link Ec2MultiRegionSnitch}. The configuration 
file, {@code cassandra-topology.yaml}, is
+ * checked periodically for updates.
+ * </p>
+ */
+public class YamlFileNetworkTopologySnitch
+        extends AbstractNetworkTopologySnitch
+{
+
+    /** Logger. */
+    private static final Logger logger = LoggerFactory
+            .getLogger(YamlFileNetworkTopologySnitch.class);
+
+    /** Node data map, keyed by broadcast address. */
+    private volatile Map<InetAddress, NodeData> nodeDataMap;
+
+    /** Node data for this node. */
+    private volatile NodeData localNodeData;
+
+    /** Node data to fall back to when there is no match. */
+    private volatile NodeData defaultNodeData;
+
+    /** Default name for the topology configuration file. */
+    private static final String DEFAULT_TOPOLOGY_CONFIG_FILENAME = 
"cassandra-topology.yaml";
+
+    /** Name of the topology configuration file. */
+    private final String topologyConfigFilename;
+
+    /**
+     * How often to check the topology configuration file, in milliseconds; 
defaults to one minute.
+     */
+    private final int checkPeriodInMs = 60 * 1000;
+
+    /** True if the gossiper has been initialized. */
+    private volatile boolean gossiperInitialized = false;
+
+    /**
+     * Constructor.
+     * 
+     * @throws ConfigurationException
+     *             on failure
+     */
+    public YamlFileNetworkTopologySnitch() throws ConfigurationException
+    {
+        this(DEFAULT_TOPOLOGY_CONFIG_FILENAME);
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param topologyConfigFilename
+     *            name of the topology configuration file
+     * @throws ConfigurationException
+     *             on failure
+     */
+    YamlFileNetworkTopologySnitch(final String topologyConfigFilename)
+            throws ConfigurationException
+    {
+        this.topologyConfigFilename = topologyConfigFilename;
+        loadTopologyConfiguration();
+
+        try
+        {
+            /*
+             * Check if the topology configuration file is a plain file.
+             */
+            FBUtilities.resourceToFile(topologyConfigFilename);
+
+            final Runnable runnable = new WrappedRunnable()
+            {
+                /**
+                 * Loads the topology.
+                 */
+                protected void runMayThrow() throws ConfigurationException
+                {
+                    loadTopologyConfiguration();
+                }
+            };
+            ResourceWatcher.watch(topologyConfigFilename, runnable,
+                    checkPeriodInMs);
+        }
+        catch (final ConfigurationException e)
+        {
+            logger.debug(
+                    "{} found, but does not look like a plain file. Will not 
watch it for changes",
+                    topologyConfigFilename);
+        }
+    }
+
+    /**
+     * Returns the name of the rack for the endpoint, or {@code UNKNOWN} if 
not known.
+     * 
+     * @return the name of the data center for the endpoint, or {@code 
UNKNOWN} if not known
+     */
+    @Override
+    public String getRack(final InetAddress endpoint)
+    {
+        final NodeData nodeData = nodeDataMap.get(endpoint);
+        return nodeData != null ? nodeData.rack : defaultNodeData.rack;
+    }
+
+    /**
+     * Returns the name of the data center for the endpoint, or {@code 
UNKNOWN} if not known.
+     * 
+     * @return the name of the data center for the endpoint, or {@code 
UNKNOWN} if not known
+     */
+    @Override
+    public String getDatacenter(final InetAddress endpoint)
+    {
+        final NodeData nodeData = nodeDataMap.get(endpoint);
+        return nodeData != null ? nodeData.datacenter
+                : defaultNodeData.datacenter;
+    }
+
+    /**
+     * Returns the preferred non-broadcast address for the endpoint, or null 
if none was specified.
+     * <p>
+     * Currently, the only preferred address that is considered is the 
data-center-local address.
+     * </p>
+     * 
+     * @param endpoint
+     *            the broadcast address for the endpoint
+     * @return the preferred non-broadcast address for the endpoint, or null 
if none was specified
+     */
+    private InetAddress getPreferredAddress(final InetAddress endpoint)
+    {
+        return getDcLocalAddress(endpoint);
+    }
+
+    /**
+     * Returns the data-center-local address for the endpoint, or null if none 
was specified.
+     * 
+     * @param endpoint
+     *            the broadcast address for the endpoint
+     * @return the data-center-local address for the endpoint, or null if none 
was specified
+     */
+    private InetAddress getDcLocalAddress(final InetAddress endpoint)
+    {
+        final NodeData nodeData = nodeDataMap.get(endpoint);
+        return nodeData != null ? nodeData.dcLocalAddress : null;
+    }
+
+    /**
+     * Reconnects to the endpoint through the preferred non-broadcast address 
if necessary.
+     * <p>
+     * A reconnect is performed if
+     * <ul>
+     * <li>the endpoint is not in the local data center and
+     * <li>
+     * <li>the endpoint has a configured preferred address as determined by 
{@link #getPreferredAddress(InetAddress)}.
+     * </ul>
+     * </p>
+     * 
+     * @param endpoint
+     *            the endpoint's broadcast address
+     */
+    private void reconnectViaPreferredAddress(final InetAddress endpoint)
+    {
+        if (!localNodeData.datacenter.equals(getDatacenter(endpoint)))
+        {
+            return;
+        }
+
+        reconnectViaPreferredAddress(endpoint, getPreferredAddress(endpoint));
+    }
+
+    /**
+     * Reconnects to the endpoint through the preferred non-broadcast address 
if necessary.
+     * <p>
+     * A reconnect is performed to {@code preferredAddress} if the {@code 
preferredAddress} argument is not null.
+     * </p>
+     * <p>
+     * This method is only meant to be called by {@link 
#reconnectViaPreferredAddress(InetAddress)}, but is declared to
+     * have package-private scope in order to facilitate unit testing.
+     * </p>
+     * 
+     * @param endpoint
+     *            the endpoint's broadcast address
+     * @param preferredAddress
+     *            the endpoint's preferred address to reconnect to
+     */
+    void reconnectViaPreferredAddress(final InetAddress endpoint,
+            final InetAddress preferredAddress)
+    {
+        if (preferredAddress == null)
+        {
+            return;
+        }
+
+        MessagingService.instance().getConnectionPool(endpoint)
+                .reset(preferredAddress);
+
+        logger.debug(
+                "Initiated reconnect to node with broadcast address {} using 
preferred address {}",
+                endpoint, preferredAddress);
+    }
+
+    /**
+     * Root object type for the YAML topology configuration.
+     */
+    public static class TopologyConfig
+    {
+        public List<Datacenter> topology;
+        public String default_dc_name = "UNKNOWN";
+        public String default_rack_name = "UNKNOWN";
+    }
+
+    /**
+     * Data center object type for the YAML topology configuration.
+     */
+    public static class Datacenter
+    {
+        public String dc_name;
+        public List<Rack> racks = Collections.emptyList();
+    }
+
+    /**
+     * Rack object type for the YAML topology configuration.
+     */
+    public static class Rack
+    {
+        public String rack_name;
+        public List<Node> nodes = Collections.emptyList();
+    }
+
+    /**
+     * Node object type for the YAML topology configuration.
+     */
+    public static class Node
+    {
+        public String broadcast_address;
+        public String dc_local_address;
+    }
+
+    /**
+     * Loads the topology configuration file.
+     * 
+     * @throws ConfigurationException
+     *             on failure
+     */
+    private synchronized void loadTopologyConfiguration()
+            throws ConfigurationException
+    {
+        logger.debug("Loading topology configuration from {}",
+                topologyConfigFilename);
+
+        final TypeDescription topologyConfigTypeDescription = new 
TypeDescription(
+                TopologyConfig.class);
+        topologyConfigTypeDescription.putListPropertyType("topology",
+                Datacenter.class);
+
+        final TypeDescription topologyTypeDescription = new TypeDescription(
+                Datacenter.class);
+        topologyTypeDescription.putListPropertyType("racks", Rack.class);
+
+        final TypeDescription rackTypeDescription = new TypeDescription(
+                Rack.class);
+        rackTypeDescription.putListPropertyType("nodes", Node.class);
+
+        final Constructor configConstructor = new Constructor(
+                TopologyConfig.class);
+        configConstructor.addTypeDescription(topologyConfigTypeDescription);
+        configConstructor.addTypeDescription(topologyTypeDescription);
+        configConstructor.addTypeDescription(rackTypeDescription);
+
+        final InputStream configFileInputStream = getClass().getClassLoader()
+                .getResourceAsStream(topologyConfigFilename);
+        if (configFileInputStream == null)
+        {
+            throw new ConfigurationException(
+                    "Could not read topology config file "
+                            + topologyConfigFilename);
+        }
+        final Yaml yaml = new Yaml(new Loader(configConstructor));
+        final TopologyConfig topologyConfig = (TopologyConfig) yaml
+                .load(configFileInputStream);
+
+        final Map<InetAddress, NodeData> nodeDataMap = new 
HashMap<InetAddress, NodeData>();
+
+        if (topologyConfig.topology == null)
+        {
+            throw new ConfigurationException(
+                    "Topology configuration file is missing the topology 
section");
+        }
+
+        for (final Datacenter datacenter : topologyConfig.topology)
+        {
+            if (datacenter.dc_name == null)
+            {
+                throw new ConfigurationException(
+                        "Topology configuration file is missing a data center 
name for some data center");
+            }
+
+            for (final Rack rack : datacenter.racks)
+            {
+                if (rack.rack_name == null)
+                {
+                    throw new ConfigurationException(
+                            String.format(
+                                    "Topology configuration file is missing a 
rack name for some rack under data center '%s'",
+                                    datacenter.dc_name));
+                }
+
+                for (final Node node : rack.nodes)
+                {
+                    if (rack.rack_name == null)
+                    {
+                        throw new ConfigurationException(
+                                String.format(
+                                        "Topology configuration file is 
missing a broadcast address for some node under data center '%s' rack '%s'",
+                                        datacenter.dc_name, rack.rack_name));
+                    }
+
+                    final InetAddress endpoint = InetAddresses
+                            .forString(node.broadcast_address);
+                    final InetAddress dcLocalAddress = node.dc_local_address 
== null ? null
+                            : InetAddresses.forString(node.dc_local_address);
+
+                    final NodeData nodeData = new NodeData();
+                    nodeData.datacenter = datacenter.dc_name;
+                    nodeData.rack = rack.rack_name;
+                    nodeData.dcLocalAddress = dcLocalAddress;
+
+                    if (nodeDataMap.put(endpoint, nodeData) != null)
+                    {
+                        throw new ConfigurationException(
+                                String.format(
+                                        "IP address '%s' appears more than 
once in the topology configuration file",
+                                        datacenter.dc_name, rack.rack_name));
+                    }
+
+                    if (dcLocalAddress != null
+                            && nodeDataMap.put(dcLocalAddress, nodeData) != 
null)
+                    {
+                        throw new ConfigurationException(
+                                String.format(
+                                        "IP address '%s' appears more than 
once in the topology configuration file",
+                                        datacenter.dc_name, rack.rack_name));
+                    }
+                }
+            }
+        }
+
+        final NodeData localNodeData = nodeDataMap.get(FBUtilities
+                .getBroadcastAddress());
+        if (localNodeData == null)
+        {
+            throw new ConfigurationException(
+                    "Topology configuration missing information for the local 
node");
+        }
+
+        final NodeData defaultNodeData = new NodeData();
+
+        if (topologyConfig.default_dc_name == null)
+        {
+            throw new ConfigurationException(
+                    "default_dc_name must be specified");
+        }
+        if (topologyConfig.default_rack_name == null)
+        {
+            throw new ConfigurationException(
+                    "default_rack_name must be specified");
+        }
+
+        defaultNodeData.datacenter = topologyConfig.default_dc_name;
+        defaultNodeData.rack = topologyConfig.default_rack_name;
+
+        // YAML configuration looks good; now make the changes
+
+        this.nodeDataMap = nodeDataMap;
+        this.localNodeData = localNodeData;
+        this.defaultNodeData = defaultNodeData;
+
+        if (logger.isDebugEnabled())
+        {
+            logger.debug(
+                    "Built topology map from config file: localNodeData={}, 
nodeDataMap={}",
+                    localNodeData, nodeDataMap);
+        }
+
+        if (gossiperInitialized)
+        {
+            StorageService.instance.gossipSnitchInfo();
+        }
+    }
+
+    /**
+     * Topology data for a node.
+     */
+    private class NodeData
+    {
+        /** Data center name. */
+        public String datacenter;
+        /** Rack name. */
+        public String rack;
+        /** Data-center-local address. */
+        public InetAddress dcLocalAddress;
+
+        /**
+         * Returns a simple key-value string representation of this node's 
data.
+         * 
+         * @return a simple key-value string representation of this node's data
+         */
+        public String toString()
+        {
+            return Objects.toStringHelper(this).add("datacenter", datacenter)
+                    .add("rack", rack).add("dcLocalAddress", dcLocalAddress)
+                    .toString();
+        }
+    }
+
+    /**
+     * Called in preparation for the initiation of the gossip loop.
+     */
+    @Override
+    public synchronized void gossiperStarting()
+    {
+        gossiperInitialized = true;
+        StorageService.instance.gossipSnitchInfo();
+
+        final IEndpointStateChangeSubscriber escs = new 
IEndpointStateChangeSubscriber()
+        {
+
+            /**
+             * Called upon a "restart" gossip event; does nothing.
+             * 
+             * @param endpoint
+             *            the endpoint's broadcast address
+             * @param state
+             *            the endpoint's state
+             */
+            @Override
+            public void onRestart(final InetAddress endpoint,
+                    final EndpointState state)
+            {
+                // No-op
+            }
+
+            /**
+             * Called upon a "remove" gossip event; does nothing.
+             * 
+             * @param endpoint
+             *            the endpoint's broadcast address
+             */
+            @Override
+            public void onRemove(final InetAddress endpoint)
+            {
+                // No-op
+            }
+
+            /**
+             * Called upon a "join" gossip event; attempts a reconnect to a 
preferred non-broadcast address if
+             * necessary.
+             * 
+             * @param endpoint
+             *            the endpoint's broadcast address
+             * @param epState
+             *            the endpoint's state
+             */
+            @Override
+            public void onJoin(final InetAddress endpoint,
+                    final EndpointState epState)
+            {
+                reconnectViaPreferredAddress(endpoint);
+            }
+
+            /**
+             * Called upon a "dead" gossip event; does nothing.
+             * 
+             * @param endpoint
+             *            the endpoint's broadcast address
+             * @param state
+             *            the endpoint's state
+             */
+            @Override
+            public void onDead(final InetAddress endpoint,
+                    final EndpointState state)
+            {
+                // No-op
+            }
+
+            /**
+             * Called upon a "change" gossip event; does nothing.
+             * 
+             * @param endpoint
+             *            the endpoint's broadcast address
+             * @param state
+             *            the application state that has changed
+             * @param value
+             *            the new value of the state
+             */
+            @Override
+            public void onChange(final InetAddress endpoint,
+                    final ApplicationState state, final VersionedValue value)
+            {
+                // No-op
+            }
+
+            /**
+             * Called upon an "alive" gossip event; attempts a reconnect to a 
preferred non-broadcast address if
+             * necessary.
+             * 
+             * @param endpoint
+             *            the endpoint's broadcast address
+             * @param state
+             *            the endpoint's state
+             */
+            @Override
+            public void onAlive(final InetAddress endpoint,
+                    final EndpointState state)
+            {
+                reconnectViaPreferredAddress(endpoint);
+            }
+        };
+        Gossiper.instance.register(escs);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/81cbdc53/test/conf/cassandra-topology.yaml
----------------------------------------------------------------------
diff --git a/test/conf/cassandra-topology.yaml 
b/test/conf/cassandra-topology.yaml
new file mode 100644
index 0000000..51542da
--- /dev/null
+++ b/test/conf/cassandra-topology.yaml
@@ -0,0 +1,74 @@
+# Test YAML topology configuration file for Cassandra,
+# to be used with YamlFileNetworkTopologySnitch.
+
+# The topology, as a list of data centers.
+topology:
+    # Each data center has a name and a list of racks.
+    - dc_name: DC1
+      racks:
+          # Each rack has a name and a list of nodes.
+          - rack_name: RAC1
+            nodes:
+                # Each node has a broadcast address (required)
+                # and a data-center-local address (optional).
+                # If dc_local_address is specified, its peers
+                # in the same data center will attempt to
+                # reconnect over that address instead.
+                - broadcast_address: 127.0.0.1
+                  dc_local_address: 9.0.0.1
+                - broadcast_address: 192.168.1.100
+                  dc_local_address: 9.0.0.2
+                - broadcast_address: 10.0.0.10
+                  dc_local_address: 9.0.0.3
+                - broadcast_address: 10.0.0.11
+                  dc_local_address: 9.0.0.4
+          - rack_name: RAC2
+            nodes:
+                - broadcast_address: 192.168.2.200
+                - broadcast_address: 10.0.0.12
+                - broadcast_address: 10.0.0.13
+                - broadcast_address: 127.0.0.2
+          - rack_name: RAC3
+            nodes:
+                - broadcast_address: 127.0.0.3
+    - dc_name: DC2
+      racks:
+          - rack_name: RAC1
+            nodes:
+                - broadcast_address: 10.20.114.10
+                - broadcast_address: 10.20.114.11
+          - rack_name: RAC2
+            nodes:
+                - broadcast_address: 10.20.114.15
+          - rack_name: RAC4
+            nodes:
+                - broadcast_address: 127.0.0.4
+          - rack_name: RAC5
+            nodes:
+                - broadcast_address: 127.0.0.5
+    - dc_name: DC3
+      racks:
+          - rack_name: RAC1
+            nodes:
+                - broadcast_address: 10.21.119.13
+                - broadcast_address: 10.21.119.10
+          - rack_name: RAC2
+            nodes:
+                - broadcast_address: 10.21.119.14
+          - rack_name: RAC6
+            nodes:
+                - broadcast_address: 127.0.0.6
+          - rack_name: RAC7
+            nodes:
+                - broadcast_address: 127.0.0.7
+          - rack_name: RAC8
+            nodes:
+                - broadcast_address: 127.0.0.8
+
+
+# Default data center name for unknown nodes
+default_dc_name: DC1
+
+# Default rack name for unknown nodes
+default_rack_name: r1
+

http://git-wip-us.apache.org/repos/asf/cassandra/blob/81cbdc53/test/unit/org/apache/cassandra/locator/YamlFileNetworkTopologySnitchTest.java
----------------------------------------------------------------------
diff --git 
a/test/unit/org/apache/cassandra/locator/YamlFileNetworkTopologySnitchTest.java 
b/test/unit/org/apache/cassandra/locator/YamlFileNetworkTopologySnitchTest.java
new file mode 100644
index 0000000..d5d0c9f
--- /dev/null
+++ 
b/test/unit/org/apache/cassandra/locator/YamlFileNetworkTopologySnitchTest.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.locator;
+
+import java.net.InetAddress;
+
+import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.utils.FBUtilities;
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.google.common.net.InetAddresses;
+
+/**
+ * Unit tests for {@link YamlFileNetworkTopologySnitch}.
+ */
+public class YamlFileNetworkTopologySnitchTest
+{
+
+    /**
+     * Testing variant of {@link YamlFileNetworkTopologySnitch}.
+     * 
+     */
+    private class TestYamlFileNetworkTopologySnitch
+            extends YamlFileNetworkTopologySnitch
+    {
+
+        /**
+         * Constructor.
+         * 
+         * @throws ConfigurationException
+         *             on configuration error
+         */
+        public TestYamlFileNetworkTopologySnitch(
+                final String topologyConfigFilename)
+                throws ConfigurationException
+        {
+            super(topologyConfigFilename);
+        }
+
+        /**
+         * Overrides {@link 
YamlFileNetworkTopologySnitch#reconnectViaPreferredAddress(InetAddress, 
InetAddress)} to not
+         * perform a reconnect.
+         * 
+         * @param endpoint
+         *            the endpoint's broadcast address
+         * @param preferredAddress
+         *            the endpoint's preferred address to reconnect to
+         */
+        @Override
+        void reconnectViaPreferredAddress(final InetAddress endpoint,
+                final InetAddress preferredAddress)
+        {
+            // No-op
+        }
+
+    }
+
+    /**
+     * A basic test case.
+     * 
+     * @throws Exception
+     *             on failure
+     */
+    @Test
+    public void testBasic() throws Exception
+    {
+        final TestYamlFileNetworkTopologySnitch snitch = new 
TestYamlFileNetworkTopologySnitch(
+                "cassandra-topology.yaml");
+        checkEndpoint(snitch, FBUtilities.getBroadcastAddress()
+                .getHostAddress(), "DC1", "RAC1");
+        checkEndpoint(snitch, "192.168.1.100", "DC1", "RAC1");
+        checkEndpoint(snitch, "10.0.0.12", "DC1", "RAC2");
+        checkEndpoint(snitch, "127.0.0.3", "DC1", "RAC3");
+        checkEndpoint(snitch, "10.20.114.10", "DC2", "RAC1");
+        checkEndpoint(snitch, "127.0.0.8", "DC3", "RAC8");
+        checkEndpoint(snitch, "6.6.6.6", "DC1", "r1");
+
+    }
+
+    /**
+     * Asserts that a snitch's determination of data center and rack for an 
endpoint match what we expect.
+     * 
+     * @param snitch
+     *            snitch
+     * @param endpointString
+     *            endpoint address as a string
+     * @param expectedDatacenter
+     *            expected data center
+     * @param expectedRack
+     *            expected rack
+     */
+    private void checkEndpoint(final AbstractNetworkTopologySnitch snitch,
+            final String endpointString, final String expectedDatacenter,
+            final String expectedRack)
+    {
+        final InetAddress endpoint = InetAddresses.forString(endpointString);
+        Assert.assertEquals(expectedDatacenter, 
snitch.getDatacenter(endpoint));
+        Assert.assertEquals(expectedRack, snitch.getRack(endpoint));
+    }
+
+}

Reply via email to