Browse Source

HADOOP-3930. Add common interfaces for the pluggable schedulers and the
cli & gui clients. (Sreekanth Ramakrishnan via omalley)


git-svn-id: https://svn.apache.org/repos/asf/hadoop/core/trunk@696768 13f79535-47bb-0310-9956-ffa450edef68

Owen O'Malley 16 năm trước cách đây
mục cha
commit
f04a9cf266
27 tập tin đã thay đổi với 1118 bổ sung205 xóa
  1. 3 0
      CHANGES.txt
  2. 4 1
      bin/hadoop
  3. 75 1
      src/contrib/capacity-scheduler/src/java/org/apache/hadoop/mapred/CapacityTaskScheduler.java
  4. 7 0
      src/contrib/fairscheduler/src/java/org/apache/hadoop/mapred/FairScheduler.java
  5. 39 0
      src/core/org/apache/hadoop/util/ServletUtil.java
  6. 36 0
      src/core/org/apache/hadoop/util/StringUtils.java
  7. 0 25
      src/hdfs/org/apache/hadoop/hdfs/server/namenode/JspHelper.java
  8. 187 0
      src/mapred/org/apache/hadoop/mapred/JSPUtil.java
  9. 45 9
      src/mapred/org/apache/hadoop/mapred/JobClient.java
  10. 12 1
      src/mapred/org/apache/hadoop/mapred/JobInProgress.java
  11. 160 0
      src/mapred/org/apache/hadoop/mapred/JobQueueClient.java
  12. 118 0
      src/mapred/org/apache/hadoop/mapred/JobQueueInfo.java
  13. 4 0
      src/mapred/org/apache/hadoop/mapred/JobQueueTaskScheduler.java
  14. 23 3
      src/mapred/org/apache/hadoop/mapred/JobStatus.java
  15. 28 2
      src/mapred/org/apache/hadoop/mapred/JobSubmissionProtocol.java
  16. 40 21
      src/mapred/org/apache/hadoop/mapred/JobTracker.java
  17. 10 1
      src/mapred/org/apache/hadoop/mapred/LimitTasksPerJobTaskScheduler.java
  18. 16 0
      src/mapred/org/apache/hadoop/mapred/LocalJobRunner.java
  19. 27 0
      src/mapred/org/apache/hadoop/mapred/QueueManager.java
  20. 10 1
      src/mapred/org/apache/hadoop/mapred/TaskScheduler.java
  21. 107 0
      src/test/org/apache/hadoop/mapred/TestJobQueueInformation.java
  22. 1 1
      src/webapps/hdfs/dfshealth.jsp
  23. 3 4
      src/webapps/job/jobdetails.jsp
  24. 120 0
      src/webapps/job/jobqueue_details.jsp
  25. 1 2
      src/webapps/job/jobtasks.jsp
  26. 39 129
      src/webapps/job/jobtracker.jsp
  27. 3 4
      src/webapps/job/taskdetails.jsp

+ 3 - 0
CHANGES.txt

@@ -175,6 +175,9 @@ Trunk (unreleased changes)
     HADOOP-3829. Narrown down skipped records based on user acceptable value.
     (Sharad Agarwal via ddas)
 
+    HADOOP-3930. Add common interfaces for the pluggable schedulers and the
+    cli & gui clients. (Sreekanth Ramakrishnan via omalley)
+
   IMPROVEMENTS
 
     HADOOP-4104. libhdfs: add time, permission and user attribute support.

+ 4 - 1
bin/hadoop

@@ -66,7 +66,8 @@ if [ $# = 0 ]; then
   echo "  jobtracker           run the MapReduce job Tracker node" 
   echo "  pipes                run a Pipes job"
   echo "  tasktracker          run a MapReduce task Tracker node" 
-  echo "  job                  manipulate MapReduce jobs" 
+  echo "  job                  manipulate MapReduce jobs"
+  echo "  queue                get information regarding JobQueues" 
   echo "  version              print the version"
   echo "  jar <jar>            run a jar file"
   echo "  distcp <srcurl> <desturl> copy file or directories recursively"
@@ -198,6 +199,8 @@ elif [ "$COMMAND" = "tasktracker" ] ; then
   HADOOP_OPTS="$HADOOP_OPTS $HADOOP_TASKTRACKER_OPTS"
 elif [ "$COMMAND" = "job" ] ; then
   CLASS=org.apache.hadoop.mapred.JobClient
+elif [ "$COMMAND" = "queue" ] ; then
+  CLASS=org.apache.hadoop.mapred.JobQueueClient
 elif [ "$COMMAND" = "pipes" ] ; then
   CLASS=org.apache.hadoop.mapred.pipes.Submitter
   HADOOP_OPTS="$HADOOP_OPTS $HADOOP_CLIENT_OPTS"

+ 75 - 1
src/contrib/capacity-scheduler/src/java/org/apache/hadoop/mapred/CapacityTaskScheduler.java

@@ -186,6 +186,54 @@ class CapacityTaskScheduler extends TaskScheduler {
     }
   }
 
