001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.mapred; 019 020import java.io.FileNotFoundException; 021import java.io.IOException; 022import java.net.InetSocketAddress; 023import java.net.URL; 024import java.security.PrivilegedExceptionAction; 025import java.util.ArrayList; 026import java.util.Collection; 027import java.util.List; 028 029import org.apache.hadoop.classification.InterfaceAudience; 030import org.apache.hadoop.classification.InterfaceStability; 031import org.apache.hadoop.conf.Configuration; 032import org.apache.hadoop.fs.FileStatus; 033import org.apache.hadoop.fs.FileSystem; 034import org.apache.hadoop.fs.Path; 035import org.apache.hadoop.io.Text; 036import org.apache.hadoop.mapred.ClusterStatus.BlackListInfo; 037import org.apache.hadoop.mapreduce.Cluster; 038import org.apache.hadoop.mapreduce.ClusterMetrics; 039import org.apache.hadoop.mapreduce.Job; 040import org.apache.hadoop.mapreduce.MRJobConfig; 041import org.apache.hadoop.mapreduce.QueueInfo; 042import org.apache.hadoop.mapreduce.TaskTrackerInfo; 043import org.apache.hadoop.mapreduce.TaskType; 044import org.apache.hadoop.mapreduce.counters.Limits; 045import org.apache.hadoop.mapreduce.filecache.DistributedCache; 046import org.apache.hadoop.mapreduce.security.token.delegation.DelegationTokenIdentifier; 047import org.apache.hadoop.mapreduce.tools.CLI; 048import org.apache.hadoop.mapreduce.util.ConfigUtil; 049import org.apache.hadoop.security.UserGroupInformation; 050import org.apache.hadoop.security.token.SecretManager.InvalidToken; 051import org.apache.hadoop.security.token.Token; 052import org.apache.hadoop.security.token.TokenRenewer; 053import org.apache.hadoop.util.Tool; 054import org.apache.hadoop.util.ToolRunner; 055 056/** 057 * <code>JobClient</code> is the primary interface for the user-job to interact 058 * with the cluster. 059 * 060 * <code>JobClient</code> provides facilities to submit jobs, track their 061 * progress, access component-tasks' reports/logs, get the Map-Reduce cluster 062 * status information etc. 063 * 064 * <p>The job submission process involves: 065 * <ol> 066 * <li> 067 * Checking the input and output specifications of the job. 068 * </li> 069 * <li> 070 * Computing the {@link InputSplit}s for the job. 071 * </li> 072 * <li> 073 * Setup the requisite accounting information for the {@link DistributedCache} 074 * of the job, if necessary. 075 * </li> 076 * <li> 077 * Copying the job's jar and configuration to the map-reduce system directory 078 * on the distributed file-system. 079 * </li> 080 * <li> 081 * Submitting the job to the cluster and optionally monitoring 082 * it's status. 083 * </li> 084 * </ol></p> 085 * 086 * Normally the user creates the application, describes various facets of the 087 * job via {@link JobConf} and then uses the <code>JobClient</code> to submit 088 * the job and monitor its progress. 089 * 090 * <p>Here is an example on how to use <code>JobClient</code>:</p> 091 * <p><blockquote><pre> 092 * // Create a new JobConf 093 * JobConf job = new JobConf(new Configuration(), MyJob.class); 094 * 095 * // Specify various job-specific parameters 096 * job.setJobName("myjob"); 097 * 098 * job.setInputPath(new Path("in")); 099 * job.setOutputPath(new Path("out")); 100 * 101 * job.setMapperClass(MyJob.MyMapper.class); 102 * job.setReducerClass(MyJob.MyReducer.class); 103 * 104 * // Submit the job, then poll for progress until the job is complete 105 * JobClient.runJob(job); 106 * </pre></blockquote></p> 107 * 108 * <h4 id="JobControl">Job Control</h4> 109 * 110 * <p>At times clients would chain map-reduce jobs to accomplish complex tasks 111 * which cannot be done via a single map-reduce job. This is fairly easy since 112 * the output of the job, typically, goes to distributed file-system and that 113 * can be used as the input for the next job.</p> 114 * 115 * <p>However, this also means that the onus on ensuring jobs are complete 116 * (success/failure) lies squarely on the clients. In such situations the 117 * various job-control options are: 118 * <ol> 119 * <li> 120 * {@link #runJob(JobConf)} : submits the job and returns only after 121 * the job has completed. 122 * </li> 123 * <li> 124 * {@link #submitJob(JobConf)} : only submits the job, then poll the 125 * returned handle to the {@link RunningJob} to query status and make 126 * scheduling decisions. 127 * </li> 128 * <li> 129 * {@link JobConf#setJobEndNotificationURI(String)} : setup a notification 130 * on job-completion, thus avoiding polling. 131 * </li> 132 * </ol></p> 133 * 134 * @see JobConf 135 * @see ClusterStatus 136 * @see Tool 137 * @see DistributedCache 138 */ 139@InterfaceAudience.Public 140@InterfaceStability.Stable 141public class JobClient extends CLI implements AutoCloseable { 142 143 @InterfaceAudience.Private 144 public static final String MAPREDUCE_CLIENT_RETRY_POLICY_ENABLED_KEY = 145 "mapreduce.jobclient.retry.policy.enabled"; 146 @InterfaceAudience.Private 147 public static final boolean MAPREDUCE_CLIENT_RETRY_POLICY_ENABLED_DEFAULT = 148 false; 149 @InterfaceAudience.Private 150 public static final String MAPREDUCE_CLIENT_RETRY_POLICY_SPEC_KEY = 151 "mapreduce.jobclient.retry.policy.spec"; 152 @InterfaceAudience.Private 153 public static final String MAPREDUCE_CLIENT_RETRY_POLICY_SPEC_DEFAULT = 154 "10000,6,60000,10"; // t1,n1,t2,n2,... 155 156 public static enum TaskStatusFilter { NONE, KILLED, FAILED, SUCCEEDED, ALL } 157 private TaskStatusFilter taskOutputFilter = TaskStatusFilter.FAILED; 158 159 private int maxRetry = MRJobConfig.DEFAULT_MR_CLIENT_JOB_MAX_RETRIES; 160 private long retryInterval = 161 MRJobConfig.DEFAULT_MR_CLIENT_JOB_RETRY_INTERVAL; 162 163 static{ 164 ConfigUtil.loadResources(); 165 } 166 167 /** 168 * A NetworkedJob is an implementation of RunningJob. It holds 169 * a JobProfile object to provide some info, and interacts with the 170 * remote service to provide certain functionality. 171 */ 172 static class NetworkedJob implements RunningJob { 173 Job job; 174 /** 175 * We store a JobProfile and a timestamp for when we last 176 * acquired the job profile. If the job is null, then we cannot 177 * perform any of the tasks. The job might be null if the cluster 178 * has completely forgotten about the job. (eg, 24 hours after the 179 * job completes.) 180 */ 181 public NetworkedJob(JobStatus status, Cluster cluster) throws IOException { 182 this(status, cluster, new JobConf(status.getJobFile())); 183 } 184 185 private NetworkedJob(JobStatus status, Cluster cluster, JobConf conf) 186 throws IOException { 187 this(Job.getInstance(cluster, status, conf)); 188 } 189 190 public NetworkedJob(Job job) throws IOException { 191 this.job = job; 192 } 193 194 public Configuration getConfiguration() { 195 return job.getConfiguration(); 196 } 197 198 /** 199 * An identifier for the job 200 */ 201 public JobID getID() { 202 return JobID.downgrade(job.getJobID()); 203 } 204 205 /** @deprecated This method is deprecated and will be removed. Applications should 206 * rather use {@link #getID()}.*/ 207 @Deprecated 208 public String getJobID() { 209 return getID().toString(); 210 } 211 212 /** 213 * The user-specified job name 214 */ 215 public String getJobName() { 216 return job.getJobName(); 217 } 218 219 /** 220 * The name of the job file 221 */ 222 public String getJobFile() { 223 return job.getJobFile(); 224 } 225 226 /** 227 * A URL where the job's status can be seen 228 */ 229 public String getTrackingURL() { 230 return job.getTrackingURL(); 231 } 232 233 /** 234 * A float between 0.0 and 1.0, indicating the % of map work 235 * completed. 236 */ 237 public float mapProgress() throws IOException { 238 return job.mapProgress(); 239 } 240 241 /** 242 * A float between 0.0 and 1.0, indicating the % of reduce work 243 * completed. 244 */ 245 public float reduceProgress() throws IOException { 246 return job.reduceProgress(); 247 } 248 249 /** 250 * A float between 0.0 and 1.0, indicating the % of cleanup work 251 * completed. 252 */ 253 public float cleanupProgress() throws IOException { 254 try { 255 return job.cleanupProgress(); 256 } catch (InterruptedException ie) { 257 throw new IOException(ie); 258 } 259 } 260 261 /** 262 * A float between 0.0 and 1.0, indicating the % of setup work 263 * completed. 264 */ 265 public float setupProgress() throws IOException { 266 return job.setupProgress(); 267 } 268 269 /** 270 * Returns immediately whether the whole job is done yet or not. 271 */ 272 public synchronized boolean isComplete() throws IOException { 273 return job.isComplete(); 274 } 275 276 /** 277 * True iff job completed successfully. 278 */ 279 public synchronized boolean isSuccessful() throws IOException { 280 return job.isSuccessful(); 281 } 282 283 /** 284 * Blocks until the job is finished 285 */ 286 public void waitForCompletion() throws IOException { 287 try { 288 job.waitForCompletion(false); 289 } catch (InterruptedException ie) { 290 throw new IOException(ie); 291 } catch (ClassNotFoundException ce) { 292 throw new IOException(ce); 293 } 294 } 295 296 /** 297 * Tells the service to get the state of the current job. 298 */ 299 public synchronized int getJobState() throws IOException { 300 try { 301 return job.getJobState().getValue(); 302 } catch (InterruptedException ie) { 303 throw new IOException(ie); 304 } 305 } 306 307 /** 308 * Tells the service to terminate the current job. 309 */ 310 public synchronized void killJob() throws IOException { 311 job.killJob(); 312 } 313 314 315 /** Set the priority of the job. 316 * @param priority new priority of the job. 317 */ 318 public synchronized void setJobPriority(String priority) 319 throws IOException { 320 try { 321 job.setPriority( 322 org.apache.hadoop.mapreduce.JobPriority.valueOf(priority)); 323 } catch (InterruptedException ie) { 324 throw new IOException(ie); 325 } 326 } 327 328 /** 329 * Kill indicated task attempt. 330 * @param taskId the id of the task to kill. 331 * @param shouldFail if true the task is failed and added to failed tasks list, otherwise 332 * it is just killed, w/o affecting job failure status. 333 */ 334 public synchronized void killTask(TaskAttemptID taskId, 335 boolean shouldFail) throws IOException { 336 if (shouldFail) { 337 job.failTask(taskId); 338 } else { 339 job.killTask(taskId); 340 } 341 } 342 343 /** @deprecated Applications should rather use {@link #killTask(TaskAttemptID, boolean)}*/ 344 @Deprecated 345 public synchronized void killTask(String taskId, boolean shouldFail) throws IOException { 346 killTask(TaskAttemptID.forName(taskId), shouldFail); 347 } 348 349 /** 350 * Fetch task completion events from cluster for this job. 351 */ 352 public synchronized TaskCompletionEvent[] getTaskCompletionEvents( 353 int startFrom) throws IOException { 354 try { 355 org.apache.hadoop.mapreduce.TaskCompletionEvent[] acls = 356 job.getTaskCompletionEvents(startFrom, 10); 357 TaskCompletionEvent[] ret = new TaskCompletionEvent[acls.length]; 358 for (int i = 0 ; i < acls.length; i++ ) { 359 ret[i] = TaskCompletionEvent.downgrade(acls[i]); 360 } 361 return ret; 362 } catch (InterruptedException ie) { 363 throw new IOException(ie); 364 } 365 } 366 367 /** 368 * Dump stats to screen 369 */ 370 @Override 371 public String toString() { 372 return job.toString(); 373 } 374 375 /** 376 * Returns the counters for this job 377 */ 378 public Counters getCounters() throws IOException { 379 Counters result = null; 380 org.apache.hadoop.mapreduce.Counters temp = job.getCounters(); 381 if(temp != null) { 382 result = Counters.downgrade(temp); 383 } 384 return result; 385 } 386 387 @Override 388 public String[] getTaskDiagnostics(TaskAttemptID id) throws IOException { 389 try { 390 return job.getTaskDiagnostics(id); 391 } catch (InterruptedException ie) { 392 throw new IOException(ie); 393 } 394 } 395 396 public String getHistoryUrl() throws IOException { 397 try { 398 return job.getHistoryUrl(); 399 } catch (InterruptedException ie) { 400 throw new IOException(ie); 401 } 402 } 403 404 public boolean isRetired() throws IOException { 405 try { 406 return job.isRetired(); 407 } catch (InterruptedException ie) { 408 throw new IOException(ie); 409 } 410 } 411 412 boolean monitorAndPrintJob() throws IOException, InterruptedException { 413 return job.monitorAndPrintJob(); 414 } 415 416 @Override 417 public String getFailureInfo() throws IOException { 418 try { 419 return job.getStatus().getFailureInfo(); 420 } catch (InterruptedException ie) { 421 throw new IOException(ie); 422 } 423 } 424 425 @Override 426 public JobStatus getJobStatus() throws IOException { 427 try { 428 return JobStatus.downgrade(job.getStatus()); 429 } catch (InterruptedException ie) { 430 throw new IOException(ie); 431 } 432 } 433 } 434 435 /** 436 * Ugi of the client. We store this ugi when the client is created and 437 * then make sure that the same ugi is used to run the various protocols. 438 */ 439 UserGroupInformation clientUgi; 440 441 /** 442 * Create a job client. 443 */ 444 public JobClient() { 445 } 446 447 /** 448 * Build a job client with the given {@link JobConf}, and connect to the 449 * default cluster 450 * 451 * @param conf the job configuration. 452 * @throws IOException 453 */ 454 public JobClient(JobConf conf) throws IOException { 455 init(conf); 456 } 457 458 /** 459 * Build a job client with the given {@link Configuration}, 460 * and connect to the default cluster 461 * 462 * @param conf the configuration. 463 * @throws IOException 464 */ 465 public JobClient(Configuration conf) throws IOException { 466 init(new JobConf(conf)); 467 } 468 469 /** 470 * Connect to the default cluster 471 * @param conf the job configuration. 472 * @throws IOException 473 */ 474 public void init(JobConf conf) throws IOException { 475 setConf(conf); 476 Limits.init(conf); 477 cluster = new Cluster(conf); 478 clientUgi = UserGroupInformation.getCurrentUser(); 479 480 maxRetry = conf.getInt(MRJobConfig.MR_CLIENT_JOB_MAX_RETRIES, 481 MRJobConfig.DEFAULT_MR_CLIENT_JOB_MAX_RETRIES); 482 483 retryInterval = 484 conf.getLong(MRJobConfig.MR_CLIENT_JOB_RETRY_INTERVAL, 485 MRJobConfig.DEFAULT_MR_CLIENT_JOB_RETRY_INTERVAL); 486 487 } 488 489 /** 490 * Build a job client, connect to the indicated job tracker. 491 * 492 * @param jobTrackAddr the job tracker to connect to. 493 * @param conf configuration. 494 */ 495 public JobClient(InetSocketAddress jobTrackAddr, 496 Configuration conf) throws IOException { 497 cluster = new Cluster(jobTrackAddr, conf); 498 clientUgi = UserGroupInformation.getCurrentUser(); 499 } 500 501 /** 502 * Close the <code>JobClient</code>. 503 */ 504 @Override 505 public synchronized void close() throws IOException { 506 cluster.close(); 507 } 508 509 /** 510 * Get a filesystem handle. We need this to prepare jobs 511 * for submission to the MapReduce system. 512 * 513 * @return the filesystem handle. 514 */ 515 public synchronized FileSystem getFs() throws IOException { 516 try { 517 return cluster.getFileSystem(); 518 } catch (InterruptedException ie) { 519 throw new IOException(ie); 520 } 521 } 522 523 /** 524 * Get a handle to the Cluster 525 */ 526 public Cluster getClusterHandle() { 527 return cluster; 528 } 529 530 /** 531 * Submit a job to the MR system. 532 * 533 * This returns a handle to the {@link RunningJob} which can be used to track 534 * the running-job. 535 * 536 * @param jobFile the job configuration. 537 * @return a handle to the {@link RunningJob} which can be used to track the 538 * running-job. 539 * @throws FileNotFoundException 540 * @throws InvalidJobConfException 541 * @throws IOException 542 */ 543 public RunningJob submitJob(String jobFile) throws FileNotFoundException, 544 InvalidJobConfException, 545 IOException { 546 // Load in the submitted job details 547 JobConf job = new JobConf(jobFile); 548 return submitJob(job); 549 } 550 551 /** 552 * Submit a job to the MR system. 553 * This returns a handle to the {@link RunningJob} which can be used to track 554 * the running-job. 555 * 556 * @param conf the job configuration. 557 * @return a handle to the {@link RunningJob} which can be used to track the 558 * running-job. 559 * @throws FileNotFoundException 560 * @throws IOException 561 */ 562 public RunningJob submitJob(final JobConf conf) throws FileNotFoundException, 563 IOException { 564 return submitJobInternal(conf); 565 } 566 567 @InterfaceAudience.Private 568 public RunningJob submitJobInternal(final JobConf conf) 569 throws FileNotFoundException, IOException { 570 try { 571 conf.setBooleanIfUnset("mapred.mapper.new-api", false); 572 conf.setBooleanIfUnset("mapred.reducer.new-api", false); 573 Job job = clientUgi.doAs(new PrivilegedExceptionAction<Job> () { 574 @Override 575 public Job run() throws IOException, ClassNotFoundException, 576 InterruptedException { 577 Job job = Job.getInstance(conf); 578 job.submit(); 579 return job; 580 } 581 }); 582 // update our Cluster instance with the one created by Job for submission 583 // (we can't pass our Cluster instance to Job, since Job wraps the config 584 // instance, and the two configs would then diverge) 585 cluster = job.getCluster(); 586 return new NetworkedJob(job); 587 } catch (InterruptedException ie) { 588 throw new IOException("interrupted", ie); 589 } 590 } 591 592 private Job getJobUsingCluster(final JobID jobid) throws IOException, 593 InterruptedException { 594 return clientUgi.doAs(new PrivilegedExceptionAction<Job>() { 595 public Job run() throws IOException, InterruptedException { 596 return cluster.getJob(jobid); 597 } 598 }); 599 } 600 601 protected RunningJob getJobInner(final JobID jobid) throws IOException { 602 try { 603 604 Job job = getJobUsingCluster(jobid); 605 if (job != null) { 606 JobStatus status = JobStatus.downgrade(job.getStatus()); 607 if (status != null) { 608 return new NetworkedJob(status, cluster, 609 new JobConf(job.getConfiguration())); 610 } 611 } 612 } catch (InterruptedException ie) { 613 throw new IOException(ie); 614 } 615 return null; 616 } 617 618 /** 619 * Get an {@link RunningJob} object to track an ongoing job. Returns 620 * null if the id does not correspond to any known job. 621 * 622 * @param jobid the jobid of the job. 623 * @return the {@link RunningJob} handle to track the job, null if the 624 * <code>jobid</code> doesn't correspond to any known job. 625 * @throws IOException 626 */ 627 public RunningJob getJob(final JobID jobid) throws IOException { 628 for (int i = 0;i <= maxRetry;i++) { 629 if (i > 0) { 630 try { 631 Thread.sleep(retryInterval); 632 } catch (Exception e) { } 633 } 634 RunningJob job = getJobInner(jobid); 635 if (job != null) { 636 return job; 637 } 638 } 639 return null; 640 } 641 642 /**@deprecated Applications should rather use {@link #getJob(JobID)}. 643 */ 644 @Deprecated 645 public RunningJob getJob(String jobid) throws IOException { 646 return getJob(JobID.forName(jobid)); 647 } 648 649 private static final TaskReport[] EMPTY_TASK_REPORTS = new TaskReport[0]; 650 651 /** 652 * Get the information of the current state of the map tasks of a job. 653 * 654 * @param jobId the job to query. 655 * @return the list of all of the map tips. 656 * @throws IOException 657 */ 658 public TaskReport[] getMapTaskReports(JobID jobId) throws IOException { 659 return getTaskReports(jobId, TaskType.MAP); 660 } 661 662 private TaskReport[] getTaskReports(final JobID jobId, TaskType type) throws 663 IOException { 664 try { 665 Job j = getJobUsingCluster(jobId); 666 if(j == null) { 667 return EMPTY_TASK_REPORTS; 668 } 669 return TaskReport.downgradeArray(j.getTaskReports(type)); 670 } catch (InterruptedException ie) { 671 throw new IOException(ie); 672 } 673 } 674 675 /**@deprecated Applications should rather use {@link #getMapTaskReports(JobID)}*/ 676 @Deprecated 677 public TaskReport[] getMapTaskReports(String jobId) throws IOException { 678 return getMapTaskReports(JobID.forName(jobId)); 679 } 680 681 /** 682 * Get the information of the current state of the reduce tasks of a job. 683 * 684 * @param jobId the job to query. 685 * @return the list of all of the reduce tips. 686 * @throws IOException 687 */ 688 public TaskReport[] getReduceTaskReports(JobID jobId) throws IOException { 689 return getTaskReports(jobId, TaskType.REDUCE); 690 } 691 692 /** 693 * Get the information of the current state of the cleanup tasks of a job. 694 * 695 * @param jobId the job to query. 696 * @return the list of all of the cleanup tips. 697 * @throws IOException 698 */ 699 public TaskReport[] getCleanupTaskReports(JobID jobId) throws IOException { 700 return getTaskReports(jobId, TaskType.JOB_CLEANUP); 701 } 702 703 /** 704 * Get the information of the current state of the setup tasks of a job. 705 * 706 * @param jobId the job to query. 707 * @return the list of all of the setup tips. 708 * @throws IOException 709 */ 710 public TaskReport[] getSetupTaskReports(JobID jobId) throws IOException { 711 return getTaskReports(jobId, TaskType.JOB_SETUP); 712 } 713 714 715 /**@deprecated Applications should rather use {@link #getReduceTaskReports(JobID)}*/ 716 @Deprecated 717 public TaskReport[] getReduceTaskReports(String jobId) throws IOException { 718 return getReduceTaskReports(JobID.forName(jobId)); 719 } 720 721 /** 722 * Display the information about a job's tasks, of a particular type and 723 * in a particular state 724 * 725 * @param jobId the ID of the job 726 * @param type the type of the task (map/reduce/setup/cleanup) 727 * @param state the state of the task 728 * (pending/running/completed/failed/killed) 729 */ 730 public void displayTasks(final JobID jobId, String type, String state) 731 throws IOException { 732 try { 733 Job job = getJobUsingCluster(jobId); 734 super.displayTasks(job, type, state); 735 } catch (InterruptedException ie) { 736 throw new IOException(ie); 737 } 738 } 739 740 /** 741 * Get status information about the Map-Reduce cluster. 742 * 743 * @return the status information about the Map-Reduce cluster as an object 744 * of {@link ClusterStatus}. 745 * @throws IOException 746 */ 747 public ClusterStatus getClusterStatus() throws IOException { 748 try { 749 return clientUgi.doAs(new PrivilegedExceptionAction<ClusterStatus>() { 750 public ClusterStatus run() throws IOException, InterruptedException { 751 ClusterMetrics metrics = cluster.getClusterStatus(); 752 return new ClusterStatus(metrics.getTaskTrackerCount(), metrics 753 .getBlackListedTaskTrackerCount(), cluster 754 .getTaskTrackerExpiryInterval(), metrics.getOccupiedMapSlots(), 755 metrics.getOccupiedReduceSlots(), metrics.getMapSlotCapacity(), 756 metrics.getReduceSlotCapacity(), cluster.getJobTrackerStatus(), 757 metrics.getDecommissionedTaskTrackerCount(), metrics 758 .getGrayListedTaskTrackerCount()); 759 } 760 }); 761 } catch (InterruptedException ie) { 762 throw new IOException(ie); 763 } 764 } 765 766 private Collection<String> arrayToStringList(TaskTrackerInfo[] objs) { 767 Collection<String> list = new ArrayList<String>(); 768 for (TaskTrackerInfo info: objs) { 769 list.add(info.getTaskTrackerName()); 770 } 771 return list; 772 } 773 774 private Collection<BlackListInfo> arrayToBlackListInfo(TaskTrackerInfo[] objs) { 775 Collection<BlackListInfo> list = new ArrayList<BlackListInfo>(); 776 for (TaskTrackerInfo info: objs) { 777 BlackListInfo binfo = new BlackListInfo(); 778 binfo.setTrackerName(info.getTaskTrackerName()); 779 binfo.setReasonForBlackListing(info.getReasonForBlacklist()); 780 binfo.setBlackListReport(info.getBlacklistReport()); 781 list.add(binfo); 782 } 783 return list; 784 } 785 786 /** 787 * Get status information about the Map-Reduce cluster. 788 * 789 * @param detailed if true then get a detailed status including the 790 * tracker names 791 * @return the status information about the Map-Reduce cluster as an object 792 * of {@link ClusterStatus}. 793 * @throws IOException 794 */ 795 public ClusterStatus getClusterStatus(boolean detailed) throws IOException { 796 try { 797 return clientUgi.doAs(new PrivilegedExceptionAction<ClusterStatus>() { 798 public ClusterStatus run() throws IOException, InterruptedException { 799 ClusterMetrics metrics = cluster.getClusterStatus(); 800 return new ClusterStatus(arrayToStringList(cluster.getActiveTaskTrackers()), 801 arrayToBlackListInfo(cluster.getBlackListedTaskTrackers()), 802 cluster.getTaskTrackerExpiryInterval(), metrics.getOccupiedMapSlots(), 803 metrics.getOccupiedReduceSlots(), metrics.getMapSlotCapacity(), 804 metrics.getReduceSlotCapacity(), 805 cluster.getJobTrackerStatus()); 806 } 807 }); 808 } catch (InterruptedException ie) { 809 throw new IOException(ie); 810 } 811 } 812 813 814 /** 815 * Get the jobs that are not completed and not failed. 816 * 817 * @return array of {@link JobStatus} for the running/to-be-run jobs. 818 * @throws IOException 819 */ 820 public JobStatus[] jobsToComplete() throws IOException { 821 List<JobStatus> stats = new ArrayList<JobStatus>(); 822 for (JobStatus stat : getAllJobs()) { 823 if (!stat.isJobComplete()) { 824 stats.add(stat); 825 } 826 } 827 return stats.toArray(new JobStatus[0]); 828 } 829 830 /** 831 * Get the jobs that are submitted. 832 * 833 * @return array of {@link JobStatus} for the submitted jobs. 834 * @throws IOException 835 */ 836 public JobStatus[] getAllJobs() throws IOException { 837 try { 838 org.apache.hadoop.mapreduce.JobStatus[] jobs = 839 clientUgi.doAs(new PrivilegedExceptionAction< 840 org.apache.hadoop.mapreduce.JobStatus[]> () { 841 public org.apache.hadoop.mapreduce.JobStatus[] run() 842 throws IOException, InterruptedException { 843 return cluster.getAllJobStatuses(); 844 } 845 }); 846 JobStatus[] stats = new JobStatus[jobs.length]; 847 for (int i = 0; i < jobs.length; i++) { 848 stats[i] = JobStatus.downgrade(jobs[i]); 849 } 850 return stats; 851 } catch (InterruptedException ie) { 852 throw new IOException(ie); 853 } 854 } 855 856 /** 857 * Utility that submits a job, then polls for progress until the job is 858 * complete. 859 * 860 * @param job the job configuration. 861 * @throws IOException if the job fails 862 */ 863 public static RunningJob runJob(JobConf job) throws IOException { 864 JobClient jc = new JobClient(job); 865 RunningJob rj = jc.submitJob(job); 866 try { 867 if (!jc.monitorAndPrintJob(job, rj)) { 868 throw new IOException("Job failed!"); 869 } 870 } catch (InterruptedException ie) { 871 Thread.currentThread().interrupt(); 872 } 873 return rj; 874 } 875 876 /** 877 * Monitor a job and print status in real-time as progress is made and tasks 878 * fail. 879 * @param conf the job's configuration 880 * @param job the job to track 881 * @return true if the job succeeded 882 * @throws IOException if communication to the JobTracker fails 883 */ 884 public boolean monitorAndPrintJob(JobConf conf, 885 RunningJob job 886 ) throws IOException, InterruptedException { 887 return ((NetworkedJob)job).monitorAndPrintJob(); 888 } 889 890 static String getTaskLogURL(TaskAttemptID taskId, String baseUrl) { 891 return (baseUrl + "/tasklog?plaintext=true&attemptid=" + taskId); 892 } 893 894 static Configuration getConfiguration(String jobTrackerSpec) 895 { 896 Configuration conf = new Configuration(); 897 if (jobTrackerSpec != null) { 898 if (jobTrackerSpec.indexOf(":") >= 0) { 899 conf.set("mapred.job.tracker", jobTrackerSpec); 900 } else { 901 String classpathFile = "hadoop-" + jobTrackerSpec + ".xml"; 902 URL validate = conf.getResource(classpathFile); 903 if (validate == null) { 904 throw new RuntimeException(classpathFile + " not found on CLASSPATH"); 905 } 906 conf.addResource(classpathFile); 907 } 908 } 909 return conf; 910 } 911 912 /** 913 * Sets the output filter for tasks. only those tasks are printed whose 914 * output matches the filter. 915 * @param newValue task filter. 916 */ 917 @Deprecated 918 public void setTaskOutputFilter(TaskStatusFilter newValue){ 919 this.taskOutputFilter = newValue; 920 } 921 922 /** 923 * Get the task output filter out of the JobConf. 924 * 925 * @param job the JobConf to examine. 926 * @return the filter level. 927 */ 928 public static TaskStatusFilter getTaskOutputFilter(JobConf job) { 929 return TaskStatusFilter.valueOf(job.get("jobclient.output.filter", 930 "FAILED")); 931 } 932 933 /** 934 * Modify the JobConf to set the task output filter. 935 * 936 * @param job the JobConf to modify. 937 * @param newValue the value to set. 938 */ 939 public static void setTaskOutputFilter(JobConf job, 940 TaskStatusFilter newValue) { 941 job.set("jobclient.output.filter", newValue.toString()); 942 } 943 944 /** 945 * Returns task output filter. 946 * @return task filter. 947 */ 948 @Deprecated 949 public TaskStatusFilter getTaskOutputFilter(){ 950 return this.taskOutputFilter; 951 } 952 953 protected long getCounter(org.apache.hadoop.mapreduce.Counters cntrs, 954 String counterGroupName, String counterName) throws IOException { 955 Counters counters = Counters.downgrade(cntrs); 956 return counters.findCounter(counterGroupName, counterName).getValue(); 957 } 958 959 /** 960 * Get status information about the max available Maps in the cluster. 961 * 962 * @return the max available Maps in the cluster 963 * @throws IOException 964 */ 965 public int getDefaultMaps() throws IOException { 966 try { 967 return clientUgi.doAs(new PrivilegedExceptionAction<Integer>() { 968 @Override 969 public Integer run() throws IOException, InterruptedException { 970 return cluster.getClusterStatus().getMapSlotCapacity(); 971 } 972 }); 973 } catch (InterruptedException ie) { 974 throw new IOException(ie); 975 } 976 } 977 978 /** 979 * Get status information about the max available Reduces in the cluster. 980 * 981 * @return the max available Reduces in the cluster 982 * @throws IOException 983 */ 984 public int getDefaultReduces() throws IOException { 985 try { 986 return clientUgi.doAs(new PrivilegedExceptionAction<Integer>() { 987 @Override 988 public Integer run() throws IOException, InterruptedException { 989 return cluster.getClusterStatus().getReduceSlotCapacity(); 990 } 991 }); 992 } catch (InterruptedException ie) { 993 throw new IOException(ie); 994 } 995 } 996 997 /** 998 * Grab the jobtracker system directory path where job-specific files are to be placed. 999 * 1000 * @return the system directory where job-specific files are to be placed. 1001 */ 1002 public Path getSystemDir() { 1003 try { 1004 return clientUgi.doAs(new PrivilegedExceptionAction<Path>() { 1005 @Override 1006 public Path run() throws IOException, InterruptedException { 1007 return cluster.getSystemDir(); 1008 } 1009 }); 1010 } catch (IOException ioe) { 1011 return null; 1012 } catch (InterruptedException ie) { 1013 return null; 1014 } 1015 } 1016 1017 /** 1018 * Checks if the job directory is clean and has all the required components 1019 * for (re) starting the job 1020 */ 1021 public static boolean isJobDirValid(Path jobDirPath, FileSystem fs) 1022 throws IOException { 1023 FileStatus[] contents = fs.listStatus(jobDirPath); 1024 int matchCount = 0; 1025 if (contents != null && contents.length >= 2) { 1026 for (FileStatus status : contents) { 1027 if ("job.xml".equals(status.getPath().getName())) { 1028 ++matchCount; 1029 } 1030 if ("job.split".equals(status.getPath().getName())) { 1031 ++matchCount; 1032 } 1033 } 1034 if (matchCount == 2) { 1035 return true; 1036 } 1037 } 1038 return false; 1039 } 1040 1041 /** 1042 * Fetch the staging area directory for the application 1043 * 1044 * @return path to staging area directory 1045 * @throws IOException 1046 */ 1047 public Path getStagingAreaDir() throws IOException { 1048 try { 1049 return clientUgi.doAs(new PrivilegedExceptionAction<Path>() { 1050 @Override 1051 public Path run() throws IOException, InterruptedException { 1052 return cluster.getStagingAreaDir(); 1053 } 1054 }); 1055 } catch (InterruptedException ie) { 1056 // throw RuntimeException instead for compatibility reasons 1057 throw new RuntimeException(ie); 1058 } 1059 } 1060 1061 private JobQueueInfo getJobQueueInfo(QueueInfo queue) { 1062 JobQueueInfo ret = new JobQueueInfo(queue); 1063 // make sure to convert any children 1064 if (queue.getQueueChildren().size() > 0) { 1065 List<JobQueueInfo> childQueues = new ArrayList<JobQueueInfo>(queue 1066 .getQueueChildren().size()); 1067 for (QueueInfo child : queue.getQueueChildren()) { 1068 childQueues.add(getJobQueueInfo(child)); 1069 } 1070 ret.setChildren(childQueues); 1071 } 1072 return ret; 1073 } 1074 1075 private JobQueueInfo[] getJobQueueInfoArray(QueueInfo[] queues) 1076 throws IOException { 1077 JobQueueInfo[] ret = new JobQueueInfo[queues.length]; 1078 for (int i = 0; i < queues.length; i++) { 1079 ret[i] = getJobQueueInfo(queues[i]); 1080 } 1081 return ret; 1082 } 1083 1084 /** 1085 * Returns an array of queue information objects about root level queues 1086 * configured 1087 * 1088 * @return the array of root level JobQueueInfo objects 1089 * @throws IOException 1090 */ 1091 public JobQueueInfo[] getRootQueues() throws IOException { 1092 try { 1093 return clientUgi.doAs(new PrivilegedExceptionAction<JobQueueInfo[]>() { 1094 public JobQueueInfo[] run() throws IOException, InterruptedException { 1095 return getJobQueueInfoArray(cluster.getRootQueues()); 1096 } 1097 }); 1098 } catch (InterruptedException ie) { 1099 throw new IOException(ie); 1100 } 1101 } 1102 1103 /** 1104 * Returns an array of queue information objects about immediate children 1105 * of queue queueName. 1106 * 1107 * @param queueName 1108 * @return the array of immediate children JobQueueInfo objects 1109 * @throws IOException 1110 */ 1111 public JobQueueInfo[] getChildQueues(final String queueName) throws IOException { 1112 try { 1113 return clientUgi.doAs(new PrivilegedExceptionAction<JobQueueInfo[]>() { 1114 public JobQueueInfo[] run() throws IOException, InterruptedException { 1115 return getJobQueueInfoArray(cluster.getChildQueues(queueName)); 1116 } 1117 }); 1118 } catch (InterruptedException ie) { 1119 throw new IOException(ie); 1120 } 1121 } 1122 1123 /** 1124 * Return an array of queue information objects about all the Job Queues 1125 * configured. 1126 * 1127 * @return Array of JobQueueInfo objects 1128 * @throws IOException 1129 */ 1130 public JobQueueInfo[] getQueues() throws IOException { 1131 try { 1132 return clientUgi.doAs(new PrivilegedExceptionAction<JobQueueInfo[]>() { 1133 public JobQueueInfo[] run() throws IOException, InterruptedException { 1134 return getJobQueueInfoArray(cluster.getQueues()); 1135 } 1136 }); 1137 } catch (InterruptedException ie) { 1138 throw new IOException(ie); 1139 } 1140 } 1141 1142 /** 1143 * Gets all the jobs which were added to particular Job Queue 1144 * 1145 * @param queueName name of the Job Queue 1146 * @return Array of jobs present in the job queue 1147 * @throws IOException 1148 */ 1149 1150 public JobStatus[] getJobsFromQueue(final String queueName) throws IOException { 1151 try { 1152 QueueInfo queue = clientUgi.doAs(new PrivilegedExceptionAction<QueueInfo>() { 1153 @Override 1154 public QueueInfo run() throws IOException, InterruptedException { 1155 return cluster.getQueue(queueName); 1156 } 1157 }); 1158 if (queue == null) { 1159 return null; 1160 } 1161 org.apache.hadoop.mapreduce.JobStatus[] stats = 1162 queue.getJobStatuses(); 1163 JobStatus[] ret = new JobStatus[stats.length]; 1164 for (int i = 0 ; i < stats.length; i++ ) { 1165 ret[i] = JobStatus.downgrade(stats[i]); 1166 } 1167 return ret; 1168 } catch (InterruptedException ie) { 1169 throw new IOException(ie); 1170 } 1171 } 1172 1173 /** 1174 * Gets the queue information associated to a particular Job Queue 1175 * 1176 * @param queueName name of the job queue. 1177 * @return Queue information associated to particular queue. 1178 * @throws IOException 1179 */ 1180 public JobQueueInfo getQueueInfo(final String queueName) throws IOException { 1181 try { 1182 QueueInfo queueInfo = clientUgi.doAs(new 1183 PrivilegedExceptionAction<QueueInfo>() { 1184 public QueueInfo run() throws IOException, InterruptedException { 1185 return cluster.getQueue(queueName); 1186 } 1187 }); 1188 if (queueInfo != null) { 1189 return new JobQueueInfo(queueInfo); 1190 } 1191 return null; 1192 } catch (InterruptedException ie) { 1193 throw new IOException(ie); 1194 } 1195 } 1196 1197 /** 1198 * Gets the Queue ACLs for current user 1199 * @return array of QueueAclsInfo object for current user. 1200 * @throws IOException 1201 */ 1202 public QueueAclsInfo[] getQueueAclsForCurrentUser() throws IOException { 1203 try { 1204 org.apache.hadoop.mapreduce.QueueAclsInfo[] acls = 1205 clientUgi.doAs(new 1206 PrivilegedExceptionAction 1207 <org.apache.hadoop.mapreduce.QueueAclsInfo[]>() { 1208 public org.apache.hadoop.mapreduce.QueueAclsInfo[] run() 1209 throws IOException, InterruptedException { 1210 return cluster.getQueueAclsForCurrentUser(); 1211 } 1212 }); 1213 QueueAclsInfo[] ret = new QueueAclsInfo[acls.length]; 1214 for (int i = 0 ; i < acls.length; i++ ) { 1215 ret[i] = QueueAclsInfo.downgrade(acls[i]); 1216 } 1217 return ret; 1218 } catch (InterruptedException ie) { 1219 throw new IOException(ie); 1220 } 1221 } 1222 1223 /** 1224 * Get a delegation token for the user from the JobTracker. 1225 * @param renewer the user who can renew the token 1226 * @return the new token 1227 * @throws IOException 1228 */ 1229 public Token<DelegationTokenIdentifier> 1230 getDelegationToken(final Text renewer) throws IOException, InterruptedException { 1231 return clientUgi.doAs(new 1232 PrivilegedExceptionAction<Token<DelegationTokenIdentifier>>() { 1233 public Token<DelegationTokenIdentifier> run() throws IOException, 1234 InterruptedException { 1235 return cluster.getDelegationToken(renewer); 1236 } 1237 }); 1238 } 1239 1240 /** 1241 * Renew a delegation token 1242 * @param token the token to renew 1243 * @return true if the renewal went well 1244 * @throws InvalidToken 1245 * @throws IOException 1246 * @deprecated Use {@link Token#renew} instead 1247 */ 1248 public long renewDelegationToken(Token<DelegationTokenIdentifier> token 1249 ) throws InvalidToken, IOException, 1250 InterruptedException { 1251 return token.renew(getConf()); 1252 } 1253 1254 /** 1255 * Cancel a delegation token from the JobTracker 1256 * @param token the token to cancel 1257 * @throws IOException 1258 * @deprecated Use {@link Token#cancel} instead 1259 */ 1260 public void cancelDelegationToken(Token<DelegationTokenIdentifier> token 1261 ) throws InvalidToken, IOException, 1262 InterruptedException { 1263 token.cancel(getConf()); 1264 } 1265 1266 /** 1267 */ 1268 public static void main(String argv[]) throws Exception { 1269 int res = ToolRunner.run(new JobClient(), argv); 1270 System.exit(res); 1271 } 1272} 1273