Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CASSANDRA-18646 Azure snitch #2462

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
623 changes: 398 additions & 225 deletions .circleci/config.yml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
5.0
* Add AzureSnitch (CASSANDRA-18646)
* Implementation of the Unified Compaction Strategy as described in CEP-26 (CASSANDRA-18397)
* Upgrade commons cli to 1.5.0 (CASSANDRA-18659)
* Disable the deprecated keyspace/table thresholds and convert them to guardrails (CASSANDRA-18617)
Expand Down
1 change: 1 addition & 0 deletions NEWS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ New features
- `cassandra-stress` has a new option called '-jmx' which enables a user to pass username and password to JMX (CASSANDRA-18544)
- It is possible to read all credentials for `cassandra-stress` from a file via option `-credentials-file` (CASSANDRA-18544)
- nodetool info displays bootstrap state a node is in as well as if it was decommissioned or if it failed to decommission (CASSANDRA-18555)
- Added snitch for Microsoft Azure of name AzureSnitch (CASSANDRA-18646)

Upgrading
---------
Expand Down
5 changes: 5 additions & 0 deletions conf/cassandra-rackdc.properties
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ rack=rack1
# it is refreshed. Defaults to 21600. Can not be smaller than 30 and bigger than 21600. Has to be an integer.
# ec2_metadata_token_ttl_seconds=21600

# AzureSnitch
# Options are:
# Version of API to talk to. When not set, defaults to '2021-12-13'.
# azure_api_version=2021-12-13

# For all cloud-based snitches, there are following options available:
#
# Property to change metadata service url for a cloud-based snitch. The endpoint of a particular
Expand Down
5 changes: 5 additions & 0 deletions conf/cassandra.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1316,6 +1316,11 @@ slow_query_log_timeout: 500ms
# Snitch for getting dc and rack of a node from metadata service of Alibaba cloud.
# This snitch that assumes an ECS region is a DC and an ECS availability_zone is a rack.
#
# AzureSnitch:
smiklosovic marked this conversation as resolved.
Show resolved Hide resolved
# Gets datacenter from 'location' and rack from 'zone' fields of 'compute' object
# from instance metadata service. If the availability zone is not enabled, it will use the fault
# domain and get its respective value.
#
# CloudstackSnitch:
# A snitch that assumes a Cloudstack Zone follows the typical convention
# country-location-az and uses a country/location tuple as a datacenter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ abstract class AbstractCloudMetadataServiceConnector
protected final String metadataServiceUrl;
protected final int requestTimeoutMs;

public AbstractCloudMetadataServiceConnector(SnitchProperties properties)
private final SnitchProperties properties;

public AbstractCloudMetadataServiceConnector(SnitchProperties snitchProperties)
{
this.properties = snitchProperties;
String parsedMetadataServiceUrl = properties.get(METADATA_URL_PROPERTY, null);

try
Expand All @@ -65,7 +68,6 @@ public AbstractCloudMetadataServiceConnector(SnitchProperties properties)
ex);
}


String metadataRequestTimeout = properties.get(METADATA_REQUEST_TIMEOUT_PROPERTY, DEFAULT_METADATA_REQUEST_TIMEOUT);

try
Expand All @@ -80,6 +82,11 @@ public AbstractCloudMetadataServiceConnector(SnitchProperties properties)
}
}

public SnitchProperties getProperties()
{
return properties;
}

public final String apiCall(String query) throws IOException
{
return apiCall(metadataServiceUrl, query, "GET", ImmutableMap.of(), 200);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,20 @@ abstract class AbstractCloudMetadataServiceSnitch extends AbstractNetworkTopolog
static final String DEFAULT_RACK = "UNKNOWN-RACK";

protected final AbstractCloudMetadataServiceConnector connector;
protected final SnitchProperties snitchProperties;

private final String localRack;
private final String localDc;

private Map<InetAddressAndPort, Map<String, String>> savedEndpoints;

public AbstractCloudMetadataServiceSnitch(AbstractCloudMetadataServiceConnector connector,
SnitchProperties snitchProperties,
Pair<String, String> dcAndRack)
public AbstractCloudMetadataServiceSnitch(AbstractCloudMetadataServiceConnector connector, Pair<String, String> dcAndRack)
{
this.connector = connector;
this.snitchProperties = snitchProperties;
this.localDc = dcAndRack.left;
this.localRack = dcAndRack.right;

logger.info(format("%s using datacenter: %s, rack: %s, connector: %s, properties: %s",
getClass().getName(), getLocalDatacenter(), getLocalRack(), connector, snitchProperties));
getClass().getName(), getLocalDatacenter(), getLocalRack(), connector, connector.getProperties()));
}

