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