+  /**
+   * Top level scheduling information to be set to the queueManager
+   */
+  
+  
+  private static class SchedulingInfo {
+    private QueueSchedulingInfo mqsi;
+    private QueueSchedulingInfo rqsi;
+    private boolean supportsPriority;
+    
+    public SchedulingInfo(QueueSchedulingInfo mqsi, 
+        QueueSchedulingInfo rqsi, boolean supportsPriority ) {
+      this.mqsi = mqsi;
+      this.rqsi = rqsi;
+      this.supportsPriority=supportsPriority;
+    }
+    
+    @Override
+    public String toString(){
+      String queue = mqsi.queueName;
+      StringBuffer sb = new StringBuffer();
+      sb.append("Guaranteed Capacity Maps (%) :");
+      sb.append(mqsi.guaranteedCapacityPercent);
+      sb.append("\n");
+      sb.append("Guaranteed Capacity Reduces (%) :");
+      sb.append(rqsi.guaranteedCapacityPercent);
+      sb.append("\n");
+      sb.append(String.format("Current Capacity Maps : %d \n",
+          mqsi.guaranteedCapacity));
+      sb.append(String.format("Current Capacity Reduces : %d \n",
+          rqsi.guaranteedCapacity));
+      sb.append(String.format("User Limit : %d \n",mqsi.ulMin));
+      sb.append(String.format("Reclaim Time limit : %d \n",mqsi.reclaimTime));
+      sb.append(String.format("Number of Running Maps : %d \n", 
+          mqsi.numRunningTasks));
+      sb.append(String.format("Number of Running Reduces : %d \n", 
+          rqsi.numRunningTasks));
+      sb.append(String.format("Number of Waiting Maps : %d \n", 
+          mqsi.numPendingTasks));
+      sb.append(String.format("Number of Waiting Reduces : %d \n", 
+          rqsi.numPendingTasks));
+      sb.append(String.format("Priority Supported : %s \n",
+          supportsPriority?"YES":"NO"));      
+      return sb.toString();
+    }
+  }
+  
+  
   /** 
    * This class handles the scheduling algorithms. 
    * The algos are the same for both Map and Reduce tasks. 
@@ -702,6 +750,10 @@ class CapacityTaskScheduler extends TaskScheduler {
       }
       LOG.debug(s);
     }
+    
+    public QueueSchedulingInfo getQueueSchedulingInfo(String queueName) {
+      return this.queueInfoMap.get(queueName) ;
+    }
 
   }
 
@@ -917,7 +969,8 @@ class CapacityTaskScheduler extends TaskScheduler {
       rmConf = new CapacitySchedulerConf();
     }
     // read queue info from config file
-    Set<String> queues = taskTrackerManager.getQueueManager().getQueues();
+    QueueManager queueManager = taskTrackerManager.getQueueManager();
+    Set<String> queues = queueManager.getQueues();
     float totalCapacity = 0.0f;
     for (String queueName: queues) {
       float gc = rmConf.getGuaranteedCapacity(queueName); 
@@ -935,6 +988,11 @@ class CapacityTaskScheduler extends TaskScheduler {
       // create the queues of job objects
       boolean supportsPrio = rmConf.isPrioritySupported(queueName);
       jobQueuesManager.createQueue(queueName, supportsPrio);
+      
+      SchedulingInfo schedulingInfo = new SchedulingInfo(
+          mapScheduler.getQueueSchedulingInfo(queueName),reduceScheduler.getQueueSchedulingInfo(queueName),supportsPrio);
+      queueManager.setSchedulerInfo(queueName, schedulingInfo);
+      
     }
     if (totalCapacity > 100.0) {
       throw new IllegalArgumentException("Sum of queue capacities over 100% at "
@@ -1064,5 +1122,21 @@ class CapacityTaskScheduler extends TaskScheduler {
     reduceScheduler.jobRemoved(job);
   }
   
+  @Override
+  public synchronized Collection<JobInProgress> getJobs(String queueName) {
+    Collection<JobInProgress> jobCollection = 
+      jobQueuesManager.getRunningJobQueue(queueName);
+    if(jobCollection == null) {
+      jobCollection = new ArrayList<JobInProgress>();
+    }
+    Collection<JobInProgress> waitingJobs = 
+      jobQueuesManager.getWaitingJobQueue(queueName);
+    if(waitingJobs != null) {
+      jobCollection.addAll(waitingJobs);
+    }
+    return jobCollection;
+  }
+
+  
 }
 

+ 7 - 0
src/contrib/fairscheduler/src/java/org/apache/hadoop/mapred/FairScheduler.java

@@ -20,6 +20,7 @@ package org.apache.hadoop.mapred;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -686,4 +687,10 @@ public class FairScheduler extends TaskScheduler {
     if (info == null) return false;
     return info.runnable;
   }
+
+  @Override
+  public synchronized Collection<JobInProgress> getJobs(String queueName) {
+    Pool myJobPool = poolMgr.getPool(queueName);
+    return myJobPool.getJobs();
+  }
 }

+ 39 - 0
src/core/org/apache/hadoop/util/ServletUtil.java

@@ -63,4 +63,43 @@ public class ServletUtil {
   public static String htmlFooter() {
     return HTML_TAIL;
   }
+  
+  /**
+   * Generate the percentage graph and returns HTML representation string
+   * of the same.
+   * 
+   * @param perc The percentage value for which graph is to be generated
+   * @param width The width of the display table
+   * @return HTML String representation of the percentage graph
+   * @throws IOException
+   */
+  public static String percentageGraph(int perc, int width) throws IOException {
+    assert perc >= 0; assert perc <= 100;
+
+    StringBuilder builder = new StringBuilder();
+
+    builder.append("<table border=\"1px\" width=\""); builder.append(width);
+    builder.append("px\"><tr>");
+    if(perc > 0) {
+      builder.append("<td cellspacing=\"0\" class=\"perc_filled\" width=\"");
+      builder.append(perc); builder.append("%\"></td>");
+    }if(perc < 100) {
+      builder.append("<td cellspacing=\"0\" class=\"perc_nonfilled\" width=\"");
+      builder.append(100 - perc); builder.append("%\"></td>");
+    }
+    builder.append("</tr></table>");
+    return builder.toString();
+  }
+  
+  /**
+   * Generate the percentage graph and returns HTML representation string
+   * of the same.
+   * @param perc The percentage value for which graph is to be generated
+   * @param width The width of the display table
+   * @return HTML String representation of the percentage graph
+   * @throws IOException
+   */
+  public static String percentageGraph(float perc, int width) throws IOException {
+    return percentageGraph((int)perc, width);
+  }
 }

+ 36 - 0
src/core/org/apache/hadoop/util/StringUtils.java

@@ -606,4 +606,40 @@ public class StringUtils {
                * Long.parseLong(s.substring(0, lastpos));
     }
   }
+  
+    /**
+     * Escapes HTML Special characters present in the string.
+     * @param string
+     * @return HTML Escaped String representation
+     */
+    public static String escapeHTML(String string) {
+      if(string == null) {
+        return null;
+      }
+      StringBuffer sb = new StringBuffer();
+      boolean lastCharacterWasSpace = false;
+      char[] chars = string.toCharArray();
+      for(char c : chars) {
+        if(c == ' ') {
+          if(lastCharacterWasSpace){
+            lastCharacterWasSpace = false;
+            sb.append("&nbsp;");
+          }else {
+            lastCharacterWasSpace=true;
+            sb.append(" ");
+          }
+        }else {
+          lastCharacterWasSpace = false;
+          switch(c) {
+          case '<': sb.append("&lt;"); break;
+          case '>': sb.append("&gt;"); break;
+          case '&': sb.append("&amp;"); break;
+          case '"': sb.append("&quot;"); break;
+          default : sb.append(c);break;
+          }
+        }
+      }
+      
+      return sb.toString();
+    }
 }

+ 0 - 25
src/hdfs/org/apache/hadoop/hdfs/server/namenode/JspHelper.java

@@ -342,29 +342,4 @@ public class JspHelper {
       file = "..." + file.substring(start, file.length());
     out.print("<title>HDFS:" + file + "</title>");
   }
-
-  public static String percentageGraph(int perc, int width) 
-    throws IOException {
-    
-    assert perc >= 0; assert perc <= 100;
-
-    StringBuilder builder = new StringBuilder();
-
-    builder.append("<table border=\"1px\" width=\""); builder.append(width);
-    builder.append("px\"><tr>");
-    if(perc > 0) {
-      builder.append("<td cellspacing=\"0\" class=\"perc_filled\" width=\"");
-      builder.append(perc); builder.append("%\"></td>");
-    }if(perc < 100) {
-      builder.append("<td cellspacing=\"0\" class=\"perc_nonfilled\" width=\"");
-      builder.append(100 - perc); builder.append("%\"></td>");
-    }
-    builder.append("</tr></table>");
-    return builder.toString();
-  }
-  
-  public static String percentageGraph(float perc, int width) throws IOException {
-  	return percentageGraph((int)perc, width);
-  }
-  
 }

+ 187 - 0
src/mapred/org/apache/hadoop/mapred/JSPUtil.java

