|
@@ -19,25 +19,26 @@ package org.apache.hadoop.hdfs.server.namenode;
|
|
|
|
|
|
import java.io.IOException;
|
|
|
import java.util.ArrayList;
|
|
|
+import java.util.Iterator;
|
|
|
import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
import java.util.SortedMap;
|
|
|
import java.util.TreeMap;
|
|
|
-import java.util.Map.Entry;
|
|
|
|
|
|
import org.apache.commons.logging.Log;
|
|
|
import org.apache.commons.logging.LogFactory;
|
|
|
import org.apache.hadoop.conf.Configuration;
|
|
|
-import org.apache.hadoop.hdfs.protocol.AlreadyBeingCreatedException;
|
|
|
+import org.apache.hadoop.fs.permission.FsAction;
|
|
|
+import org.apache.hadoop.hdfs.protocol.AddPathCacheDirectiveException.InvalidPoolError;
|
|
|
+import org.apache.hadoop.hdfs.protocol.AddPathCacheDirectiveException.PoolWritePermissionDeniedError;
|
|
|
+import org.apache.hadoop.hdfs.protocol.AddPathCacheDirectiveException.UnexpectedAddPathCacheDirectiveException;
|
|
|
import org.apache.hadoop.hdfs.protocol.CachePoolInfo;
|
|
|
import org.apache.hadoop.hdfs.protocol.PathCacheDirective;
|
|
|
import org.apache.hadoop.hdfs.protocol.PathCacheEntry;
|
|
|
-import org.apache.hadoop.hdfs.protocol.AddPathCacheDirectiveException.InvalidPoolNameError;
|
|
|
-import org.apache.hadoop.hdfs.protocol.AddPathCacheDirectiveException.UnexpectedAddPathCacheDirectiveException;
|
|
|
-import org.apache.hadoop.hdfs.protocol.AddPathCacheDirectiveException.PoolWritePermissionDeniedError;
|
|
|
import org.apache.hadoop.hdfs.protocol.RemovePathCacheEntryException.InvalidIdException;
|
|
|
import org.apache.hadoop.hdfs.protocol.RemovePathCacheEntryException.NoSuchIdException;
|
|
|
-import org.apache.hadoop.hdfs.protocol.RemovePathCacheEntryException.UnexpectedRemovePathCacheEntryException;
|
|
|
import org.apache.hadoop.hdfs.protocol.RemovePathCacheEntryException.RemovePermissionDeniedException;
|
|
|
+import org.apache.hadoop.hdfs.protocol.RemovePathCacheEntryException.UnexpectedRemovePathCacheEntryException;
|
|
|
import org.apache.hadoop.util.Fallible;
|
|
|
|
|
|
/**
|
|
@@ -64,14 +65,25 @@ final class CacheManager {
|
|
|
/**
|
|
|
* Cache pools, sorted by name.
|
|
|
*/
|
|
|
- private final TreeMap<String, CachePool> cachePools =
|
|
|
+ private final TreeMap<String, CachePool> cachePoolsByName =
|
|
|
new TreeMap<String, CachePool>();
|
|
|
|
|
|
+ /**
|
|
|
+ * Cache pools, sorted by ID
|
|
|
+ */
|
|
|
+ private final TreeMap<Long, CachePool> cachePoolsById =
|
|
|
+ new TreeMap<Long, CachePool>();
|
|
|
+
|
|
|
/**
|
|
|
* The entry ID to use for a new entry.
|
|
|
*/
|
|
|
private long nextEntryId;
|
|
|
|
|
|
+ /**
|
|
|
+ * The pool ID to use for a new pool.
|
|
|
+ */
|
|
|
+ private long nextPoolId;
|
|
|
+
|
|
|
CacheManager(FSDirectory dir, Configuration conf) {
|
|
|
// TODO: support loading and storing of the CacheManager state
|
|
|
clear();
|
|
@@ -80,26 +92,35 @@ final class CacheManager {
|
|
|
synchronized void clear() {
|
|
|
entriesById.clear();
|
|
|
entriesByDirective.clear();
|
|
|
+ cachePoolsByName.clear();
|
|
|
+ cachePoolsById.clear();
|
|
|
nextEntryId = 1;
|
|
|
+ nextPoolId = 1;
|
|
|
}
|
|
|
|
|
|
synchronized long getNextEntryId() throws IOException {
|
|
|
if (nextEntryId == Long.MAX_VALUE) {
|
|
|
- throw new IOException("no more available IDs");
|
|
|
+ throw new IOException("no more available entry IDs");
|
|
|
}
|
|
|
return nextEntryId++;
|
|
|
}
|
|
|
|
|
|
+ synchronized long getNextPoolId() throws IOException {
|
|
|
+ if (nextPoolId == Long.MAX_VALUE) {
|
|
|
+ throw new IOException("no more available pool IDs");
|
|
|
+ }
|
|
|
+ return nextPoolId++;
|
|
|
+ }
|
|
|
+
|
|
|
private synchronized Fallible<PathCacheEntry> addDirective(
|
|
|
- PathCacheDirective directive, FSPermissionChecker pc) {
|
|
|
- CachePool pool = cachePools.get(directive.getPool());
|
|
|
+ FSPermissionChecker pc, PathCacheDirective directive) {
|
|
|
+ CachePool pool = cachePoolsById.get(directive.getPoolId());
|
|
|
if (pool == null) {
|
|
|
LOG.info("addDirective " + directive + ": pool not found.");
|
|
|
return new Fallible<PathCacheEntry>(
|
|
|
- new InvalidPoolNameError(directive));
|
|
|
+ new InvalidPoolError(directive));
|
|
|
}
|
|
|
- if (!pc.checkWritePermission(pool.getOwnerName(),
|
|
|
- pool.getGroupName(), pool.getMode())) {
|
|
|
+ if (!pc.checkPermission(pool, FsAction.WRITE)) {
|
|
|
LOG.info("addDirective " + directive + ": write permission denied.");
|
|
|
return new Fallible<PathCacheEntry>(
|
|
|
new PoolWritePermissionDeniedError(directive));
|
|
@@ -134,17 +155,17 @@ final class CacheManager {
|
|
|
}
|
|
|
|
|
|
public synchronized List<Fallible<PathCacheEntry>> addDirectives(
|
|
|
- List<PathCacheDirective> directives, FSPermissionChecker pc) {
|
|
|
+ FSPermissionChecker pc, List<PathCacheDirective> directives) {
|
|
|
ArrayList<Fallible<PathCacheEntry>> results =
|
|
|
new ArrayList<Fallible<PathCacheEntry>>(directives.size());
|
|
|
for (PathCacheDirective directive: directives) {
|
|
|
- results.add(addDirective(directive, pc));
|
|
|
+ results.add(addDirective(pc, directive));
|
|
|
}
|
|
|
return results;
|
|
|
}
|
|
|
|
|
|
- private synchronized Fallible<Long> removeEntry(long entryId,
|
|
|
- FSPermissionChecker pc) {
|
|
|
+ private synchronized Fallible<Long> removeEntry(FSPermissionChecker pc,
|
|
|
+ long entryId) {
|
|
|
// Check for invalid IDs.
|
|
|
if (entryId <= 0) {
|
|
|
LOG.info("removeEntry " + entryId + ": invalid non-positive entry ID.");
|
|
@@ -156,23 +177,20 @@ final class CacheManager {
|
|
|
LOG.info("removeEntry " + entryId + ": entry not found.");
|
|
|
return new Fallible<Long>(new NoSuchIdException(entryId));
|
|
|
}
|
|
|
- CachePool pool = cachePools.get(existing.getDirective().getPool());
|
|
|
+ CachePool pool = cachePoolsById.get(existing.getDirective().getPoolId());
|
|
|
if (pool == null) {
|
|
|
LOG.info("removeEntry " + entryId + ": pool not found for directive " +
|
|
|
existing.getDirective());
|
|
|
return new Fallible<Long>(
|
|
|
new UnexpectedRemovePathCacheEntryException(entryId));
|
|
|
}
|
|
|
- if (!pc.isSuperUser()) {
|
|
|
- if (!pc.checkWritePermission(pool.getOwnerName(),
|
|
|
- pool.getGroupName(), pool.getMode())) {
|
|
|
- LOG.info("removeEntry " + entryId + ": write permission denied to " +
|
|
|
- "pool " + pool + " for entry " + existing);
|
|
|
- return new Fallible<Long>(
|
|
|
- new RemovePermissionDeniedException(entryId));
|
|
|
- }
|
|
|
+ if (!pc.checkPermission(pool, FsAction.WRITE)) {
|
|
|
+ LOG.info("removeEntry " + entryId + ": write permission denied to " +
|
|
|
+ "pool " + pool + " for entry " + existing);
|
|
|
+ return new Fallible<Long>(
|
|
|
+ new RemovePermissionDeniedException(entryId));
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// Remove the corresponding entry in entriesByDirective.
|
|
|
if (entriesByDirective.remove(existing.getDirective()) == null) {
|
|
|
LOG.warn("removeEntry " + entryId + ": failed to find existing entry " +
|
|
@@ -184,36 +202,43 @@ final class CacheManager {
|
|
|
return new Fallible<Long>(entryId);
|
|
|
}
|
|
|
|
|
|
- public synchronized List<Fallible<Long>> removeEntries(List<Long> entryIds,
|
|
|
- FSPermissionChecker pc) {
|
|
|
+ public synchronized List<Fallible<Long>> removeEntries(FSPermissionChecker pc,
|
|
|
+ List<Long> entryIds) {
|
|
|
ArrayList<Fallible<Long>> results =
|
|
|
new ArrayList<Fallible<Long>>(entryIds.size());
|
|
|
for (Long entryId : entryIds) {
|
|
|
- results.add(removeEntry(entryId, pc));
|
|
|
+ results.add(removeEntry(pc, entryId));
|
|
|
}
|
|
|
return results;
|
|
|
}
|
|
|
|
|
|
- public synchronized List<PathCacheEntry> listPathCacheEntries(long prevId,
|
|
|
- String pool, int maxReplies) {
|
|
|
+ public synchronized List<PathCacheEntry> listPathCacheEntries(
|
|
|
+ FSPermissionChecker pc, long prevId, Long poolId, int maxReplies) {
|
|
|
final int MAX_PRE_ALLOCATED_ENTRIES = 16;
|
|
|
- ArrayList<PathCacheEntry> replies =
|
|
|
- new ArrayList<PathCacheEntry>(Math.min(MAX_PRE_ALLOCATED_ENTRIES, maxReplies));
|
|
|
+ ArrayList<PathCacheEntry> replies = new ArrayList<PathCacheEntry>(
|
|
|
+ Math.min(MAX_PRE_ALLOCATED_ENTRIES, maxReplies));
|
|
|
int numReplies = 0;
|
|
|
SortedMap<Long, PathCacheEntry> tailMap = entriesById.tailMap(prevId + 1);
|
|
|
- for (Entry<Long, PathCacheEntry> cur : tailMap.entrySet()) {
|
|
|
+ for (PathCacheEntry entry : tailMap.values()) {
|
|
|
if (numReplies >= maxReplies) {
|
|
|
return replies;
|
|
|
}
|
|
|
- if (pool.isEmpty() || cur.getValue().getDirective().
|
|
|
- getPool().equals(pool)) {
|
|
|
- replies.add(cur.getValue());
|
|
|
- numReplies++;
|
|
|
+ long entryPoolId = entry.getDirective().getPoolId();
|
|
|
+ if (poolId == null || poolId <= 0 || entryPoolId == poolId) {
|
|
|
+ if (pc.checkPermission(
|
|
|
+ cachePoolsById.get(entryPoolId), FsAction.EXECUTE)) {
|
|
|
+ replies.add(entry);
|
|
|
+ numReplies++;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
return replies;
|
|
|
}
|
|
|
|
|
|
+ synchronized CachePool getCachePool(long id) {
|
|
|
+ return cachePoolsById.get(id);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Create a cache pool.
|
|
|
*
|
|
@@ -221,22 +246,24 @@ final class CacheManager {
|
|
|
*
|
|
|
* @param info
|
|
|
* The info for the cache pool to create.
|
|
|
+ * @return created CachePool
|
|
|
*/
|
|
|
- public synchronized void addCachePool(CachePoolInfo info)
|
|
|
+ public synchronized CachePool addCachePool(CachePoolInfo info)
|
|
|
throws IOException {
|
|
|
String poolName = info.getPoolName();
|
|
|
- if (poolName.isEmpty()) {
|
|
|
+ if (poolName == null || poolName.isEmpty()) {
|
|
|
throw new IOException("invalid empty cache pool name");
|
|
|
}
|
|
|
- CachePool pool = cachePools.get(poolName);
|
|
|
- if (pool != null) {
|
|
|
+ if (cachePoolsByName.containsKey(poolName)) {
|
|
|
throw new IOException("cache pool " + poolName + " already exists.");
|
|
|
}
|
|
|
- CachePool cachePool = new CachePool(poolName,
|
|
|
+ CachePool cachePool = new CachePool(getNextPoolId(), poolName,
|
|
|
info.getOwnerName(), info.getGroupName(), info.getMode(),
|
|
|
info.getWeight());
|
|
|
- cachePools.put(poolName, cachePool);
|
|
|
+ cachePoolsById.put(cachePool.getId(), cachePool);
|
|
|
+ cachePoolsByName.put(poolName, cachePool);
|
|
|
LOG.info("created new cache pool " + cachePool);
|
|
|
+ return cachePool;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -247,46 +274,62 @@ final class CacheManager {
|
|
|
* @param info
|
|
|
* The info for the cache pool to modify.
|
|
|
*/
|
|
|
- public synchronized void modifyCachePool(CachePoolInfo info)
|
|
|
+ public synchronized void modifyCachePool(long poolId, CachePoolInfo info)
|
|
|
throws IOException {
|
|
|
- String poolName = info.getPoolName();
|
|
|
- if (poolName.isEmpty()) {
|
|
|
- throw new IOException("invalid empty cache pool name");
|
|
|
+ if (poolId <= 0) {
|
|
|
+ throw new IOException("invalid pool id " + poolId);
|
|
|
}
|
|
|
- CachePool pool = cachePools.get(poolName);
|
|
|
- if (pool == null) {
|
|
|
- throw new IOException("cache pool " + poolName + " does not exist.");
|
|
|
+ if (!cachePoolsById.containsKey(poolId)) {
|
|
|
+ throw new IOException("cache pool id " + poolId + " does not exist.");
|
|
|
}
|
|
|
+ CachePool pool = cachePoolsById.get(poolId);
|
|
|
+ // Remove the old CachePoolInfo
|
|
|
+ removeCachePool(poolId);
|
|
|
+ // Build up the new CachePoolInfo
|
|
|
+ CachePoolInfo.Builder newInfo = CachePoolInfo.newBuilder(pool.getInfo());
|
|
|
StringBuilder bld = new StringBuilder();
|
|
|
String prefix = "";
|
|
|
+ if (info.getPoolName() != null) {
|
|
|
+ newInfo.setPoolName(info.getPoolName());
|
|
|
+ bld.append(prefix).
|
|
|
+ append("set name to ").append(info.getOwnerName());
|
|
|
+ prefix = "; ";
|
|
|
+ }
|
|
|
if (info.getOwnerName() != null) {
|
|
|
- pool.setOwnerName(info.getOwnerName());
|
|
|
+ newInfo.setOwnerName(info.getOwnerName());
|
|
|
bld.append(prefix).
|
|
|
append("set owner to ").append(info.getOwnerName());
|
|
|
prefix = "; ";
|
|
|
}
|
|
|
if (info.getGroupName() != null) {
|
|
|
- pool.setGroupName(info.getGroupName());
|
|
|
+ newInfo.setGroupName(info.getGroupName());
|
|
|
bld.append(prefix).
|
|
|
append("set group to ").append(info.getGroupName());
|
|
|
prefix = "; ";
|
|
|
}
|
|
|
if (info.getMode() != null) {
|
|
|
- pool.setMode(info.getMode());
|
|
|
+ newInfo.setMode(info.getMode());
|
|
|
bld.append(prefix).
|
|
|
- append(String.format("set mode to 0%3o", info.getMode()));
|
|
|
+ append(String.format("set mode to ", info.getMode()));
|
|
|
prefix = "; ";
|
|
|
}
|
|
|
if (info.getWeight() != null) {
|
|
|
- pool.setWeight(info.getWeight());
|
|
|
+ newInfo.setWeight(info.getWeight());
|
|
|
bld.append(prefix).
|
|
|
append("set weight to ").append(info.getWeight());
|
|
|
prefix = "; ";
|
|
|
}
|
|
|
if (prefix.isEmpty()) {
|
|
|
bld.append("no changes.");
|
|
|
+ } else {
|
|
|
+ pool.setInfo(newInfo.build());
|
|
|
}
|
|
|
- LOG.info("modified " + poolName + "; " + bld.toString());
|
|
|
+ // Put the newly modified info back in
|
|
|
+ cachePoolsById.put(poolId, pool);
|
|
|
+ cachePoolsByName.put(info.getPoolName(), pool);
|
|
|
+ LOG.info("modified pool id " + pool.getId()
|
|
|
+ + " (" + pool.getInfo().getPoolName() + "); "
|
|
|
+ + bld.toString());
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -294,27 +337,38 @@ final class CacheManager {
|
|
|
*
|
|
|
* Only the superuser should be able to call this function.
|
|
|
*
|
|
|
- * @param poolName
|
|
|
- * The name for the cache pool to remove.
|
|
|
+ * @param poolId
|
|
|
+ * The id of the cache pool to remove.
|
|
|
*/
|
|
|
- public synchronized void removeCachePool(String poolName)
|
|
|
- throws IOException {
|
|
|
- CachePool pool = cachePools.remove(poolName);
|
|
|
- if (pool == null) {
|
|
|
- throw new IOException("can't remove nonexistent cache pool " + poolName);
|
|
|
+ public synchronized void removeCachePool(long poolId) throws IOException {
|
|
|
+ if (!cachePoolsById.containsKey(poolId)) {
|
|
|
+ throw new IOException("can't remove nonexistent cache pool id " + poolId);
|
|
|
+ }
|
|
|
+ // Remove all the entries associated with the pool
|
|
|
+ Iterator<Map.Entry<Long, PathCacheEntry>> it =
|
|
|
+ entriesById.entrySet().iterator();
|
|
|
+ while (it.hasNext()) {
|
|
|
+ Map.Entry<Long, PathCacheEntry> entry = it.next();
|
|
|
+ if (entry.getValue().getDirective().getPoolId() == poolId) {
|
|
|
+ it.remove();
|
|
|
+ entriesByDirective.remove(entry.getValue().getDirective());
|
|
|
+ }
|
|
|
}
|
|
|
+ // Remove the pool
|
|
|
+ CachePool pool = cachePoolsById.remove(poolId);
|
|
|
+ cachePoolsByName.remove(pool.getInfo().getPoolName());
|
|
|
}
|
|
|
|
|
|
- public synchronized List<CachePoolInfo>
|
|
|
- listCachePools(FSPermissionChecker pc, String prevKey,
|
|
|
- int maxRepliesPerRequest) {
|
|
|
+ public synchronized List<CachePool> listCachePools(Long prevKey,
|
|
|
+ int maxRepliesPerRequest) {
|
|
|
final int MAX_PREALLOCATED_REPLIES = 16;
|
|
|
- ArrayList<CachePoolInfo> results =
|
|
|
- new ArrayList<CachePoolInfo>(Math.min(MAX_PREALLOCATED_REPLIES,
|
|
|
+ ArrayList<CachePool> results =
|
|
|
+ new ArrayList<CachePool>(Math.min(MAX_PREALLOCATED_REPLIES,
|
|
|
maxRepliesPerRequest));
|
|
|
- SortedMap<String, CachePool> tailMap = cachePools.tailMap(prevKey, false);
|
|
|
- for (Entry<String, CachePool> cur : tailMap.entrySet()) {
|
|
|
- results.add(cur.getValue().getInfo(pc));
|
|
|
+ SortedMap<Long, CachePool> tailMap =
|
|
|
+ cachePoolsById.tailMap(prevKey, false);
|
|
|
+ for (CachePool pool : tailMap.values()) {
|
|
|
+ results.add(pool);
|
|
|
}
|
|
|
return results;
|
|
|
}
|