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     */
018    package org.apache.hadoop.hdfs;
019    
020    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_PROXY_PROVIDER_KEY_PREFIX;
021    
022    import java.io.IOException;
023    import java.lang.reflect.Constructor;
024    import java.net.InetSocketAddress;
025    import java.net.URI;
026    import java.util.HashMap;
027    import java.util.Map;
028    import java.util.concurrent.TimeUnit;
029    
030    import org.apache.commons.logging.Log;
031    import org.apache.commons.logging.LogFactory;
032    import org.apache.hadoop.conf.Configuration;
033    import org.apache.hadoop.hdfs.DFSClient.Conf;
034    import org.apache.hadoop.hdfs.protocol.AlreadyBeingCreatedException;
035    import org.apache.hadoop.hdfs.protocol.ClientProtocol;
036    import org.apache.hadoop.hdfs.protocol.HdfsConstants;
037    import org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolPB;
038    import org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB;
039    import org.apache.hadoop.hdfs.protocolPB.JournalProtocolPB;
040    import org.apache.hadoop.hdfs.protocolPB.JournalProtocolTranslatorPB;
041    import org.apache.hadoop.hdfs.protocolPB.NamenodeProtocolPB;
042    import org.apache.hadoop.hdfs.protocolPB.NamenodeProtocolTranslatorPB;
043    import org.apache.hadoop.hdfs.server.namenode.NameNode;
044    import org.apache.hadoop.hdfs.server.namenode.SafeModeException;
045    import org.apache.hadoop.hdfs.server.protocol.JournalProtocol;
046    import org.apache.hadoop.hdfs.server.protocol.NamenodeProtocol;
047    import org.apache.hadoop.hdfs.server.protocol.NamenodeProtocols;
048    import org.apache.hadoop.io.Text;
049    import org.apache.hadoop.io.retry.DefaultFailoverProxyProvider;
050    import org.apache.hadoop.io.retry.FailoverProxyProvider;
051    import org.apache.hadoop.io.retry.RetryPolicies;
052    import org.apache.hadoop.io.retry.RetryPolicy;
053    import org.apache.hadoop.io.retry.RetryProxy;
054    import org.apache.hadoop.io.retry.RetryUtils;
055    import org.apache.hadoop.ipc.ProtobufRpcEngine;
056    import org.apache.hadoop.ipc.RPC;
057    import org.apache.hadoop.ipc.RemoteException;
058    import org.apache.hadoop.net.NetUtils;
059    import org.apache.hadoop.security.RefreshUserMappingsProtocol;
060    import org.apache.hadoop.security.SecurityUtil;
061    import org.apache.hadoop.security.UserGroupInformation;
062    import org.apache.hadoop.security.authorize.RefreshAuthorizationPolicyProtocol;
063    import org.apache.hadoop.security.protocolPB.RefreshAuthorizationPolicyProtocolClientSideTranslatorPB;
064    import org.apache.hadoop.security.protocolPB.RefreshAuthorizationPolicyProtocolPB;
065    import org.apache.hadoop.security.protocolPB.RefreshUserMappingsProtocolClientSideTranslatorPB;
066    import org.apache.hadoop.security.protocolPB.RefreshUserMappingsProtocolPB;
067    import org.apache.hadoop.tools.GetUserMappingsProtocol;
068    import org.apache.hadoop.tools.protocolPB.GetUserMappingsProtocolClientSideTranslatorPB;
069    import org.apache.hadoop.tools.protocolPB.GetUserMappingsProtocolPB;
070    
071    import com.google.common.annotations.VisibleForTesting;
072    import com.google.common.base.Preconditions;
073    
074    /**
075     * Create proxy objects to communicate with a remote NN. All remote access to an
076     * NN should be funneled through this class. Most of the time you'll want to use
077     * {@link NameNodeProxies#createProxy(Configuration, URI, Class)}, which will
078     * create either an HA- or non-HA-enabled client proxy as appropriate.
079     */
080    public class NameNodeProxies {
081      
082      private static final Log LOG = LogFactory.getLog(NameNodeProxies.class);
083    
084      /**
085       * Wrapper for a client proxy as well as its associated service ID.
086       * This is simply used as a tuple-like return type for
087       * {@link NameNodeProxies#createProxy} and
088       * {@link NameNodeProxies#createNonHAProxy}.
089       */
090      public static class ProxyAndInfo<PROXYTYPE> {
091        private final PROXYTYPE proxy;
092        private final Text dtService;
093        
094        public ProxyAndInfo(PROXYTYPE proxy, Text dtService) {
095          this.proxy = proxy;
096          this.dtService = dtService;
097        }
098        
099        public PROXYTYPE getProxy() {
100          return proxy;
101        }
102        
103        public Text getDelegationTokenService() {
104          return dtService;
105        }
106      }
107    
108      /**
109       * Creates the namenode proxy with the passed protocol. This will handle
110       * creation of either HA- or non-HA-enabled proxy objects, depending upon
111       * if the provided URI is a configured logical URI.
112       * 
113       * @param conf the configuration containing the required IPC
114       *        properties, client failover configurations, etc.
115       * @param nameNodeUri the URI pointing either to a specific NameNode
116       *        or to a logical nameservice.
117       * @param xface the IPC interface which should be created
118       * @return an object containing both the proxy and the associated
119       *         delegation token service it corresponds to
120       * @throws IOException if there is an error creating the proxy
121       **/
122      @SuppressWarnings("unchecked")
123      public static <T> ProxyAndInfo<T> createProxy(Configuration conf,
124          URI nameNodeUri, Class<T> xface) throws IOException {
125        Class<FailoverProxyProvider<T>> failoverProxyProviderClass =
126            getFailoverProxyProviderClass(conf, nameNodeUri, xface);
127      
128        if (failoverProxyProviderClass == null) {
129          // Non-HA case
130          return createNonHAProxy(conf, NameNode.getAddress(nameNodeUri), xface,
131              UserGroupInformation.getCurrentUser(), true);
132        } else {
133          // HA case
134          FailoverProxyProvider<T> failoverProxyProvider = NameNodeProxies
135              .createFailoverProxyProvider(conf, failoverProxyProviderClass, xface,
136                  nameNodeUri);
137          Conf config = new Conf(conf);
138          T proxy = (T) RetryProxy.create(xface, failoverProxyProvider, RetryPolicies
139              .failoverOnNetworkException(RetryPolicies.TRY_ONCE_THEN_FAIL,
140                  config.maxFailoverAttempts, config.failoverSleepBaseMillis,
141                  config.failoverSleepMaxMillis));
142          
143          Text dtService = HAUtil.buildTokenServiceForLogicalUri(nameNodeUri);
144          return new ProxyAndInfo<T>(proxy, dtService);
145        }
146      }
147    
148      /**
149       * Creates an explicitly non-HA-enabled proxy object. Most of the time you
150       * don't want to use this, and should instead use {@link NameNodeProxies#createProxy}.
151       * 
152       * @param conf the configuration object
153       * @param nnAddr address of the remote NN to connect to
154       * @param xface the IPC interface which should be created
155       * @param ugi the user who is making the calls on the proxy object
156       * @param withRetries certain interfaces have a non-standard retry policy
157       * @return an object containing both the proxy and the associated
158       *         delegation token service it corresponds to
159       * @throws IOException
160       */
161      @SuppressWarnings("unchecked")
162      public static <T> ProxyAndInfo<T> createNonHAProxy(
163          Configuration conf, InetSocketAddress nnAddr, Class<T> xface,
164          UserGroupInformation ugi, boolean withRetries) throws IOException {
165        Text dtService = SecurityUtil.buildTokenService(nnAddr);
166      
167        T proxy;
168        if (xface == ClientProtocol.class) {
169          proxy = (T) createNNProxyWithClientProtocol(nnAddr, conf, ugi,
170              withRetries);
171        } else if (xface == JournalProtocol.class) {
172          proxy = (T) createNNProxyWithJournalProtocol(nnAddr, conf, ugi);
173        } else if (xface == NamenodeProtocol.class) {
174          proxy = (T) createNNProxyWithNamenodeProtocol(nnAddr, conf, ugi,
175              withRetries);
176        } else if (xface == GetUserMappingsProtocol.class) {
177          proxy = (T) createNNProxyWithGetUserMappingsProtocol(nnAddr, conf, ugi);
178        } else if (xface == RefreshUserMappingsProtocol.class) {
179          proxy = (T) createNNProxyWithRefreshUserMappingsProtocol(nnAddr, conf, ugi);
180        } else if (xface == RefreshAuthorizationPolicyProtocol.class) {
181          proxy = (T) createNNProxyWithRefreshAuthorizationPolicyProtocol(nnAddr,
182              conf, ugi);
183        } else {
184          String message = "Upsupported protocol found when creating the proxy " +
185              "connection to NameNode: " +
186              ((xface != null) ? xface.getClass().getName() : "null");
187          LOG.error(message);
188          throw new IllegalStateException(message);
189        }
190        return new ProxyAndInfo<T>(proxy, dtService);
191      }
192      
193      private static JournalProtocol createNNProxyWithJournalProtocol(
194          InetSocketAddress address, Configuration conf, UserGroupInformation ugi)
195          throws IOException {
196        JournalProtocolPB proxy = (JournalProtocolPB) createNameNodeProxy(address,
197            conf, ugi, JournalProtocolPB.class);
198        return new JournalProtocolTranslatorPB(proxy);
199      }
200    
201      private static RefreshAuthorizationPolicyProtocol
202          createNNProxyWithRefreshAuthorizationPolicyProtocol(InetSocketAddress address,
203              Configuration conf, UserGroupInformation ugi) throws IOException {
204        RefreshAuthorizationPolicyProtocolPB proxy = (RefreshAuthorizationPolicyProtocolPB)
205            createNameNodeProxy(address, conf, ugi, RefreshAuthorizationPolicyProtocolPB.class);
206        return new RefreshAuthorizationPolicyProtocolClientSideTranslatorPB(proxy);
207      }
208      
209      private static RefreshUserMappingsProtocol
210          createNNProxyWithRefreshUserMappingsProtocol(InetSocketAddress address,
211              Configuration conf, UserGroupInformation ugi) throws IOException {
212        RefreshUserMappingsProtocolPB proxy = (RefreshUserMappingsProtocolPB)
213            createNameNodeProxy(address, conf, ugi, RefreshUserMappingsProtocolPB.class);
214        return new RefreshUserMappingsProtocolClientSideTranslatorPB(proxy);
215      }
216    
217      private static GetUserMappingsProtocol createNNProxyWithGetUserMappingsProtocol(
218          InetSocketAddress address, Configuration conf, UserGroupInformation ugi)
219          throws IOException {
220        GetUserMappingsProtocolPB proxy = (GetUserMappingsProtocolPB)
221            createNameNodeProxy(address, conf, ugi, GetUserMappingsProtocolPB.class);
222        return new GetUserMappingsProtocolClientSideTranslatorPB(proxy);
223      }
224      
225      private static NamenodeProtocol createNNProxyWithNamenodeProtocol(
226          InetSocketAddress address, Configuration conf, UserGroupInformation ugi,
227          boolean withRetries) throws IOException {
228        NamenodeProtocolPB proxy = (NamenodeProtocolPB) createNameNodeProxy(
229            address, conf, ugi, NamenodeProtocolPB.class);
230        if (withRetries) { // create the proxy with retries
231          RetryPolicy timeoutPolicy = RetryPolicies.exponentialBackoffRetry(5, 200,
232              TimeUnit.MILLISECONDS);
233          Map<Class<? extends Exception>, RetryPolicy> exceptionToPolicyMap 
234                         = new HashMap<Class<? extends Exception>, RetryPolicy>();
235          RetryPolicy methodPolicy = RetryPolicies.retryByException(timeoutPolicy,
236              exceptionToPolicyMap);
237          Map<String, RetryPolicy> methodNameToPolicyMap 
238                         = new HashMap<String, RetryPolicy>();
239          methodNameToPolicyMap.put("getBlocks", methodPolicy);
240          methodNameToPolicyMap.put("getAccessKeys", methodPolicy);
241          proxy = (NamenodeProtocolPB) RetryProxy.create(NamenodeProtocolPB.class,
242              proxy, methodNameToPolicyMap);
243        }
244        return new NamenodeProtocolTranslatorPB(proxy);
245      }
246      
247      private static ClientProtocol createNNProxyWithClientProtocol(
248          InetSocketAddress address, Configuration conf, UserGroupInformation ugi,
249          boolean withRetries) throws IOException {
250        RPC.setProtocolEngine(conf, ClientNamenodeProtocolPB.class, ProtobufRpcEngine.class);
251    
252        final RetryPolicy defaultPolicy = 
253            RetryUtils.getDefaultRetryPolicy(
254                conf, 
255                DFSConfigKeys.DFS_CLIENT_RETRY_POLICY_ENABLED_KEY, 
256                DFSConfigKeys.DFS_CLIENT_RETRY_POLICY_ENABLED_DEFAULT, 
257                DFSConfigKeys.DFS_CLIENT_RETRY_POLICY_SPEC_KEY,
258                DFSConfigKeys.DFS_CLIENT_RETRY_POLICY_SPEC_DEFAULT,
259                SafeModeException.class);
260        
261        final long version = RPC.getProtocolVersion(ClientNamenodeProtocolPB.class);
262        ClientNamenodeProtocolPB proxy = RPC.getProtocolProxy(
263            ClientNamenodeProtocolPB.class, version, address, ugi, conf,
264            NetUtils.getDefaultSocketFactory(conf),
265            org.apache.hadoop.ipc.Client.getTimeout(conf), defaultPolicy)
266                .getProxy();
267    
268        if (withRetries) { // create the proxy with retries
269    
270          RetryPolicy createPolicy = RetryPolicies
271              .retryUpToMaximumCountWithFixedSleep(5,
272                  HdfsConstants.LEASE_SOFTLIMIT_PERIOD, TimeUnit.MILLISECONDS);
273        
274          Map<Class<? extends Exception>, RetryPolicy> remoteExceptionToPolicyMap 
275                     = new HashMap<Class<? extends Exception>, RetryPolicy>();
276          remoteExceptionToPolicyMap.put(AlreadyBeingCreatedException.class,
277              createPolicy);
278        
279          Map<Class<? extends Exception>, RetryPolicy> exceptionToPolicyMap
280                     = new HashMap<Class<? extends Exception>, RetryPolicy>();
281          exceptionToPolicyMap.put(RemoteException.class, RetryPolicies
282              .retryByRemoteException(defaultPolicy,
283                  remoteExceptionToPolicyMap));
284          RetryPolicy methodPolicy = RetryPolicies.retryByException(
285              defaultPolicy, exceptionToPolicyMap);
286          Map<String, RetryPolicy> methodNameToPolicyMap 
287                     = new HashMap<String, RetryPolicy>();
288        
289          methodNameToPolicyMap.put("create", methodPolicy);
290        
291          proxy = (ClientNamenodeProtocolPB) RetryProxy.create(
292              ClientNamenodeProtocolPB.class,
293              new DefaultFailoverProxyProvider<ClientNamenodeProtocolPB>(
294                  ClientNamenodeProtocolPB.class, proxy),
295              methodNameToPolicyMap,
296              defaultPolicy);
297        }
298        return new ClientNamenodeProtocolTranslatorPB(proxy);
299      }
300      
301      private static Object createNameNodeProxy(InetSocketAddress address,
302          Configuration conf, UserGroupInformation ugi, Class<?> xface)
303          throws IOException {
304        RPC.setProtocolEngine(conf, xface, ProtobufRpcEngine.class);
305        Object proxy = RPC.getProxy(xface, RPC.getProtocolVersion(xface), address,
306            ugi, conf, NetUtils.getDefaultSocketFactory(conf));
307        return proxy;
308      }
309    
310      /** Gets the configured Failover proxy provider's class */
311      @VisibleForTesting
312      public static <T> Class<FailoverProxyProvider<T>> getFailoverProxyProviderClass(
313          Configuration conf, URI nameNodeUri, Class<T> xface) throws IOException {
314        if (nameNodeUri == null) {
315          return null;
316        }
317        String host = nameNodeUri.getHost();
318      
319        String configKey = DFS_CLIENT_FAILOVER_PROXY_PROVIDER_KEY_PREFIX + "."
320            + host;
321        try {
322          @SuppressWarnings("unchecked")
323          Class<FailoverProxyProvider<T>> ret = (Class<FailoverProxyProvider<T>>) conf
324              .getClass(configKey, null, FailoverProxyProvider.class);
325          if (ret != null) {
326            // If we found a proxy provider, then this URI should be a logical NN.
327            // Given that, it shouldn't have a non-default port number.
328            int port = nameNodeUri.getPort();
329            if (port > 0 && port != NameNode.DEFAULT_PORT) {
330              throw new IOException("Port " + port + " specified in URI "
331                  + nameNodeUri + " but host '" + host
332                  + "' is a logical (HA) namenode"
333                  + " and does not use port information.");
334            }
335          }
336          return ret;
337        } catch (RuntimeException e) {
338          if (e.getCause() instanceof ClassNotFoundException) {
339            throw new IOException("Could not load failover proxy provider class "
340                + conf.get(configKey) + " which is configured for authority "
341                + nameNodeUri, e);
342          } else {
343            throw e;
344          }
345        }
346      }
347    
348      /** Creates the Failover proxy provider instance*/
349      @VisibleForTesting
350      public static <T> FailoverProxyProvider<T> createFailoverProxyProvider(
351          Configuration conf, Class<FailoverProxyProvider<T>> failoverProxyProviderClass,
352          Class<T> xface, URI nameNodeUri) throws IOException {
353        Preconditions.checkArgument(
354            xface.isAssignableFrom(NamenodeProtocols.class),
355            "Interface %s is not a NameNode protocol", xface);
356        try {
357          Constructor<FailoverProxyProvider<T>> ctor = failoverProxyProviderClass
358              .getConstructor(Configuration.class, URI.class, Class.class);
359          FailoverProxyProvider<T> provider = ctor.newInstance(conf, nameNodeUri,
360              xface);
361          return provider;
362        } catch (Exception e) {
363          String message = "Couldn't create proxy provider " + failoverProxyProviderClass;
364          if (LOG.isDebugEnabled()) {
365            LOG.debug(message, e);
366          }
367          if (e.getCause() instanceof IOException) {
368            throw (IOException) e.getCause();
369          } else {
370            throw new IOException(message, e);
371          }
372        }
373      }
374    
375    }