@Override
Expand Down
13 changes: 5 additions & 8 deletions src/java/org/apache/cassandra/locator/AlibabaCloudSnitch.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@

import java.io.IOException;

import com.google.common.collect.ImmutableMap;

import org.apache.cassandra.locator.AbstractCloudMetadataServiceConnector.DefaultCloudMetadataServiceConnector;

import static org.apache.cassandra.locator.AbstractCloudMetadataServiceConnector.METADATA_URL_PROPERTY;
Expand All @@ -44,14 +42,13 @@ public AlibabaCloudSnitch() throws IOException

public AlibabaCloudSnitch(SnitchProperties properties) throws IOException
{
this(properties, new DefaultCloudMetadataServiceConnector(properties.putIfAbsent(METADATA_URL_PROPERTY,
DEFAULT_METADATA_SERVICE_URL)));
this(new DefaultCloudMetadataServiceConnector(properties.putIfAbsent(METADATA_URL_PROPERTY,
DEFAULT_METADATA_SERVICE_URL)));
}

public AlibabaCloudSnitch(SnitchProperties properties, AbstractCloudMetadataServiceConnector connector) throws IOException
public AlibabaCloudSnitch(AbstractCloudMetadataServiceConnector connector) throws IOException
{
super(connector, properties, SnitchUtils.parseDcAndRack(connector.apiCall(ZONE_NAME_QUERY_URL,
ImmutableMap.of()),
properties.getDcSuffix()));
super(connector, SnitchUtils.parseDcAndRack(connector.apiCall(ZONE_NAME_QUERY_URL),
connector.getProperties().getDcSuffix()));
}
}
102 changes: 102 additions & 0 deletions src/java/org/apache/cassandra/locator/AzureSnitch.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* 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.IOException;

import com.google.common.collect.ImmutableMap;

import com.fasterxml.jackson.databind.JsonNode;
import org.apache.cassandra.locator.AbstractCloudMetadataServiceConnector.DefaultCloudMetadataServiceConnector;
import org.apache.cassandra.utils.JsonUtils;
import org.apache.cassandra.utils.Pair;

import static java.lang.String.format;
import static org.apache.cassandra.locator.AbstractCloudMetadataServiceConnector.METADATA_URL_PROPERTY;

