HDFS-15079. RBF: Namenode needs to use the actual client Id and callId when going through RBF proxy. (#4530)

This commit is contained in:
xuzq 2022-07-23 22:19:37 +08:00 committed by GitHub
parent 5c84cb81ba
commit 2c96357051
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 395 additions and 108 deletions

View File

@ -46,6 +46,10 @@ public class RetryInvocationHandler<T> implements RpcInvocationHandler {
public static final Logger LOG = LoggerFactory.getLogger( public static final Logger LOG = LoggerFactory.getLogger(
RetryInvocationHandler.class); RetryInvocationHandler.class);
@VisibleForTesting
public static final ThreadLocal<Boolean> SET_CALL_ID_FOR_TEST =
ThreadLocal.withInitial(() -> true);
static class Call { static class Call {
private final Method method; private final Method method;
private final Object[] args; private final Object[] args;
@ -159,7 +163,7 @@ CallReturn invoke() throws Throwable {
} }
Object invokeMethod() throws Throwable { Object invokeMethod() throws Throwable {
if (isRpc) { if (isRpc && SET_CALL_ID_FOR_TEST.get()) {
Client.setCallIdAndRetryCount(callId, counters.retries, Client.setCallIdAndRetryCount(callId, counters.retries,
retryInvocationHandler.asyncCallHandler); retryInvocationHandler.asyncCallHandler);
} }

View File

@ -47,6 +47,8 @@ public final class CallerContext {
// field names // field names
public static final String CLIENT_IP_STR = "clientIp"; public static final String CLIENT_IP_STR = "clientIp";
public static final String CLIENT_PORT_STR = "clientPort"; public static final String CLIENT_PORT_STR = "clientPort";
public static final String CLIENT_ID_STR = "clientId";
public static final String CLIENT_CALL_ID_STR = "clientCallId";
/** The caller context. /** The caller context.
* *

View File

@ -55,14 +55,14 @@ public static class CacheEntry implements LightWeightCache.Entry {
/** /**
* Processing state of the requests. * Processing state of the requests.
*/ */
private static byte INPROGRESS = 0; private static final byte INPROGRESS = 0;
private static byte SUCCESS = 1; private static final byte SUCCESS = 1;
private static byte FAILED = 2; private static final byte FAILED = 2;
private byte state = INPROGRESS; private byte state = INPROGRESS;
// Store uuid as two long for better memory utilization // Store uuid as two long for better memory utilization
private final long clientIdMsb; // Most signficant bytes private final long clientIdMsb; // Most significant bytes
private final long clientIdLsb; // Least significant bytes private final long clientIdLsb; // Least significant bytes
private final int callId; private final int callId;
@ -140,8 +140,8 @@ public long getExpirationTime() {
@Override @Override
public String toString() { public String toString() {
return (new UUID(this.clientIdMsb, this.clientIdLsb)).toString() + ":" return String.format("%s:%s:%s", new UUID(this.clientIdMsb, this.clientIdLsb),
+ this.callId + ":" + this.state; this.callId, this.state);
} }
} }
@ -183,7 +183,7 @@ public Object getPayload() {
private final LightWeightGSet<CacheEntry, CacheEntry> set; private final LightWeightGSet<CacheEntry, CacheEntry> set;
private final long expirationTime; private final long expirationTime;
private String cacheName; private final String cacheName;
private final ReentrantLock lock = new ReentrantLock(); private final ReentrantLock lock = new ReentrantLock();
@ -195,7 +195,7 @@ public Object getPayload() {
*/ */
public RetryCache(String cacheName, double percentage, long expirationTime) { public RetryCache(String cacheName, double percentage, long expirationTime) {
int capacity = LightWeightGSet.computeCapacity(percentage, cacheName); int capacity = LightWeightGSet.computeCapacity(percentage, cacheName);
capacity = capacity > MAX_CAPACITY ? capacity : MAX_CAPACITY; capacity = Math.max(capacity, MAX_CAPACITY);
this.set = new LightWeightCache<CacheEntry, CacheEntry>(capacity, capacity, this.set = new LightWeightCache<CacheEntry, CacheEntry>(capacity, capacity,
expirationTime, 0); expirationTime, 0);
this.expirationTime = expirationTime; this.expirationTime = expirationTime;
@ -203,11 +203,11 @@ public RetryCache(String cacheName, double percentage, long expirationTime) {
this.retryCacheMetrics = RetryCacheMetrics.create(this); this.retryCacheMetrics = RetryCacheMetrics.create(this);
} }
private static boolean skipRetryCache() { private static boolean skipRetryCache(byte[] clientId, int callId) {
// Do not track non RPC invocation or RPC requests with // Do not track non RPC invocation or RPC requests with
// invalid callId or clientId in retry cache // invalid callId or clientId in retry cache
return !Server.isRpcInvocation() || Server.getCallId() < 0 return !Server.isRpcInvocation() || callId < 0
|| Arrays.equals(Server.getClientId(), RpcConstants.DUMMY_CLIENT_ID); || Arrays.equals(clientId, RpcConstants.DUMMY_CLIENT_ID);
} }
public void lock() { public void lock() {
@ -332,43 +332,51 @@ public void addCacheEntryWithPayload(byte[] clientId, int callId,
retryCacheMetrics.incrCacheUpdated(); retryCacheMetrics.incrCacheUpdated();
} }
private static CacheEntry newEntry(long expirationTime) { private static CacheEntry newEntry(long expirationTime,
return new CacheEntry(Server.getClientId(), Server.getCallId(), byte[] clientId, int callId) {
return new CacheEntry(clientId, callId,
System.nanoTime() + expirationTime); System.nanoTime() + expirationTime);
} }
private static CacheEntryWithPayload newEntry(Object payload, private static CacheEntryWithPayload newEntry(Object payload,
long expirationTime) { long expirationTime, byte[] clientId, int callId) {
return new CacheEntryWithPayload(Server.getClientId(), Server.getCallId(), return new CacheEntryWithPayload(clientId, callId,
payload, System.nanoTime() + expirationTime); payload, System.nanoTime() + expirationTime);
} }
/** /**
* Static method that provides null check for retryCache. * Static method that provides null check for retryCache.
* @param cache input Cache. * @param cache input Cache.
* @param clientId client id of this request
* @param callId client call id of this request
* @return CacheEntry. * @return CacheEntry.
*/ */
public static CacheEntry waitForCompletion(RetryCache cache) { public static CacheEntry waitForCompletion(RetryCache cache,
if (skipRetryCache()) { byte[] clientId, int callId) {
if (skipRetryCache(clientId, callId)) {
return null; return null;
} }
return cache != null ? cache return cache != null ? cache
.waitForCompletion(newEntry(cache.expirationTime)) : null; .waitForCompletion(newEntry(cache.expirationTime,
clientId, callId)) : null;
} }
/** /**
* Static method that provides null check for retryCache. * Static method that provides null check for retryCache.
* @param cache input cache. * @param cache input cache.
* @param payload input payload. * @param payload input payload.
* @param clientId client id of this request
* @param callId client call id of this request
* @return CacheEntryWithPayload. * @return CacheEntryWithPayload.
*/ */
public static CacheEntryWithPayload waitForCompletion(RetryCache cache, public static CacheEntryWithPayload waitForCompletion(RetryCache cache,
Object payload) { Object payload, byte[] clientId, int callId) {
if (skipRetryCache()) { if (skipRetryCache(clientId, callId)) {
return null; return null;
} }
return (CacheEntryWithPayload) (cache != null ? cache return (CacheEntryWithPayload) (cache != null ? cache
.waitForCompletion(newEntry(payload, cache.expirationTime)) : null); .waitForCompletion(newEntry(payload, cache.expirationTime,
clientId, callId)) : null);
} }
public static void setState(CacheEntry e, boolean success) { public static void setState(CacheEntry e, boolean success) {

View File

@ -50,14 +50,14 @@ public void setup() {
static class TestServer { static class TestServer {
AtomicInteger retryCount = new AtomicInteger(); AtomicInteger retryCount = new AtomicInteger();
AtomicInteger operationCount = new AtomicInteger(); AtomicInteger operationCount = new AtomicInteger();
private RetryCache retryCache = new RetryCache("TestRetryCache", 1, private final RetryCache retryCache = new RetryCache(
100 * 1000 * 1000 * 1000L); "TestRetryCache", 1, 100 * 1000 * 1000 * 1000L);
/** /**
* A server method implemented using {@link RetryCache}. * A server method implemented using {@link RetryCache}.
* *
* @param input is returned back in echo, if {@code success} is true. * @param input is returned back in echo, if {@code success} is true.
* @param failureOuput returned on failure, if {@code success} is false. * @param failureOutput returned on failure, if {@code success} is false.
* @param methodTime time taken by the operation. By passing smaller/larger * @param methodTime time taken by the operation. By passing smaller/larger
* value one can simulate an operation that takes short/long time. * value one can simulate an operation that takes short/long time.
* @param success whether this operation completes successfully or not * @param success whether this operation completes successfully or not
@ -67,7 +67,7 @@ static class TestServer {
int echo(int input, int failureOutput, long methodTime, boolean success) int echo(int input, int failureOutput, long methodTime, boolean success)
throws InterruptedException { throws InterruptedException {
CacheEntryWithPayload entry = RetryCache.waitForCompletion(retryCache, CacheEntryWithPayload entry = RetryCache.waitForCompletion(retryCache,
null); null, Server.getClientId(), Server.getCallId());
if (entry != null && entry.isSuccess()) { if (entry != null && entry.isSuccess()) {
System.out.println("retryCount incremented " + retryCount.get()); System.out.println("retryCount incremented " + retryCount.get());
retryCount.incrementAndGet(); retryCount.incrementAndGet();
@ -173,16 +173,13 @@ public void testOperations(final int input, final int numberOfThreads,
final int failureOutput = input + 1; final int failureOutput = input + 1;
ExecutorService executorService = Executors ExecutorService executorService = Executors
.newFixedThreadPool(numberOfThreads); .newFixedThreadPool(numberOfThreads);
List<Future<Integer>> list = new ArrayList<Future<Integer>>(); List<Future<Integer>> list = new ArrayList<>();
for (int i = 0; i < numberOfThreads; i++) { for (int i = 0; i < numberOfThreads; i++) {
Callable<Integer> worker = new Callable<Integer>() { Callable<Integer> worker = () -> {
@Override
public Integer call() throws Exception {
Server.getCurCall().set(call); Server.getCurCall().set(call);
Assert.assertEquals(Server.getCurCall().get(), call); Assert.assertEquals(Server.getCurCall().get(), call);
int randomPause = pause == 0 ? pause : r.nextInt(pause); int randomPause = pause == 0 ? pause : r.nextInt(pause);
return testServer.echo(input, failureOutput, randomPause, success); return testServer.echo(input, failureOutput, randomPause, success);
}
}; };
Future<Integer> submit = executorService.submit(worker); Future<Integer> submit = executorService.submit(worker);
list.add(submit); list.add(submit);

View File

@ -80,6 +80,7 @@
import org.apache.hadoop.ipc.StandbyException; import org.apache.hadoop.ipc.StandbyException;
import org.apache.hadoop.net.ConnectTimeoutException; import org.apache.hadoop.net.ConnectTimeoutException;
import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.util.StringUtils;
import org.eclipse.jetty.util.ajax.JSON; import org.eclipse.jetty.util.ajax.JSON;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -464,7 +465,7 @@ public Object invokeMethod(
+ router.getRouterId()); + router.getRouterId());
} }
addClientIpToCallerContext(); addClientInfoToCallerContext();
Object ret = null; Object ret = null;
if (rpcMonitor != null) { if (rpcMonitor != null) {
@ -584,12 +585,13 @@ public Object invokeMethod(
} }
/** /**
* For tracking which is the actual client address. * For tracking some information about the actual client.
* It adds trace info "clientIp:ip" and "clientPort:port" * It adds trace info "clientIp:ip", "clientPort:port",
* "clientId:id" and "clientCallId:callId"
* in the caller context, removing the old values if they were * in the caller context, removing the old values if they were
* already present. * already present.
*/ */
private void addClientIpToCallerContext() { private void addClientInfoToCallerContext() {
CallerContext ctx = CallerContext.getCurrent(); CallerContext ctx = CallerContext.getCurrent();
String origContext = ctx == null ? null : ctx.getContext(); String origContext = ctx == null ? null : ctx.getContext();
byte[] origSignature = ctx == null ? null : ctx.getSignature(); byte[] origSignature = ctx == null ? null : ctx.getSignature();
@ -598,6 +600,10 @@ private void addClientIpToCallerContext() {
.append(CallerContext.CLIENT_IP_STR, Server.getRemoteAddress()) .append(CallerContext.CLIENT_IP_STR, Server.getRemoteAddress())
.append(CallerContext.CLIENT_PORT_STR, .append(CallerContext.CLIENT_PORT_STR,
Integer.toString(Server.getRemotePort())) Integer.toString(Server.getRemotePort()))
.append(CallerContext.CLIENT_ID_STR,
StringUtils.byteToHexString(Server.getClientId()))
.append(CallerContext.CLIENT_CALL_ID_STR,
Integer.toString(Server.getCallId()))
.setSignature(origSignature); .setSignature(origSignature);
// Append the original caller context // Append the original caller context
if (origContext != null) { if (origContext != null) {

View File

@ -0,0 +1,144 @@
/**
* 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.hdfs.server.federation.router;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.ha.HAServiceProtocol;
import org.apache.hadoop.hdfs.server.federation.MiniRouterDFSCluster;
import org.apache.hadoop.hdfs.server.namenode.NameNode;
import org.apache.hadoop.io.retry.RetryInvocationHandler;
import org.apache.hadoop.ipc.Client;
import org.apache.hadoop.security.UserGroupInformation;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.security.PrivilegedExceptionAction;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_IP_PROXY_USERS;
import static org.apache.hadoop.hdfs.server.federation.FederationTestUtils.NAMENODES;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class TestRouterRetryCache {
/** Federated HDFS cluster. */
private MiniRouterDFSCluster cluster;
@Before
public void setup() throws Exception {
Configuration namenodeConf = new Configuration();
namenodeConf.set(DFS_NAMENODE_IP_PROXY_USERS, "fake_joe");
cluster = new MiniRouterDFSCluster(true, 1);
cluster.addNamenodeOverrides(namenodeConf);
// Start NNs and DNs and wait until ready
cluster.startCluster();
// Start routers with only an RPC service
cluster.startRouters();
// Register and verify all NNs with all routers
cluster.registerNamenodes();
cluster.waitNamenodeRegistration();
// Setup the mount table
cluster.installMockLocations();
// Making one Namenodes active per nameservice
if (cluster.isHighAvailability()) {
for (String ns : cluster.getNameservices()) {
cluster.switchToActive(ns, NAMENODES[0]);
cluster.switchToStandby(ns, NAMENODES[1]);
}
}
cluster.waitActiveNamespaces();
}
@After
public void teardown() throws IOException {
if (cluster != null) {
cluster.shutdown();
cluster = null;
}
}
@Test
public void testRetryCache() throws Exception {
RetryInvocationHandler.SET_CALL_ID_FOR_TEST.set(false);
FileSystem routerFS = cluster.getRandomRouter().getFileSystem();
Path testDir = new Path("/target-ns0/testdir");
routerFS.mkdirs(testDir);
routerFS.setPermission(testDir, FsPermission.getDefault());
// Run as fake joe to authorize the test
UserGroupInformation joe =
UserGroupInformation.createUserForTesting("fake_joe",
new String[]{"fake_group"});
FileSystem joeFS = joe.doAs(
(PrivilegedExceptionAction<FileSystem>) () ->
FileSystem.newInstance(routerFS.getUri(), routerFS.getConf()));
Path renameSrc = new Path(testDir, "renameSrc");
Path renameDst = new Path(testDir, "renameDst");
joeFS.mkdirs(renameSrc);
assertEquals(HAServiceProtocol.HAServiceState.ACTIVE,
cluster.getCluster().getNamesystem(0).getState());
int callId = Client.nextCallId();
Client.setCallIdAndRetryCount(callId, 0, null);
assertTrue(joeFS.rename(renameSrc, renameDst));
Client.setCallIdAndRetryCount(callId, 0, null);
assertTrue(joeFS.rename(renameSrc, renameDst));
String ns0 = cluster.getNameservices().get(0);
cluster.switchToStandby(ns0, NAMENODES[0]);
cluster.switchToActive(ns0, NAMENODES[1]);
assertEquals(HAServiceProtocol.HAServiceState.ACTIVE,
cluster.getCluster().getNamesystem(1).getState());
Client.setCallIdAndRetryCount(callId, 0, null);
assertTrue(joeFS.rename(renameSrc, renameDst));
}
@Test
public void testParseSpecialValue() {
String mockContent = "mockContent,clientIp:127.0.0.1," +
"clientCallId:12345,clientId:mockClientId";
String clientIp = NameNode.parseSpecialValue(mockContent, "clientIp:");
assertEquals("127.0.0.1", clientIp);
String clientCallId = NameNode.parseSpecialValue(
mockContent, "clientCallId:");
assertEquals("12345", clientCallId);
String clientId = NameNode.parseSpecialValue(mockContent, "clientId:");
assertEquals("mockClientId", clientId);
String clientRetryNum = NameNode.parseSpecialValue(
mockContent, "clientRetryNum:");
assertNull(clientRetryNum);
}
}

View File

@ -2053,6 +2053,8 @@ public void testMkdirsWithCallerContext() throws IOException {
final String logOutput = auditlog.getOutput(); final String logOutput = auditlog.getOutput();
assertTrue(logOutput.contains("callerContext=clientIp:")); assertTrue(logOutput.contains("callerContext=clientIp:"));
assertTrue(logOutput.contains(",clientContext")); assertTrue(logOutput.contains(",clientContext"));
assertTrue(logOutput.contains(",clientId"));
assertTrue(logOutput.contains(",clientCallId"));
assertTrue(verifyFileExists(routerFS, dirPath)); assertTrue(verifyFileExists(routerFS, dirPath));
} }
@ -2103,6 +2105,41 @@ public void testAddClientIpPortToCallerContext() throws IOException {
assertFalse(auditLog.getOutput().contains("clientPort:1234")); assertFalse(auditLog.getOutput().contains("clientPort:1234"));
} }
@Test
public void testAddClientIdAndCallIdToCallerContext() throws IOException {
GenericTestUtils.LogCapturer auditLog =
GenericTestUtils.LogCapturer.captureLogs(FSNamesystem.auditLog);
// 1. ClientId and ClientCallId are not set on the client.
// Set client context.
CallerContext.setCurrent(
new CallerContext.Builder("clientContext").build());
// Create a directory via the router.
String dirPath = "/test";
routerProtocol.mkdirs(dirPath, new FsPermission("755"), false);
// The audit log should contains "clientId:" and "clientCallId:".
assertTrue(auditLog.getOutput().contains("clientId:"));
assertTrue(auditLog.getOutput().contains("clientCallId:"));
assertTrue(verifyFileExists(routerFS, dirPath));
auditLog.clearOutput();
// 2. ClientId and ClientCallId are set on the client.
// Reset client context.
CallerContext.setCurrent(
new CallerContext.Builder(
"clientContext,clientId:mockClientId,clientCallId:4321").build());
// Create a directory via the router.
routerProtocol.getFileInfo(dirPath);
// The audit log should not contain the original clientId and clientCallId
// set by client.
assertFalse(auditLog.getOutput().contains("clientId:mockClientId"));
assertFalse(auditLog.getOutput().contains("clientCallId:4321"));
}
@Test @Test
public void testContentSummaryWithSnapshot() throws Exception { public void testContentSummaryWithSnapshot() throws Exception {
DistributedFileSystem routerDFS = (DistributedFileSystem) routerFS; DistributedFileSystem routerDFS = (DistributedFileSystem) routerFS;

View File

@ -17,6 +17,7 @@
*/ */
package org.apache.hadoop.hdfs.server.namenode; package org.apache.hadoop.hdfs.server.namenode;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_IP_PROXY_USERS;
import static org.apache.hadoop.util.ExitUtil.terminate; import static org.apache.hadoop.util.ExitUtil.terminate;
import static org.apache.hadoop.util.Time.monotonicNow; import static org.apache.hadoop.util.Time.monotonicNow;
@ -30,6 +31,7 @@
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.LongAdder; import java.util.concurrent.atomic.LongAdder;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
@ -107,7 +109,6 @@
import org.apache.hadoop.hdfs.server.protocol.NamespaceInfo; import org.apache.hadoop.hdfs.server.protocol.NamespaceInfo;
import org.apache.hadoop.hdfs.server.protocol.RemoteEditLogManifest; import org.apache.hadoop.hdfs.server.protocol.RemoteEditLogManifest;
import org.apache.hadoop.io.IOUtils; import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.ipc.Server;
import org.apache.hadoop.security.token.delegation.DelegationKey; import org.apache.hadoop.security.token.delegation.DelegationKey;
import org.apache.hadoop.util.Lists; import org.apache.hadoop.util.Lists;
@ -195,6 +196,9 @@ private enum State {
protected final OpInstanceCache cache = new OpInstanceCache(); protected final OpInstanceCache cache = new OpInstanceCache();
// Users who can override the client ip
private final String[] ipProxyUsers;
/** /**
* The edit directories that are shared between primary and secondary. * The edit directories that are shared between primary and secondary.
*/ */
@ -246,6 +250,7 @@ static FSEditLog newInstance(Configuration conf, NNStorage storage,
* @param editsDirs List of journals to use * @param editsDirs List of journals to use
*/ */
FSEditLog(Configuration conf, NNStorage storage, List<URI> editsDirs) { FSEditLog(Configuration conf, NNStorage storage, List<URI> editsDirs) {
ipProxyUsers = conf.getStrings(DFS_NAMENODE_IP_PROXY_USERS);
isSyncRunning = false; isSyncRunning = false;
this.conf = conf; this.conf = conf;
this.storage = storage; this.storage = storage;
@ -799,8 +804,10 @@ private void printStatistics(boolean force) {
/** Record the RPC IDs if necessary */ /** Record the RPC IDs if necessary */
private void logRpcIds(FSEditLogOp op, boolean toLogRpcIds) { private void logRpcIds(FSEditLogOp op, boolean toLogRpcIds) {
if (toLogRpcIds) { if (toLogRpcIds) {
op.setRpcClientId(Server.getClientId()); Pair<byte[], Integer> clientIdAndCallId =
op.setRpcCallId(Server.getCallId()); NameNode.getClientIdAndCallId(this.ipProxyUsers);
op.setRpcClientId(clientIdAndCallId.getLeft());
op.setRpcCallId(clientIdAndCallId.getRight());
} }
} }

View File

@ -17,7 +17,10 @@
*/ */
package org.apache.hadoop.hdfs.server.namenode; package org.apache.hadoop.hdfs.server.namenode;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.hadoop.classification.VisibleForTesting; import org.apache.hadoop.classification.VisibleForTesting;
import org.apache.hadoop.ipc.CallerContext;
import org.apache.hadoop.thirdparty.com.google.common.base.Joiner; import org.apache.hadoop.thirdparty.com.google.common.base.Joiner;
import org.apache.hadoop.util.Preconditions; import org.apache.hadoop.util.Preconditions;
@ -494,6 +497,94 @@ public static NameNodeMetrics getNameNodeMetrics() {
return metrics; return metrics;
} }
/**
* Try to obtain the actual client info according to the current user.
* @param ipProxyUsers Users who can override client infos
*/
private static String clientInfoFromContext(
final String[] ipProxyUsers) {
if (ipProxyUsers != null) {
UserGroupInformation user =
UserGroupInformation.getRealUserOrSelf(Server.getRemoteUser());
if (user != null &&
ArrayUtils.contains(ipProxyUsers, user.getShortUserName())) {
CallerContext context = CallerContext.getCurrent();
if (context != null && context.isContextValid()) {
return context.getContext();
}
}
}
return null;
}
/**
* Try to obtain the value corresponding to the key by parsing the content.
* @param content the full content to be parsed.
* @param key trying to obtain the value of the key.
* @return the value corresponding to the key.
*/
@VisibleForTesting
public static String parseSpecialValue(String content, String key) {
int posn = content.indexOf(key);
if (posn != -1) {
posn += key.length();
int end = content.indexOf(",", posn);
return end == -1 ? content.substring(posn)
: content.substring(posn, end);
}
return null;
}
/**
* Try to obtain the actual client's machine according to the current user.
* @param ipProxyUsers Users who can override client infos.
* @return The actual client's machine.
*/
public static String getClientMachine(final String[] ipProxyUsers) {
String cc = clientInfoFromContext(ipProxyUsers);
if (cc != null) {
// if the rpc has a caller context of "clientIp:1.2.3.4,CLI",
// return "1.2.3.4" as the client machine.
String key = CallerContext.CLIENT_IP_STR +
CallerContext.Builder.KEY_VALUE_SEPARATOR;
return parseSpecialValue(cc, key);
}
String clientMachine = Server.getRemoteAddress();
if (clientMachine == null) { //not a RPC client
clientMachine = "";
}
return clientMachine;
}
/**
* Try to obtain the actual client's id and call id
* according to the current user.
* @param ipProxyUsers Users who can override client infos
* @return The actual client's id and call id.
*/
public static Pair<byte[], Integer> getClientIdAndCallId(
final String[] ipProxyUsers) {
byte[] clientId = Server.getClientId();
int callId = Server.getCallId();
String cc = clientInfoFromContext(ipProxyUsers);
if (cc != null) {
String clientIdKey = CallerContext.CLIENT_ID_STR +
CallerContext.Builder.KEY_VALUE_SEPARATOR;
String clientIdStr = parseSpecialValue(cc, clientIdKey);
if (clientIdStr != null) {
clientId = StringUtils.hexStringToByte(clientIdStr);
}
String callIdKey = CallerContext.CLIENT_CALL_ID_STR +
CallerContext.Builder.KEY_VALUE_SEPARATOR;
String callIdStr = parseSpecialValue(cc, callIdKey);
if (callIdStr != null) {
callId = Integer.parseInt(callIdStr);
}
}
return Pair.of(clientId, callId);
}
/** /**
* Returns object used for reporting namenode startup progress. * Returns object used for reporting namenode startup progress.
* *

View File

@ -46,8 +46,7 @@
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.tuple.Pair;
import org.apache.hadoop.ipc.CallerContext;
import org.apache.hadoop.HadoopIllegalArgumentException; import org.apache.hadoop.HadoopIllegalArgumentException;
import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceAudience;
@ -271,7 +270,7 @@ public class NameNodeRpcServer implements NamenodeProtocols {
private final String defaultECPolicyName; private final String defaultECPolicyName;
// Users who can override the client ip // Users who can override the client info
private final String[] ipProxyUsers; private final String[] ipProxyUsers;
public NameNodeRpcServer(Configuration conf, NameNode nn) public NameNodeRpcServer(Configuration conf, NameNode nn)
@ -711,8 +710,7 @@ public NamenodeCommand startCheckpoint(NamenodeRegistration registration)
if(!nn.isRole(NamenodeRole.NAMENODE)) if(!nn.isRole(NamenodeRole.NAMENODE))
throw new IOException("Only an ACTIVE node can invoke startCheckpoint."); throw new IOException("Only an ACTIVE node can invoke startCheckpoint.");
CacheEntryWithPayload cacheEntry = RetryCache.waitForCompletion(retryCache, CacheEntryWithPayload cacheEntry = getCacheEntryWithPayload(null);
null);
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return (NamenodeCommand) cacheEntry.getPayload(); return (NamenodeCommand) cacheEntry.getPayload();
} }
@ -725,13 +723,33 @@ public NamenodeCommand startCheckpoint(NamenodeRegistration registration)
return ret; return ret;
} }
/**
* Return the current CacheEntry.
*/
private CacheEntry getCacheEntry() {
Pair<byte[], Integer> clientInfo =
NameNode.getClientIdAndCallId(this.ipProxyUsers);
return RetryCache.waitForCompletion(
retryCache, clientInfo.getLeft(), clientInfo.getRight());
}
/**
* Return the current CacheEntryWithPayload.
*/
private CacheEntryWithPayload getCacheEntryWithPayload(Object payload) {
Pair<byte[], Integer> clientInfo =
NameNode.getClientIdAndCallId(this.ipProxyUsers);
return RetryCache.waitForCompletion(retryCache, payload,
clientInfo.getLeft(), clientInfo.getRight());
}
@Override // NamenodeProtocol @Override // NamenodeProtocol
public void endCheckpoint(NamenodeRegistration registration, public void endCheckpoint(NamenodeRegistration registration,
CheckpointSignature sig) throws IOException { CheckpointSignature sig) throws IOException {
String operationName = "endCheckpoint"; String operationName = "endCheckpoint";
checkNNStartup(); checkNNStartup();
namesystem.checkSuperuserPrivilege(operationName); namesystem.checkSuperuserPrivilege(operationName);
CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; // Return previous response return; // Return previous response
} }
@ -801,7 +819,7 @@ public HdfsFileStatus create(String src, FsPermission masked,
+ MAX_PATH_LENGTH + " characters, " + MAX_PATH_DEPTH + " levels."); + MAX_PATH_LENGTH + " characters, " + MAX_PATH_DEPTH + " levels.");
} }
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
CacheEntryWithPayload cacheEntry = RetryCache.waitForCompletion(retryCache, null); CacheEntryWithPayload cacheEntry = getCacheEntryWithPayload(null);
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return (HdfsFileStatus) cacheEntry.getPayload(); return (HdfsFileStatus) cacheEntry.getPayload();
} }
@ -832,8 +850,7 @@ public LastBlockWithStatus append(String src, String clientName,
+src+" for "+clientName+" at "+clientMachine); +src+" for "+clientName+" at "+clientMachine);
} }
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
CacheEntryWithPayload cacheEntry = RetryCache.waitForCompletion(retryCache, CacheEntryWithPayload cacheEntry = getCacheEntryWithPayload(null);
null);
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return (LastBlockWithStatus) cacheEntry.getPayload(); return (LastBlockWithStatus) cacheEntry.getPayload();
} }
@ -999,7 +1016,7 @@ public void updatePipeline(String clientName, ExtendedBlock oldBlock,
throws IOException { throws IOException {
checkNNStartup(); checkNNStartup();
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; // Return previous response return; // Return previous response
} }
@ -1044,7 +1061,7 @@ public boolean rename(String src, String dst) throws IOException {
+ MAX_PATH_LENGTH + " characters, " + MAX_PATH_DEPTH + " levels."); + MAX_PATH_LENGTH + " characters, " + MAX_PATH_DEPTH + " levels.");
} }
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return true; // Return previous response return true; // Return previous response
} }
@ -1067,7 +1084,7 @@ public void concat(String trg, String[] src) throws IOException {
stateChangeLog.debug("*DIR* NameNode.concat: src path {} to" + stateChangeLog.debug("*DIR* NameNode.concat: src path {} to" +
" target path {}", Arrays.toString(src), trg); " target path {}", Arrays.toString(src), trg);
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; // Return previous response return; // Return previous response
} }
@ -1093,7 +1110,7 @@ public void rename2(String src, String dst, Options.Rename... options)
+ MAX_PATH_LENGTH + " characters, " + MAX_PATH_DEPTH + " levels."); + MAX_PATH_LENGTH + " characters, " + MAX_PATH_DEPTH + " levels.");
} }
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; // Return previous response return; // Return previous response
} }
@ -1130,7 +1147,7 @@ public boolean delete(String src, boolean recursive) throws IOException {
+ ", recursive=" + recursive); + ", recursive=" + recursive);
} }
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return true; // Return previous response return true; // Return previous response
} }
@ -1315,7 +1332,7 @@ public boolean restoreFailedStorage(String arg) throws IOException {
@Override // ClientProtocol @Override // ClientProtocol
public boolean saveNamespace(long timeWindow, long txGap) throws IOException { public boolean saveNamespace(long timeWindow, long txGap) throws IOException {
checkNNStartup(); checkNNStartup();
CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return true; // Return previous response return true; // Return previous response
} }
@ -1503,7 +1520,7 @@ public QuotaUsage getQuotaUsage(String path) throws IOException {
public void satisfyStoragePolicy(String src) throws IOException { public void satisfyStoragePolicy(String src) throws IOException {
checkNNStartup(); checkNNStartup();
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; // Return previous response return; // Return previous response
} }
@ -1550,7 +1567,7 @@ public void createSymlink(String target, String link, FsPermission dirPerms,
boolean createParent) throws IOException { boolean createParent) throws IOException {
checkNNStartup(); checkNNStartup();
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; // Return previous response return; // Return previous response
} }
@ -1920,34 +1937,11 @@ private void verifySoftwareVersion(DatanodeRegistration dnReg)
} }
} }
/**
* Get the actual client's machine.
*/
private String getClientMachine() { private String getClientMachine() {
if (ipProxyUsers != null) { return NameNode.getClientMachine(this.ipProxyUsers);
// Get the real user (or effective if it isn't a proxy user)
UserGroupInformation user =
UserGroupInformation.getRealUserOrSelf(Server.getRemoteUser());
if (user != null &&
ArrayUtils.contains(ipProxyUsers, user.getShortUserName())) {
CallerContext context = CallerContext.getCurrent();
if (context != null && context.isContextValid()) {
String cc = context.getContext();
// if the rpc has a caller context of "clientIp:1.2.3.4,CLI",
// return "1.2.3.4" as the client machine.
String key = CallerContext.CLIENT_IP_STR +
CallerContext.Builder.KEY_VALUE_SEPARATOR;
int posn = cc.indexOf(key);
if (posn != -1) {
posn += key.length();
int end = cc.indexOf(",", posn);
return end == -1 ? cc.substring(posn) : cc.substring(posn, end);
}
}
}
}
String clientMachine = Server.getRemoteAddress();
if (clientMachine == null) { //not a RPC client
clientMachine = "";
}
return clientMachine;
} }
@Override @Override
@ -1967,8 +1961,7 @@ public String createSnapshot(String snapshotRoot, String snapshotName)
+ MAX_PATH_LENGTH + " characters, " + MAX_PATH_DEPTH + " levels."); + MAX_PATH_LENGTH + " characters, " + MAX_PATH_DEPTH + " levels.");
} }
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
CacheEntryWithPayload cacheEntry = RetryCache.waitForCompletion(retryCache, CacheEntryWithPayload cacheEntry = getCacheEntryWithPayload(null);
null);
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return (String) cacheEntry.getPayload(); return (String) cacheEntry.getPayload();
} }
@ -1995,7 +1988,7 @@ public void deleteSnapshot(String snapshotRoot, String snapshotName)
} }
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
metrics.incrDeleteSnapshotOps(); metrics.incrDeleteSnapshotOps();
CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; // Return previous response return; // Return previous response
} }
@ -2037,7 +2030,7 @@ public void renameSnapshot(String snapshotRoot, String snapshotOldName,
} }
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
metrics.incrRenameSnapshotOps(); metrics.incrRenameSnapshotOps();
CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; // Return previous response return; // Return previous response
} }
@ -2098,8 +2091,7 @@ public long addCacheDirective(
CacheDirectiveInfo path, EnumSet<CacheFlag> flags) throws IOException { CacheDirectiveInfo path, EnumSet<CacheFlag> flags) throws IOException {
checkNNStartup(); checkNNStartup();
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
CacheEntryWithPayload cacheEntry = RetryCache.waitForCompletion CacheEntryWithPayload cacheEntry = getCacheEntryWithPayload(null);
(retryCache, null);
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return (Long) cacheEntry.getPayload(); return (Long) cacheEntry.getPayload();
} }
@ -2120,7 +2112,7 @@ public void modifyCacheDirective(
CacheDirectiveInfo directive, EnumSet<CacheFlag> flags) throws IOException { CacheDirectiveInfo directive, EnumSet<CacheFlag> flags) throws IOException {
checkNNStartup(); checkNNStartup();
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; return;
} }
@ -2138,7 +2130,7 @@ public void modifyCacheDirective(
public void removeCacheDirective(long id) throws IOException { public void removeCacheDirective(long id) throws IOException {
checkNNStartup(); checkNNStartup();
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; return;
} }
@ -2165,7 +2157,7 @@ public BatchedEntries<CacheDirectiveEntry> listCacheDirectives(long prevId,
public void addCachePool(CachePoolInfo info) throws IOException { public void addCachePool(CachePoolInfo info) throws IOException {
checkNNStartup(); checkNNStartup();
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; // Return previous response return; // Return previous response
} }
@ -2182,7 +2174,7 @@ public void addCachePool(CachePoolInfo info) throws IOException {
public void modifyCachePool(CachePoolInfo info) throws IOException { public void modifyCachePool(CachePoolInfo info) throws IOException {
checkNNStartup(); checkNNStartup();
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; // Return previous response return; // Return previous response
} }
@ -2199,7 +2191,7 @@ public void modifyCachePool(CachePoolInfo info) throws IOException {
public void removeCachePool(String cachePoolName) throws IOException { public void removeCachePool(String cachePoolName) throws IOException {
checkNNStartup(); checkNNStartup();
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; return;
} }
@ -2262,7 +2254,7 @@ public void createEncryptionZone(String src, String keyName)
throws IOException { throws IOException {
checkNNStartup(); checkNNStartup();
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
final CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); final CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; return;
} }
@ -2294,7 +2286,7 @@ public void reencryptEncryptionZone(final String zone,
final ReencryptAction action) throws IOException { final ReencryptAction action) throws IOException {
checkNNStartup(); checkNNStartup();
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
final CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); final CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; return;
} }
@ -2318,7 +2310,7 @@ public BatchedEntries<ZoneReencryptionStatus> listReencryptionStatus(
public void setErasureCodingPolicy(String src, String ecPolicyName) public void setErasureCodingPolicy(String src, String ecPolicyName)
throws IOException { throws IOException {
checkNNStartup(); checkNNStartup();
final CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); final CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; return;
} }
@ -2342,7 +2334,7 @@ public void setXAttr(String src, XAttr xAttr, EnumSet<XAttrSetFlag> flag)
throws IOException { throws IOException {
checkNNStartup(); checkNNStartup();
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; // Return previous response return; // Return previous response
} }
@ -2372,7 +2364,7 @@ public List<XAttr> listXAttrs(String src) throws IOException {
public void removeXAttr(String src, XAttr xAttr) throws IOException { public void removeXAttr(String src, XAttr xAttr) throws IOException {
checkNNStartup(); checkNNStartup();
namesystem.checkOperation(OperationCategory.WRITE); namesystem.checkOperation(OperationCategory.WRITE);
CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; // Return previous response return; // Return previous response
} }
@ -2555,7 +2547,7 @@ public ErasureCodingPolicy getErasureCodingPolicy(String src) throws IOException
@Override // ClientProtocol @Override // ClientProtocol
public void unsetErasureCodingPolicy(String src) throws IOException { public void unsetErasureCodingPolicy(String src) throws IOException {
checkNNStartup(); checkNNStartup();
final CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); final CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; return;
} }
@ -2581,8 +2573,7 @@ public AddErasureCodingPolicyResponse[] addErasureCodingPolicies(
String operationName = "addErasureCodingPolicies"; String operationName = "addErasureCodingPolicies";
checkNNStartup(); checkNNStartup();
namesystem.checkSuperuserPrivilege(operationName); namesystem.checkSuperuserPrivilege(operationName);
final CacheEntryWithPayload cacheEntry = final CacheEntryWithPayload cacheEntry = getCacheEntryWithPayload(null);
RetryCache.waitForCompletion(retryCache, null);
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return (AddErasureCodingPolicyResponse[]) cacheEntry.getPayload(); return (AddErasureCodingPolicyResponse[]) cacheEntry.getPayload();
} }
@ -2605,7 +2596,7 @@ public void removeErasureCodingPolicy(String ecPolicyName)
String operationName = "removeErasureCodingPolicy"; String operationName = "removeErasureCodingPolicy";
checkNNStartup(); checkNNStartup();
namesystem.checkSuperuserPrivilege(operationName); namesystem.checkSuperuserPrivilege(operationName);
final CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); final CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; return;
} }
@ -2624,7 +2615,7 @@ public void enableErasureCodingPolicy(String ecPolicyName)
String operationName = "enableErasureCodingPolicy"; String operationName = "enableErasureCodingPolicy";
checkNNStartup(); checkNNStartup();
namesystem.checkSuperuserPrivilege(operationName); namesystem.checkSuperuserPrivilege(operationName);
final CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); final CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; return;
} }
@ -2643,7 +2634,7 @@ public void disableErasureCodingPolicy(String ecPolicyName)
String operationName = "disableErasureCodingPolicy"; String operationName = "disableErasureCodingPolicy";
checkNNStartup(); checkNNStartup();
namesystem.checkSuperuserPrivilege(operationName); namesystem.checkSuperuserPrivilege(operationName);
final CacheEntry cacheEntry = RetryCache.waitForCompletion(retryCache); final CacheEntry cacheEntry = getCacheEntry();
if (cacheEntry != null && cacheEntry.isSuccess()) { if (cacheEntry != null && cacheEntry.isSuccess()) {
return; return;
} }