blob: edf95d7480909244f14464f9ae7741639fbc749d [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.ipc;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import junit.framework.Assert;
import org.apache.commons.logging.impl.Log4JLogger;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.security.KerberosInfo;
import org.apache.hadoop.security.SecurityUtil;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.authorize.ProxyUsers;
import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.security.token.TokenInfo;
import org.apache.hadoop.security.token.delegation.AbstractDelegationTokenSelector;
import org.apache.hadoop.security.token.delegation.TestDelegationToken.TestDelegationTokenIdentifier;
import org.apache.hadoop.security.token.delegation.TestDelegationToken.TestDelegationTokenSecretManager;
import org.apache.hadoop.util.Time;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
/**
* MiniRPCBenchmark measures time to establish an RPC connection
* to a secure RPC server.
* It sequentially establishes connections the specified number of times,
* and calculates the average time taken to connect.
* The time to connect includes the server side authentication time.
* The benchmark supports three authentication methods:
* <ol>
* <li>simple - no authentication. In order to enter this mode
* the configuration file <tt>core-site.xml</tt> should specify
* <tt>hadoop.security.authentication = simple</tt>.
* This is the default mode.</li>
* <li>kerberos - kerberos authentication. In order to enter this mode
* the configuration file <tt>core-site.xml</tt> should specify
* <tt>hadoop.security.authentication = kerberos</tt> and
* the argument string should provide qualifying
* <tt>keytabFile</tt> and <tt>userName</tt> parameters.
* <li>delegation token - authentication using delegation token.
* In order to enter this mode the benchmark should provide all the
* mentioned parameters for kerberos authentication plus the
* <tt>useToken</tt> argument option.
* </ol>
* Input arguments:
* <ul>
* <li>numIterations - number of connections to establish</li>
* <li>keytabFile - keytab file for kerberos authentication</li>
* <li>userName - principal name for kerberos authentication</li>
* <li>useToken - should be specified for delegation token authentication</li>
* <li>logLevel - logging level, see {@link Level}</li>
* </ul>
*/
public class MiniRPCBenchmark {
private static final String KEYTAB_FILE_KEY = "test.keytab.file";
private static final String USER_NAME_KEY = "test.user.name";
private static final String MINI_USER = "miniUser";
private static final String RENEWER = "renewer";
private static final String GROUP_NAME_1 = "MiniGroup1";
private static final String GROUP_NAME_2 = "MiniGroup2";
private static final String[] GROUP_NAMES =
new String[] {GROUP_NAME_1, GROUP_NAME_2};
private UserGroupInformation currentUgi;
private Level logLevel;
MiniRPCBenchmark(Level l) {
currentUgi = null;
logLevel = l;
}
public static class TestDelegationTokenSelector extends
AbstractDelegationTokenSelector<TestDelegationTokenIdentifier>{
protected TestDelegationTokenSelector() {
super(new Text("MY KIND"));
}
}
@KerberosInfo(
serverPrincipal=USER_NAME_KEY)
@TokenInfo(TestDelegationTokenSelector.class)
public static interface MiniProtocol extends VersionedProtocol {
public static final long versionID = 1L;
/**
* Get a Delegation Token.
*/
public Token<TestDelegationTokenIdentifier> getDelegationToken(Text renewer)
throws IOException;
}
/**
* Primitive RPC server, which
* allows clients to connect to it.
*/
static class MiniServer implements MiniProtocol {
private static final String DEFAULT_SERVER_ADDRESS = "0.0.0.0";
private TestDelegationTokenSecretManager secretManager;
private Server rpcServer;
@Override // VersionedProtocol
public long getProtocolVersion(String protocol,
long clientVersion) throws IOException {
if (protocol.equals(MiniProtocol.class.getName()))
return versionID;
throw new IOException("Unknown protocol: " + protocol);
}
@Override // VersionedProtocol
public ProtocolSignature getProtocolSignature(String protocol,
long clientVersion,
int clientMethodsHashCode) throws IOException {
if (protocol.equals(MiniProtocol.class.getName()))
return new ProtocolSignature(versionID, null);
throw new IOException("Unknown protocol: " + protocol);
}
@Override // MiniProtocol
public Token<TestDelegationTokenIdentifier> getDelegationToken(Text renewer)
throws IOException {
String owner = UserGroupInformation.getCurrentUser().getUserName();
String realUser =
UserGroupInformation.getCurrentUser().getRealUser() == null ? "":
UserGroupInformation.getCurrentUser().getRealUser().getUserName();
TestDelegationTokenIdentifier tokenId =
new TestDelegationTokenIdentifier(
new Text(owner), renewer, new Text(realUser));
return new Token<TestDelegationTokenIdentifier>(tokenId, secretManager);
}
/** Start RPC server */
MiniServer(Configuration conf, String user, String keytabFile)
throws IOException {
UserGroupInformation.setConfiguration(conf);
UserGroupInformation.loginUserFromKeytab(user, keytabFile);
secretManager =
new TestDelegationTokenSecretManager(24*60*60*1000,
7*24*60*60*1000,24*60*60*1000,3600000);
secretManager.startThreads();
rpcServer = new RPC.Builder(conf).setProtocol(MiniProtocol.class)
.setInstance(this).setBindAddress(DEFAULT_SERVER_ADDRESS).setPort(0)
.setNumHandlers(1).setVerbose(false).setSecretManager(secretManager)
.build();
rpcServer.start();
}
/** Stop RPC server */
void stop() {
if(rpcServer != null) rpcServer.stop();
rpcServer = null;
}
/** Get RPC server address */
InetSocketAddress getAddress() {
if(rpcServer == null) return null;
return NetUtils.getConnectAddress(rpcServer);
}
}
long connectToServer(Configuration conf, InetSocketAddress addr)
throws IOException {
MiniProtocol client = null;
try {
long start = Time.now();
client = (MiniProtocol) RPC.getProxy(MiniProtocol.class,
MiniProtocol.versionID, addr, conf);
long end = Time.now();
return end - start;
} finally {
RPC.stopProxy(client);
}
}
void connectToServerAndGetDelegationToken(
final Configuration conf, final InetSocketAddress addr) throws IOException {
MiniProtocol client = null;
try {
UserGroupInformation current = UserGroupInformation.getCurrentUser();
UserGroupInformation proxyUserUgi =
UserGroupInformation.createProxyUserForTesting(
MINI_USER, current, GROUP_NAMES);
try {
client = proxyUserUgi.doAs(new PrivilegedExceptionAction<MiniProtocol>() {
@Override
public MiniProtocol run() throws IOException {
MiniProtocol p = (MiniProtocol) RPC.getProxy(MiniProtocol.class,
MiniProtocol.versionID, addr, conf);
Token<TestDelegationTokenIdentifier> token;
token = p.getDelegationToken(new Text(RENEWER));
currentUgi = UserGroupInformation.createUserForTesting(MINI_USER,
GROUP_NAMES);
SecurityUtil.setTokenService(token, addr);
currentUgi.addToken(token);
return p;
}
});
} catch (InterruptedException e) {
Assert.fail(Arrays.toString(e.getStackTrace()));
}
} finally {
RPC.stopProxy(client);
}
}
long connectToServerUsingDelegationToken(
final Configuration conf, final InetSocketAddress addr) throws IOException {
MiniProtocol client = null;
try {
long start = Time.now();
try {
client = currentUgi.doAs(new PrivilegedExceptionAction<MiniProtocol>() {
@Override
public MiniProtocol run() throws IOException {
return (MiniProtocol) RPC.getProxy(MiniProtocol.class,
MiniProtocol.versionID, addr, conf);
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = Time.now();
return end - start;
} finally {
RPC.stopProxy(client);
}
}
static void setLoggingLevel(Level level) {
LogManager.getLogger(Server.class.getName()).setLevel(level);
((Log4JLogger)Server.AUDITLOG).getLogger().setLevel(level);
LogManager.getLogger(Client.class.getName()).setLevel(level);
}
/**
* Run MiniBenchmark with MiniServer as the RPC server.
*
* @param conf - configuration
* @param count - connect this many times
* @param keytabKey - key for keytab file in the configuration
* @param userNameKey - key for user name in the configuration
* @return average time to connect
* @throws IOException
*/
long runMiniBenchmark(Configuration conf,
int count,
String keytabKey,
String userNameKey) throws IOException {
// get login information
String user = System.getProperty("user.name");
if(userNameKey != null)
user = conf.get(userNameKey, user);
String keytabFile = null;
if(keytabKey != null)
keytabFile = conf.get(keytabKey, keytabFile);
MiniServer miniServer = null;
try {
// start the server
miniServer = new MiniServer(conf, user, keytabFile);
InetSocketAddress addr = miniServer.getAddress();
connectToServer(conf, addr);
// connect to the server count times
setLoggingLevel(logLevel);
long elapsed = 0L;
for(int idx = 0; idx < count; idx ++) {
elapsed += connectToServer(conf, addr);
}
return elapsed;
} finally {
if(miniServer != null) miniServer.stop();
}
}
/**
* Run MiniBenchmark using delegation token authentication.
*
* @param conf - configuration
* @param count - connect this many times
* @param keytabKey - key for keytab file in the configuration
* @param userNameKey - key for user name in the configuration
* @return average time to connect
* @throws IOException
*/
long runMiniBenchmarkWithDelegationToken(Configuration conf,
int count,
String keytabKey,
String userNameKey)
throws IOException {
// get login information
String user = System.getProperty("user.name");
if(userNameKey != null)
user = conf.get(userNameKey, user);
String keytabFile = null;
if(keytabKey != null)
keytabFile = conf.get(keytabKey, keytabFile);
MiniServer miniServer = null;
UserGroupInformation.setConfiguration(conf);
String shortUserName =
UserGroupInformation.createRemoteUser(user).getShortUserName();
try {
conf.setStrings(ProxyUsers.getProxySuperuserGroupConfKey(shortUserName),
GROUP_NAME_1);
configureSuperUserIPAddresses(conf, shortUserName);
// start the server
miniServer = new MiniServer(conf, user, keytabFile);
InetSocketAddress addr = miniServer.getAddress();
connectToServerAndGetDelegationToken(conf, addr);
// connect to the server count times
setLoggingLevel(logLevel);
long elapsed = 0L;
for(int idx = 0; idx < count; idx ++) {
elapsed += connectToServerUsingDelegationToken(conf, addr);
}
return elapsed;
} finally {
if(miniServer != null) miniServer.stop();
}
}
static void printUsage() {
System.err.println(
"Usage: MiniRPCBenchmark <numIterations> [<keytabFile> [<userName> " +
"[useToken|useKerberos [<logLevel>]]]]");
System.exit(-1);
}
public static void main(String[] args) throws Exception {
System.out.println("Benchmark: RPC session establishment.");
if(args.length < 1)
printUsage();
Configuration conf = new Configuration();
int count = Integer.parseInt(args[0]);
if(args.length > 1)
conf.set(KEYTAB_FILE_KEY, args[1]);
if(args.length > 2)
conf.set(USER_NAME_KEY, args[2]);
boolean useDelegationToken = false;
if(args.length > 3)
useDelegationToken = args[3].equalsIgnoreCase("useToken");
Level l = Level.ERROR;
if(args.length > 4)
l = Level.toLevel(args[4]);
MiniRPCBenchmark mb = new MiniRPCBenchmark(l);
long elapsedTime = 0;
if(useDelegationToken) {
System.out.println(
"Running MiniRPCBenchmark with delegation token authentication.");
elapsedTime = mb.runMiniBenchmarkWithDelegationToken(
conf, count, KEYTAB_FILE_KEY, USER_NAME_KEY);
} else {
String auth = SecurityUtil.getAuthenticationMethod(conf).toString();
System.out.println(
"Running MiniRPCBenchmark with " + auth + " authentication.");
elapsedTime = mb.runMiniBenchmark(
conf, count, KEYTAB_FILE_KEY, USER_NAME_KEY);
}
System.out.println(org.apache.hadoop.util.VersionInfo.getVersion());
System.out.println("Number of connects: " + count);
System.out.println("Average connect time: " + ((double)elapsedTime/count));
}
private void configureSuperUserIPAddresses(Configuration conf,
String superUserShortName) throws IOException {
ArrayList<String> ipList = new ArrayList<String>();
Enumeration<NetworkInterface> netInterfaceList = NetworkInterface
.getNetworkInterfaces();
while (netInterfaceList.hasMoreElements()) {
NetworkInterface inf = netInterfaceList.nextElement();
Enumeration<InetAddress> addrList = inf.getInetAddresses();
while (addrList.hasMoreElements()) {
InetAddress addr = addrList.nextElement();
ipList.add(addr.getHostAddress());
}
}
StringBuilder builder = new StringBuilder();
for (String ip : ipList) {
builder.append(ip);
builder.append(',');
}
builder.append("127.0.1.1,");
builder.append(InetAddress.getLocalHost().getCanonicalHostName());
conf.setStrings(ProxyUsers.getProxySuperuserIpConfKey(superUserShortName),
builder.toString());
}
}