@@ -0,0 +1,187 @@
+/**
+ * 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 java.io.IOException;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.ServletUtil;
+
+class JSPUtil {
+  private static final String PRIVATE_ACTIONS_KEY = "webinterface.private.actions";
+  
+  public static final Configuration conf = new Configuration();
+
+  /**
+   * Method used to process the request from the job page based on the 
+   * request which it has received. For example like changing priority.
+   * 
+   * @param request HTTP request Object.
+   * @param response HTTP response object.
+   * @param tracker {@link JobTracker} instance
+   * @throws IOException
+   */
+  public static void processButtons(HttpServletRequest request,
+      HttpServletResponse response, JobTracker tracker) throws IOException {
+
+    if (conf.getBoolean(PRIVATE_ACTIONS_KEY, false)
+        && request.getParameter("killJobs") != null) {
+      String[] jobs = request.getParameterValues("jobCheckBox");
+      if (jobs != null) {
+        for (String job : jobs) {
+          tracker.killJob(JobID.forName(job));
+        }
+      }
+    }
+
+    if (request.getParameter("changeJobPriority") != null) {
+      String[] jobs = request.getParameterValues("jobCheckBox");
+
+      if (jobs != null) {
+        JobPriority jobPri = JobPriority.valueOf(request
+            .getParameter("setJobPriority"));
+
+        for (String job : jobs) {
+          tracker.setJobPriority(JobID.forName(job), jobPri);
+        }
+      }
+    }
+  }
+
+  /**
+   * Method used to generate the Job table for Job pages.
+   * 
+   * @param label display heading to be used in the job table.
+   * @param jobs vector of jobs to be displayed in table.
+   * @param refresh refresh interval to be used in jobdetails page.
+   * @param rowId beginning row id to be used in the table.
+   * @return
+   * @throws IOException
+   */
+  public static String generateJobTable(String label, Vector<JobInProgress> jobs
+      , int refresh, int rowId) throws IOException {
+
+    boolean isModifiable = label.equals("Running");
+    StringBuffer sb = new StringBuffer();
+    
+    sb.append("<table border=\"1\" cellpadding=\"5\" cellspacing=\"0\">\n");
+
+    if (jobs.size() > 0) {
+      if (isModifiable) {
+        sb.append("<form action=\"/jobtracker.jsp\" onsubmit=\"return confirmAction();\" method=\"POST\">");
+        sb.append("<tr>");
+        sb.append("<td><input type=\"Button\" onclick=\"selectAll()\" " +
+        		"value=\"Select All\" id=\"checkEm\"></td>");
+        sb.append("<td>");
+        if (conf.getBoolean(PRIVATE_ACTIONS_KEY, false)) {
+         sb.append("<input type=\"submit\" name=\"killJobs\" " +
+         		"value=\"Kill Selected Jobs\">");
+        }
+        sb.append("</td");
+        sb.append("<td><nobr>");
+        sb.append("<select name=\"setJobPriority\">");
+
+        for (JobPriority prio : JobPriority.values()) {
+          sb.append("<option"
+              + (JobPriority.NORMAL == prio ? " selected=\"selected\">" : ">")
+              + prio + "</option>");
+        }
+
+        sb.append("</select>");
+        sb.append("<input type=\"submit\" name=\"changeJobPriority\" " +
+        		"value=\"Change\">");
+        sb.append("</nobr></td>");
+        sb.append("<td colspan=\"10\">&nbsp;</td>");
+        sb.append("</tr>");
+        sb.append("<td>&nbsp;</td>");
+      } else {
+        sb.append("<tr>");
+      }
+
+      sb.append("<td><b>Jobid</b></td><td><b>Priority" +
+      		"</b></td><td><b>User</b></td>");
+      sb.append("<td><b>Name</b></td>");
+      sb.append("<td><b>Map % Complete</b></td>");
+      sb.append("<td><b>Map Total</b></td>");
+      sb.append("<td><b>Maps Completed</b></td>");
+      sb.append("<td><b>Reduce % Complete</b></td>");
+      sb.append("<td><b>Reduce Total</b></td>");
+      sb.append("<td><b>Reduces Completed</b></td>");
+      sb.append("<td><b>Job Scheduling Information</b></td>");
+      sb.append("</tr>\n");
+      for (Iterator<JobInProgress> it = jobs.iterator(); it.hasNext(); ++rowId) {
+        JobInProgress job = it.next();
+        JobProfile profile = job.getProfile();
+        JobStatus status = job.getStatus();
+        JobID jobid = profile.getJobID();
+
+        int desiredMaps = job.desiredMaps();
+        int desiredReduces = job.desiredReduces();
+        int completedMaps = job.finishedMaps();
+        int completedReduces = job.finishedReduces();
+        String name = profile.getJobName();
+        String jobpri = job.getPriority().toString();
+        String schedulingInfo = job.getStatus().getSchedulingInfo();
+        if(schedulingInfo.trim().equals("")){
+          schedulingInfo = "&nbsp;&nbsp;&nbsp;";
+        }
+
+        if (isModifiable) {
+          sb.append("<tr><td><input TYPE=\"checkbox\" " +
+          		"onclick=\"checkButtonVerbage()\" " +
+          		"name=\"jobCheckBox\" value="
+                  + jobid + "></td>");
+        } else {
+          sb.append("<tr>");
+        }
+
+        sb.append("<td id=\"job_" + rowId
+            + "\"><a href=\"jobdetails.jsp?jobid=" + jobid + "&refresh="
+            + refresh + "\">" + jobid + "</a></td>" + "<td id=\"priority_"
+            + rowId + "\">" + jobpri + "</td>" + "<td id=\"user_" + rowId
+            + "\">" + profile.getUser() + "</td>" + "<td id=\"name_" + rowId
+            + "\">" + ("".equals(name) ? "&nbsp;" : name) + "</td>" + "<td>"
+            + StringUtils.formatPercent(status.mapProgress(), 2)
+            + ServletUtil.percentageGraph(status.mapProgress() * 100, 80)
+            + "</td><td>" + desiredMaps + "</td><td>" + completedMaps
+            + "</td><td>"
+            + StringUtils.formatPercent(status.reduceProgress(), 2)
+            + ServletUtil.percentageGraph(status.reduceProgress() * 100, 80)
+            + "</td><td>" + desiredReduces + "</td><td> " + completedReduces 
+            + "</td><td>" + schedulingInfo
+            + "</td></tr>\n");
+      }
+      if (isModifiable) {
+        sb.append("</form>\n");
+      }
+    } else {
+      sb.append("<tr><td align=\"center\" colspan=\"8\"><i>none</i>" +
+      		"</td></tr>\n");
+    }
+    sb.append("</table>\n");
+    
+    return sb.toString();
+  }
+
+}

+ 45 - 9
src/mapred/org/apache/hadoop/mapred/JobClient.java

@@ -1554,10 +1554,7 @@ public class JobClient extends Configured implements MRConstants, Tool  {
 
     System.out.printf("%d jobs currently running\n", jobs.length);
     System.out.printf("JobId\tState\tStartTime\tUserName\n");
-    for (JobStatus job : jobs) {
-      System.out.printf("%s\t%d\t%d\t%s\n", job.getJobID(), job.getRunState(),
-          job.getStartTime(), job.getUsername());
-    }
+    displayJobList(jobs);
   }
     
   /**
@@ -1568,14 +1565,17 @@ public class JobClient extends Configured implements MRConstants, Tool  {
     JobStatus[] jobs = getAllJobs();
     if (jobs == null)
       jobs = new JobStatus[0];
-
     System.out.printf("%d jobs submitted\n", jobs.length);
     System.out.printf("States are:\n\tRunning : 1\tSucceded : 2" +
-                       "\tFailed : 3\tPrep : 4\n");
-    System.out.printf("JobId\tState\tStartTime\tUserName\n");
+    "\tFailed : 3\tPrep : 4\n");
+    displayJobList(jobs);
+  }
+
+  void displayJobList(JobStatus[] jobs) {
+    System.out.printf("JobId\tState\tStartTime\tUserName\tSchedulingInfo\n");
     for (JobStatus job : jobs) {
-      System.out.printf("%s\t%d\t%d\t%s\n", job.getJobID(), job.getRunState(),
-          job.getStartTime(), job.getUsername());
+      System.out.printf("%s\t%d\t%d\t%s\t%s\n", job.getJobID(), job.getRunState(),
+          job.getStartTime(), job.getUsername(),job.getSchedulingInfo());
     }
   }
 
@@ -1610,6 +1610,42 @@ public class JobClient extends Configured implements MRConstants, Tool  {
     }
     return sysDir;
   }
+  
+  
+  /**
+   * Return an array of queue information objects about all the Job Queues
+   * configured.
+   * 
+   * @return Array of JobQueueInfo objects
+   * @throws IOException
+   */
+  public JobQueueInfo[] getQueues() throws IOException {
+    return jobSubmitClient.getQueues();
+  }
+  
+  /**
+   * Gets all the jobs which were added to particular Job Queue
+   * 
+   * @param queueName name of the Job Queue
+   * @return Array of jobs present in the job queue
+   * @throws IOException
+   */
+  
+  public JobStatus[] getJobsFromQueue(String queueName) throws IOException {
+    return jobSubmitClient.getJobsFromQueue(queueName);
+  }
+  
+  /**
+   * Gets the queue information associated to a particular Job Queue
+   * 
+   * @param queueName name of the job queue.
+   * @return Queue information associated to particular queue.
+   * @throws IOException
+   */
+  public JobQueueInfo getQueueInfo(String queueName) throws IOException {
+    return jobSubmitClient.getQueueInfo(queueName);
+  }
+  
 
   /**
    */

+ 12 - 1
src/mapred/org/apache/hadoop/mapred/JobInProgress.java

@@ -31,7 +31,6 @@ import java.util.Set;
 import java.util.TreeMap;
 import java.util.Vector;
 import java.util.concurrent.atomic.AtomicBoolean;
