001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.ha;
019
020import java.io.IOException;
021import java.net.InetSocketAddress;
022import java.util.Map;
023
024import javax.net.SocketFactory;
025
026import org.apache.hadoop.classification.InterfaceAudience;
027import org.apache.hadoop.classification.InterfaceStability;
028import org.apache.hadoop.conf.Configuration;
029import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
030import org.apache.hadoop.ha.protocolPB.HAServiceProtocolClientSideTranslatorPB;
031import org.apache.hadoop.ha.protocolPB.ZKFCProtocolClientSideTranslatorPB;
032import org.apache.hadoop.net.NetUtils;
033
034import com.google.common.collect.Maps;
035
036/**
037 * Represents a target of the client side HA administration commands.
038 */
039@InterfaceAudience.Public
040@InterfaceStability.Evolving
041public abstract class HAServiceTarget {
042
043  private static final String HOST_SUBST_KEY = "host";
044  private static final String PORT_SUBST_KEY = "port";
045  private static final String ADDRESS_SUBST_KEY = "address";
046
047  /**
048   * @return the IPC address of the target node.
049   */
050  public abstract InetSocketAddress getAddress();
051
052  /**
053   * Returns an optional separate RPC server address for health checks at the
054   * target node.  If defined, then this address is used by the health monitor
055   * for the {@link HAServiceProtocol#monitorHealth()} and
056   * {@link HAServiceProtocol#getServiceStatus()} calls.  This can be useful for
057   * separating out these calls onto separate RPC handlers to protect against
058   * resource exhaustion in the main RPC handler pool.  If null (which is the
059   * default implementation), then all RPC calls go to the address defined by
060   * {@link #getAddress()}.
061   *
062   * @return IPC address of the lifeline RPC server on the target node, or null
063   *     if no lifeline RPC server is used
064   */
065  public InetSocketAddress getHealthMonitorAddress() {
066    return null;
067  }
068
069  /**
070   * @return the IPC address of the ZKFC on the target node
071   */
072  public abstract InetSocketAddress getZKFCAddress();
073
074  /**
075   * @return a Fencer implementation configured for this target node
076   */
077  public abstract NodeFencer getFencer();
078  
079  /**
080   * @throws BadFencingConfigurationException if the fencing configuration
081   * appears to be invalid. This is divorced from the above
082   * {@link #getFencer()} method so that the configuration can be checked
083   * during the pre-flight phase of failover.
084   */
085  public abstract void checkFencingConfigured()
086      throws BadFencingConfigurationException;
087  
088  /**
089   * @return a proxy to connect to the target HA Service.
090   */
091  public HAServiceProtocol getProxy(Configuration conf, int timeoutMs)
092      throws IOException {
093    return getProxyForAddress(conf, timeoutMs, getAddress());
094  }
095
096  /**
097   * Returns a proxy to connect to the target HA service for health monitoring.
098   * If {@link #getHealthMonitorAddress()} is implemented to return a non-null
099   * address, then this proxy will connect to that address.  Otherwise, the
100   * returned proxy defaults to using {@link #getAddress()}, which means this
101   * method's behavior is identical to {@link #getProxy(Configuration, int)}.
102   *
103   * @param conf Configuration
104   * @param timeoutMs timeout in milliseconds
105   * @return a proxy to connect to the target HA service for health monitoring
106   * @throws IOException if there is an error
107   */
108  public HAServiceProtocol getHealthMonitorProxy(Configuration conf,
109      int timeoutMs) throws IOException {
110    InetSocketAddress addr = getHealthMonitorAddress();
111    if (addr == null) {
112      addr = getAddress();
113    }
114    return getProxyForAddress(conf, timeoutMs, addr);
115  }
116
117  private HAServiceProtocol getProxyForAddress(Configuration conf,
118      int timeoutMs, InetSocketAddress addr) throws IOException {
119    Configuration confCopy = new Configuration(conf);
120    // Lower the timeout so we quickly fail to connect
121    confCopy.setInt(
122        CommonConfigurationKeysPublic.IPC_CLIENT_CONNECT_MAX_RETRIES_KEY, 1);
123    SocketFactory factory = NetUtils.getDefaultSocketFactory(confCopy);
124    return new HAServiceProtocolClientSideTranslatorPB(
125        addr,
126        confCopy, factory, timeoutMs);
127  }
128
129  /**
130   * @return a proxy to the ZKFC which is associated with this HA service.
131   */
132  public ZKFCProtocol getZKFCProxy(Configuration conf, int timeoutMs)
133      throws IOException {
134    Configuration confCopy = new Configuration(conf);
135    // Lower the timeout so we quickly fail to connect
136    confCopy.setInt(CommonConfigurationKeysPublic.IPC_CLIENT_CONNECT_MAX_RETRIES_KEY, 1);
137    SocketFactory factory = NetUtils.getDefaultSocketFactory(confCopy);
138    return new ZKFCProtocolClientSideTranslatorPB(
139        getZKFCAddress(),
140        confCopy, factory, timeoutMs);
141  }
142  
143  public final Map<String, String> getFencingParameters() {
144    Map<String, String> ret = Maps.newHashMap();
145    addFencingParameters(ret);
146    return ret;
147  }
148  
149  /**
150   * Hook to allow subclasses to add any parameters they would like to
151   * expose to fencing implementations/scripts. Fencing methods are free
152   * to use this map as they see fit -- notably, the shell script
153   * implementation takes each entry, prepends 'target_', substitutes
154   * '_' for '.', and adds it to the environment of the script.
155   *
156   * Subclass implementations should be sure to delegate to the superclass
157   * implementation as well as adding their own keys.
158   *
159   * @param ret map which can be mutated to pass parameters to the fencer
160   */
161  protected void addFencingParameters(Map<String, String> ret) {
162    ret.put(ADDRESS_SUBST_KEY, String.valueOf(getAddress()));
163    ret.put(HOST_SUBST_KEY, getAddress().getHostName());
164    ret.put(PORT_SUBST_KEY, String.valueOf(getAddress().getPort()));
165  }
166
167  /**
168   * @return true if auto failover should be considered enabled
169   */
170  public boolean isAutoFailoverEnabled() {
171    return false;
172  }
173}