/**
* AzureSnitch will resolve datacenter and rack by calling {@code /metadata/instance/compute} endpoint returning
* the response in JSON format for API version {@code 2021-12-13}. The version of API is configurable via property
* {@code azure_api_version} in cassandra-rackdc.properties.
* <p>
* A datacenter is resolved from {@code location} field and a rack is resolved by looking into {@code zone} field first.
* When zone is not set, or it is empty string, it will look into {@code platformFaultDomain} field. Such resolved
* value is prepended by {@code rack-} string.
*/
public class AzureSnitch extends AbstractCloudMetadataServiceSnitch
{
static final String DEFAULT_METADATA_SERVICE_URL = "http://169.254.169.254";
static final String METADATA_QUERY_TEMPLATE = "/metadata/instance/compute?api-version=%s&format=json";
static final String METADATA_HEADER = "Metadata";
static final String API_VERSION_PROPERTY_KEY = "azure_api_version";
static final String DEFAULT_API_VERSION = "2021-12-13";

public AzureSnitch() throws IOException
{
this(new SnitchProperties());
}

public AzureSnitch(SnitchProperties properties) throws IOException
{
this(new DefaultCloudMetadataServiceConnector(properties.putIfAbsent(METADATA_URL_PROPERTY,
DEFAULT_METADATA_SERVICE_URL)));
}

public AzureSnitch(AbstractCloudMetadataServiceConnector connector) throws IOException
{
super(connector, resolveDcAndRack(connector));
}

private static Pair<String, String> resolveDcAndRack(AbstractCloudMetadataServiceConnector connector) throws IOException
{
String apiVersion = connector.getProperties().get(API_VERSION_PROPERTY_KEY, DEFAULT_API_VERSION);
String response = connector.apiCall(format(METADATA_QUERY_TEMPLATE, apiVersion), ImmutableMap.of(METADATA_HEADER, "true"));
JsonNode jsonNode = JsonUtils.JSON_OBJECT_MAPPER.readTree(response);

JsonNode location = jsonNode.get("location");
JsonNode zone = jsonNode.get("zone");
smiklosovic marked this conversation as resolved.
Show resolved Hide resolved
JsonNode platformFaultDomain = jsonNode.get("platformFaultDomain");

String datacenter;
String rack;

if (location == null || location.isNull() || location.asText().isEmpty())
datacenter = DEFAULT_DC;
else
datacenter = location.asText();
smiklosovic marked this conversation as resolved.
Show resolved Hide resolved

if (zone == null || zone.isNull() || zone.asText().isEmpty())
{
if (platformFaultDomain == null || platformFaultDomain.isNull() || platformFaultDomain.asText().isEmpty())
{
rack = DEFAULT_RACK;
}
else
{
rack = platformFaultDomain.asText();
}
}
else
{
rack = zone.asText();
}

return Pair.create(datacenter + connector.getProperties().getDcSuffix(), "rack-" + rack);
}
}
20 changes: 6 additions & 14 deletions src/java/org/apache/cassandra/locator/CloudstackSnitch.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.io.util.File;
import org.apache.cassandra.io.util.FileReader;
import org.apache.cassandra.locator.AbstractCloudMetadataServiceConnector.DefaultCloudMetadataServiceConnector;
import org.apache.cassandra.utils.JVMStabilityInspector;
import org.apache.cassandra.utils.Pair;

import static org.apache.cassandra.locator.AbstractCloudMetadataServiceConnector.METADATA_URL_PROPERTY;
import static org.apache.cassandra.locator.CloudstackSnitch.CloudstackConnector.ZONE_NAME_QUERY_URI;