-
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.fs.FileSystem;
@@ -165,6 +164,9 @@ class JobInProgress {
   // Map of mapTaskId -> no. of fetch failures
   private Map<TaskAttemptID, Integer> mapTaskIdToFetchFailuresMap =
     new TreeMap<TaskAttemptID, Integer>();
+
+  private Object schedulingInfo;
+
   
   /**
    * Create an almost empty JobInProgress, which can be used only for tests
@@ -2106,4 +2108,13 @@ class JobInProgress {
   public JobID getJobID() {
     return jobId;
   }
+  
+  public synchronized Object getSchedulingInfo() {
+    return this.schedulingInfo;
+  }
+  
+  public synchronized void setSchedulingInfo(Object schedulingInfo) {
+    this.schedulingInfo = schedulingInfo;
+    this.status.setSchedulingInfo(schedulingInfo.toString());
+  }
 }

+ 160 - 0
src/mapred/org/apache/hadoop/mapred/JobQueueClient.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.mapred;
+
+import java.io.IOException;
+
+import org.apache.hadoop.conf.Configured;
+import org.apache.hadoop.util.Tool;
+import org.apache.hadoop.util.ToolRunner;
+/**
+ * <code>JobQueueClient</code> is interface provided to the user in order
+ * to get JobQueue related information from the {@link JobTracker}
+ * 
+ * It provides the facility to list the JobQueues present and ability to 
+ * view the list of jobs within a specific JobQueue 
+ * 
+**/
+
+class JobQueueClient extends Configured implements  Tool {
+  
+  JobClient jc;
+
+  
+  public JobQueueClient() {
+  }
+  
+  public JobQueueClient(JobConf conf) throws IOException {
+    setConf(conf);
+  }
+  
+  private void init(JobConf conf) throws IOException {
+    setConf(conf);
+    jc = new JobClient(conf);
+  }
+  
+  @Override
+  public int run(String[] argv) throws Exception {
+    int exitcode = -1;
+    
+    if(argv.length < 1){
+      displayUsage("");
+      return exitcode;
+    }
+    String cmd = argv[0];
+    boolean displayQueueList = false;
+    boolean displayQueueInfoWithJobs = false;
+    boolean displayQueueInfoWithoutJobs = false;
+    
+    if("-list".equals(cmd)){
+      displayQueueList = true;
+    }else if("-info".equals(cmd)){
+      if(argv.length == 2 && !(argv[1].equals("-showJobs"))) {
+        displayQueueInfoWithoutJobs = true;
+      } else if(argv.length == 3){
+        if(argv[2].equals("-showJobs")){
+          displayQueueInfoWithJobs = true;
+        }else {
+          displayUsage(cmd);
+          return exitcode;
+        }
+      }else {
+        displayUsage(cmd);
+        return exitcode;
+      }      
+    } else {
+      displayUsage(cmd);
+      return exitcode;
+    }
+    JobConf conf = new JobConf(getConf());
+    init(conf);
+    if (displayQueueList) {
+      displayQueueList();
+      exitcode = 0;
+    } else if (displayQueueInfoWithoutJobs){
+      displayQueueInfo(argv[1],false);
+      exitcode = 0;
+    } else if (displayQueueInfoWithJobs) {
+      displayQueueInfo(argv[1],true);
+      exitcode = 0;
+    }
+    
+    return exitcode;
+  }
+  
+  /**
+   * Method used to display information pertaining to a Single JobQueue 
+   * registered with the {@link QueueManager}. Display of the Jobs is 
+   * determine by the boolean 
+   * 
+   * @throws IOException
+   */
+
+  private void displayQueueInfo(String queue, boolean showJobs) throws IOException {
+    JobQueueInfo schedInfo = jc.getQueueInfo(queue);
+    if (schedInfo == null) {
+      System.out.printf("Queue Name : %s has no scheduling information \n", queue);
+    } else {
+      System.out.printf("Queue Name : %s \n", schedInfo.getQueueName());
+      System.out.printf("Scheduling Info : %s \n",schedInfo.getSchedulingInfo());
+    }
+    if (showJobs) {
+      System.out.printf("Job List\n");
+      JobStatus[] jobs = jc.getJobsFromQueue(queue);
+      if (jobs == null)
+        jobs = new JobStatus[0];
+      jc.displayJobList(jobs);
+    }
+  }
+
+  /**
+   * Method used to display the list of the JobQueues registered
+   * with the {@link QueueManager}
+   * 
+   * @throws IOException
+   */
+  private void displayQueueList() throws IOException {
+    JobQueueInfo[] queues = jc.getQueues();
+    for (JobQueueInfo queue : queues) {
+      String schedInfo = queue.getSchedulingInfo();
+      if(schedInfo.trim().equals("")){
+        schedInfo = "N/A";
+      }
+      System.out.printf("Queue Name : %s \n", queue.getQueueName());
+      System.out.printf("Scheduling Info : %s \n",queue.getSchedulingInfo());
+    }
+  }
+
+  private void displayUsage(String cmd) {
+    String prefix = "Usage: JobQueueClient ";
+    if ("-queueinfo".equals(cmd)){
+      System.err.println(prefix + "[" + cmd + "<job-queue-name> [-showJobs]]");
+    }else {
+      System.err.printf(prefix + "<command> <args>\n");
+      System.err.printf("\t[-list]\n");
+      System.err.printf("\t[-info <job-queue-name> [-showJobs]]\n\n");
+      ToolRunner.printGenericCommandUsage(System.out);
+    }
+  }
+
+  public static void main(String[] argv) throws Exception {
+    int res = ToolRunner.run(new JobQueueClient(), argv);
+    System.exit(res);
+  }
+  
+}

+ 118 - 0
src/mapred/org/apache/hadoop/mapred/JobQueueInfo.java

@@ -0,0 +1,118 @@
+/**
+ * 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 java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.io.Writable;
+
+/**
+ * Class that contains the information regarding the Job Queues which are 
+ * maintained by the Hadoop Map/Reduce framework.
+ * 
+ */
+
+public class JobQueueInfo implements Writable {
+
+  private String queueName = "";
+  //The scheduling Information object is read back as String.
+  //Once the scheduling information is set there is no way to recover it.
+  private String schedulingInfo; 
+  
+  
+  /**
+   * Default constructor for Job Queue Info.
+   * 
+   */
+  public JobQueueInfo() {
+    
+  }
+  /**
+   * Construct a new JobQueueInfo object using the queue name and the
+   * scheduling information passed.
+   * 
+   * @param queueName Name of the job queue
+   * @param schedulingInfo Scheduling Information associated with the job
+   * queue
+   */
+  public JobQueueInfo(String queueName, String schedulingInfo) {
+    this.queueName = queueName;
+    this.schedulingInfo = schedulingInfo;
+  }
+  
+  
+  /**
+   * Set the queue name of the JobQueueInfo
+   * 
+   * @param queueName Name of the job queue.
+   */
+  public void setQueueName(String queueName) {
+    this.queueName = queueName;
+  }
+
+  /**
+   * Get the queue name from JobQueueInfo
+   * 
+   * @return queue name
+   */
+  public String getQueueName() {
+    return queueName;
+  }
+
+  /**
+   * Set the scheduling information associated to particular job queue
+   * 
+   * @param schedulingInfo
+   */
+  public void setSchedulingInfo(String schedulingInfo) {
+    this.schedulingInfo = schedulingInfo;
+  }
+
+  /**
+   * Gets the scheduling information associated to particular job queue.
+   * If nothing is set would return <b>"N/A"</b>
+   * 
+   * @return Scheduling information associated to particular Job Queue
+   */
+  public String getSchedulingInfo() {
+    if(schedulingInfo != null) {
+      return schedulingInfo;
+    }else {
+      return "N/A";
+    }
+  }
+  
+  @Override
+  public void readFields(DataInput in) throws IOException {
+    queueName = Text.readString(in);
+    schedulingInfo = Text.readString(in);
+  }
+
+  @Override
+  public void write(DataOutput out) throws IOException {
+    Text.writeString(out, queueName);
+    if(schedulingInfo!= null) {
+      Text.writeString(out, schedulingInfo);
+    }else {
+      Text.writeString(out, "N/A");
+    }
+  }
+}

+ 4 - 0
src/mapred/org/apache/hadoop/mapred/JobQueueTaskScheduler.java

@@ -211,4 +211,8 @@ class JobQueueTaskScheduler extends TaskScheduler {
     return null;
   }
 
+  @Override
+  public synchronized Collection<JobInProgress> getJobs(String queueName) {
+    return jobQueueJobInProgressListener.getJobQueue();
+  }  
 }

+ 23 - 3
src/mapred/org/apache/hadoop/mapred/JobStatus.java

