diff --git a/hadoop-common-project/hadoop-common/dev-support/findbugsExcludeFile.xml b/hadoop-common-project/hadoop-common/dev-support/findbugsExcludeFile.xml index b650eaeb391..ec7c3967af4 100644 --- a/hadoop-common-project/hadoop-common/dev-support/findbugsExcludeFile.xml +++ b/hadoop-common-project/hadoop-common/dev-support/findbugsExcludeFile.xml @@ -44,7 +44,7 @@ --> - + - - - - - + | * @@ -598,6 +611,7 @@ public class StripedBlockUtil { for (StripingCell cell : cells) { long cellStart = cell.idxInInternalBlk * cellSize + cell.offset; long cellEnd = cellStart + cell.size - 1; + StripingChunk chunk; for (AlignedStripe s : stripes) { long stripeEnd = s.getOffsetInBlock() + s.getSpanInBlock() - 1; long overlapStart = Math.max(cellStart, s.getOffsetInBlock()); @@ -606,11 +620,13 @@ public class StripedBlockUtil { if (overLapLen <= 0) { continue; } - if (s.chunks[cell.idxInStripe] == null) { - s.chunks[cell.idxInStripe] = new StripingChunk(buf); + chunk = s.chunks[cell.idxInStripe]; + if (chunk == null) { + chunk = new StripingChunk(); + s.chunks[cell.idxInStripe] = chunk; } - s.chunks[cell.idxInStripe].addByteArraySlice( - (int)(offsetInBuf + done + overlapStart - cellStart), overLapLen); + chunk.getChunkBuffer().addSlice(buf, + (int) (done + overlapStart - cellStart), overLapLen); } done += cell.size; } @@ -833,88 +849,89 @@ public class StripedBlockUtil { */ public int state = REQUESTED; - public final ChunkByteArray byteArray; - public final ByteBuffer byteBuffer; + private final ChunkByteBuffer chunkBuffer; + private final ByteBuffer byteBuffer; - public StripingChunk(byte[] buf) { - this.byteArray = new ChunkByteArray(buf); + public StripingChunk() { + this.chunkBuffer = new ChunkByteBuffer(); byteBuffer = null; } public StripingChunk(ByteBuffer buf) { - this.byteArray = null; + this.chunkBuffer = null; this.byteBuffer = buf; } public StripingChunk(int state) { - this.byteArray = null; + this.chunkBuffer = null; this.byteBuffer = null; this.state = state; } - public void addByteArraySlice(int offset, int length) { - assert byteArray != null; - byteArray.offsetsInBuf.add(offset); - byteArray.lengthsInBuf.add(length); + public boolean useByteBuffer(){ + return byteBuffer != null; } - void copyTo(byte[] target) { - assert byteArray != null; - byteArray.copyTo(target); + public boolean useChunkBuffer() { + return chunkBuffer != null; } - void copyFrom(byte[] src) { - assert byteArray != null; - byteArray.copyFrom(src); + public ByteBuffer getByteBuffer() { + assert byteBuffer != null; + return byteBuffer; + } + + public ChunkByteBuffer getChunkBuffer() { + assert chunkBuffer != null; + return chunkBuffer; } } - public static class ChunkByteArray { - private final byte[] buf; - private final List offsetsInBuf; - private final List lengthsInBuf; + /** + * A utility to manage ByteBuffer slices for a reader. + */ + public static class ChunkByteBuffer { + private final List slices; - ChunkByteArray(byte[] buf) { - this.buf = buf; - this.offsetsInBuf = new ArrayList<>(); - this.lengthsInBuf = new ArrayList<>(); + ChunkByteBuffer() { + this.slices = new ArrayList<>(); } - public int[] getOffsets() { - int[] offsets = new int[offsetsInBuf.size()]; - for (int i = 0; i < offsets.length; i++) { - offsets[i] = offsetsInBuf.get(i); + public void addSlice(ByteBuffer buffer, int offset, int len) { + ByteBuffer tmp = buffer.duplicate(); + tmp.position(buffer.position() + offset); + tmp.limit(buffer.position() + offset + len); + slices.add(tmp.slice()); + } + + public ByteBuffer getSlice(int i) { + return slices.get(i); + } + + public List getSlices() { + return slices; + } + + /** + * Note: target will be ready-to-read state after the call. + */ + void copyTo(ByteBuffer target) { + for (ByteBuffer slice : slices) { + slice.flip(); + target.put(slice); } - return offsets; + target.flip(); } - public int[] getLengths() { - int[] lens = new int[this.lengthsInBuf.size()]; - for (int i = 0; i < lens.length; i++) { - lens[i] = this.lengthsInBuf.get(i); - } - return lens; - } - - public byte[] buf() { - return buf; - } - - void copyTo(byte[] target) { - int posInBuf = 0; - for (int i = 0; i < offsetsInBuf.size(); i++) { - System.arraycopy(buf, offsetsInBuf.get(i), - target, posInBuf, lengthsInBuf.get(i)); - posInBuf += lengthsInBuf.get(i); - } - } - - void copyFrom(byte[] src) { - int srcPos = 0; - for (int j = 0; j < offsetsInBuf.size(); j++) { - System.arraycopy(src, srcPos, buf, offsetsInBuf.get(j), - lengthsInBuf.get(j)); - srcPos += lengthsInBuf.get(j); + void copyFrom(ByteBuffer src) { + ByteBuffer tmp; + int len; + for (ByteBuffer slice : slices) { + len = slice.remaining(); + tmp = src.duplicate(); + tmp.limit(tmp.position() + len); + slice.put(tmp); + src.position(src.position() + len); } } } diff --git a/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/web/URLConnectionFactory.java b/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/web/URLConnectionFactory.java index 975f72e8f61..96095db8114 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/web/URLConnectionFactory.java +++ b/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/web/URLConnectionFactory.java @@ -183,6 +183,7 @@ public class URLConnectionFactory { return openConnection(url, false); } catch (AuthenticationException e) { // Unreachable + LOG.error("Open connection {} failed", url, e); return null; } } diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/fs/http/client/HttpFSFileSystem.java b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/fs/http/client/HttpFSFileSystem.java index 3a6ce7dc9e8..0f97d90ea25 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/fs/http/client/HttpFSFileSystem.java +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/fs/http/client/HttpFSFileSystem.java @@ -38,6 +38,7 @@ import org.apache.hadoop.fs.permission.AclEntry; import org.apache.hadoop.fs.permission.AclStatus; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.hdfs.DFSConfigKeys; +import org.apache.hadoop.hdfs.protocol.FsPermissionExtension; import org.apache.hadoop.lib.wsrs.EnumSetParam; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.token.Token; @@ -181,6 +182,8 @@ public class HttpFSFileSystem extends FileSystem public static final String ACL_ENTRIES_JSON = "entries"; public static final String ACL_BIT_JSON = "aclBit"; + public static final String ENC_BIT_JSON = "encBit"; + public static final int HTTP_TEMPORARY_REDIRECT = 307; private static final String HTTP_GET = "GET"; @@ -955,6 +958,21 @@ public class HttpFSFileSystem extends FileSystem return createAclStatus(json); } + /** Convert a string to a FsPermission object. */ + static FsPermission toFsPermission(JSONObject json) { + final String s = (String) json.get(PERMISSION_JSON); + final Boolean aclBit = (Boolean) json.get(ACL_BIT_JSON); + final Boolean encBit = (Boolean) json.get(ENC_BIT_JSON); + FsPermission perm = new FsPermission(Short.parseShort(s, 8)); + final boolean aBit = (aclBit != null) ? aclBit : false; + final boolean eBit = (encBit != null) ? encBit : false; + if (aBit || eBit) { + return new FsPermissionExtension(perm, aBit, eBit); + } else { + return perm; + } + } + private FileStatus createFileStatus(Path parent, JSONObject json) { String pathSuffix = (String) json.get(PATH_SUFFIX_JSON); Path path = (pathSuffix.equals("")) ? parent : new Path(parent, pathSuffix); @@ -962,8 +980,7 @@ public class HttpFSFileSystem extends FileSystem long len = (Long) json.get(LENGTH_JSON); String owner = (String) json.get(OWNER_JSON); String group = (String) json.get(GROUP_JSON); - FsPermission permission = - new FsPermission(Short.parseShort((String) json.get(PERMISSION_JSON), 8)); + final FsPermission permission = toFsPermission(json); long aTime = (Long) json.get(ACCESS_TIME_JSON); long mTime = (Long) json.get(MODIFICATION_TIME_JSON); long blockSize = (Long) json.get(BLOCK_SIZE_JSON); diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/fs/http/server/FSOperations.java b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/fs/http/server/FSOperations.java index 57bf025f80c..39597ebb120 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/fs/http/server/FSOperations.java +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/fs/http/server/FSOperations.java @@ -31,7 +31,6 @@ import org.apache.hadoop.fs.http.client.HttpFSFileSystem; import org.apache.hadoop.fs.permission.AclEntry; import org.apache.hadoop.fs.permission.AclStatus; import org.apache.hadoop.fs.permission.FsPermission; -import org.apache.hadoop.hdfs.protocol.AclException; import org.apache.hadoop.io.IOUtils; import org.apache.hadoop.lib.service.FileSystemAccess; import org.apache.hadoop.util.StringUtils; @@ -54,148 +53,59 @@ import java.util.Map.Entry; public class FSOperations { /** - * This class is used to group a FileStatus and an AclStatus together. - * It's needed for the GETFILESTATUS and LISTSTATUS calls, which take - * most info from the FileStatus and a wee bit from the AclStatus. + * @param fileStatus a FileStatus object + * @return JSON map suitable for wire transport */ - private static class StatusPair { - private FileStatus fileStatus; - private AclStatus aclStatus; - - /** - * Simple constructor - * @param fileStatus Existing FileStatus object - * @param aclStatus Existing AclStatus object - */ - public StatusPair(FileStatus fileStatus, AclStatus aclStatus) { - this.fileStatus = fileStatus; - this.aclStatus = aclStatus; - } - - /** - * Create one StatusPair by performing the underlying calls to - * fs.getFileStatus and fs.getAclStatus - * @param fs The FileSystem where 'path' lives - * @param path The file/directory to query - * @throws IOException - */ - public StatusPair(FileSystem fs, Path path) throws IOException { - fileStatus = fs.getFileStatus(path); - aclStatus = null; - try { - aclStatus = fs.getAclStatus(path); - } catch (AclException e) { - /* - * The cause is almost certainly an "ACLS aren't enabled" - * exception, so leave aclStatus at null and carry on. - */ - } catch (UnsupportedOperationException e) { - /* Ditto above - this is the case for a local file system */ - } - } - - /** - * Return a Map suitable for conversion into JSON format - * @return The JSONish Map - */ - public Map toJson() { - Map json = new LinkedHashMap(); - json.put(HttpFSFileSystem.FILE_STATUS_JSON, toJsonInner(true)); - return json; - } - - /** - * Return in inner part of the JSON for the status - used by both the - * GETFILESTATUS and LISTSTATUS calls. - * @param emptyPathSuffix Whether or not to include PATH_SUFFIX_JSON - * @return The JSONish Map - */ - public Map toJsonInner(boolean emptyPathSuffix) { - Map json = new LinkedHashMap(); - json.put(HttpFSFileSystem.PATH_SUFFIX_JSON, - (emptyPathSuffix) ? "" : fileStatus.getPath().getName()); - json.put(HttpFSFileSystem.TYPE_JSON, - HttpFSFileSystem.FILE_TYPE.getType(fileStatus).toString()); - json.put(HttpFSFileSystem.LENGTH_JSON, fileStatus.getLen()); - json.put(HttpFSFileSystem.OWNER_JSON, fileStatus.getOwner()); - json.put(HttpFSFileSystem.GROUP_JSON, fileStatus.getGroup()); - json.put(HttpFSFileSystem.PERMISSION_JSON, - HttpFSFileSystem.permissionToString(fileStatus.getPermission())); - json.put(HttpFSFileSystem.ACCESS_TIME_JSON, fileStatus.getAccessTime()); - json.put(HttpFSFileSystem.MODIFICATION_TIME_JSON, - fileStatus.getModificationTime()); - json.put(HttpFSFileSystem.BLOCK_SIZE_JSON, fileStatus.getBlockSize()); - json.put(HttpFSFileSystem.REPLICATION_JSON, fileStatus.getReplication()); - if ( (aclStatus != null) && !(aclStatus.getEntries().isEmpty()) ) { - json.put(HttpFSFileSystem.ACL_BIT_JSON,true); - } - return json; - } + private static Map toJson(FileStatus fileStatus) { + Map json = new LinkedHashMap<>(); + json.put(HttpFSFileSystem.FILE_STATUS_JSON, toJsonInner(fileStatus, true)); + return json; } /** - * Simple class used to contain and operate upon a list of StatusPair - * objects. Used by LISTSTATUS. + * @param fileStatuses list of FileStatus objects + * @return JSON map suitable for wire transport */ - private static class StatusPairs { - private StatusPair[] statusPairs; - - /** - * Construct a list of StatusPair objects - * @param fs The FileSystem where 'path' lives - * @param path The directory to query - * @param filter A possible filter for entries in the directory - * @throws IOException - */ - public StatusPairs(FileSystem fs, Path path, PathFilter filter) - throws IOException { - /* Grab all the file statuses at once in an array */ - FileStatus[] fileStatuses = fs.listStatus(path, filter); - - /* We'll have an array of StatusPairs of the same length */ - AclStatus aclStatus = null; - statusPairs = new StatusPair[fileStatuses.length]; - - /* - * For each FileStatus, attempt to acquire an AclStatus. If the - * getAclStatus throws an exception, we assume that ACLs are turned - * off entirely and abandon the attempt. - */ - boolean useAcls = true; // Assume ACLs work until proven otherwise - for (int i = 0; i < fileStatuses.length; i++) { - if (useAcls) { - try { - aclStatus = fs.getAclStatus(fileStatuses[i].getPath()); - } catch (AclException e) { - /* Almost certainly due to an "ACLs not enabled" exception */ - aclStatus = null; - useAcls = false; - } catch (UnsupportedOperationException e) { - /* Ditto above - this is the case for a local file system */ - aclStatus = null; - useAcls = false; - } - } - statusPairs[i] = new StatusPair(fileStatuses[i], aclStatus); - } + @SuppressWarnings({"unchecked"}) + private static Map toJson(FileStatus[] fileStatuses) { + Map json = new LinkedHashMap<>(); + Map inner = new LinkedHashMap<>(); + JSONArray statuses = new JSONArray(); + for (FileStatus f : fileStatuses) { + statuses.add(toJsonInner(f, false)); } + inner.put(HttpFSFileSystem.FILE_STATUS_JSON, statuses); + json.put(HttpFSFileSystem.FILE_STATUSES_JSON, inner); + return json; + } - /** - * Return a Map suitable for conversion into JSON. - * @return A JSONish Map - */ - @SuppressWarnings({"unchecked"}) - public Map toJson() { - Map json = new LinkedHashMap(); - Map inner = new LinkedHashMap(); - JSONArray statuses = new JSONArray(); - for (StatusPair s : statusPairs) { - statuses.add(s.toJsonInner(false)); - } - inner.put(HttpFSFileSystem.FILE_STATUS_JSON, statuses); - json.put(HttpFSFileSystem.FILE_STATUSES_JSON, inner); - return json; + /** + * Not meant to be called directly except by the other toJson functions. + */ + private static Map toJsonInner(FileStatus fileStatus, + boolean emptyPathSuffix) { + Map json = new LinkedHashMap(); + json.put(HttpFSFileSystem.PATH_SUFFIX_JSON, + (emptyPathSuffix) ? "" : fileStatus.getPath().getName()); + json.put(HttpFSFileSystem.TYPE_JSON, + HttpFSFileSystem.FILE_TYPE.getType(fileStatus).toString()); + json.put(HttpFSFileSystem.LENGTH_JSON, fileStatus.getLen()); + json.put(HttpFSFileSystem.OWNER_JSON, fileStatus.getOwner()); + json.put(HttpFSFileSystem.GROUP_JSON, fileStatus.getGroup()); + json.put(HttpFSFileSystem.PERMISSION_JSON, + HttpFSFileSystem.permissionToString(fileStatus.getPermission())); + json.put(HttpFSFileSystem.ACCESS_TIME_JSON, fileStatus.getAccessTime()); + json.put(HttpFSFileSystem.MODIFICATION_TIME_JSON, + fileStatus.getModificationTime()); + json.put(HttpFSFileSystem.BLOCK_SIZE_JSON, fileStatus.getBlockSize()); + json.put(HttpFSFileSystem.REPLICATION_JSON, fileStatus.getReplication()); + if (fileStatus.getPermission().getAclBit()) { + json.put(HttpFSFileSystem.ACL_BIT_JSON, true); } + if (fileStatus.getPermission().getEncryptedBit()) { + json.put(HttpFSFileSystem.ENC_BIT_JSON, true); + } + return json; } /** Converts an AclStatus object into a JSON object. @@ -637,8 +547,8 @@ public class FSOperations { */ @Override public Map execute(FileSystem fs) throws IOException { - StatusPair sp = new StatusPair(fs, path); - return sp.toJson(); + FileStatus status = fs.getFileStatus(path); + return toJson(status); } } @@ -703,8 +613,8 @@ public class FSOperations { */ @Override public Map execute(FileSystem fs) throws IOException { - StatusPairs sp = new StatusPairs(fs, path, filter); - return sp.toJson(); + FileStatus[] fileStatuses = fs.listStatus(path, filter); + return toJson(fileStatuses); } @Override diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/client/BaseTestHttpFSWith.java b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/client/BaseTestHttpFSWith.java index 575a477f9dc..aea6cf80b59 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/client/BaseTestHttpFSWith.java +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/client/BaseTestHttpFSWith.java @@ -40,6 +40,7 @@ import org.apache.hadoop.test.HadoopUsersConfTestHelper; import org.apache.hadoop.test.TestDir; import org.apache.hadoop.test.TestDirHelper; import org.apache.hadoop.test.TestHdfs; +import org.apache.hadoop.test.TestHdfsHelper; import org.apache.hadoop.test.TestJetty; import org.apache.hadoop.test.TestJettyHelper; import org.junit.Assert; @@ -66,6 +67,11 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + @RunWith(value = Parameterized.class) public abstract class BaseTestHttpFSWith extends HFSTestCase { @@ -81,9 +87,9 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { private void createHttpFSServer() throws Exception { File homeDir = TestDirHelper.getTestDir(); - Assert.assertTrue(new File(homeDir, "conf").mkdir()); - Assert.assertTrue(new File(homeDir, "log").mkdir()); - Assert.assertTrue(new File(homeDir, "temp").mkdir()); + assertTrue(new File(homeDir, "conf").mkdir()); + assertTrue(new File(homeDir, "log").mkdir()); + assertTrue(new File(homeDir, "temp").mkdir()); HttpFSServerWebApp.setHomeDirForCurrentThread(homeDir.getAbsolutePath()); File secretFile = new File(new File(homeDir, "conf"), "secret"); @@ -143,7 +149,7 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { Assert.assertNotNull(fs); URI uri = new URI(getScheme() + "://" + TestJettyHelper.getJettyURL().toURI().getAuthority()); - Assert.assertEquals(fs.getUri(), uri); + assertEquals(fs.getUri(), uri); fs.close(); } @@ -156,7 +162,7 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { fs.close(); fs = getHttpFSFileSystem(); InputStream is = fs.open(new Path(path.toUri().getPath())); - Assert.assertEquals(is.read(), 1); + assertEquals(is.read(), 1); is.close(); fs.close(); } @@ -173,12 +179,12 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { fs = FileSystem.get(getProxiedFSConf()); FileStatus status = fs.getFileStatus(path); if (!isLocalFS()) { - Assert.assertEquals(status.getReplication(), 2); - Assert.assertEquals(status.getBlockSize(), 100 * 1024 * 1024); + assertEquals(status.getReplication(), 2); + assertEquals(status.getBlockSize(), 100 * 1024 * 1024); } - Assert.assertEquals(status.getPermission(), permission); + assertEquals(status.getPermission(), permission); InputStream is = fs.open(path); - Assert.assertEquals(is.read(), 1); + assertEquals(is.read(), 1); is.close(); fs.close(); } @@ -216,9 +222,9 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { fs.close(); fs = FileSystem.get(getProxiedFSConf()); InputStream is = fs.open(path); - Assert.assertEquals(is.read(), 1); - Assert.assertEquals(is.read(), 2); - Assert.assertEquals(is.read(), -1); + assertEquals(is.read(), 1); + assertEquals(is.read(), 2); + assertEquals(is.read(), -1); is.close(); fs.close(); } @@ -239,10 +245,10 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { final int newLength = blockSize; boolean isReady = fs.truncate(file, newLength); - Assert.assertTrue("Recovery is not expected.", isReady); + assertTrue("Recovery is not expected.", isReady); FileStatus fileStatus = fs.getFileStatus(file); - Assert.assertEquals(fileStatus.getLen(), newLength); + assertEquals(fileStatus.getLen(), newLength); AppendTestUtil.checkFullFile(fs, file, newLength, data, file.toString()); fs.close(); @@ -266,9 +272,9 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { fs.concat(path1, new Path[]{path2, path3}); fs.close(); fs = FileSystem.get(config); - Assert.assertTrue(fs.exists(path1)); - Assert.assertFalse(fs.exists(path2)); - Assert.assertFalse(fs.exists(path3)); + assertTrue(fs.exists(path1)); + assertFalse(fs.exists(path2)); + assertFalse(fs.exists(path3)); fs.close(); } } @@ -284,8 +290,8 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { fs.rename(oldPath, newPath); fs.close(); fs = FileSystem.get(getProxiedFSConf()); - Assert.assertFalse(fs.exists(oldPath)); - Assert.assertTrue(fs.exists(newPath)); + assertFalse(fs.exists(oldPath)); + assertTrue(fs.exists(newPath)); fs.close(); } @@ -299,8 +305,8 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { fs.mkdirs(foe); FileSystem hoopFs = getHttpFSFileSystem(); - Assert.assertTrue(hoopFs.delete(new Path(foo.toUri().getPath()), false)); - Assert.assertFalse(fs.exists(foo)); + assertTrue(hoopFs.delete(new Path(foo.toUri().getPath()), false)); + assertFalse(fs.exists(foo)); try { hoopFs.delete(new Path(bar.toUri().getPath()), false); Assert.fail(); @@ -308,13 +314,13 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { } catch (Exception ex) { Assert.fail(); } - Assert.assertTrue(fs.exists(bar)); - Assert.assertTrue(hoopFs.delete(new Path(bar.toUri().getPath()), true)); - Assert.assertFalse(fs.exists(bar)); + assertTrue(fs.exists(bar)); + assertTrue(hoopFs.delete(new Path(bar.toUri().getPath()), true)); + assertFalse(fs.exists(bar)); - Assert.assertTrue(fs.exists(foe)); - Assert.assertTrue(hoopFs.delete(foe, true)); - Assert.assertFalse(fs.exists(foe)); + assertTrue(fs.exists(foe)); + assertTrue(hoopFs.delete(foe, true)); + assertFalse(fs.exists(foe)); hoopFs.close(); fs.close(); @@ -333,19 +339,20 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { FileStatus status2 = fs.getFileStatus(new Path(path.toUri().getPath())); fs.close(); - Assert.assertEquals(status2.getPermission(), status1.getPermission()); - Assert.assertEquals(status2.getPath().toUri().getPath(), status1.getPath().toUri().getPath()); - Assert.assertEquals(status2.getReplication(), status1.getReplication()); - Assert.assertEquals(status2.getBlockSize(), status1.getBlockSize()); - Assert.assertEquals(status2.getAccessTime(), status1.getAccessTime()); - Assert.assertEquals(status2.getModificationTime(), status1.getModificationTime()); - Assert.assertEquals(status2.getOwner(), status1.getOwner()); - Assert.assertEquals(status2.getGroup(), status1.getGroup()); - Assert.assertEquals(status2.getLen(), status1.getLen()); + assertEquals(status2.getPermission(), status1.getPermission()); + assertEquals(status2.getPath().toUri().getPath(), + status1.getPath().toUri().getPath()); + assertEquals(status2.getReplication(), status1.getReplication()); + assertEquals(status2.getBlockSize(), status1.getBlockSize()); + assertEquals(status2.getAccessTime(), status1.getAccessTime()); + assertEquals(status2.getModificationTime(), status1.getModificationTime()); + assertEquals(status2.getOwner(), status1.getOwner()); + assertEquals(status2.getGroup(), status1.getGroup()); + assertEquals(status2.getLen(), status1.getLen()); FileStatus[] stati = fs.listStatus(path.getParent()); - Assert.assertEquals(stati.length, 1); - Assert.assertEquals(stati[0].getPath().getName(), path.getName()); + assertEquals(stati.length, 1); + assertEquals(stati[0].getPath().getName(), path.getName()); } private void testWorkingdirectory() throws Exception { @@ -359,14 +366,15 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { } Path httpFSWorkingDir = fs.getWorkingDirectory(); fs.close(); - Assert.assertEquals(httpFSWorkingDir.toUri().getPath(), + assertEquals(httpFSWorkingDir.toUri().getPath(), workingDir.toUri().getPath()); fs = getHttpFSFileSystem(); fs.setWorkingDirectory(new Path("/tmp")); workingDir = fs.getWorkingDirectory(); fs.close(); - Assert.assertEquals(workingDir.toUri().getPath(), new Path("/tmp").toUri().getPath()); + assertEquals(workingDir.toUri().getPath(), + new Path("/tmp").toUri().getPath()); } private void testMkdirs() throws Exception { @@ -375,7 +383,7 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { fs.mkdirs(path); fs.close(); fs = FileSystem.get(getProxiedFSConf()); - Assert.assertTrue(fs.exists(path)); + assertTrue(fs.exists(path)); fs.close(); } @@ -400,8 +408,8 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { fs.close(); long atNew = status1.getAccessTime(); long mtNew = status1.getModificationTime(); - Assert.assertEquals(mtNew, mt - 10); - Assert.assertEquals(atNew, at - 20); + assertEquals(mtNew, mt - 10); + assertEquals(atNew, at - 20); } } @@ -419,7 +427,7 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { FileStatus status1 = fs.getFileStatus(path); fs.close(); FsPermission permission2 = status1.getPermission(); - Assert.assertEquals(permission2, permission1); + assertEquals(permission2, permission1); //sticky bit fs = getHttpFSFileSystem(); @@ -431,8 +439,8 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { status1 = fs.getFileStatus(path); fs.close(); permission2 = status1.getPermission(); - Assert.assertTrue(permission2.getStickyBit()); - Assert.assertEquals(permission2, permission1); + assertTrue(permission2.getStickyBit()); + assertEquals(permission2, permission1); } private void testSetOwner() throws Exception { @@ -454,8 +462,8 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { fs = FileSystem.get(getProxiedFSConf()); FileStatus status1 = fs.getFileStatus(path); fs.close(); - Assert.assertEquals(status1.getOwner(), user); - Assert.assertEquals(status1.getGroup(), group); + assertEquals(status1.getOwner(), user); + assertEquals(status1.getGroup(), group); } } @@ -475,7 +483,7 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { fs = FileSystem.get(getProxiedFSConf()); FileStatus status1 = fs.getFileStatus(path); fs.close(); - Assert.assertEquals(status1.getReplication(), (short) 1); + assertEquals(status1.getReplication(), (short) 1); } private void testChecksum() throws Exception { @@ -491,9 +499,10 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { fs = getHttpFSFileSystem(); FileChecksum httpChecksum = fs.getFileChecksum(path); fs.close(); - Assert.assertEquals(httpChecksum.getAlgorithmName(), hdfsChecksum.getAlgorithmName()); - Assert.assertEquals(httpChecksum.getLength(), hdfsChecksum.getLength()); - Assert.assertArrayEquals(httpChecksum.getBytes(), hdfsChecksum.getBytes()); + assertEquals(httpChecksum.getAlgorithmName(), + hdfsChecksum.getAlgorithmName()); + assertEquals(httpChecksum.getLength(), hdfsChecksum.getLength()); + assertArrayEquals(httpChecksum.getBytes(), hdfsChecksum.getBytes()); } } @@ -508,12 +517,17 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { fs = getHttpFSFileSystem(); ContentSummary httpContentSummary = fs.getContentSummary(path); fs.close(); - Assert.assertEquals(httpContentSummary.getDirectoryCount(), hdfsContentSummary.getDirectoryCount()); - Assert.assertEquals(httpContentSummary.getFileCount(), hdfsContentSummary.getFileCount()); - Assert.assertEquals(httpContentSummary.getLength(), hdfsContentSummary.getLength()); - Assert.assertEquals(httpContentSummary.getQuota(), hdfsContentSummary.getQuota()); - Assert.assertEquals(httpContentSummary.getSpaceConsumed(), hdfsContentSummary.getSpaceConsumed()); - Assert.assertEquals(httpContentSummary.getSpaceQuota(), hdfsContentSummary.getSpaceQuota()); + assertEquals(httpContentSummary.getDirectoryCount(), + hdfsContentSummary.getDirectoryCount()); + assertEquals(httpContentSummary.getFileCount(), + hdfsContentSummary.getFileCount()); + assertEquals(httpContentSummary.getLength(), + hdfsContentSummary.getLength()); + assertEquals(httpContentSummary.getQuota(), hdfsContentSummary.getQuota()); + assertEquals(httpContentSummary.getSpaceConsumed(), + hdfsContentSummary.getSpaceConsumed()); + assertEquals(httpContentSummary.getSpaceQuota(), + hdfsContentSummary.getSpaceQuota()); } /** Set xattr */ @@ -552,11 +566,11 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { fs = FileSystem.get(getProxiedFSConf()); Map xAttrs = fs.getXAttrs(path); fs.close(); - Assert.assertEquals(4, xAttrs.size()); - Assert.assertArrayEquals(value1, xAttrs.get(name1)); - Assert.assertArrayEquals(value2, xAttrs.get(name2)); - Assert.assertArrayEquals(new byte[0], xAttrs.get(name3)); - Assert.assertArrayEquals(value4, xAttrs.get(name4)); + assertEquals(4, xAttrs.size()); + assertArrayEquals(value1, xAttrs.get(name1)); + assertArrayEquals(value2, xAttrs.get(name2)); + assertArrayEquals(new byte[0], xAttrs.get(name3)); + assertArrayEquals(value4, xAttrs.get(name4)); } } @@ -595,16 +609,16 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { names.add(name4); Map xAttrs = fs.getXAttrs(path, names); fs.close(); - Assert.assertEquals(4, xAttrs.size()); - Assert.assertArrayEquals(value1, xAttrs.get(name1)); - Assert.assertArrayEquals(value2, xAttrs.get(name2)); - Assert.assertArrayEquals(new byte[0], xAttrs.get(name3)); - Assert.assertArrayEquals(value4, xAttrs.get(name4)); + assertEquals(4, xAttrs.size()); + assertArrayEquals(value1, xAttrs.get(name1)); + assertArrayEquals(value2, xAttrs.get(name2)); + assertArrayEquals(new byte[0], xAttrs.get(name3)); + assertArrayEquals(value4, xAttrs.get(name4)); // Get specific xattr fs = getHttpFSFileSystem(); byte[] value = fs.getXAttr(path, name1); - Assert.assertArrayEquals(value1, value); + assertArrayEquals(value1, value); final String name5 = "a1"; try { value = fs.getXAttr(path, name5); @@ -618,11 +632,11 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { fs = getHttpFSFileSystem(); xAttrs = fs.getXAttrs(path); fs.close(); - Assert.assertEquals(4, xAttrs.size()); - Assert.assertArrayEquals(value1, xAttrs.get(name1)); - Assert.assertArrayEquals(value2, xAttrs.get(name2)); - Assert.assertArrayEquals(new byte[0], xAttrs.get(name3)); - Assert.assertArrayEquals(value4, xAttrs.get(name4)); + assertEquals(4, xAttrs.size()); + assertArrayEquals(value1, xAttrs.get(name1)); + assertArrayEquals(value2, xAttrs.get(name2)); + assertArrayEquals(new byte[0], xAttrs.get(name3)); + assertArrayEquals(value4, xAttrs.get(name4)); } } @@ -667,8 +681,8 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { fs = FileSystem.get(getProxiedFSConf()); Map xAttrs = fs.getXAttrs(path); fs.close(); - Assert.assertEquals(1, xAttrs.size()); - Assert.assertArrayEquals(value2, xAttrs.get(name2)); + assertEquals(1, xAttrs.size()); + assertArrayEquals(value2, xAttrs.get(name2)); } } @@ -700,11 +714,11 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { fs = getHttpFSFileSystem(); List names = fs.listXAttrs(path); - Assert.assertEquals(4, names.size()); - Assert.assertTrue(names.contains(name1)); - Assert.assertTrue(names.contains(name2)); - Assert.assertTrue(names.contains(name3)); - Assert.assertTrue(names.contains(name4)); + assertEquals(4, names.size()); + assertTrue(names.contains(name1)); + assertTrue(names.contains(name2)); + assertTrue(names.contains(name3)); + assertTrue(names.contains(name4)); } } @@ -715,18 +729,26 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { * @throws Exception */ private void assertSameAcls(AclStatus a, AclStatus b) throws Exception { - Assert.assertTrue(a.getOwner().equals(b.getOwner())); - Assert.assertTrue(a.getGroup().equals(b.getGroup())); - Assert.assertTrue(a.isStickyBit() == b.isStickyBit()); - Assert.assertTrue(a.getEntries().size() == b.getEntries().size()); + assertTrue(a.getOwner().equals(b.getOwner())); + assertTrue(a.getGroup().equals(b.getGroup())); + assertTrue(a.isStickyBit() == b.isStickyBit()); + assertTrue(a.getEntries().size() == b.getEntries().size()); for (AclEntry e : a.getEntries()) { - Assert.assertTrue(b.getEntries().contains(e)); + assertTrue(b.getEntries().contains(e)); } for (AclEntry e : b.getEntries()) { - Assert.assertTrue(a.getEntries().contains(e)); + assertTrue(a.getEntries().contains(e)); } } + private static void assertSameAclBit(FileSystem expected, FileSystem actual, + Path path) throws IOException { + FileStatus expectedFileStatus = expected.getFileStatus(path); + FileStatus actualFileStatus = actual.getFileStatus(path); + assertEquals(actualFileStatus.getPermission().getAclBit(), + expectedFileStatus.getPermission().getAclBit()); + } + /** * Simple ACL tests on a file: Set an acl, add an acl, remove one acl, * and remove all acls. @@ -755,26 +777,31 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { AclStatus proxyAclStat = proxyFs.getAclStatus(path); AclStatus httpfsAclStat = httpfs.getAclStatus(path); assertSameAcls(httpfsAclStat, proxyAclStat); + assertSameAclBit(httpfs, proxyFs, path); httpfs.setAcl(path, AclEntry.parseAclSpec(aclSet,true)); proxyAclStat = proxyFs.getAclStatus(path); httpfsAclStat = httpfs.getAclStatus(path); assertSameAcls(httpfsAclStat, proxyAclStat); + assertSameAclBit(httpfs, proxyFs, path); httpfs.modifyAclEntries(path, AclEntry.parseAclSpec(aclUser2, true)); proxyAclStat = proxyFs.getAclStatus(path); httpfsAclStat = httpfs.getAclStatus(path); assertSameAcls(httpfsAclStat, proxyAclStat); + assertSameAclBit(httpfs, proxyFs, path); httpfs.removeAclEntries(path, AclEntry.parseAclSpec(rmAclUser1, false)); proxyAclStat = proxyFs.getAclStatus(path); httpfsAclStat = httpfs.getAclStatus(path); assertSameAcls(httpfsAclStat, proxyAclStat); + assertSameAclBit(httpfs, proxyFs, path); httpfs.removeAcl(path); proxyAclStat = proxyFs.getAclStatus(path); httpfsAclStat = httpfs.getAclStatus(path); assertSameAcls(httpfsAclStat, proxyAclStat); + assertSameAclBit(httpfs, proxyFs, path); } /** @@ -797,25 +824,46 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { AclStatus proxyAclStat = proxyFs.getAclStatus(dir); AclStatus httpfsAclStat = httpfs.getAclStatus(dir); assertSameAcls(httpfsAclStat, proxyAclStat); + assertSameAclBit(httpfs, proxyFs, dir); /* Set a default ACL on the directory */ httpfs.setAcl(dir, (AclEntry.parseAclSpec(defUser1,true))); proxyAclStat = proxyFs.getAclStatus(dir); httpfsAclStat = httpfs.getAclStatus(dir); assertSameAcls(httpfsAclStat, proxyAclStat); + assertSameAclBit(httpfs, proxyFs, dir); /* Remove the default ACL */ httpfs.removeDefaultAcl(dir); proxyAclStat = proxyFs.getAclStatus(dir); httpfsAclStat = httpfs.getAclStatus(dir); assertSameAcls(httpfsAclStat, proxyAclStat); + assertSameAclBit(httpfs, proxyFs, dir); + } + + private void testEncryption() throws Exception { + if (isLocalFS()) { + return; + } + FileSystem proxyFs = FileSystem.get(getProxiedFSConf()); + FileSystem httpFs = getHttpFSFileSystem(); + FileStatus proxyStatus = proxyFs.getFileStatus(TestHdfsHelper + .ENCRYPTED_FILE); + assertTrue(proxyStatus.isEncrypted()); + FileStatus httpStatus = httpFs.getFileStatus(TestHdfsHelper + .ENCRYPTED_FILE); + assertTrue(httpStatus.isEncrypted()); + proxyStatus = proxyFs.getFileStatus(new Path("/")); + httpStatus = httpFs.getFileStatus(new Path("/")); + assertFalse(proxyStatus.isEncrypted()); + assertFalse(httpStatus.isEncrypted()); } protected enum Operation { GET, OPEN, CREATE, APPEND, TRUNCATE, CONCAT, RENAME, DELETE, LIST_STATUS, WORKING_DIRECTORY, MKDIRS, SET_TIMES, SET_PERMISSION, SET_OWNER, SET_REPLICATION, CHECKSUM, CONTENT_SUMMARY, FILEACLS, DIRACLS, SET_XATTR, - GET_XATTRS, REMOVE_XATTR, LIST_XATTRS + GET_XATTRS, REMOVE_XATTR, LIST_XATTRS, ENCRYPTION } private void operation(Operation op) throws Exception { @@ -889,6 +937,9 @@ public abstract class BaseTestHttpFSWith extends HFSTestCase { case LIST_XATTRS: testListXAttrs(); break; + case ENCRYPTION: + testEncryption(); + break; } } diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/test/TestHdfsHelper.java b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/test/TestHdfsHelper.java index 85cf48dec7b..56952859610 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/test/TestHdfsHelper.java +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/test/TestHdfsHelper.java @@ -21,10 +21,14 @@ import java.io.File; import java.util.concurrent.atomic.AtomicInteger; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.crypto.key.JavaKeyStoreProvider; import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.FileSystemTestHelper; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.hdfs.DFSConfigKeys; +import org.apache.hadoop.hdfs.DFSTestUtil; +import org.apache.hadoop.hdfs.DistributedFileSystem; import org.apache.hadoop.hdfs.MiniDFSCluster; import org.junit.Test; import org.junit.runners.model.FrameworkMethod; @@ -129,6 +133,9 @@ public class TestHdfsHelper extends TestDirHelper { return new Configuration(conf); } + public static final Path ENCRYPTION_ZONE = new Path("/ez"); + public static final Path ENCRYPTED_FILE = new Path("/ez/encfile"); + private static MiniDFSCluster MINI_DFS = null; private static synchronized MiniDFSCluster startMiniHdfs(Configuration conf) throws Exception { @@ -148,14 +155,28 @@ public class TestHdfsHelper extends TestDirHelper { conf.set("hadoop.security.authentication", "simple"); conf.setBoolean(DFSConfigKeys.DFS_NAMENODE_ACLS_ENABLED_KEY, true); conf.setBoolean(DFSConfigKeys.DFS_NAMENODE_XATTRS_ENABLED_KEY, true); + FileSystemTestHelper helper = new FileSystemTestHelper(); + final String jceksPath = JavaKeyStoreProvider.SCHEME_NAME + "://file" + + new Path(helper.getTestRootDir(), "test.jks").toUri(); + conf.set(DFSConfigKeys.DFS_ENCRYPTION_KEY_PROVIDER_URI, jceksPath); MiniDFSCluster.Builder builder = new MiniDFSCluster.Builder(conf); builder.numDataNodes(2); MiniDFSCluster miniHdfs = builder.build(); - FileSystem fileSystem = miniHdfs.getFileSystem(); + final String testkey = "testkey"; + DFSTestUtil.createKey(testkey, miniHdfs, conf); + + DistributedFileSystem fileSystem = miniHdfs.getFileSystem(); + fileSystem.getClient().setKeyProvider(miniHdfs.getNameNode() + .getNamesystem().getProvider()); + fileSystem.mkdirs(new Path("/tmp")); fileSystem.mkdirs(new Path("/user")); fileSystem.setPermission(new Path("/tmp"), FsPermission.valueOf("-rwxrwxrwx")); fileSystem.setPermission(new Path("/user"), FsPermission.valueOf("-rwxrwxrwx")); + fileSystem.mkdirs(ENCRYPTION_ZONE); + fileSystem.createEncryptionZone(ENCRYPTION_ZONE, testkey); + fileSystem.create(ENCRYPTED_FILE).close(); + MINI_DFS = miniHdfs; } return MINI_DFS; diff --git a/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfs/hdfs.c b/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfs/hdfs.c index 4618dbb3a5a..1dcc768106f 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfs/hdfs.c +++ b/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfs/hdfs.c @@ -114,7 +114,7 @@ int hdfsFileGetReadStatistics(hdfsFile file, jthr = invokeMethod(env, &jVal, INSTANCE, file->file, "org/apache/hadoop/hdfs/client/HdfsDataInputStream", "getReadStatistics", - "()Lorg/apache/hadoop/hdfs/DFSInputStream$ReadStatistics;"); + "()Lorg/apache/hadoop/hdfs/ReadStatistics;"); if (jthr) { ret = printExceptionAndFree(env, jthr, PRINT_EXC_ALL, "hdfsFileGetReadStatistics: getReadStatistics failed"); @@ -127,7 +127,7 @@ int hdfsFileGetReadStatistics(hdfsFile file, goto done; } jthr = invokeMethod(env, &jVal, INSTANCE, readStats, - "org/apache/hadoop/hdfs/DFSInputStream$ReadStatistics", + "org/apache/hadoop/hdfs/ReadStatistics", "getTotalBytesRead", "()J"); if (jthr) { ret = printExceptionAndFree(env, jthr, PRINT_EXC_ALL, @@ -137,7 +137,7 @@ int hdfsFileGetReadStatistics(hdfsFile file, s->totalBytesRead = jVal.j; jthr = invokeMethod(env, &jVal, INSTANCE, readStats, - "org/apache/hadoop/hdfs/DFSInputStream$ReadStatistics", + "org/apache/hadoop/hdfs/ReadStatistics", "getTotalLocalBytesRead", "()J"); if (jthr) { ret = printExceptionAndFree(env, jthr, PRINT_EXC_ALL, @@ -147,7 +147,7 @@ int hdfsFileGetReadStatistics(hdfsFile file, s->totalLocalBytesRead = jVal.j; jthr = invokeMethod(env, &jVal, INSTANCE, readStats, - "org/apache/hadoop/hdfs/DFSInputStream$ReadStatistics", + "org/apache/hadoop/hdfs/ReadStatistics", "getTotalShortCircuitBytesRead", "()J"); if (jthr) { ret = printExceptionAndFree(env, jthr, PRINT_EXC_ALL, @@ -156,7 +156,7 @@ int hdfsFileGetReadStatistics(hdfsFile file, } s->totalShortCircuitBytesRead = jVal.j; jthr = invokeMethod(env, &jVal, INSTANCE, readStats, - "org/apache/hadoop/hdfs/DFSInputStream$ReadStatistics", + "org/apache/hadoop/hdfs/ReadStatistics", "getTotalZeroCopyBytesRead", "()J"); if (jthr) { ret = printExceptionAndFree(env, jthr, PRINT_EXC_ALL, diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/bin/hdfs b/hadoop-hdfs-project/hadoop-hdfs/src/main/bin/hdfs index 50595286d4d..6d6088fc62c 100755 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/bin/hdfs +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/bin/hdfs @@ -79,8 +79,6 @@ function hdfscmd_case balancer) HADOOP_SUBCMD_SUPPORTDAEMONIZATION="true" HADOOP_CLASSNAME=org.apache.hadoop.hdfs.server.balancer.Balancer - hadoop_debug "Appending HADOOP_BALANCER_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_BALANCER_OPTS}" ;; cacheadmin) HADOOP_CLASSNAME=org.apache.hadoop.hdfs.tools.CacheAdmin @@ -103,13 +101,8 @@ function hdfscmd_case HADOOP_SECURE_PID_DIR="${HADOOP_SECURE_PID_DIR:-$HADOOP_SECURE_DN_PID_DIR}" HADOOP_SECURE_LOG_DIR="${HADOOP_SECURE_LOG_DIR:-$HADOOP_SECURE_DN_LOG_DIR}" - hadoop_debug "Appending HADOOP_DATANODE_OPTS onto HADOOP_OPTS" - hadoop_debug "Appending HADOOP_DN_SECURE_EXTRA_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_DATANODE_OPTS} ${HADOOP_DN_SECURE_EXTRA_OPTS}" HADOOP_CLASSNAME="org.apache.hadoop.hdfs.server.datanode.SecureDataNodeStarter" else - hadoop_debug "Appending HADOOP_DATANODE_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_DATANODE_OPTS}" HADOOP_CLASSNAME='org.apache.hadoop.hdfs.server.datanode.DataNode' fi ;; @@ -118,18 +111,12 @@ function hdfscmd_case ;; dfs) HADOOP_CLASSNAME=org.apache.hadoop.fs.FsShell - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" ;; dfsadmin) HADOOP_CLASSNAME=org.apache.hadoop.hdfs.tools.DFSAdmin - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" ;; diskbalancer) - HADOOP_CLASSNAME=org.apache.hadoop.hdfs.tools.DiskBalancer - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" + HADOOP_CLASSNAME=org.apache.hadoop.hdfs.tools.DiskBalancerCLI ;; envvars) echo "JAVA_HOME='${JAVA_HOME}'" @@ -144,16 +131,12 @@ function hdfscmd_case ;; erasurecode) HADOOP_CLASSNAME=org.apache.hadoop.hdfs.tools.erasurecode.ECCli - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" ;; fetchdt) HADOOP_CLASSNAME=org.apache.hadoop.hdfs.tools.DelegationTokenFetcher ;; fsck) HADOOP_CLASSNAME=org.apache.hadoop.hdfs.tools.DFSck - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" ;; getconf) HADOOP_CLASSNAME=org.apache.hadoop.hdfs.tools.GetConf @@ -163,14 +146,10 @@ function hdfscmd_case ;; haadmin) HADOOP_CLASSNAME=org.apache.hadoop.hdfs.tools.DFSHAAdmin - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" ;; journalnode) HADOOP_SUBCMD_SUPPORTDAEMONIZATION="true" HADOOP_CLASSNAME='org.apache.hadoop.hdfs.qjournal.server.JournalNode' - hadoop_debug "Appending HADOOP_JOURNALNODE_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_JOURNALNODE_OPTS}" ;; jmxget) HADOOP_CLASSNAME=org.apache.hadoop.hdfs.tools.JMXGet @@ -181,14 +160,10 @@ function hdfscmd_case mover) HADOOP_SUBCMD_SUPPORTDAEMONIZATION="true" HADOOP_CLASSNAME=org.apache.hadoop.hdfs.server.mover.Mover - hadoop_debug "Appending HADOOP_MOVER_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_MOVER_OPTS}" ;; namenode) HADOOP_SUBCMD_SUPPORTDAEMONIZATION="true" HADOOP_CLASSNAME='org.apache.hadoop.hdfs.server.namenode.NameNode' - hadoop_debug "Appending HADOOP_NAMENODE_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_NAMENODE_OPTS}" hadoop_add_param HADOOP_OPTS hdfs.audit.logger "-Dhdfs.audit.logger=${HDFS_AUDIT_LOGGER}" ;; nfs3) @@ -201,13 +176,8 @@ function hdfscmd_case HADOOP_SECURE_PID_DIR="${HADOOP_SECURE_PID_DIR:-$HADOOP_SECURE_NFS3_PID_DIR}" HADOOP_SECURE_LOG_DIR="${HADOOP_SECURE_LOG_DIR:-$HADOOP_SECURE_NFS3_LOG_DIR}" - hadoop_debug "Appending HADOOP_NFS3_OPTS onto HADOOP_OPTS" - hadoop_debug "Appending HADOOP_NFS3_SECURE_EXTRA_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_NFS3_OPTS} ${HADOOP_NFS3_SECURE_EXTRA_OPTS}" HADOOP_CLASSNAME=org.apache.hadoop.hdfs.nfs.nfs3.PrivilegedNfsGatewayStarter else - hadoop_debug "Appending HADOOP_NFS3_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_NFS3_OPTS}" HADOOP_CLASSNAME=org.apache.hadoop.hdfs.nfs.nfs3.Nfs3 fi ;; @@ -223,14 +193,10 @@ function hdfscmd_case portmap) HADOOP_SUBCMD_SUPPORTDAEMONIZATION="true" HADOOP_CLASSNAME=org.apache.hadoop.portmap.Portmap - hadoop_debug "Appending HADOOP_PORTMAP_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_PORTMAP_OPTS}" ;; secondarynamenode) HADOOP_SUBCMD_SUPPORTDAEMONIZATION="true" HADOOP_CLASSNAME='org.apache.hadoop.hdfs.server.namenode.SecondaryNameNode' - hadoop_debug "Appending HADOOP_SECONDARYNAMENODE_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_SECONDARYNAMENODE_OPTS}" hadoop_add_param HADOOP_OPTS hdfs.audit.logger "-Dhdfs.audit.logger=${HDFS_AUDIT_LOGGER}" ;; snapshotDiff) @@ -245,8 +211,6 @@ function hdfscmd_case zkfc) HADOOP_SUBCMD_SUPPORTDAEMONIZATION="true" HADOOP_CLASSNAME='org.apache.hadoop.hdfs.tools.DFSZKFailoverController' - hadoop_debug "Appending HADOOP_ZKFC_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_ZKFC_OPTS}" ;; *) HADOOP_CLASSNAME="${subcmd}" @@ -282,6 +246,8 @@ fi HADOOP_SUBCMD=$1 shift +hadoop_verify_user "${HADOOP_SHELL_EXECNAME}" "${HADOOP_SUBCMD}" + HADOOP_SUBCMD_ARGS=("$@") if declare -f hdfs_subcommand_"${HADOOP_SUBCMD}" >/dev/null 2>&1; then @@ -291,15 +257,20 @@ else hdfscmd_case "${HADOOP_SUBCMD}" "${HADOOP_SUBCMD_ARGS[@]}" fi -hadoop_verify_user "${HADOOP_SUBCMD}" +hadoop_add_client_opts if [[ ${HADOOP_WORKER_MODE} = true ]]; then hadoop_common_worker_mode_execute "${HADOOP_HDFS_HOME}/bin/hdfs" "${HADOOP_USER_PARAMS[@]}" exit $? fi +hadoop_subcommand_opts "${HADOOP_SHELL_EXECNAME}" "${HADOOP_SUBCMD}" + if [[ "${HADOOP_SUBCMD_SECURESERVICE}" = true ]]; then HADOOP_SECURE_USER="${HADOOP_SUBCMD_SECUREUSER}" + + hadoop_subcommand_secure_opts "${HADOOP_SHELL_EXECNAME}" "${HADOOP_SUBCMD}" + hadoop_verify_secure_prereq hadoop_setup_secure_service priv_outfile="${HADOOP_LOG_DIR}/privileged-${HADOOP_IDENT_STRING}-${HADOOP_SUBCMD}-${HOSTNAME}.out" diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/bin/hdfs-config.sh b/hadoop-hdfs-project/hadoop-hdfs/src/main/bin/hdfs-config.sh old mode 100644 new mode 100755 index d440210992f..cba37a44950 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/bin/hdfs-config.sh +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/bin/hdfs-config.sh @@ -26,7 +26,7 @@ function hadoop_subproject_init export HADOOP_HDFS_ENV_PROCESSED=true fi fi - + # at some point in time, someone thought it would be a good idea to # create separate vars for every subproject. *sigh* # let's perform some overrides and setup some defaults for bw compat @@ -42,23 +42,31 @@ function hadoop_subproject_init hadoop_deprecate_envvar HADOOP_HDFS_NICENESS HADOOP_NICENESS hadoop_deprecate_envvar HADOOP_HDFS_STOP_TIMEOUT HADOOP_STOP_TIMEOUT - + hadoop_deprecate_envvar HADOOP_HDFS_PID_DIR HADOOP_PID_DIR hadoop_deprecate_envvar HADOOP_HDFS_ROOT_LOGGER HADOOP_ROOT_LOGGER hadoop_deprecate_envvar HADOOP_HDFS_IDENT_STRING HADOOP_IDENT_STRING - + + hadoop_deprecate_envvar HADOOP_DN_SECURE_EXTRA_OPTS HDFS_DATANODE_SECURE_EXTRA_OPTS + + hadoop_deprecate_envvar HADOOP_NFS3_SECURE_EXTRA_OPTS HDFS_NFS3_SECURE_EXTRA_OPTS + + HADOOP_HDFS_HOME="${HADOOP_HDFS_HOME:-$HADOOP_HOME}" - + # turn on the defaults export HDFS_AUDIT_LOGGER=${HDFS_AUDIT_LOGGER:-INFO,NullAppender} - export HADOOP_NAMENODE_OPTS=${HADOOP_NAMENODE_OPTS:-"-Dhadoop.security.logger=INFO,RFAS"} - export HADOOP_SECONDARYNAMENODE_OPTS=${HADOOP_SECONDARYNAMENODE_OPTS:-"-Dhadoop.security.logger=INFO,RFAS"} - export HADOOP_DATANODE_OPTS=${HADOOP_DATANODE_OPTS:-"-Dhadoop.security.logger=ERROR,RFAS"} - export HADOOP_DN_SECURE_EXTRA_OPTS=${HADOOP_DN_SECURE_EXTRA_OPTS:-"-jvm server"} - export HADOOP_NFS3_SECURE_EXTRA_OPTS=${HADOOP_NFS3_SECURE_EXTRA_OPTS:-"-jvm server"} - export HADOOP_PORTMAP_OPTS=${HADOOP_PORTMAP_OPTS:-"-Xmx512m"} + export HDFS_NAMENODE_OPTS=${HDFS_NAMENODE_OPTS:-"-Dhadoop.security.logger=INFO,RFAS"} + export HDFS_SECONDARYNAMENODE_OPTS=${HDFS_SECONDARYNAMENODE_OPTS:-"-Dhadoop.security.logger=INFO,RFAS"} + export HDFS_DATANODE_OPTS=${HDFS_DATANODE_OPTS:-"-Dhadoop.security.logger=ERROR,RFAS"} + export HDFS_PORTMAP_OPTS=${HDFS_PORTMAP_OPTS:-"-Xmx512m"} + + # depending upon what is being used to start Java, these may need to be + # set empty. (thus no colon) + export HDFS_DATANODE_SECURE_EXTRA_OPTS=${HDFS_DATANODE_SECURE_EXTRA_OPTS-"-jvm server"} + export HDFS_NFS3_SECURE_EXTRA_OPTS=${HDFS_NFS3_SECURE_EXTRA_OPTS-"-jvm server"} } if [[ -z "${HADOOP_LIBEXEC_DIR}" ]]; then diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSConfigKeys.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSConfigKeys.java index 7bdfd440d58..caf6b6078dc 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSConfigKeys.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSConfigKeys.java @@ -419,6 +419,11 @@ public class DFSConfigKeys extends CommonConfigurationKeys { public static final String DFS_NAMENODE_READ_LOCK_REPORTING_THRESHOLD_MS_KEY = "dfs.namenode.read-lock-reporting-threshold-ms"; public static final long DFS_NAMENODE_READ_LOCK_REPORTING_THRESHOLD_MS_DEFAULT = 5000L; + // Threshold for how long the lock warnings must be suppressed + public static final String DFS_LOCK_SUPPRESS_WARNING_INTERVAL_KEY = + "dfs.lock.suppress.warning.interval"; + public static final long DFS_LOCK_SUPPRESS_WARNING_INTERVAL_DEFAULT = + 10000; //ms public static final String DFS_UPGRADE_DOMAIN_FACTOR = "dfs.namenode.upgrade.domain.factor"; public static final int DFS_UPGRADE_DOMAIN_FACTOR_DEFAULT = DFS_REPLICATION_DEFAULT; diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/InstrumentedLock.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/InstrumentedLock.java new file mode 100644 index 00000000000..6279e955221 --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/InstrumentedLock.java @@ -0,0 +1,185 @@ +/** + * 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; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceStability; +import org.apache.commons.logging.Log; +import org.apache.hadoop.util.StringUtils; +import org.apache.hadoop.util.Timer; + +import com.google.common.annotations.VisibleForTesting; + +/** + * This is a debugging class that can be used by callers to track + * whether a specifc lock is being held for too long and periodically + * log a warning and stack trace, if so. + * + * The logged warnings are throttled so that logs are not spammed. + * + * A new instance of InstrumentedLock can be created for each object + * that needs to be instrumented. + */ +@InterfaceAudience.Private +@InterfaceStability.Unstable +public class InstrumentedLock implements Lock { + + private final Lock lock; + private final Log logger; + private final String name; + private final Timer clock; + + /** Minimum gap between two lock warnings. */ + private final long minLoggingGap; + /** Threshold for detecting long lock held time. */ + private final long lockWarningThreshold; + + // Tracking counters for lock statistics. + private volatile long lockAcquireTimestamp; + private final AtomicLong lastLogTimestamp; + private final AtomicLong warningsSuppressed = new AtomicLong(0); + + /** + * Create a instrumented lock instance which logs a warning message + * when lock held time is above given threshold. + * + * @param name the identifier of the lock object + * @param logger this class does not have its own logger, will log to the + * given logger instead + * @param minLoggingGapMs the minimum time gap between two log messages, + * this is to avoid spamming to many logs + * @param lockWarningThresholdMs the time threshold to view lock held + * time as being "too long" + */ + public InstrumentedLock(String name, Log logger, long minLoggingGapMs, + long lockWarningThresholdMs) { + this(name, logger, new ReentrantLock(), + minLoggingGapMs, lockWarningThresholdMs); + } + + public InstrumentedLock(String name, Log logger, Lock lock, + long minLoggingGapMs, long lockWarningThresholdMs) { + this(name, logger, lock, + minLoggingGapMs, lockWarningThresholdMs, new Timer()); + } + + @VisibleForTesting + InstrumentedLock(String name, Log logger, Lock lock, + long minLoggingGapMs, long lockWarningThresholdMs, Timer clock) { + this.name = name; + this.lock = lock; + this.clock = clock; + this.logger = logger; + minLoggingGap = minLoggingGapMs; + lockWarningThreshold = lockWarningThresholdMs; + lastLogTimestamp = new AtomicLong( + clock.monotonicNow() - Math.max(minLoggingGap, lockWarningThreshold)); + } + + @Override + public void lock() { + lock.lock(); + lockAcquireTimestamp = clock.monotonicNow(); + } + + @Override + public void lockInterruptibly() throws InterruptedException { + lock.lockInterruptibly(); + lockAcquireTimestamp = clock.monotonicNow(); + } + + @Override + public boolean tryLock() { + if (lock.tryLock()) { + lockAcquireTimestamp = clock.monotonicNow(); + return true; + } + return false; + } + + @Override + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { + if (lock.tryLock(time, unit)) { + lockAcquireTimestamp = clock.monotonicNow(); + return true; + } + return false; + } + + @Override + public void unlock() { + long localLockReleaseTime = clock.monotonicNow(); + long localLockAcquireTime = lockAcquireTimestamp; + lock.unlock(); + check(localLockAcquireTime, localLockReleaseTime); + } + + @Override + public Condition newCondition() { + return lock.newCondition(); + } + + @VisibleForTesting + void logWarning(long lockHeldTime, long suppressed) { + logger.warn(String.format("Lock held time above threshold: " + + "lock identifier: %s " + + "lockHeldTimeMs=%d ms. Suppressed %d lock warnings. " + + "The stack trace is: %s" , + name, lockHeldTime, suppressed, + StringUtils.getStackTrace(Thread.currentThread()))); + } + + /** + * Log a warning if the lock was held for too long. + * + * Should be invoked by the caller immediately AFTER releasing the lock. + * + * @param acquireTime - timestamp just after acquiring the lock. + * @param releaseTime - timestamp just before releasing the lock. + */ + private void check(long acquireTime, long releaseTime) { + if (!logger.isWarnEnabled()) { + return; + } + + final long lockHeldTime = releaseTime - acquireTime; + if (lockWarningThreshold - lockHeldTime < 0) { + long now; + long localLastLogTs; + do { + now = clock.monotonicNow(); + localLastLogTs = lastLogTimestamp.get(); + long deltaSinceLastLog = now - localLastLogTs; + // check should print log or not + if (deltaSinceLastLog - minLoggingGap < 0) { + warningsSuppressed.incrementAndGet(); + return; + } + } while (!lastLogTimestamp.compareAndSet(localLastLogTs, now)); + long suppressed = warningsSuppressed.getAndSet(0); + logWarning(lockHeldTime, suppressed); + } + } + +} diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/BlockManager.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/BlockManager.java index 1362c0bba95..3a12d74120a 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/BlockManager.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/BlockManager.java @@ -2624,7 +2624,7 @@ public class BlockManager implements BlockStatsMXBean { } while (storageBlock != null); } - // Iterate any remaing blocks that have not been reported and remove them + // Iterate any remaining blocks that have not been reported and remove them while (storageBlocksIterator.hasNext()) { toRemove.add(storageBlocksIterator.next()); } @@ -2677,7 +2677,7 @@ public class BlockManager implements BlockStatsMXBean { corruptReplicas.isReplicaCorrupt(storedBlock, dn))) { // Add replica if appropriate. If the replica was previously corrupt // but now okay, it might need to be updated. - toAdd.add(new BlockInfoToAdd(storedBlock, replica)); + toAdd.add(new BlockInfoToAdd(storedBlock, new Block(replica))); } } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/BPServiceActor.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/BPServiceActor.java index 4bde758bc19..f3247fca27d 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/BPServiceActor.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/BPServiceActor.java @@ -1179,10 +1179,11 @@ class BPServiceActor implements Runnable { resetBlockReportTime = false; } else { /* say the last block report was at 8:20:14. The current report - * should have started around 9:20:14 (default 1 hour interval). + * should have started around 14:20:14 (default 6 hour interval). * If current time is : - * 1) normal like 9:20:18, next report should be at 10:20:14 - * 2) unexpected like 11:35:43, next report should be at 12:20:14 + * 1) normal like 14:20:18, next report should be at 20:20:14. + * 2) unexpected like 21:35:43, next report should be at 2:20:14 + * on the next day. */ nextBlockReportTime += (((monotonicNow() - nextBlockReportTime + blockReportIntervalMs) / diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/DiskBalancer.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/DiskBalancer.java index ec72d97d5c7..d853ae945e4 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/DiskBalancer.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/DiskBalancer.java @@ -121,17 +121,23 @@ public class DiskBalancer { */ public void shutdown() { lock.lock(); + boolean needShutdown = false; try { this.isDiskBalancerEnabled = false; this.currentResult = Result.NO_PLAN; if ((this.future != null) && (!this.future.isDone())) { this.currentResult = Result.PLAN_CANCELLED; this.blockMover.setExitFlag(); - shutdownExecutor(); + scheduler.shutdown(); + needShutdown = true; } } finally { lock.unlock(); } + // no need to hold lock while shutting down executor. + if (needShutdown) { + shutdownExecutor(); + } } /** @@ -139,7 +145,6 @@ public class DiskBalancer { */ private void shutdownExecutor() { final int secondsTowait = 10; - scheduler.shutdown(); try { if (!scheduler.awaitTermination(secondsTowait, TimeUnit.SECONDS)) { scheduler.shutdownNow(); @@ -228,6 +233,7 @@ public class DiskBalancer { */ public void cancelPlan(String planID) throws DiskBalancerException { lock.lock(); + boolean needShutdown = false; try { checkDiskBalancerEnabled(); if (this.planID == null || @@ -239,13 +245,18 @@ public class DiskBalancer { DiskBalancerException.Result.NO_SUCH_PLAN); } if (!this.future.isDone()) { - this.blockMover.setExitFlag(); - shutdownExecutor(); this.currentResult = Result.PLAN_CANCELLED; + this.blockMover.setExitFlag(); + scheduler.shutdown(); + needShutdown = true; } } finally { lock.unlock(); } + // no need to hold lock while shutting down executor. + if (needShutdown) { + shutdownExecutor(); + } } /** @@ -490,14 +501,11 @@ public class DiskBalancer { public void run() { Thread.currentThread().setName("DiskBalancerThread"); LOG.info("Executing Disk balancer plan. Plan File: {}, Plan ID: {}", - planFile, planID); - try { - for (Map.Entry entry : - workMap.entrySet()) { - blockMover.copyBlocks(entry.getKey(), entry.getValue()); - } - } finally { - blockMover.setExitFlag(); + planFile, planID); + for (Map.Entry entry : + workMap.entrySet()) { + blockMover.setRunnable(); + blockMover.copyBlocks(entry.getKey(), entry.getValue()); } } }); @@ -846,8 +854,8 @@ public class DiskBalancer { if (item.getErrorCount() >= getMaxError(item)) { item.setErrMsg("Error count exceeded."); - LOG.info("Maximum error count exceeded. Error count: {} Max error:{} " - , item.getErrorCount(), item.getMaxDiskErrors()); + LOG.info("Maximum error count exceeded. Error count: {} Max error:{} ", + item.getErrorCount(), item.getMaxDiskErrors()); } return null; @@ -951,7 +959,8 @@ public class DiskBalancer { LOG.error("Exceeded the max error count. source {}, dest: {} " + "error count: {}", source.getBasePath(), dest.getBasePath(), item.getErrorCount()); - break; + this.setExitFlag(); + continue; } // Check for the block tolerance constraint. @@ -960,7 +969,8 @@ public class DiskBalancer { "blocks.", source.getBasePath(), dest.getBasePath(), item.getBytesCopied(), item.getBlocksCopied()); - break; + this.setExitFlag(); + continue; } ExtendedBlock block = getNextBlock(poolIters, item); @@ -968,7 +978,8 @@ public class DiskBalancer { if (block == null) { LOG.error("No source blocks, exiting the copy. Source: {}, " + "dest:{}", source.getBasePath(), dest.getBasePath()); - break; + this.setExitFlag(); + continue; } // check if someone told us exit, treat this as an interruption @@ -976,7 +987,7 @@ public class DiskBalancer { // for the thread, since both getNextBlock and moveBlocAcrossVolume // can take some time. if (!shouldRun()) { - break; + continue; } long timeUsed; @@ -995,7 +1006,8 @@ public class DiskBalancer { LOG.error("Destination volume: {} does not have enough space to" + " accommodate a block. Block Size: {} Exiting from" + " copyBlocks.", dest.getBasePath(), block.getNumBytes()); - break; + this.setExitFlag(); + continue; } LOG.debug("Moved block with size {} from {} to {}", diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/FsDatasetImpl.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/FsDatasetImpl.java index ffd2f8ab34d..e9f1dc13a8a 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/FsDatasetImpl.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/FsDatasetImpl.java @@ -40,6 +40,8 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.TimeUnit; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; @@ -60,6 +62,7 @@ import org.apache.hadoop.fs.StorageType; import org.apache.hadoop.hdfs.DFSConfigKeys; import org.apache.hadoop.hdfs.DFSUtilClient; import org.apache.hadoop.hdfs.ExtendedBlockId; +import org.apache.hadoop.hdfs.InstrumentedLock; import org.apache.hadoop.util.AutoCloseableLock; import org.apache.hadoop.hdfs.protocol.Block; import org.apache.hadoop.hdfs.protocol.BlockListAsLongs; @@ -267,6 +270,7 @@ class FsDatasetImpl implements FsDatasetSpi { private final int maxDataLength; private final AutoCloseableLock datasetLock; + private final Condition datasetLockCondition; /** * An FSDataset has a directory where it loads its data files. @@ -278,7 +282,15 @@ class FsDatasetImpl implements FsDatasetSpi { this.dataStorage = storage; this.conf = conf; this.smallBufferSize = DFSUtilClient.getSmallBufferSize(conf); - this.datasetLock = new AutoCloseableLock(); + this.datasetLock = new AutoCloseableLock( + new InstrumentedLock(getClass().getName(), LOG, + conf.getTimeDuration( + DFSConfigKeys.DFS_LOCK_SUPPRESS_WARNING_INTERVAL_KEY, + DFSConfigKeys.DFS_LOCK_SUPPRESS_WARNING_INTERVAL_DEFAULT, + TimeUnit.MILLISECONDS), + 300)); + this.datasetLockCondition = datasetLock.newCondition(); + // The number of volumes required for operation is the total number // of volumes minus the number of failed volumes we can tolerate. volFailuresTolerated = datanode.getDnConf().getVolFailuresTolerated(); @@ -515,7 +527,7 @@ class FsDatasetImpl implements FsDatasetSpi { // Disable the volume from the service. asyncDiskService.removeVolume(sd.getCurrentDir()); volumes.removeVolume(absRoot, clearFailure); - volumes.waitVolumeRemoved(5000, this); + volumes.waitVolumeRemoved(5000, datasetLockCondition); // Removed all replica information for the blocks on the volume. // Unlike updating the volumeMap in addVolume(), this operation does diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/FsVolumeList.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/FsVolumeList.java index ea4d5975cd0..634ad42d89c 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/FsVolumeList.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/FsVolumeList.java @@ -31,6 +31,8 @@ import java.util.Map; import java.util.TreeMap; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.StorageType; @@ -41,6 +43,7 @@ import org.apache.hadoop.hdfs.server.datanode.fsdataset.VolumeChoosingPolicy; import org.apache.hadoop.hdfs.server.datanode.BlockScanner; import org.apache.hadoop.hdfs.server.protocol.DatanodeStorage; import org.apache.hadoop.io.IOUtils; +import org.apache.hadoop.util.AutoCloseableLock; import org.apache.hadoop.util.DiskChecker.DiskErrorException; import org.apache.hadoop.util.Time; @@ -52,7 +55,8 @@ class FsVolumeList { Collections.synchronizedMap(new TreeMap()); private final ConcurrentLinkedQueue volumesBeingRemoved = new ConcurrentLinkedQueue<>(); - private Object checkDirsMutex = new Object(); + private final AutoCloseableLock checkDirsLock; + private final Condition checkDirsLockCondition; private final VolumeChoosingPolicy blockChooser; private final BlockScanner blockScanner; @@ -62,6 +66,8 @@ class FsVolumeList { VolumeChoosingPolicy blockChooser) { this.blockChooser = blockChooser; this.blockScanner = blockScanner; + this.checkDirsLock = new AutoCloseableLock(); + this.checkDirsLockCondition = checkDirsLock.newCondition(); for (VolumeFailureInfo volumeFailureInfo: initialVolumeFailureInfos) { volumeFailureInfos.put(volumeFailureInfo.getFailedStorageLocation(), volumeFailureInfo); @@ -224,12 +230,12 @@ class FsVolumeList { /** * Calls {@link FsVolumeImpl#checkDirs()} on each volume. * - * Use checkDirsMutext to allow only one instance of checkDirs() call + * Use {@link checkDirsLock} to allow only one instance of checkDirs() call. * * @return list of all the failed volumes. */ Set checkDirs() { - synchronized(checkDirsMutex) { + try (AutoCloseableLock lock = checkDirsLock.acquire()) { Set failedVols = null; // Make a copy of volumes for performing modification @@ -260,7 +266,7 @@ class FsVolumeList { + " failure volumes."); } - waitVolumeRemoved(5000, checkDirsMutex); + waitVolumeRemoved(5000, checkDirsLockCondition); return failedVols; } } @@ -271,13 +277,13 @@ class FsVolumeList { * * @param sleepMillis interval to recheck. */ - void waitVolumeRemoved(int sleepMillis, Object monitor) { + void waitVolumeRemoved(int sleepMillis, Condition condition) { while (!checkVolumesRemoved()) { if (FsDatasetImpl.LOG.isDebugEnabled()) { FsDatasetImpl.LOG.debug("Waiting for volume reference to be released."); } try { - monitor.wait(sleepMillis); + condition.await(sleepMillis, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { FsDatasetImpl.LOG.info("Thread interrupted when waiting for " + "volume reference to be released."); diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/DiskBalancerException.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/DiskBalancerException.java index a420b047354..95ff722a2fa 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/DiskBalancerException.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/DiskBalancerException.java @@ -38,7 +38,8 @@ public class DiskBalancerException extends IOException { INVALID_MOVE, INTERNAL_ERROR, NO_SUCH_PLAN, - UNKNOWN_KEY + UNKNOWN_KEY, + INVALID_NODE, } private final Result result; diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/CancelCommand.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/CancelCommand.java index 8b83e270f23..007272eda9e 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/CancelCommand.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/CancelCommand.java @@ -29,7 +29,7 @@ import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.hdfs.protocol.ClientDatanodeProtocol; import org.apache.hadoop.hdfs.server.diskbalancer.DiskBalancerException; import org.apache.hadoop.hdfs.server.diskbalancer.planner.NodePlan; -import org.apache.hadoop.hdfs.tools.DiskBalancer; +import org.apache.hadoop.hdfs.tools.DiskBalancerCLI; import java.io.IOException; @@ -44,9 +44,10 @@ public class CancelCommand extends Command { */ public CancelCommand(Configuration conf) { super(conf); - addValidCommandParameters(DiskBalancer.CANCEL, "Cancels a running plan."); - addValidCommandParameters(DiskBalancer.NODE, "Node to run the command " + - "against in node:port format."); + addValidCommandParameters(DiskBalancerCLI.CANCEL, + "Cancels a running plan."); + addValidCommandParameters(DiskBalancerCLI.NODE, + "Node to run the command against in node:port format."); } /** @@ -57,20 +58,20 @@ public class CancelCommand extends Command { @Override public void execute(CommandLine cmd) throws Exception { LOG.info("Executing \"Cancel plan\" command."); - Preconditions.checkState(cmd.hasOption(DiskBalancer.CANCEL)); - verifyCommandOptions(DiskBalancer.CANCEL, cmd); + Preconditions.checkState(cmd.hasOption(DiskBalancerCLI.CANCEL)); + verifyCommandOptions(DiskBalancerCLI.CANCEL, cmd); // We can cancel a plan using datanode address and plan ID // that you can read from a datanode using queryStatus - if(cmd.hasOption(DiskBalancer.NODE)) { - String nodeAddress = cmd.getOptionValue(DiskBalancer.NODE); - String planHash = cmd.getOptionValue(DiskBalancer.CANCEL); + if(cmd.hasOption(DiskBalancerCLI.NODE)) { + String nodeAddress = cmd.getOptionValue(DiskBalancerCLI.NODE); + String planHash = cmd.getOptionValue(DiskBalancerCLI.CANCEL); cancelPlanUsingHash(nodeAddress, planHash); } else { // Or you can cancel a plan using the plan file. If the user // points us to the plan file, we can compute the hash as well as read // the address of the datanode from the plan file. - String planFile = cmd.getOptionValue(DiskBalancer.CANCEL); + String planFile = cmd.getOptionValue(DiskBalancerCLI.CANCEL); Preconditions.checkArgument(planFile != null && !planFile.isEmpty(), "Invalid plan file specified."); String planData = null; @@ -142,6 +143,6 @@ public class CancelCommand extends Command { HelpFormatter helpFormatter = new HelpFormatter(); helpFormatter.printHelp("hdfs diskbalancer -cancel | -cancel " + " -node ", - header, DiskBalancer.getCancelOptions(), footer); + header, DiskBalancerCLI.getCancelOptions(), footer); } } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/Command.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/Command.java index 5acd0aca3e4..24976694f39 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/Command.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/Command.java @@ -37,13 +37,14 @@ import org.apache.hadoop.hdfs.DFSConfigKeys; import org.apache.hadoop.hdfs.DFSUtilClient; import org.apache.hadoop.hdfs.protocol.ClientDatanodeProtocol; import org.apache.hadoop.hdfs.server.diskbalancer.DiskBalancerConstants; +import org.apache.hadoop.hdfs.server.diskbalancer.DiskBalancerException; import org.apache.hadoop.hdfs.server.diskbalancer.connectors.ClusterConnector; import org.apache.hadoop.hdfs.server.diskbalancer.connectors.ConnectorFactory; import org.apache.hadoop.hdfs.server.diskbalancer.datamodel.DiskBalancerCluster; import org.apache.hadoop.hdfs.server.diskbalancer.datamodel.DiskBalancerDataNode; import org.apache.hadoop.hdfs.server.diskbalancer.datamodel.DiskBalancerVolume; import org.apache.hadoop.hdfs.server.diskbalancer.datamodel.DiskBalancerVolumeSet; -import org.apache.hadoop.hdfs.tools.DiskBalancer; +import org.apache.hadoop.hdfs.tools.DiskBalancerCLI; import org.apache.hadoop.net.NetUtils; import org.apache.hadoop.security.UserGroupInformation; import org.codehaus.jackson.map.ObjectMapper; @@ -256,6 +257,7 @@ public abstract class Command extends Configured { throws IOException { Set nodeNames = null; List nodeList = Lists.newArrayList(); + List invalidNodeList = Lists.newArrayList(); if ((listArg == null) || listArg.isEmpty()) { return nodeList; @@ -269,10 +271,22 @@ public abstract class Command extends Configured { if (node != null) { nodeList.add(node); + } else { + invalidNodeList.add(name); } } } + if (!invalidNodeList.isEmpty()) { + String invalidNodes = StringUtils.join(invalidNodeList.toArray(), ","); + String warnMsg = String.format( + "The node(s) '%s' not found. " + + "Please make sure that '%s' exists in the cluster.", + invalidNodes, invalidNodes); + throw new DiskBalancerException(warnMsg, + DiskBalancerException.Result.INVALID_NODE); + } + return nodeList; } @@ -418,7 +432,7 @@ public abstract class Command extends Configured { * @return default top number of nodes. */ protected int getDefaultTop() { - return DiskBalancer.DEFAULT_TOP; + return DiskBalancerCLI.DEFAULT_TOP; } /** @@ -437,7 +451,7 @@ public abstract class Command extends Configured { protected int parseTopNodes(final CommandLine cmd, final StrBuilder result) { String outputLine = ""; int nodes = 0; - final String topVal = cmd.getOptionValue(DiskBalancer.TOP); + final String topVal = cmd.getOptionValue(DiskBalancerCLI.TOP); if (StringUtils.isBlank(topVal)) { outputLine = String.format( "No top limit specified, using default top value %d.", diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/ExecuteCommand.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/ExecuteCommand.java index f363c340fa2..3a348c9facb 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/ExecuteCommand.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/ExecuteCommand.java @@ -29,7 +29,7 @@ import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.hdfs.protocol.ClientDatanodeProtocol; import org.apache.hadoop.hdfs.server.diskbalancer.DiskBalancerException; import org.apache.hadoop.hdfs.server.diskbalancer.planner.NodePlan; -import org.apache.hadoop.hdfs.tools.DiskBalancer; +import org.apache.hadoop.hdfs.tools.DiskBalancerCLI; import java.io.IOException; @@ -46,7 +46,8 @@ public class ExecuteCommand extends Command { */ public ExecuteCommand(Configuration conf) { super(conf); - addValidCommandParameters(DiskBalancer.EXECUTE, "Executes a given plan."); + addValidCommandParameters(DiskBalancerCLI.EXECUTE, + "Executes a given plan."); } /** @@ -57,10 +58,10 @@ public class ExecuteCommand extends Command { @Override public void execute(CommandLine cmd) throws Exception { LOG.info("Executing \"execute plan\" command"); - Preconditions.checkState(cmd.hasOption(DiskBalancer.EXECUTE)); - verifyCommandOptions(DiskBalancer.EXECUTE, cmd); + Preconditions.checkState(cmd.hasOption(DiskBalancerCLI.EXECUTE)); + verifyCommandOptions(DiskBalancerCLI.EXECUTE, cmd); - String planFile = cmd.getOptionValue(DiskBalancer.EXECUTE); + String planFile = cmd.getOptionValue(DiskBalancerCLI.EXECUTE); Preconditions.checkArgument(planFile != null && !planFile.isEmpty(), "Invalid plan file specified."); @@ -88,7 +89,7 @@ public class ExecuteCommand extends Command { String planHash = DigestUtils.shaHex(planData); try { // TODO : Support skipping date check. - dataNode.submitDiskBalancerPlan(planHash, DiskBalancer.PLAN_VERSION, + dataNode.submitDiskBalancerPlan(planHash, DiskBalancerCLI.PLAN_VERSION, planFile, planData, false); } catch (DiskBalancerException ex) { LOG.error("Submitting plan on {} failed. Result: {}, Message: {}", @@ -111,6 +112,6 @@ public class ExecuteCommand extends Command { HelpFormatter helpFormatter = new HelpFormatter(); helpFormatter.printHelp("hdfs diskbalancer -execute ", - header, DiskBalancer.getExecuteOptions(), footer); + header, DiskBalancerCLI.getExecuteOptions(), footer); } } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/HelpCommand.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/HelpCommand.java index 3c2fd0cf7db..c7352997e2b 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/HelpCommand.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/HelpCommand.java @@ -23,7 +23,7 @@ import com.google.common.base.Preconditions; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.HelpFormatter; import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.hdfs.tools.DiskBalancer; +import org.apache.hadoop.hdfs.tools.DiskBalancerCLI; /** * Help Command prints out detailed help about each command. @@ -37,7 +37,7 @@ public class HelpCommand extends Command { */ public HelpCommand(Configuration conf) { super(conf); - addValidCommandParameters(DiskBalancer.HELP, "Help Command"); + addValidCommandParameters(DiskBalancerCLI.HELP, "Help Command"); } /** @@ -53,9 +53,9 @@ public class HelpCommand extends Command { return; } - Preconditions.checkState(cmd.hasOption(DiskBalancer.HELP)); - verifyCommandOptions(DiskBalancer.HELP, cmd); - String helpCommand = cmd.getOptionValue(DiskBalancer.HELP); + Preconditions.checkState(cmd.hasOption(DiskBalancerCLI.HELP)); + verifyCommandOptions(DiskBalancerCLI.HELP, cmd); + String helpCommand = cmd.getOptionValue(DiskBalancerCLI.HELP); if (helpCommand == null || helpCommand.isEmpty()) { this.printHelp(); return; @@ -65,19 +65,19 @@ public class HelpCommand extends Command { helpCommand = helpCommand.toLowerCase(); Command command = null; switch (helpCommand) { - case DiskBalancer.PLAN: + case DiskBalancerCLI.PLAN: command = new PlanCommand(getConf()); break; - case DiskBalancer.EXECUTE: + case DiskBalancerCLI.EXECUTE: command = new ExecuteCommand(getConf()); break; - case DiskBalancer.QUERY: + case DiskBalancerCLI.QUERY: command = new QueryCommand(getConf()); break; - case DiskBalancer.CANCEL: + case DiskBalancerCLI.CANCEL: command = new CancelCommand(getConf()); break; - case DiskBalancer.REPORT: + case DiskBalancerCLI.REPORT: command = new ReportCommand(getConf(), null); break; default: @@ -102,7 +102,7 @@ public class HelpCommand extends Command { HelpFormatter helpFormatter = new HelpFormatter(); helpFormatter.printHelp("hdfs diskbalancer [command] [options]", - header, DiskBalancer.getHelpOptions(), ""); + header, DiskBalancerCLI.getHelpOptions(), ""); } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/PlanCommand.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/PlanCommand.java index 72ad2c6bdcb..97494097e6d 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/PlanCommand.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/PlanCommand.java @@ -28,7 +28,7 @@ import org.apache.hadoop.hdfs.server.diskbalancer.datamodel .DiskBalancerDataNode; import org.apache.hadoop.hdfs.server.diskbalancer.planner.NodePlan; import org.apache.hadoop.hdfs.server.diskbalancer.planner.Step; -import org.apache.hadoop.hdfs.tools.DiskBalancer; +import org.apache.hadoop.hdfs.tools.DiskBalancerCLI; import java.nio.charset.StandardCharsets; import java.util.List; @@ -53,18 +53,18 @@ public class PlanCommand extends Command { this.thresholdPercentage = 1; this.bandwidth = 0; this.maxError = 0; - addValidCommandParameters(DiskBalancer.OUTFILE, "Output directory in " + + addValidCommandParameters(DiskBalancerCLI.OUTFILE, "Output directory in " + "HDFS. The generated plan will be written to a file in this " + "directory."); - addValidCommandParameters(DiskBalancer.BANDWIDTH, "Maximum Bandwidth to " + - "be used while copying."); - addValidCommandParameters(DiskBalancer.THRESHOLD, "Percentage skew that " + - "we tolerate before diskbalancer starts working."); - addValidCommandParameters(DiskBalancer.MAXERROR, "Max errors to tolerate " + - "between 2 disks"); - addValidCommandParameters(DiskBalancer.VERBOSE, "Run plan command in " + + addValidCommandParameters(DiskBalancerCLI.BANDWIDTH, + "Maximum Bandwidth to be used while copying."); + addValidCommandParameters(DiskBalancerCLI.THRESHOLD, + "Percentage skew that we tolerate before diskbalancer starts working."); + addValidCommandParameters(DiskBalancerCLI.MAXERROR, + "Max errors to tolerate between 2 disks"); + addValidCommandParameters(DiskBalancerCLI.VERBOSE, "Run plan command in " + "verbose mode."); - addValidCommandParameters(DiskBalancer.PLAN, "Plan Command"); + addValidCommandParameters(DiskBalancerCLI.PLAN, "Plan Command"); } /** @@ -77,36 +77,37 @@ public class PlanCommand extends Command { @Override public void execute(CommandLine cmd) throws Exception { LOG.debug("Processing Plan Command."); - Preconditions.checkState(cmd.hasOption(DiskBalancer.PLAN)); - verifyCommandOptions(DiskBalancer.PLAN, cmd); + Preconditions.checkState(cmd.hasOption(DiskBalancerCLI.PLAN)); + verifyCommandOptions(DiskBalancerCLI.PLAN, cmd); - if (cmd.getOptionValue(DiskBalancer.PLAN) == null) { + if (cmd.getOptionValue(DiskBalancerCLI.PLAN) == null) { throw new IllegalArgumentException("A node name is required to create a" + " plan."); } - if (cmd.hasOption(DiskBalancer.BANDWIDTH)) { - this.bandwidth = Integer.parseInt(cmd.getOptionValue(DiskBalancer + if (cmd.hasOption(DiskBalancerCLI.BANDWIDTH)) { + this.bandwidth = Integer.parseInt(cmd.getOptionValue(DiskBalancerCLI .BANDWIDTH)); } - if (cmd.hasOption(DiskBalancer.MAXERROR)) { - this.maxError = Integer.parseInt(cmd.getOptionValue(DiskBalancer + if (cmd.hasOption(DiskBalancerCLI.MAXERROR)) { + this.maxError = Integer.parseInt(cmd.getOptionValue(DiskBalancerCLI .MAXERROR)); } readClusterInfo(cmd); String output = null; - if (cmd.hasOption(DiskBalancer.OUTFILE)) { - output = cmd.getOptionValue(DiskBalancer.OUTFILE); + if (cmd.hasOption(DiskBalancerCLI.OUTFILE)) { + output = cmd.getOptionValue(DiskBalancerCLI.OUTFILE); } setOutputPath(output); // -plan nodename is the command line argument. - DiskBalancerDataNode node = getNode(cmd.getOptionValue(DiskBalancer.PLAN)); + DiskBalancerDataNode node = + getNode(cmd.getOptionValue(DiskBalancerCLI.PLAN)); if (node == null) { throw new IllegalArgumentException("Unable to find the specified node. " + - cmd.getOptionValue(DiskBalancer.PLAN)); + cmd.getOptionValue(DiskBalancerCLI.PLAN)); } this.thresholdPercentage = getThresholdPercentage(cmd); @@ -124,8 +125,8 @@ public class PlanCommand extends Command { try (FSDataOutputStream beforeStream = create(String.format( - DiskBalancer.BEFORE_TEMPLATE, - cmd.getOptionValue(DiskBalancer.PLAN)))) { + DiskBalancerCLI.BEFORE_TEMPLATE, + cmd.getOptionValue(DiskBalancerCLI.PLAN)))) { beforeStream.write(getCluster().toJson() .getBytes(StandardCharsets.UTF_8)); } @@ -133,17 +134,17 @@ public class PlanCommand extends Command { if (plan != null && plan.getVolumeSetPlans().size() > 0) { LOG.info("Writing plan to : {}", getOutputPath()); try (FSDataOutputStream planStream = create(String.format( - DiskBalancer.PLAN_TEMPLATE, - cmd.getOptionValue(DiskBalancer.PLAN)))) { + DiskBalancerCLI.PLAN_TEMPLATE, + cmd.getOptionValue(DiskBalancerCLI.PLAN)))) { planStream.write(plan.toJson().getBytes(StandardCharsets.UTF_8)); } } else { LOG.info("No plan generated. DiskBalancing not needed for node: {} " + - "threshold used: {}", cmd.getOptionValue(DiskBalancer.PLAN), + "threshold used: {}", cmd.getOptionValue(DiskBalancerCLI.PLAN), this.thresholdPercentage); } - if (cmd.hasOption(DiskBalancer.VERBOSE) && plans.size() > 0) { + if (cmd.hasOption(DiskBalancerCLI.VERBOSE) && plans.size() > 0) { printToScreen(plans); } } @@ -162,8 +163,8 @@ public class PlanCommand extends Command { " will balance the data."; HelpFormatter helpFormatter = new HelpFormatter(); - helpFormatter.printHelp("hdfs diskbalancer -plan " + - " [options]", header, DiskBalancer.getPlanOptions(), footer); + helpFormatter.printHelp("hdfs diskbalancer -plan [options]", + header, DiskBalancerCLI.getPlanOptions(), footer); } /** @@ -174,8 +175,8 @@ public class PlanCommand extends Command { */ private double getThresholdPercentage(CommandLine cmd) { Double value = 0.0; - if (cmd.hasOption(DiskBalancer.THRESHOLD)) { - value = Double.parseDouble(cmd.getOptionValue(DiskBalancer.THRESHOLD)); + if (cmd.hasOption(DiskBalancerCLI.THRESHOLD)) { + value = Double.parseDouble(cmd.getOptionValue(DiskBalancerCLI.THRESHOLD)); } if ((value <= 0.0) || (value > 100.0)) { diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/QueryCommand.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/QueryCommand.java index 1557a023f30..a8adcbd5621 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/QueryCommand.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/QueryCommand.java @@ -27,7 +27,7 @@ import org.apache.hadoop.hdfs.DFSConfigKeys; import org.apache.hadoop.hdfs.protocol.ClientDatanodeProtocol; import org.apache.hadoop.hdfs.server.datanode.DiskBalancerWorkStatus; import org.apache.hadoop.hdfs.server.diskbalancer.DiskBalancerException; -import org.apache.hadoop.hdfs.tools.DiskBalancer; +import org.apache.hadoop.hdfs.tools.DiskBalancerCLI; import org.apache.hadoop.net.NetUtils; /** @@ -42,9 +42,10 @@ public class QueryCommand extends Command { */ public QueryCommand(Configuration conf) { super(conf); - addValidCommandParameters(DiskBalancer.QUERY, "Queries the status of disk" + - " plan running on a given datanode."); - addValidCommandParameters(DiskBalancer.VERBOSE, "Prints verbose results."); + addValidCommandParameters(DiskBalancerCLI.QUERY, + "Queries the status of disk plan running on a given datanode."); + addValidCommandParameters(DiskBalancerCLI.VERBOSE, + "Prints verbose results."); } /** @@ -55,9 +56,9 @@ public class QueryCommand extends Command { @Override public void execute(CommandLine cmd) throws Exception { LOG.info("Executing \"query plan\" command."); - Preconditions.checkState(cmd.hasOption(DiskBalancer.QUERY)); - verifyCommandOptions(DiskBalancer.QUERY, cmd); - String nodeName = cmd.getOptionValue(DiskBalancer.QUERY); + Preconditions.checkState(cmd.hasOption(DiskBalancerCLI.QUERY)); + verifyCommandOptions(DiskBalancerCLI.QUERY, cmd); + String nodeName = cmd.getOptionValue(DiskBalancerCLI.QUERY); Preconditions.checkNotNull(nodeName); nodeName = nodeName.trim(); String nodeAddress = nodeName; @@ -79,7 +80,7 @@ public class QueryCommand extends Command { workStatus.getPlanID(), workStatus.getResult().toString()); - if (cmd.hasOption(DiskBalancer.VERBOSE)) { + if (cmd.hasOption(DiskBalancerCLI.VERBOSE)) { System.out.printf("%s", workStatus.currentStateString()); } } catch (DiskBalancerException ex) { @@ -101,6 +102,6 @@ public class QueryCommand extends Command { HelpFormatter helpFormatter = new HelpFormatter(); helpFormatter.printHelp("hdfs diskbalancer -query [options]", - header, DiskBalancer.getQueryOptions(), footer); + header, DiskBalancerCLI.getQueryOptions(), footer); } } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/ReportCommand.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/ReportCommand.java index 18dd77eacc2..79ba14f5fe3 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/ReportCommand.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/diskbalancer/command/ReportCommand.java @@ -27,10 +27,11 @@ import org.apache.commons.cli.HelpFormatter; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.text.StrBuilder; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hdfs.server.diskbalancer.DiskBalancerException; import org.apache.hadoop.hdfs.server.diskbalancer.datamodel.DiskBalancerDataNode; import org.apache.hadoop.hdfs.server.diskbalancer.datamodel.DiskBalancerVolume; import org.apache.hadoop.hdfs.server.diskbalancer.datamodel.DiskBalancerVolumeSet; -import org.apache.hadoop.hdfs.tools.DiskBalancer; +import org.apache.hadoop.hdfs.tools.DiskBalancerCLI; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; @@ -52,15 +53,15 @@ public class ReportCommand extends Command { super(conf); this.out = out; - addValidCommandParameters(DiskBalancer.REPORT, + addValidCommandParameters(DiskBalancerCLI.REPORT, "Report volume information of nodes."); String desc = String.format( "Top number of nodes to be processed. Default: %d", getDefaultTop()); - addValidCommandParameters(DiskBalancer.TOP, desc); + addValidCommandParameters(DiskBalancerCLI.TOP, desc); - desc = String.format("Print out volume information for a DataNode."); - addValidCommandParameters(DiskBalancer.NODE, desc); + desc = String.format("Print out volume information for DataNode(s)."); + addValidCommandParameters(DiskBalancerCLI.NODE, desc); } @Override @@ -69,8 +70,8 @@ public class ReportCommand extends Command { String outputLine = "Processing report command"; recordOutput(result, outputLine); - Preconditions.checkState(cmd.hasOption(DiskBalancer.REPORT)); - verifyCommandOptions(DiskBalancer.REPORT, cmd); + Preconditions.checkState(cmd.hasOption(DiskBalancerCLI.REPORT)); + verifyCommandOptions(DiskBalancerCLI.REPORT, cmd); readClusterInfo(cmd); final String nodeFormat = @@ -81,9 +82,9 @@ public class ReportCommand extends Command { "[%s: volume-%s] - %.2f used: %d/%d, %.2f free: %d/%d, " + "isFailed: %s, isReadOnly: %s, isSkip: %s, isTransient: %s."; - if (cmd.hasOption(DiskBalancer.NODE)) { + if (cmd.hasOption(DiskBalancerCLI.NODE)) { /* - * Reporting volume information for a specific DataNode + * Reporting volume information for specific DataNode(s) */ handleNodeReport(cmd, result, nodeFormatWithoutSequence, volumeFormat); @@ -133,84 +134,100 @@ public class ReportCommand extends Command { final String nodeFormat, final String volumeFormat) throws Exception { String outputLine = ""; /* - * get value that identifies a DataNode from command line, it could be UUID, - * IP address or host name. + * get value that identifies DataNode(s) from command line, it could be + * UUID, IP address or host name. */ - final String nodeVal = cmd.getOptionValue(DiskBalancer.NODE); + final String nodeVal = cmd.getOptionValue(DiskBalancerCLI.NODE); if (StringUtils.isBlank(nodeVal)) { outputLine = "The value for '-node' is neither specified or empty."; recordOutput(result, outputLine); } else { /* - * Reporting volume information for a specific DataNode + * Reporting volume information for specific DataNode(s) */ outputLine = String.format( - "Reporting volume information for DataNode '%s'.", nodeVal); + "Reporting volume information for DataNode(s) '%s'.", nodeVal); recordOutput(result, outputLine); - final String trueStr = "True"; - final String falseStr = "False"; - DiskBalancerDataNode dbdn = getNode(nodeVal); - // get storage path of datanode - populatePathNames(dbdn); + List dbdns = Lists.newArrayList(); + try { + dbdns = getNodes(nodeVal); + } catch (DiskBalancerException e) { + // If there are some invalid nodes that contained in nodeVal, + // the exception will be threw. + recordOutput(result, e.getMessage()); + return; + } - if (dbdn == null) { - outputLine = String.format( - "Can't find a DataNode that matches '%s'.", nodeVal); - recordOutput(result, outputLine); - } else { - result.appendln(String.format(nodeFormat, - dbdn.getDataNodeName(), - dbdn.getDataNodeIP(), - dbdn.getDataNodePort(), - dbdn.getDataNodeUUID(), - dbdn.getVolumeCount(), - dbdn.getNodeDataDensity())); - - List volumeList = Lists.newArrayList(); - for (DiskBalancerVolumeSet vset : dbdn.getVolumeSets().values()) { - for (DiskBalancerVolume vol : vset.getVolumes()) { - volumeList.add(String.format(volumeFormat, - vol.getStorageType(), - vol.getPath(), - vol.getUsedRatio(), - vol.getUsed(), - vol.getCapacity(), - vol.getFreeRatio(), - vol.getFreeSpace(), - vol.getCapacity(), - vol.isFailed() ? trueStr : falseStr, - vol.isReadOnly() ? trueStr : falseStr, - vol.isSkip() ? trueStr : falseStr, - vol.isTransient() ? trueStr : falseStr)); - } + if (!dbdns.isEmpty()) { + for (DiskBalancerDataNode node : dbdns) { + recordNodeReport(result, node, nodeFormat, volumeFormat); + result.append(System.lineSeparator()); } - - Collections.sort(volumeList); - result.appendln( - StringUtils.join(volumeList.toArray(), System.lineSeparator())); } } } + /** + * Put node report lines to string buffer. + */ + private void recordNodeReport(StrBuilder result, DiskBalancerDataNode dbdn, + final String nodeFormat, final String volumeFormat) throws Exception { + final String trueStr = "True"; + final String falseStr = "False"; + + // get storage path of datanode + populatePathNames(dbdn); + result.appendln(String.format(nodeFormat, + dbdn.getDataNodeName(), + dbdn.getDataNodeIP(), + dbdn.getDataNodePort(), + dbdn.getDataNodeUUID(), + dbdn.getVolumeCount(), + dbdn.getNodeDataDensity())); + + List volumeList = Lists.newArrayList(); + for (DiskBalancerVolumeSet vset : dbdn.getVolumeSets().values()) { + for (DiskBalancerVolume vol : vset.getVolumes()) { + volumeList.add(String.format(volumeFormat, + vol.getStorageType(), + vol.getPath(), + vol.getUsedRatio(), + vol.getUsed(), + vol.getCapacity(), + vol.getFreeRatio(), + vol.getFreeSpace(), + vol.getCapacity(), + vol.isFailed() ? trueStr : falseStr, + vol.isReadOnly() ? trueStr: falseStr, + vol.isSkip() ? trueStr : falseStr, + vol.isTransient() ? trueStr : falseStr)); + } + } + + Collections.sort(volumeList); + result.appendln( + StringUtils.join(volumeList.toArray(), System.lineSeparator())); + } + /** * Prints the help message. */ @Override public void printHelp() { - String header = "Report command reports the volume information of a given" + - " datanode, or prints out the list of nodes that will benefit from " + - "running disk balancer. Top defaults to " + getDefaultTop(); + String header = "Report command reports the volume information of given" + + " datanode(s), or prints out the list of nodes that will benefit " + + "from running disk balancer. Top defaults to " + getDefaultTop(); String footer = ". E.g.:\n" + "hdfs diskbalancer -report\n" + "hdfs diskbalancer -report -top 5\n" + "hdfs diskbalancer -report " - + "-node {DataNodeID | IP | Hostname}"; + + "-node [,...]"; HelpFormatter helpFormatter = new HelpFormatter(); helpFormatter.printHelp("hdfs diskbalancer -fs http://namenode.uri " + "-report [options]", - header, DiskBalancer.getReportOptions(), footer); + header, DiskBalancerCLI.getReportOptions(), footer); } } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNodeRpcServer.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNodeRpcServer.java index 57f7cb197b6..6b529498686 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNodeRpcServer.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNodeRpcServer.java @@ -168,6 +168,7 @@ import org.apache.hadoop.ipc.RetryCache.CacheEntry; import org.apache.hadoop.ipc.RetryCache.CacheEntryWithPayload; import org.apache.hadoop.ipc.Server; import org.apache.hadoop.ipc.StandbyException; +import org.apache.hadoop.ipc.WritableRpcEngine; import org.apache.hadoop.ipc.RefreshRegistry; import org.apache.hadoop.ipc.RefreshResponse; import org.apache.hadoop.net.Node; @@ -316,6 +317,8 @@ public class NameNodeRpcServer implements NamenodeProtocols { new TraceAdminProtocolServerSideTranslatorPB(this); BlockingService traceAdminService = TraceAdminService .newReflectiveBlockingService(traceAdminXlator); + + WritableRpcEngine.ensureInitialized(); InetSocketAddress serviceRpcAddr = nn.getServiceRpcServerAddress(conf); if (serviceRpcAddr != null) { diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/DiskBalancer.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/DiskBalancerCLI.java similarity index 96% rename from hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/DiskBalancer.java rename to hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/DiskBalancerCLI.java index 1ed2fdcea2f..e961c149d83 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/DiskBalancer.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/DiskBalancerCLI.java @@ -50,7 +50,7 @@ import java.io.PrintStream; * At very high level diskbalancer computes a set of moves that will make disk * utilization equal and then those moves are executed by the datanode. */ -public class DiskBalancer extends Configured implements Tool { +public class DiskBalancerCLI extends Configured implements Tool { /** * Computes a plan for a given set of nodes. */ @@ -126,7 +126,7 @@ public class DiskBalancer extends Configured implements Tool { */ public static final String PLAN_TEMPLATE = "%s.plan.json"; private static final Logger LOG = - LoggerFactory.getLogger(DiskBalancer.class); + LoggerFactory.getLogger(DiskBalancerCLI.class); private static final Options PLAN_OPTIONS = new Options(); private static final Options EXECUTE_OPTIONS = new Options(); @@ -140,7 +140,7 @@ public class DiskBalancer extends Configured implements Tool { * * @param conf */ - public DiskBalancer(Configuration conf) { + public DiskBalancerCLI(Configuration conf) { super(conf); } @@ -151,7 +151,7 @@ public class DiskBalancer extends Configured implements Tool { * @throws Exception */ public static void main(String[] argv) throws Exception { - DiskBalancer shell = new DiskBalancer(new HdfsConfiguration()); + DiskBalancerCLI shell = new DiskBalancerCLI(new HdfsConfiguration()); int res = 0; try { res = ToolRunner.run(shell, argv); @@ -446,27 +446,27 @@ public class DiskBalancer extends Configured implements Tool { private int dispatch(CommandLine cmd, Options opts, final PrintStream out) throws Exception { Command currentCommand = null; - if (cmd.hasOption(DiskBalancer.PLAN)) { + if (cmd.hasOption(DiskBalancerCLI.PLAN)) { currentCommand = new PlanCommand(getConf()); } - if (cmd.hasOption(DiskBalancer.EXECUTE)) { + if (cmd.hasOption(DiskBalancerCLI.EXECUTE)) { currentCommand = new ExecuteCommand(getConf()); } - if (cmd.hasOption(DiskBalancer.QUERY)) { + if (cmd.hasOption(DiskBalancerCLI.QUERY)) { currentCommand = new QueryCommand(getConf()); } - if (cmd.hasOption(DiskBalancer.CANCEL)) { + if (cmd.hasOption(DiskBalancerCLI.CANCEL)) { currentCommand = new CancelCommand(getConf()); } - if (cmd.hasOption(DiskBalancer.REPORT)) { + if (cmd.hasOption(DiskBalancerCLI.REPORT)) { currentCommand = new ReportCommand(getConf(), out); } - if (cmd.hasOption(DiskBalancer.HELP)) { + if (cmd.hasOption(DiskBalancerCLI.HELP)) { currentCommand = new HelpCommand(getConf()); } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/FileDistributionCalculator.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/FileDistributionCalculator.java index 33ab641e54c..71fb822eed6 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/FileDistributionCalculator.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/FileDistributionCalculator.java @@ -31,6 +31,7 @@ import org.apache.hadoop.hdfs.server.namenode.FSImageUtil; import org.apache.hadoop.hdfs.server.namenode.FsImageProto.FileSummary; import org.apache.hadoop.hdfs.server.namenode.FsImageProto.INodeSection; import org.apache.hadoop.util.LimitInputStream; +import org.apache.hadoop.util.StringUtils; import com.google.common.base.Preconditions; @@ -75,11 +76,14 @@ final class FileDistributionCalculator { private long totalSpace; private long maxFileSize; + private boolean formatOutput = false; + FileDistributionCalculator(Configuration conf, long maxSize, int steps, - PrintStream out) { + boolean formatOutput, PrintStream out) { this.conf = conf; this.maxSize = maxSize == 0 ? MAX_SIZE_DEFAULT : maxSize; this.steps = steps == 0 ? INTERVAL_DEFAULT : steps; + this.formatOutput = formatOutput; this.out = out; long numIntervals = this.maxSize / this.steps; // avoid OutOfMemoryError when allocating an array @@ -148,10 +152,20 @@ final class FileDistributionCalculator { private void output() { // write the distribution into the output file - out.print("Size\tNumFiles\n"); + out.print((formatOutput ? "Size Range" : "Size") + "\tNumFiles\n"); for (int i = 0; i < distribution.length; i++) { if (distribution[i] != 0) { - out.print(((long) i * steps) + "\t" + distribution[i]); + if (formatOutput) { + out.print((i == 0 ? "[" : "(") + + StringUtils.byteDesc(((long) (i == 0 ? 0 : i - 1) * steps)) + + ", " + + StringUtils.byteDesc((long) + (i == distribution.length - 1 ? maxFileSize : i * steps)) + + "]\t" + distribution[i]); + } else { + out.print(((long) i * steps) + "\t" + distribution[i]); + } + out.print('\n'); } } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/FileDistributionVisitor.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/FileDistributionVisitor.java index 1cef720624d..7dcc29998f3 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/FileDistributionVisitor.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/FileDistributionVisitor.java @@ -20,6 +20,8 @@ package org.apache.hadoop.hdfs.tools.offlineImageViewer; import java.io.IOException; import java.util.LinkedList; +import org.apache.hadoop.util.StringUtils; + /** * File size distribution visitor. * @@ -67,6 +69,7 @@ class FileDistributionVisitor extends TextWriterImageVisitor { private FileContext current; private boolean inInode = false; + private boolean formatOutput = false; /** * File or directory information. @@ -78,12 +81,12 @@ class FileDistributionVisitor extends TextWriterImageVisitor { int replication; } - public FileDistributionVisitor(String filename, - long maxSize, - int step) throws IOException { + public FileDistributionVisitor(String filename, long maxSize, int step, + boolean formatOutput) throws IOException { super(filename, false); this.maxSize = (maxSize == 0 ? MAX_SIZE_DEFAULT : maxSize); this.step = (step == 0 ? INTERVAL_DEFAULT : step); + this.formatOutput = formatOutput; long numIntervals = this.maxSize / this.step; if(numIntervals >= Integer.MAX_VALUE) throw new IOException("Too many distribution intervals " + numIntervals); @@ -113,9 +116,22 @@ class FileDistributionVisitor extends TextWriterImageVisitor { private void output() throws IOException { // write the distribution into the output file - write("Size\tNumFiles\n"); - for(int i = 0; i < distribution.length; i++) - write(((long)i * step) + "\t" + distribution[i] + "\n"); + write((formatOutput ? "Size Range" : "Size") + "\tNumFiles\n"); + for (int i = 0; i < distribution.length; i++) { + if (distribution[i] > 0) { + if (formatOutput) { + write((i == 0 ? "[" : "(") + + StringUtils.byteDesc(((long) (i == 0 ? 0 : i - 1) * step)) + + ", " + + StringUtils.byteDesc((long) + (i == distribution.length - 1 ? maxFileSize : i * step)) + + "]\t" + + distribution[i] + "\n"); + } else { + write(((long) i * step) + "\t" + distribution[i] + "\n"); + } + } + } System.out.println("totalFiles = " + totalFiles); System.out.println("totalDirectories = " + totalDirectories); System.out.println("totalBlocks = " + totalBlocks); diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/OfflineImageViewer.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/OfflineImageViewer.java index 770cde14855..c542d908b6e 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/OfflineImageViewer.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/OfflineImageViewer.java @@ -46,61 +46,63 @@ import org.apache.hadoop.hdfs.server.namenode.FSEditLogLoader.PositionTrackingIn public class OfflineImageViewer { public static final Log LOG = LogFactory.getLog(OfflineImageViewer.class); - private final static String usage = - "Usage: bin/hdfs oiv_legacy [OPTIONS] -i INPUTFILE -o OUTPUTFILE\n" + - "Offline Image Viewer\n" + - "View a Hadoop fsimage INPUTFILE using the specified PROCESSOR,\n" + - "saving the results in OUTPUTFILE.\n" + - "\n" + - "The oiv utility will attempt to parse correctly formed image files\n" + - "and will abort fail with mal-formed image files.\n" + - "\n" + - "The tool works offline and does not require a running cluster in\n" + - "order to process an image file.\n" + - "\n" + - "The following image processors are available:\n" + - " * Ls: The default image processor generates an lsr-style listing\n" + - " of the files in the namespace, with the same fields in the same\n" + - " order. Note that in order to correctly determine file sizes,\n" + - " this formatter cannot skip blocks and will override the\n" + - " -skipBlocks option.\n" + - " * Indented: This processor enumerates over all of the elements in\n" + - " the fsimage file, using levels of indentation to delineate\n" + - " sections within the file.\n" + - " * Delimited: Generate a text file with all of the elements common\n" + - " to both inodes and inodes-under-construction, separated by a\n" + - " delimiter. The default delimiter is \u0001, though this may be\n" + - " changed via the -delimiter argument. This processor also overrides\n" + - " the -skipBlocks option for the same reason as the Ls processor\n" + - " * XML: This processor creates an XML document with all elements of\n" + - " the fsimage enumerated, suitable for further analysis by XML\n" + - " tools.\n" + - " * FileDistribution: This processor analyzes the file size\n" + - " distribution in the image.\n" + - " -maxSize specifies the range [0, maxSize] of file sizes to be\n" + - " analyzed (128GB by default).\n" + - " -step defines the granularity of the distribution. (2MB by default)\n" + - " * NameDistribution: This processor analyzes the file names\n" + - " in the image and prints total number of file names and how frequently\n" + - " file names are reused.\n" + - "\n" + - "Required command line arguments:\n" + - "-i,--inputFile FSImage file to process.\n" + - "-o,--outputFile Name of output file. If the specified\n" + - " file exists, it will be overwritten.\n" + - "\n" + - "Optional command line arguments:\n" + - "-p,--processor Select which type of processor to apply\n" + - " against image file." + - " (Ls|XML|Delimited|Indented|FileDistribution).\n" + - "-h,--help Display usage information and exit\n" + - "-printToScreen For processors that write to a file, also\n" + - " output to screen. On large image files this\n" + - " will dramatically increase processing time.\n" + - "-skipBlocks Skip inodes' blocks information. May\n" + - " significantly decrease output.\n" + - " (default = false).\n" + - "-delimiter Delimiting string to use with Delimited processor\n"; + private final static String usage = + "Usage: bin/hdfs oiv_legacy [OPTIONS] -i INPUTFILE -o OUTPUTFILE\n" + + "Offline Image Viewer\n" + + "View a Hadoop fsimage INPUTFILE using the specified PROCESSOR,\n" + + "saving the results in OUTPUTFILE.\n" + + "\n" + + "The oiv utility will attempt to parse correctly formed image files\n" + + "and will abort fail with mal-formed image files.\n" + + "\n" + + "The tool works offline and does not require a running cluster in\n" + + "order to process an image file.\n" + + "\n" + + "The following image processors are available:\n" + + " * Ls: The default image processor generates an lsr-style listing\n" + + " of the files in the namespace, with the same fields in the same\n" + + " order. Note that in order to correctly determine file sizes,\n" + + " this formatter cannot skip blocks and will override the\n" + + " -skipBlocks option.\n" + + " * Indented: This processor enumerates over all of the elements in\n" + + " the fsimage file, using levels of indentation to delineate\n" + + " sections within the file.\n" + + " * Delimited: Generate a text file with all of the elements common\n" + + " to both inodes and inodes-under-construction, separated by a\n" + + " delimiter. The default delimiter is \u0001, though this may be\n" + + " changed via the -delimiter argument. This processor also overrides\n" + + " the -skipBlocks option for the same reason as the Ls processor\n" + + " * XML: This processor creates an XML document with all elements of\n" + + " the fsimage enumerated, suitable for further analysis by XML\n" + + " tools.\n" + + " * FileDistribution: This processor analyzes the file size\n" + + " distribution in the image.\n" + + " -maxSize specifies the range [0, maxSize] of file sizes to be\n" + + " analyzed (128GB by default).\n" + + " -step defines the granularity of the distribution. (2MB by default)\n" + + " -format formats the output result in a human-readable fashion\n" + + " rather than a number of bytes. (false by default)\n" + + " * NameDistribution: This processor analyzes the file names\n" + + " in the image and prints total number of file names and how frequently\n" + + " file names are reused.\n" + + "\n" + + "Required command line arguments:\n" + + "-i,--inputFile FSImage file to process.\n" + + "-o,--outputFile Name of output file. If the specified\n" + + " file exists, it will be overwritten.\n" + + "\n" + + "Optional command line arguments:\n" + + "-p,--processor Select which type of processor to apply\n" + + " against image file." + + " (Ls|XML|Delimited|Indented|FileDistribution).\n" + + "-h,--help Display usage information and exit\n" + + "-printToScreen For processors that write to a file, also\n" + + " output to screen. On large image files this\n" + + " will dramatically increase processing time.\n" + + "-skipBlocks Skip inodes' blocks information. May\n" + + " significantly decrease output.\n" + + " (default = false).\n" + + "-delimiter Delimiting string to use with Delimited processor\n"; private final boolean skipBlocks; private final String inputFile; @@ -188,6 +190,7 @@ public class OfflineImageViewer { options.addOption("h", "help", false, ""); options.addOption("maxSize", true, ""); options.addOption("step", true, ""); + options.addOption("format", false, ""); options.addOption("skipBlocks", false, ""); options.addOption("printToScreen", false, ""); options.addOption("delimiter", true, ""); @@ -253,7 +256,8 @@ public class OfflineImageViewer { } else if (processor.equals("FileDistribution")) { long maxSize = Long.parseLong(cmd.getOptionValue("maxSize", "0")); int step = Integer.parseInt(cmd.getOptionValue("step", "0")); - v = new FileDistributionVisitor(outputFile, maxSize, step); + boolean formatOutput = cmd.hasOption("format"); + v = new FileDistributionVisitor(outputFile, maxSize, step, formatOutput); } else if (processor.equals("NameDistribution")) { v = new NameDistributionVisitor(outputFile, printToScreen); } else { diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/OfflineImageViewerPB.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/OfflineImageViewerPB.java index b514b3f86bb..c1141f30aec 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/OfflineImageViewerPB.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/OfflineImageViewerPB.java @@ -67,6 +67,8 @@ public class OfflineImageViewerPB { + " -maxSize specifies the range [0, maxSize] of file sizes to be\n" + " analyzed (128GB by default).\n" + " -step defines the granularity of the distribution. (2MB by default)\n" + + " -format formats the output result in a human-readable fashion\n" + + " rather than a number of bytes. (false by default)\n" + " * Web: Run a viewer to expose read-only WebHDFS API.\n" + " -addr specifies the address to listen. (localhost:5978 by default)\n" + " * Delimited (experimental): Generate a text file with all of the elements common\n" @@ -111,6 +113,7 @@ public class OfflineImageViewerPB { options.addOption("h", "help", false, ""); options.addOption("maxSize", true, ""); options.addOption("step", true, ""); + options.addOption("format", false, ""); options.addOption("addr", true, ""); options.addOption("delimiter", true, ""); options.addOption("t", "temp", true, ""); @@ -172,43 +175,44 @@ public class OfflineImageViewerPB { try (PrintStream out = outputFile.equals("-") ? System.out : new PrintStream(outputFile, "UTF-8")) { switch (processor) { - case "FileDistribution": - long maxSize = Long.parseLong(cmd.getOptionValue("maxSize", "0")); - int step = Integer.parseInt(cmd.getOptionValue("step", "0")); - new FileDistributionCalculator(conf, maxSize, step, out).visit( - new RandomAccessFile(inputFile, "r")); - break; - case "XML": - new PBImageXmlWriter(conf, out).visit( - new RandomAccessFile(inputFile, "r")); - break; - case "ReverseXML": - try { - OfflineImageReconstructor.run(inputFile, outputFile); - } catch (Exception e) { - System.err.println("OfflineImageReconstructor failed: " + - e.getMessage()); - e.printStackTrace(System.err); - System.exit(1); - } - break; - case "Web": - String addr = cmd.getOptionValue("addr", "localhost:5978"); - try (WebImageViewer viewer = new WebImageViewer( - NetUtils.createSocketAddr(addr))) { - viewer.start(inputFile); - } - break; - case "Delimited": - try (PBImageDelimitedTextWriter writer = - new PBImageDelimitedTextWriter(out, delimiter, tempPath)) { - writer.visit(new RandomAccessFile(inputFile, "r")); - } - break; - default: - System.err.println("Invalid processor specified : " + processor); - printUsage(); - return -1; + case "FileDistribution": + long maxSize = Long.parseLong(cmd.getOptionValue("maxSize", "0")); + int step = Integer.parseInt(cmd.getOptionValue("step", "0")); + boolean formatOutput = cmd.hasOption("format"); + new FileDistributionCalculator(conf, maxSize, step, formatOutput, out) + .visit(new RandomAccessFile(inputFile, "r")); + break; + case "XML": + new PBImageXmlWriter(conf, out).visit(new RandomAccessFile(inputFile, + "r")); + break; + case "ReverseXML": + try { + OfflineImageReconstructor.run(inputFile, outputFile); + } catch (Exception e) { + System.err.println("OfflineImageReconstructor failed: " + + e.getMessage()); + e.printStackTrace(System.err); + System.exit(1); + } + break; + case "Web": + String addr = cmd.getOptionValue("addr", "localhost:5978"); + try (WebImageViewer viewer = + new WebImageViewer(NetUtils.createSocketAddr(addr))) { + viewer.start(inputFile); + } + break; + case "Delimited": + try (PBImageDelimitedTextWriter writer = + new PBImageDelimitedTextWriter(out, delimiter, tempPath)) { + writer.visit(new RandomAccessFile(inputFile, "r")); + } + break; + default: + System.err.println("Invalid processor specified : " + processor); + printUsage(); + return -1; } return 0; } catch (EOFException e) { diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/resources/hdfs-default.xml b/hadoop-hdfs-project/hadoop-hdfs/src/main/resources/hdfs-default.xml index d427f887067..29c9ef25cf0 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/resources/hdfs-default.xml +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/resources/hdfs-default.xml @@ -163,7 +163,7 @@ dfs.namenode.http-bind-host - The actual adress the HTTP server will bind to. If this optional address + The actual address the HTTP server will bind to. If this optional address is set, it overrides only the hostname portion of dfs.namenode.http-address. It can also be specified per name node or name service for HA/Federation. This is useful for making the name node HTTP server listen on all @@ -243,7 +243,7 @@ dfs.namenode.https-bind-host - The actual adress the HTTPS server will bind to. If this optional address + The actual address the HTTPS server will bind to. If this optional address is set, it overrides only the hostname portion of dfs.namenode.https-address. It can also be specified per name node or name service for HA/Federation. This is useful for making the name node HTTPS server listen on all @@ -650,7 +650,7 @@ dfs.blockreport.initialDelay - 0 + 0s Delay for first block report in seconds. Support multiple time unit suffix(case insensitive), as described in dfs.heartbeat.interval. @@ -694,7 +694,7 @@ dfs.datanode.directoryscan.interval - 21600 + 21600s Interval in seconds for Datanode to scan data directories and reconcile the difference between blocks in memory and on the disk. Support multiple time unit suffix(case insensitive), as described @@ -732,7 +732,7 @@ dfs.heartbeat.interval - 3 + 3s Determines datanode heartbeat interval in seconds. Can use the following suffix (case insensitive): @@ -942,7 +942,7 @@ dfs.namenode.decommission.interval - 30 + 30s Namenode periodicity in seconds to check if decommission is complete. Support multiple time unit suffix(case insensitive), as described in dfs.heartbeat.interval. @@ -973,7 +973,7 @@ dfs.namenode.replication.interval - 3 + 3s The periodicity in seconds with which the namenode computes replication work for datanodes. Support multiple time unit suffix(case insensitive), as described in dfs.heartbeat.interval. @@ -1071,7 +1071,7 @@ dfs.namenode.checkpoint.period - 3600 + 3600s The number of seconds between two periodic checkpoints. Support multiple time unit suffix(case insensitive), as described @@ -1090,7 +1090,7 @@ dfs.namenode.checkpoint.check.period - 60 + 60s The SecondaryNameNode and CheckpointNode will poll the NameNode every 'dfs.namenode.checkpoint.check.period' seconds to query the number of uncheckpointed transactions. Support multiple time unit suffix(case insensitive), @@ -1433,7 +1433,7 @@ dfs.client.datanode-restart.timeout - 30 + 30s Expert only. The time to wait, in seconds, from reception of an datanode shutdown notification for quick restart, until declaring @@ -1502,7 +1502,7 @@ dfs.ha.log-roll.period - 120 + 120s How often, in seconds, the StandbyNode should ask the active to roll edit logs. Since the StandbyNode only reads from finalized @@ -1516,7 +1516,7 @@ dfs.ha.tail-edits.period - 60 + 60s How often, in seconds, the StandbyNode should check for new finalized log segments in the shared edits log. @@ -2950,7 +2950,7 @@ dfs.datanode.bp-ready.timeout - 20 + 20s The maximum wait time for datanode to be ready before failing the received request. Setting this to 0 fails requests right away if the @@ -4273,4 +4273,12 @@ a plan. + + + dfs.lock.suppress.warning.interval + 10s + Instrumentation reporting long critical sections will suppress + consecutive warnings within this interval. + + diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/ArchivalStorage.md b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/ArchivalStorage.md index 31bea7c77f7..06b73904b0a 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/ArchivalStorage.md +++ b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/ArchivalStorage.md @@ -89,7 +89,7 @@ Note 2: For the erasure coded files with striping layout, the suitable storage p When a file or directory is created, its storage policy is *unspecified*. The storage policy can be specified using the "[`storagepolicies -setStoragePolicy`](#Set_Storage_Policy)" command. The effective storage policy of a file or directory is resolved by the following rules. -1. If the file or directory is specificed with a storage policy, return it. +1. If the file or directory is specified with a storage policy, return it. 2. For an unspecified file or directory, if it is the root directory, return the *default storage policy*. Otherwise, return its parent's effective storage policy. diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HDFSCommands.md b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HDFSCommands.md index 22886d3c84a..83035bbb200 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HDFSCommands.md +++ b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HDFSCommands.md @@ -239,6 +239,7 @@ Usage: `hdfs oiv [OPTIONS] -i INPUT_FILE` | `-addr` *address* | Specify the address(host:port) to listen. (localhost:5978 by default). This option is used with Web processor. | | `-maxSize` *size* | Specify the range [0, maxSize] of file sizes to be analyzed in bytes (128GB by default). This option is used with FileDistribution processor. | | `-step` *size* | Specify the granularity of the distribution in bytes (2MB by default). This option is used with FileDistribution processor. | +| `-format` | Format the output result in a human-readable fashion rather than a number of bytes. (false by default). This option is used with FileDistribution processor. | | `-delimiter` *arg* | Delimiting string to use with Delimited processor. | | `-t`,`--temp` *temporary dir* | Use temporary dir to cache intermediate result to generate Delimited outputs. If not set, Delimited processor constructs the namespace in memory before outputting text. | | `-h`,`--help` | Display the tool usage and help information and exit. | @@ -260,6 +261,9 @@ Usage: `hdfs oiv_legacy [OPTIONS] -i INPUT_FILE -o OUTPUT_FILE` | COMMAND\_OPTION | Description | |:---- |:---- | | `-p`\|`--processor` *processor* | Specify the image processor to apply against the image file. Valid options are Ls (default), XML, Delimited, Indented, and FileDistribution. | +| `-maxSize` *size* | Specify the range [0, maxSize] of file sizes to be analyzed in bytes (128GB by default). This option is used with FileDistribution processor. | +| `-step` *size* | Specify the granularity of the distribution in bytes (2MB by default). This option is used with FileDistribution processor. | +| `-format` | Format the output result in a human-readable fashion rather than a number of bytes. (false by default). This option is used with FileDistribution processor. | | `-skipBlocks` | Do not enumerate individual blocks within files. This may save processing time and outfile file space on namespaces with very large files. The Ls processor reads the blocks to correctly determine file sizes and ignores this option. | | `-printToScreen` | Pipe output of processor to console as well as specified file. On extremely large namespaces, this may increase processing time by an order of magnitude. | | `-delimiter` *arg* | When used in conjunction with the Delimited processor, replaces the default tab delimiter with the string specified by *arg*. | diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HDFSDiskbalancer.md b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HDFSDiskbalancer.md index aea440a3de9..f2bb2b39945 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HDFSDiskbalancer.md +++ b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HDFSDiskbalancer.md @@ -102,9 +102,9 @@ or Plan ID can be read from datanode using query command. ### Report -Report command provides detailed report about a node. +Report command provides detailed report about node(s). -`hdfs diskbalancer -fs http://namenode.uri -report -node {DataNodeID | IP | Hostname}` +`hdfs diskbalancer -fs http://namenode.uri -report -node [,...]` Settings diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HDFSHighAvailabilityWithNFS.md b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HDFSHighAvailabilityWithNFS.md index d9f895a7d88..b743233c100 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HDFSHighAvailabilityWithNFS.md +++ b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HDFSHighAvailabilityWithNFS.md @@ -87,7 +87,7 @@ In order to deploy an HA cluster, you should prepare the following: * **NameNode machines** - the machines on which you run the Active and Standby NameNodes should have equivalent hardware to each other, and equivalent hardware to what would be used in a non-HA cluster. -* **Shared storage** - you will need to have a shared directory which the NameNode machines have read/write access to. Typically this is a remote filer which supports NFS and is mounted on each of the NameNode machines. Currently only a single shared edits directory is supported. Thus, the availability of the system is limited by the availability of this shared edits directory, and therefore in order to remove all single points of failure there needs to be redundancy for the shared edits directory. Specifically, multiple network paths to the storage, and redundancy in the storage itself (disk, network, and power). Beacuse of this, it is recommended that the shared storage server be a high-quality dedicated NAS appliance rather than a simple Linux server. +* **Shared storage** - you will need to have a shared directory which the NameNode machines have read/write access to. Typically this is a remote filer which supports NFS and is mounted on each of the NameNode machines. Currently only a single shared edits directory is supported. Thus, the availability of the system is limited by the availability of this shared edits directory, and therefore in order to remove all single points of failure there needs to be redundancy for the shared edits directory. Specifically, multiple network paths to the storage, and redundancy in the storage itself (disk, network, and power). Because of this, it is recommended that the shared storage server be a high-quality dedicated NAS appliance rather than a simple Linux server. Note that, in an HA cluster, the Standby NameNodes also perform checkpoints of the namespace state, and thus it is not necessary to run a Secondary NameNode, CheckpointNode, or BackupNode in an HA cluster. In fact, to do so would be an error. This also allows one who is reconfiguring a non-HA-enabled HDFS cluster to be HA-enabled to reuse the hardware which they had previously dedicated to the Secondary NameNode. @@ -137,7 +137,7 @@ The order in which you set these configurations is unimportant, but the values y * **dfs.namenode.rpc-address.[nameservice ID].[name node ID]** - the fully-qualified RPC address for each NameNode to listen on For both of the previously-configured NameNode IDs, set the full address and - IPC port of the NameNode processs. Note that this results in two separate + IPC port of the NameNode process. Note that this results in two separate configuration options. For example: diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HdfsImageViewer.md b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HdfsImageViewer.md index de27fc2679f..f55c9fda024 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HdfsImageViewer.md +++ b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HdfsImageViewer.md @@ -50,10 +50,13 @@ The Offline Image Viewer provides several output processors: ..., s[n-1], maxSize], and the processor calculates how many files in the system fall into each segment [s[i-1], s[i]). Note that files larger than maxSize always fall into the very last segment. - The output file is formatted as a tab separated two column table: - Size and NumFiles. Where Size represents the start of the segment, + By default, the output file is formatted as a tab separated two column + table: Size and NumFiles. Where Size represents the start of the segment, and numFiles is the number of files form the image which size falls - in this segment. + in this segment. By specifying the option -format, the output file will be + formatted in a human-readable fashion rather than a number of bytes that + showed in Size column. In addition, the Size column will be changed to the + Size Range column. 4. Delimited (experimental): Generate a text file with all of the elements common to both inodes and inodes-under-construction, separated by a @@ -150,6 +153,7 @@ Options | `-addr` *address* | Specify the address(host:port) to listen. (localhost:5978 by default). This option is used with Web processor. | | `-maxSize` *size* | Specify the range [0, maxSize] of file sizes to be analyzed in bytes (128GB by default). This option is used with FileDistribution processor. | | `-step` *size* | Specify the granularity of the distribution in bytes (2MB by default). This option is used with FileDistribution processor. | +| `-format` | Format the output result in a human-readable fashion rather than a number of bytes. (false by default). This option is used with FileDistribution processor. | | `-delimiter` *arg* | Delimiting string to use with Delimited processor. | | `-t`\|`--temp` *temporary dir* | Use temporary dir to cache intermediate result to generate Delimited outputs. If not set, Delimited processor constructs the namespace in memory before outputting text. | | `-h`\|`--help` | Display the tool usage and help information and exit. | @@ -181,6 +185,9 @@ Due to the internal layout changes introduced by the ProtocolBuffer-based fsimag | `-i`\|`--inputFile` *input file* | Specify the input fsimage file to process. Required. | | `-o`\|`--outputFile` *output file* | Specify the output filename, if the specified output processor generates one. If the specified file already exists, it is silently overwritten. Required. | | `-p`\|`--processor` *processor* | Specify the image processor to apply against the image file. Valid options are Ls (default), XML, Delimited, Indented, and FileDistribution. | +| `-maxSize` *size* | Specify the range [0, maxSize] of file sizes to be analyzed in bytes (128GB by default). This option is used with FileDistribution processor. | +| `-step` *size* | Specify the granularity of the distribution in bytes (2MB by default). This option is used with FileDistribution processor. | +| `-format` | Format the output result in a human-readable fashion rather than a number of bytes. (false by default). This option is used with FileDistribution processor. | | `-skipBlocks` | Do not enumerate individual blocks within files. This may save processing time and outfile file space on namespaces with very large files. The Ls processor reads the blocks to correctly determine file sizes and ignores this option. | | `-printToScreen` | Pipe output of processor to console as well as specified file. On extremely large namespaces, this may increase processing time by an order of magnitude. | | `-delimiter` *arg* | When used in conjunction with the Delimited processor, replaces the default tab delimiter with the string specified by *arg*. | diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HdfsMultihoming.md b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HdfsMultihoming.md index 4be55118f5e..4e1d4804f4f 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HdfsMultihoming.md +++ b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HdfsMultihoming.md @@ -86,7 +86,7 @@ The solution is to have separate setting for server endpoints to force binding t dfs.namenode.http-bind-host 0.0.0.0 - The actual adress the HTTP server will bind to. If this optional address + The actual address the HTTP server will bind to. If this optional address is set, it overrides only the hostname portion of dfs.namenode.http-address. It can also be specified per name node or name service for HA/Federation. This is useful for making the name node HTTP server listen on all @@ -98,7 +98,7 @@ The solution is to have separate setting for server endpoints to force binding t dfs.namenode.https-bind-host 0.0.0.0 - The actual adress the HTTPS server will bind to. If this optional address + The actual address the HTTPS server will bind to. If this optional address is set, it overrides only the hostname portion of dfs.namenode.https-address. It can also be specified per name node or name service for HA/Federation. This is useful for making the name node HTTPS server listen on all diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HdfsNfsGateway.md b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HdfsNfsGateway.md index 67311891586..37a2042d089 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HdfsNfsGateway.md +++ b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HdfsNfsGateway.md @@ -148,7 +148,7 @@ It's strongly recommended for the users to update a few configuration properties characters. The machine name format can be a single host, a "*", a Java regular expression, or an IPv4 address. The access privilege uses rw or ro to specify read/write or read-only access of the machines to exports. If the access privilege is not provided, the default is read-only. Entries are separated by ";". For example: "192.168.0.0/22 rw ; \\\\w\*\\\\.example\\\\.com ; host1.test.org ro;". Only the NFS gateway needs to restart after - this property is updated. Note that, here Java regular expression is differnt with the regrulation expression used in + this property is updated. Note that, here Java regular expression is different with the regulation expression used in Linux NFS export table, such as, using "\\\\w\*\\\\.example\\\\.com" instead of "\*.example.com", "192\\\\.168\\\\.0\\\\.(11|22)" instead of "192.168.0.[11|22]" and so on. @@ -183,7 +183,7 @@ It's strongly recommended for the users to update a few configuration properties * JVM and log settings. You can export JVM settings (e.g., heap size and GC log) in - HADOOP\_NFS3\_OPTS. More NFS related settings can be found in hadoop-env.sh. + HDFS\_NFS3\_OPTS. More NFS related settings can be found in hadoop-env.sh. To get NFS debug trace, you can edit the log4j.property file to add the following. Note, debug trace, especially for ONCRPC, can be very verbose. diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/ViewFs.md b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/ViewFs.md index 94662f55df6..5f88deffe2a 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/ViewFs.md +++ b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/ViewFs.md @@ -143,7 +143,7 @@ Hence on Cluster X, where the `core-site.xml` is set to make the default fs to u ### Pathname Usage Best Practices -When one is within a cluster, it is recommended to use the pathname of type (1) above instead of a fully qualified URI like (2). Futher, applications should not use the knowledge of the mount points and use a path like `hdfs://namenodeContainingUserDirs:port/joe/foo/bar` to refer to a file in a particular namenode. One should use `/user/joe/foo/bar` instead. +When one is within a cluster, it is recommended to use the pathname of type (1) above instead of a fully qualified URI like (2). Further, applications should not use the knowledge of the mount points and use a path like `hdfs://namenodeContainingUserDirs:port/joe/foo/bar` to refer to a file in a particular namenode. One should use `/user/joe/foo/bar` instead. ### Renaming Pathnames Across Namespaces diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSStripedInputStream.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSStripedInputStream.java index 18c2de91d79..1e27745e499 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSStripedInputStream.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSStripedInputStream.java @@ -57,7 +57,8 @@ import static org.junit.Assert.assertTrue; public class TestDFSStripedInputStream { - public static final Log LOG = LogFactory.getLog(TestDFSStripedInputStream.class); + public static final Log LOG = + LogFactory.getLog(TestDFSStripedInputStream.class); private MiniDFSCluster cluster; private Configuration conf = new Configuration(); @@ -272,12 +273,16 @@ public class TestDFSStripedInputStream { // |10 | done += in.read(0, readBuffer, 0, delta); assertEquals(delta, done); + assertArrayEquals(Arrays.copyOf(expected, done), + Arrays.copyOf(readBuffer, done)); // both head and trail cells are partial // |c_0 |c_1 |c_2 |c_3 |c_4 |c_5 | // |256K - 10|missing|256K|256K|256K - 10|not in range| done += in.read(delta, readBuffer, delta, CELLSIZE * (DATA_BLK_NUM - 1) - 2 * delta); assertEquals(CELLSIZE * (DATA_BLK_NUM - 1) - delta, done); + assertArrayEquals(Arrays.copyOf(expected, done), + Arrays.copyOf(readBuffer, done)); // read the rest done += in.read(done, readBuffer, done, readSize - done); assertEquals(readSize, done); @@ -291,8 +296,8 @@ public class TestDFSStripedInputStream { testStatefulRead(true, true); } - private void testStatefulRead(boolean useByteBuffer, boolean cellMisalignPacket) - throws Exception { + private void testStatefulRead(boolean useByteBuffer, + boolean cellMisalignPacket) throws Exception { final int numBlocks = 2; final int fileSize = numBlocks * BLOCK_GROUP_SIZE; if (cellMisalignPacket) { @@ -302,7 +307,8 @@ public class TestDFSStripedInputStream { } DFSTestUtil.createStripedFile(cluster, filePath, null, numBlocks, NUM_STRIPE_PER_BLOCK, false); - LocatedBlocks lbs = fs.getClient().namenode.getBlockLocations(filePath.toString(), 0, fileSize); + LocatedBlocks lbs = fs.getClient().namenode. + getBlockLocations(filePath.toString(), 0, fileSize); assert lbs.getLocatedBlocks().size() == numBlocks; for (LocatedBlock lb : lbs.getLocatedBlocks()) { @@ -360,4 +366,111 @@ public class TestDFSStripedInputStream { } fs.delete(filePath, true); } + + @Test + public void testStatefulReadWithDNFailure() throws Exception { + final int numBlocks = 4; + final int failedDNIdx = DATA_BLK_NUM - 1; + DFSTestUtil.createStripedFile(cluster, filePath, null, numBlocks, + NUM_STRIPE_PER_BLOCK, false); + LocatedBlocks lbs = fs.getClient().namenode.getBlockLocations( + filePath.toString(), 0, BLOCK_GROUP_SIZE); + + assert lbs.get(0) instanceof LocatedStripedBlock; + LocatedStripedBlock bg = (LocatedStripedBlock) (lbs.get(0)); + for (int i = 0; i < DATA_BLK_NUM + PARITY_BLK_NUM; i++) { + Block blk = new Block(bg.getBlock().getBlockId() + i, + NUM_STRIPE_PER_BLOCK * CELLSIZE, + bg.getBlock().getGenerationStamp()); + blk.setGenerationStamp(bg.getBlock().getGenerationStamp()); + cluster.injectBlocks(i, Arrays.asList(blk), + bg.getBlock().getBlockPoolId()); + } + DFSStripedInputStream in = + new DFSStripedInputStream(fs.getClient(), filePath.toString(), false, + ecPolicy, null); + int readSize = BLOCK_GROUP_SIZE; + byte[] readBuffer = new byte[readSize]; + byte[] expected = new byte[readSize]; + /** A variation of {@link DFSTestUtil#fillExpectedBuf} for striped blocks */ + for (int i = 0; i < NUM_STRIPE_PER_BLOCK; i++) { + for (int j = 0; j < DATA_BLK_NUM; j++) { + for (int k = 0; k < CELLSIZE; k++) { + int posInBlk = i * CELLSIZE + k; + int posInFile = i * CELLSIZE * DATA_BLK_NUM + j * CELLSIZE + k; + expected[posInFile] = SimulatedFSDataset.simulatedByte( + new Block(bg.getBlock().getBlockId() + j), posInBlk); + } + } + } + + ErasureCoderOptions coderOptions = new ErasureCoderOptions( + DATA_BLK_NUM, PARITY_BLK_NUM); + RawErasureDecoder rawDecoder = CodecUtil.createRawDecoder(conf, + ecPolicy.getCodecName(), coderOptions); + + // Update the expected content for decoded data + int[] missingBlkIdx = new int[PARITY_BLK_NUM]; + for (int i = 0; i < missingBlkIdx.length; i++) { + if (i == 0) { + missingBlkIdx[i] = failedDNIdx; + } else { + missingBlkIdx[i] = DATA_BLK_NUM + i; + } + } + cluster.stopDataNode(failedDNIdx); + for (int i = 0; i < NUM_STRIPE_PER_BLOCK; i++) { + byte[][] decodeInputs = new byte[DATA_BLK_NUM + PARITY_BLK_NUM][CELLSIZE]; + byte[][] decodeOutputs = new byte[missingBlkIdx.length][CELLSIZE]; + for (int j = 0; j < DATA_BLK_NUM; j++) { + int posInBuf = i * CELLSIZE * DATA_BLK_NUM + j * CELLSIZE; + if (j != failedDNIdx) { + System.arraycopy(expected, posInBuf, decodeInputs[j], 0, CELLSIZE); + } + } + for (int j = DATA_BLK_NUM; j < DATA_BLK_NUM + PARITY_BLK_NUM; j++) { + for (int k = 0; k < CELLSIZE; k++) { + int posInBlk = i * CELLSIZE + k; + decodeInputs[j][k] = SimulatedFSDataset.simulatedByte( + new Block(bg.getBlock().getBlockId() + j), posInBlk); + } + } + for (int m : missingBlkIdx) { + decodeInputs[m] = null; + } + rawDecoder.decode(decodeInputs, missingBlkIdx, decodeOutputs); + int posInBuf = i * CELLSIZE * DATA_BLK_NUM + failedDNIdx * CELLSIZE; + System.arraycopy(decodeOutputs[0], 0, expected, posInBuf, CELLSIZE); + } + + int delta = 10; + int done = 0; + // read a small delta, shouldn't trigger decode + // |cell_0 | + // |10 | + done += in.read(readBuffer, 0, delta); + assertEquals(delta, done); + // both head and trail cells are partial + // |c_0 |c_1 |c_2 |c_3 |c_4 |c_5 | + // |256K - 10|missing|256K|256K|256K - 10|not in range| + while (done < (CELLSIZE * (DATA_BLK_NUM - 1) - 2 * delta)) { + int ret = in.read(readBuffer, delta, + CELLSIZE * (DATA_BLK_NUM - 1) - 2 * delta); + assertTrue(ret > 0); + done += ret; + } + assertEquals(CELLSIZE * (DATA_BLK_NUM - 1) - delta, done); + // read the rest + + int restSize; + restSize = readSize - done; + while (done < restSize) { + int ret = in.read(readBuffer, done, restSize); + assertTrue(ret > 0); + done += ret; + } + + assertEquals(readSize, done); + assertArrayEquals(expected, readBuffer); + } } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestInstrumentedLock.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestInstrumentedLock.java new file mode 100644 index 00000000000..f470688a184 --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestInstrumentedLock.java @@ -0,0 +1,166 @@ +/** + * 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; + +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; + +import org.apache.hadoop.util.AutoCloseableLock; +import org.apache.hadoop.util.Timer; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; + +/** + * A test class for InstrumentedLock. + */ +public class TestInstrumentedLock { + + static final Log LOG = LogFactory.getLog(TestInstrumentedLock.class); + + @Rule public TestName name = new TestName(); + + /** + * Test exclusive access of the lock. + * @throws Exception + */ + @Test(timeout=10000) + public void testMultipleThread() throws Exception { + String testname = name.getMethodName(); + InstrumentedLock lock = new InstrumentedLock(testname, LOG, 0, 300); + lock.lock(); + try { + Thread competingThread = new Thread() { + @Override + public void run() { + assertFalse(lock.tryLock()); + } + }; + competingThread.start(); + competingThread.join(); + } finally { + lock.unlock(); + } + } + + /** + * Test the correctness with try-with-resource syntax. + * @throws Exception + */ + @Test(timeout=10000) + public void testTryWithResourceSyntax() throws Exception { + String testname = name.getMethodName(); + final AtomicReference lockThread = new AtomicReference<>(null); + Lock lock = new InstrumentedLock(testname, LOG, 0, 300) { + @Override + public void lock() { + super.lock(); + lockThread.set(Thread.currentThread()); + } + @Override + public void unlock() { + super.unlock(); + lockThread.set(null); + } + }; + AutoCloseableLock acl = new AutoCloseableLock(lock); + try (AutoCloseable localLock = acl.acquire()) { + assertEquals(acl, localLock); + Thread competingThread = new Thread() { + @Override + public void run() { + assertNotEquals(Thread.currentThread(), lockThread.get()); + assertFalse(lock.tryLock()); + } + }; + competingThread.start(); + competingThread.join(); + assertEquals(Thread.currentThread(), lockThread.get()); + } + assertNull(lockThread.get()); + } + + /** + * Test the lock logs warning when lock held time is greater than threshold + * and not log warning otherwise. + * @throws Exception + */ + @Test(timeout=10000) + public void testLockLongHoldingReport() throws Exception { + String testname = name.getMethodName(); + final AtomicLong time = new AtomicLong(0); + Timer mclock = new Timer() { + @Override + public long monotonicNow() { + return time.get(); + } + }; + Lock mlock = mock(Lock.class); + + final AtomicLong wlogged = new AtomicLong(0); + final AtomicLong wsuppresed = new AtomicLong(0); + InstrumentedLock lock = new InstrumentedLock( + testname, LOG, mlock, 2000, 300, mclock) { + @Override + void logWarning(long lockHeldTime, long suppressed) { + wlogged.incrementAndGet(); + wsuppresed.set(suppressed); + } + }; + + // do not log warning when the lock held time is short + lock.lock(); // t = 0 + time.set(200); + lock.unlock(); // t = 200 + assertEquals(0, wlogged.get()); + assertEquals(0, wsuppresed.get()); + + lock.lock(); // t = 200 + time.set(700); + lock.unlock(); // t = 700 + assertEquals(1, wlogged.get()); + assertEquals(0, wsuppresed.get()); + + // despite the lock held time is greater than threshold + // suppress the log warning due to the logging gap + // (not recorded in wsuppressed until next log message) + lock.lock(); // t = 700 + time.set(1100); + lock.unlock(); // t = 1100 + assertEquals(1, wlogged.get()); + assertEquals(0, wsuppresed.get()); + + // log a warning message when the lock held time is greater the threshold + // and the logging time gap is satisfied. Also should display suppressed + // previous warnings. + time.set(2400); + lock.lock(); // t = 2400 + time.set(2800); + lock.unlock(); // t = 2800 + assertEquals(2, wlogged.get()); + assertEquals(1, wsuppresed.get()); + } + +} diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/security/TestClientProtocolWithDelegationToken.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/security/TestClientProtocolWithDelegationToken.java new file mode 100644 index 00000000000..0b7ee337d8b --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/security/TestClientProtocolWithDelegationToken.java @@ -0,0 +1,119 @@ +/** + * 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.security; + +import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION; +import static org.mockito.Mockito.mock; + +import java.net.InetSocketAddress; +import java.security.PrivilegedExceptionAction; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hdfs.DFSConfigKeys; +import org.apache.hadoop.hdfs.protocol.ClientProtocol; +import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier; +import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenSecretManager; +import org.apache.hadoop.hdfs.server.namenode.FSNamesystem; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.ipc.Client; +import org.apache.hadoop.ipc.RPC; +import org.apache.hadoop.ipc.Server; +import org.apache.hadoop.net.NetUtils; +import org.apache.hadoop.security.SaslInputStream; +import org.apache.hadoop.security.SaslRpcClient; +import org.apache.hadoop.security.SaslRpcServer; +import org.apache.hadoop.security.SecurityUtil; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.security.token.Token; +import org.apache.hadoop.test.GenericTestUtils; +import org.apache.log4j.Level; +import org.junit.Test; + +/** Unit tests for using Delegation Token over RPC. */ +public class TestClientProtocolWithDelegationToken { + private static final String ADDRESS = "0.0.0.0"; + + public static final Log LOG = LogFactory + .getLog(TestClientProtocolWithDelegationToken.class); + + private static final Configuration conf; + static { + conf = new Configuration(); + conf.set(HADOOP_SECURITY_AUTHENTICATION, "kerberos"); + UserGroupInformation.setConfiguration(conf); + } + + static { + GenericTestUtils.setLogLevel(Client.LOG, Level.ALL); + GenericTestUtils.setLogLevel(Server.LOG, Level.ALL); + GenericTestUtils.setLogLevel(SaslRpcClient.LOG, Level.ALL); + GenericTestUtils.setLogLevel(SaslRpcServer.LOG, Level.ALL); + GenericTestUtils.setLogLevel(SaslInputStream.LOG, Level.ALL); + } + + @Test + public void testDelegationTokenRpc() throws Exception { + ClientProtocol mockNN = mock(ClientProtocol.class); + FSNamesystem mockNameSys = mock(FSNamesystem.class); + + DelegationTokenSecretManager sm = new DelegationTokenSecretManager( + DFSConfigKeys.DFS_NAMENODE_DELEGATION_KEY_UPDATE_INTERVAL_DEFAULT, + DFSConfigKeys.DFS_NAMENODE_DELEGATION_KEY_UPDATE_INTERVAL_DEFAULT, + DFSConfigKeys.DFS_NAMENODE_DELEGATION_TOKEN_MAX_LIFETIME_DEFAULT, + 3600000, mockNameSys); + sm.startThreads(); + final Server server = new RPC.Builder(conf) + .setProtocol(ClientProtocol.class).setInstance(mockNN) + .setBindAddress(ADDRESS).setPort(0).setNumHandlers(5).setVerbose(true) + .setSecretManager(sm).build(); + + server.start(); + + final UserGroupInformation current = UserGroupInformation.getCurrentUser(); + final InetSocketAddress addr = NetUtils.getConnectAddress(server); + String user = current.getUserName(); + Text owner = new Text(user); + DelegationTokenIdentifier dtId = new DelegationTokenIdentifier(owner, owner, null); + Token token = new Token( + dtId, sm); + SecurityUtil.setTokenService(token, addr); + LOG.info("Service for token is " + token.getService()); + current.addToken(token); + current.doAs(new PrivilegedExceptionAction() { + @Override + public Object run() throws Exception { + ClientProtocol proxy = null; + try { + proxy = RPC.getProxy(ClientProtocol.class, + ClientProtocol.versionID, addr, conf); + proxy.getServerDefaults(); + } finally { + server.stop(); + if (proxy != null) { + RPC.stopProxy(proxy); + } + } + return null; + } + }); + } + +} diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestFsDatasetImpl.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestFsDatasetImpl.java index b9468032811..a330fbfb27d 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestFsDatasetImpl.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestFsDatasetImpl.java @@ -589,20 +589,21 @@ public class TestFsDatasetImpl { // Will write and remove on dn0. final ExtendedBlock eb = new ExtendedBlock(BLOCK_POOL_IDS[0], 0); final CountDownLatch startFinalizeLatch = new CountDownLatch(1); - final CountDownLatch brReceivedLatch = new CountDownLatch(1); - final CountDownLatch volRemovedLatch = new CountDownLatch(1); + final CountDownLatch blockReportReceivedLatch = new CountDownLatch(1); + final CountDownLatch volRemoveStartedLatch = new CountDownLatch(1); + final CountDownLatch volRemoveCompletedLatch = new CountDownLatch(1); class BlockReportThread extends Thread { public void run() { // Lets wait for the volume remove process to start try { - volRemovedLatch.await(); + volRemoveStartedLatch.await(); } catch (Exception e) { LOG.info("Unexpected exception when waiting for vol removal:", e); } LOG.info("Getting block report"); dataset.getBlockReports(eb.getBlockPoolId()); LOG.info("Successfully received block report"); - brReceivedLatch.countDown(); + blockReportReceivedLatch.countDown(); } } @@ -623,7 +624,7 @@ public class TestFsDatasetImpl { } // Lets wait for the other thread finish getting block report - brReceivedLatch.await(); + blockReportReceivedLatch.await(); dataset.finalizeBlock(eb); LOG.info("FinalizeBlock finished"); @@ -633,34 +634,53 @@ public class TestFsDatasetImpl { } } - ResponderThread res = new ResponderThread(); - res.start(); - startFinalizeLatch.await(); - - // Verify if block report can be received - // when volume is being removed - final BlockReportThread brt = new BlockReportThread(); - brt.start(); - - Set volumesToRemove = new HashSet<>(); - volumesToRemove.add( - StorageLocation.parse(dataset.getVolume(eb).getBasePath()).getFile()); - /** - * TODO: {@link FsDatasetImpl#removeVolumes(Set, boolean)} is throwing - * IllegalMonitorStateException when there is a parallel reader/writer - * to the volume. Remove below try/catch block after fixing HDFS-10830. - */ - try { - LOG.info("Removing volume " + volumesToRemove); - dataset.removeVolumes(volumesToRemove, true); - } catch (Exception e) { - LOG.info("Unexpected issue while removing volume: ", e); - } finally { - volRemovedLatch.countDown(); + class VolRemoveThread extends Thread { + public void run() { + Set volumesToRemove = new HashSet<>(); + try { + volumesToRemove.add(StorageLocation.parse( + dataset.getVolume(eb).getBasePath()).getFile()); + } catch (Exception e) { + LOG.info("Problem preparing volumes to remove: ", e); + Assert.fail("Exception in remove volume thread, check log for " + + "details."); + } + LOG.info("Removing volume " + volumesToRemove); + dataset.removeVolumes(volumesToRemove, true); + volRemoveCompletedLatch.countDown(); + LOG.info("Removed volume " + volumesToRemove); + } } - LOG.info("Volumes removed"); - brReceivedLatch.await(); + // Start the volume write operation + ResponderThread responderThread = new ResponderThread(); + responderThread.start(); + startFinalizeLatch.await(); + + // Start the block report get operation + final BlockReportThread blockReportThread = new BlockReportThread(); + blockReportThread.start(); + + // Start the volume remove operation + VolRemoveThread volRemoveThread = new VolRemoveThread(); + volRemoveThread.start(); + + // Let volume write and remove operation be + // blocked for few seconds + Thread.sleep(2000); + + // Signal block report receiver and volume writer + // thread to complete their operations so that vol + // remove can proceed + volRemoveStartedLatch.countDown(); + + // Verify if block report can be received + // when volume is in use and also being removed + blockReportReceivedLatch.await(); + + // Verify if volume can be removed safely when there + // are read/write operation in-progress + volRemoveCompletedLatch.await(); } /** diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/diskbalancer/TestDiskBalancer.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/diskbalancer/TestDiskBalancer.java index dc177fddd14..eb15bdc656c 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/diskbalancer/TestDiskBalancer.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/diskbalancer/TestDiskBalancer.java @@ -25,7 +25,6 @@ import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.StorageType; import org.apache.hadoop.hdfs.DFSConfigKeys; import org.apache.hadoop.hdfs.DFSTestUtil; -import org.apache.hadoop.hdfs.DistributedFileSystem; import org.apache.hadoop.hdfs.HdfsConfiguration; import org.apache.hadoop.hdfs.MiniDFSCluster; import org.apache.hadoop.hdfs.server.balancer.TestBalancer; @@ -37,19 +36,18 @@ import org.apache.hadoop.hdfs.server.datanode.fsdataset.impl.FsVolumeImpl; import org.apache.hadoop.hdfs.server.diskbalancer.connectors.ClusterConnector; import org.apache.hadoop.hdfs.server.diskbalancer.connectors.ConnectorFactory; import org.apache.hadoop.hdfs.server.diskbalancer.datamodel.DiskBalancerCluster; -import org.apache.hadoop.hdfs.server.diskbalancer.datamodel - .DiskBalancerDataNode; +import org.apache.hadoop.hdfs.server.diskbalancer.datamodel.DiskBalancerDataNode; import org.apache.hadoop.hdfs.server.diskbalancer.datamodel.DiskBalancerVolume; import org.apache.hadoop.hdfs.server.diskbalancer.planner.NodePlan; import org.apache.hadoop.test.GenericTestUtils; import org.apache.hadoop.util.Time; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; -import java.net.URISyntaxException; import java.util.LinkedList; import java.util.List; -import java.util.Random; import java.util.concurrent.TimeoutException; import static org.junit.Assert.assertEquals; @@ -62,6 +60,7 @@ import static org.junit.Assert.assertTrue; public class TestDiskBalancer { private static final String PLAN_FILE = "/system/current.plan.json"; + static final Logger LOG = LoggerFactory.getLogger(TestDiskBalancer.class); @Test public void testDiskBalancerNameNodeConnectivity() throws Exception { @@ -110,227 +109,77 @@ public class TestDiskBalancer { */ @Test public void testDiskBalancerEndToEnd() throws Exception { + Configuration conf = new HdfsConfiguration(); - final int defaultBlockSize = 100; conf.setBoolean(DFSConfigKeys.DFS_DISK_BALANCER_ENABLED, true); - conf.setLong(DFSConfigKeys.DFS_BLOCK_SIZE_KEY, defaultBlockSize); - conf.setInt(DFSConfigKeys.DFS_BYTES_PER_CHECKSUM_KEY, defaultBlockSize); - conf.setLong(DFSConfigKeys.DFS_HEARTBEAT_INTERVAL_KEY, 1L); - final int numDatanodes = 1; - final String fileName = "/tmp.txt"; - final Path filePath = new Path(fileName); - final int blocks = 100; - final int blocksSize = 1024; - final int fileLen = blocks * blocksSize; + final int blockCount = 100; + final int blockSize = 1024; + final int diskCount = 2; + final int dataNodeCount = 1; + final int dataNodeIndex = 0; + final int sourceDiskIndex = 0; - - // Write a file and restart the cluster - long[] capacities = new long[]{defaultBlockSize * 2 * fileLen, - defaultBlockSize * 2 * fileLen}; - MiniDFSCluster cluster = new MiniDFSCluster.Builder(conf) - .numDataNodes(numDatanodes) - .storageCapacities(capacities) - .storageTypes(new StorageType[]{StorageType.DISK, StorageType.DISK}) - .storagesPerDatanode(2) + MiniDFSCluster cluster = new ClusterBuilder() + .setBlockCount(blockCount) + .setBlockSize(blockSize) + .setDiskCount(diskCount) + .setNumDatanodes(dataNodeCount) + .setConf(conf) .build(); - FsVolumeImpl source = null; - FsVolumeImpl dest = null; try { - cluster.waitActive(); - Random r = new Random(); - FileSystem fs = cluster.getFileSystem(0); - TestBalancer.createFile(cluster, filePath, fileLen, (short) 1, - numDatanodes - 1); - - DFSTestUtil.waitReplication(fs, filePath, (short) 1); - cluster.restartDataNodes(); - cluster.waitActive(); - - // Get the data node and move all data to one disk. - DataNode dnNode = cluster.getDataNodes().get(numDatanodes - 1); - try (FsDatasetSpi.FsVolumeReferences refs = - dnNode.getFSDataset().getFsVolumeReferences()) { - source = (FsVolumeImpl) refs.get(0); - dest = (FsVolumeImpl) refs.get(1); - assertTrue(DiskBalancerTestUtil.getBlockCount(source) > 0); - DiskBalancerTestUtil.moveAllDataToDestVolume(dnNode.getFSDataset(), - source, dest); - assertTrue(DiskBalancerTestUtil.getBlockCount(source) == 0); - } - - cluster.restartDataNodes(); - cluster.waitActive(); - - // Start up a disk balancer and read the cluster info. - final DataNode newDN = cluster.getDataNodes().get(numDatanodes - 1); - ClusterConnector nameNodeConnector = - ConnectorFactory.getCluster(cluster.getFileSystem(0).getUri(), conf); - - DiskBalancerCluster diskBalancerCluster = - new DiskBalancerCluster(nameNodeConnector); - diskBalancerCluster.readClusterInfo(); - List nodesToProcess = new LinkedList<>(); - - // Rewrite the capacity in the model to show that disks need - // re-balancing. - setVolumeCapacity(diskBalancerCluster, defaultBlockSize * 2 * fileLen, - "DISK"); - // Pick a node to process. - nodesToProcess.add(diskBalancerCluster.getNodeByUUID(dnNode - .getDatanodeUuid())); - diskBalancerCluster.setNodesToProcess(nodesToProcess); - - // Compute a plan. - List clusterplan = diskBalancerCluster.computePlan(0.0f); - - // Now we must have a plan,since the node is imbalanced and we - // asked the disk balancer to create a plan. - assertTrue(clusterplan.size() == 1); - - NodePlan plan = clusterplan.get(0); - plan.setNodeUUID(dnNode.getDatanodeUuid()); - plan.setTimeStamp(Time.now()); - String planJson = plan.toJson(); - String planID = DigestUtils.shaHex(planJson); - assertNotNull(plan.getVolumeSetPlans()); - assertTrue(plan.getVolumeSetPlans().size() > 0); - plan.getVolumeSetPlans().get(0).setTolerancePercent(10); - - // Submit the plan and wait till the execution is done. - newDN.submitDiskBalancerPlan(planID, 1, PLAN_FILE, planJson, false); - String jmxString = newDN.getDiskBalancerStatus(); - assertNotNull(jmxString); - DiskBalancerWorkStatus status = - DiskBalancerWorkStatus.parseJson(jmxString); - DiskBalancerWorkStatus realStatus = newDN.queryDiskBalancerPlan(); - assertEquals(realStatus.getPlanID(), status.getPlanID()); - - GenericTestUtils.waitFor(new Supplier() { - @Override - public Boolean get() { - try { - return newDN.queryDiskBalancerPlan().getResult() == - DiskBalancerWorkStatus.Result.PLAN_DONE; - } catch (IOException ex) { - return false; - } - } - }, 1000, 100000); - - - //verify that it worked. - dnNode = cluster.getDataNodes().get(numDatanodes - 1); - assertEquals(dnNode.queryDiskBalancerPlan().getResult(), - DiskBalancerWorkStatus.Result.PLAN_DONE); - try (FsDatasetSpi.FsVolumeReferences refs = - dnNode.getFSDataset().getFsVolumeReferences()) { - source = (FsVolumeImpl) refs.get(0); - assertTrue(DiskBalancerTestUtil.getBlockCount(source) > 0); - } - - - // Tolerance - long delta = (plan.getVolumeSetPlans().get(0).getBytesToMove() - * 10) / 100; - assertTrue( - (DiskBalancerTestUtil.getBlockCount(source) * - defaultBlockSize + delta) >= - plan.getVolumeSetPlans().get(0).getBytesToMove()); - + DataMover dataMover = new DataMover(cluster, dataNodeIndex, + sourceDiskIndex, conf, blockSize, blockCount); + dataMover.moveDataToSourceDisk(); + NodePlan plan = dataMover.generatePlan(); + dataMover.executePlan(plan); + dataMover.verifyPlanExectionDone(); + dataMover.verifyAllVolumesHaveData(); + dataMover.verifyTolerance(plan, 0, sourceDiskIndex, 10); } finally { cluster.shutdown(); } } - @Test(timeout=60000) + @Test public void testBalanceDataBetweenMultiplePairsOfVolumes() throws Exception { + Configuration conf = new HdfsConfiguration(); - final int DEFAULT_BLOCK_SIZE = 2048; conf.setBoolean(DFSConfigKeys.DFS_DISK_BALANCER_ENABLED, true); - conf.setLong(DFSConfigKeys.DFS_BLOCK_SIZE_KEY, DEFAULT_BLOCK_SIZE); - conf.setInt(DFSConfigKeys.DFS_BYTES_PER_CHECKSUM_KEY, DEFAULT_BLOCK_SIZE); - conf.setLong(DFSConfigKeys.DFS_HEARTBEAT_INTERVAL_KEY, 1L); - final int NUM_DATANODES = 1; - final long CAP = 512 * 1024; - final Path testFile = new Path("/testfile"); - MiniDFSCluster cluster = new MiniDFSCluster.Builder(conf) - .numDataNodes(NUM_DATANODES) - .storageCapacities(new long[]{CAP, CAP, CAP, CAP}) - .storagesPerDatanode(4) + final int blockCount = 1000; + final int blockSize = 1024; + + // create 3 disks, that means we will have 2 plans + // Move Data from disk0->disk1 and disk0->disk2. + final int diskCount = 3; + final int dataNodeCount = 1; + final int dataNodeIndex = 0; + final int sourceDiskIndex = 0; + + + MiniDFSCluster cluster = new ClusterBuilder() + .setBlockCount(blockCount) + .setBlockSize(blockSize) + .setDiskCount(diskCount) + .setNumDatanodes(dataNodeCount) + .setConf(conf) .build(); + + try { - cluster.waitActive(); - DistributedFileSystem fs = cluster.getFileSystem(); - TestBalancer.createFile(cluster, testFile, CAP, (short) 1, 0); + DataMover dataMover = new DataMover(cluster, dataNodeIndex, + sourceDiskIndex, conf, blockSize, blockCount); + dataMover.moveDataToSourceDisk(); + NodePlan plan = dataMover.generatePlan(); - DFSTestUtil.waitReplication(fs, testFile, (short) 1); - DataNode dnNode = cluster.getDataNodes().get(0); - // Move data out of two volumes to make them empty. - try (FsDatasetSpi.FsVolumeReferences refs = - dnNode.getFSDataset().getFsVolumeReferences()) { - assertEquals(4, refs.size()); - for (int i = 0; i < refs.size(); i += 2) { - FsVolumeImpl source = (FsVolumeImpl) refs.get(i); - FsVolumeImpl dest = (FsVolumeImpl) refs.get(i + 1); - assertTrue(DiskBalancerTestUtil.getBlockCount(source) > 0); - DiskBalancerTestUtil.moveAllDataToDestVolume(dnNode.getFSDataset(), - source, dest); - assertTrue(DiskBalancerTestUtil.getBlockCount(source) == 0); - } - } + // 3 disks , The plan should move data both disks, + // so we must have 2 plan steps. + assertEquals(plan.getVolumeSetPlans().size(), 2); - cluster.restartDataNodes(); - cluster.waitActive(); - - // Start up a disk balancer and read the cluster info. - final DataNode dataNode = cluster.getDataNodes().get(0); - ClusterConnector nameNodeConnector = - ConnectorFactory.getCluster(cluster.getFileSystem(0).getUri(), conf); - - DiskBalancerCluster diskBalancerCluster = - new DiskBalancerCluster(nameNodeConnector); - diskBalancerCluster.readClusterInfo(); - List nodesToProcess = new LinkedList<>(); - // Rewrite the capacity in the model to show that disks need - // re-balancing. - setVolumeCapacity(diskBalancerCluster, CAP, "DISK"); - nodesToProcess.add(diskBalancerCluster.getNodeByUUID( - dataNode.getDatanodeUuid())); - diskBalancerCluster.setNodesToProcess(nodesToProcess); - - // Compute a plan. - List clusterPlan = diskBalancerCluster.computePlan(10.0f); - - NodePlan plan = clusterPlan.get(0); - assertEquals(2, plan.getVolumeSetPlans().size()); - plan.setNodeUUID(dnNode.getDatanodeUuid()); - plan.setTimeStamp(Time.now()); - String planJson = plan.toJson(); - String planID = DigestUtils.shaHex(planJson); - - dataNode.submitDiskBalancerPlan(planID, 1, PLAN_FILE, planJson, false); - - GenericTestUtils.waitFor(new Supplier() { - @Override - public Boolean get() { - try { - return dataNode.queryDiskBalancerPlan().getResult() == - DiskBalancerWorkStatus.Result.PLAN_DONE; - } catch (IOException ex) { - return false; - } - } - }, 1000, 100000); - assertEquals(dataNode.queryDiskBalancerPlan().getResult(), - DiskBalancerWorkStatus.Result.PLAN_DONE); - - try (FsDatasetSpi.FsVolumeReferences refs = - dataNode.getFSDataset().getFsVolumeReferences()) { - for (FsVolumeSpi vol : refs) { - assertTrue(DiskBalancerTestUtil.getBlockCount(vol) > 0); - } - } + dataMover.executePlan(plan); + dataMover.verifyPlanExectionDone(); + dataMover.verifyAllVolumesHaveData(); + dataMover.verifyTolerance(plan, 0, sourceDiskIndex, 10); } finally { cluster.shutdown(); } @@ -353,4 +202,293 @@ public class TestDiskBalancer { node.getVolumeSets().get(diskType).computeVolumeDataDensity(); } } + + /** + * Helper class that allows us to create different kinds of MiniDFSClusters + * and populate data. + */ + static class ClusterBuilder { + private Configuration conf; + private int blockSize; + private int numDatanodes; + private int fileLen; + private int blockCount; + private int diskCount; + + public ClusterBuilder setConf(Configuration conf) { + this.conf = conf; + return this; + } + + public ClusterBuilder setBlockSize(int blockSize) { + this.blockSize = blockSize; + return this; + } + + public ClusterBuilder setNumDatanodes(int datanodeCount) { + this.numDatanodes = datanodeCount; + return this; + } + + public ClusterBuilder setBlockCount(int blockCount) { + this.blockCount = blockCount; + return this; + } + + public ClusterBuilder setDiskCount(int diskCount) { + this.diskCount = diskCount; + return this; + } + + private long[] getCapacities(int diskCount, int bSize, int fSize) { + Preconditions.checkState(diskCount > 0); + long[] capacities = new long[diskCount]; + for (int x = 0; x < diskCount; x++) { + capacities[x] = diskCount * bSize * fSize * 2L; + } + return capacities; + } + + private StorageType[] getStorageTypes(int diskCount) { + Preconditions.checkState(diskCount > 0); + StorageType[] array = new StorageType[diskCount]; + for (int x = 0; x < diskCount; x++) { + array[x] = StorageType.DISK; + } + return array; + } + + public MiniDFSCluster build() throws IOException, TimeoutException, + InterruptedException { + Preconditions.checkNotNull(this.conf); + Preconditions.checkState(blockSize > 0); + Preconditions.checkState(numDatanodes > 0); + fileLen = blockCount * blockSize; + Preconditions.checkState(fileLen > 0); + conf.setBoolean(DFSConfigKeys.DFS_DISK_BALANCER_ENABLED, true); + conf.setLong(DFSConfigKeys.DFS_BLOCK_SIZE_KEY, blockSize); + conf.setInt(DFSConfigKeys.DFS_BYTES_PER_CHECKSUM_KEY, blockSize); + conf.setLong(DFSConfigKeys.DFS_HEARTBEAT_INTERVAL_KEY, 1L); + + final String fileName = "/tmp.txt"; + Path filePath = new Path(fileName); + fileLen = blockCount * blockSize; + + + // Write a file and restart the cluster + MiniDFSCluster cluster = new MiniDFSCluster.Builder(conf) + .numDataNodes(numDatanodes) + .storageCapacities(getCapacities(diskCount, blockSize, fileLen)) + .storageTypes(getStorageTypes(diskCount)) + .storagesPerDatanode(diskCount) + .build(); + generateData(filePath, cluster); + cluster.restartDataNodes(); + cluster.waitActive(); + return cluster; + } + + private void generateData(Path filePath, MiniDFSCluster cluster) + throws IOException, InterruptedException, TimeoutException { + cluster.waitActive(); + FileSystem fs = cluster.getFileSystem(0); + TestBalancer.createFile(cluster, filePath, fileLen, (short) 1, + numDatanodes - 1); + DFSTestUtil.waitReplication(fs, filePath, (short) 1); + cluster.restartDataNodes(); + cluster.waitActive(); + } + } + + class DataMover { + private final MiniDFSCluster cluster; + private final int sourceDiskIndex; + private final int dataNodeIndex; + private final Configuration conf; + private final int blockCount; + private final int blockSize; + private DataNode node; + + /** + * Constructs a DataMover class. + * + * @param cluster - MiniDFSCluster. + * @param dataNodeIndex - Datanode to operate against. + * @param sourceDiskIndex - source Disk Index. + */ + public DataMover(MiniDFSCluster cluster, int dataNodeIndex, int + sourceDiskIndex, Configuration conf, int blockSize, int + blockCount) { + this.cluster = cluster; + this.dataNodeIndex = dataNodeIndex; + this.node = cluster.getDataNodes().get(dataNodeIndex); + this.sourceDiskIndex = sourceDiskIndex; + this.conf = conf; + this.blockCount = blockCount; + this.blockSize = blockSize; + } + + /** + * Moves all data to a source disk to create disk imbalance so we can run a + * planner. + * + * @throws IOException + */ + public void moveDataToSourceDisk() throws IOException { + moveAllDataToDestDisk(this.node, sourceDiskIndex); + cluster.restartDataNodes(); + cluster.waitActive(); + + } + + /** + * Moves all data in the data node to one disk. + * + * @param dataNode - Datanode + * @param destDiskindex - Index of the destination disk. + */ + private void moveAllDataToDestDisk(DataNode dataNode, int destDiskindex) + throws IOException { + Preconditions.checkNotNull(dataNode); + Preconditions.checkState(destDiskindex >= 0); + try (FsDatasetSpi.FsVolumeReferences refs = + dataNode.getFSDataset().getFsVolumeReferences()) { + if (refs.size() <= destDiskindex) { + throw new IllegalArgumentException("Invalid Disk index."); + } + FsVolumeImpl dest = (FsVolumeImpl) refs.get(destDiskindex); + for (int x = 0; x < refs.size(); x++) { + if (x == destDiskindex) { + continue; + } + FsVolumeImpl source = (FsVolumeImpl) refs.get(x); + DiskBalancerTestUtil.moveAllDataToDestVolume(dataNode.getFSDataset(), + source, dest); + + } + } + } + + /** + * Generates a NodePlan for the datanode specified. + * + * @return NodePlan. + */ + public NodePlan generatePlan() throws Exception { + + // Start up a disk balancer and read the cluster info. + node = cluster.getDataNodes().get(dataNodeIndex); + ClusterConnector nameNodeConnector = + ConnectorFactory.getCluster(cluster.getFileSystem(dataNodeIndex) + .getUri(), conf); + + DiskBalancerCluster diskBalancerCluster = + new DiskBalancerCluster(nameNodeConnector); + diskBalancerCluster.readClusterInfo(); + List nodesToProcess = new LinkedList<>(); + + // Rewrite the capacity in the model to show that disks need + // re-balancing. + setVolumeCapacity(diskBalancerCluster, blockSize * 2L * blockCount, + "DISK"); + // Pick a node to process. + nodesToProcess.add(diskBalancerCluster.getNodeByUUID( + node.getDatanodeUuid())); + diskBalancerCluster.setNodesToProcess(nodesToProcess); + + // Compute a plan. + List clusterplan = diskBalancerCluster.computePlan(0.0f); + + // Now we must have a plan,since the node is imbalanced and we + // asked the disk balancer to create a plan. + assertTrue(clusterplan.size() == 1); + + NodePlan plan = clusterplan.get(0); + plan.setNodeUUID(node.getDatanodeUuid()); + plan.setTimeStamp(Time.now()); + + assertNotNull(plan.getVolumeSetPlans()); + assertTrue(plan.getVolumeSetPlans().size() > 0); + plan.getVolumeSetPlans().get(0).setTolerancePercent(10); + return plan; + } + + /** + * Waits for a plan executing to finish. + */ + public void executePlan(NodePlan plan) throws + IOException, TimeoutException, InterruptedException { + + node = cluster.getDataNodes().get(dataNodeIndex); + String planJson = plan.toJson(); + String planID = DigestUtils.shaHex(planJson); + + // Submit the plan and wait till the execution is done. + node.submitDiskBalancerPlan(planID, 1, PLAN_FILE, planJson, + false); + String jmxString = node.getDiskBalancerStatus(); + assertNotNull(jmxString); + DiskBalancerWorkStatus status = + DiskBalancerWorkStatus.parseJson(jmxString); + DiskBalancerWorkStatus realStatus = node.queryDiskBalancerPlan(); + assertEquals(realStatus.getPlanID(), status.getPlanID()); + + GenericTestUtils.waitFor(new Supplier() { + @Override + public Boolean get() { + try { + return node.queryDiskBalancerPlan().getResult() == + DiskBalancerWorkStatus.Result.PLAN_DONE; + } catch (IOException ex) { + return false; + } + } + }, 1000, 100000); + } + + /** + * Verifies the Plan Execution has been done. + */ + public void verifyPlanExectionDone() throws IOException { + node = cluster.getDataNodes().get(dataNodeIndex); + assertEquals(node.queryDiskBalancerPlan().getResult(), + DiskBalancerWorkStatus.Result.PLAN_DONE); + } + + /** + * Once diskBalancer is run, all volumes mush has some data. + */ + public void verifyAllVolumesHaveData() throws IOException { + node = cluster.getDataNodes().get(dataNodeIndex); + try (FsDatasetSpi.FsVolumeReferences refs = + node.getFSDataset().getFsVolumeReferences()) { + for (FsVolumeSpi volume : refs) { + assertTrue(DiskBalancerTestUtil.getBlockCount(volume) > 0); + LOG.info(refs.toString() + " : Block Count : {}", + DiskBalancerTestUtil.getBlockCount(volume)); + } + } + } + + /** + * Verifies that tolerance values are honored correctly. + */ + public void verifyTolerance(NodePlan plan, int planIndex, int + sourceDiskIndex, int tolerance) throws IOException { + // Tolerance + long delta = (plan.getVolumeSetPlans().get(planIndex).getBytesToMove() + * tolerance) / 100; + FsVolumeImpl volume = null; + try (FsDatasetSpi.FsVolumeReferences refs = + node.getFSDataset().getFsVolumeReferences()) { + volume = (FsVolumeImpl) refs.get(sourceDiskIndex); + assertTrue(DiskBalancerTestUtil.getBlockCount(volume) > 0); + + assertTrue( + (DiskBalancerTestUtil.getBlockCount(volume) * + (blockSize + delta)) >= + plan.getVolumeSetPlans().get(0).getBytesToMove()); + } + } + } } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/diskbalancer/TestDiskBalancerWithMockMover.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/diskbalancer/TestDiskBalancerWithMockMover.java index c362f49706f..794a887aa68 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/diskbalancer/TestDiskBalancerWithMockMover.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/diskbalancer/TestDiskBalancerWithMockMover.java @@ -358,14 +358,13 @@ public class TestDiskBalancerWithMockMover { private AtomicBoolean shouldRun; private FsDatasetSpi dataset; - private Integer runCount; + private int runCount; private volatile boolean sleepInCopyBlocks; private long delay; public TestMover(FsDatasetSpi dataset) { this.dataset = dataset; this.shouldRun = new AtomicBoolean(false); - this.runCount = new Integer(0); } public void setSleep() { @@ -401,7 +400,7 @@ public class TestDiskBalancerWithMockMover { if (delay > 0) { Thread.sleep(delay); } - synchronized (runCount) { + synchronized (this) { if (shouldRun()) { runCount++; } @@ -461,9 +460,9 @@ public class TestDiskBalancerWithMockMover { } public int getRunCount() { - synchronized (runCount) { - LOG.info("Run count : " + runCount.intValue()); - return runCount.intValue(); + synchronized (this) { + LOG.info("Run count : " + runCount); + return runCount; } } } @@ -510,7 +509,7 @@ public class TestDiskBalancerWithMockMover { } } - private class DiskBalancerBuilder { + private static class DiskBalancerBuilder { private TestMover blockMover; private Configuration conf; private String nodeID; @@ -546,7 +545,7 @@ public class TestDiskBalancerWithMockMover { } } - private class DiskBalancerClusterBuilder { + private static class DiskBalancerClusterBuilder { private String jsonFilePath; private Configuration conf; @@ -573,7 +572,7 @@ public class TestDiskBalancerWithMockMover { } } - private class PlanBuilder { + private static class PlanBuilder { private String sourcePath; private String destPath; private String sourceUUID; diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/diskbalancer/command/TestDiskBalancerCommand.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/diskbalancer/command/TestDiskBalancerCommand.java index 7d659af54c8..1950c858d8d 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/diskbalancer/command/TestDiskBalancerCommand.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/diskbalancer/command/TestDiskBalancerCommand.java @@ -23,6 +23,7 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import java.io.ByteArrayOutputStream; import java.io.PrintStream; @@ -41,18 +42,19 @@ import org.apache.hadoop.hdfs.server.diskbalancer.connectors.ClusterConnector; import org.apache.hadoop.hdfs.server.diskbalancer.connectors.ConnectorFactory; import org.apache.hadoop.hdfs.server.diskbalancer.datamodel.DiskBalancerCluster; import org.apache.hadoop.hdfs.server.diskbalancer.datamodel.DiskBalancerDataNode; +import org.apache.hadoop.hdfs.tools.DiskBalancerCLI; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.google.common.collect.Lists; -import static org.apache.hadoop.hdfs.tools.DiskBalancer.CANCEL; -import static org.apache.hadoop.hdfs.tools.DiskBalancer.HELP; -import static org.apache.hadoop.hdfs.tools.DiskBalancer.NODE; -import static org.apache.hadoop.hdfs.tools.DiskBalancer.PLAN; -import static org.apache.hadoop.hdfs.tools.DiskBalancer.QUERY; -import static org.apache.hadoop.hdfs.tools.DiskBalancer.REPORT; +import static org.apache.hadoop.hdfs.tools.DiskBalancerCLI.CANCEL; +import static org.apache.hadoop.hdfs.tools.DiskBalancerCLI.HELP; +import static org.apache.hadoop.hdfs.tools.DiskBalancerCLI.NODE; +import static org.apache.hadoop.hdfs.tools.DiskBalancerCLI.PLAN; +import static org.apache.hadoop.hdfs.tools.DiskBalancerCLI.QUERY; +import static org.apache.hadoop.hdfs.tools.DiskBalancerCLI.REPORT; import org.junit.Rule; import org.junit.rules.ExpectedException; @@ -387,8 +389,7 @@ public class TestDiskBalancerCommand { private List runCommandInternal(final String cmdLine) throws Exception { String[] cmds = StringUtils.split(cmdLine, ' '); - org.apache.hadoop.hdfs.tools.DiskBalancer db = - new org.apache.hadoop.hdfs.tools.DiskBalancer(conf); + DiskBalancerCLI db = new DiskBalancerCLI(conf); ByteArrayOutputStream bufOut = new ByteArrayOutputStream(); PrintStream out = new PrintStream(bufOut); @@ -457,4 +458,52 @@ public class TestDiskBalancerCommand { List nodeList = command.getNodes(listArg.toString()); assertEquals(nodeNum, nodeList.size()); } + + @Test(timeout = 60000) + public void testReportCommandWithMultipleNodes() throws Exception { + String dataNodeUuid1 = cluster.getDataNodes().get(0).getDatanodeUuid(); + String dataNodeUuid2 = cluster.getDataNodes().get(1).getDatanodeUuid(); + final String planArg = String.format("-%s -%s %s,%s", + REPORT, NODE, dataNodeUuid1, dataNodeUuid2); + final String cmdLine = String.format("hdfs diskbalancer %s", planArg); + List outputs = runCommand(cmdLine, cluster); + + assertThat( + outputs.get(0), + containsString("Processing report command")); + assertThat( + outputs.get(1), + is(allOf(containsString("Reporting volume information for DataNode"), + containsString(dataNodeUuid1), containsString(dataNodeUuid2)))); + // Since the order of input nodes will be disrupted when parse + // the node string, we should compare UUID with both output lines. + assertTrue(outputs.get(2).contains(dataNodeUuid1) + || outputs.get(6).contains(dataNodeUuid1)); + assertTrue(outputs.get(2).contains(dataNodeUuid2) + || outputs.get(6).contains(dataNodeUuid2)); + } + + @Test(timeout = 60000) + public void testReportCommandWithInvalidNode() throws Exception { + String dataNodeUuid1 = cluster.getDataNodes().get(0).getDatanodeUuid(); + String invalidNode = "invalidNode"; + final String planArg = String.format("-%s -%s %s,%s", + REPORT, NODE, dataNodeUuid1, invalidNode); + final String cmdLine = String.format("hdfs diskbalancer %s", planArg); + List outputs = runCommand(cmdLine, cluster); + + assertThat( + outputs.get(0), + containsString("Processing report command")); + assertThat( + outputs.get(1), + is(allOf(containsString("Reporting volume information for DataNode"), + containsString(dataNodeUuid1), containsString(invalidNode)))); + + String invalidNodeInfo = + String.format("The node(s) '%s' not found. " + + "Please make sure that '%s' exists in the cluster." + , invalidNode, invalidNode); + assertTrue(outputs.get(2).contains(invalidNodeInfo)); + } } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestAddStripedBlockInFBR.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestAddStripedBlockInFBR.java new file mode 100644 index 00000000000..37b334f4428 --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestAddStripedBlockInFBR.java @@ -0,0 +1,109 @@ +/** + * 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.namenode; + + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.hdfs.DFSTestUtil; +import org.apache.hadoop.hdfs.DistributedFileSystem; +import org.apache.hadoop.hdfs.HdfsConfiguration; +import org.apache.hadoop.hdfs.MiniDFSCluster; +import org.apache.hadoop.hdfs.protocol.DatanodeID; +import org.apache.hadoop.hdfs.server.blockmanagement.BlockInfoStriped; +import org.apache.hadoop.hdfs.server.blockmanagement.BlockManager; +import org.apache.hadoop.hdfs.server.blockmanagement.NumberReplicas; +import org.apache.hadoop.hdfs.server.datanode.DataNode; +import org.apache.hadoop.hdfs.server.datanode.DataNodeTestUtils; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; +import org.mockito.Mockito; +import org.mockito.internal.util.reflection.Whitebox; + +import java.io.IOException; + +import static org.apache.hadoop.hdfs.StripedFileTestUtil.BLOCK_STRIPED_CELL_SIZE; +import static org.apache.hadoop.hdfs.StripedFileTestUtil.NUM_DATA_BLOCKS; +import static org.apache.hadoop.hdfs.StripedFileTestUtil.NUM_PARITY_BLOCKS; + +public class TestAddStripedBlockInFBR { + private final short GROUP_SIZE = (short) (NUM_DATA_BLOCKS + NUM_PARITY_BLOCKS); + + private MiniDFSCluster cluster; + private DistributedFileSystem dfs; + + @Rule + public Timeout globalTimeout = new Timeout(300000); + + @Before + public void setup() throws IOException { + Configuration conf = new HdfsConfiguration(); + cluster = new MiniDFSCluster.Builder(conf).numDataNodes(GROUP_SIZE).build(); + cluster.waitActive(); + dfs = cluster.getFileSystem(); + } + + @After + public void tearDown() { + if (cluster != null) { + cluster.shutdown(); + cluster = null; + } + } + + @Test + public void testAddBlockInFullBlockReport() throws Exception { + BlockManager spy = Mockito.spy(cluster.getNamesystem().getBlockManager()); + // let NN ignore one DataNode's IBR + final DataNode dn = cluster.getDataNodes().get(0); + final DatanodeID datanodeID = dn.getDatanodeId(); + Mockito.doNothing().when(spy) + .processIncrementalBlockReport(Mockito.eq(datanodeID), Mockito.any()); + Whitebox.setInternalState(cluster.getNamesystem(), "blockManager", spy); + + final Path ecDir = new Path("/ec"); + final Path repDir = new Path("/rep"); + dfs.mkdirs(ecDir); + dfs.mkdirs(repDir); + dfs.getClient().setErasureCodingPolicy(ecDir.toString(), null); + + // create several non-EC files and one EC file + final Path[] repFiles = new Path[GROUP_SIZE]; + for (int i = 0; i < GROUP_SIZE; i++) { + repFiles[i] = new Path(repDir, "f" + i); + DFSTestUtil.createFile(dfs, repFiles[i], 1L, (short) 3, 0L); + } + final Path ecFile = new Path(ecDir, "f"); + DFSTestUtil.createFile(dfs, ecFile, + BLOCK_STRIPED_CELL_SIZE * NUM_DATA_BLOCKS, (short) 1, 0L); + + // trigger dn's FBR. The FBR will add block-dn mapping. + DataNodeTestUtils.triggerBlockReport(dn); + + // make sure NN has correct block-dn mapping + BlockInfoStriped blockInfo = (BlockInfoStriped) cluster.getNamesystem() + .getFSDirectory().getINode(ecFile.toString()).asFile().getLastBlock(); + NumberReplicas nr = spy.countNodes(blockInfo); + Assert.assertEquals(GROUP_SIZE, nr.liveReplicas()); + Assert.assertEquals(0, nr.excessReplicas()); + } +} diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/TestOfflineImageViewer.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/TestOfflineImageViewer.java index a7c30ecf353..740a8ab2743 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/TestOfflineImageViewer.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/TestOfflineImageViewer.java @@ -237,7 +237,7 @@ public class TestOfflineImageViewer { File truncatedFile = new File(tempDir, "truncatedFsImage"); PrintStream output = new PrintStream(NullOutputStream.NULL_OUTPUT_STREAM); copyPartOfFile(originalFsimage, truncatedFile); - new FileDistributionCalculator(new Configuration(), 0, 0, output) + new FileDistributionCalculator(new Configuration(), 0, 0, false, output) .visit(new RandomAccessFile(truncatedFile, "r")); } @@ -259,7 +259,7 @@ public class TestOfflineImageViewer { public void testFileDistributionCalculator() throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); PrintStream o = new PrintStream(output); - new FileDistributionCalculator(new Configuration(), 0, 0, o) + new FileDistributionCalculator(new Configuration(), 0, 0, false, o) .visit(new RandomAccessFile(originalFsimage, "r")); o.close(); @@ -620,4 +620,24 @@ public class TestOfflineImageViewer { IOUtils.closeStream(out); } } + + @Test + public void testOfflineImageViewerWithFormatOption() throws Exception { + final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + final PrintStream out = new PrintStream(bytes); + final PrintStream oldOut = System.out; + try { + System.setOut(out); + int status = + OfflineImageViewerPB.run(new String[] {"-i", + originalFsimage.getAbsolutePath(), "-o", "-", "-p", + "FileDistribution", "-maxSize", "512", "-step", "8", + "-format"}); + assertEquals(0, status); + Assert.assertTrue(bytes.toString().contains("(0 B, 8 B]")); + } finally { + System.setOut(oldOut); + IOUtils.closeStream(out); + } + } } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/util/TestStripedBlockUtil.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/util/TestStripedBlockUtil.java index 96fc79c46cc..7d9d7dc540b 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/util/TestStripedBlockUtil.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/util/TestStripedBlockUtil.java @@ -36,6 +36,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.Timeout; +import java.nio.ByteBuffer; import java.util.Random; import static org.junit.Assert.assertEquals; @@ -242,7 +243,8 @@ public class TestStripedBlockUtil { */ @Test public void testDivideByteRangeIntoStripes() { - byte[] assembled = new byte[BLK_GROUP_STRIPE_NUM * FULL_STRIPE_SIZE]; + ByteBuffer assembled = + ByteBuffer.allocate(BLK_GROUP_STRIPE_NUM * FULL_STRIPE_SIZE); for (int bgSize : blockGroupSizes) { LocatedStripedBlock blockGroup = createDummyLocatedBlock(bgSize); byte[][] internalBlkBufs = createInternalBlkBuffers(bgSize); @@ -252,7 +254,7 @@ public class TestStripedBlockUtil { continue; } AlignedStripe[] stripes = divideByteRangeIntoStripes(EC_POLICY, - CELLSIZE, blockGroup, brStart, brStart + brSize - 1, assembled, 0); + CELLSIZE, blockGroup, brStart, brStart + brSize - 1, assembled); for (AlignedStripe stripe : stripes) { for (int i = 0; i < DATA_BLK_NUM; i++) { @@ -261,21 +263,21 @@ public class TestStripedBlockUtil { continue; } int done = 0; - for (int j = 0; j < chunk.byteArray.getLengths().length; j++) { - System.arraycopy(internalBlkBufs[i], - (int) stripe.getOffsetInBlock() + done, assembled, - chunk.byteArray.getOffsets()[j], - chunk.byteArray.getLengths()[j]); - done += chunk.byteArray.getLengths()[j]; + int len; + for (ByteBuffer slice : chunk.getChunkBuffer().getSlices()) { + len = slice.remaining(); + slice.put(internalBlkBufs[i], + (int) stripe.getOffsetInBlock() + done, len); + done += len; } } } for (int i = 0; i < brSize; i++) { - if (hashIntToByte(brStart + i) != assembled[i]) { + if (hashIntToByte(brStart + i) != assembled.get(i)) { System.out.println("Oops"); } assertEquals("Byte at " + (brStart + i) + " should be the same", - hashIntToByte(brStart + i), assembled[i]); + hashIntToByte(brStart + i), assembled.get(i)); } } } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/testAclCLI.xml b/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/testAclCLI.xml index 82a580926a1..7d9ecf863b4 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/testAclCLI.xml +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/testAclCLI.xml @@ -679,63 +679,63 @@ - SubstringComparator + ExactLineComparator # file: /dir1/dir2 - SubstringComparator + ExactLineComparator # owner: USERNAME - SubstringComparator + ExactLineComparator # group: supergroup - SubstringComparator + ExactLineComparator user::rwx - SubstringComparator + ExactLineComparator user:charlie:r-x - SubstringComparator + ExactLineComparator group::r-x - SubstringComparator - group:admin:rwx + RegexpComparator + ^group:admin:rwx\b.* - SubstringComparator - mask::rwx + ExactLineComparator + mask::r-x - SubstringComparator + ExactLineComparator default:user::rwx - SubstringComparator + ExactLineComparator default:user:charlie:r-x - SubstringComparator + ExactLineComparator default:group::r-x - SubstringComparator + ExactLineComparator default:group:admin:rwx - SubstringComparator + ExactLineComparator default:mask::rwx - SubstringComparator + ExactLineComparator default:other::r-x - SubstringComparator + ExactLineComparator other::r-x diff --git a/hadoop-mapreduce-project/bin/mapred b/hadoop-mapreduce-project/bin/mapred index 046d48c4cfd..fe7c56ae08e 100755 --- a/hadoop-mapreduce-project/bin/mapred +++ b/hadoop-mapreduce-project/bin/mapred @@ -69,8 +69,6 @@ function mapredcmd_case historyserver) HADOOP_SUBCMD_SUPPORTDAEMONIZATION="true" HADOOP_CLASSNAME=org.apache.hadoop.mapreduce.v2.hs.JobHistoryServer - hadoop_debug "Appending HADOOP_JOB_HISTORYSERVER_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_JOB_HISTORYSERVER_OPTS}" if [ -n "${HADOOP_JOB_HISTORYSERVER_HEAPSIZE}" ]; then # shellcheck disable=SC2034 HADOOP_HEAPSIZE_MAX="${HADOOP_JOB_HISTORYSERVER_HEAPSIZE}" @@ -79,31 +77,21 @@ function mapredcmd_case ;; hsadmin) HADOOP_CLASSNAME=org.apache.hadoop.mapreduce.v2.hs.client.HSAdmin - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" ;; job) HADOOP_CLASSNAME=org.apache.hadoop.mapred.JobClient - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" ;; pipes) HADOOP_CLASSNAME=org.apache.hadoop.mapred.pipes.Submitter - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" ;; queue) HADOOP_CLASSNAME=org.apache.hadoop.mapred.JobQueueClient ;; sampler) HADOOP_CLASSNAME=org.apache.hadoop.mapred.lib.InputSampler - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" ;; version) HADOOP_CLASSNAME=org.apache.hadoop.util.VersionInfo - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" ;; *) HADOOP_CLASSNAME="${subcmd}" @@ -141,6 +129,8 @@ fi HADOOP_SUBCMD=$1 shift +hadoop_verify_user "${HADOOP_SHELL_EXECNAME}" "${HADOOP_SUBCMD}" + HADOOP_SUBCMD_ARGS=("$@") if declare -f mapred_subcommand_"${HADOOP_SUBCMD}" >/dev/null 2>&1; then @@ -150,15 +140,20 @@ else mapredcmd_case "${HADOOP_SUBCMD}" "${HADOOP_SUBCMD_ARGS[@]}" fi -hadoop_verify_user "${HADOOP_SUBCMD}" +hadoop_add_client_opts -if [[ ${HADOOP_SLAVE_MODE} = true ]]; then - hadoop_common_slave_mode_execute "${HADOOP_MAPRED_HOME}/bin/mapred" "${HADOOP_USER_PARAMS[@]}" +if [[ ${HADOOP_WORKER_MODE} = true ]]; then + hadoop_common_worker_mode_execute "${HADOOP_MAPRED_HOME}/bin/mapred" "${HADOOP_USER_PARAMS[@]}" exit $? fi +hadoop_subcommand_opts "${HADOOP_SHELL_EXECNAME}" "${HADOOP_SUBCMD}" + if [[ "${HADOOP_SUBCMD_SECURESERVICE}" = true ]]; then HADOOP_SECURE_USER="${HADOOP_SUBCMD_SECUREUSER}" + + hadoop_subcommand_secure_opts "${HADOOP_SHELL_EXECNAME}" "${HADOOP_SUBCMD}" + hadoop_verify_secure_prereq hadoop_setup_secure_service priv_outfile="${HADOOP_LOG_DIR}/privileged-${HADOOP_IDENT_STRING}-${HADOOP_SUBCMD}-${HOSTNAME}.out" diff --git a/hadoop-mapreduce-project/bin/mapred-config.sh b/hadoop-mapreduce-project/bin/mapred-config.sh old mode 100644 new mode 100755 index a9897929c59..68d3463de69 --- a/hadoop-mapreduce-project/bin/mapred-config.sh +++ b/hadoop-mapreduce-project/bin/mapred-config.sh @@ -26,7 +26,7 @@ function hadoop_subproject_init export HADOOP_MAPRED_ENV_PROCESSED=true fi fi - + # at some point in time, someone thought it would be a good idea to # create separate vars for every subproject. *sigh* # let's perform some overrides and setup some defaults for bw compat @@ -38,15 +38,17 @@ function hadoop_subproject_init hadoop_deprecate_envvar HADOOP_MAPRED_LOG_DIR HADOOP_LOG_DIR hadoop_deprecate_envvar HADOOP_MAPRED_LOGFILE HADOOP_LOGFILE - + hadoop_deprecate_envvar HADOOP_MAPRED_NICENESS HADOOP_NICENESS - + hadoop_deprecate_envvar HADOOP_MAPRED_STOP_TIMEOUT HADOOP_STOP_TIMEOUT - + hadoop_deprecate_envvar HADOOP_MAPRED_PID_DIR HADOOP_PID_DIR hadoop_deprecate_envvar HADOOP_MAPRED_ROOT_LOGGER HADOOP_ROOT_LOGGER + hadoop_deprecate_envvar HADOOP_JOB_HISTORY_OPTS MAPRED_HISTORYSERVER_OPTS + HADOOP_MAPRED_HOME="${HADOOP_MAPRED_HOME:-$HADOOP_HOME}" hadoop_deprecate_envvar HADOOP_MAPRED_IDENT_STRING HADOOP_IDENT_STRING diff --git a/hadoop-mapreduce-project/conf/mapred-env.sh b/hadoop-mapreduce-project/conf/mapred-env.sh index bbe4a4980cd..53bc2964c3b 100644 --- a/hadoop-mapreduce-project/conf/mapred-env.sh +++ b/hadoop-mapreduce-project/conf/mapred-env.sh @@ -31,14 +31,14 @@ # Specify the max heapsize for the JobHistoryServer. If no units are # given, it will be assumed to be in MB. # This value will be overridden by an Xmx setting specified in HADOOP_OPTS, -# and/or HADOOP_JOB_HISTORYSERVER_OPTS. +# and/or MAPRED_HISTORYSERVER_OPTS. # Default is the same as HADOOP_HEAPSIZE_MAX. #export HADOOP_JOB_HISTORYSERVER_HEAPSIZE= # Specify the JVM options to be used when starting the HistoryServer. # These options will be appended to the options specified as HADOOP_OPTS # and therefore may override any similar flags set in HADOOP_OPTS -#export HADOOP_JOB_HISTORYSERVER_OPTS= +#export MAPRED_HISTORYSERVER_OPTS= # Specify the log4j settings for the JobHistoryServer # Java property: hadoop.root.logger diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/MapTask.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/MapTask.java index a5232911de9..45431e65ed8 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/MapTask.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/MapTask.java @@ -1588,6 +1588,7 @@ public class MapTask extends Task { final long size = distanceTo(bufstart, bufend, bufvoid) + partitions * APPROX_HEADER_LENGTH; FSDataOutputStream out = null; + FSDataOutputStream partitionOut = null; try { // create spill file final SpillRecord spillRec = new SpillRecord(partitions); @@ -1608,7 +1609,7 @@ public class MapTask extends Task { IFile.Writer writer = null; try { long segmentStart = out.getPos(); - FSDataOutputStream partitionOut = CryptoUtils.wrapIfNecessary(job, out); + partitionOut = CryptoUtils.wrapIfNecessary(job, out, false); writer = new Writer(job, partitionOut, keyClass, valClass, codec, spilledRecordsCounter); if (combinerRunner == null) { @@ -1643,6 +1644,10 @@ public class MapTask extends Task { // close the writer writer.close(); + if (partitionOut != out) { + partitionOut.close(); + partitionOut = null; + } // record offsets rec.startOffset = segmentStart; @@ -1671,6 +1676,9 @@ public class MapTask extends Task { ++numSpills; } finally { if (out != null) out.close(); + if (partitionOut != null) { + partitionOut.close(); + } } } @@ -1683,6 +1691,7 @@ public class MapTask extends Task { int partition) throws IOException { long size = kvbuffer.length + partitions * APPROX_HEADER_LENGTH; FSDataOutputStream out = null; + FSDataOutputStream partitionOut = null; try { // create spill file final SpillRecord spillRec = new SpillRecord(partitions); @@ -1697,7 +1706,7 @@ public class MapTask extends Task { try { long segmentStart = out.getPos(); // Create a new codec, don't care! - FSDataOutputStream partitionOut = CryptoUtils.wrapIfNecessary(job, out); + partitionOut = CryptoUtils.wrapIfNecessary(job, out, false); writer = new IFile.Writer(job, partitionOut, keyClass, valClass, codec, spilledRecordsCounter); @@ -1709,6 +1718,10 @@ public class MapTask extends Task { mapOutputByteCounter.increment(out.getPos() - recordStart); } writer.close(); + if (partitionOut != out) { + partitionOut.close(); + partitionOut = null; + } // record offsets rec.startOffset = segmentStart; @@ -1736,6 +1749,9 @@ public class MapTask extends Task { ++numSpills; } finally { if (out != null) out.close(); + if (partitionOut != null) { + partitionOut.close(); + } } } @@ -1847,6 +1863,7 @@ public class MapTask extends Task { //The output stream for the final single output file FSDataOutputStream finalOut = rfs.create(finalOutputFile, true, 4096); + FSDataOutputStream finalPartitionOut = null; if (numSpills == 0) { //create dummy files @@ -1855,10 +1872,15 @@ public class MapTask extends Task { try { for (int i = 0; i < partitions; i++) { long segmentStart = finalOut.getPos(); - FSDataOutputStream finalPartitionOut = CryptoUtils.wrapIfNecessary(job, finalOut); + finalPartitionOut = CryptoUtils.wrapIfNecessary(job, finalOut, + false); Writer writer = new Writer(job, finalPartitionOut, keyClass, valClass, codec, null); writer.close(); + if (finalPartitionOut != finalOut) { + finalPartitionOut.close(); + finalPartitionOut = null; + } rec.startOffset = segmentStart; rec.rawLength = writer.getRawLength() + CryptoUtils.cryptoPadding(job); rec.partLength = writer.getCompressedLength() + CryptoUtils.cryptoPadding(job); @@ -1867,6 +1889,9 @@ public class MapTask extends Task { sr.writeToFile(finalIndexFile, job); } finally { finalOut.close(); + if (finalPartitionOut != null) { + finalPartitionOut.close(); + } } sortPhase.complete(); return; @@ -1910,7 +1935,7 @@ public class MapTask extends Task { //write merged output to disk long segmentStart = finalOut.getPos(); - FSDataOutputStream finalPartitionOut = CryptoUtils.wrapIfNecessary(job, finalOut); + finalPartitionOut = CryptoUtils.wrapIfNecessary(job, finalOut, false); Writer writer = new Writer(job, finalPartitionOut, keyClass, valClass, codec, spilledRecordsCounter); @@ -1923,6 +1948,10 @@ public class MapTask extends Task { //close writer.close(); + if (finalPartitionOut != finalOut) { + finalPartitionOut.close(); + finalPartitionOut = null; + } sortPhase.startNextPhase(); @@ -1934,6 +1963,9 @@ public class MapTask extends Task { } spillRec.writeToFile(finalIndexFile, job); finalOut.close(); + if (finalPartitionOut != null) { + finalPartitionOut.close(); + } for(int i = 0; i < numSpills; i++) { rfs.delete(filename[i],true); } diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/CryptoUtils.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/CryptoUtils.java index c4130b1b07f..c05b6b08156 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/CryptoUtils.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/CryptoUtils.java @@ -57,9 +57,9 @@ public class CryptoUtils { /** * This method creates and initializes an IV (Initialization Vector) * - * @param conf - * @return byte[] - * @throws IOException + * @param conf configuration + * @return byte[] initialization vector + * @throws IOException exception in case of error */ public static byte[] createIV(Configuration conf) throws IOException { CryptoCodec cryptoCodec = CryptoCodec.getInstance(conf); @@ -94,13 +94,33 @@ public class CryptoUtils { * "mapreduce.job.encrypted-intermediate-data.buffer.kb" Job configuration * variable. * - * @param conf - * @param out - * @return FSDataOutputStream - * @throws IOException + * @param conf configuration + * @param out given output stream + * @return FSDataOutputStream encrypted output stream if encryption is + * enabled; otherwise the given output stream itself + * @throws IOException exception in case of error */ public static FSDataOutputStream wrapIfNecessary(Configuration conf, FSDataOutputStream out) throws IOException { + return wrapIfNecessary(conf, out, true); + } + + /** + * Wraps a given FSDataOutputStream with a CryptoOutputStream. The size of the + * data buffer required for the stream is specified by the + * "mapreduce.job.encrypted-intermediate-data.buffer.kb" Job configuration + * variable. + * + * @param conf configuration + * @param out given output stream + * @param closeOutputStream flag to indicate whether closing the wrapped + * stream will close the given output stream + * @return FSDataOutputStream encrypted output stream if encryption is + * enabled; otherwise the given output stream itself + * @throws IOException exception in case of error + */ + public static FSDataOutputStream wrapIfNecessary(Configuration conf, + FSDataOutputStream out, boolean closeOutputStream) throws IOException { if (isEncryptedSpillEnabled(conf)) { out.write(ByteBuffer.allocate(8).putLong(out.getPos()).array()); byte[] iv = createIV(conf); @@ -110,7 +130,7 @@ public class CryptoUtils { + Base64.encodeBase64URLSafeString(iv) + "]"); } return new CryptoFSDataOutputStream(out, CryptoCodec.getInstance(conf), - getBufferSize(conf), getEncryptionKey(), iv); + getBufferSize(conf), getEncryptionKey(), iv, closeOutputStream); } else { return out; } @@ -128,11 +148,12 @@ public class CryptoUtils { * LimitInputStream will ensure that the CryptoStream does not read past the * provided length from the given Input Stream. * - * @param conf - * @param in - * @param length - * @return InputStream - * @throws IOException + * @param conf configuration + * @param in given input stream + * @param length maximum number of bytes to read from the input stream + * @return InputStream encrypted input stream if encryption is + * enabled; otherwise the given input stream itself + * @throws IOException exception in case of error */ public static InputStream wrapIfNecessary(Configuration conf, InputStream in, long length) throws IOException { @@ -166,10 +187,11 @@ public class CryptoUtils { * "mapreduce.job.encrypted-intermediate-data.buffer.kb" Job configuration * variable. * - * @param conf - * @param in - * @return FSDataInputStream - * @throws IOException + * @param conf configuration + * @param in given input stream + * @return FSDataInputStream encrypted input stream if encryption is + * enabled; otherwise the given input stream itself + * @throws IOException exception in case of error */ public static FSDataInputStream wrapIfNecessary(Configuration conf, FSDataInputStream in) throws IOException { diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/server/HSAdminServer.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/server/HSAdminServer.java index 729af0a951d..3fef5e278b0 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/server/HSAdminServer.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/server/HSAdminServer.java @@ -29,6 +29,7 @@ import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.CommonConfigurationKeysPublic; import org.apache.hadoop.ipc.ProtobufRpcEngine; import org.apache.hadoop.ipc.RPC; +import org.apache.hadoop.ipc.WritableRpcEngine; import org.apache.hadoop.mapreduce.v2.jobhistory.JHAdminConfig; import org.apache.hadoop.security.AccessControlException; import org.apache.hadoop.security.Groups; @@ -97,6 +98,8 @@ public class HSAdminServer extends AbstractService implements HSAdminProtocol { BlockingService refreshHSAdminProtocolService = HSAdminRefreshProtocolService .newReflectiveBlockingService(refreshHSAdminProtocolXlator); + WritableRpcEngine.ensureInitialized(); + clientRpcAddress = conf.getSocketAddr( JHAdminConfig.MR_HISTORY_BIND_HOST, JHAdminConfig.JHS_ADMIN_ADDRESS, diff --git a/hadoop-project/pom.xml b/hadoop-project/pom.xml index 29c2760d7d1..49ea40f80d0 100644 --- a/hadoop-project/pom.xml +++ b/hadoop-project/pom.xml @@ -88,6 +88,7 @@ 6.0.44 4.0 + 2.9.4 1.8 @@ -1059,6 +1060,12 @@ test + + joda-time + joda-time + ${joda-time.version} + + com.nimbusds nimbus-jose-jwt diff --git a/hadoop-tools/hadoop-archive-logs/src/main/shellprofile.d/hadoop-archive-logs.sh b/hadoop-tools/hadoop-archive-logs/src/main/shellprofile.d/hadoop-archive-logs.sh index ae7b6c67d1e..c889816a124 100755 --- a/hadoop-tools/hadoop-archive-logs/src/main/shellprofile.d/hadoop-archive-logs.sh +++ b/hadoop-tools/hadoop-archive-logs/src/main/shellprofile.d/hadoop-archive-logs.sh @@ -32,8 +32,6 @@ function mapred_subcommand_archive-logs # shellcheck disable=SC2034 HADOOP_CLASSNAME=org.apache.hadoop.tools.HadoopArchiveLogs hadoop_add_to_classpath_tools hadoop-archive-logs - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" } fi diff --git a/hadoop-tools/hadoop-aws/pom.xml b/hadoop-tools/hadoop-aws/pom.xml index 13dcdf16fe8..49b0379b96e 100644 --- a/hadoop-tools/hadoop-aws/pom.xml +++ b/hadoop-tools/hadoop-aws/pom.xml @@ -285,6 +285,10 @@ aws-java-sdk-s3 compile + + joda-time + joda-time + com.amazonaws aws-java-sdk-sts diff --git a/hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/index.md b/hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/index.md index 51000301950..7fcadb94c5b 100644 --- a/hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/index.md +++ b/hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/index.md @@ -1023,7 +1023,7 @@ the classpath. This means that one or more of the `aws-*-sdk` JARs are missing. Add them. -### Missing method in AWS class +### Missing method in `com.amazonaws` class This can be triggered by incompatibilities between the AWS SDK on the classpath and the version which Hadoop was compiled with. @@ -1047,23 +1047,84 @@ classpath. All Jackson JARs on the classpath *must* be of the same version. ### Authentication failure -The general cause is: you have the wrong credentials —or somehow +If Hadoop cannot authenticate with the S3 service endpoint, +the client retries a number of times before eventually failing. +When it finally gives up, it will report a message about signature mismatch: + +``` +com.amazonaws.services.s3.model.AmazonS3Exception: + The request signature we calculated does not match the signature you provided. + Check your key and signing method. + (Service: Amazon S3; Status Code: 403; Error Code: SignatureDoesNotMatch, +``` + +The likely cause is that you either have the wrong credentials or somehow the credentials were not readable on the host attempting to read or write the S3 Bucket. -There's not much that Hadoop can do for diagnostics here. Enabling debug logging for the package `org.apache.hadoop.fs.s3a` -can help somewhat. +can help provide more information. -Most common: there's an error in the key or secret. +The most common cause is that you have the wrong credentials for any of the current +authentication mechanism(s) —or somehow +the credentials were not readable on the host attempting to read or write +the S3 Bucket. However, there are a couple of system configuration problems +(JVM version, system clock) which also need to be checked. -Otherwise, try to use the AWS command line tools with the same credentials. -If you set the environment variables, you can take advantage of S3A's support -of environment-variable authentication by attempting to use the `hdfs fs` command -to read or write data on S3. That is: comment out the `fs.s3a` secrets and rely on -the environment variables. +Most common: there's an error in the configuration properties. -### Authentication failure when using URLs with embedded secrets + +1. Make sure that the name of the bucket is the correct one. +That is: check the URL. + +1. Make sure the property names are correct. For S3A, they are +`fs.s3a.access.key` and `fs.s3a.secret.key` —you cannot just copy the S3N +properties and replace `s3n` with `s3a`. + +1. Make sure the properties are visible to the process attempting to +talk to the object store. Placing them in `core-site.xml` is the standard +mechanism. + +1. If using session authentication, the session may have expired. +Generate a new session token and secret. + +1. If using environement variable-based authentication, make sure that the +relevant variables are set in the environment in which the process is running. + +The standard first step is: try to use the AWS command line tools with the same +credentials, through a command such as: + + hdfs fs -ls s3a://my-bucket/ + +Note the trailing "/" here; without that the shell thinks you are trying to list +your home directory under the bucket, which will only exist if explicitly created. + + +Attempting to list a bucket using inline credentials is a +means of verifying that the key and secret can access a bucket; + + hdfs fs -ls s3a://key:secret@my-bucket/ + +Do escape any `+` or `/` symbols in the secret, as discussed below, and never +share the URL, logs generated using it, or use such an inline authentication +mechanism in production. + +Finally, if you set the environment variables, you can take advantage of S3A's +support of environment-variable authentication by attempting the same ls operation. +That is: unset the `fs.s3a` secrets and rely on the environment variables. + +#### Authentication failure due to clock skew + +The timestamp is used in signing to S3, so as to +defend against replay attacks. If the system clock is too far behind *or ahead* +of Amazon's, requests will be rejected. + +This can surface as the situation where +read requests are allowed, but operations which write to the bucket are denied. + +Check the system clock. + +#### Authentication failure when using URLs with embedded secrets If using the (strongly discouraged) mechanism of including the AWS Key and secret in a URL, then both "+" and "/" symbols need @@ -1076,23 +1137,25 @@ encoding problems are not uncommon. | `/` | `%2F` | -That is, a URL for `bucket` with AWS ID `user1` and secret `a+b/c` would +As an example, a URL for `bucket` with AWS ID `user1` and secret `a+b/c` would be represented as ``` -s3a://user1:a%2Bb%2Fc@bucket +s3a://user1:a%2Bb%2Fc@bucket/ ``` This technique is only needed when placing secrets in the URL. Again, this is something users are strongly advised against using. -### Authentication failures running on Java 8u60+ +#### Authentication Failures When Running on Java 8u60+ A change in the Java 8 JVM broke some of the `toString()` string generation of Joda Time 2.8.0, which stopped the Amazon S3 client from being able to generate authentication headers suitable for validation by S3. -Fix: make sure that the version of Joda Time is 2.8.1 or later. +**Fix**: Make sure that the version of Joda Time is 2.8.1 or later, or +use a new version of Java 8. + ### "Bad Request" exception when working with AWS S3 Frankfurt, Seoul, or other "V4" endpoint @@ -1291,10 +1354,12 @@ expense of sequential read performance and bandwidth. The slow performance of `rename()` surfaces during the commit phase of work, including -* The MapReduce FileOutputCommitter. -* DistCp's rename after copy operation. +* The MapReduce `FileOutputCommitter`. +* DistCp's rename-after-copy operation. +* The `hdfs fs -rm` command renaming the file under `.Trash` rather than +deleting it. Use `-skipTrash` to eliminate that step. -Both these operations can be significantly slower when S3 is the destination +These operations can be significantly slower when S3 is the destination compared to HDFS or other "real" filesystem. *Improving S3 load-balancing behavior* diff --git a/hadoop-tools/hadoop-distcp/src/main/java/org/apache/hadoop/tools/DistCp.java b/hadoop-tools/hadoop-distcp/src/main/java/org/apache/hadoop/tools/DistCp.java index 36e5ee4cd60..be58f131647 100644 --- a/hadoop-tools/hadoop-distcp/src/main/java/org/apache/hadoop/tools/DistCp.java +++ b/hadoop-tools/hadoop-distcp/src/main/java/org/apache/hadoop/tools/DistCp.java @@ -71,6 +71,7 @@ public class DistCp extends Configured implements Tool { private static final String PREFIX = "_distcp"; private static final String WIP_PREFIX = "._WIP_"; private static final String DISTCP_DEFAULT_XML = "distcp-default.xml"; + private static final String DISTCP_SITE_XML = "distcp-site.xml"; static final Random rand = new Random(); private boolean submitted; @@ -86,6 +87,7 @@ public class DistCp extends Configured implements Tool { public DistCp(Configuration configuration, DistCpOptions inputOptions) throws Exception { Configuration config = new Configuration(configuration); config.addResource(DISTCP_DEFAULT_XML); + config.addResource(DISTCP_SITE_XML); setConf(config); this.inputOptions = inputOptions; this.metaFolder = createMetaFolderPath(); @@ -393,10 +395,12 @@ public class DistCp extends Configured implements Tool { * Loads properties from distcp-default.xml into configuration * object * @return Configuration which includes properties from distcp-default.xml + * and distcp-site.xml */ private static Configuration getDefaultConf() { Configuration config = new Configuration(); config.addResource(DISTCP_DEFAULT_XML); + config.addResource(DISTCP_SITE_XML); return config; } diff --git a/hadoop-tools/hadoop-distcp/src/main/java/org/apache/hadoop/tools/DistCpOptions.java b/hadoop-tools/hadoop-distcp/src/main/java/org/apache/hadoop/tools/DistCpOptions.java index e6f53f5b60a..4c5518f3adb 100644 --- a/hadoop-tools/hadoop-distcp/src/main/java/org/apache/hadoop/tools/DistCpOptions.java +++ b/hadoop-tools/hadoop-distcp/src/main/java/org/apache/hadoop/tools/DistCpOptions.java @@ -47,7 +47,7 @@ public class DistCpOptions { public static final int maxNumListstatusThreads = 40; private int numListstatusThreads = 0; // Indicates that flag is not set. private int maxMaps = DistCpConstants.DEFAULT_MAPS; - private float mapBandwidth = DistCpConstants.DEFAULT_BANDWIDTH_MB; + private float mapBandwidth = 0; // Indicates that we should use the default. private String copyStrategy = DistCpConstants.UNIFORMSIZE; @@ -609,8 +609,10 @@ public class DistCpOptions { String.valueOf(useDiff)); DistCpOptionSwitch.addToConf(conf, DistCpOptionSwitch.SKIP_CRC, String.valueOf(skipCRC)); - DistCpOptionSwitch.addToConf(conf, DistCpOptionSwitch.BANDWIDTH, - String.valueOf(mapBandwidth)); + if (mapBandwidth > 0) { + DistCpOptionSwitch.addToConf(conf, DistCpOptionSwitch.BANDWIDTH, + String.valueOf(mapBandwidth)); + } DistCpOptionSwitch.addToConf(conf, DistCpOptionSwitch.PRESERVE_STATUS, DistCpUtils.packAttributes(preserveStatus)); if (filtersFile != null) { diff --git a/hadoop-tools/hadoop-distcp/src/main/shellprofile.d/hadoop-distcp.sh b/hadoop-tools/hadoop-distcp/src/main/shellprofile.d/hadoop-distcp.sh index 0178c54e911..6e93ec17872 100755 --- a/hadoop-tools/hadoop-distcp/src/main/shellprofile.d/hadoop-distcp.sh +++ b/hadoop-tools/hadoop-distcp/src/main/shellprofile.d/hadoop-distcp.sh @@ -32,8 +32,6 @@ function hadoop_subcommand_distcp # shellcheck disable=SC2034 HADOOP_CLASSNAME=org.apache.hadoop.tools.DistCp hadoop_add_to_classpath_tools hadoop-distcp - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" } fi @@ -55,8 +53,6 @@ function mapred_subcommand_distcp # shellcheck disable=SC2034 HADOOP_CLASSNAME=org.apache.hadoop.tools.DistCp hadoop_add_to_classpath_tools hadoop-distcp - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" } fi diff --git a/hadoop-tools/hadoop-distcp/src/test/java/org/apache/hadoop/tools/TestOptionsParser.java b/hadoop-tools/hadoop-distcp/src/test/java/org/apache/hadoop/tools/TestOptionsParser.java index c46bcd9cf5e..218de4edfd7 100644 --- a/hadoop-tools/hadoop-distcp/src/test/java/org/apache/hadoop/tools/TestOptionsParser.java +++ b/hadoop-tools/hadoop-distcp/src/test/java/org/apache/hadoop/tools/TestOptionsParser.java @@ -107,7 +107,7 @@ public class TestOptionsParser { DistCpOptions options = OptionsParser.parse(new String[] { "hdfs://localhost:9820/source/first", "hdfs://localhost:9820/target/"}); - Assert.assertEquals(options.getMapBandwidth(), DistCpConstants.DEFAULT_BANDWIDTH_MB, DELTA); + Assert.assertEquals(options.getMapBandwidth(), 0, DELTA); options = OptionsParser.parse(new String[] { "-bandwidth", @@ -389,7 +389,7 @@ public class TestOptionsParser { + "deleteMissing=false, ignoreFailures=false, overwrite=false, " + "append=false, useDiff=false, fromSnapshot=null, toSnapshot=null, " + "skipCRC=false, blocking=true, numListstatusThreads=0, maxMaps=20, " - + "mapBandwidth=100.0, " + + "mapBandwidth=0.0, " + "copyStrategy='uniformsize', preserveStatus=[], " + "preserveRawXattrs=false, atomicWorkPath=null, logPath=null, " + "sourceFileListing=abc, sourcePaths=null, targetPath=xyz, " @@ -572,6 +572,8 @@ public class TestOptionsParser { Configuration conf = new Configuration(); Assert.assertFalse(conf.getBoolean(DistCpOptionSwitch.IGNORE_FAILURES.getConfigLabel(), false)); Assert.assertFalse(conf.getBoolean(DistCpOptionSwitch.ATOMIC_COMMIT.getConfigLabel(), false)); + Assert.assertEquals( + conf.getRaw(DistCpOptionSwitch.BANDWIDTH.getConfigLabel()), null); DistCpOptions options = OptionsParser.parse(new String[] { "-atomic", "-i", @@ -581,7 +583,7 @@ public class TestOptionsParser { Assert.assertTrue(conf.getBoolean(DistCpOptionSwitch.IGNORE_FAILURES.getConfigLabel(), false)); Assert.assertTrue(conf.getBoolean(DistCpOptionSwitch.ATOMIC_COMMIT.getConfigLabel(), false)); Assert.assertEquals(conf.getFloat(DistCpOptionSwitch.BANDWIDTH.getConfigLabel(), -1), - DistCpConstants.DEFAULT_BANDWIDTH_MB, DELTA); + -1.0, DELTA); conf = new Configuration(); Assert.assertFalse(conf.getBoolean(DistCpOptionSwitch.SYNC_FOLDERS.getConfigLabel(), false)); @@ -602,6 +604,62 @@ public class TestOptionsParser { Assert.assertEquals(conf.getFloat(DistCpOptionSwitch.BANDWIDTH.getConfigLabel(), -1), 11.2, DELTA); } + @Test + public void testOptionsAppendToConfDoesntOverwriteBandwidth() { + Configuration conf = new Configuration(); + Assert.assertEquals( + conf.getRaw(DistCpOptionSwitch.BANDWIDTH.getConfigLabel()), null); + DistCpOptions options = OptionsParser.parse(new String[] { + "hdfs://localhost:8020/source/first", + "hdfs://localhost:8020/target/"}); + options.appendToConf(conf); + Assert.assertEquals( + conf.getFloat(DistCpOptionSwitch.BANDWIDTH.getConfigLabel(), -1), -1.0, + DELTA); + + conf = new Configuration(); + Assert.assertEquals( + conf.getRaw(DistCpOptionSwitch.BANDWIDTH.getConfigLabel()), null); + options = OptionsParser.parse(new String[] { + "-update", + "-delete", + "-pu", + "-bandwidth", + "77", + "hdfs://localhost:8020/source/first", + "hdfs://localhost:8020/target/"}); + options.appendToConf(conf); + Assert.assertEquals( + conf.getFloat(DistCpOptionSwitch.BANDWIDTH.getConfigLabel(), -1), 77.0, + DELTA); + + conf = new Configuration(); + conf.set(DistCpOptionSwitch.BANDWIDTH.getConfigLabel(), "88"); + Assert.assertEquals( + conf.getRaw(DistCpOptionSwitch.BANDWIDTH.getConfigLabel()), "88"); + options = OptionsParser.parse(new String[] { + "hdfs://localhost:8020/source/first", + "hdfs://localhost:8020/target/"}); + options.appendToConf(conf); + Assert.assertEquals( + conf.getFloat(DistCpOptionSwitch.BANDWIDTH.getConfigLabel(), -1), 88.0, + DELTA); + + conf = new Configuration(); + conf.set(DistCpOptionSwitch.BANDWIDTH.getConfigLabel(), "88.0"); + Assert.assertEquals( + conf.getRaw(DistCpOptionSwitch.BANDWIDTH.getConfigLabel()), "88.0"); + options = OptionsParser.parse(new String[] { + "-bandwidth", + "99", + "hdfs://localhost:8020/source/first", + "hdfs://localhost:8020/target/"}); + options.appendToConf(conf); + Assert.assertEquals( + conf.getFloat(DistCpOptionSwitch.BANDWIDTH.getConfigLabel(), -1), 99.0, + DELTA); + } + @Test public void testAppendOption() { Configuration conf = new Configuration(); diff --git a/hadoop-tools/hadoop-extras/src/main/shellprofile.d/hadoop-extras.sh b/hadoop-tools/hadoop-extras/src/main/shellprofile.d/hadoop-extras.sh index 829d4062922..1ce9aeee984 100755 --- a/hadoop-tools/hadoop-extras/src/main/shellprofile.d/hadoop-extras.sh +++ b/hadoop-tools/hadoop-extras/src/main/shellprofile.d/hadoop-extras.sh @@ -32,8 +32,6 @@ function hadoop_subcommand_distch # shellcheck disable=SC2034 HADOOP_CLASSNAME=org.apache.hadoop.tools.DistCh hadoop_add_to_classpath_tools hadoop-extras - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" } fi diff --git a/hadoop-tools/hadoop-rumen/src/main/shellprofile.d/hadoop-rumen.sh b/hadoop-tools/hadoop-rumen/src/main/shellprofile.d/hadoop-rumen.sh index d7d4022453c..77023ff888e 100755 --- a/hadoop-tools/hadoop-rumen/src/main/shellprofile.d/hadoop-rumen.sh +++ b/hadoop-tools/hadoop-rumen/src/main/shellprofile.d/hadoop-rumen.sh @@ -30,8 +30,6 @@ function hadoop_subcommand_rumenfolder # shellcheck disable=SC2034 HADOOP_CLASSNAME=org.apache.hadoop.tools.rumen.Folder hadoop_add_to_classpath_tools hadoop-rumen - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" } fi @@ -51,8 +49,6 @@ function hadoop_subcommand_rumentrace # shellcheck disable=SC2034 HADOOP_CLASSNAME=org.apache.hadoop.tools.rumen.TraceBuilder hadoop_add_to_classpath_tools hadoop-rumen - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" } fi diff --git a/hadoop-tools/hadoop-sls/src/main/bin/rumen2sls.sh b/hadoop-tools/hadoop-sls/src/main/bin/rumen2sls.sh index 0bd291bb8f3..565dfe6f5cb 100644 --- a/hadoop-tools/hadoop-sls/src/main/bin/rumen2sls.sh +++ b/hadoop-tools/hadoop-sls/src/main/bin/rumen2sls.sh @@ -68,8 +68,7 @@ function run_sls_generator() hadoop_add_param args -outputJobs "-outputJobs ${outputdir}/${outputprefix}-jobs.json" hadoop_add_param args -outputNodes "-outputNodes ${outputdir}/${outputprefix}-nodes.json" - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" + hadoop_add_client_opts hadoop_finalize # shellcheck disable=SC2086 diff --git a/hadoop-tools/hadoop-sls/src/main/bin/slsrun.sh b/hadoop-tools/hadoop-sls/src/main/bin/slsrun.sh index 403c4bb05f6..218dee43fc5 100644 --- a/hadoop-tools/hadoop-sls/src/main/bin/slsrun.sh +++ b/hadoop-tools/hadoop-sls/src/main/bin/slsrun.sh @@ -96,8 +96,7 @@ function run_simulation() { hadoop_add_param args -printsimulation "-printsimulation" fi - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" + hadoop_add_client_opts hadoop_finalize # shellcheck disable=SC2086 diff --git a/hadoop-tools/hadoop-streaming/src/main/shellprofile.d/hadoop-streaming.sh b/hadoop-tools/hadoop-streaming/src/main/shellprofile.d/hadoop-streaming.sh index cca016d8602..c3010ffce06 100755 --- a/hadoop-tools/hadoop-streaming/src/main/shellprofile.d/hadoop-streaming.sh +++ b/hadoop-tools/hadoop-streaming/src/main/shellprofile.d/hadoop-streaming.sh @@ -46,10 +46,6 @@ function mapred_subcommand_streaming done IFS=${oldifs} - - hadoop_debug "Appending HADOOP_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${HADOOP_CLIENT_OPTS}" - } fi diff --git a/hadoop-yarn-project/hadoop-yarn/bin/yarn b/hadoop-yarn-project/hadoop-yarn/bin/yarn index bd91633368e..804fd1a77fb 100755 --- a/hadoop-yarn-project/hadoop-yarn/bin/yarn +++ b/hadoop-yarn-project/hadoop-yarn/bin/yarn @@ -68,8 +68,6 @@ function yarncmd_case case ${subcmd} in application|applicationattempt|container) HADOOP_CLASSNAME=org.apache.hadoop.yarn.client.cli.ApplicationCLI - hadoop_debug "Append YARN_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${YARN_CLIENT_OPTS}" set -- "${subcmd}" "$@" HADOOP_SUBCMD_ARGS=("$@") ;; @@ -78,13 +76,9 @@ function yarncmd_case ;; cluster) HADOOP_CLASSNAME=org.apache.hadoop.yarn.client.cli.ClusterCLI - hadoop_debug "Append YARN_CLIENT_OPTS onto YARN_OPTS" - YARN_OPTS="${YARN_OPTS} ${YARN_CLIENT_OPTS}" ;; daemonlog) HADOOP_CLASSNAME=org.apache.hadoop.log.LogLevel - hadoop_debug "Append YARN_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${YARN_CLIENT_OPTS}" ;; envvars) echo "JAVA_HOME='${JAVA_HOME}'" @@ -99,8 +93,6 @@ function yarncmd_case ;; jar) HADOOP_CLASSNAME=org.apache.hadoop.util.RunJar - hadoop_debug "Append YARN_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${YARN_CLIENT_OPTS}" ;; historyserver) HADOOP_SUBCMD_SUPPORTDAEMONIZATION="true" @@ -111,19 +103,13 @@ function yarncmd_case ;; logs) HADOOP_CLASSNAME=org.apache.hadoop.yarn.client.cli.LogsCLI - hadoop_debug "Append YARN_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${YARN_CLIENT_OPTS}" ;; node) HADOOP_CLASSNAME=org.apache.hadoop.yarn.client.cli.NodeCLI - hadoop_debug "Append YARN_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${YARN_CLIENT_OPTS}" ;; nodemanager) HADOOP_SUBCMD_SUPPORTDAEMONIZATION="true" HADOOP_CLASSNAME='org.apache.hadoop.yarn.server.nodemanager.NodeManager' - hadoop_debug "Append YARN_NODEMANAGER_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${YARN_NODEMANAGER_OPTS}" # Backwards compatibility if [[ -n "${YARN_NODEMANAGER_HEAPSIZE}" ]]; then HADOOP_HEAPSIZE_MAX="${YARN_NODEMANAGER_HEAPSIZE}" @@ -132,8 +118,6 @@ function yarncmd_case proxyserver) HADOOP_SUBCMD_SUPPORTDAEMONIZATION="true" HADOOP_CLASSNAME='org.apache.hadoop.yarn.server.webproxy.WebAppProxyServer' - hadoop_debug "Append YARN_PROXYSERVER_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${YARN_PROXYSERVER_OPTS}" # Backwards compatibility if [[ -n "${YARN_PROXYSERVER_HEAPSIZE}" ]]; then # shellcheck disable=SC2034 @@ -142,14 +126,10 @@ function yarncmd_case ;; queue) HADOOP_CLASSNAME=org.apache.hadoop.yarn.client.cli.QueueCLI - hadoop_debug "Append YARN_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${YARN_CLIENT_OPTS}" ;; resourcemanager) HADOOP_SUBCMD_SUPPORTDAEMONIZATION="true" HADOOP_CLASSNAME='org.apache.hadoop.yarn.server.resourcemanager.ResourceManager' - HADOOP_OPTS="${HADOOP_OPTS} ${YARN_RESOURCEMANAGER_OPTS}" - hadoop_debug "Append YARN_RESOURCEMANAGER_OPTS onto HADOOP_OPTS" # Backwards compatibility if [[ -n "${YARN_RESOURCEMANAGER_HEAPSIZE}" ]]; then # shellcheck disable=SC2034 @@ -158,31 +138,21 @@ function yarncmd_case ;; rmadmin) HADOOP_CLASSNAME='org.apache.hadoop.yarn.client.cli.RMAdminCLI' - hadoop_debug "Append YARN_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${YARN_CLIENT_OPTS}" ;; scmadmin) HADOOP_CLASSNAME='org.apache.hadoop.yarn.client.SCMAdmin' - hadoop_debug "Append YARN_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${YARN_CLIENT_OPTS}" ;; sharedcachemanager) HADOOP_SUBCMD_SUPPORTDAEMONIZATION="true" HADOOP_CLASSNAME='org.apache.hadoop.yarn.server.sharedcachemanager.SharedCacheManager' - hadoop_debug "Append YARN_SHAREDCACHEMANAGER_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${YARN_SHAREDCACHEMANAGER_OPTS}" ;; timelinereader) HADOOP_SUBCMD_SUPPORTDAEMONIZATION="true" HADOOP_CLASSNAME='org.apache.hadoop.yarn.server.timelineservice.reader.TimelineReaderServer' - hadoop_debug "Append YARN_TIMELINEREADER_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${YARN_TIMELINEREADER_OPTS}" ;; timelineserver) HADOOP_SUBCMD_SUPPORTDAEMONIZATION="true" HADOOP_CLASSNAME='org.apache.hadoop.yarn.server.applicationhistoryservice.ApplicationHistoryServer' - hadoop_debug "Append YARN_TIMELINESERVER_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${YARN_TIMELINESERVER_OPTS}" # Backwards compatibility if [[ -n "${YARN_TIMELINESERVER_HEAPSIZE}" ]]; then # shellcheck disable=SC2034 @@ -191,8 +161,6 @@ function yarncmd_case ;; version) HADOOP_CLASSNAME=org.apache.hadoop.util.VersionInfo - hadoop_debug "Append YARN_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${YARN_CLIENT_OPTS}" ;; top) doNotSetCols=0 @@ -222,8 +190,6 @@ function yarncmd_case fi fi HADOOP_CLASSNAME=org.apache.hadoop.yarn.client.cli.TopCLI - hadoop_debug "Append YARN_CLIENT_OPTS onto HADOOP_OPTS" - HADOOP_OPTS="${HADOOP_OPTS} ${YARN_CLIENT_OPTS}" HADOOP_SUBCMD_ARGS=("$@") ;; *) @@ -262,6 +228,8 @@ fi HADOOP_SUBCMD=$1 shift +hadoop_verify_user "${HADOOP_SHELL_EXECNAME}" "${HADOOP_SUBCMD}" + HADOOP_SUBCMD_ARGS=("$@") if declare -f yarn_subcommand_"${HADOOP_SUBCMD}" >/dev/null 2>&1; then @@ -271,15 +239,30 @@ else yarncmd_case "${HADOOP_SUBCMD}" "${HADOOP_SUBCMD_ARGS[@]}" fi -hadoop_verify_user "${HADOOP_SUBCMD}" +# It's unclear if YARN_CLIENT_OPTS is actually a useful +# thing to have separate from HADOOP_CLIENT_OPTS. Someone +# might use it, so let's not deprecate it and just override +# HADOOP_CLIENT_OPTS instead before we (potentially) add it +# to the command line +if [[ -n "${YARN_CLIENT_OPTS}" ]]; then + # shellcheck disable=SC2034 + HADOOP_CLIENT_OPTS=${YARN_CLIENT_OPTS} +fi + +hadoop_add_client_opts if [[ ${HADOOP_WORKER_MODE} = true ]]; then hadoop_common_worker_mode_execute "${HADOOP_YARN_HOME}/bin/yarn" "${HADOOP_USER_PARAMS[@]}" exit $? fi +hadoop_subcommand_opts "${HADOOP_SHELL_EXECNAME}" "${HADOOP_SUBCMD}" + if [[ "${HADOOP_SUBCMD_SECURESERVICE}" = true ]]; then HADOOP_SECURE_USER="${HADOOP_SUBCMD_SECUREUSER}" + + hadoop_subcommand_secure_opts "${HADOOP_SHELL_EXECNAME}" "${HADOOP_SUBCMD}" + hadoop_verify_secure_prereq hadoop_setup_secure_service priv_outfile="${HADOOP_LOG_DIR}/privileged-${HADOOP_IDENT_STRING}-${HADOOP_SUBCMD}-${HOSTNAME}.out" diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairScheduler.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairScheduler.java index ac8dbe1babe..15e23187050 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairScheduler.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairScheduler.java @@ -186,7 +186,6 @@ public class FairScheduler extends // an app can be reserved on protected boolean sizeBasedWeight; // Give larger weights to larger jobs - protected WeightAdjuster weightAdjuster; // Can be null for no weight adjuster protected boolean continuousSchedulingEnabled; // Continuous Scheduling enabled or not protected int continuousSchedulingSleepMs; // Sleep time for each pass in continuous scheduling private Comparator nodeAvailableResourceComparator = @@ -563,10 +562,6 @@ public class FairScheduler extends weight = Math.log1p(app.getDemand().getMemorySize()) / Math.log(2); } weight *= app.getPriority().getPriority(); - if (weightAdjuster != null) { - // Run weight through the user-supplied weightAdjuster - weight = weightAdjuster.adjustWeight(app, weight); - } ResourceWeights resourceWeights = app.getResourceWeights(); resourceWeights.setWeight((float)weight); return resourceWeights; diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/NewAppWeightBooster.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/NewAppWeightBooster.java deleted file mode 100644 index fb32e565808..00000000000 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/NewAppWeightBooster.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * 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.yarn.server.resourcemanager.scheduler.fair; - -import org.apache.hadoop.classification.InterfaceAudience.Private; -import org.apache.hadoop.classification.InterfaceStability.Unstable; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.conf.Configured; - -/** - * A {@link WeightAdjuster} implementation that gives a weight boost to new jobs - * for a certain amount of time -- by default, a 3x weight boost for 60 seconds. - * This can be used to make shorter jobs finish faster, emulating Shortest Job - * First scheduling while not starving long jobs. - */ -@Private -@Unstable -public class NewAppWeightBooster extends Configured implements WeightAdjuster { - private static final float DEFAULT_FACTOR = 3; - private static final long DEFAULT_DURATION = 5 * 60 * 1000; - - private float factor; - private long duration; - - public void setConf(Configuration conf) { - if (conf != null) { - factor = conf.getFloat("mapred.newjobweightbooster.factor", - DEFAULT_FACTOR); - duration = conf.getLong("mapred.newjobweightbooster.duration", - DEFAULT_DURATION); - } - super.setConf(conf); - } - - public double adjustWeight(FSAppAttempt app, double curWeight) { - long start = app.getStartTime(); - long now = System.currentTimeMillis(); - if (now - start < duration) { - return curWeight * factor; - } else { - return curWeight; - } - } -}