From a61a18cc098591eacd998e4a2f61babe27353a31 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Mon, 19 Dec 2011 23:07:17 +0000 Subject: [PATCH 01/16] MAPREDUCE-3563. Fixed LocalJobRunner to work correctly with new mapreduce apis. git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1220996 13f79535-47bb-0310-9956-ffa450edef68 --- hadoop-mapreduce-project/CHANGES.txt | 6 +- .../apache/hadoop/mapred/LocalJobRunner.java | 40 ++++- .../mapred/TestLocalModeWithNewApis.java | 157 ++++++++++++++++++ .../lib/output/FileOutputCommitter.java | 2 +- 4 files changed, 201 insertions(+), 4 deletions(-) create mode 100644 hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/test/java/org/apache/hadoop/mapred/TestLocalModeWithNewApis.java diff --git a/hadoop-mapreduce-project/CHANGES.txt b/hadoop-mapreduce-project/CHANGES.txt index 0887d51c86c..f6e41d9da45 100644 --- a/hadoop-mapreduce-project/CHANGES.txt +++ b/hadoop-mapreduce-project/CHANGES.txt @@ -331,7 +331,11 @@ Release 0.23.1 - Unreleased before the job started, so that it works properly with oozie throughout the job execution. (Robert Joseph Evans via vinodkv) - MAPREDUCE-3579. ConverterUtils shouldn't include a port in a path from a url without a port. (atm via harsh) + MAPREDUCE-3579. ConverterUtils shouldn't include a port in a path from a url + without a port. (atm via harsh) + + MAPREDUCE-3563. Fixed LocalJobRunner to work correctly with new mapreduce + apis. (acmurthy) Release 0.23.0 - 2011-11-01 diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapred/LocalJobRunner.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapred/LocalJobRunner.java index c8b59ebdac3..7fe5b99aeb7 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapred/LocalJobRunner.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapred/LocalJobRunner.java @@ -43,6 +43,7 @@ import org.apache.hadoop.ipc.ProtocolSignature; import org.apache.hadoop.mapreduce.Cluster.JobTrackerStatus; import org.apache.hadoop.mapreduce.ClusterMetrics; import org.apache.hadoop.mapreduce.MRConfig; +import org.apache.hadoop.mapreduce.OutputFormat; import org.apache.hadoop.mapreduce.QueueInfo; import org.apache.hadoop.mapreduce.TaskCompletionEvent; import org.apache.hadoop.mapreduce.TaskTrackerInfo; @@ -52,11 +53,13 @@ import org.apache.hadoop.mapreduce.security.token.delegation.DelegationTokenIden import org.apache.hadoop.mapreduce.server.jobtracker.JTConfig; import org.apache.hadoop.mapreduce.split.JobSplit.TaskSplitMetaInfo; import org.apache.hadoop.mapreduce.split.SplitMetaInfoReader; +import org.apache.hadoop.mapreduce.task.TaskAttemptContextImpl; import org.apache.hadoop.mapreduce.v2.LogParams; import org.apache.hadoop.security.Credentials; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.authorize.AccessControlList; import org.apache.hadoop.security.token.Token; +import org.apache.hadoop.util.ReflectionUtils; /** Implements MapReduce locally, in-process, for debugging. */ @InterfaceAudience.Private @@ -304,12 +307,45 @@ public class LocalJobRunner implements ClientProtocol { return executor; } + private org.apache.hadoop.mapreduce.OutputCommitter + createOutputCommitter(boolean newApiCommitter, JobID jobId, Configuration conf) throws Exception { + org.apache.hadoop.mapreduce.OutputCommitter committer = null; + + LOG.info("OutputCommitter set in config " + + conf.get("mapred.output.committer.class")); + + if (newApiCommitter) { + org.apache.hadoop.mapreduce.TaskID taskId = + new org.apache.hadoop.mapreduce.TaskID(jobId, TaskType.MAP, 0); + org.apache.hadoop.mapreduce.TaskAttemptID taskAttemptID = + new org.apache.hadoop.mapreduce.TaskAttemptID(taskId, 0); + org.apache.hadoop.mapreduce.TaskAttemptContext taskContext = + new TaskAttemptContextImpl(conf, taskAttemptID); + OutputFormat outputFormat = + ReflectionUtils.newInstance(taskContext.getOutputFormatClass(), conf); + committer = outputFormat.getOutputCommitter(taskContext); + } else { + committer = ReflectionUtils.newInstance(conf.getClass( + "mapred.output.committer.class", FileOutputCommitter.class, + org.apache.hadoop.mapred.OutputCommitter.class), conf); + } + LOG.info("OutputCommitter is " + committer.getClass().getName()); + return committer; + } + @Override public void run() { JobID jobId = profile.getJobID(); JobContext jContext = new JobContextImpl(job, jobId); - OutputCommitter outputCommitter = job.getOutputCommitter(); - + + org.apache.hadoop.mapreduce.OutputCommitter outputCommitter = null; + try { + outputCommitter = createOutputCommitter(conf.getUseNewMapper(), jobId, conf); + } catch (Exception e) { + LOG.info("Failed to createOutputCommitter", e); + return; + } + try { TaskSplitMetaInfo[] taskSplitMetaInfos = SplitMetaInfoReader.readSplitMetaInfo(jobId, localFs, conf, systemJobDir); diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/test/java/org/apache/hadoop/mapred/TestLocalModeWithNewApis.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/test/java/org/apache/hadoop/mapred/TestLocalModeWithNewApis.java new file mode 100644 index 00000000000..e9e04709e32 --- /dev/null +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/test/java/org/apache/hadoop/mapred/TestLocalModeWithNewApis.java @@ -0,0 +1,157 @@ +/** + * 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.mapred; + +import static org.junit.Assert.*; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Random; +import java.util.StringTokenizer; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.FileUtil; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.io.IntWritable; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.mapreduce.Job; +import org.apache.hadoop.mapreduce.MRConfig; +import org.apache.hadoop.mapreduce.Mapper; +import org.apache.hadoop.mapreduce.Reducer; +import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; +import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class TestLocalModeWithNewApis { + + public static final Log LOG = + LogFactory.getLog(TestLocalModeWithNewApis.class); + + Configuration conf; + + @Before + public void setUp() throws Exception { + conf = new Configuration(); + conf.set(MRConfig.FRAMEWORK_NAME, MRConfig.LOCAL_FRAMEWORK_NAME); + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testNewApis() throws Exception { + Random r = new Random(System.currentTimeMillis()); + Path tmpBaseDir = new Path("/tmp/wc-" + r.nextInt()); + final Path inDir = new Path(tmpBaseDir, "input"); + final Path outDir = new Path(tmpBaseDir, "output"); + String input = "The quick brown fox\nhas many silly\nred fox sox\n"; + FileSystem inFs = inDir.getFileSystem(conf); + FileSystem outFs = outDir.getFileSystem(conf); + outFs.delete(outDir, true); + if (!inFs.mkdirs(inDir)) { + throw new IOException("Mkdirs failed to create " + inDir.toString()); + } + { + DataOutputStream file = inFs.create(new Path(inDir, "part-0")); + file.writeBytes(input); + file.close(); + } + + Job job = Job.getInstance(conf, "word count"); + job.setJarByClass(TestLocalModeWithNewApis.class); + job.setMapperClass(TokenizerMapper.class); + job.setCombinerClass(IntSumReducer.class); + job.setReducerClass(IntSumReducer.class); + job.setOutputKeyClass(Text.class); + job.setOutputValueClass(IntWritable.class); + FileInputFormat.addInputPath(job, inDir); + FileOutputFormat.setOutputPath(job, outDir); + assertEquals(job.waitForCompletion(true), true); + + String output = readOutput(outDir, conf); + assertEquals("The\t1\nbrown\t1\nfox\t2\nhas\t1\nmany\t1\n" + + "quick\t1\nred\t1\nsilly\t1\nsox\t1\n", output); + + outFs.delete(tmpBaseDir, true); + } + + static String readOutput(Path outDir, Configuration conf) + throws IOException { + FileSystem fs = outDir.getFileSystem(conf); + StringBuffer result = new StringBuffer(); + + Path[] fileList = FileUtil.stat2Paths(fs.listStatus(outDir, + new Utils.OutputFileUtils.OutputFilesFilter())); + for (Path outputFile : fileList) { + LOG.info("Path" + ": "+ outputFile); + BufferedReader file = + new BufferedReader(new InputStreamReader(fs.open(outputFile))); + String line = file.readLine(); + while (line != null) { + result.append(line); + result.append("\n"); + line = file.readLine(); + } + file.close(); + } + return result.toString(); + } + + public static class TokenizerMapper + extends Mapper{ + + private final static IntWritable one = new IntWritable(1); + private Text word = new Text(); + + public void map(Object key, Text value, Context context + ) throws IOException, InterruptedException { + StringTokenizer itr = new StringTokenizer(value.toString()); + while (itr.hasMoreTokens()) { + word.set(itr.nextToken()); + context.write(word, one); + } + } + } + + + public static class IntSumReducer + extends Reducer { + private IntWritable result = new IntWritable(); + + public void reduce(Text key, Iterable values, + Context context + ) throws IOException, InterruptedException { + int sum = 0; + for (IntWritable val : values) { + sum += val.get(); + } + result.set(sum); + context.write(key, result); + } + } + +} diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/output/FileOutputCommitter.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/output/FileOutputCommitter.java index 497ca317fd3..ccd32e0c1bd 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/output/FileOutputCommitter.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/output/FileOutputCommitter.java @@ -233,7 +233,7 @@ public class FileOutputCommitter extends OutputCommitter { " directory of task: " + attemptId + " - " + workPath); } LOG.info("Saved output of task '" + attemptId + "' to " + - outputPath); + jobOutputPath); } } } From 3503d9a4a8f5ca2ff13badc7e87c12d4a665e681 Mon Sep 17 00:00:00 2001 From: Tsz-wo Sze Date: Tue, 20 Dec 2011 05:49:24 +0000 Subject: [PATCH 02/16] HDFS-2706. Use configuration for blockInvalidateLimit if it is set. git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1221106 13f79535-47bb-0310-9956-ffa450edef68 --- hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt | 3 +++ .../hadoop/hdfs/server/blockmanagement/DatanodeManager.java | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt index 300d9fb4461..71ecda11604 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt +++ b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt @@ -283,6 +283,9 @@ Release 0.23.1 - UNRELEASED HDFS-2553. Fix BlockPoolSliceScanner spinning in a tight loop (Uma Maheswara Rao G via todd) + HDFS-2706. Use configuration for blockInvalidateLimit if it is set. + (szetszwo) + Release 0.23.0 - 2011-11-01 INCOMPATIBLE CHANGES diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/DatanodeManager.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/DatanodeManager.java index 33b648c5bad..5d795e74455 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/DatanodeManager.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/DatanodeManager.java @@ -155,8 +155,10 @@ public class DatanodeManager { DFSConfigKeys.DFS_NAMENODE_HEARTBEAT_RECHECK_INTERVAL_DEFAULT); // 5 minutes this.heartbeatExpireInterval = 2 * heartbeatRecheckInterval + 10 * 1000 * heartbeatIntervalSeconds; - this.blockInvalidateLimit = Math.max(20*(int)(heartbeatIntervalSeconds), + final int blockInvalidateLimit = Math.max(20*(int)(heartbeatIntervalSeconds), DFSConfigKeys.DFS_BLOCK_INVALIDATE_LIMIT_DEFAULT); + this.blockInvalidateLimit = conf.getInt( + DFSConfigKeys.DFS_BLOCK_INVALIDATE_LIMIT_KEY, blockInvalidateLimit); LOG.info(DFSConfigKeys.DFS_BLOCK_INVALIDATE_LIMIT_KEY + "=" + this.blockInvalidateLimit); } From f9b2197d2a7fa46d59509b281cfbd0dab75ce3b3 Mon Sep 17 00:00:00 2001 From: Eli Collins Date: Tue, 20 Dec 2011 17:17:26 +0000 Subject: [PATCH 03/16] HADOOP-7837. no NullAppender in the log4j config. Contributed by Eli Collins git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1221348 13f79535-47bb-0310-9956-ffa450edef68 --- hadoop-common-project/hadoop-common/CHANGES.txt | 2 ++ .../hadoop-common/src/main/conf/log4j.properties | 3 +++ .../src/main/packages/templates/conf/log4j.properties | 3 +++ 3 files changed, 8 insertions(+) diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt index ce83dd415ca..ddc2c46495f 100644 --- a/hadoop-common-project/hadoop-common/CHANGES.txt +++ b/hadoop-common-project/hadoop-common/CHANGES.txt @@ -220,6 +220,8 @@ Release 0.23.1 - Unreleased HADOOP-7914. Remove the duplicated declaration of hadoop-hdfs test-jar in hadoop-project/pom.xml. (szetszwo) + HADOOP-7837. no NullAppender in the log4j config. (eli) + Release 0.23.0 - 2011-11-01 INCOMPATIBLE CHANGES diff --git a/hadoop-common-project/hadoop-common/src/main/conf/log4j.properties b/hadoop-common-project/hadoop-common/src/main/conf/log4j.properties index ede0c93480b..777b2df8c56 100644 --- a/hadoop-common-project/hadoop-common/src/main/conf/log4j.properties +++ b/hadoop-common-project/hadoop-common/src/main/conf/log4j.properties @@ -19,6 +19,9 @@ log4j.rootLogger=${hadoop.root.logger}, EventCounter # Logging Threshold log4j.threshold=ALL +# Null Appender +log4j.appender.NullAppender=org.apache.log4j.varia.NullAppender + # # Daily Rolling File Appender # diff --git a/hadoop-common-project/hadoop-common/src/main/packages/templates/conf/log4j.properties b/hadoop-common-project/hadoop-common/src/main/packages/templates/conf/log4j.properties index 43da1b49be3..cbc31ad5e52 100644 --- a/hadoop-common-project/hadoop-common/src/main/packages/templates/conf/log4j.properties +++ b/hadoop-common-project/hadoop-common/src/main/packages/templates/conf/log4j.properties @@ -28,6 +28,9 @@ log4j.rootLogger=${hadoop.root.logger}, EventCounter # Logging Threshold log4j.threshold=ALL +# Null Appender +log4j.appender.NullAppender=org.apache.log4j.varia.NullAppender + # # Daily Rolling File Appender # From 96247ead035cc4d6b7be477e1875e8112298ce3d Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Tue, 20 Dec 2011 22:01:13 +0000 Subject: [PATCH 04/16] MAPREDUCE-3376. Fixed Task to ensure it passes reporter to combiners using old MR api. Contributed by Subroto Sanyal. git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1221501 13f79535-47bb-0310-9956-ffa450edef68 --- hadoop-mapreduce-project/CHANGES.txt | 3 + .../java/org/apache/hadoop/mapred/Task.java | 4 +- .../mapreduce/v2/TestMRAppWithCombiner.java | 160 ++++++++++++++++++ 3 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/v2/TestMRAppWithCombiner.java diff --git a/hadoop-mapreduce-project/CHANGES.txt b/hadoop-mapreduce-project/CHANGES.txt index f6e41d9da45..2cb12be1e57 100644 --- a/hadoop-mapreduce-project/CHANGES.txt +++ b/hadoop-mapreduce-project/CHANGES.txt @@ -337,6 +337,9 @@ Release 0.23.1 - Unreleased MAPREDUCE-3563. Fixed LocalJobRunner to work correctly with new mapreduce apis. (acmurthy) + MAPREDUCE-3376. Fixed Task to ensure it passes reporter to combiners using + old MR api. (Subroto Sanyal via acmurthy) + Release 0.23.0 - 2011-11-01 INCOMPATIBLE CHANGES diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/Task.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/Task.java index 29ce4822b76..17bf8cb4aa3 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/Task.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/Task.java @@ -1458,11 +1458,11 @@ abstract public class Task implements Writable, Configurable { try { CombineValuesIterator values = new CombineValuesIterator(kvIter, comparator, keyClass, - valueClass, job, Reporter.NULL, + valueClass, job, reporter, inputCounter); while (values.more()) { combiner.reduce(values.getKey(), values, combineCollector, - Reporter.NULL); + reporter); values.nextKey(); } } finally { diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/v2/TestMRAppWithCombiner.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/v2/TestMRAppWithCombiner.java new file mode 100644 index 00000000000..a96befaee68 --- /dev/null +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/v2/TestMRAppWithCombiner.java @@ -0,0 +1,160 @@ +/** + * 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.mapreduce.v2; + +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.util.Iterator; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.CustomOutputCommitter; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.fs.permission.FsPermission; +import org.apache.hadoop.io.LongWritable; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.mapred.FileInputFormat; +import org.apache.hadoop.mapred.FileOutputFormat; +import org.apache.hadoop.mapred.JobClient; +import org.apache.hadoop.mapred.JobConf; +import org.apache.hadoop.mapred.OutputCollector; +import org.apache.hadoop.mapred.Reporter; +import org.apache.hadoop.mapred.RunningJob; +import org.apache.hadoop.mapred.TextInputFormat; +import org.apache.hadoop.mapred.lib.IdentityMapper; +import org.apache.hadoop.mapred.lib.IdentityReducer; +import org.apache.hadoop.mapreduce.filecache.DistributedCache; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +@SuppressWarnings("deprecation") +public class TestMRAppWithCombiner { + + protected static MiniMRYarnCluster mrCluster; + private static Configuration conf = new Configuration(); + private static FileSystem localFs; + private static final Log LOG = LogFactory.getLog(TestMRAppWithCombiner.class); + + static { + try { + localFs = FileSystem.getLocal(conf); + } catch (IOException io) { + throw new RuntimeException("problem getting local fs", io); + } + } + + @BeforeClass + public static void setup() throws IOException { + + if (!(new File(MiniMRYarnCluster.APPJAR)).exists()) { + LOG.info("MRAppJar " + MiniMRYarnCluster.APPJAR + + " not found. Not running test."); + return; + } + + if (mrCluster == null) { + mrCluster = new MiniMRYarnCluster(TestMRJobs.class.getName(), 3); + Configuration conf = new Configuration(); + mrCluster.init(conf); + mrCluster.start(); + } + + // Copy MRAppJar and make it private. TODO: FIXME. This is a hack to + // workaround the absent public discache. + localFs.copyFromLocalFile(new Path(MiniMRYarnCluster.APPJAR), + TestMRJobs.APP_JAR); + localFs.setPermission(TestMRJobs.APP_JAR, new FsPermission("700")); + } + + @AfterClass + public static void tearDown() { + if (mrCluster != null) { + mrCluster.stop(); + mrCluster = null; + } + } + + @Test + public void testCombinerShouldUpdateTheReporter() throws Exception { + JobConf conf = new JobConf(mrCluster.getConfig()); + int numMaps = 5; + int numReds = 2; + Path in = new Path(mrCluster.getTestWorkDir().getAbsolutePath(), + "testCombinerShouldUpdateTheReporter-in"); + Path out = new Path(mrCluster.getTestWorkDir().getAbsolutePath(), + "testCombinerShouldUpdateTheReporter-out"); + createInputOutPutFolder(in, out, numMaps); + conf.setJobName("test-job-with-combiner"); + conf.setMapperClass(IdentityMapper.class); + conf.setCombinerClass(MyCombinerToCheckReporter.class); + //conf.setJarByClass(MyCombinerToCheckReporter.class); + conf.setReducerClass(IdentityReducer.class); + DistributedCache.addFileToClassPath(TestMRJobs.APP_JAR, conf); + conf.setOutputCommitter(CustomOutputCommitter.class); + conf.setInputFormat(TextInputFormat.class); + conf.setOutputKeyClass(LongWritable.class); + conf.setOutputValueClass(Text.class); + + FileInputFormat.setInputPaths(conf, in); + FileOutputFormat.setOutputPath(conf, out); + conf.setNumMapTasks(numMaps); + conf.setNumReduceTasks(numReds); + + runJob(conf); + } + + static void createInputOutPutFolder(Path inDir, Path outDir, int numMaps) + throws Exception { + FileSystem fs = FileSystem.get(conf); + if (fs.exists(outDir)) { + fs.delete(outDir, true); + } + if (!fs.exists(inDir)) { + fs.mkdirs(inDir); + } + String input = "The quick brown fox\n" + "has many silly\n" + + "red fox sox\n"; + for (int i = 0; i < numMaps; ++i) { + DataOutputStream file = fs.create(new Path(inDir, "part-" + i)); + file.writeBytes(input); + file.close(); + } + } + + static boolean runJob(JobConf conf) throws Exception { + JobClient jobClient = new JobClient(conf); + RunningJob job = jobClient.submitJob(conf); + return jobClient.monitorAndPrintJob(conf, job); + } + + class MyCombinerToCheckReporter extends IdentityReducer { + public void reduce(K key, Iterator values, OutputCollector output, + Reporter reporter) throws IOException { + if (Reporter.NULL == reporter) { + Assert.fail("A valid Reporter should have been used but, Reporter.NULL is used"); + } + } + } + +} From 4e1d5a0d71d4bdde0d8b7b4c2a9571279496daaa Mon Sep 17 00:00:00 2001 From: Vinod Kumar Vavilapalli Date: Tue, 20 Dec 2011 23:13:15 +0000 Subject: [PATCH 05/16] MAPREDUCE-3391. Making a trivial change to correct a log message in DistributedShell app's AM. Contributed by Subroto Sanyal. git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1221516 13f79535-47bb-0310-9956-ffa450edef68 --- hadoop-mapreduce-project/CHANGES.txt | 3 + .../distributedshell/ApplicationMaster.java | 134 +++++++++--------- 2 files changed, 70 insertions(+), 67 deletions(-) diff --git a/hadoop-mapreduce-project/CHANGES.txt b/hadoop-mapreduce-project/CHANGES.txt index 2cb12be1e57..b558442d8b5 100644 --- a/hadoop-mapreduce-project/CHANGES.txt +++ b/hadoop-mapreduce-project/CHANGES.txt @@ -161,6 +161,9 @@ Release 0.23.1 - Unreleased MAPREDUCE-3518. mapred queue -info -showJobs throws NPE. (Jonathan Eagles via mahadev) + MAPREDUCE-3391. Making a trivial change to correct a log message in + DistributedShell app's AM. (Subroto Sanyal via vinodkv) + OPTIMIZATIONS BUG FIXES diff --git a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/main/java/org/apache/hadoop/yarn/applications/distributedshell/ApplicationMaster.java b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/main/java/org/apache/hadoop/yarn/applications/distributedshell/ApplicationMaster.java index ab371f51fe4..611bdf88678 100644 --- a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/main/java/org/apache/hadoop/yarn/applications/distributedshell/ApplicationMaster.java +++ b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/main/java/org/apache/hadoop/yarn/applications/distributedshell/ApplicationMaster.java @@ -184,7 +184,7 @@ public class ApplicationMaster { private CopyOnWriteArrayList releasedContainers = new CopyOnWriteArrayList(); // Launch threads - private List launchThreads = new ArrayList(); + private List launchThreads = new ArrayList(); /** * @param args Command line args @@ -194,7 +194,7 @@ public class ApplicationMaster { try { ApplicationMaster appMaster = new ApplicationMaster(); LOG.info("Initializing ApplicationMaster"); - boolean doRun = appMaster.init(args); + boolean doRun = appMaster.init(args); if (!doRun) { System.exit(0); } @@ -202,14 +202,14 @@ public class ApplicationMaster { } catch (Throwable t) { LOG.fatal("Error running ApplicationMaster", t); System.exit(1); - } + } if (result) { LOG.info("Application Master completed successfully. exiting"); System.exit(0); } else { LOG.info("Application Master failed. exiting"); - System.exit(2); + System.exit(2); } } @@ -218,7 +218,7 @@ public class ApplicationMaster { */ private void dumpOutDebugInfo() { - LOG.info("Dump debug output"); + LOG.info("Dump debug output"); Map envs = System.getenv(); for (Map.Entry env : envs.entrySet()) { LOG.info("System env: key=" + env.getKey() + ", val=" + env.getValue()); @@ -277,7 +277,7 @@ public class ApplicationMaster { if (args.length == 0) { printUsage(opts); throw new IllegalArgumentException("No args specified for application master to initialize"); - } + } if (cliParser.hasOption("help")) { printUsage(opts); @@ -297,8 +297,8 @@ public class ApplicationMaster { appAttemptID = ConverterUtils.toApplicationAttemptId(appIdStr); } else { - throw new IllegalArgumentException("Application Attempt Id not set in the environment"); - } + throw new IllegalArgumentException("Application Attempt Id not set in the environment"); + } } else { ContainerId containerId = ConverterUtils.toContainerId(envs.get(ApplicationConstants.AM_CONTAINER_ID_ENV)); appAttemptID = containerId.getApplicationAttemptId(); @@ -338,11 +338,11 @@ public class ApplicationMaster { if (envs.containsKey(DSConstants.DISTRIBUTEDSHELLSCRIPTLOCATION)) { shellScriptPath = envs.get(DSConstants.DISTRIBUTEDSHELLSCRIPTLOCATION); - if (envs.containsKey(DSConstants.DISTRIBUTEDSHELLSCRIPTTIMESTAMP)) { - shellScriptPathTimestamp = Long.valueOf(envs.get(DSConstants.DISTRIBUTEDSHELLSCRIPTTIMESTAMP)); - } + if (envs.containsKey(DSConstants.DISTRIBUTEDSHELLSCRIPTTIMESTAMP)) { + shellScriptPathTimestamp = Long.valueOf(envs.get(DSConstants.DISTRIBUTEDSHELLSCRIPTTIMESTAMP)); + } if (envs.containsKey(DSConstants.DISTRIBUTEDSHELLSCRIPTLEN)) { - shellScriptPathLen = Long.valueOf(envs.get(DSConstants.DISTRIBUTEDSHELLSCRIPTLEN)); + shellScriptPathLen = Long.valueOf(envs.get(DSConstants.DISTRIBUTEDSHELLSCRIPTLEN)); } if (!shellScriptPath.isEmpty() @@ -351,7 +351,7 @@ public class ApplicationMaster { LOG.error("Illegal values in env for shell script path" + ", path=" + shellScriptPath + ", len=" + shellScriptPathLen - + ", timestamp=" + shellScriptPathTimestamp); + + ", timestamp=" + shellScriptPathTimestamp); throw new IllegalArgumentException("Illegal values in env for shell script path"); } } @@ -368,7 +368,7 @@ public class ApplicationMaster { * @param opts Parsed command line options */ private void printUsage(Options opts) { - new HelpFormatter().printHelp("ApplicationMaster", opts); + new HelpFormatter().printHelp("ApplicationMaster", opts); } /** @@ -378,7 +378,7 @@ public class ApplicationMaster { public boolean run() throws YarnRemoteException { LOG.info("Starting ApplicationMaster"); - // Connect to ResourceManager + // Connect to ResourceManager resourceManager = connectToRM(); // Setup local RPC Server to accept status requests directly from clients @@ -395,7 +395,7 @@ public class ApplicationMaster { // A resource ask has to be atleast the minimum of the capability of the cluster, the value has to be // a multiple of the min value and cannot exceed the max. - // If it is not an exact multiple of min, the RM will allocate to the nearest multiple of min + // If it is not an exact multiple of min, the RM will allocate to the nearest multiple of min if (containerMemory < minMem) { LOG.info("Container memory specified below min threshold of cluster. Using min value." + ", specified=" + containerMemory @@ -409,14 +409,14 @@ public class ApplicationMaster { containerMemory = maxMem; } - // Setup heartbeat emitter + // Setup heartbeat emitter // TODO poll RM every now and then with an empty request to let RM know that we are alive // The heartbeat interval after which an AM is timed out by the RM is defined by a config setting: // RM_AM_EXPIRY_INTERVAL_MS with default defined by DEFAULT_RM_AM_EXPIRY_INTERVAL_MS // The allocate calls to the RM count as heartbeats so, for now, this additional heartbeat emitter // is not required. - // Setup ask for containers from RM + // Setup ask for containers from RM // Send request for containers to RM // Until we get our fully allocated quota, we keep on polling RM for containers // Keep looping until all the containers are launched and shell script executed on them @@ -426,7 +426,7 @@ public class ApplicationMaster { while (numCompletedContainers.get() < numTotalContainers && !appDone) { - loopCounter++; + loopCounter++; // log current state LOG.info("Current application state: loop=" + loopCounter @@ -435,7 +435,7 @@ public class ApplicationMaster { + ", requested=" + numRequestedContainers + ", completed=" + numCompletedContainers + ", failed=" + numFailedContainers - + ", currentAllocated=" + numAllocatedContainers); + + ", currentAllocated=" + numAllocatedContainers); // Sleep before each loop when asking RM for containers // to avoid flooding RM with spurious requests when it @@ -444,7 +444,7 @@ public class ApplicationMaster { try { Thread.sleep(1000); } catch (InterruptedException e) { - LOG.info("Sleep interrupted " + e.getMessage()); + LOG.info("Sleep interrupted " + e.getMessage()); } // No. of containers to request @@ -457,14 +457,14 @@ public class ApplicationMaster { // Setup request to be sent to RM to allocate containers List resourceReq = new ArrayList(); if (askCount > 0) { - ResourceRequest containerAsk = setupContainerAskForRM(askCount); + ResourceRequest containerAsk = setupContainerAskForRM(askCount); resourceReq.add(containerAsk); } // Send the request to RM LOG.info("Asking RM for containers" + ", askCount=" + askCount); - AMResponse amResp = sendContainerAskToRM(resourceReq); + AMResponse amResp =sendContainerAskToRM(resourceReq); // Retrieve list of allocated containers from the response List allocatedContainers = amResp.getAllocatedContainers(); @@ -478,10 +478,10 @@ public class ApplicationMaster { + ", containerNodeURI=" + allocatedContainer.getNodeHttpAddress() + ", containerState" + allocatedContainer.getState() + ", containerResourceMemory" + allocatedContainer.getResource().getMemory()); - // + ", containerToken" + allocatedContainer.getContainerToken().getIdentifier().toString()); + //+ ", containerToken" + allocatedContainer.getContainerToken().getIdentifier().toString()); LaunchContainerRunnable runnableLaunchContainer = new LaunchContainerRunnable(allocatedContainer); - Thread launchThread = new Thread(runnableLaunchContainer); + Thread launchThread = new Thread(runnableLaunchContainer); // launch and start the container on a separate thread to keep the main thread unblocked // as all containers may not be allocated at one go. @@ -492,14 +492,14 @@ public class ApplicationMaster { // Check what the current available resources in the cluster are // TODO should we do anything if the available resources are not enough? Resource availableResources = amResp.getAvailableResources(); - LOG.info("Current available resources in the cluster " + availableResources); + LOG.info("Current available resources in the cluster " + availableResources); - // Check the completed containers + // Check the completed containers List completedContainers = amResp.getCompletedContainersStatuses(); LOG.info("Got response from RM for container ask, completedCnt=" + completedContainers.size()); - for (ContainerStatus containerStatus : completedContainers) { + for (ContainerStatus containerStatus : completedContainers) { LOG.info("Got container status for containerID= " + containerStatus.getContainerId() - + ", state=" + containerStatus.getState() + + ", state=" + containerStatus.getState() + ", exitStatus=" + containerStatus.getExitStatus() + ", diagnostics=" + containerStatus.getDiagnostics()); @@ -514,7 +514,7 @@ public class ApplicationMaster { // shell script failed // counts as completed numCompletedContainers.incrementAndGet(); - numFailedContainers.incrementAndGet(); + numFailedContainers.incrementAndGet(); } else { // something else bad happened @@ -541,15 +541,15 @@ public class ApplicationMaster { LOG.info("Current application state: loop=" + loopCounter + ", appDone=" + appDone - + ", total=" + numTotalContainers + + ", total=" + numTotalContainers + ", requested=" + numRequestedContainers + ", completed=" + numCompletedContainers + ", failed=" + numFailedContainers - + ", currentAllocated=" + numAllocatedContainers); + + ", currentAllocated=" + numAllocatedContainers); // TODO // Add a timeout handling layer - // for misbehaving shell commands + // for misbehaving shell commands } // Join all launched threads @@ -561,7 +561,7 @@ public class ApplicationMaster { } catch (InterruptedException e) { LOG.info("Exception thrown in thread join: " + e.getMessage()); e.printStackTrace(); - } + } } // When the application completes, it should send a finish application signal @@ -610,10 +610,11 @@ public class ApplicationMaster { * Helper function to connect to CM */ private void connectToCM() { - String cmIpPortStr = container.getNodeId().getHost() + ":" - + container.getNodeId().getPort(); - InetSocketAddress cmAddress = NetUtils.createSocketAddr(cmIpPortStr); - LOG.info("Connecting to ResourceManager at " + cmIpPortStr); + LOG.debug("Connecting to ContainerManager for containerid=" + container.getId()); + String cmIpPortStr = container.getNodeId().getHost() + ":" + + container.getNodeId().getPort(); + InetSocketAddress cmAddress = NetUtils.createSocketAddr(cmIpPortStr); + LOG.info("Connecting to ContainerManager at " + cmIpPortStr); this.cm = ((ContainerManager) rpc.getProxy(ContainerManager.class, cmAddress, conf)); } @@ -626,7 +627,6 @@ public class ApplicationMaster { */ public void run() { // Connect to ContainerManager - LOG.info("Connecting to container manager for containerid=" + container.getId()); connectToCM(); LOG.info("Setting up container launch container for containerid=" + container.getId()); @@ -654,7 +654,7 @@ public class ApplicationMaster { if (!shellScriptPath.isEmpty()) { LocalResource shellRsrc = Records.newRecord(LocalResource.class); shellRsrc.setType(LocalResourceType.FILE); - shellRsrc.setVisibility(LocalResourceVisibility.APPLICATION); + shellRsrc.setVisibility(LocalResourceVisibility.APPLICATION); try { shellRsrc.setResource(ConverterUtils.getYarnUrlFromURI(new URI(shellScriptPath))); } catch (URISyntaxException e) { @@ -664,17 +664,17 @@ public class ApplicationMaster { // A failure scenario on bad input such as invalid shell script path // We know we cannot continue launching the container - // so we should release it. + // so we should release it. // TODO numCompletedContainers.incrementAndGet(); numFailedContainers.incrementAndGet(); - return; + return; } shellRsrc.setTimestamp(shellScriptPathTimestamp); shellRsrc.setSize(shellScriptPathLen); localResources.put(ExecShellStringPath, shellRsrc); - } - ctx.setLocalResources(localResources); + } + ctx.setLocalResources(localResources); // Set the necessary command to execute on the allocated container Vector vargs = new Vector(5); @@ -686,7 +686,7 @@ public class ApplicationMaster { vargs.add(ExecShellStringPath); } - // Set args for the shell command if any + // Set args for the shell command if any vargs.add(shellArgs); // Add log redirect params // TODO @@ -722,19 +722,19 @@ public class ApplicationMaster { // Left commented out as the shell scripts are short lived // and we are relying on the status for completed containers from RM to detect status - // GetContainerStatusRequest statusReq = Records.newRecord(GetContainerStatusRequest.class); - // statusReq.setContainerId(container.getId()); - // GetContainerStatusResponse statusResp; - // try { - // statusResp = cm.getContainerStatus(statusReq); - // LOG.info("Container Status" - // + ", id=" + container.getId() - // + ", status=" +statusResp.getStatus()); - // } catch (YarnRemoteException e) { - // e.printStackTrace(); - // } - } - } + // GetContainerStatusRequest statusReq = Records.newRecord(GetContainerStatusRequest.class); + // statusReq.setContainerId(container.getId()); + // GetContainerStatusResponse statusResp; + //try { + //statusResp = cm.getContainerStatus(statusReq); + // LOG.info("Container Status" + // + ", id=" + container.getId() + // + ", status=" +statusResp.getStatus()); + //} catch (YarnRemoteException e) { + //e.printStackTrace(); + //} + } + } /** * Connect to the Resource Manager @@ -744,25 +744,25 @@ public class ApplicationMaster { YarnConfiguration yarnConf = new YarnConfiguration(conf); InetSocketAddress rmAddress = NetUtils.createSocketAddr(yarnConf.get( YarnConfiguration.RM_SCHEDULER_ADDRESS, - YarnConfiguration.DEFAULT_RM_SCHEDULER_ADDRESS)); + YarnConfiguration.DEFAULT_RM_SCHEDULER_ADDRESS)); LOG.info("Connecting to ResourceManager at " + rmAddress); return ((AMRMProtocol) rpc.getProxy(AMRMProtocol.class, rmAddress, conf)); - } + } /** * Register the Application Master to the Resource Manager * @return the registration response from the RM * @throws YarnRemoteException */ - private RegisterApplicationMasterResponse registerToRM() throws YarnRemoteException { - RegisterApplicationMasterRequest appMasterRequest = Records.newRecord(RegisterApplicationMasterRequest.class); + private RegisterApplicationMasterResponse registerToRM() throws YarnRemoteException { + RegisterApplicationMasterRequest appMasterRequest = Records.newRecord(RegisterApplicationMasterRequest.class); // set the required info into the registration request: // application attempt id, // host on which the app master is running // rpc port on which the app master accepts requests from the client // tracking url for the app master - appMasterRequest.setApplicationAttemptId(appAttemptID); + appMasterRequest.setApplicationAttemptId(appAttemptID); appMasterRequest.setHost(appMasterHostname); appMasterRequest.setRpcPort(appMasterRpcPort); appMasterRequest.setTrackingUrl(appMasterTrackingUrl); @@ -792,7 +792,7 @@ public class ApplicationMaster { Priority pri = Records.newRecord(Priority.class); // TODO - what is the range for priority? how to decide? pri.setPriority(requestPriority); - request.setPriority(pri); + request.setPriority(pri); // Set up resource type requirements // For now, only memory is supported so we set memory requirements @@ -810,7 +810,7 @@ public class ApplicationMaster { * @throws YarnRemoteException */ private AMResponse sendContainerAskToRM(List requestedContainers) - throws YarnRemoteException { + throws YarnRemoteException { AllocateRequest req = Records.newRecord(AllocateRequest.class); req.setResponseId(rmRequestID.incrementAndGet()); req.setApplicationAttemptId(appAttemptID); @@ -830,7 +830,7 @@ public class ApplicationMaster { LOG.info("Released container, id=" + id.getId()); } - AllocateResponse resp = resourceManager.allocate(req); - return resp.getAMResponse(); + AllocateResponse resp = resourceManager.allocate(req); + return resp.getAMResponse(); } } From e7543b944c2b35d0a1ca0a92efeca47ad414ac7a Mon Sep 17 00:00:00 2001 From: Vinod Kumar Vavilapalli Date: Tue, 20 Dec 2011 23:27:02 +0000 Subject: [PATCH 06/16] MAPREDUCE-3339. Fixed MR AM to stop considering node blacklisting after the number of nodes blacklisted crosses a threshold. Contributed by Siddharth Seth. git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1221523 13f79535-47bb-0310-9956-ffa450edef68 --- hadoop-mapreduce-project/CHANGES.txt | 3 + .../mapreduce/v2/app/rm/RMCommunicator.java | 1 + .../v2/app/rm/RMContainerAllocator.java | 12 +- .../v2/app/rm/RMContainerRequestor.java | 64 ++++++- .../v2/app/TestRMContainerAllocator.java | 173 ++++++++++++++++++ .../apache/hadoop/mapreduce/MRJobConfig.java | 8 +- .../api/protocolrecords/AllocateResponse.java | 13 ++ .../impl/pb/AllocateResponsePBImpl.java | 19 +- .../src/main/proto/yarn_service_protos.proto | 1 + .../ApplicationMasterService.java | 1 + .../scheduler/YarnScheduler.java | 8 + .../scheduler/capacity/CapacityScheduler.java | 1 + .../scheduler/fifo/FifoScheduler.java | 7 +- 13 files changed, 293 insertions(+), 18 deletions(-) diff --git a/hadoop-mapreduce-project/CHANGES.txt b/hadoop-mapreduce-project/CHANGES.txt index b558442d8b5..7a64f9cfce2 100644 --- a/hadoop-mapreduce-project/CHANGES.txt +++ b/hadoop-mapreduce-project/CHANGES.txt @@ -343,6 +343,9 @@ Release 0.23.1 - Unreleased MAPREDUCE-3376. Fixed Task to ensure it passes reporter to combiners using old MR api. (Subroto Sanyal via acmurthy) + MAPREDUCE-3339. Fixed MR AM to stop considering node blacklisting after the + number of nodes blacklisted crosses a threshold. (Siddharth Seth via vinodkv) + Release 0.23.0 - 2011-11-01 INCOMPATIBLE CHANGES diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMCommunicator.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMCommunicator.java index 5028355acf3..4281e0a4842 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMCommunicator.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMCommunicator.java @@ -68,6 +68,7 @@ public abstract class RMCommunicator extends AbstractService { protected ApplicationAttemptId applicationAttemptId; private AtomicBoolean stopped; protected Thread allocatorThread; + @SuppressWarnings("rawtypes") protected EventHandler eventHandler; protected AMRMProtocol scheduler; private final ClientService clientService; diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerAllocator.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerAllocator.java index 81a5a75b503..d55dc2981f7 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerAllocator.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerAllocator.java @@ -479,12 +479,16 @@ public class RMContainerAllocator extends RMContainerRequestor //something changed recalculateReduceSchedule = true; } - - List allocatedContainers = new ArrayList(); - for (Container cont : newContainers) { - allocatedContainers.add(cont); + + if (LOG.isDebugEnabled()) { + for (Container cont : newContainers) { LOG.debug("Received new Container :" + cont); + } } + + //Called on each allocation. Will know about newly blacklisted/added hosts. + computeIgnoreBlacklisting(); + for (ContainerStatus cont : finishedContainers) { LOG.info("Received completed container " + cont); TaskAttemptId attemptID = assignedRequests.get(cont.getContainerId()); diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerRequestor.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerRequestor.java index 6c03c6690cf..2f25075ee84 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerRequestor.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerRequestor.java @@ -18,15 +18,15 @@ package org.apache.hadoop.mapreduce.v2.app.rm; -import java.net.InetAddress; -import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -35,6 +35,7 @@ import org.apache.hadoop.mapreduce.MRJobConfig; import org.apache.hadoop.mapreduce.v2.api.records.TaskAttemptId; import org.apache.hadoop.mapreduce.v2.app.AppContext; import org.apache.hadoop.mapreduce.v2.app.client.ClientService; +import org.apache.hadoop.yarn.YarnException; import org.apache.hadoop.yarn.api.protocolrecords.AllocateRequest; import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse; import org.apache.hadoop.yarn.api.records.AMResponse; @@ -47,6 +48,7 @@ import org.apache.hadoop.yarn.factories.RecordFactory; import org.apache.hadoop.yarn.factory.providers.RecordFactoryProvider; import org.apache.hadoop.yarn.util.BuilderUtils; + /** * Keeps the data structures to send container requests to RM. */ @@ -74,9 +76,15 @@ public abstract class RMContainerRequestor extends RMCommunicator { private final Set release = new TreeSet(); private boolean nodeBlacklistingEnabled; + private int blacklistDisablePercent; + private AtomicBoolean ignoreBlacklisting = new AtomicBoolean(false); + private int blacklistedNodeCount = 0; + private int lastClusterNmCount = 0; + private int clusterNmCount = 0; private int maxTaskFailuresPerNode; private final Map nodeFailures = new HashMap(); - private final Set blacklistedNodes = new HashSet(); + private final Set blacklistedNodes = Collections + .newSetFromMap(new ConcurrentHashMap()); public RMContainerRequestor(ClientService clientService, AppContext context) { super(clientService, context); @@ -122,7 +130,17 @@ public abstract class RMContainerRequestor extends RMCommunicator { LOG.info("nodeBlacklistingEnabled:" + nodeBlacklistingEnabled); maxTaskFailuresPerNode = conf.getInt(MRJobConfig.MAX_TASK_FAILURES_PER_TRACKER, 3); + blacklistDisablePercent = + conf.getInt( + MRJobConfig.MR_AM_IGNORE_BLACKLISTING_BLACKLISTED_NODE_PERECENT, + MRJobConfig.DEFAULT_MR_AM_IGNORE_BLACKLISTING_BLACKLISTED_NODE_PERCENT); LOG.info("maxTaskFailuresPerNode is " + maxTaskFailuresPerNode); + if (blacklistDisablePercent < -1 || blacklistDisablePercent > 100) { + throw new YarnException("Invalid blacklistDisablePercent: " + + blacklistDisablePercent + + ". Should be an integer between 0 and 100 or -1 to disabled"); + } + LOG.info("blacklistDisablePercent is " + blacklistDisablePercent); } protected AMResponse makeRemoteRequest() throws YarnRemoteException { @@ -134,19 +152,49 @@ public abstract class RMContainerRequestor extends RMCommunicator { AMResponse response = allocateResponse.getAMResponse(); lastResponseID = response.getResponseId(); availableResources = response.getAvailableResources(); + lastClusterNmCount = clusterNmCount; + clusterNmCount = allocateResponse.getNumClusterNodes(); LOG.info("getResources() for " + applicationId + ":" + " ask=" + ask.size() + " release= " + release.size() + " newContainers=" + response.getAllocatedContainers().size() + " finishedContainers=" + response.getCompletedContainersStatuses().size() + - " resourcelimit=" + availableResources); + " resourcelimit=" + availableResources + + "knownNMs=" + clusterNmCount); ask.clear(); release.clear(); return response; } + // May be incorrect if there's multiple NodeManagers running on a single host. + // knownNodeCount is based on node managers, not hosts. blacklisting is + // currently based on hosts. + protected void computeIgnoreBlacklisting() { + if (blacklistDisablePercent != -1 + && (blacklistedNodeCount != blacklistedNodes.size() || + clusterNmCount != lastClusterNmCount)) { + blacklistedNodeCount = blacklistedNodes.size(); + if (clusterNmCount == 0) { + LOG.info("KnownNode Count at 0. Not computing ignoreBlacklisting"); + return; + } + int val = (int) ((float) blacklistedNodes.size() / clusterNmCount * 100); + if (val >= blacklistDisablePercent) { + if (ignoreBlacklisting.compareAndSet(false, true)) { + LOG.info("Ignore blacklisting set to true. Known: " + clusterNmCount + + ", Blacklisted: " + blacklistedNodeCount + ", " + val + "%"); + } + } else { + if (ignoreBlacklisting.compareAndSet(true, false)) { + LOG.info("Ignore blacklisting set to false. Known: " + clusterNmCount + + ", Blacklisted: " + blacklistedNodeCount + ", " + val + "%"); + } + } + } + } + protected void containerFailedOnHost(String hostName) { if (!nodeBlacklistingEnabled) { return; @@ -161,8 +209,10 @@ public abstract class RMContainerRequestor extends RMCommunicator { LOG.info(failures + " failures on node " + hostName); if (failures >= maxTaskFailuresPerNode) { blacklistedNodes.add(hostName); + //Even if blacklisting is ignored, continue to remove the host from + // the request table. The RM may have additional nodes it can allocate on. LOG.info("Blacklisted host " + hostName); - + //remove all the requests corresponding to this hostname for (Map> remoteRequests : remoteRequestsTable.values()){ @@ -316,7 +366,7 @@ public abstract class RMContainerRequestor extends RMCommunicator { } protected boolean isNodeBlacklisted(String hostname) { - if (!nodeBlacklistingEnabled) { + if (!nodeBlacklistingEnabled || ignoreBlacklisting.get()) { return false; } return blacklistedNodes.contains(hostname); diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/TestRMContainerAllocator.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/TestRMContainerAllocator.java index c9436e5645a..785d8a7d03f 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/TestRMContainerAllocator.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/TestRMContainerAllocator.java @@ -488,6 +488,8 @@ public class TestRMContainerAllocator { Configuration conf = new Configuration(); conf.setBoolean(MRJobConfig.MR_AM_JOB_NODE_BLACKLISTING_ENABLE, true); conf.setInt(MRJobConfig.MAX_TASK_FAILURES_PER_TRACKER, 1); + conf.setInt( + MRJobConfig.MR_AM_IGNORE_BLACKLISTING_BLACKLISTED_NODE_PERECENT, -1); MyResourceManager rm = new MyResourceManager(conf); rm.start(); @@ -580,6 +582,175 @@ public class TestRMContainerAllocator { } } + @Test + public void testIgnoreBlacklisting() throws Exception { + LOG.info("Running testIgnoreBlacklisting"); + + Configuration conf = new Configuration(); + conf.setBoolean(MRJobConfig.MR_AM_JOB_NODE_BLACKLISTING_ENABLE, true); + conf.setInt(MRJobConfig.MAX_TASK_FAILURES_PER_TRACKER, 1); + conf.setInt( + MRJobConfig.MR_AM_IGNORE_BLACKLISTING_BLACKLISTED_NODE_PERECENT, 33); + + MyResourceManager rm = new MyResourceManager(conf); + rm.start(); + DrainDispatcher dispatcher = + (DrainDispatcher) rm.getRMContext().getDispatcher(); + + // Submit the application + RMApp app = rm.submitApp(1024); + dispatcher.await(); + + MockNM[] nodeManagers = new MockNM[10]; + int nmNum = 0; + List assigned = null; + nodeManagers[nmNum] = registerNodeManager(nmNum++, rm, dispatcher); + nodeManagers[0].nodeHeartbeat(true); + dispatcher.await(); + + ApplicationAttemptId appAttemptId = + app.getCurrentAppAttempt().getAppAttemptId(); + rm.sendAMLaunched(appAttemptId); + dispatcher.await(); + + JobId jobId = MRBuilderUtils.newJobId(appAttemptId.getApplicationId(), 0); + Job mockJob = mock(Job.class); + when(mockJob.getReport()).thenReturn( + MRBuilderUtils.newJobReport(jobId, "job", "user", JobState.RUNNING, 0, + 0, 0, 0, 0, 0, 0, "jobfile", null, false)); + MyContainerAllocator allocator = + new MyContainerAllocator(rm, conf, appAttemptId, mockJob); + + // Known=1, blacklisted=0, ignore should be false - assign first container + assigned = + getContainerOnHost(jobId, 1, 1024, new String[] { "h1" }, + nodeManagers[0], dispatcher, allocator); + Assert.assertEquals("No of assignments must be 1", 1, assigned.size()); + + LOG.info("Failing container _1 on H1 (Node should be blacklisted and" + + " ignore blacklisting enabled"); + // Send events to blacklist nodes h1 and h2 + ContainerFailedEvent f1 = createFailEvent(jobId, 1, "h1", false); + allocator.sendFailure(f1); + + // Test single node. + // Known=1, blacklisted=1, ignore should be true - assign 1 + assigned = + getContainerOnHost(jobId, 2, 1024, new String[] { "h1" }, + nodeManagers[0], dispatcher, allocator); + Assert.assertEquals("No of assignments must be 1", 1, assigned.size()); + + nodeManagers[nmNum] = registerNodeManager(nmNum++, rm, dispatcher); + // Known=2, blacklisted=1, ignore should be true - assign 1 anyway. + assigned = + getContainerOnHost(jobId, 3, 1024, new String[] { "h2" }, + nodeManagers[1], dispatcher, allocator); + Assert.assertEquals("No of assignments must be 1", 1, assigned.size()); + + nodeManagers[nmNum] = registerNodeManager(nmNum++, rm, dispatcher); + // Known=3, blacklisted=1, ignore should be true - assign 1 anyway. + assigned = + getContainerOnHost(jobId, 4, 1024, new String[] { "h3" }, + nodeManagers[2], dispatcher, allocator); + Assert.assertEquals("No of assignments must be 1", 1, assigned.size()); + + // Known=3, blacklisted=1, ignore should be true - assign 1 + assigned = + getContainerOnHost(jobId, 5, 1024, new String[] { "h1" }, + nodeManagers[0], dispatcher, allocator); + Assert.assertEquals("No of assignments must be 1", 1, assigned.size()); + + nodeManagers[nmNum] = registerNodeManager(nmNum++, rm, dispatcher); + // Known=4, blacklisted=1, ignore should be false - assign 1 anyway + assigned = + getContainerOnHost(jobId, 6, 1024, new String[] { "h4" }, + nodeManagers[3], dispatcher, allocator); + Assert.assertEquals("No of assignments must be 1", 1, assigned.size()); + + // Test blacklisting re-enabled. + // Known=4, blacklisted=1, ignore should be false - no assignment on h1 + assigned = + getContainerOnHost(jobId, 7, 1024, new String[] { "h1" }, + nodeManagers[0], dispatcher, allocator); + Assert.assertEquals("No of assignments must be 0", 0, assigned.size()); + // RMContainerRequestor would have created a replacement request. + + // Blacklist h2 + ContainerFailedEvent f2 = createFailEvent(jobId, 3, "h2", false); + allocator.sendFailure(f2); + + // Test ignore blacklisting re-enabled + // Known=4, blacklisted=2, ignore should be true. Should assign 2 + // containers. + assigned = + getContainerOnHost(jobId, 8, 1024, new String[] { "h1" }, + nodeManagers[0], dispatcher, allocator); + Assert.assertEquals("No of assignments must be 2", 2, assigned.size()); + + // Known=4, blacklisted=2, ignore should be true. + assigned = + getContainerOnHost(jobId, 9, 1024, new String[] { "h2" }, + nodeManagers[1], dispatcher, allocator); + Assert.assertEquals("No of assignments must be 1", 1, assigned.size()); + + // Test blacklist while ignore blacklisting enabled + ContainerFailedEvent f3 = createFailEvent(jobId, 4, "h3", false); + allocator.sendFailure(f3); + + nodeManagers[nmNum] = registerNodeManager(nmNum++, rm, dispatcher); + // Known=5, blacklisted=3, ignore should be true. + assigned = + getContainerOnHost(jobId, 10, 1024, new String[] { "h3" }, + nodeManagers[2], dispatcher, allocator); + Assert.assertEquals("No of assignments must be 1", 1, assigned.size()); + + // Assign on 5 more nodes - to re-enable blacklisting + for (int i = 0; i < 5; i++) { + nodeManagers[nmNum] = registerNodeManager(nmNum++, rm, dispatcher); + assigned = + getContainerOnHost(jobId, 11 + i, 1024, + new String[] { String.valueOf(5 + i) }, nodeManagers[4 + i], + dispatcher, allocator); + Assert.assertEquals("No of assignments must be 1", 1, assigned.size()); + } + + // Test h3 (blacklisted while ignoring blacklisting) is blacklisted. + assigned = + getContainerOnHost(jobId, 20, 1024, new String[] { "h3" }, + nodeManagers[2], dispatcher, allocator); + Assert.assertEquals("No of assignments must be 0", 0, assigned.size()); + } + + private MockNM registerNodeManager(int i, MyResourceManager rm, + DrainDispatcher dispatcher) throws Exception { + MockNM nm = rm.registerNode("h" + (i + 1) + ":1234", 10240); + dispatcher.await(); + return nm; + } + + private + List getContainerOnHost(JobId jobId, + int taskAttemptId, int memory, String[] hosts, MockNM mockNM, + DrainDispatcher dispatcher, MyContainerAllocator allocator) + throws Exception { + ContainerRequestEvent reqEvent = + createReq(jobId, taskAttemptId, memory, hosts); + allocator.sendRequest(reqEvent); + + // Send the request to the RM + List assigned = allocator.schedule(); + dispatcher.await(); + Assert.assertEquals("No of assignments must be 0", 0, assigned.size()); + + // Heartbeat from the required nodeManager + mockNM.nodeHeartbeat(true); + dispatcher.await(); + + assigned = allocator.schedule(); + dispatcher.await(); + return assigned; + } + @Test public void testBlackListedNodesWithSchedulingToThatNode() throws Exception { LOG.info("Running testBlackListedNodesWithSchedulingToThatNode"); @@ -587,6 +758,8 @@ public class TestRMContainerAllocator { Configuration conf = new Configuration(); conf.setBoolean(MRJobConfig.MR_AM_JOB_NODE_BLACKLISTING_ENABLE, true); conf.setInt(MRJobConfig.MAX_TASK_FAILURES_PER_TRACKER, 1); + conf.setInt( + MRJobConfig.MR_AM_IGNORE_BLACKLISTING_BLACKLISTED_NODE_PERECENT, -1); MyResourceManager rm = new MyResourceManager(conf); rm.start(); diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/MRJobConfig.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/MRJobConfig.java index 621787981d1..9dfa8f633ef 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/MRJobConfig.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/MRJobConfig.java @@ -348,8 +348,14 @@ public interface MRJobConfig { /** Enable blacklisting of nodes in the job.*/ public static final String MR_AM_JOB_NODE_BLACKLISTING_ENABLE = - MR_AM_PREFIX + "job.node.blacklisting.enable"; + MR_AM_PREFIX + "job.node-blacklisting.enable"; + /** Ignore blacklisting if a certain percentage of nodes have been blacklisted */ + public static final String MR_AM_IGNORE_BLACKLISTING_BLACKLISTED_NODE_PERECENT = + MR_AM_PREFIX + "job.node-blacklisting.ignore-threshold-node-percent"; + public static final int DEFAULT_MR_AM_IGNORE_BLACKLISTING_BLACKLISTED_NODE_PERCENT = + 33; + /** Enable job recovery.*/ public static final String MR_AM_JOB_RECOVERY_ENABLE = MR_AM_PREFIX + "job.recovery.enable"; diff --git a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/AllocateResponse.java b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/AllocateResponse.java index 267a252918c..a9ef8994201 100644 --- a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/AllocateResponse.java +++ b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/AllocateResponse.java @@ -61,4 +61,17 @@ public interface AllocateResponse { @Private @Unstable public abstract void setAMResponse(AMResponse amResponse); + + + /** + * Get the number of hosts available on the cluster. + * @return the available host count. + */ + @Public + @Stable + public int getNumClusterNodes(); + + @Private + @Unstable + public void setNumClusterNodes(int numNodes); } diff --git a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/AllocateResponsePBImpl.java b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/AllocateResponsePBImpl.java index 80946c1993d..971f23a7717 100644 --- a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/AllocateResponsePBImpl.java +++ b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/AllocateResponsePBImpl.java @@ -29,7 +29,8 @@ import org.apache.hadoop.yarn.proto.YarnServiceProtos.AllocateResponseProtoOrBui -public class AllocateResponsePBImpl extends ProtoBase implements AllocateResponse { +public class AllocateResponsePBImpl extends ProtoBase + implements AllocateResponse { AllocateResponseProto proto = AllocateResponseProto.getDefaultInstance(); AllocateResponseProto.Builder builder = null; boolean viaProto = false; @@ -95,7 +96,20 @@ public class AllocateResponsePBImpl extends ProtoBase imp builder.clearAMResponse(); this.amResponse = aMResponse; } + + @Override + public int getNumClusterNodes() { + AllocateResponseProtoOrBuilder p = viaProto ? proto : builder; + return p.getNumClusterNodes(); + } + + @Override + public void setNumClusterNodes(int numNodes) { + maybeInitBuilder(); + builder.setNumClusterNodes(numNodes); + } + private AMResponsePBImpl convertFromProtoFormat(AMResponseProto p) { return new AMResponsePBImpl(p); } @@ -103,7 +117,4 @@ public class AllocateResponsePBImpl extends ProtoBase imp private AMResponseProto convertToProtoFormat(AMResponse t) { return ((AMResponsePBImpl)t).getProto(); } - - - } diff --git a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-api/src/main/proto/yarn_service_protos.proto b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-api/src/main/proto/yarn_service_protos.proto index 99ca8b782d5..5444185b7ee 100644 --- a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-api/src/main/proto/yarn_service_protos.proto +++ b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-api/src/main/proto/yarn_service_protos.proto @@ -59,6 +59,7 @@ message AllocateRequestProto { message AllocateResponseProto { optional AMResponseProto AM_response = 1; + optional int32 num_cluster_nodes = 2; } diff --git a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ApplicationMasterService.java b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ApplicationMasterService.java index 175542cc2a7..2de40440246 100644 --- a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ApplicationMasterService.java +++ b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ApplicationMasterService.java @@ -285,6 +285,7 @@ public class ApplicationMasterService extends AbstractService implements response.setAvailableResources(allocation.getResourceLimit()); responseMap.put(appAttemptId, response); allocateResponse.setAMResponse(response); + allocateResponse.setNumClusterNodes(this.rScheduler.getNumClusterNodes()); return allocateResponse; } } diff --git a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/YarnScheduler.java b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/YarnScheduler.java index 7f7e994e7b4..f0846497235 100644 --- a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/YarnScheduler.java +++ b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/YarnScheduler.java @@ -79,6 +79,14 @@ public interface YarnScheduler extends EventHandler { @Stable public Resource getMaximumResourceCapability(); + /** + * Get the number of nodes available in the cluster. + * @return the number of available nodes. + */ + @Public + @Stable + public int getNumClusterNodes(); + /** * The main api between the ApplicationMaster and the Scheduler. * The ApplicationMaster is updating his future resource requirements diff --git a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacityScheduler.java b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacityScheduler.java index 5f080f83645..8aa85c3ab11 100644 --- a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacityScheduler.java +++ b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacityScheduler.java @@ -159,6 +159,7 @@ implements ResourceScheduler, CapacitySchedulerContext { return maximumAllocation; } + @Override public synchronized int getNumClusterNodes() { return numNodeManagers; } diff --git a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fifo/FifoScheduler.java b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fifo/FifoScheduler.java index c90566bd5b5..8e274956b57 100644 --- a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fifo/FifoScheduler.java +++ b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fifo/FifoScheduler.java @@ -19,7 +19,6 @@ package org.apache.hadoop.yarn.server.resourcemanager.scheduler.fifo; import java.io.IOException; -import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; @@ -36,7 +35,6 @@ import org.apache.hadoop.classification.InterfaceAudience.LimitedPrivate; import org.apache.hadoop.classification.InterfaceAudience.Private; import org.apache.hadoop.classification.InterfaceStability.Evolving; import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.net.NetUtils; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.authorize.AccessControlList; import org.apache.hadoop.yarn.Lock; @@ -179,6 +177,11 @@ public class FifoScheduler implements ResourceScheduler { return minimumAllocation; } + @Override + public int getNumClusterNodes() { + return nodes.size(); + } + @Override public Resource getMaximumResourceCapability() { return maximumAllocation; From 8626f1bc6c0fd696d02f46de310909b0d6919aba Mon Sep 17 00:00:00 2001 From: Vinod Kumar Vavilapalli Date: Wed, 21 Dec 2011 00:06:06 +0000 Subject: [PATCH 07/16] MAPREDUCE-3588. Fixed bin/yarn which was broken by MAPREDUCE-3366 so that yarn daemons can start. Contributed by Arun C Murthy. git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1221533 13f79535-47bb-0310-9956-ffa450edef68 --- hadoop-mapreduce-project/CHANGES.txt | 3 +++ hadoop-mapreduce-project/hadoop-yarn/bin/yarn | 2 ++ 2 files changed, 5 insertions(+) diff --git a/hadoop-mapreduce-project/CHANGES.txt b/hadoop-mapreduce-project/CHANGES.txt index 7a64f9cfce2..d6ea552ed00 100644 --- a/hadoop-mapreduce-project/CHANGES.txt +++ b/hadoop-mapreduce-project/CHANGES.txt @@ -346,6 +346,9 @@ Release 0.23.1 - Unreleased MAPREDUCE-3339. Fixed MR AM to stop considering node blacklisting after the number of nodes blacklisted crosses a threshold. (Siddharth Seth via vinodkv) + MAPREDUCE-3588. Fixed bin/yarn which was broken by MAPREDUCE-3366 so that + yarn daemons can start. (Arun C Murthy via vinodkv) + Release 0.23.0 - 2011-11-01 INCOMPATIBLE CHANGES diff --git a/hadoop-mapreduce-project/hadoop-yarn/bin/yarn b/hadoop-mapreduce-project/hadoop-yarn/bin/yarn index f5c8c1f8e84..4d19181b81a 100644 --- a/hadoop-mapreduce-project/hadoop-yarn/bin/yarn +++ b/hadoop-mapreduce-project/hadoop-yarn/bin/yarn @@ -141,6 +141,8 @@ if [ -d "$YARN_HOME/build/tools" ]; then CLASSPATH=${CLASSPATH}:$YARN_HOME/build/tools fi +CLASSPATH=${CLASSPATH}:$YARN_HOME/share/hadoop/mapreduce/* +CLASSPATH=${CLASSPATH}:$YARN_HOME/share/hadoop/mapreduce/lib/* # so that filenames w/ spaces are handled correctly in loops below IFS= From 45620eee687b1093c812873a99833dab068e43d2 Mon Sep 17 00:00:00 2001 From: Alejandro Abdelnur Date: Wed, 21 Dec 2011 02:46:04 +0000 Subject: [PATCH 08/16] HDFS-2646. Hadoop HttpFS introduced 4 findbug warnings. (tucu) git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1221572 13f79535-47bb-0310-9956-ffa450edef68 --- .../dev-support/findbugsExcludeFile.xml | 12 ++++++++++++ hadoop-hdfs-project/hadoop-hdfs-httpfs/pom.xml | 7 +++++++ .../java/org/apache/hadoop/lib/lang/XException.java | 2 +- .../apache/hadoop/lib/wsrs/InputStreamEntity.java | 5 ++++- hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt | 2 ++ 5 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 hadoop-hdfs-project/hadoop-hdfs-httpfs/dev-support/findbugsExcludeFile.xml diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/dev-support/findbugsExcludeFile.xml b/hadoop-hdfs-project/hadoop-hdfs-httpfs/dev-support/findbugsExcludeFile.xml new file mode 100644 index 00000000000..1f5a4f5bc4f --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/dev-support/findbugsExcludeFile.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/pom.xml b/hadoop-hdfs-project/hadoop-hdfs-httpfs/pom.xml index fbe56b6395c..956540dcf5d 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-httpfs/pom.xml +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/pom.xml @@ -384,6 +384,13 @@ + + org.codehaus.mojo + findbugs-maven-plugin + + ${basedir}/dev-support/findbugsExcludeFile.xml + + diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/lib/lang/XException.java b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/lib/lang/XException.java index 1fb2fb77668..767e3fa4cbb 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/lib/lang/XException.java +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/lib/lang/XException.java @@ -111,7 +111,7 @@ public class XException extends Exception { } template = sb.deleteCharAt(0).toString(); } - return error + ": " + MessageFormat.format(error.getTemplate(), args); + return error + ": " + MessageFormat.format(template, args); } /** diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/lib/wsrs/InputStreamEntity.java b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/lib/wsrs/InputStreamEntity.java index 336a62ce9e2..e5361a80efe 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/lib/wsrs/InputStreamEntity.java +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/lib/wsrs/InputStreamEntity.java @@ -42,7 +42,10 @@ public class InputStreamEntity implements StreamingOutput { @Override public void write(OutputStream os) throws IOException { - is.skip(offset); + long skipped = is.skip(offset); + if (skipped < offset) { + throw new IOException("Requested offset beyond stream size"); + } if (len == -1) { IOUtils.copyBytes(is, os, 4096, true); } else { diff --git a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt index 71ecda11604..60bb488370b 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt +++ b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt @@ -180,6 +180,8 @@ Trunk (unreleased changes) HDFS-2658. HttpFS introduced 70 javadoc warnings. (tucu) + HDFS-2646. Hadoop HttpFS introduced 4 findbug warnings. (tucu) + Release 0.23.1 - UNRELEASED INCOMPATIBLE CHANGES From 264d3b7dd0c81fe02baaa09b6e3aaad5ee6d191a Mon Sep 17 00:00:00 2001 From: Amar Kamat Date: Wed, 21 Dec 2011 02:58:00 +0000 Subject: [PATCH 09/16] MAPREDUCE-3349. Log rack-name in JobHistory for unsuccessful tasks. (Devaraj K and Amar Kamat via amarrk) git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1221578 13f79535-47bb-0310-9956-ffa450edef68 --- hadoop-mapreduce-project/CHANGES.txt | 3 + .../v2/app/job/impl/TaskAttemptImpl.java | 2 + .../src/main/avro/Events.avpr | 2 + .../jobhistory/JobHistoryParser.java | 3 +- .../jobhistory/MapAttemptFinishedEvent.java | 11 ++- .../ReduceAttemptFinishedEvent.java | 10 +- .../jobhistory/TaskAttemptFinishedEvent.java | 11 ++- ...askAttemptUnsuccessfulCompletionEvent.java | 18 +++- .../v2/hs/TestJobHistoryParsing.java | 89 ++++++++++++++++- .../apache/hadoop/mapred/JobInProgress.java | 10 +- .../jobhistory/TestJobHistoryEvents.java | 5 +- .../tools/rumen/TestRumenJobTraces.java | 33 ++++--- .../counters-test-trace.json.gz | Bin 2985 -> 3013 bytes .../dispatch-trace-output.json.gz | Bin 27850 -> 28317 bytes .../job-tracker-logs-topology-output | 91 ++++++++++++++++-- .../tools/rumen/HadoopLogsAnalyzer.java | 12 ++- .../apache/hadoop/tools/rumen/JobBuilder.java | 42 ++++++-- .../apache/hadoop/tools/rumen/ParsedHost.java | 14 ++- .../rumen/TaskAttempt20LineEventEmitter.java | 16 ++- .../hadoop/tools/rumen/TopologyBuilder.java | 39 +++++++- 20 files changed, 348 insertions(+), 63 deletions(-) diff --git a/hadoop-mapreduce-project/CHANGES.txt b/hadoop-mapreduce-project/CHANGES.txt index d6ea552ed00..44ee434d251 100644 --- a/hadoop-mapreduce-project/CHANGES.txt +++ b/hadoop-mapreduce-project/CHANGES.txt @@ -47,6 +47,9 @@ Trunk (unreleased changes) PB and Avro can all use it (Sanjay) BUG FIXES + MAPREDUCE-3349. Log rack-name in JobHistory for unsuccessful tasks. + (Devaraj K and Amar Kamat via amarrk) + MAPREDUCE-3412. Fix 'ant docs'. (amarrk) MAPREDUCE-3346. [Rumen] LoggedTaskAttempt#getHostName() returns null. diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TaskAttemptImpl.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TaskAttemptImpl.java index 3a52c11d2a5..46479ee142c 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TaskAttemptImpl.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TaskAttemptImpl.java @@ -926,6 +926,8 @@ public abstract class TaskAttemptImpl implements : taskAttempt.containerNodeId.getHost(), taskAttempt.containerNodeId == null ? -1 : taskAttempt.containerNodeId.getPort(), + taskAttempt.nodeRackName == null ? "UNKNOWN" + : taskAttempt.nodeRackName, StringUtils.join( LINE_SEPARATOR, taskAttempt.getDiagnostics()), taskAttempt .getProgressSplitBlock().burst()); diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/avro/Events.avpr b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/avro/Events.avpr index 34c40900e6b..22864a6fc2f 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/avro/Events.avpr +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/avro/Events.avpr @@ -175,6 +175,7 @@ {"name": "taskType", "type": "string"}, {"name": "taskStatus", "type": "string"}, {"name": "finishTime", "type": "long"}, + {"name": "rackname", "type": "string"}, {"name": "hostname", "type": "string"}, {"name": "state", "type": "string"}, {"name": "counters", "type": "JhCounters"} @@ -202,6 +203,7 @@ {"name": "finishTime", "type": "long"}, {"name": "hostname", "type": "string"}, {"name": "port", "type": "int"}, + {"name": "rackname", "type": "string"}, {"name": "status", "type": "string"}, {"name": "error", "type": "string"}, {"name": "clockSplits", "type": { "type": "array", "items": "int"}}, diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/JobHistoryParser.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/JobHistoryParser.java index a12db54afc5..c9be77c29d8 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/JobHistoryParser.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/JobHistoryParser.java @@ -224,7 +224,7 @@ public class JobHistoryParser { attemptInfo.counters = event.getCounters(); attemptInfo.hostname = event.getHostname(); attemptInfo.port = event.getPort(); - attemptInfo.rackname = event.getRackname(); + attemptInfo.rackname = event.getRackName(); } private void handleTaskAttemptFailedEvent( @@ -237,6 +237,7 @@ public class JobHistoryParser { attemptInfo.status = event.getTaskStatus(); attemptInfo.hostname = event.getHostname(); attemptInfo.port = event.getPort(); + attemptInfo.rackname = event.getRackName(); attemptInfo.shuffleFinishTime = event.getFinishTime(); attemptInfo.sortFinishTime = event.getFinishTime(); attemptInfo.mapFinishTime = event.getFinishTime(); diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/MapAttemptFinishedEvent.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/MapAttemptFinishedEvent.java index cdeecb0b987..5838c971f37 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/MapAttemptFinishedEvent.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/MapAttemptFinishedEvent.java @@ -68,7 +68,10 @@ public class MapAttemptFinishedEvent implements HistoryEvent { datum.finishTime = finishTime; datum.hostname = new Utf8(hostname); datum.port = port; - datum.rackname = new Utf8(rackName); + // This is needed for reading old jh files + if (rackName != null) { + datum.rackname = new Utf8(rackName); + } datum.state = new Utf8(state); datum.counters = EventWriter.toAvro(counters); @@ -139,8 +142,12 @@ public class MapAttemptFinishedEvent implements HistoryEvent { public String getHostname() { return datum.hostname.toString(); } /** Get the tracker rpc port */ public int getPort() { return datum.port; } + /** Get the rack name */ - public String getRackname() { return datum.rackname.toString(); } + public String getRackName() { + return datum.rackname == null ? null : datum.rackname.toString(); + } + /** Get the state string */ public String getState() { return datum.state.toString(); } /** Get the counters */ diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/ReduceAttemptFinishedEvent.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/ReduceAttemptFinishedEvent.java index 1eadb274cc9..99b2212316c 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/ReduceAttemptFinishedEvent.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/ReduceAttemptFinishedEvent.java @@ -69,7 +69,9 @@ public class ReduceAttemptFinishedEvent implements HistoryEvent { datum.finishTime = finishTime; datum.hostname = new Utf8(hostname); datum.port = port; - datum.rackname = new Utf8(rackName); + if (rackName != null) { + datum.rackname = new Utf8(rackName); + } datum.state = new Utf8(state); datum.counters = EventWriter.toAvro(counters); @@ -142,8 +144,12 @@ public class ReduceAttemptFinishedEvent implements HistoryEvent { public String getHostname() { return datum.hostname.toString(); } /** Get the tracker rpc port */ public int getPort() { return datum.port; } + /** Get the rack name of the node where the attempt ran */ - public String getRackName() { return datum.rackname.toString(); } + public String getRackName() { + return datum.rackname == null ? null : datum.rackname.toString(); + } + /** Get the state string */ public String getState() { return datum.state.toString(); } /** Get the counters for the attempt */ diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/TaskAttemptFinishedEvent.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/TaskAttemptFinishedEvent.java index fff9232e5a7..9938182fc6c 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/TaskAttemptFinishedEvent.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/TaskAttemptFinishedEvent.java @@ -51,13 +51,16 @@ public class TaskAttemptFinishedEvent implements HistoryEvent { */ public TaskAttemptFinishedEvent(TaskAttemptID id, TaskType taskType, String taskStatus, - long finishTime, + long finishTime, String rackName, String hostname, String state, Counters counters) { datum.taskid = new Utf8(id.getTaskID().toString()); datum.attemptId = new Utf8(id.toString()); datum.taskType = new Utf8(taskType.name()); datum.taskStatus = new Utf8(taskStatus); datum.finishTime = finishTime; + if (rackName != null) { + datum.rackname = new Utf8(rackName); + } datum.hostname = new Utf8(hostname); datum.state = new Utf8(state); datum.counters = EventWriter.toAvro(counters); @@ -86,6 +89,12 @@ public class TaskAttemptFinishedEvent implements HistoryEvent { public long getFinishTime() { return datum.finishTime; } /** Get the host where the attempt executed */ public String getHostname() { return datum.hostname.toString(); } + + /** Get the rackname where the attempt executed */ + public String getRackName() { + return datum.rackname == null ? null : datum.rackname.toString(); + } + /** Get the state string */ public String getState() { return datum.state.toString(); } /** Get the counters for the attempt */ diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/TaskAttemptUnsuccessfulCompletionEvent.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/TaskAttemptUnsuccessfulCompletionEvent.java index 56cb0e34633..066f0af6466 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/TaskAttemptUnsuccessfulCompletionEvent.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/TaskAttemptUnsuccessfulCompletionEvent.java @@ -47,6 +47,7 @@ public class TaskAttemptUnsuccessfulCompletionEvent implements HistoryEvent { * @param finishTime Finish time of the attempt * @param hostname Name of the host where the attempt executed * @param port rpc port for for the tracker + * @param rackName Name of the rack where the attempt executed * @param error Error string * @param allSplits the "splits", or a pixelated graph of various * measurable worker node state variables against progress. @@ -55,14 +56,17 @@ public class TaskAttemptUnsuccessfulCompletionEvent implements HistoryEvent { */ public TaskAttemptUnsuccessfulCompletionEvent (TaskAttemptID id, TaskType taskType, - String status, long finishTime, - String hostname, int port, String error, - int[][] allSplits) { + String status, long finishTime, + String hostname, int port, String rackName, + String error, int[][] allSplits) { datum.taskid = new Utf8(id.getTaskID().toString()); datum.taskType = new Utf8(taskType.name()); datum.attemptId = new Utf8(id.toString()); datum.finishTime = finishTime; datum.hostname = new Utf8(hostname); + if (rackName != null) { + datum.rackname = new Utf8(rackName); + } datum.port = port; datum.error = new Utf8(error); datum.status = new Utf8(status); @@ -99,7 +103,7 @@ public class TaskAttemptUnsuccessfulCompletionEvent implements HistoryEvent { (TaskAttemptID id, TaskType taskType, String status, long finishTime, String hostname, String error) { - this(id, taskType, status, finishTime, hostname, -1, error, null); + this(id, taskType, status, finishTime, hostname, -1, null, error, null); } TaskAttemptUnsuccessfulCompletionEvent() {} @@ -125,6 +129,12 @@ public class TaskAttemptUnsuccessfulCompletionEvent implements HistoryEvent { public String getHostname() { return datum.hostname.toString(); } /** Get the rpc port for the host where the attempt executed */ public int getPort() { return datum.port; } + + /** Get the rack name of the node where the attempt ran */ + public String getRackName() { + return datum.rackname == null ? null : datum.rackname.toString(); + } + /** Get the error string */ public String getError() { return datum.error.toString(); } /** Get the task status */ diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestJobHistoryParsing.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestJobHistoryParsing.java index 08563f0091c..3755eba9466 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestJobHistoryParsing.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestJobHistoryParsing.java @@ -44,10 +44,13 @@ import org.apache.hadoop.mapreduce.jobhistory.JobHistoryParser.TaskAttemptInfo; import org.apache.hadoop.mapreduce.jobhistory.JobHistoryParser.TaskInfo; import org.apache.hadoop.mapreduce.v2.api.records.JobId; import org.apache.hadoop.mapreduce.v2.api.records.JobState; +import org.apache.hadoop.mapreduce.v2.api.records.TaskAttemptId; import org.apache.hadoop.mapreduce.v2.app.MRApp; import org.apache.hadoop.mapreduce.v2.app.job.Job; import org.apache.hadoop.mapreduce.v2.app.job.Task; import org.apache.hadoop.mapreduce.v2.app.job.TaskAttempt; +import org.apache.hadoop.mapreduce.v2.app.job.event.TaskAttemptEvent; +import org.apache.hadoop.mapreduce.v2.app.job.event.TaskAttemptEventType; import org.apache.hadoop.mapreduce.v2.hs.TestJobHistoryEvents.MRAppWithHistory; import org.apache.hadoop.mapreduce.v2.jobhistory.FileNameIndexUtils; import org.apache.hadoop.mapreduce.v2.jobhistory.JobHistoryUtils; @@ -62,10 +65,12 @@ import org.junit.Test; public class TestJobHistoryParsing { private static final Log LOG = LogFactory.getLog(TestJobHistoryParsing.class); + private static final String RACK_NAME = "/MyRackName"; + public static class MyResolver implements DNSToSwitchMapping { @Override public List resolve(List names) { - return Arrays.asList(new String[]{"/MyRackName"}); + return Arrays.asList(new String[]{RACK_NAME}); } } @@ -172,7 +177,7 @@ public class TestJobHistoryParsing { // Verify rack-name Assert.assertEquals("rack-name is incorrect", taskAttemptInfo - .getRackname(), "/MyRackName"); + .getRackname(), RACK_NAME); } } @@ -217,9 +222,89 @@ public class TestJobHistoryParsing { Assert.assertEquals("Status does not match", "SUCCEEDED", jobSummaryElements.get("status")); } + + @Test + public void testHistoryParsingForFailedAttempts() throws Exception { + Configuration conf = new Configuration(); + conf + .setClass( + CommonConfigurationKeysPublic.NET_TOPOLOGY_NODE_SWITCH_MAPPING_IMPL_KEY, + MyResolver.class, DNSToSwitchMapping.class); + RackResolver.init(conf); + MRApp app = new MRAppWithHistoryWithFailedAttempt(2, 1, true, this.getClass().getName(), + true); + app.submit(conf); + Job job = app.getContext().getAllJobs().values().iterator().next(); + JobId jobId = job.getID(); + app.waitForState(job, JobState.SUCCEEDED); + + // make sure all events are flushed + app.waitForState(Service.STATE.STOPPED); + + String jobhistoryDir = JobHistoryUtils + .getHistoryIntermediateDoneDirForUser(conf); + JobHistory jobHistory = new JobHistory(); + jobHistory.init(conf); + + JobIndexInfo jobIndexInfo = jobHistory.getJobMetaInfo(jobId) + .getJobIndexInfo(); + String jobhistoryFileName = FileNameIndexUtils + .getDoneFileName(jobIndexInfo); + + Path historyFilePath = new Path(jobhistoryDir, jobhistoryFileName); + FSDataInputStream in = null; + FileContext fc = null; + try { + fc = FileContext.getFileContext(conf); + in = fc.open(fc.makeQualified(historyFilePath)); + } catch (IOException ioe) { + LOG.info("Can not open history file: " + historyFilePath, ioe); + throw (new Exception("Can not open History File")); + } + + JobHistoryParser parser = new JobHistoryParser(in); + JobInfo jobInfo = parser.parse(); + int noOffailedAttempts = 0; + Map allTasks = jobInfo.getAllTasks(); + for (Task task : job.getTasks().values()) { + TaskInfo taskInfo = allTasks.get(TypeConverter.fromYarn(task.getID())); + for (TaskAttempt taskAttempt : task.getAttempts().values()) { + TaskAttemptInfo taskAttemptInfo = taskInfo.getAllTaskAttempts().get( + TypeConverter.fromYarn((taskAttempt.getID()))); + // Verify rack-name for all task attempts + Assert.assertEquals("rack-name is incorrect", taskAttemptInfo + .getRackname(), RACK_NAME); + if (taskAttemptInfo.getTaskStatus().equals("FAILED")) { + noOffailedAttempts++; + } + } + } + Assert.assertEquals("No of Failed tasks doesn't match.", 2, noOffailedAttempts); + } + + static class MRAppWithHistoryWithFailedAttempt extends MRAppWithHistory { + + public MRAppWithHistoryWithFailedAttempt(int maps, int reduces, boolean autoComplete, + String testName, boolean cleanOnStart) { + super(maps, reduces, autoComplete, testName, cleanOnStart); + } + + @SuppressWarnings("unchecked") + @Override + protected void attemptLaunched(TaskAttemptId attemptID) { + if (attemptID.getTaskId().getId() == 0 && attemptID.getId() == 0) { + getContext().getEventHandler().handle( + new TaskAttemptEvent(attemptID, TaskAttemptEventType.TA_FAILMSG)); + } else { + getContext().getEventHandler().handle( + new TaskAttemptEvent(attemptID, TaskAttemptEventType.TA_DONE)); + } + } + } public static void main(String[] args) throws Exception { TestJobHistoryParsing t = new TestJobHistoryParsing(); t.testHistoryParsing(); + t.testHistoryParsingForFailedAttempts(); } } diff --git a/hadoop-mapreduce-project/src/java/org/apache/hadoop/mapred/JobInProgress.java b/hadoop-mapreduce-project/src/java/org/apache/hadoop/mapred/JobInProgress.java index fc8bdc624b7..791b92c82f7 100644 --- a/hadoop-mapreduce-project/src/java/org/apache/hadoop/mapred/JobInProgress.java +++ b/hadoop-mapreduce-project/src/java/org/apache/hadoop/mapred/JobInProgress.java @@ -2671,7 +2671,9 @@ public class JobInProgress { // Update jobhistory TaskTrackerStatus ttStatus = this.jobtracker.getTaskTrackerStatus(status.getTaskTracker()); - String trackerHostname = jobtracker.getNode(ttStatus.getHost()).toString(); + Node node = jobtracker.getNode(ttStatus.getHost()); + String trackerHostname = node.getName(); + String trackerRackName = node.getParent().getName(); TaskType taskType = getTaskType(tip); TaskAttemptStartedEvent tse = new TaskAttemptStartedEvent( @@ -2685,7 +2687,7 @@ public class JobInProgress { MapAttemptFinishedEvent mfe = new MapAttemptFinishedEvent( statusAttemptID, taskType, TaskStatus.State.SUCCEEDED.toString(), status.getMapFinishTime(), - status.getFinishTime(), trackerHostname, -1, "", + status.getFinishTime(), trackerHostname, -1, trackerRackName, status.getStateString(), new org.apache.hadoop.mapreduce.Counters(status.getCounters()), tip.getSplits(statusAttemptID).burst() @@ -2698,7 +2700,7 @@ public class JobInProgress { statusAttemptID, taskType, TaskStatus.State.SUCCEEDED.toString(), status.getShuffleFinishTime(), status.getSortFinishTime(), status.getFinishTime(), - trackerHostname, -1, "", status.getStateString(), + trackerHostname, -1, trackerRackName, status.getStateString(), new org.apache.hadoop.mapreduce.Counters(status.getCounters()), tip.getSplits(statusAttemptID).burst() ); @@ -3208,7 +3210,7 @@ public class JobInProgress { (taskid, taskType, taskStatus.getRunState().toString(), finishTime, - taskTrackerHostName, -1, diagInfo, + taskTrackerHostName, -1, null, diagInfo, splits.burst()); jobHistory.logEvent(tue, taskid.getJobID()); diff --git a/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/mapreduce/jobhistory/TestJobHistoryEvents.java b/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/mapreduce/jobhistory/TestJobHistoryEvents.java index 27c149de462..c297ac06e3f 100644 --- a/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/mapreduce/jobhistory/TestJobHistoryEvents.java +++ b/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/mapreduce/jobhistory/TestJobHistoryEvents.java @@ -83,7 +83,7 @@ public class TestJobHistoryEvents extends TestCase { for (TaskType t : types) { TaskAttemptUnsuccessfulCompletionEvent tauce = new TaskAttemptUnsuccessfulCompletionEvent - (id, t, state, 0L, "", -1, "", NULL_SPLITS_ARRAY); + (id, t, state, 0L, "", -1, "", "", NULL_SPLITS_ARRAY); assertEquals(expected, tauce.getEventType()); } } @@ -132,7 +132,8 @@ public class TestJobHistoryEvents extends TestCase { for (TaskType t : types) { TaskAttemptFinishedEvent tafe = new TaskAttemptFinishedEvent(id, t, - TaskStatus.State.SUCCEEDED.toString(), 0L, "", "", new Counters()); + TaskStatus.State.SUCCEEDED.toString(), 0L, "", "", "", + new Counters()); assertEquals(expected, tafe.getEventType()); } } diff --git a/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/tools/rumen/TestRumenJobTraces.java b/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/tools/rumen/TestRumenJobTraces.java index cb383e760d5..5b563e69e5d 100644 --- a/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/tools/rumen/TestRumenJobTraces.java +++ b/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/tools/rumen/TestRumenJobTraces.java @@ -42,6 +42,7 @@ import org.apache.hadoop.mapreduce.TaskAttemptID; import org.apache.hadoop.mapreduce.TaskCounter; import org.apache.hadoop.mapreduce.TaskID; import org.apache.hadoop.mapreduce.TaskType; +import org.apache.hadoop.mapreduce.TypeConverter; import org.apache.hadoop.mapreduce.TestNoJobSetupCleanup.MyOutputFormat; import org.apache.hadoop.mapreduce.jobhistory.HistoryEvent; import org.apache.hadoop.mapreduce.jobhistory.JobHistory; @@ -49,6 +50,9 @@ import org.apache.hadoop.mapreduce.jobhistory.TaskAttemptFinishedEvent; import org.apache.hadoop.mapreduce.jobhistory.TaskAttemptUnsuccessfulCompletionEvent; import org.apache.hadoop.mapreduce.jobhistory.TaskStartedEvent; import org.apache.hadoop.mapreduce.server.tasktracker.TTConfig; +import org.apache.hadoop.mapreduce.v2.api.records.JobId; +import org.apache.hadoop.mapreduce.v2.jobhistory.FileNameIndexUtils; +import org.apache.hadoop.mapreduce.v2.jobhistory.JobIndexInfo; import org.apache.hadoop.tools.rumen.TraceBuilder.MyOptions; import org.apache.hadoop.util.Tool; import org.apache.hadoop.util.ToolRunner; @@ -246,8 +250,10 @@ public class TestRumenJobTraces { } /** - * Validate the parsing of given history file name. Also validate the history - * file name suffixed with old/stale file suffix. + * Validate the parsing of given history file name. + * + * TODO: Also validate the history file name suffixed with old/stale file + * suffix. * @param jhFileName job history file path * @param jid JobID */ @@ -257,13 +263,7 @@ public class TestRumenJobTraces { JobID.forName(JobHistoryUtils.extractJobID(jhFileName.getName())); assertEquals("TraceBuilder failed to parse the current JH filename" + jhFileName, jid, extractedJID); - // test jobhistory filename with old/stale file suffix - jhFileName = jhFileName.suffix(JobHistory.getOldFileSuffix("123")); - extractedJID = - JobID.forName(JobHistoryUtils.extractJobID(jhFileName.getName())); - assertEquals("TraceBuilder failed to parse the current JH filename" - + "(old-suffix):" + jhFileName, - jid, extractedJID); + //TODO test jobhistory filename with old/stale file suffix } /** @@ -318,8 +318,9 @@ public class TestRumenJobTraces { .makeQualified(lfs.getUri(), lfs.getWorkingDirectory()); // Check if current jobhistory filenames are detected properly - Path jhFilename = org.apache.hadoop.mapreduce.v2.jobhistory.JobHistoryUtils - .getStagingJobHistoryFile(rootInputDir, jid.toString(), 1); + JobId jobId = TypeConverter.toYarn(jid); + JobIndexInfo info = new JobIndexInfo(0L, 0L, "", "", jobId, 0, 0, ""); + Path jhFilename = new Path(FileNameIndexUtils.getDoneFileName(info)); validateHistoryFileNameParsing(jhFilename, jid); // Check if Pre21 V1 jophistory file names are detected properly @@ -932,18 +933,18 @@ public class TestRumenJobTraces { subject.process(new TaskAttemptFinishedEvent(TaskAttemptID .forName("attempt_200904211745_0003_m_000004_0"), TaskType .valueOf("MAP"), "STATUS", 1234567890L, - "/194\\.6\\.134\\.64/cluster50261\\.secondleveldomain\\.com", + "/194\\.6\\.134\\.64", "cluster50261\\.secondleveldomain\\.com", "SUCCESS", null)); subject.process(new TaskAttemptUnsuccessfulCompletionEvent (TaskAttemptID.forName("attempt_200904211745_0003_m_000004_1"), TaskType.valueOf("MAP"), "STATUS", 1234567890L, - "/194\\.6\\.134\\.80/cluster50262\\.secondleveldomain\\.com", - -1, "MACHINE_EXPLODED", splits)); + "cluster50262\\.secondleveldomain\\.com", + -1, "/194\\.6\\.134\\.80", "MACHINE_EXPLODED", splits)); subject.process(new TaskAttemptUnsuccessfulCompletionEvent (TaskAttemptID.forName("attempt_200904211745_0003_m_000004_2"), TaskType.valueOf("MAP"), "STATUS", 1234567890L, - "/194\\.6\\.134\\.80/cluster50263\\.secondleveldomain\\.com", - -1, "MACHINE_EXPLODED", splits)); + "cluster50263\\.secondleveldomain\\.com", + -1, "/194\\.6\\.134\\.80", "MACHINE_EXPLODED", splits)); subject.process(new TaskStartedEvent(TaskID .forName("task_200904211745_0003_m_000004"), 1234567890L, TaskType .valueOf("MAP"), diff --git a/hadoop-mapreduce-project/src/test/tools/data/rumen/small-trace-test/counters-test-trace.json.gz b/hadoop-mapreduce-project/src/test/tools/data/rumen/small-trace-test/counters-test-trace.json.gz index c1eb1b3d070a23df9749008ffa71d06c6e0f7174..47ee42869198b842622e9b35cf1811f46f9a6f5e 100644 GIT binary patch literal 3013 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 HK864QJBaGc delta 2984 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 ew_rzWhW${#VGS?qH~b$=(-9KIg#ZATH`|2( diff --git a/hadoop-mapreduce-project/src/test/tools/data/rumen/small-trace-test/dispatch-trace-output.json.gz b/hadoop-mapreduce-project/src/test/tools/data/rumen/small-trace-test/dispatch-trace-output.json.gz index 98cfbdb844b80a04f4d3ec21d0540552c8839bd8..355e143bb76b80d5e48901603e59bb3ce0e539b9 100644 GIT binary patch literal 28317 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%0Am<*d&e58>inI}*)O2t|=cioWL#}l$ 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%0D}ZQ*{`I*95*? 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 locs = task.getPreferredLocations(); @@ -1470,9 +1472,13 @@ public class HadoopLogsAnalyzer extends Configured implements Tool { } } - ParsedHost host = getAndRecordParsedHost(hostName); - if (host != null) { - attempt.setHostName(host.getNodeName(), host.getRackName()); + if (hostName != null) { + ParsedHost host = getAndRecordParsedHost(hostName); + if (host != null) { + attempt.setHostName(host.getNodeName(), host.getRackName()); + } else { + attempt.setHostName(hostName, null); + } } if (attemptID != null) { diff --git a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/JobBuilder.java b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/JobBuilder.java index 6487d10c4f4..30597d3f5c4 100644 --- a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/JobBuilder.java +++ b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/JobBuilder.java @@ -463,10 +463,11 @@ public class JobBuilder { } attempt.setResult(getPre21Value(event.getTaskStatus())); - ParsedHost parsedHost = getAndRecordParsedHost(event.getHostname()); - - if (parsedHost != null) { - attempt.setLocation(parsedHost.makeLoggedLocation()); + attempt.setHostName(event.getHostname(), event.getRackName()); + ParsedHost pHost = + getAndRecordParsedHost(event.getRackName(), event.getHostname()); + if (pHost != null) { + attempt.setLocation(pHost.makeLoggedLocation()); } attempt.setFinishTime(event.getFinishTime()); @@ -495,8 +496,10 @@ public class JobBuilder { return; } attempt.setResult(getPre21Value(event.getTaskStatus())); - attempt.setLocation(getAndRecordParsedHost(event.getHostname()) - .makeLoggedLocation()); + ParsedHost pHost = getAndRecordParsedHost(event.getRackName(), event.getHostname()); + if (pHost != null) { + attempt.setLocation(pHost.makeLoggedLocation()); + } attempt.setFinishTime(event.getFinishTime()); attempt .incorporateCounters(((TaskAttemptFinished) event.getDatum()).counters); @@ -512,6 +515,11 @@ public class JobBuilder { } attempt.setResult(getPre21Value(event.getTaskStatus())); attempt.setHostName(event.getHostname(), event.getRackName()); + ParsedHost pHost = + getAndRecordParsedHost(event.getRackName(), event.getHostname()); + if (pHost != null) { + attempt.setLocation(pHost.makeLoggedLocation()); + } // XXX There may be redundant location info available in the event. // We might consider extracting it from this event. Currently this @@ -535,8 +543,14 @@ public class JobBuilder { return; } attempt.setResult(getPre21Value(event.getTaskStatus())); - attempt.setHostName(event.getHostname(), event.getRackname()); + attempt.setHostName(event.getHostname(), event.getRackName()); + ParsedHost pHost = + getAndRecordParsedHost(event.getRackName(), event.getHostname()); + if (pHost != null) { + attempt.setLocation(pHost.makeLoggedLocation()); + } + // XXX There may be redundant location info available in the event. // We might consider extracting it from this event. Currently this // is redundant, but making this will add future-proofing. @@ -665,7 +679,19 @@ public class JobBuilder { } private ParsedHost getAndRecordParsedHost(String hostName) { - ParsedHost result = ParsedHost.parse(hostName); + return getAndRecordParsedHost(null, hostName); + } + + private ParsedHost getAndRecordParsedHost(String rackName, String hostName) { + ParsedHost result = null; + if (rackName == null) { + // for old (pre-23) job history files where hostname was represented as + // /rackname/hostname + result = ParsedHost.parse(hostName); + } else { + // for new (post-23) job history files + result = new ParsedHost(rackName, hostName); + } if (result != null) { ParsedHost canonicalResult = allHosts.get(result); diff --git a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedHost.java b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedHost.java index 082f6844fb2..ef2dda78324 100644 --- a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedHost.java +++ b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedHost.java @@ -71,11 +71,17 @@ public class ParsedHost { return new ParsedHost(matcher.group(1), matcher.group(2)); } + private String process(String name) { + return name == null + ? null + : name.startsWith("/") ? name.substring(1) : name; + } + public ParsedHost(LoggedLocation loc) { List coordinates = loc.getLayers(); - rackName = coordinates.get(0).getRackName(); - nodeName = coordinates.get(1).getHostName(); + rackName = process(coordinates.get(0).getRackName()); + nodeName = process(coordinates.get(1).getHostName()); } LoggedLocation makeLoggedLocation() { @@ -101,8 +107,8 @@ public class ParsedHost { // expects the broadest name first ParsedHost(String rackName, String nodeName) { - this.rackName = rackName; - this.nodeName = nodeName; + this.rackName = process(rackName); + this.nodeName = process(nodeName); } @Override diff --git a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/TaskAttempt20LineEventEmitter.java b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/TaskAttempt20LineEventEmitter.java index 4bf25ae1f2b..e4da6ccc624 100644 --- a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/TaskAttempt20LineEventEmitter.java +++ b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/TaskAttempt20LineEventEmitter.java @@ -108,9 +108,12 @@ public abstract class TaskAttempt20LineEventEmitter extends HistoryEventEmitter TaskAttempt20LineEventEmitter that = (TaskAttempt20LineEventEmitter) thatg; + ParsedHost pHost = ParsedHost.parse(hostName); + return new TaskAttemptFinishedEvent(taskAttemptID, that.originalTaskType, status, Long.parseLong(finishTime), - hostName, state, maybeParseCounters(counters)); + pHost.getRackName(), pHost.getNodeName(), state, + maybeParseCounters(counters)); } return null; @@ -138,10 +141,19 @@ public abstract class TaskAttempt20LineEventEmitter extends HistoryEventEmitter TaskAttempt20LineEventEmitter that = (TaskAttempt20LineEventEmitter) thatg; + ParsedHost pHost = ParsedHost.parse(hostName); + String rackName = null; + + // Earlier versions of MR logged on hostnames (without rackname) for + // unsuccessful attempts + if (pHost != null) { + rackName = pHost.getRackName(); + hostName = pHost.getNodeName(); + } return new TaskAttemptUnsuccessfulCompletionEvent (taskAttemptID, that.originalTaskType, status, Long.parseLong(finishTime), - hostName, -1, error, null); + hostName, -1, rackName, error, null); } return null; diff --git a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/TopologyBuilder.java b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/TopologyBuilder.java index 4ab1fa6ad9d..7cef6d19092 100644 --- a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/TopologyBuilder.java +++ b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/TopologyBuilder.java @@ -25,6 +25,8 @@ import java.util.StringTokenizer; import org.apache.hadoop.mapreduce.jobhistory.HistoryEvent; import org.apache.hadoop.mapreduce.jobhistory.TaskAttemptFinishedEvent; import org.apache.hadoop.mapreduce.jobhistory.TaskAttemptUnsuccessfulCompletionEvent; +import org.apache.hadoop.mapreduce.jobhistory.MapAttemptFinishedEvent; +import org.apache.hadoop.mapreduce.jobhistory.ReduceAttemptFinishedEvent; import org.apache.hadoop.mapreduce.jobhistory.TaskStartedEvent; /** @@ -46,6 +48,10 @@ public class TopologyBuilder { processTaskAttemptUnsuccessfulCompletionEvent((TaskAttemptUnsuccessfulCompletionEvent) event); } else if (event instanceof TaskStartedEvent) { processTaskStartedEvent((TaskStartedEvent) event); + } else if (event instanceof MapAttemptFinishedEvent) { + processMapAttemptFinishedEvent((MapAttemptFinishedEvent) event); + } else if (event instanceof ReduceAttemptFinishedEvent) { + processReduceAttemptFinishedEvent((ReduceAttemptFinishedEvent) event); } // I do NOT expect these if statements to be exhaustive. @@ -78,15 +84,40 @@ public class TopologyBuilder { private void processTaskAttemptUnsuccessfulCompletionEvent( TaskAttemptUnsuccessfulCompletionEvent event) { - recordParsedHost(event.getHostname()); + recordParsedHost(event.getHostname(), event.getRackName()); } private void processTaskAttemptFinishedEvent(TaskAttemptFinishedEvent event) { - recordParsedHost(event.getHostname()); + recordParsedHost(event.getHostname(), event.getRackName()); } - private void recordParsedHost(String hostName) { - ParsedHost result = ParsedHost.parse(hostName); + private void processMapAttemptFinishedEvent(MapAttemptFinishedEvent event) { + recordParsedHost(event.getHostname(), event.getRackName()); + } + + private void processReduceAttemptFinishedEvent(ReduceAttemptFinishedEvent event) { + recordParsedHost(event.getHostname(), event.getRackName()); + } + + private void recordParsedHost(String hostName, String rackName) { + if (hostName == null) { + return; + } + ParsedHost result = null; + if (rackName == null) { + result = ParsedHost.parse(hostName); + } else { + result = new ParsedHost(rackName, hostName); + } + + + if (result != null && !allHosts.contains(result)) { + allHosts.add(result); + } + } + + private void recordParsedHost(String nodeName) { + ParsedHost result = ParsedHost.parse(nodeName); if (result != null && !allHosts.contains(result)) { allHosts.add(result); From 8fe3dd3feace207e8de94bd872ac044b5bdaf5ca Mon Sep 17 00:00:00 2001 From: Alejandro Abdelnur Date: Wed, 21 Dec 2011 03:03:16 +0000 Subject: [PATCH 10/16] HDFS-2657. TestHttpFSServer and TestServerWebApp are failing on trunk. (tucu) git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1221580 13f79535-47bb-0310-9956-ffa450edef68 --- .../fs/http/server/TestHttpFSServer.java | 3 ++ .../service/security/DummyGroupMapping.java | 34 +++++++++++++++++++ ...roperties => testserverwebapp1.properties} | 0 ...roperties => testserverwebapp2.properties} | 2 -- hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt | 3 ++ 5 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/lib/service/security/DummyGroupMapping.java rename hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/resources/{TestServerWebApp1.properties => testserverwebapp1.properties} (100%) rename hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/resources/{TestServerWebApp2.properties => testserverwebapp2.properties} (99%) diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/server/TestHttpFSServer.java b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/server/TestHttpFSServer.java index 3247c14fb47..faac97de1a0 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/server/TestHttpFSServer.java +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/server/TestHttpFSServer.java @@ -20,8 +20,10 @@ package org.apache.hadoop.fs.http.server; import junit.framework.Assert; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.CommonConfigurationKeys; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; +import org.apache.hadoop.lib.service.security.DummyGroupMapping; import org.apache.hadoop.test.HFSTestCase; import org.apache.hadoop.test.HadoopUsersConfTestHelper; import org.apache.hadoop.test.TestDir; @@ -66,6 +68,7 @@ public class TestHttpFSServer extends HFSTestCase { String fsDefaultName = TestHdfsHelper.getHdfsConf().get("fs.default.name"); Configuration conf = new Configuration(false); conf.set("httpfs.hadoop.conf:fs.default.name", fsDefaultName); + conf.set("httpfs.groups." + CommonConfigurationKeys.HADOOP_SECURITY_GROUP_MAPPING, DummyGroupMapping.class.getName()); File hoopSite = new File(new File(homeDir, "conf"), "httpfs-site.xml"); OutputStream os = new FileOutputStream(hoopSite); conf.writeXml(os); diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/lib/service/security/DummyGroupMapping.java b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/lib/service/security/DummyGroupMapping.java new file mode 100644 index 00000000000..544dc14ce88 --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/lib/service/security/DummyGroupMapping.java @@ -0,0 +1,34 @@ +package org.apache.hadoop.lib.service.security; + +import org.apache.hadoop.security.GroupMappingServiceProvider; +import org.apache.hadoop.test.HadoopUsersConfTestHelper; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class DummyGroupMapping implements GroupMappingServiceProvider { + + @Override + @SuppressWarnings("unchecked") + public List getGroups(String user) throws IOException { + if (user.equals("root")) { + return Arrays.asList("admin"); + } + else if (user.equals("nobody")) { + return Arrays.asList("nobody"); + } else { + String[] groups = HadoopUsersConfTestHelper.getHadoopUserGroups(user); + return (groups != null) ? Arrays.asList(groups) : Collections.EMPTY_LIST; + } + } + + @Override + public void cacheGroupsRefresh() throws IOException { + } + + @Override + public void cacheGroupsAdd(List groups) throws IOException { + } +} diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/resources/TestServerWebApp1.properties b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/resources/testserverwebapp1.properties similarity index 100% rename from hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/resources/TestServerWebApp1.properties rename to hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/resources/testserverwebapp1.properties diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/resources/TestServerWebApp2.properties b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/resources/testserverwebapp2.properties similarity index 99% rename from hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/resources/TestServerWebApp2.properties rename to hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/resources/testserverwebapp2.properties index 33fd8317850..84f97bba9e3 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/resources/TestServerWebApp2.properties +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/resources/testserverwebapp2.properties @@ -11,5 +11,3 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# -# diff --git a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt index 60bb488370b..c6aedddd59a 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt +++ b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt @@ -182,6 +182,9 @@ Trunk (unreleased changes) HDFS-2646. Hadoop HttpFS introduced 4 findbug warnings. (tucu) + HDFS-2657. TestHttpFSServer and TestServerWebApp are failing on trunk. + (tucu) + Release 0.23.1 - UNRELEASED INCOMPATIBLE CHANGES From 6b4f40cbf913c048fb33da6c5d8271cd77766d0a Mon Sep 17 00:00:00 2001 From: Alejandro Abdelnur Date: Wed, 21 Dec 2011 05:26:20 +0000 Subject: [PATCH 11/16] HttpFS server should check that upload requests have correct content-type. (tucu) git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1221616 13f79535-47bb-0310-9956-ffa450edef68 --- .../fs/http/client/HttpFSFileSystem.java | 4 +- .../server/CheckUploadContentTypeFilter.java | 112 ++++++++++++++++++ .../src/main/webapp/WEB-INF/web.xml | 10 ++ .../TestCheckUploadContentTypeFilter.java | 91 ++++++++++++++ hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt | 3 + 5 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/fs/http/server/CheckUploadContentTypeFilter.java create mode 100644 hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/server/TestCheckUploadContentTypeFilter.java 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 520c7325fa1..03c8548813c 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 @@ -98,6 +98,8 @@ public class HttpFSFileSystem extends FileSystem { public static final String SET_REPLICATION_JSON = "boolean"; + public static final String UPLOAD_CONTENT_TYPE= "application/octet-stream"; + public static enum FILE_TYPE { FILE, DIRECTORY, SYMLINK; @@ -459,7 +461,7 @@ public class HttpFSFileSystem extends FileSystem { String location = conn.getHeaderField("Location"); if (location != null) { conn = getConnection(new URL(location), method); - conn.setRequestProperty("Content-Type", "application/octet-stream"); + conn.setRequestProperty("Content-Type", UPLOAD_CONTENT_TYPE); try { OutputStream os = new BufferedOutputStream(conn.getOutputStream(), bufferSize); return new HttpFSDataOutputStream(conn, os, expectedStatus, statistics); diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/fs/http/server/CheckUploadContentTypeFilter.java b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/fs/http/server/CheckUploadContentTypeFilter.java new file mode 100644 index 00000000000..7e73666f58c --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/fs/http/server/CheckUploadContentTypeFilter.java @@ -0,0 +1,112 @@ +/** + * 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.fs.http.server; + + +import org.apache.hadoop.fs.http.client.HttpFSFileSystem; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.InetAddress; +import java.util.HashSet; +import java.util.Set; + +/** + * Filter that Enforces the content-type to be application/octet-stream for + * POST and PUT requests. + */ +public class CheckUploadContentTypeFilter implements Filter { + + private static final Set UPLOAD_OPERATIONS = new HashSet(); + + static { + UPLOAD_OPERATIONS.add(HttpFSFileSystem.PostOpValues.APPEND.toString()); + UPLOAD_OPERATIONS.add(HttpFSFileSystem.PutOpValues.CREATE.toString()); + } + + /** + * Initializes the filter. + *

+ * This implementation is a NOP. + * + * @param config filter configuration. + * + * @throws ServletException thrown if the filter could not be initialized. + */ + @Override + public void init(FilterConfig config) throws ServletException { + } + + /** + * Enforces the content-type to be application/octet-stream for + * POST and PUT requests. + * + * @param request servlet request. + * @param response servlet response. + * @param chain filter chain. + * + * @throws IOException thrown if an IO error occurrs. + * @throws ServletException thrown if a servet error occurrs. + */ + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) + throws IOException, ServletException { + boolean contentTypeOK = true; + HttpServletRequest httpReq = (HttpServletRequest) request; + HttpServletResponse httpRes = (HttpServletResponse) response; + String method = httpReq.getMethod(); + if (method.equals("PUT") || method.equals("POST")) { + String op = httpReq.getParameter(HttpFSFileSystem.OP_PARAM); + if (op != null && UPLOAD_OPERATIONS.contains(op.toUpperCase())) { + if ("true".equalsIgnoreCase(httpReq.getParameter(HttpFSParams.DataParam.NAME))) { + String contentType = httpReq.getContentType(); + contentTypeOK = + HttpFSFileSystem.UPLOAD_CONTENT_TYPE.equalsIgnoreCase(contentType); + } + } + } + if (contentTypeOK) { + chain.doFilter(httpReq, httpRes); + } + else { + httpRes.sendError(HttpServletResponse.SC_BAD_REQUEST, + "Data upload requests must have content-type set to '" + + HttpFSFileSystem.UPLOAD_CONTENT_TYPE + "'"); + + } + } + + /** + * Destroys the filter. + *

+ * This implementation is a NOP. + */ + @Override + public void destroy() { + } + +} diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/webapp/WEB-INF/web.xml b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/webapp/WEB-INF/web.xml index 3ba374e3696..4d5e976fc5a 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/webapp/WEB-INF/web.xml +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/webapp/WEB-INF/web.xml @@ -60,6 +60,11 @@ org.apache.hadoop.lib.servlet.HostnameFilter + + checkUploadContentType + org.apache.hadoop.fs.http.server.CheckUploadContentTypeFilter + + fsReleaseFilter org.apache.hadoop.fs.http.server.HttpFSReleaseFilter @@ -80,6 +85,11 @@ * + + checkUploadContentType + * + + fsReleaseFilter * diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/server/TestCheckUploadContentTypeFilter.java b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/server/TestCheckUploadContentTypeFilter.java new file mode 100644 index 00000000000..89497a4e043 --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/server/TestCheckUploadContentTypeFilter.java @@ -0,0 +1,91 @@ +/** + * 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.fs.http.server; + +import org.apache.hadoop.fs.http.client.HttpFSFileSystem; +import org.junit.Test; +import org.mockito.Mockito; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class TestCheckUploadContentTypeFilter { + + @Test + public void putUpload() throws Exception { + test("PUT", HttpFSFileSystem.PutOpValues.CREATE.toString(), "application/octet-stream", true, false); + } + + @Test + public void postUpload() throws Exception { + test("POST", HttpFSFileSystem.PostOpValues.APPEND.toString(), "APPLICATION/OCTET-STREAM", true, false); + } + + @Test + public void putUploadWrong() throws Exception { + test("PUT", HttpFSFileSystem.PutOpValues.CREATE.toString(), "plain/text", false, false); + test("PUT", HttpFSFileSystem.PutOpValues.CREATE.toString(), "plain/text", true, true); + } + + @Test + public void postUploadWrong() throws Exception { + test("POST", HttpFSFileSystem.PostOpValues.APPEND.toString(), "plain/text", false, false); + test("POST", HttpFSFileSystem.PostOpValues.APPEND.toString(), "plain/text", true, true); + } + + @Test + public void getOther() throws Exception { + test("GET", HttpFSFileSystem.GetOpValues.GETHOMEDIR.toString(), "plain/text", false, false); + } + + @Test + public void putOther() throws Exception { + test("PUT", HttpFSFileSystem.PutOpValues.MKDIRS.toString(), "plain/text", false, false); + } + + private void test(String method, String operation, String contentType, + boolean upload, boolean error) throws Exception { + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + HttpServletResponse response = Mockito.mock(HttpServletResponse.class); + Mockito.reset(request); + Mockito.when(request.getMethod()).thenReturn(method); + Mockito.when(request.getParameter(HttpFSFileSystem.OP_PARAM)).thenReturn(operation); + Mockito.when(request.getParameter(HttpFSParams.DataParam.NAME)). + thenReturn(Boolean.toString(upload)); + Mockito.when(request.getContentType()).thenReturn(contentType); + + FilterChain chain = Mockito.mock(FilterChain.class); + + Filter filter = new CheckUploadContentTypeFilter(); + + filter.doFilter(request, response, chain); + + if (error) { + Mockito.verify(response).sendError(Mockito.eq(HttpServletResponse.SC_BAD_REQUEST), + Mockito.contains("Data upload")); + } + else { + Mockito.verify(chain).doFilter(request, response); + } + } + + +} diff --git a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt index c6aedddd59a..df916217d0c 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt +++ b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt @@ -185,6 +185,9 @@ Trunk (unreleased changes) HDFS-2657. TestHttpFSServer and TestServerWebApp are failing on trunk. (tucu) + HttpFS server should check that upload requests have correct + content-type. (tucu) + Release 0.23.1 - UNRELEASED INCOMPATIBLE CHANGES From 55ad01711e61e1df576c3492e91b3c71fd23671d Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Wed, 21 Dec 2011 19:34:09 +0000 Subject: [PATCH 12/16] HADOOP-7890. Redirect hadoop script's deprecation message to stderr. (Koji Knoguchi via mahadev) git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1221849 13f79535-47bb-0310-9956-ffa450edef68 --- hadoop-common-project/hadoop-common/CHANGES.txt | 3 +++ hadoop-common-project/hadoop-common/src/main/bin/hadoop | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt index ddc2c46495f..5e704f153f4 100644 --- a/hadoop-common-project/hadoop-common/CHANGES.txt +++ b/hadoop-common-project/hadoop-common/CHANGES.txt @@ -189,6 +189,9 @@ Release 0.23.1 - Unreleased HADOOP-7912. test-patch should run eclipse:eclipse to verify that it does not break again. (Robert Joseph Evans via tomwhite) + HADOOP-7890. Redirect hadoop script's deprecation message to stderr. + (Koji Knoguchi via mahadev) + OPTIMIZATIONS BUG FIXES diff --git a/hadoop-common-project/hadoop-common/src/main/bin/hadoop b/hadoop-common-project/hadoop-common/src/main/bin/hadoop index 9e92b5c2cea..4ca78972aa3 100755 --- a/hadoop-common-project/hadoop-common/src/main/bin/hadoop +++ b/hadoop-common-project/hadoop-common/src/main/bin/hadoop @@ -51,9 +51,9 @@ COMMAND=$1 case $COMMAND in #hdfs commands namenode|secondarynamenode|datanode|dfs|dfsadmin|fsck|balancer) - echo "DEPRECATED: Use of this script to execute hdfs command is deprecated." - echo "Instead use the hdfs command for it." - echo "" + echo "DEPRECATED: Use of this script to execute hdfs command is deprecated." 1>&2 + echo "Instead use the hdfs command for it." 1>&2 + echo "" 1>&2 #try to locate hdfs and if present, delegate to it. if [ -f "${HADOOP_HDFS_HOME}"/bin/hdfs ]; then exec "${HADOOP_HDFS_HOME}"/bin/hdfs $* From ef9b9748796471f80630695e5512a530b1227fd9 Mon Sep 17 00:00:00 2001 From: Vinod Kumar Vavilapalli Date: Wed, 21 Dec 2011 23:47:54 +0000 Subject: [PATCH 13/16] MAPREDUCE-3586. Modified CompositeService to avoid duplicate stop operations thereby solving race conditions in MR AM shutdown. (vinodkv) git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1221950 13f79535-47bb-0310-9956-ffa450edef68 --- hadoop-mapreduce-project/CHANGES.txt | 3 +++ .../apache/hadoop/yarn/service/CompositeService.java | 4 ++++ .../apache/hadoop/yarn/util/TestCompositeService.java | 10 +++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/hadoop-mapreduce-project/CHANGES.txt b/hadoop-mapreduce-project/CHANGES.txt index 44ee434d251..cef1447751e 100644 --- a/hadoop-mapreduce-project/CHANGES.txt +++ b/hadoop-mapreduce-project/CHANGES.txt @@ -352,6 +352,9 @@ Release 0.23.1 - Unreleased MAPREDUCE-3588. Fixed bin/yarn which was broken by MAPREDUCE-3366 so that yarn daemons can start. (Arun C Murthy via vinodkv) + MAPREDUCE-3586. Modified CompositeService to avoid duplicate stop operations + thereby solving race conditions in MR AM shutdown. (vinodkv) + Release 0.23.0 - 2011-11-01 INCOMPATIBLE CHANGES diff --git a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/service/CompositeService.java b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/service/CompositeService.java index 36fecc38ff8..00cebcfef46 100644 --- a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/service/CompositeService.java +++ b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/service/CompositeService.java @@ -81,6 +81,10 @@ public class CompositeService extends AbstractService { } public synchronized void stop() { + if (this.getServiceState() == STATE.STOPPED) { + // The base composite-service is already stopped, don't do anything again. + return; + } if (serviceList.size() > 0) { stop(serviceList.size() - 1); } diff --git a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/util/TestCompositeService.java b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/util/TestCompositeService.java index 9ca0e4b42c9..7a8aef88f7d 100644 --- a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/util/TestCompositeService.java +++ b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/util/TestCompositeService.java @@ -88,6 +88,14 @@ public class TestCompositeService { ((NUM_OF_SERVICES - 1) - i), services[i].getCallSequenceNumber()); } + // Try to stop again. This should be a no-op. + serviceManager.stop(); + // Verify that stop() call sequence numbers for every service don't change. + for (int i = 0; i < NUM_OF_SERVICES; i++) { + assertEquals("For " + services[i] + + " service, stop() call sequence number should have been ", + ((NUM_OF_SERVICES - 1) - i), services[i].getCallSequenceNumber()); + } } @Test @@ -153,7 +161,7 @@ public class TestCompositeService { serviceManager.start(); - // Start the composite service + // Stop the composite service try { serviceManager.stop(); } catch (YarnException e) { From 8fa0a3c737f27ff9d12fb657a7b22865754a5fd8 Mon Sep 17 00:00:00 2001 From: Siddharth Seth Date: Thu, 22 Dec 2011 22:34:40 +0000 Subject: [PATCH 14/16] MAPREDUCE-3567. Extraneous JobConf objects in AM heap. Contributed by Vinod Kumar Vavilapalli) git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1222498 13f79535-47bb-0310-9956-ffa450edef68 --- hadoop-mapreduce-project/CHANGES.txt | 3 +++ .../hadoop/mapred/LocalContainerLauncher.java | 4 ++-- .../apache/hadoop/mapred/MapTaskAttemptImpl.java | 4 ++-- .../hadoop/mapred/ReduceTaskAttemptImpl.java | 5 ++--- .../hadoop/mapreduce/v2/app/job/impl/JobImpl.java | 8 ++++---- .../mapreduce/v2/app/job/impl/MapTaskImpl.java | 8 ++++---- .../mapreduce/v2/app/job/impl/ReduceTaskImpl.java | 7 ++++--- .../v2/app/job/impl/TaskAttemptImpl.java | 10 +++++----- .../mapreduce/v2/app/job/impl/TaskImpl.java | 11 ++++++----- .../org/apache/hadoop/mapreduce/v2/app/MRApp.java | 4 ++++ .../hadoop/mapreduce/v2/app/MRAppBenchmark.java | 3 +-- .../mapreduce/v2/app/job/impl/TestTaskImpl.java | 15 ++++++--------- .../apache/hadoop/yarn/event/AsyncDispatcher.java | 14 +++++++------- 13 files changed, 50 insertions(+), 46 deletions(-) diff --git a/hadoop-mapreduce-project/CHANGES.txt b/hadoop-mapreduce-project/CHANGES.txt index cef1447751e..5b13add82bd 100644 --- a/hadoop-mapreduce-project/CHANGES.txt +++ b/hadoop-mapreduce-project/CHANGES.txt @@ -169,6 +169,9 @@ Release 0.23.1 - Unreleased OPTIMIZATIONS + MAPREDUCE-3567. Extraneous JobConf objects in AM heap. (Vinod Kumar + Vavilapalli via sseth) + BUG FIXES MAPREDUCE-3221. Reenabled the previously ignored test in TestSubmitJob diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapred/LocalContainerLauncher.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapred/LocalContainerLauncher.java index cb3e80b8b21..e1163a171df 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapred/LocalContainerLauncher.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapred/LocalContainerLauncher.java @@ -29,9 +29,9 @@ import java.util.concurrent.LinkedBlockingQueue; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.fs.FSError; +import org.apache.hadoop.fs.FileContext; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; -import org.apache.hadoop.fs.FileContext; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.UnsupportedFileSystemException; import org.apache.hadoop.mapreduce.JobContext; @@ -41,11 +41,11 @@ import org.apache.hadoop.mapreduce.TypeConverter; import org.apache.hadoop.mapreduce.v2.api.records.TaskAttemptId; import org.apache.hadoop.mapreduce.v2.api.records.TaskType; import org.apache.hadoop.mapreduce.v2.app.AppContext; +import org.apache.hadoop.mapreduce.v2.app.job.Job; import org.apache.hadoop.mapreduce.v2.app.job.event.JobCounterUpdateEvent; import org.apache.hadoop.mapreduce.v2.app.job.event.TaskAttemptContainerLaunchedEvent; import org.apache.hadoop.mapreduce.v2.app.job.event.TaskAttemptEvent; import org.apache.hadoop.mapreduce.v2.app.job.event.TaskAttemptEventType; -import org.apache.hadoop.mapreduce.v2.app.job.Job; import org.apache.hadoop.mapreduce.v2.app.launcher.ContainerLauncher; import org.apache.hadoop.mapreduce.v2.app.launcher.ContainerLauncherEvent; import org.apache.hadoop.mapreduce.v2.app.launcher.ContainerRemoteLaunchEvent; diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapred/MapTaskAttemptImpl.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapred/MapTaskAttemptImpl.java index a948604085f..aa0894fdb2d 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapred/MapTaskAttemptImpl.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapred/MapTaskAttemptImpl.java @@ -20,7 +20,6 @@ package org.apache.hadoop.mapred; import java.util.Collection; -import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.mapreduce.MRJobConfig; import org.apache.hadoop.mapreduce.OutputCommitter; @@ -35,13 +34,14 @@ import org.apache.hadoop.security.token.TokenIdentifier; import org.apache.hadoop.yarn.Clock; import org.apache.hadoop.yarn.event.EventHandler; +@SuppressWarnings({ "rawtypes", "deprecation" }) public class MapTaskAttemptImpl extends TaskAttemptImpl { private final TaskSplitMetaInfo splitInfo; public MapTaskAttemptImpl(TaskId taskId, int attempt, EventHandler eventHandler, Path jobFile, - int partition, TaskSplitMetaInfo splitInfo, Configuration conf, + int partition, TaskSplitMetaInfo splitInfo, JobConf conf, TaskAttemptListener taskAttemptListener, OutputCommitter committer, Token jobToken, Collection> fsTokens, Clock clock) { diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapred/ReduceTaskAttemptImpl.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapred/ReduceTaskAttemptImpl.java index 1034a5633b2..7af06b9b1a9 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapred/ReduceTaskAttemptImpl.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapred/ReduceTaskAttemptImpl.java @@ -20,7 +20,6 @@ package org.apache.hadoop.mapred; import java.util.Collection; -import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.mapreduce.MRJobConfig; import org.apache.hadoop.mapreduce.OutputCommitter; @@ -34,14 +33,14 @@ import org.apache.hadoop.security.token.TokenIdentifier; import org.apache.hadoop.yarn.Clock; import org.apache.hadoop.yarn.event.EventHandler; - +@SuppressWarnings({ "rawtypes", "deprecation" }) public class ReduceTaskAttemptImpl extends TaskAttemptImpl { private final int numMapTasks; public ReduceTaskAttemptImpl(TaskId id, int attempt, EventHandler eventHandler, Path jobFile, int partition, - int numMapTasks, Configuration conf, + int numMapTasks, JobConf conf, TaskAttemptListener taskAttemptListener, OutputCommitter committer, Token jobToken, Collection> fsTokens, Clock clock) { diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/JobImpl.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/JobImpl.java index 9291075e6d2..cb9171cedc2 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/JobImpl.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/JobImpl.java @@ -54,7 +54,6 @@ import org.apache.hadoop.mapreduce.jobhistory.JobSubmittedEvent; import org.apache.hadoop.mapreduce.jobhistory.JobUnsuccessfulCompletionEvent; import org.apache.hadoop.mapreduce.lib.chain.ChainMapper; import org.apache.hadoop.mapreduce.lib.chain.ChainReducer; -import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.security.TokenCache; import org.apache.hadoop.mapreduce.security.token.JobTokenIdentifier; import org.apache.hadoop.mapreduce.security.token.JobTokenSecretManager; @@ -110,6 +109,7 @@ import org.apache.hadoop.yarn.state.StateMachineFactory; /** Implementation of Job interface. Maintains the state machines of Job. * The read and write calls use ReadWriteLock for concurrency. */ +@SuppressWarnings({ "rawtypes", "deprecation" }) public class JobImpl implements org.apache.hadoop.mapreduce.v2.app.job.Job, EventHandler { @@ -154,7 +154,7 @@ public class JobImpl implements org.apache.hadoop.mapreduce.v2.app.job.Job, // Can then replace task-level uber counters (MR-2424) with job-level ones // sent from LocalContainerLauncher, and eventually including a count of // of uber-AM attempts (probably sent from MRAppMaster). - public Configuration conf; + public JobConf conf; //fields initialized in init private FileSystem fs; @@ -371,7 +371,7 @@ public class JobImpl implements org.apache.hadoop.mapreduce.v2.app.job.Job, this.applicationAttemptId = applicationAttemptId; this.jobId = jobId; this.jobName = conf.get(JobContext.JOB_NAME, ""); - this.conf = conf; + this.conf = new JobConf(conf); this.metrics = metrics; this.clock = clock; this.completedTasksFromPreviousRun = completedTasksFromPreviousRun; @@ -979,7 +979,7 @@ public class JobImpl implements org.apache.hadoop.mapreduce.v2.app.job.Job, job.oldJobId); } else { job.jobContext = new org.apache.hadoop.mapred.JobContextImpl( - new JobConf(job.conf), job.oldJobId); + job.conf, job.oldJobId); } long inputLength = 0; diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/MapTaskImpl.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/MapTaskImpl.java index 119cc5190af..5bf3d94c877 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/MapTaskImpl.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/MapTaskImpl.java @@ -21,8 +21,8 @@ package org.apache.hadoop.mapreduce.v2.app.job.impl; import java.util.Collection; import java.util.Set; -import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; +import org.apache.hadoop.mapred.JobConf; import org.apache.hadoop.mapred.MapTaskAttemptImpl; import org.apache.hadoop.mapreduce.MRJobConfig; import org.apache.hadoop.mapreduce.OutputCommitter; @@ -31,20 +31,20 @@ import org.apache.hadoop.mapreduce.split.JobSplit.TaskSplitMetaInfo; import org.apache.hadoop.mapreduce.v2.api.records.JobId; import org.apache.hadoop.mapreduce.v2.api.records.TaskId; import org.apache.hadoop.mapreduce.v2.api.records.TaskType; -import org.apache.hadoop.mapreduce.v2.app.metrics.MRAppMetrics; import org.apache.hadoop.mapreduce.v2.app.TaskAttemptListener; +import org.apache.hadoop.mapreduce.v2.app.metrics.MRAppMetrics; import org.apache.hadoop.security.token.Token; import org.apache.hadoop.security.token.TokenIdentifier; import org.apache.hadoop.yarn.Clock; import org.apache.hadoop.yarn.event.EventHandler; - +@SuppressWarnings({ "rawtypes", "deprecation" }) public class MapTaskImpl extends TaskImpl { private final TaskSplitMetaInfo taskSplitMetaInfo; public MapTaskImpl(JobId jobId, int partition, EventHandler eventHandler, - Path remoteJobConfFile, Configuration conf, + Path remoteJobConfFile, JobConf conf, TaskSplitMetaInfo taskSplitMetaInfo, TaskAttemptListener taskAttemptListener, OutputCommitter committer, Token jobToken, diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/ReduceTaskImpl.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/ReduceTaskImpl.java index ae2e84a3778..a2f386aaab0 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/ReduceTaskImpl.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/ReduceTaskImpl.java @@ -21,8 +21,8 @@ package org.apache.hadoop.mapreduce.v2.app.job.impl; import java.util.Collection; import java.util.Set; -import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; +import org.apache.hadoop.mapred.JobConf; import org.apache.hadoop.mapred.ReduceTaskAttemptImpl; import org.apache.hadoop.mapreduce.MRJobConfig; import org.apache.hadoop.mapreduce.OutputCommitter; @@ -30,19 +30,20 @@ import org.apache.hadoop.mapreduce.security.token.JobTokenIdentifier; import org.apache.hadoop.mapreduce.v2.api.records.JobId; import org.apache.hadoop.mapreduce.v2.api.records.TaskId; import org.apache.hadoop.mapreduce.v2.api.records.TaskType; -import org.apache.hadoop.mapreduce.v2.app.metrics.MRAppMetrics; import org.apache.hadoop.mapreduce.v2.app.TaskAttemptListener; +import org.apache.hadoop.mapreduce.v2.app.metrics.MRAppMetrics; import org.apache.hadoop.security.token.Token; import org.apache.hadoop.security.token.TokenIdentifier; import org.apache.hadoop.yarn.Clock; import org.apache.hadoop.yarn.event.EventHandler; +@SuppressWarnings({ "rawtypes", "deprecation" }) public class ReduceTaskImpl extends TaskImpl { private final int numMapTasks; public ReduceTaskImpl(JobId jobId, int partition, - EventHandler eventHandler, Path jobFile, Configuration conf, + EventHandler eventHandler, Path jobFile, JobConf conf, int numMapTasks, TaskAttemptListener taskAttemptListener, OutputCommitter committer, Token jobToken, Collection> fsTokens, Clock clock, diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TaskAttemptImpl.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TaskAttemptImpl.java index 46479ee142c..52d3b47b62b 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TaskAttemptImpl.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TaskAttemptImpl.java @@ -125,6 +125,7 @@ import org.apache.hadoop.yarn.util.RackResolver; /** * Implementation of TaskAttempt interface. */ +@SuppressWarnings({ "rawtypes", "deprecation" }) public abstract class TaskAttemptImpl implements org.apache.hadoop.mapreduce.v2.app.job.TaskAttempt, EventHandler { @@ -135,10 +136,9 @@ public abstract class TaskAttemptImpl implements private static final int REDUCE_MEMORY_MB_DEFAULT = 1024; private final static RecordFactory recordFactory = RecordFactoryProvider.getRecordFactory(null); - protected final Configuration conf; + protected final JobConf conf; protected final Path jobFile; protected final int partition; - @SuppressWarnings("rawtypes") protected final EventHandler eventHandler; private final TaskAttemptId attemptId; private final Clock clock; @@ -445,9 +445,9 @@ public abstract class TaskAttemptImpl implements .getProperty("line.separator"); public TaskAttemptImpl(TaskId taskId, int i, - @SuppressWarnings("rawtypes") EventHandler eventHandler, + EventHandler eventHandler, TaskAttemptListener taskAttemptListener, Path jobFile, int partition, - Configuration conf, String[] dataLocalHosts, OutputCommitter committer, + JobConf conf, String[] dataLocalHosts, OutputCommitter committer, Token jobToken, Collection> fsTokens, Clock clock) { oldJobId = TypeConverter.fromYarn(taskId.getJobId()); @@ -1199,7 +1199,7 @@ public abstract class TaskAttemptImpl implements TaskAttemptEvent event) { @SuppressWarnings("deprecation") TaskAttemptContext taskContext = - new TaskAttemptContextImpl(new JobConf(taskAttempt.conf), + new TaskAttemptContextImpl(taskAttempt.conf, TypeConverter.fromYarn(taskAttempt.attemptId)); taskAttempt.eventHandler.handle(new TaskCleanupEvent( taskAttempt.attemptId, diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TaskImpl.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TaskImpl.java index a7c64915124..b0708540834 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TaskImpl.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TaskImpl.java @@ -31,8 +31,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; +import org.apache.hadoop.mapred.JobConf; import org.apache.hadoop.mapreduce.OutputCommitter; import org.apache.hadoop.mapreduce.TypeConverter; import org.apache.hadoop.mapreduce.jobhistory.JobHistoryEvent; @@ -50,8 +50,6 @@ import org.apache.hadoop.mapreduce.v2.api.records.TaskId; import org.apache.hadoop.mapreduce.v2.api.records.TaskReport; import org.apache.hadoop.mapreduce.v2.api.records.TaskState; import org.apache.hadoop.mapreduce.v2.api.records.TaskType; -import org.apache.hadoop.mapreduce.v2.app.metrics.MRAppMetrics; -import org.apache.hadoop.mapreduce.v2.app.rm.ContainerFailedEvent; import org.apache.hadoop.mapreduce.v2.app.TaskAttemptListener; import org.apache.hadoop.mapreduce.v2.app.job.Task; import org.apache.hadoop.mapreduce.v2.app.job.TaskAttempt; @@ -66,6 +64,8 @@ import org.apache.hadoop.mapreduce.v2.app.job.event.TaskAttemptEventType; import org.apache.hadoop.mapreduce.v2.app.job.event.TaskEvent; import org.apache.hadoop.mapreduce.v2.app.job.event.TaskEventType; import org.apache.hadoop.mapreduce.v2.app.job.event.TaskTAttemptEvent; +import org.apache.hadoop.mapreduce.v2.app.metrics.MRAppMetrics; +import org.apache.hadoop.mapreduce.v2.app.rm.ContainerFailedEvent; import org.apache.hadoop.security.token.Token; import org.apache.hadoop.security.token.TokenIdentifier; import org.apache.hadoop.yarn.Clock; @@ -81,11 +81,12 @@ import org.apache.hadoop.yarn.state.StateMachineFactory; /** * Implementation of Task interface. */ +@SuppressWarnings({ "rawtypes", "unchecked", "deprecation" }) public abstract class TaskImpl implements Task, EventHandler { private static final Log LOG = LogFactory.getLog(TaskImpl.class); - protected final Configuration conf; + protected final JobConf conf; protected final Path jobFile; protected final OutputCommitter committer; protected final int partition; @@ -225,7 +226,7 @@ public abstract class TaskImpl implements Task, EventHandler { } public TaskImpl(JobId jobId, TaskType taskType, int partition, - EventHandler eventHandler, Path remoteJobConfFile, Configuration conf, + EventHandler eventHandler, Path remoteJobConfFile, JobConf conf, TaskAttemptListener taskAttemptListener, OutputCommitter committer, Token jobToken, Collection> fsTokens, Clock clock, diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/MRApp.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/MRApp.java index 888bec3e508..561ecac8a91 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/MRApp.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/MRApp.java @@ -119,6 +119,10 @@ public class MRApp extends MRAppMaster { this(maps, reduces, autoComplete, testName, cleanOnStart, 1); } + @Override + protected void downloadTokensAndSetupUGI(Configuration conf) { + } + private static ApplicationAttemptId getApplicationAttemptId( ApplicationId applicationId, int startCount) { ApplicationAttemptId applicationAttemptId = diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/MRAppBenchmark.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/MRAppBenchmark.java index 0d6c7d7576c..279b81199ae 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/MRAppBenchmark.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/MRAppBenchmark.java @@ -30,7 +30,6 @@ import org.apache.hadoop.mapreduce.v2.app.job.event.TaskAttemptContainerAssigned import org.apache.hadoop.mapreduce.v2.app.rm.ContainerAllocator; import org.apache.hadoop.mapreduce.v2.app.rm.ContainerAllocatorEvent; import org.apache.hadoop.yarn.YarnException; -import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; import org.apache.hadoop.yarn.api.records.Container; import org.apache.hadoop.yarn.api.records.ContainerId; import org.apache.hadoop.yarn.api.records.NodeId; @@ -169,7 +168,7 @@ public class MRAppBenchmark { } public void benchmark1() throws Exception { - int maps = 900; + int maps = 100000; int reduces = 100; System.out.println("Running benchmark with maps:"+maps + " reduces:"+reduces); diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TestTaskImpl.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TestTaskImpl.java index 5a0f89ffd9e..b55bae22546 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TestTaskImpl.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TestTaskImpl.java @@ -17,7 +17,7 @@ */ package org.apache.hadoop.mapreduce.v2.app.job.impl; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -29,7 +29,6 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.mapred.JobConf; import org.apache.hadoop.mapred.Task; @@ -60,11 +59,12 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; +@SuppressWarnings({ "rawtypes", "deprecation" }) public class TestTaskImpl { private static final Log LOG = LogFactory.getLog(TestTaskImpl.class); - private Configuration conf; + private JobConf conf; private TaskAttemptListener taskAttemptListener; private OutputCommitter committer; private Token jobToken; @@ -91,9 +91,8 @@ public class TestTaskImpl { private int taskAttemptCounter = 0; - @SuppressWarnings("rawtypes") public MockTaskImpl(JobId jobId, int partition, - EventHandler eventHandler, Path remoteJobConfFile, Configuration conf, + EventHandler eventHandler, Path remoteJobConfFile, JobConf conf, TaskAttemptListener taskAttemptListener, OutputCommitter committer, Token jobToken, Collection> fsTokens, Clock clock, @@ -132,10 +131,9 @@ public class TestTaskImpl { private TaskAttemptState state = TaskAttemptState.NEW; private TaskAttemptId attemptId; - @SuppressWarnings("rawtypes") public MockTaskAttemptImpl(TaskId taskId, int id, EventHandler eventHandler, TaskAttemptListener taskAttemptListener, Path jobFile, int partition, - Configuration conf, OutputCommitter committer, + JobConf conf, OutputCommitter committer, Token jobToken, Collection> fsTokens, Clock clock) { super(taskId, id, eventHandler, taskAttemptListener, jobFile, partition, conf, @@ -175,7 +173,6 @@ public class TestTaskImpl { private class MockTask extends Task { @Override - @SuppressWarnings("deprecation") public void run(JobConf job, TaskUmbilicalProtocol umbilical) throws IOException, ClassNotFoundException, InterruptedException { return; @@ -195,7 +192,7 @@ public class TestTaskImpl { ++startCount; - conf = new Configuration(); + conf = new JobConf(); taskAttemptListener = mock(TaskAttemptListener.class); committer = mock(OutputCommitter.class); jobToken = (Token) mock(Token.class); diff --git a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/event/AsyncDispatcher.java b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/event/AsyncDispatcher.java index 8a5fceecb09..ffa2e9cfc60 100644 --- a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/event/AsyncDispatcher.java +++ b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/event/AsyncDispatcher.java @@ -75,7 +75,7 @@ public class AsyncDispatcher extends AbstractService implements Dispatcher { try { event = eventQueue.take(); } catch(InterruptedException ie) { - LOG.info("AsyncDispatcher thread interrupted", ie); + LOG.warn("AsyncDispatcher thread interrupted", ie); return; } if (event != null) { @@ -114,8 +114,10 @@ public class AsyncDispatcher extends AbstractService implements Dispatcher { @SuppressWarnings("unchecked") protected void dispatch(Event event) { //all events go thru this loop - LOG.debug("Dispatching the event " + event.getClass().getName() + "." - + event.toString()); + if (LOG.isDebugEnabled()) { + LOG.debug("Dispatching the event " + event.getClass().getName() + "." + + event.toString()); + } Class type = event.getType().getDeclaringClass(); @@ -131,12 +133,11 @@ public class AsyncDispatcher extends AbstractService implements Dispatcher { } } + @SuppressWarnings("unchecked") @Override - @SuppressWarnings("rawtypes") public void register(Class eventType, EventHandler handler) { /* check to see if we have a listener registered */ - @SuppressWarnings("unchecked") EventHandler registeredHandler = (EventHandler) eventDispatchers.get(eventType); LOG.info("Registering " + eventType + " for " + handler.getClass()); @@ -170,7 +171,7 @@ public class AsyncDispatcher extends AbstractService implements Dispatcher { } int remCapacity = eventQueue.remainingCapacity(); if (remCapacity < 1000) { - LOG.info("Very low remaining capacity in the event-queue: " + LOG.warn("Very low remaining capacity in the event-queue: " + remCapacity); } try { @@ -186,7 +187,6 @@ public class AsyncDispatcher extends AbstractService implements Dispatcher { * are interested in the event. * @param the type of event these multiple handlers are interested in. */ - @SuppressWarnings("rawtypes") static class MultiListenerHandler implements EventHandler { List> listofHandlers; From 1dcc4b57ee29c372934b72511302b689cd93c1cf Mon Sep 17 00:00:00 2001 From: Amar Kamat Date: Fri, 23 Dec 2011 14:47:23 +0000 Subject: [PATCH 15/16] MAPREDUCE-3597. [Rumen] Rumen should provide APIs to access all the job-history related information. git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1222695 13f79535-47bb-0310-9956-ffa450edef68 --- hadoop-mapreduce-project/CHANGES.txt | 3 + .../tools/rumen/TestRumenJobTraces.java | 162 +++++++++++++++- .../counters-test-trace.json.gz | Bin 3013 -> 3008 bytes .../dispatch-sample-v20-jt-log.gz | Bin 29910 -> 29932 bytes .../dispatch-trace-output.json.gz | Bin 28317 -> 28321 bytes .../apache/hadoop/tools/rumen/JobBuilder.java | 105 ++++++---- .../hadoop/tools/rumen/JobHistoryUtils.java | 22 +++ .../apache/hadoop/tools/rumen/LoggedJob.java | 4 + .../apache/hadoop/tools/rumen/ParsedJob.java | 179 ++++++++++++++++++ .../apache/hadoop/tools/rumen/ParsedTask.java | 128 +++++++++++++ .../hadoop/tools/rumen/ParsedTaskAttempt.java | 119 ++++++++++++ 11 files changed, 683 insertions(+), 39 deletions(-) create mode 100644 hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedJob.java create mode 100644 hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedTask.java create mode 100644 hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedTaskAttempt.java diff --git a/hadoop-mapreduce-project/CHANGES.txt b/hadoop-mapreduce-project/CHANGES.txt index 5b13add82bd..c00919a4e03 100644 --- a/hadoop-mapreduce-project/CHANGES.txt +++ b/hadoop-mapreduce-project/CHANGES.txt @@ -12,6 +12,9 @@ Trunk (unreleased changes) (Plamen Jeliazkov via shv) IMPROVEMENTS + MAPREDUCE-3597. [Rumen] Rumen should provide APIs to access all the + job-history related information. + MAPREDUCE-3375. [Gridmix] Memory Emulation system tests. (Vinay Thota via amarrk) diff --git a/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/tools/rumen/TestRumenJobTraces.java b/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/tools/rumen/TestRumenJobTraces.java index 5b563e69e5d..bb92426f5ff 100644 --- a/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/tools/rumen/TestRumenJobTraces.java +++ b/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/tools/rumen/TestRumenJobTraces.java @@ -26,6 +26,8 @@ import java.util.List; import java.util.Properties; import java.util.concurrent.TimeUnit; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.FileSystem; @@ -61,6 +63,8 @@ import org.junit.Test; import static org.junit.Assert.*; public class TestRumenJobTraces { + private static final Log LOG = LogFactory.getLog(TestRumenJobTraces.class); + @Test public void testSmallTrace() throws Exception { performSingleTest("sample-job-tracker-logs.gz", @@ -236,11 +240,21 @@ public class TestRumenJobTraces { parser = new Hadoop20JHParser(ris); ArrayList seenEvents = new ArrayList(150); - getHistoryEvents(parser, seenEvents, null); // get events into seenEvents + // this is same as the one in input history file + String jobId = "job_200904211745_0002"; + JobBuilder builder = new JobBuilder(jobId); + + // get events into seenEvents + getHistoryEvents(parser, seenEvents, builder); // Validate the events seen by history parser from // history file v20-single-input-log.gz validateSeenHistoryEvents(seenEvents, goldLines); + + ParsedJob parsedJob = builder.build(); + // validate the obtainXXX api of ParsedJob, ParsedTask and + // ParsedTaskAttempt + validateParsedJob(parsedJob, 20, 1, true); } finally { if (parser != null) { parser.close(); @@ -584,9 +598,11 @@ public class TestRumenJobTraces { // validate resource usage metrics // get the job counters Counters counters = job.getTaskReports(TaskType.MAP)[0].getTaskCounters(); - + + // get the parsed job + ParsedJob parsedJob = builder.build(); // get the logged job - LoggedJob loggedJob = builder.build(); + LoggedJob loggedJob = parsedJob; // get the logged attempts LoggedTaskAttempt attempt = loggedJob.getMapTasks().get(0).getAttempts().get(0); @@ -600,6 +616,10 @@ public class TestRumenJobTraces { counters.findCounter(TaskCounter.PHYSICAL_MEMORY_BYTES).getValue(), counters.findCounter(TaskCounter.COMMITTED_HEAP_BYTES).getValue(), true); + + // validate the obtainXXX api of ParsedJob, ParsedTask and + // ParsedTaskAttempt + validateParsedJob(parsedJob, 1, 1, false); } finally { // stop the MR cluster mrCluster.shutdown(); @@ -616,6 +636,142 @@ public class TestRumenJobTraces { } } + /** + * Verify if the obtainXXX methods of {@link ParsedJob}, {@link ParsedTask} + * and {@link ParsedTaskAttempt} give valid info + */ + private void validateParsedJob(ParsedJob parsedJob, int numMaps, + int numReduces, boolean pre21JobHistory) { + validateParsedJobAPI(parsedJob, numMaps, numReduces, pre21JobHistory); + + List maps = parsedJob.obtainMapTasks(); + for (ParsedTask task : maps) { + validateParsedTask(task); + } + List reduces = parsedJob.obtainReduceTasks(); + for (ParsedTask task : reduces) { + validateParsedTask(task); + } + List others = parsedJob.obtainOtherTasks(); + for (ParsedTask task : others) { + validateParsedTask(task); + } + } + + /** Verify if the obtainXXX methods of {@link ParsedJob} give valid info */ + private void validateParsedJobAPI(ParsedJob parsedJob, int numMaps, + int numReduces, boolean pre21JobHistory) { + LOG.info("Validating ParsedJob.obtainXXX api... for " + + parsedJob.getJobID()); + assertNotNull("Job acls in ParsedJob is null", + parsedJob.obtainJobAcls()); + assertNotNull("Job conf path in ParsedJob is null", + parsedJob.obtainJobConfpath()); + + assertNotNull("Map Counters in ParsedJob is null", + parsedJob.obtainMapCounters()); + assertNotNull("Reduce Counters in ParsedJob is null", + parsedJob.obtainReduceCounters()); + assertNotNull("Total Counters in ParsedJob is null", + parsedJob.obtainTotalCounters()); + + assertNotNull("Map Tasks List in ParsedJob is null", + parsedJob.obtainMapTasks()); + assertNotNull("Reduce Tasks List in ParsedJob is null", + parsedJob.obtainReduceTasks()); + assertNotNull("Other Tasks List in ParsedJob is null", + parsedJob.obtainOtherTasks()); + + // 1 map and 1 reduce task should be there + assertEquals("Number of map tasks in ParsedJob is wrong", + numMaps, parsedJob.obtainMapTasks().size()); + assertEquals("Number of reduce tasks in ParsedJob is wrong", + numReduces, parsedJob.obtainReduceTasks().size(), 1); + + // old hadoop20 version history files don't have job-level-map-counters and + // job-level-reduce-counters. Only total counters exist there. + assertTrue("Total Counters in ParsedJob is empty", + parsedJob.obtainTotalCounters().size() > 0); + if (!pre21JobHistory) { + assertTrue("Map Counters in ParsedJob is empty", + parsedJob.obtainMapCounters().size() > 0); + assertTrue("Reduce Counters in ParsedJob is empty", + parsedJob.obtainReduceCounters().size() > 0); + } + } + + /** + * Verify if the obtainXXX methods of {@link ParsedTask} and + * {@link ParsedTaskAttempt} give valid info + */ + private void validateParsedTask(ParsedTask parsedTask) { + validateParsedTaskAPI(parsedTask); + + List attempts = parsedTask.obtainTaskAttempts(); + for (ParsedTaskAttempt attempt : attempts) { + validateParsedTaskAttemptAPI(attempt); + } + } + + /** Verify if the obtainXXX methods of {@link ParsedTask} give valid info */ + private void validateParsedTaskAPI(ParsedTask parsedTask) { + LOG.info("Validating ParsedTask.obtainXXX api... for " + + parsedTask.getTaskID()); + assertNotNull("Task counters in ParsedTask is null", + parsedTask.obtainCounters()); + + if (parsedTask.getTaskStatus() + == Pre21JobHistoryConstants.Values.SUCCESS) { + // task counters should not be empty + assertTrue("Task counters in ParsedTask is empty", + parsedTask.obtainCounters().size() > 0); + assertNull("Diagnostic-info is non-null for a succeeded task", + parsedTask.obtainDiagnosticInfo()); + assertNull("Failed-due-to-attemptId is non-null for a succeeded task", + parsedTask.obtainFailedDueToAttemptId()); + } else { + assertNotNull("Diagnostic-info is non-null for a succeeded task", + parsedTask.obtainDiagnosticInfo()); + assertNotNull("Failed-due-to-attemptId is non-null for a succeeded task", + parsedTask.obtainFailedDueToAttemptId()); + } + + List attempts = parsedTask.obtainTaskAttempts(); + assertNotNull("TaskAttempts list in ParsedTask is null", attempts); + assertTrue("TaskAttempts list in ParsedTask is empty", + attempts.size() > 0); + } + + /** + * Verify if the obtainXXX methods of {@link ParsedTaskAttempt} give + * valid info + */ + private void validateParsedTaskAttemptAPI( + ParsedTaskAttempt parsedTaskAttempt) { + LOG.info("Validating ParsedTaskAttempt.obtainXXX api... for " + + parsedTaskAttempt.getAttemptID()); + assertNotNull("Counters in ParsedTaskAttempt is null", + parsedTaskAttempt.obtainCounters()); + + if (parsedTaskAttempt.getResult() + == Pre21JobHistoryConstants.Values.SUCCESS) { + assertTrue("Counters in ParsedTaskAttempt is empty", + parsedTaskAttempt.obtainCounters().size() > 0); + assertNull("Diagnostic-info is non-null for a succeeded taskAttempt", + parsedTaskAttempt.obtainDiagnosticInfo()); + } else { + assertNotNull("Diagnostic-info is non-null for a succeeded taskAttempt", + parsedTaskAttempt.obtainDiagnosticInfo()); + } + assertNotNull("TrackerName in ParsedTaskAttempt is null", + parsedTaskAttempt.obtainTrackerName()); + + assertNotNull("http-port info in ParsedTaskAttempt is null", + parsedTaskAttempt.obtainHttpPort()); + assertNotNull("Shuffle-port info in ParsedTaskAttempt is null", + parsedTaskAttempt.obtainShufflePort()); + } + @Test public void testJobConfigurationParser() throws Exception { diff --git a/hadoop-mapreduce-project/src/test/tools/data/rumen/small-trace-test/counters-test-trace.json.gz b/hadoop-mapreduce-project/src/test/tools/data/rumen/small-trace-test/counters-test-trace.json.gz index 47ee42869198b842622e9b35cf1811f46f9a6f5e..35d38054ab402e3b49d22ce4d312a2c199d01ec6 100644 GIT binary patch delta 2930 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 cZYf?@?BU*zG?iaZ?sAIOccgfxZ#0O5MBy#N3J delta 2935 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 diff --git a/hadoop-mapreduce-project/src/test/tools/data/rumen/small-trace-test/dispatch-sample-v20-jt-log.gz b/hadoop-mapreduce-project/src/test/tools/data/rumen/small-trace-test/dispatch-sample-v20-jt-log.gz index 580db3be0f0818d73288a7a316c9d194a4af1c2e..c68f42383495d85e1d61fc552a524856cb93a241 100644 GIT binary patch delta 28658 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


UCRo|g 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

C~s(7)0B4wE3 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 l)Uu02mtwc0J6`z;8NbJvHFY{FUFtt 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 delta 24960 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ν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 PJiFl)>e?e}B_a5KUEx-O diff --git a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/JobBuilder.java b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/JobBuilder.java index 30597d3f5c4..479e62e5518 100644 --- a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/JobBuilder.java +++ b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/JobBuilder.java @@ -31,6 +31,7 @@ import org.apache.hadoop.mapreduce.TaskAttemptID; import org.apache.hadoop.mapreduce.TaskType; import org.apache.hadoop.mapreduce.jobhistory.AMStartedEvent; import org.apache.hadoop.mapreduce.jobhistory.HistoryEvent; +import org.apache.hadoop.mapreduce.jobhistory.JobFinished; import org.apache.hadoop.mapreduce.jobhistory.JobFinishedEvent; import org.apache.hadoop.mapreduce.jobhistory.JobInfoChangeEvent; import org.apache.hadoop.mapreduce.jobhistory.JobInitedEvent; @@ -45,14 +46,15 @@ import org.apache.hadoop.mapreduce.jobhistory.ReduceAttemptFinishedEvent; import org.apache.hadoop.mapreduce.jobhistory.TaskAttemptFinished; import org.apache.hadoop.mapreduce.jobhistory.TaskAttemptFinishedEvent; import org.apache.hadoop.mapreduce.jobhistory.TaskAttemptStartedEvent; +import org.apache.hadoop.mapreduce.jobhistory.TaskAttemptUnsuccessfulCompletion; import org.apache.hadoop.mapreduce.jobhistory.TaskAttemptUnsuccessfulCompletionEvent; +import org.apache.hadoop.mapreduce.jobhistory.TaskFailed; import org.apache.hadoop.mapreduce.jobhistory.TaskFailedEvent; import org.apache.hadoop.mapreduce.jobhistory.TaskFinished; import org.apache.hadoop.mapreduce.jobhistory.TaskFinishedEvent; import org.apache.hadoop.mapreduce.jobhistory.TaskStartedEvent; import org.apache.hadoop.mapreduce.jobhistory.TaskUpdatedEvent; import org.apache.hadoop.tools.rumen.Pre21JobHistoryConstants.Values; -import org.apache.hadoop.tools.rumen.datatypes.JobProperties; import org.apache.hadoop.util.StringUtils; /** @@ -67,16 +69,16 @@ public class JobBuilder { private boolean finalized = false; - private LoggedJob result = new LoggedJob(); + private ParsedJob result = new ParsedJob(); - private Map mapTasks = new HashMap(); - private Map reduceTasks = - new HashMap(); - private Map otherTasks = - new HashMap(); + private Map mapTasks = new HashMap(); + private Map reduceTasks = + new HashMap(); + private Map otherTasks = + new HashMap(); - private Map attempts = - new HashMap(); + private Map attempts = + new HashMap(); private Map allHosts = new HashMap(); @@ -123,7 +125,7 @@ public class JobBuilder { public void process(HistoryEvent event) { if (finalized) { throw new IllegalStateException( - "JobBuilder.process(HistoryEvent event) called after LoggedJob built"); + "JobBuilder.process(HistoryEvent event) called after ParsedJob built"); } // these are in lexicographical order by class name. @@ -229,12 +231,16 @@ public class JobBuilder { public void process(Properties conf) { if (finalized) { throw new IllegalStateException( - "JobBuilder.process(Properties conf) called after LoggedJob built"); + "JobBuilder.process(Properties conf) called after ParsedJob built"); } //TODO remove this once the deprecate APIs in LoggedJob are removed - result.setQueue(extract(conf, JobConfPropertyNames.QUEUE_NAMES - .getCandidates(), "default")); + String queue = extract(conf, JobConfPropertyNames.QUEUE_NAMES + .getCandidates(), null); + // set the queue name if existing + if (queue != null) { + result.setQueue(queue); + } result.setJobName(extract(conf, JobConfPropertyNames.JOB_NAMES .getCandidates(), null)); @@ -252,9 +258,9 @@ public class JobBuilder { * Request the builder to build the final object. Once called, the * {@link JobBuilder} would accept no more events or job-conf properties. * - * @return Parsed {@link LoggedJob} object. + * @return Parsed {@link ParsedJob} object. */ - public LoggedJob build() { + public ParsedJob build() { // The main job here is to build CDFs and manage the conf finalized = true; @@ -416,7 +422,7 @@ public class JobBuilder { } private void processTaskUpdatedEvent(TaskUpdatedEvent event) { - LoggedTask task = getTask(event.getTaskId().toString()); + ParsedTask task = getTask(event.getTaskId().toString()); if (task == null) { return; } @@ -424,7 +430,7 @@ public class JobBuilder { } private void processTaskStartedEvent(TaskStartedEvent event) { - LoggedTask task = + ParsedTask task = getOrMakeTask(event.getTaskType(), event.getTaskId().toString(), true); task.setStartTime(event.getStartTime()); task.setPreferredLocations(preferredLocationForSplits(event @@ -432,7 +438,7 @@ public class JobBuilder { } private void processTaskFinishedEvent(TaskFinishedEvent event) { - LoggedTask task = + ParsedTask task = getOrMakeTask(event.getTaskType(), event.getTaskId().toString(), false); if (task == null) { return; @@ -443,18 +449,22 @@ public class JobBuilder { } private void processTaskFailedEvent(TaskFailedEvent event) { - LoggedTask task = + ParsedTask task = getOrMakeTask(event.getTaskType(), event.getTaskId().toString(), false); if (task == null) { return; } task.setFinishTime(event.getFinishTime()); task.setTaskStatus(getPre21Value(event.getTaskStatus())); + TaskFailed t = (TaskFailed)(event.getDatum()); + task.putDiagnosticInfo(t.error.toString()); + task.putFailedDueToAttemptId(t.failedDueToAttempt.toString()); + // No counters in TaskFailedEvent } private void processTaskAttemptUnsuccessfulCompletionEvent( TaskAttemptUnsuccessfulCompletionEvent event) { - LoggedTaskAttempt attempt = + ParsedTaskAttempt attempt = getOrMakeTaskAttempt(event.getTaskType(), event.getTaskId().toString(), event.getTaskAttemptId().toString()); @@ -476,20 +486,27 @@ public class JobBuilder { attempt.arraySetCpuUsages(event.getCpuUsages()); attempt.arraySetVMemKbytes(event.getVMemKbytes()); attempt.arraySetPhysMemKbytes(event.getPhysMemKbytes()); + TaskAttemptUnsuccessfulCompletion t = + (TaskAttemptUnsuccessfulCompletion) (event.getDatum()); + attempt.putDiagnosticInfo(t.error.toString()); + // No counters in TaskAttemptUnsuccessfulCompletionEvent } private void processTaskAttemptStartedEvent(TaskAttemptStartedEvent event) { - LoggedTaskAttempt attempt = + ParsedTaskAttempt attempt = getOrMakeTaskAttempt(event.getTaskType(), event.getTaskId().toString(), event.getTaskAttemptId().toString()); if (attempt == null) { return; } attempt.setStartTime(event.getStartTime()); + attempt.putTrackerName(event.getTrackerName()); + attempt.putHttpPort(event.getHttpPort()); + attempt.putShufflePort(event.getShufflePort()); } private void processTaskAttemptFinishedEvent(TaskAttemptFinishedEvent event) { - LoggedTaskAttempt attempt = + ParsedTaskAttempt attempt = getOrMakeTaskAttempt(event.getTaskType(), event.getTaskId().toString(), event.getAttemptId().toString()); if (attempt == null) { @@ -507,7 +524,7 @@ public class JobBuilder { private void processReduceAttemptFinishedEvent( ReduceAttemptFinishedEvent event) { - LoggedTaskAttempt attempt = + ParsedTaskAttempt attempt = getOrMakeTaskAttempt(event.getTaskType(), event.getTaskId().toString(), event.getAttemptId().toString()); if (attempt == null) { @@ -536,7 +553,7 @@ public class JobBuilder { } private void processMapAttemptFinishedEvent(MapAttemptFinishedEvent event) { - LoggedTaskAttempt attempt = + ParsedTaskAttempt attempt = getOrMakeTaskAttempt(event.getTaskType(), event.getTaskId().toString(), event.getAttemptId().toString()); if (attempt == null) { @@ -568,6 +585,7 @@ public class JobBuilder { result.setOutcome(Pre21JobHistoryConstants.Values .valueOf(event.getStatus())); result.setFinishTime(event.getFinishTime()); + // No counters in JobUnsuccessfulCompletionEvent } private void processJobSubmittedEvent(JobSubmittedEvent event) { @@ -575,8 +593,14 @@ public class JobBuilder { result.setJobName(event.getJobName()); result.setUser(event.getUserName()); result.setSubmitTime(event.getSubmitTime()); - // job queue name is set when conf file is processed. - // See JobBuilder.process(Properties) method for details. + result.putJobConfPath(event.getJobConfPath()); + result.putJobAcls(event.getJobAcls()); + + // set the queue name if existing + String queue = event.getJobQueueName(); + if (queue != null) { + result.setQueue(queue); + } } private void processJobStatusChangedEvent(JobStatusChangedEvent event) { @@ -603,10 +627,19 @@ public class JobBuilder { result.setFinishTime(event.getFinishTime()); result.setJobID(jobID); result.setOutcome(Values.SUCCESS); + + JobFinished job = (JobFinished)event.getDatum(); + Map countersMap = + JobHistoryUtils.extractCounters(job.totalCounters); + result.putTotalCounters(countersMap); + countersMap = JobHistoryUtils.extractCounters(job.mapCounters); + result.putMapCounters(countersMap); + countersMap = JobHistoryUtils.extractCounters(job.reduceCounters); + result.putReduceCounters(countersMap); } - private LoggedTask getTask(String taskIDname) { - LoggedTask result = mapTasks.get(taskIDname); + private ParsedTask getTask(String taskIDname) { + ParsedTask result = mapTasks.get(taskIDname); if (result != null) { return result; @@ -630,9 +663,9 @@ public class JobBuilder { * if true, we can create a task. * @return */ - private LoggedTask getOrMakeTask(TaskType type, String taskIDname, + private ParsedTask getOrMakeTask(TaskType type, String taskIDname, boolean allowCreate) { - Map taskMap = otherTasks; + Map taskMap = otherTasks; List tasks = this.result.getOtherTasks(); switch (type) { @@ -650,10 +683,10 @@ public class JobBuilder { // no code } - LoggedTask result = taskMap.get(taskIDname); + ParsedTask result = taskMap.get(taskIDname); if (result == null && allowCreate) { - result = new LoggedTask(); + result = new ParsedTask(); result.setTaskType(getPre21Value(type.toString())); result.setTaskID(taskIDname); taskMap.put(taskIDname, result); @@ -663,13 +696,13 @@ public class JobBuilder { return result; } - private LoggedTaskAttempt getOrMakeTaskAttempt(TaskType type, + private ParsedTaskAttempt getOrMakeTaskAttempt(TaskType type, String taskIDName, String taskAttemptName) { - LoggedTask task = getOrMakeTask(type, taskIDName, false); - LoggedTaskAttempt result = attempts.get(taskAttemptName); + ParsedTask task = getOrMakeTask(type, taskIDName, false); + ParsedTaskAttempt result = attempts.get(taskAttemptName); if (result == null && task != null) { - result = new LoggedTaskAttempt(); + result = new ParsedTaskAttempt(); result.setAttemptID(taskAttemptName); attempts.put(taskAttemptName, result); task.getAttempts().add(result); diff --git a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/JobHistoryUtils.java b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/JobHistoryUtils.java index f09726d60db..22a18fedb45 100644 --- a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/JobHistoryUtils.java +++ b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/JobHistoryUtils.java @@ -18,10 +18,15 @@ package org.apache.hadoop.tools.rumen; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.hadoop.mapreduce.JobID; +import org.apache.hadoop.mapreduce.jobhistory.JhCounter; +import org.apache.hadoop.mapreduce.jobhistory.JhCounterGroup; +import org.apache.hadoop.mapreduce.jobhistory.JhCounters; import org.apache.hadoop.mapreduce.jobhistory.JobHistory; /** @@ -143,4 +148,21 @@ public class JobHistoryUtils { String jobId = extractJobIDFromConfFileName(fileName); return jobId != null; } + + /** + * Extract/Add counters into the Map from the given JhCounters object. + * @param counters the counters to be extracted from + * @return the map of counters + */ + static Map extractCounters(JhCounters counters) { + Map countersMap = new HashMap(); + if (counters != null) { + for (JhCounterGroup group : counters.groups) { + for (JhCounter counter : group.counts) { + countersMap.put(counter.name.toString(), counter.value); + } + } + } + return countersMap; + } } diff --git a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/LoggedJob.java b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/LoggedJob.java index ba91d5d895f..785feb31325 100644 --- a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/LoggedJob.java +++ b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/LoggedJob.java @@ -360,6 +360,10 @@ public class LoggedJob implements DeepCompare { this.relativeTime = relativeTime; } + /** + * @return job queue name if it is available in job history file or + * job history conf file. Returns null otherwise. + */ public QueueName getQueue() { return queue; } diff --git a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedJob.java b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedJob.java new file mode 100644 index 00000000000..dcd854968ac --- /dev/null +++ b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedJob.java @@ -0,0 +1,179 @@ +/** + * 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.tools.rumen; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.mapreduce.JobACL; +import org.apache.hadoop.security.authorize.AccessControlList; + +/** + * This is a wrapper class around {@link LoggedJob}. This provides also the + * extra information about the job obtained from job history which is not + * written to the JSON trace file. + */ +public class ParsedJob extends LoggedJob { + + private static final Log LOG = LogFactory.getLog(ParsedJob.class); + + private Map totalCountersMap = new HashMap(); + private Map mapCountersMap = new HashMap(); + private Map reduceCountersMap = new HashMap(); + + private String jobConfPath; + private Map jobAcls; + + ParsedJob() { + + } + + ParsedJob(String jobID) { + super(); + + setJobID(jobID); + } + + /** Set the job total counters */ + void putTotalCounters(Map totalCounters) { + this.totalCountersMap = totalCounters; + } + + /** + * @return the job total counters + */ + public Map obtainTotalCounters() { + return totalCountersMap; + } + + /** Set the job level map tasks' counters */ + void putMapCounters(Map mapCounters) { + this.mapCountersMap = mapCounters; + } + + /** + * @return the job level map tasks' counters + */ + public Map obtainMapCounters() { + return mapCountersMap; + } + + /** Set the job level reduce tasks' counters */ + void putReduceCounters(Map reduceCounters) { + this.reduceCountersMap = reduceCounters; + } + + /** + * @return the job level reduce tasks' counters + */ + public Map obtainReduceCounters() { + return reduceCountersMap; + } + + /** Set the job conf path in staging dir on hdfs */ + void putJobConfPath(String confPath) { + jobConfPath = confPath; + } + + /** + * @return the job conf path in staging dir on hdfs + */ + public String obtainJobConfpath() { + return jobConfPath; + } + + /** Set the job acls */ + void putJobAcls(Map acls) { + jobAcls = acls; + } + + /** + * @return the job acls + */ + public Map obtainJobAcls() { + return jobAcls; + } + + /** + * @return the list of map tasks of this job + */ + public List obtainMapTasks() { + List tasks = super.getMapTasks(); + return convertTasks(tasks); + } + + /** + * @return the list of reduce tasks of this job + */ + public List obtainReduceTasks() { + List tasks = super.getReduceTasks(); + return convertTasks(tasks); + } + + /** + * @return the list of other tasks of this job + */ + public List obtainOtherTasks() { + List tasks = super.getOtherTasks(); + return convertTasks(tasks); + } + + /** As we know that this list of {@link LoggedTask} objects is actually a list + * of {@link ParsedTask} objects, we go ahead and cast them. + * @return the list of {@link ParsedTask} objects + */ + private List convertTasks(List tasks) { + List result = new ArrayList(); + + for (LoggedTask t : tasks) { + if (t instanceof ParsedTask) { + result.add((ParsedTask)t); + } else { + throw new RuntimeException("Unexpected type of tasks in the list..."); + } + } + return result; + } + + /** Dump the extra info of ParsedJob */ + void dumpParsedJob() { + LOG.info("ParsedJob details:" + obtainTotalCounters() + ";" + + obtainMapCounters() + ";" + obtainReduceCounters() + + "\n" + obtainJobConfpath() + "\n" + obtainJobAcls() + + ";Q=" + (getQueue() == null ? "null" : getQueue().getValue())); + List maps = obtainMapTasks(); + for (ParsedTask task : maps) { + task.dumpParsedTask(); + } + List reduces = obtainReduceTasks(); + for (ParsedTask task : reduces) { + task.dumpParsedTask(); + } + List others = obtainOtherTasks(); + for (ParsedTask task : others) { + task.dumpParsedTask(); + } + } +} diff --git a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedTask.java b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedTask.java new file mode 100644 index 00000000000..90eebd66fbb --- /dev/null +++ b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedTask.java @@ -0,0 +1,128 @@ +/** + * 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.tools.rumen; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.mapreduce.jobhistory.JhCounters; + +/** + * This is a wrapper class around {@link LoggedTask}. This provides also the + * extra information about the task obtained from job history which is not + * written to the JSON trace file. + */ +public class ParsedTask extends LoggedTask { + + private static final Log LOG = LogFactory.getLog(ParsedTask.class); + + private String diagnosticInfo; + private String failedDueToAttempt; + private Map countersMap = new HashMap(); + + ParsedTask() { + super(); + } + + public void incorporateCounters(JhCounters counters) { + Map countersMap = + JobHistoryUtils.extractCounters(counters); + putCounters(countersMap); + + super.incorporateCounters(counters); + } + + /** Set the task counters */ + public void putCounters(Map counters) { + this.countersMap = counters; + } + + /** + * @return the task counters + */ + public Map obtainCounters() { + return countersMap; + } + + /** Set the task diagnostic-info */ + public void putDiagnosticInfo(String msg) { + diagnosticInfo = msg; + } + + /** + * @return the diagnostic-info of this task. + * If the task is successful, returns null. + */ + public String obtainDiagnosticInfo() { + return diagnosticInfo; + } + + /** + * Set the failed-due-to-attemptId info of this task. + */ + public void putFailedDueToAttemptId(String attempt) { + failedDueToAttempt = attempt; + } + + /** + * @return the failed-due-to-attemptId info of this task. + * If the task is successful, returns null. + */ + public String obtainFailedDueToAttemptId() { + return failedDueToAttempt; + } + + List obtainTaskAttempts() { + List attempts = getAttempts(); + return convertTaskAttempts(attempts); + } + + List convertTaskAttempts( + List attempts) { + List result = new ArrayList(); + + for (LoggedTaskAttempt t : attempts) { + if (t instanceof ParsedTaskAttempt) { + result.add((ParsedTaskAttempt)t); + } else { + throw new RuntimeException( + "Unexpected type of taskAttempts in the list..."); + } + } + return result; + } + + /** Dump the extra info of ParsedTask */ + void dumpParsedTask() { + LOG.info("ParsedTask details:" + obtainCounters() + + "\n" + obtainFailedDueToAttemptId() + + "\nPreferred Locations are:"); + List loc = getPreferredLocations(); + for (LoggedLocation l : loc) { + LOG.info(l.getLayers() + ";" + l.toString()); + } + List attempts = obtainTaskAttempts(); + for (ParsedTaskAttempt attempt : attempts) { + attempt.dumpParsedTaskAttempt(); + } + } +} diff --git a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedTaskAttempt.java b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedTaskAttempt.java new file mode 100644 index 00000000000..6374368b720 --- /dev/null +++ b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedTaskAttempt.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.tools.rumen; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.mapreduce.jobhistory.JhCounters; + +/** + * This is a wrapper class around {@link LoggedTaskAttempt}. This provides + * also the extra information about the task attempt obtained from + * job history which is not written to the JSON trace file. + */ +public class ParsedTaskAttempt extends LoggedTaskAttempt { + + private static final Log LOG = LogFactory.getLog(ParsedTaskAttempt.class); + + private String diagnosticInfo; + private String trackerName; + private Integer httpPort, shufflePort; + private Map countersMap = new HashMap(); + + ParsedTaskAttempt() { + super(); + } + + /** incorporate event counters */ + public void incorporateCounters(JhCounters counters) { + + Map countersMap = + JobHistoryUtils.extractCounters(counters); + putCounters(countersMap); + + super.incorporateCounters(counters); + } + + /** Set the task attempt counters */ + public void putCounters(Map counters) { + this.countersMap = counters; + } + + /** + * @return the task attempt counters + */ + public Map obtainCounters() { + return countersMap; + } + + /** Set the task attempt diagnostic-info */ + public void putDiagnosticInfo(String msg) { + diagnosticInfo = msg; + } + + /** + * @return the diagnostic-info of this task attempt. + * If the attempt is successful, returns null. + */ + public String obtainDiagnosticInfo() { + return diagnosticInfo; + } + + void putTrackerName(String trackerName) { + this.trackerName = trackerName; + } + + public String obtainTrackerName() { + return trackerName; + } + + void putHttpPort(int port) { + httpPort = port; + } + + /** + * @return http port if set. Returns null otherwise. + */ + public Integer obtainHttpPort() { + return httpPort; + } + + void putShufflePort(int port) { + shufflePort = port; + } + + /** + * @return shuffle port if set. Returns null otherwise. + */ + public Integer obtainShufflePort() { + return shufflePort; + } + + /** Dump the extra info of ParsedTaskAttempt */ + void dumpParsedTaskAttempt() { + LOG.info("ParsedTaskAttempt details:" + obtainCounters() + + ";DiagnosticInfo=" + obtainDiagnosticInfo() + "\n" + + obtainTrackerName() + ";" + obtainHttpPort() + ";" + + obtainShufflePort() + ";rack=" + getHostName().getRackName() + + ";host=" + getHostName().getHostName()); + } +} From ae0d48854d5ee0d1281c630263762e1cdeb781ff Mon Sep 17 00:00:00 2001 From: Alejandro Abdelnur Date: Mon, 26 Dec 2011 19:37:25 +0000 Subject: [PATCH 16/16] HDFS-2707. HttpFS should read the hadoop-auth secret from a file instead inline from the configuration. (tucu) git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1224794 13f79535-47bb-0310-9956-ffa450edef68 --- .../src/main/webapp/WEB-INF/web.xml | 2 +- .../server/TestAuthenticationFilter.java | 6 ++-- .../content/xdocs/HttpAuthentication.xml | 2 +- .../AuthenticationFilterInitializer.java | 6 ++-- .../src/main/conf/httpfs-signature.secret | 1 + .../hadoop/fs/http/server/AuthFilter.java | 30 +++++++++++++++++-- .../src/main/resources/httpfs-default.xml | 13 ++++++++ .../fs/http/client/TestHttpFSFileSystem.java | 8 +++++ .../fs/http/server/TestHttpFSServer.java | 8 +++++ hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt | 3 ++ 10 files changed, 68 insertions(+), 11 deletions(-) create mode 100644 hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/conf/httpfs-signature.secret diff --git a/hadoop-common-project/hadoop-auth-examples/src/main/webapp/WEB-INF/web.xml b/hadoop-common-project/hadoop-auth-examples/src/main/webapp/WEB-INF/web.xml index e287abdd9e0..ebd07689cd0 100644 --- a/hadoop-common-project/hadoop-auth-examples/src/main/webapp/WEB-INF/web.xml +++ b/hadoop-common-project/hadoop-auth-examples/src/main/webapp/WEB-INF/web.xml @@ -86,7 +86,7 @@ kerberos.keytab - /tmp/alfredo.keytab + /tmp/my.keytab token.validity diff --git a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestAuthenticationFilter.java b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestAuthenticationFilter.java index 415600e97e9..2989500ec50 100644 --- a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestAuthenticationFilter.java +++ b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/server/TestAuthenticationFilter.java @@ -459,7 +459,7 @@ public class TestAuthenticationFilter extends TestCase { AuthenticationToken token = new AuthenticationToken("u", "p", "t"); token.setExpires(System.currentTimeMillis() + 1000); - Signer signer = new Signer("alfredo".getBytes()); + Signer signer = new Signer("secret".getBytes()); String tokenSigned = signer.sign(token.toString()); Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, tokenSigned); @@ -504,7 +504,7 @@ public class TestAuthenticationFilter extends TestCase { AuthenticationToken token = new AuthenticationToken("u", "p", DummyAuthenticationHandler.TYPE); token.setExpires(System.currentTimeMillis() - 1000); - Signer signer = new Signer("alfredo".getBytes()); + Signer signer = new Signer("secret".getBytes()); String tokenSigned = signer.sign(token.toString()); Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, tokenSigned); @@ -564,7 +564,7 @@ public class TestAuthenticationFilter extends TestCase { AuthenticationToken token = new AuthenticationToken("u", "p", "invalidtype"); token.setExpires(System.currentTimeMillis() + 1000); - Signer signer = new Signer("alfredo".getBytes()); + Signer signer = new Signer("secret".getBytes()); String tokenSigned = signer.sign(token.toString()); Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, tokenSigned); diff --git a/hadoop-common-project/hadoop-common/src/main/docs/src/documentation/content/xdocs/HttpAuthentication.xml b/hadoop-common-project/hadoop-common/src/main/docs/src/documentation/content/xdocs/HttpAuthentication.xml index 5c756ac21c2..ad98c112f76 100644 --- a/hadoop-common-project/hadoop-common/src/main/docs/src/documentation/content/xdocs/HttpAuthentication.xml +++ b/hadoop-common-project/hadoop-common/src/main/docs/src/documentation/content/xdocs/HttpAuthentication.xml @@ -52,7 +52,7 @@

If a custom authentication mechanism is required for the HTTP web-consoles, it is possible to implement a plugin to support the alternate authentication mechanism (refer to - Hadoop Alfredo for details on writing an AuthenticatorHandler). + Hadoop hadoop-auth for details on writing an AuthenticatorHandler).

The next section describes how to configure Hadoop HTTP web-consoles to require user diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/AuthenticationFilterInitializer.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/AuthenticationFilterInitializer.java index 666632d5bfa..37fc3be05c9 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/AuthenticationFilterInitializer.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/AuthenticationFilterInitializer.java @@ -29,7 +29,7 @@ import java.util.HashMap; import java.util.Map; /** - * Initializes Alfredo AuthenticationFilter which provides support for + * Initializes hadoop-auth AuthenticationFilter which provides support for * Kerberos HTTP SPENGO authentication. *

* It enables anonymous access, simple/speudo and Kerberos HTTP SPNEGO @@ -48,9 +48,9 @@ public class AuthenticationFilterInitializer extends FilterInitializer { static final String SIGNATURE_SECRET_FILE = AuthenticationFilter.SIGNATURE_SECRET + ".file"; /** - * Initializes Alfredo AuthenticationFilter. + * Initializes hadoop-auth AuthenticationFilter. *

- * Propagates to Alfredo AuthenticationFilter configuration all Hadoop + * Propagates to hadoop-auth AuthenticationFilter configuration all Hadoop * configuration properties prefixed with "hadoop.http.authentication." * * @param container The filter container diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/conf/httpfs-signature.secret b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/conf/httpfs-signature.secret new file mode 100644 index 00000000000..56466e94dea --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/conf/httpfs-signature.secret @@ -0,0 +1 @@ +hadoop httpfs secret diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/fs/http/server/AuthFilter.java b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/fs/http/server/AuthFilter.java index cc33e0af2cf..ab778f6c692 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/fs/http/server/AuthFilter.java +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/java/org/apache/hadoop/fs/http/server/AuthFilter.java @@ -21,18 +21,23 @@ import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.security.authentication.server.AuthenticationFilter; import javax.servlet.FilterConfig; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; import java.util.Map; import java.util.Properties; /** - * Subclass of Alfredo's AuthenticationFilter that obtains its configuration + * Subclass of hadoop-auth AuthenticationFilter that obtains its configuration * from HttpFSServer's server configuration. */ public class AuthFilter extends AuthenticationFilter { private static final String CONF_PREFIX = "httpfs.authentication."; + private static final String SIGNATURE_SECRET_FILE = SIGNATURE_SECRET + ".file"; + /** - * Returns the Alfredo configuration from HttpFSServer's configuration. + * Returns the hadoop-auth configuration from HttpFSServer's configuration. *

* It returns all HttpFSServer's configuration properties prefixed with * httpfs.authentication. The httpfs.authentication @@ -41,7 +46,7 @@ public class AuthFilter extends AuthenticationFilter { * @param configPrefix parameter not used. * @param filterConfig parameter not used. * - * @return Alfredo configuration read from HttpFSServer's configuration. + * @return hadoop-auth configuration read from HttpFSServer's configuration. */ @Override protected Properties getConfiguration(String configPrefix, FilterConfig filterConfig) { @@ -57,6 +62,25 @@ public class AuthFilter extends AuthenticationFilter { props.setProperty(name, value); } } + + String signatureSecretFile = props.getProperty(SIGNATURE_SECRET_FILE, null); + if (signatureSecretFile == null) { + throw new RuntimeException("Undefined property: " + SIGNATURE_SECRET_FILE); + } + + try { + StringBuilder secret = new StringBuilder(); + Reader reader = new FileReader(signatureSecretFile); + int c = reader.read(); + while (c > -1) { + secret.append((char)c); + c = reader.read(); + } + reader.close(); + props.setProperty(AuthenticationFilter.SIGNATURE_SECRET, secret.toString()); + } catch (IOException ex) { + throw new RuntimeException("Could not read HttpFS signature secret file: " + signatureSecretFile); + } return props; } diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/resources/httpfs-default.xml b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/resources/httpfs-default.xml index 6fac2651f54..c58c925663e 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/resources/httpfs-default.xml +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/main/resources/httpfs-default.xml @@ -69,6 +69,19 @@ + + httpfs.authentication.signature.secret.file + ${httpfs.config.dir}/httpfs-signature.secret + + File containing the secret to sign HttpFS hadoop-auth cookies. + + This file should be readable only by the system user running HttpFS service. + + If multiple HttpFS servers are used in a load-balancer/round-robin fashion, + they should share the secret file. + + + httpfs.authentication.type simple diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/client/TestHttpFSFileSystem.java b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/client/TestHttpFSFileSystem.java index 48bc7240d57..579498713f5 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/client/TestHttpFSFileSystem.java +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/client/TestHttpFSFileSystem.java @@ -45,9 +45,11 @@ import org.mortbay.jetty.webapp.WebAppContext; import java.io.File; import java.io.FileOutputStream; +import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.Writer; import java.net.URL; import java.security.PrivilegedExceptionAction; import java.util.Arrays; @@ -63,6 +65,11 @@ public class TestHttpFSFileSystem extends HFSTestCase { Assert.assertTrue(new File(homeDir, "temp").mkdir()); HttpFSServerWebApp.setHomeDirForCurrentThread(homeDir.getAbsolutePath()); + File secretFile = new File(new File(homeDir, "conf"), "secret"); + Writer w = new FileWriter(secretFile); + w.write("secret"); + w.close(); + String fsDefaultName = TestHdfsHelper.getHdfsConf().get("fs.default.name"); Configuration conf = new Configuration(false); conf.set("httpfs.hadoop.conf:fs.default.name", fsDefaultName); @@ -70,6 +77,7 @@ public class TestHttpFSFileSystem extends HFSTestCase { .getHadoopProxyUserGroups()); conf.set("httpfs.proxyuser." + HadoopUsersConfTestHelper.getHadoopProxyUser() + ".hosts", HadoopUsersConfTestHelper .getHadoopProxyUserHosts()); + conf.set("httpfs.authentication.signature.secret.file", secretFile.getAbsolutePath()); File hoopSite = new File(new File(homeDir, "conf"), "httpfs-site.xml"); OutputStream os = new FileOutputStream(hoopSite); conf.writeXml(os); diff --git a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/server/TestHttpFSServer.java b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/server/TestHttpFSServer.java index faac97de1a0..d397fa35a51 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/server/TestHttpFSServer.java +++ b/hadoop-hdfs-project/hadoop-hdfs-httpfs/src/test/java/org/apache/hadoop/fs/http/server/TestHttpFSServer.java @@ -39,8 +39,10 @@ import org.mortbay.jetty.webapp.WebAppContext; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; +import java.io.FileWriter; import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.Writer; import java.net.HttpURLConnection; import java.net.URL; import java.text.MessageFormat; @@ -65,10 +67,16 @@ public class TestHttpFSServer extends HFSTestCase { Assert.assertTrue(new File(homeDir, "temp").mkdir()); HttpFSServerWebApp.setHomeDirForCurrentThread(homeDir.getAbsolutePath()); + File secretFile = new File(new File(homeDir, "conf"), "secret"); + Writer w = new FileWriter(secretFile); + w.write("secret"); + w.close(); + String fsDefaultName = TestHdfsHelper.getHdfsConf().get("fs.default.name"); Configuration conf = new Configuration(false); conf.set("httpfs.hadoop.conf:fs.default.name", fsDefaultName); conf.set("httpfs.groups." + CommonConfigurationKeys.HADOOP_SECURITY_GROUP_MAPPING, DummyGroupMapping.class.getName()); + conf.set("httpfs.authentication.signature.secret.file", secretFile.getAbsolutePath()); File hoopSite = new File(new File(homeDir, "conf"), "httpfs-site.xml"); OutputStream os = new FileOutputStream(hoopSite); conf.writeXml(os); diff --git a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt index df916217d0c..fd4788e4f78 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt +++ b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt @@ -188,6 +188,9 @@ Trunk (unreleased changes) HttpFS server should check that upload requests have correct content-type. (tucu) + HDFS-2707. HttpFS should read the hadoop-auth secret from a file + instead inline from the configuration. (tucu) + Release 0.23.1 - UNRELEASED INCOMPATIBLE CHANGES