@@ -53,6 +53,7 @@ public class JobStatus implements Writable {
   private int runState;
   private long startTime;
   private String user;
+  private String schedulingInfo="";
     
   /**
    */
@@ -171,11 +172,28 @@ public class JobStatus implements Writable {
    * @return the username of the job
    */
   public synchronized String getUsername() { return this.user;}
-    
+  
+  /**
+   * Gets the Scheduling information associated to a particular Job.
+   * @return the scheduling information of the job
+   */
+  public synchronized String getSchedulingInfo() {
+   return schedulingInfo;
+  }
+
+  /**
+   * Used to set the scheduling information associated to a particular Job.
+   * 
+   * @param schedulingInfo Scheduling information of the job
+   */
+  public synchronized void setSchedulingInfo(String schedulingInfo) {
+    this.schedulingInfo = schedulingInfo;
+  }
+  
   ///////////////////////////////////////
   // Writable
   ///////////////////////////////////////
-  public void write(DataOutput out) throws IOException {
+  public synchronized void write(DataOutput out) throws IOException {
     jobid.write(out);
     out.writeFloat(mapProgress);
     out.writeFloat(reduceProgress);
@@ -183,9 +201,10 @@ public class JobStatus implements Writable {
     out.writeInt(runState);
     out.writeLong(startTime);
     Text.writeString(out, user);
+    Text.writeString(out, schedulingInfo);
   }
 
-  public void readFields(DataInput in) throws IOException {
+  public synchronized void readFields(DataInput in) throws IOException {
     this.jobid = JobID.read(in);
     this.mapProgress = in.readFloat();
     this.reduceProgress = in.readFloat();
@@ -193,5 +212,6 @@ public class JobStatus implements Writable {
     this.runState = in.readInt();
     this.startTime = in.readLong();
     this.user = Text.readString(in);
+    this.schedulingInfo = Text.readString(in);
   }
 }

+ 28 - 2
src/mapred/org/apache/hadoop/mapred/JobSubmissionProtocol.java

@@ -43,8 +43,10 @@ interface JobSubmissionProtocol extends VersionedProtocol {
    * Version 11: changed JobProfile to include the queue name for HADOOP-3698
    * Version 12: Added getCleanupTaskReports and 
    *             cleanupProgress to JobStatus as part of HADOOP-3150
+   * Version 13: Added getJobQueueInfos and getJobQueueInfo(queue name)
+   *             and getAllJobs(queue) as a part of HADOOP-3930
    */
-  public static final long versionID = 12L;
+  public static final long versionID = 13L;
 
   /**
    * Allocate a name for the job.
@@ -158,5 +160,29 @@ interface JobSubmissionProtocol extends VersionedProtocol {
    * @return the system directory where job-specific files are to be placed.
    */
   public String getSystemDir();  
-
+  
+  /**
+   * Gets set of Job Queues associated with the Job Tracker
+   * 
+   * @return Array of the Job Queue Information Object
+   * @throws IOException 
+   */
+  public JobQueueInfo[] getQueues() throws IOException;
+  
+  /**
+   * Gets scheduling information associated with the particular Job queue
+   * 
+   * @param queue Queue Name
+   * @return Scheduling Information of the Queue
+   * @throws IOException 
+   */
+  public JobQueueInfo getQueueInfo(String queue) throws IOException;
+  
+  /**
+   * Gets all the jobs submitted to the particular Queue
+   * @param queue Queue name
+   * @return array of JobStatus for the submitted jobs
+   * @throws IOException
+   */
+  public JobStatus[] getJobsFromQueue(String queue) throws IOException;
 }

+ 40 - 21
src/mapred/org/apache/hadoop/mapred/JobTracker.java

@@ -2423,30 +2423,11 @@ public class JobTracker implements MRConstants, InterTrackerProtocol,
   }
     
   public JobStatus[] jobsToComplete() {
-    Vector<JobStatus> v = new Vector<JobStatus>();
-    for (Iterator it = jobs.values().iterator(); it.hasNext();) {
-      JobInProgress jip = (JobInProgress) it.next();
-      JobStatus status = jip.getStatus();
-      if (status.getRunState() == JobStatus.RUNNING 
-          || status.getRunState() == JobStatus.PREP) {
-        status.setStartTime(jip.getStartTime());
-        status.setUsername(jip.getProfile().getUser());
-        v.add(status);
-      }
-    }
-    return v.toArray(new JobStatus[v.size()]);
+    return getJobStatus(jobs.values(), true);
   } 
   
   public JobStatus[] getAllJobs() {
-    Vector<JobStatus> v = new Vector<JobStatus>();
-    for (Iterator it = jobs.values().iterator(); it.hasNext();) {
-      JobInProgress jip = (JobInProgress) it.next();
-      JobStatus status = jip.getStatus();
-      status.setStartTime(jip.getStartTime());
-      status.setUsername(jip.getProfile().getUser());
-      v.add(status);
-    }
-    return v.toArray(new JobStatus[v.size()]);
+    return getJobStatus(jobs.values(),false);
   }
     
   /**
@@ -2622,6 +2603,44 @@ public class JobTracker implements MRConstants, InterTrackerProtocol,
     }
   }
 
+  @Override
+  public JobQueueInfo[] getQueues() throws IOException {
+    return queueManager.getJobQueueInfos();
+  }
 
 
+  @Override
+  public JobQueueInfo getQueueInfo(String queue) throws IOException {
+    return queueManager.getJobQueueInfo(queue);
+  }
+
+  @Override
+  public JobStatus[] getJobsFromQueue(String queue) throws IOException {
+    Collection<JobInProgress> jips = taskScheduler.getJobs(queue);
+    return getJobStatus(jips,false);
+  }
+  
+  private synchronized JobStatus[] getJobStatus(Collection<JobInProgress> jips,
+      boolean toComplete) {
+    if(jips == null || jips.isEmpty()) {
+      return new JobStatus[]{};
+    }
+    ArrayList<JobStatus> jobStatusList = new ArrayList<JobStatus>();
+    for(JobInProgress jip : jips) {
+      JobStatus status = jip.getStatus();
+      status.setStartTime(jip.getStartTime());
+      status.setUsername(jip.getProfile().getUser());
+      if(toComplete) {
+        if(status.getRunState() == JobStatus.RUNNING || 
+            status.getRunState() == JobStatus.PREP) {
+          jobStatusList.add(status);
+        }
+      }else {
+        jobStatusList.add(status);
+      }
+    }
+    return (JobStatus[]) jobStatusList.toArray(
+        new JobStatus[jobStatusList.size()]);
+  }
+  
 }

+ 10 - 1
src/mapred/org/apache/hadoop/mapred/LimitTasksPerJobTaskScheduler.java

@@ -47,7 +47,16 @@ class LimitTasksPerJobTaskScheduler extends JobQueueTaskScheduler {
   }
   
   @Override
-  public void setConf(Configuration conf) {
+  public synchronized void start() throws IOException {
+    super.start();
+    QueueManager queueManager = taskTrackerManager.getQueueManager();
+    String queueName = queueManager.getJobQueueInfos()[0].getQueueName();
+    queueManager.setSchedulerInfo(queueName
+        ,"Maximum Tasks Per Job :: " + String.valueOf(maxTasksPerJob));
+  }
+  
+  @Override
+  public synchronized void setConf(Configuration conf) {
     super.setConf(conf);
     maxTasksPerJob = conf.getLong(MAX_TASKS_PER_JOB_PROPERTY ,Long.MAX_VALUE);
     if (maxTasksPerJob <= 0) {

+ 16 - 0
src/mapred/org/apache/hadoop/mapred/LocalJobRunner.java

@@ -382,4 +382,20 @@ class LocalJobRunner implements JobSubmissionProtocol {
     return fs.makeQualified(sysDir).toString();
   }
 
+  @Override
+  public JobStatus[] getJobsFromQueue(String queue) throws IOException {
+    return null;
+  }
+
+  @Override
+  public JobQueueInfo[] getQueues() throws IOException {
+    return null;
+  }
+
+
+  @Override
+  public JobQueueInfo getQueueInfo(String queue) throws IOException {
+    return null;
+  }
+
 }

+ 27 - 0
src/mapred/org/apache/hadoop/mapred/QueueManager.java

@@ -18,6 +18,8 @@
 
 package org.apache.hadoop.mapred;
 
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Set;
 import java.util.TreeSet;
@@ -285,6 +287,7 @@ public class QueueManager {
   public synchronized void refresh(Configuration conf) {
     queueNames.clear();
     aclsMap.clear();
+    schedulerInfoObjects.clear();
     initialize(conf);
   }
   
@@ -315,4 +318,28 @@ public class QueueManager {
       set.add(elem);
     }
   }
+
+
+  synchronized JobQueueInfo[] getJobQueueInfos() {
+    ArrayList<JobQueueInfo> queueInfoList = new ArrayList<JobQueueInfo>();
+    for(String queue : queueNames) {
+      Object schedulerInfo = schedulerInfoObjects.get(queue);
+      if(schedulerInfo != null) {
+        queueInfoList.add(new JobQueueInfo(queue,schedulerInfo.toString()));
+      }else {
+        queueInfoList.add(new JobQueueInfo(queue,null));
+      }
+    }
+    return (JobQueueInfo[]) queueInfoList.toArray(new JobQueueInfo[queueInfoList
+        .size()]);
+  }
+
+  JobQueueInfo getJobQueueInfo(String queue) {
+    Object schedulingInfo = schedulerInfoObjects.get(queue);
+    if(schedulingInfo!=null){
+      return new JobQueueInfo(queue,schedulingInfo.toString());
+    }else {
+      return new JobQueueInfo(queue,null);
+    }
+  }
 }

+ 10 - 1
src/mapred/org/apache/hadoop/mapred/TaskScheduler.java

@@ -18,6 +18,7 @@
 package org.apache.hadoop.mapred;
 
 import java.io.IOException;
+import java.util.Collection;
 import java.util.List;
 
 import org.apache.hadoop.conf.Configurable;
@@ -81,5 +82,13 @@ abstract class TaskScheduler implements Configurable {
    */
   public abstract List<Task> assignTasks(TaskTrackerStatus taskTracker)
     throws IOException;
-  
+
+  /**
+   * Returns a collection of jobs in an order which is specific to 
+   * the particular scheduler.
+   * @param queueName
+   * @return
+   */
+  public abstract Collection<JobInProgress> getJobs(String queueName);
+    
 }

+ 107 - 0
src/test/org/apache/hadoop/mapred/TestJobQueueInformation.java

@@ -0,0 +1,107 @@
+/**
+ * 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 java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.List;
+
+import javax.security.auth.login.LoginException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hdfs.MiniDFSCluster;
+import org.apache.hadoop.ipc.RPC;
+import org.apache.hadoop.net.NetUtils;
+import org.apache.hadoop.security.UnixUserGroupInformation;
+
+import junit.framework.TestCase;
+
+public class TestJobQueueInformation extends TestCase {
+
+  private MiniMRCluster mrCluster;
+  private MiniDFSCluster dfsCluster;
+  private JobConf jc;
+  private static final String JOB_SCHEDULING_INFO = "TESTSCHEDULINGINFO";
+
+  public static class TestTaskScheduler extends LimitTasksPerJobTaskScheduler {
+
+    @Override
+    public synchronized List<Task> assignTasks(TaskTrackerStatus taskTracker)
+        throws IOException {
+      Collection<JobInProgress> jips = jobQueueJobInProgressListener
+          .getJobQueue();
+      if (jips != null && !jips.isEmpty()) {
+        for (JobInProgress jip : jips) {
+          jip.setSchedulingInfo(JOB_SCHEDULING_INFO);
+        }
+      }
+      return super.assignTasks(taskTracker);
+    }
+  }
+
+  @Override
+  protected void setUp() throws Exception {
+    super.setUp();
+    final int taskTrackers = 4;
+    Configuration conf = new Configuration();
+    dfsCluster = new MiniDFSCluster(conf, 4, true, null);
+
+    jc = new JobConf();
+    jc.setClass("mapred.jobtracker.taskScheduler", TestTaskScheduler.class,
+        TaskScheduler.class);
+    jc.setLong("mapred.jobtracker.taskScheduler.maxRunningTasksPerJob", 10L);
+    mrCluster = new MiniMRCluster(0, 0, taskTrackers, dfsCluster
+        .getFileSystem().getUri().toString(), 1, null, null, null, jc);
+  }
+
+  @Override
+  protected void tearDown() throws Exception {
+    super.tearDown();
+    mrCluster.shutdown();
+    dfsCluster.shutdown();
+  }
+
+  public void testJobQueues() throws IOException {
+    JobClient jc = new JobClient(mrCluster.createJobConf());
+    String expectedQueueInfo = "Maximum Tasks Per Job :: 10";
+    JobQueueInfo[] queueInfos = jc.getQueues();
+    assertNotNull(queueInfos);
+    assertEquals(1, queueInfos.length);
+    assertEquals("default", queueInfos[0].getQueueName());
+    TestMiniMRWithDFS.runWordCount(mrCluster, mrCluster.createJobConf());
+    int numberOfJobs = 0;
+
+    for (JobQueueInfo queueInfo : queueInfos) {
+      JobStatus[] jobStatusList = jc.getJobsFromQueue(queueInfo
+          .getQueueName());
+      assertNotNull(queueInfo.getQueueName());
+      assertNotNull(queueInfo.getSchedulingInfo());
+      assertEquals(expectedQueueInfo, queueInfo.getSchedulingInfo());
+      numberOfJobs += jobStatusList.length;
+      for (JobStatus status : jobStatusList) {
+        assertEquals(JOB_SCHEDULING_INFO, status.getSchedulingInfo());
+      }
+    }
+    // Three jobs are launched by runwordcount
+    assertEquals(3, numberOfJobs);
+  }
+}

+ 1 - 1
src/webapps/hdfs/dfshealth.jsp

@@ -106,7 +106,7 @@
 	      "<td class=\"size\">" +
               FsShell.limitDecimalTo2(c*1.0/diskBytes) +
 	      "<td class=\"pcused\">" + percentUsed +"<td class=\"pcused\">" +
-	      JspHelper.percentageGraph( (int)Double.parseDouble(percentUsed) , 100) +
+	      ServletUtil.percentageGraph( (int)Double.parseDouble(percentUsed) , 100) +
 	      "<td class=\"size\">" +
               FsShell.limitDecimalTo2(d.getRemaining()*1.0/diskBytes) +
               "<td title=" + "\"blocks scheduled : " + d.getBlocksScheduled() + 

+ 3 - 4
src/webapps/job/jobdetails.jsp

@@ -8,7 +8,6 @@
   import="java.text.DecimalFormat"
   import="org.apache.hadoop.mapred.*"
   import="org.apache.hadoop.util.*"
-  import="org.apache.hadoop.hdfs.server.namenode.JspHelper"
 %>
 
 <%
@@ -49,7 +48,7 @@
               "&type="+ kind + "&pagenum=1\">" + kind + 
               "</a></th><td align=\"right\">" + 
               StringUtils.formatPercent(completePercent, 2) +
-              JspHelper.percentageGraph((int)(completePercent * 100), 80) +
+              ServletUtil.percentageGraph((int)(completePercent * 100), 80) +
               "</td><td align=\"right\">" + 
               totalTasks + 
               "</td><td align=\"right\">" + 
@@ -165,7 +164,7 @@
                              JobPriority.valueOf(request.getParameter("prio")));
     }
     
-    if(JspHelper.conf.getBoolean(PRIVATE_ACTIONS_KEY, false)) {
+    if(JSPUtil.conf.getBoolean(PRIVATE_ACTIONS_KEY, false)) {
         action = request.getParameter("action");
 	    if(action!=null && action.equalsIgnoreCase("confirm")) {
   	      printConfirm(out, jobId);
@@ -354,7 +353,7 @@ Change priority from <%=job.getPriority()%> to:
 
 <table border="0"> <tr>
     
-<% if(JspHelper.conf.getBoolean(PRIVATE_ACTIONS_KEY, false) 
+<% if(JSPUtil.conf.getBoolean(PRIVATE_ACTIONS_KEY, false) 
     	&& runState == JobStatus.RUNNING) { %>
 	<br/><a href="jobdetails.jsp?action=confirm&jobid=<%=jobId%>"> Kill this job </a>
 <% } %>

+ 120 - 0
src/webapps/job/jobqueue_details.jsp

@@ -0,0 +1,120 @@
+<%@ page
+  contentType="text/html; charset=UTF-8"
+  import="javax.servlet.*"
+  import="javax.servlet.http.*"
+  import="java.util.Vector"
+  import="java.util.Collection"
+  import="org.apache.hadoop.mapred.*"
+  import="org.apache.hadoop.util.StringUtils"
+  import="org.apache.hadoop.util.ServletUtil"
+%>
+<%!
+private static final long serialVersionUID = 526456771152222127L; 
+%>
+<%
+  JobTracker tracker = 
+    (JobTracker) application.getAttribute("job.tracker");
+  String trackerName = 
+    StringUtils.simpleHostname(tracker.getJobTrackerMachine());
+  String queueName = 
+    StringUtils.escapeHTML(request.getParameter("queueName"));
+  Vector<JobInProgress> completedJobs = new Vector<JobInProgress>();
+  Vector<JobInProgress> failedJobs = new Vector<JobInProgress>();
+  Vector<JobInProgress> runningJobs = new Vector<JobInProgress>();
+  Vector<JobInProgress> waitingJobs = new Vector<JobInProgress>();
+  Collection<JobInProgress> jobs = null;
+  TaskScheduler scheduler = tracker.getTaskScheduler();
+  
+  
+  
+  if((queueName != null) && !(queueName.trim().equals(""))) {
+    jobs = scheduler.getJobs(queueName);
+  }
+  
+  if(jobs!=null && !jobs.isEmpty()) {
+    for(JobInProgress job :jobs) {
+      switch(job.getStatus().getRunState()){
+      case JobStatus.RUNNING:
+        runningJobs.add(job);
+        break;
+      case JobStatus.PREP:
+        waitingJobs.add(job);
+        break;
+      case JobStatus.SUCCEEDED:
+        completedJobs.add(job);
+        break;
+      case JobStatus.FAILED:
+        failedJobs.add(job);
+        break;
+      }
+    }
+  }
+  JobQueueInfo schedInfo = tracker.getQueueInfo(queueName);
+%>
+<html>
+<head>
+<title>Queue details for <%=queueName!=null?queueName:""%> </title>
+<link rel="stylesheet" type="text/css" href="/static/hadoop.css">
+<script type="text/javascript" src="/static/jobtracker.js"></script>
+</head>
+<body>
+<% JSPUtil.processButtons(request, response, tracker); %>
+<%
+  String schedulingInfoString = schedInfo.getSchedulingInfo();
+%>
+<h1>Hadoop Job Queue Scheduling Information on 
+  <a href="jobtracker.jsp"><%=trackerName%></a>
+</h1>
+<div>
+Scheduling Information : <%= schedulingInfoString.replaceAll("\n","<br/>") %>
+</div>
+<hr/>
+<%
+if(jobs == null || jobs.isEmpty()) {
+%>
+<center>
+<h2> No Jobs found for the Queue :: <%=queueName!=null?queueName:""%> </h2>
+<hr/>
+</center>
+<%
+}else {
+%>
+<center>
+<h2> Job Summary for the Queue :: <%=queueName!=null?queueName:"" %> </h2>
+<hr/>
+</center>
+<h2>Running Jobs</h2>
+<%=
+  JSPUtil.generateJobTable("Running", runningJobs, 30, 0)
+%>
+<hr>
+
+<h2>Waiting Jobs</h2>
+<%=
+  JSPUtil.generateJobTable("Waiting", waitingJobs, 30, runningJobs.size())
+%>
+<hr>
+
+<h2>Completed Jobs</h2>
+<%=
+  JSPUtil.generateJobTable("Completed", completedJobs, 0,
+      (runningJobs.size()+waitingJobs.size()))
+%>
+
+<hr>
+
+<h2>Failed Jobs</h2>
+<%=
+  JSPUtil.generateJobTable("Failed", failedJobs, 0,
+      (runningJobs.size()+waitingJobs.size()+completedJobs.size()))
+%>
+
+<hr>
+
+
+<% } %>
+
+<%
+out.println(ServletUtil.htmlFooter());
+%>
+

+ 1 - 2
src/webapps/job/jobtasks.jsp

@@ -48,7 +48,6 @@
   }
 %>
 
-<%@page import="org.apache.hadoop.hdfs.server.namenode.JspHelper"%>
 <html>
   <head>
     <title>Hadoop <%=type%> task list for <%=jobid%> on <%=trackerName%></title>
@@ -108,7 +107,7 @@
                     "&tipid=" + report.getTaskId() + "\">"  + 
                     report.getTaskId() + "</a></td>");
          out.print("<td>" + StringUtils.formatPercent(report.getProgress(),2) +
-        		   JspHelper.percentageGraph(report.getProgress() * 100f, 80) + "</td>");
+        		   ServletUtil.percentageGraph(report.getProgress() * 100f, 80) + "</td>");
          out.print("<td>"  + report.getState() + "<br/></td>");
          out.println("<td>" + StringUtils.getFormattedTimeWithDiff(dateFormat, report.getStartTime(),0) + "<br/></td>");
          out.println("<td>" + StringUtils.getFormattedTimeWithDiff(dateFormat, 

+ 39 - 129
src/webapps/job/jobtracker.jsp

@@ -12,125 +12,14 @@
   JobTracker tracker = (JobTracker) application.getAttribute("job.tracker");
   String trackerName = 
            StringUtils.simpleHostname(tracker.getJobTrackerMachine());
-  int rowId = 0;
+  JobQueueInfo[] queues = tracker.getQueues();
+  Vector<JobInProgress> runningJobs = tracker.runningJobs();
+  Vector<JobInProgress> completedJobs = tracker.completedJobs();
+  Vector<JobInProgress> failedJobs = tracker.failedJobs();
 %>
 <%!
-  private static final String PRIVATE_ACTIONS_KEY 
-    = "webinterface.private.actions";
   private static DecimalFormat percentFormat = new DecimalFormat("##0.00");
   
-  public void processButtons(JspWriter out, 
-          HttpServletRequest request,
-          HttpServletResponse response,
-          JobTracker tracker) throws IOException {
-  
-    if (JspHelper.conf.getBoolean(PRIVATE_ACTIONS_KEY, false) &&
-        request.getParameter("killJobs") != null) {
-      String[] jobs = request.getParameterValues("jobCheckBox");
-      if (jobs != null) {
-        for (String job : jobs) {
-          tracker.killJob(JobID.forName(job));
-        }
-      }
-    }
-    
-    if (request.getParameter("changeJobPriority") != null) {
-      String[] jobs = request.getParameterValues("jobCheckBox");
-      
-      if (jobs != null) {
-        JobPriority jobPri = JobPriority.valueOf(request.getParameter("setJobPriority"));
-          
-        for (String job : jobs) {
-          tracker.setJobPriority(JobID.forName(job), jobPri);
-        }
-      }
-    }
-  }
-
-  public int generateJobTable(JspWriter out, String label, Vector jobs, int refresh, int rowId) throws IOException {
-    
-    boolean isModifiable = label.equals("Running");
-
-    out.print("<table border=\"1\" cellpadding=\"5\" cellspacing=\"0\">\n");
-    
-    if (jobs.size() > 0) {
-      if (isModifiable) {
-        out.print("<tr>");
-        out.print("<td><input type=\"Button\" onclick=\"selectAll()\" value=\"Select All\" id=\"checkEm\"></td>");
-        out.print("<td>");
-        if (JspHelper.conf.getBoolean(PRIVATE_ACTIONS_KEY, false)) {
-          out.print("<input type=\"submit\" name=\"killJobs\" value=\"Kill Selected Jobs\">");
-        }
-        out.print("</td");
-        out.print("<td><nobr>");
-        out.print("<select name=\"setJobPriority\">");
-
-        for (JobPriority prio : JobPriority.values()) {
-         out.print("<option" + (JobPriority.NORMAL == prio ? " selected=\"selected\">" : ">") + prio + "</option>");
-        }
-
-        out.print("</select>");
-        out.print("<input type=\"submit\" name=\"changeJobPriority\" value=\"Change\">");
-        out.print("</nobr></td>");
-        out.print("<td colspan=\"8\">&nbsp;</td>");
-        out.print("</tr>");
-        out.print("<td>&nbsp;</td>");
-      } else {
-        out.print("<tr>");
-      }
-      
-      out.print("<td><b>Jobid</b></td><td><b>Priority</b></td><td><b>User</b></td>");
-      out.print("<td><b>Name</b></td>");
-      out.print("<td><b>Map % Complete</b></td>");
-      out.print("<td><b>Map Total</b></td>");
-      out.print("<td><b>Maps Completed</b></td>");
-      out.print("<td><b>Reduce % Complete</b></td>");
-      out.print("<td><b>Reduce Total</b></td>");
-      out.print("<td><b>Reduces Completed</b></td></tr>\n");
-      for (Iterator it = jobs.iterator(); it.hasNext(); ++rowId) {
-        JobInProgress job = (JobInProgress) it.next();
-        JobProfile profile = job.getProfile();
-        JobStatus status = job.getStatus();
-        JobID jobid = profile.getJobID();
-
-        int desiredMaps = job.desiredMaps();
-        int desiredReduces = job.desiredReduces();
-        int completedMaps = job.finishedMaps();
-        int completedReduces = job.finishedReduces();
-        String name = profile.getJobName();
-        String jobpri = job.getPriority().toString();
-        
-        if (isModifiable) {
-          out.print("<tr><td><input TYPE=\"checkbox\" onclick=\"checkButtonVerbage()\" name=\"jobCheckBox\" value=" +
-                    jobid + "></td>");
-        } else {
-            out.print("<tr>");
-        }
-
-        out.print("<td id=\"job_" + rowId + "\"><a href=\"jobdetails.jsp?jobid=" +
-                  jobid + "&refresh=" + refresh + "\">" + jobid +"</a></td>" +
-                  "<td id=\"priority_" + rowId + "\">" + jobpri + "</td>" +
-                  "<td id=\"user_" + rowId + "\">" + profile.getUser() + "</td>" + 
-                  "<td id=\"name_" + rowId + "\">" + ("".equals(name) ? "&nbsp;" : name) +
-                  "</td>" +
-                  "<td>" + StringUtils.formatPercent(status.mapProgress(),2) +
-                  JspHelper.percentageGraph(status.mapProgress()  * 100, 80) +
-                  "</td><td>" + 
-                  desiredMaps + "</td><td>" + completedMaps + "</td><td>" + 
-                  StringUtils.formatPercent(status.reduceProgress(),2) +
-                  JspHelper.percentageGraph(status.reduceProgress() * 100, 80) +
-                  "</td><td>" + 
-                  desiredReduces + "</td><td> " + completedReduces + 
-                  "</td></tr>\n");
-      }
-    } else {
-      out.print("<tr><td align=\"center\" colspan=\"8\"><i>none</i></td></tr>\n");
-    }
-    out.print("</table>\n");
-    
-    return rowId;
-  }
-
   public void generateSummaryTable(JspWriter out,
                                    JobTracker tracker) throws IOException {
     ClusterStatus status = tracker.getClusterStatus();
@@ -166,7 +55,7 @@
     }
   }%>
 
-<%@page import="org.apache.hadoop.hdfs.server.namenode.JspHelper"%>
+
 <html>
 <head>
 <title><%= trackerName %> Hadoop Map/Reduce Administration</title>
@@ -175,13 +64,14 @@
 </head>
 <body>
 
-<% processButtons(out, request, response, tracker); %>
+<% JSPUtil.processButtons(request, response, tracker); %>
 
 <h1><%= trackerName %> Hadoop Map/Reduce Administration</h1>
 
 <div id="quicklinks">
   <a href="#quicklinks" onclick="toggle('quicklinks-list'); return false;">Quick Links</a>
   <ul id="quicklinks-list">
+    <li><a href="#scheduling_info">Scheduling Info</a></li>
     <li><a href="#running_jobs">Running Jobs</a></li>
     <li><a href="#completed_jobs">Completed Jobs</a></li>
     <li><a href="#failed_jobs">Failed Jobs</a></li>
@@ -203,29 +93,49 @@
  generateSummaryTable(out, tracker); 
 %>
 <hr>
-
+<h2 id="scheduling_info">Scheduling Information</h2>
+<table border="2" cellpadding="5" cellspacing="2">
+<thead style="font-weight: bold">
+<tr>
+<td> Queue Name </td>
+<td> Scheduling Information</td>
+</tr>
+</thead>
+<tbody>
+<%
+for(JobQueueInfo queue: queues) {
+  String queueName = queue.getQueueName();
+  String schedulingInformation = queue.getSchedulingInfo();
+  if(schedulingInformation == null || schedulingInformation.trim().equals("")) {
+    schedulingInformation = "NA";
+  }
+%>
+<tr>
+<td><a href="jobqueue_details.jsp?queueName=<%=queueName%>"><%=queueName%></a></td>
+<td><%=schedulingInformation.replaceAll("\n","<br/>") %>
+</td>
+</tr>
+<%
+}
+%>
+</tbody>
+</table>
+<hr/>
 <b>Filter (Jobid, Priority, User, Name)</b> <input type="text" id="filter" onkeyup="applyfilter()"> <br>
 <span class="small">Example: 'user:smith 3200' will filter by 'smith' only in the user field and '3200' in all fields</span>
 <hr>
 
 <h2 id="running_jobs">Running Jobs</h2>
-<%
-  out.print("<form action=\"/jobtracker.jsp\" onsubmit=\"return confirmAction();\" method=\"POST\">");
-  rowId = generateJobTable(out, "Running", tracker.runningJobs(), 30, rowId);
-  out.print("</form>\n");
-%>
+<%=JSPUtil.generateJobTable("Running", runningJobs, 30, 0)%>
 <hr>
 
 <h2 id="completed_jobs">Completed Jobs</h2>
-<%
-  rowId = generateJobTable(out, "Completed", tracker.completedJobs(), 0, rowId);
-%>
+<%=JSPUtil.generateJobTable("Completed", completedJobs, 0, runningJobs.size())%>
 <hr>
 
 <h2 id="failed_jobs">Failed Jobs</h2>
-<%
-    generateJobTable(out, "Failed", tracker.failedJobs(), 0, rowId);
-%>
+<%=JSPUtil.generateJobTable("Failed", failedJobs, 0, 
+    (runningJobs.size()+completedJobs.size()))%>
 <hr>
 
 <h2 id="local_logs">Local Logs</h2>

+ 3 - 4
src/webapps/job/taskdetails.jsp

@@ -9,7 +9,6 @@
   import="org.apache.hadoop.util.*"
   import="java.text.SimpleDateFormat"  
   import="org.apache.hadoop.util.*"
-  import="org.apache.hadoop.hdfs.server.namenode.JspHelper"
 %>
 <%!static SimpleDateFormat dateFormat = new SimpleDateFormat(
       "d-MMM-yyyy HH:mm:ss");
@@ -41,7 +40,7 @@
     
     JobInProgress job = (JobInProgress) tracker.getJob(jobidObj);
     
-    boolean privateActions = JspHelper.conf.getBoolean(PRIVATE_ACTIONS_KEY,
+    boolean privateActions = JSPUtil.conf.getBoolean(PRIVATE_ACTIONS_KEY,
         false);
     if (privateActions) {
       String action = request.getParameter("action");
@@ -76,7 +75,7 @@
     }
 %>
 
-<%@page import="org.apache.hadoop.hdfs.server.namenode.JspHelper"%>
+
 <html>
 <head>
   <link rel="stylesheet" type="text/css" href="/static/hadoop.css">
@@ -123,7 +122,7 @@
         }
         out.print("<td>" + status.getRunState() + "</td>");
         out.print("<td>" + StringUtils.formatPercent(status.getProgress(), 2)
-          + JspHelper.percentageGraph(status.getProgress() * 100f, 80) + "</td>");
+          + ServletUtil.percentageGraph(status.getProgress() * 100f, 80) + "</td>");
         out.print("<td>"
           + StringUtils.getFormattedTimeWithDiff(dateFormat, status
           .getStartTime(), 0) + "</td>");