/*
* Copyright 2017 LinkedIn Corp. All rights reserved.
*
* Licensed 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.
*/
package com.github.ambry.clustermap;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.helix.model.InstanceConfig;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* A class with clustermap related utility methods for use by other classes.
*/
public class ClusterMapUtils {
static final String DISK_CAPACITY_STR = "capacityInBytes";
static final String DISK_STATE = "diskState";
static final String REPLICAS_STR = "Replicas";
static final String REPLICAS_DELIM_STR = ",";
static final String REPLICAS_STR_SEPARATOR = ":";
static final String SSLPORT_STR = "sslPort";
static final String RACKID_STR = "rackId";
static final String SEALED_STR = "SEALED";
static final String AVAILABLE_STR = "AVAILABLE";
static final String UNAVAILABLE_STR = "UNAVAILABLE";
static final String ZKCONNECTSTR_STR = "zkConnectStr";
static final String ZKINFO_STR = "zkInfo";
static final String DATACENTER_STR = "datacenter";
static final int UNKNOWN_RACK_ID = -1;
static final int MIN_PORT = 1025;
static final int MAX_PORT = 65535;
static final long MIN_REPLICA_CAPACITY_IN_BYTES = 1024 * 1024 * 1024L;
static final long MAX_REPLICA_CAPACITY_IN_BYTES = 10L * 1024 * 1024 * 1024 * 1024;
static final long MIN_DISK_CAPACITY_IN_BYTES = 10L * 1024 * 1024 * 1024;
static final long MAX_DISK_CAPACITY_IN_BYTES = 10L * 1024 * 1024 * 1024 * 1024;
/**
* Construct and return the instance name given the host and port.
* @param host the hostname of the instance.
* @param port the port of the instance. Can be null.
* @return the constructed instance name.
*/
public static String getInstanceName(String host, Integer port) {
return port == null ? host : host + "_" + port;
}
/**
* Parses zk layout JSON string and populates and returns a map of datacenter name to Zk connect strings.
* @param zkLayoutJsonString the string containing the Zookeeper layout.
* @return a map of dcName -> zkConnectStr.
* @throws JSONException if there is an error parsing the JSON.
*/
public static Map<String, String> parseZkJsonAndPopulateZkInfo(String zkLayoutJsonString) throws JSONException {
Map<String, String> dataCenterToZkAddress = new HashMap<>();
JSONObject root = new JSONObject(zkLayoutJsonString);
JSONArray all = root.getJSONArray(ZKINFO_STR);
for (int i = 0; i < all.length(); i++) {
JSONObject entry = all.getJSONObject(i);
dataCenterToZkAddress.put(entry.getString(DATACENTER_STR), entry.getString(ZKCONNECTSTR_STR));
}
return dataCenterToZkAddress;
}
/**
* Get the list of sealed replicas on a given instance.
* @param instanceConfig the {@link InstanceConfig} associated with the interested instance.
* @return the list of sealed replicas.
*/
static List<String> getSealedReplicas(InstanceConfig instanceConfig) {
return instanceConfig.getRecord().getListField(ClusterMapUtils.SEALED_STR);
}
/**
* Get the rack id associated with the given instance (if any).
* @param instanceConfig the {@link InstanceConfig} associated with the interested instance.
* @return the rack id associated with the given instance.
*/
static Long getRackId(InstanceConfig instanceConfig) {
return Long.valueOf(instanceConfig.getRecord().getSimpleField(RACKID_STR));
}
/**
* Get the ssl port associated with the given instance (if any).
* @param instanceConfig the {@link InstanceConfig} associated with the interested instance.
* @return the ssl port associated with the given instance.
*/
static Integer getSslPortStr(InstanceConfig instanceConfig) {
String sslPortStr = instanceConfig.getRecord().getSimpleField(SSLPORT_STR);
return sslPortStr == null ? null : Integer.valueOf(sslPortStr);
}
/**
* Converts a hostname into a canonical hostname.
*
* @param unqualifiedHostname hostname to be fully qualified
* @return canonical hostname that can be compared with DataNode.getHostname()
*/
static String getFullyQualifiedDomainName(String unqualifiedHostname) {
if (unqualifiedHostname == null) {
throw new IllegalStateException("Hostname cannot be null.");
} else if (unqualifiedHostname.length() == 0) {
throw new IllegalStateException("Hostname cannot be zero length.");
}
try {
return InetAddress.getByName(unqualifiedHostname).getCanonicalHostName().toLowerCase();
} catch (UnknownHostException e) {
throw new IllegalStateException(
"Host (" + unqualifiedHostname + ") is unknown so cannot determine fully qualified domain name.");
}
}
/**
* Serialize and return the arguments.
* @param shortValue a Short value.
* @param longValue a Long value.
* @return the serialized byte array.
*/
static byte[] serializeShortAndLong(Short shortValue, Long longValue) {
ByteBuffer buffer = ByteBuffer.allocate(Short.SIZE / Byte.SIZE + Long.SIZE / Byte.SIZE);
buffer.putShort(shortValue);
buffer.putLong(longValue);
return buffer.array();
}
/**
* Validate the replica capacity.
* @param replicaCapacityInBytes the replica capacity to validate.
*/
static void validateReplicaCapacityInBytes(long replicaCapacityInBytes) {
if (replicaCapacityInBytes < MIN_REPLICA_CAPACITY_IN_BYTES) {
throw new IllegalStateException(
"Invalid disk capacity: " + replicaCapacityInBytes + " is less than " + MIN_REPLICA_CAPACITY_IN_BYTES);
} else if (replicaCapacityInBytes > MAX_REPLICA_CAPACITY_IN_BYTES) {
throw new IllegalStateException(
"Invalid disk capacity: " + replicaCapacityInBytes + " is more than " + MAX_REPLICA_CAPACITY_IN_BYTES);
}
}
/**
* Validate the disk capacity.
* @param diskCapacityInBytes the disk capacity to validate.
*/
static void validateDiskCapacity(long diskCapacityInBytes) {
if (diskCapacityInBytes < MIN_DISK_CAPACITY_IN_BYTES) {
throw new IllegalStateException(
"Invalid disk capacity: " + diskCapacityInBytes + " is less than " + MIN_DISK_CAPACITY_IN_BYTES);
} else if (diskCapacityInBytes > MAX_DISK_CAPACITY_IN_BYTES) {
throw new IllegalStateException(
"Invalid disk capacity: " + diskCapacityInBytes + " is more than " + MAX_DISK_CAPACITY_IN_BYTES);
}
}
}