/**
* A snitch that assumes a Cloudstack Zone follows the typical convention
Expand All @@ -43,6 +43,8 @@
@Deprecated
public class CloudstackSnitch extends AbstractCloudMetadataServiceSnitch
{
static final String ZONE_NAME_QUERY_URI = "/latest/meta-data/availability-zone";

private static final String[] LEASE_FILES =
{
"file:///var/lib/dhcp/dhclient.eth0.leases",
Expand All @@ -56,26 +58,16 @@ public CloudstackSnitch() throws IOException

public CloudstackSnitch(SnitchProperties snitchProperties) throws IOException
{
this(snitchProperties, new CloudstackConnector(snitchProperties.putIfAbsent(METADATA_URL_PROPERTY, csMetadataEndpoint())));
this(new DefaultCloudMetadataServiceConnector(snitchProperties.putIfAbsent(METADATA_URL_PROPERTY, csMetadataEndpoint())));
}

public CloudstackSnitch(SnitchProperties properties, AbstractCloudMetadataServiceConnector connector) throws IOException
public CloudstackSnitch(AbstractCloudMetadataServiceConnector connector) throws IOException
{
super(connector, properties, resolveDcAndRack(connector));
super(connector, resolveDcAndRack(connector));
logger.warn("{} is deprecated and not actively maintained. It will be removed in the next " +
"major version of Cassandra.", CloudstackSnitch.class.getName());
}

static class CloudstackConnector extends AbstractCloudMetadataServiceConnector
{
static final String ZONE_NAME_QUERY_URI = "/latest/meta-data/availability-zone";

protected CloudstackConnector(SnitchProperties properties)
{
super(properties);
}
}

private static Pair<String, String> resolveDcAndRack(AbstractCloudMetadataServiceConnector connector) throws IOException
{
String zone = connector.apiCall(ZONE_NAME_QUERY_URI);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ public Ec2MultiRegionSnitch() throws IOException, ConfigurationException

public Ec2MultiRegionSnitch(SnitchProperties props) throws IOException, ConfigurationException
{
this(props, Ec2MetadataServiceConnector.create(props));
this(Ec2MetadataServiceConnector.create(props));
}

Ec2MultiRegionSnitch(SnitchProperties props, AbstractCloudMetadataServiceConnector connector) throws IOException
Ec2MultiRegionSnitch(AbstractCloudMetadataServiceConnector connector) throws IOException
{
super(props, connector);
super(connector);
InetAddress localPublicAddress = InetAddress.getByName(connector.apiCall(PUBLIC_IP_QUERY));
logger.info("EC2Snitch using publicIP as identifier: {}", localPublicAddress);
localPrivateAddress = connector.apiCall(PRIVATE_IP_QUERY);
Expand Down
14 changes: 7 additions & 7 deletions src/java/org/apache/cassandra/locator/Ec2Snitch.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,22 @@ public Ec2Snitch() throws IOException, ConfigurationException

public Ec2Snitch(SnitchProperties props) throws IOException, ConfigurationException
{
this(props, Ec2MetadataServiceConnector.create(props));
this(Ec2MetadataServiceConnector.create(props));
}

Ec2Snitch(SnitchProperties props, AbstractCloudMetadataServiceConnector connector) throws IOException
Ec2Snitch(AbstractCloudMetadataServiceConnector connector) throws IOException
{
super(connector, props, getDcAndRack(props, connector));
usingLegacyNaming = isUsingLegacyNaming(props);
super(connector, getDcAndRack(connector));
usingLegacyNaming = isUsingLegacyNaming(connector.getProperties());
}

private static Pair<String, String> getDcAndRack(SnitchProperties props, AbstractCloudMetadataServiceConnector connector) throws IOException
private static Pair<String, String> getDcAndRack(AbstractCloudMetadataServiceConnector connector) throws IOException
{
String az = connector.apiCall(ZONE_NAME_QUERY);

// if using the full naming scheme, region name is created by removing letters from the
// end of the availability zone and zone is the full zone name
boolean usingLegacyNaming = isUsingLegacyNaming(props);
boolean usingLegacyNaming = isUsingLegacyNaming(connector.getProperties());
String region;
String localDc;
String localRack;
Expand All @@ -101,7 +101,7 @@ private static Pair<String, String> getDcAndRack(SnitchProperties props, Abstrac
localRack = az;
}

localDc = region.concat(props.getDcSuffix());
localDc = region.concat(connector.getProperties().getDcSuffix());

return Pair.create(localDc, localRack);
}
Expand Down
12 changes: 6 additions & 6 deletions src/java/org/apache/cassandra/locator/GoogleCloudSnitch.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ public GoogleCloudSnitch() throws IOException

public GoogleCloudSnitch(SnitchProperties properties) throws IOException
{
this(properties, new DefaultCloudMetadataServiceConnector(properties.putIfAbsent(METADATA_URL_PROPERTY,
DEFAULT_METADATA_SERVICE_URL)));
this(new DefaultCloudMetadataServiceConnector(properties.putIfAbsent(METADATA_URL_PROPERTY,
DEFAULT_METADATA_SERVICE_URL)));
}

public GoogleCloudSnitch(SnitchProperties properties, AbstractCloudMetadataServiceConnector connector) throws IOException
public GoogleCloudSnitch(AbstractCloudMetadataServiceConnector connector) throws IOException
{
super(connector, properties, SnitchUtils.parseDcAndRack(connector.apiCall(ZONE_NAME_QUERY_URL,
ImmutableMap.of("Metadata-Flavor", "Google")),
properties.getDcSuffix()));
super(connector, SnitchUtils.parseDcAndRack(connector.apiCall(ZONE_NAME_QUERY_URL,
ImmutableMap.of("Metadata-Flavor", "Google")),
connector.getProperties().getDcSuffix()));
}
}