You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by jj...@apache.org on 2019/09/18 16:13:57 UTC

[cassandra] branch trunk updated: Add a new snitch for Alibaba cloud platform

This is an automated email from the ASF dual-hosted git repository.

jjirsa pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new a7a0e7f  Add a new snitch for Alibaba cloud platform
a7a0e7f is described below

commit a7a0e7fe40cd375c647d2f5e56d04fd98963cff2
Author: xuanling.gc <xu...@alibaba-inc.com>
AuthorDate: Tue Sep 3 11:13:42 2019 +0800

    Add a new snitch for Alibaba cloud platform
    
    Closes #350
    
    Patch by xuanling.gc, reviewed by Brandon Williams for CASSANDRA-15092
---
 CHANGES.txt                                        |   2 +
 .../cassandra/locator/AlibabaCloudSnitch.java      | 146 +++++++++++++++++++++
 .../cassandra/locator/AlibabaCloudSnitchTest.java  | 103 +++++++++++++++
 3 files changed, 251 insertions(+)

diff --git a/CHANGES.txt b/CHANGES.txt
index 709e436..2929a98 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,7 @@
 4.0-alpha2
  * Add `allocate_tokens_for_local_replication_factor` option for token allocation (CASSANDRA-15260)
+ * Add Alibaba Cloud Platform snitch (CASSANDRA-15092)
+
 
 4.0-alpha1
  * Inaccurate exception message with nodetool snapshot (CASSANDRA-15287)
