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 }