/* * 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 com.github.ambry.config.ClusterMapConfig; import com.github.ambry.network.Port; import com.github.ambry.network.PortType; import com.github.ambry.utils.Utils; import java.util.List; import java.util.TreeSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static com.github.ambry.clustermap.ClusterMapUtils.*; /** * {@link DataNodeId} implementation to use within dynamic cluster managers. */ class AmbryDataNode extends DataNodeId implements Resource { private final String hostName; private final Port plainTextPort; private final Port sslPort; private final String dataCenterName; private final long rackId; private final List<String> sslEnabledDataCenters; private final Logger logger = LoggerFactory.getLogger(getClass()); private final ResourceStatePolicy resourceStatePolicy; /** * Instantiate an AmbryDataNode object. * @param dataCenterName the name of the dataCenter associated with this data node. * @param clusterMapConfig the {@link ClusterMapConfig} to use. * @param hostName the hostName identifying this data node. * @param portNum the port identifying this data node. * @param rackId the rack Id associated with this data node (may be null). * @param sslPortNum the ssl port associated with this data node (may be null). * @throws Exception if there is an exception in instantiating the {@link ResourceStatePolicy} */ AmbryDataNode(String dataCenterName, ClusterMapConfig clusterMapConfig, String hostName, int portNum, Long rackId, Integer sslPortNum) throws Exception { this.hostName = hostName; this.plainTextPort = new Port(portNum, PortType.PLAINTEXT); this.sslPort = sslPortNum != null ? new Port(sslPortNum, PortType.SSL) : null; this.dataCenterName = dataCenterName; this.rackId = rackId != null ? rackId : UNKNOWN_RACK_ID; this.sslEnabledDataCenters = Utils.splitString(clusterMapConfig.clusterMapSslEnabledDatacenters, ","); ResourceStatePolicyFactory resourceStatePolicyFactory = Utils.getObj(clusterMapConfig.clusterMapResourceStatePolicyFactory, this, HardwareState.AVAILABLE, clusterMapConfig); this.resourceStatePolicy = resourceStatePolicyFactory.getResourceStatePolicy(); validate(); } /** * Validate the constructed AmbryDataNode. */ private void validate() { // validate hostname String fqdn = getFullyQualifiedDomainName(hostName); if (!fqdn.equals(hostName)) { throw new IllegalStateException( "Hostname for AmbryDataNode (" + hostName + ") does not match its fully qualified domain name: " + fqdn); } // validate ports TreeSet<Integer> ports = new TreeSet<>(); ports.add(plainTextPort.getPort()); if (sslPort != null) { if (sslPort.getPort() == plainTextPort.getPort()) { throw new IllegalStateException("Same port number for both plain and ssl ports"); } ports.add(sslPort.getPort()); } else if (sslEnabledDataCenters.contains(dataCenterName)) { throw new IllegalArgumentException("No SSL port to a datanode to which SSL is enabled."); } if (ports.first() < MIN_PORT || ports.last() > MAX_PORT) { throw new IllegalStateException("Ports " + ports + " not in valid range [" + MIN_PORT + " - " + MAX_PORT + "]"); } } @Override public String getHostname() { return hostName; } @Override public int getPort() { return plainTextPort.getPort(); } @Override public int getSSLPort() { return sslPort.getPort(); } @Override public boolean hasSSLPort() { return sslPort != null; } @Override public Port getPortToConnectTo() { return sslEnabledDataCenters.contains(dataCenterName) ? sslPort : plainTextPort; } @Override public HardwareState getState() { return resourceStatePolicy.isDown() ? HardwareState.UNAVAILABLE : HardwareState.AVAILABLE; } @Override public String getDatacenterName() { return dataCenterName; } @Override public long getRackId() { return rackId; } @Override public int compareTo(DataNodeId o) { if (getClass() != o.getClass()) { throw new IllegalStateException("Incompatible objects to compare"); } AmbryDataNode other = (AmbryDataNode) o; int compare = (plainTextPort.getPort() < other.plainTextPort.getPort()) ? -1 : ((plainTextPort.getPort() == other.plainTextPort.getPort()) ? 0 : 1); if (compare == 0) { compare = hostName.compareTo(other.hostName); } return compare; } @Override public String toString() { return "DataNode[" + getHostname() + ":" + getPort() + "]"; } /** * Set the hard state of this data node dynamically. * @param newState the updated {@link HardwareState} */ void setState(HardwareState newState) { logger.trace("Setting state of instance " + getInstanceName(hostName, plainTextPort.getPort()) + " to " + newState); if (newState == HardwareState.AVAILABLE) { resourceStatePolicy.onHardUp(); } else { resourceStatePolicy.onHardDown(); } } /** * Take actions, if any, when a request to this node times out. */ void onNodeTimeout() { resourceStatePolicy.onError(); } /** * Take actions, if any, when a request to this node receives a response. */ void onNodeResponse() { resourceStatePolicy.onSuccess(); } }