diff --git a/src/java/org/apache/cassandra/locator/AlibabaCloudSnitch.java b/src/java/org/apache/cassandra/locator/AlibabaCloudSnitch.java
new file mode 100644
index 0000000..729e1b3
--- /dev/null
+++ b/src/java/org/apache/cassandra/locator/AlibabaCloudSnitch.java
@@ -0,0 +1,146 @@
+/*
+ * 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.DataInputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import org.apache.cassandra.db.SystemKeyspace;
+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.io.util.FileUtils;
+import org.apache.cassandra.utils.FBUtilities;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *  A snitch that assumes an ECS region is a DC and an ECS availability_zone
+ *  is a rack. This information is available in the config for the node. the 
+ *  format of the zone-id is like :cn-hangzhou-a where cn means china, hangzhou
+ *  means the hangzhou region, a means the az id. We use cn-hangzhou as the dc,
+ *  and f as the zone-id.
+ */
+public class AlibabaCloudSnitch extends AbstractNetworkTopologySnitch
+{
+    protected static final Logger logger = LoggerFactory.getLogger(AlibabaCloudSnitch.class);
+    protected static final String ZONE_NAME_QUERY_URL = "http://100.100.100.200/latest/meta-data/zone-id";
+    private static final String DEFAULT_DC = "UNKNOWN-DC";
+    private static final String DEFAULT_RACK = "UNKNOWN-RACK";
+    private Map<InetAddressAndPort, Map<String, String>> savedEndpoints; 
+    protected String ecsZone;
+    protected String ecsRegion;
+    
+    private static final int HTTP_CONNECT_TIMEOUT = 30000;
+    
+    
+    public AlibabaCloudSnitch() throws MalformedURLException, IOException 
+    {
+        String response = alibabaApiCall(ZONE_NAME_QUERY_URL);
+        String[] splits = response.split("/");
+        String az = splits[splits.length - 1];
+
+        // Split "us-central1-a" or "asia-east1-a" into "us-central1"/"a" and "asia-east1"/"a".
+        splits = az.split("-");
+        ecsZone = splits[splits.length - 1];
+
+        int lastRegionIndex = az.lastIndexOf("-");
+        ecsRegion = az.substring(0, lastRegionIndex);
+
+        String datacenterSuffix = (new SnitchProperties()).get("dc_suffix", "");
+        ecsRegion = ecsRegion.concat(datacenterSuffix);
+        logger.info("AlibabaSnitch using region: {}, zone: {}.", ecsRegion, ecsZone);
+    
+    }
+    
+    String alibabaApiCall(String url) throws ConfigurationException, IOException, SocketTimeoutException
+    {
+        // Populate the region and zone by introspection, fail if 404 on metadata
+        HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
+        DataInputStream d = null;
+        try
+        {
+            conn.setConnectTimeout(HTTP_CONNECT_TIMEOUT);
+            conn.setRequestMethod("GET");
+            
+            int code = conn.getResponseCode();
+            if (code != HttpURLConnection.HTTP_OK)
+                throw new ConfigurationException("AlibabaSnitch was unable to execute the API call. Not an ecs node? and the returun code is " + code);
+
+            // Read the information. I wish I could say (String) conn.getContent() here...
+            int cl = conn.getContentLength();
+            byte[] b = new byte[cl];
+            d = new DataInputStream((FilterInputStream) conn.getContent());
+            d.readFully(b);
+            return new String(b, StandardCharsets.UTF_8);
+        }
+        catch (SocketTimeoutException e)
+        {
+            throw new SocketTimeoutException("Timeout occurred reading a response from the Alibaba ECS metadata");
+        }
+        finally
+        {
+            FileUtils.close(d);
+            conn.disconnect();
+        }
+    }
+    
+    @Override
+    public String getRack(InetAddressAndPort endpoint)
+    {
+        if (endpoint.equals(FBUtilities.getBroadcastAddressAndPort()))
+            return ecsZone;
+        EndpointState state = Gossiper.instance.getEndpointStateForEndpoint(endpoint);
+        if (state == null || state.getApplicationState(ApplicationState.RACK) == null)
+        {
+            if (savedEndpoints == null)
+                savedEndpoints = SystemKeyspace.loadDcRackInfo();
+            if (savedEndpoints.containsKey(endpoint))
+                return savedEndpoints.get(endpoint).get("rack");
+            return DEFAULT_RACK;
+        }
+        return state.getApplicationState(ApplicationState.RACK).value;
+    
+    }
+
+    @Override
+    public String getDatacenter(InetAddressAndPort endpoint) 
+    {
+        if (endpoint.equals(FBUtilities.getBroadcastAddressAndPort()))
+            return ecsRegion;
+        EndpointState state = Gossiper.instance.getEndpointStateForEndpoint(endpoint);
+        if (state == null || state.getApplicationState(ApplicationState.DC) == null)
+        {
+            if (savedEndpoints == null)
+                savedEndpoints = SystemKeyspace.loadDcRackInfo();
+            if (savedEndpoints.containsKey(endpoint))
+                return savedEndpoints.get(endpoint).get("data_center");
+            return DEFAULT_DC;
+        }
+        return state.getApplicationState(ApplicationState.DC).value;
+    
+    }
+
+}
diff --git a/test/unit/org/apache/cassandra/locator/AlibabaCloudSnitchTest.java b/test/unit/org/apache/cassandra/locator/AlibabaCloudSnitchTest.java
new file mode 100644
index 0000000..4e8ab16
--- /dev/null
+++ b/test/unit/org/apache/cassandra/locator/AlibabaCloudSnitchTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.EnumMap;
+import java.util.Map;
+import org.apache.cassandra.SchemaLoader;
+import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.db.Keyspace;
+import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.gms.ApplicationState;
+import org.apache.cassandra.gms.Gossiper;
+import org.apache.cassandra.gms.VersionedValue;
+import org.apache.cassandra.service.StorageService;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class AlibabaCloudSnitchTest 
+{
+    private static String az;
+
+    @BeforeClass
+    public static void setup() throws Exception
+    {
+        System.setProperty(Gossiper.Props.DISABLE_THREAD_VALIDATION, "true");
+        DatabaseDescriptor.daemonInitialization();
+        SchemaLoader.mkdirs();
+        SchemaLoader.cleanup();
+        Keyspace.setInitialized();
+        StorageService.instance.initServer(0);
+    }
+
+    private class TestAlibabaCloudSnitch extends AlibabaCloudSnitch
+    {
+        public TestAlibabaCloudSnitch() throws IOException, ConfigurationException
+        {
+            super();
+        }
+
+        @Override
+        String alibabaApiCall(String url) throws IOException, ConfigurationException
+        {
+            return az;
+        }
+    }
+
+    @Test
+    public void testRac() throws IOException, ConfigurationException
+    {
+        az = "cn-hangzhou-f";
+        AlibabaCloudSnitch snitch = new TestAlibabaCloudSnitch();
+        InetAddressAndPort local = InetAddressAndPort.getByName("127.0.0.1");
+        InetAddressAndPort nonlocal = InetAddressAndPort.getByName("127.0.0.7");
+
+        Gossiper.instance.addSavedEndpoint(nonlocal);
+        Map<ApplicationState, VersionedValue> stateMap = new EnumMap<>(ApplicationState.class);
+        stateMap.put(ApplicationState.DC, StorageService.instance.valueFactory.datacenter("cn-shanghai"));
+        stateMap.put(ApplicationState.RACK, StorageService.instance.valueFactory.datacenter("a"));
+        Gossiper.instance.getEndpointStateForEndpoint(nonlocal).addApplicationStates(stateMap);
+
+        assertEquals("cn-shanghai", snitch.getDatacenter(nonlocal));
+        assertEquals("a", snitch.getRack(nonlocal));
+
+        assertEquals("cn-hangzhou", snitch.getDatacenter(local));
+        assertEquals("f", snitch.getRack(local));
+    }
+    
+    @Test
+    public void testNewRegions() throws IOException, ConfigurationException
+    {
+        az = "us-east-1a";
+        AlibabaCloudSnitch snitch = new TestAlibabaCloudSnitch();
+        InetAddressAndPort local = InetAddressAndPort.getByName("127.0.0.1");
+        assertEquals("us-east", snitch.getDatacenter(local));
+        assertEquals("1a", snitch.getRack(local));
+    }
+
+    @AfterClass
+    public static void tearDown()
+    {
+        StorageService.instance.stopClient();
+    }
+    
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org