Browse Source

HUE-612 [jb] Show retired jobs in the job browser

Retired jobs are listed but can't be viewed.
Romain Rigaux 13 years ago
parent
commit
7ad2b31ad8

+ 22 - 15
apps/jobbrowser/src/jobbrowser/models.py

@@ -106,10 +106,13 @@ class Job(JobLinkage):
     self._conf_keys = None
     self._full_job_conf = None
     self._init_attributes()
+    self.is_retired = hasattr(thriftJob, 'is_retired')
 
   @property
   def counters(self):
-    if self._counters is None:
+    if self.is_retired:
+      self._counters = {}
+    elif self._counters is None:
       rollups = self.jt.get_job_counter_rollups(self.job.jobID)
       # We get back a structure with counter lists for maps, reduces, and total
       # and we need to invert this
@@ -231,20 +234,24 @@ class Job(JobLinkage):
     return [ t for t in self.tasks if is_good_match(t) ]
 
   def _initialize_conf_keys(self):
-    conf_keys = [
-      'mapred.mapper.class',
-      'mapred.reducer.class',
-      'mapred.input.format.class',
-      'mapred.output.format.class',
-      'mapred.input.dir',
-      'mapred.output.dir',
-      ]
-    jobconf = get_jobconf(self.jt, self.jobId)
-    self._full_job_conf = jobconf
-    self._conf_keys = {}
-    for k, v in jobconf.iteritems():
-      if k in conf_keys:
-        self._conf_keys[dots_to_camel_case(k)] = v
+    if self.is_retired:
+      self._conf_keys = {}
+      self._full_job_conf = {}
+    else:
+      conf_keys = [
+        'mapred.mapper.class',
+        'mapred.reducer.class',
+        'mapred.input.format.class',
+        'mapred.output.format.class',
+        'mapred.input.dir',
+        'mapred.output.dir',
+        ]
+      jobconf = get_jobconf(self.jt, self.jobId)
+      self._full_job_conf = jobconf
+      self._conf_keys = {}
+      for k, v in jobconf.iteritems():
+        if k in conf_keys:
+          self._conf_keys[dots_to_camel_case(k)] = v
 
 
 class TaskList(object):

+ 44 - 9
apps/jobbrowser/src/jobbrowser/templates/jobs.mako

@@ -35,7 +35,6 @@ ${commonheader(_('Job Browser'), "jobbrowser")}
 <div class="well hueWell">
     <form action="/jobbrowser/jobs" method="GET">
         <b>${_('Filter jobs:')}</b>
-
                 <select name="state" class="submitter">
                     <option value="all" ${get_state('all', state_filter)}>${_('All States')}</option>
                     <option value="running" ${get_state('running', state_filter)}>${_('Running')}</option>
@@ -45,9 +44,7 @@ ${commonheader(_('Job Browser'), "jobbrowser")}
                 </select>
 
                 <input type="text" name="user" title="${_('User Name Filter')}" value="${user_filter}" placeholder="${_('User Name Filter')}" class="submitter"/>
-
                 <input type="text" name="text" title="${_('Text Filter')}" value="${text_filter}" placeholder="${_('Text Filter')}" class="submitter"/>
-
     </form>
 </div>
 
@@ -58,7 +55,8 @@ ${commonheader(_('Job Browser'), "jobbrowser")}
 <table class="datatables table table-striped table-condensed">
     <thead>
         <tr>
-            <th>${_('Name / Id')}</th>
+            <th>${_('Id')}</th>
+            <th>${_('Name')}</th>
             <th>${_('Status')}</th>
             <th>${_('User')}</th>
             <th>${_('Maps')}</th>
@@ -73,23 +71,58 @@ ${commonheader(_('Job Browser'), "jobbrowser")}
     <tbody>
         % for job in jobs:
         <tr>
-            <td>${job.jobName}
+            <td>
                 <div class="jobbrowser_jobid_short">${job.jobId_short}</div>
             </td>
             <td>
-                <a href="${url('jobbrowser.views.jobs')}?${get_state_link(request, 'state', job.status.lower())}" title="${_('Show only %(status)s jobs') % dict(status=job.status.lower())}">${job.status.lower()}</a>
+                ${job.jobName}
+            </td>
+            <td>
+                <a href="${url('jobbrowser.views.jobs')}?${get_state_link(request, 'state', job.status.lower())}" title="${_('Show only %(status)s jobs') % dict(status=job.status.lower())}">
+                  ${job.status.lower()}
+                </a>
+                % if job.is_retired:
+                  </br>
+                  ${_('retired')}
+                % endif
             </td>
             <td>
                 <a href="${url('jobbrowser.views.jobs')}?${get_state_link(request, 'user', job.user.lower())}" title="${_('Show only %(status)s jobs') % dict(status=job.user.lower())}">${job.user}</a>
             </td>
-            <td><span alt="${job.maps_percent_complete}">${comps.mr_graph_maps(job)}</span></td>
-            <td><span alt="${job.reduces_percent_complete}">${comps.mr_graph_reduces(job)}</span></td>
+            <td>
+                <span alt="${job.maps_percent_complete}">
+                    % if job.is_retired:
+                        ${_('N/A')}
+                    % else:
+                    ${comps.mr_graph_maps(job)}
+                    % endif
+                 </span>
+            </td>
+            <td>
+                <span alt="${job.reduces_percent_complete}">
+                    % if job.is_retired:
+                        ${_('N/A')}
+                    % else:
+                        ${comps.mr_graph_reduces(job)}
+                    % endif
+                </span>
+            </td>
             <td>${job.queueName}</td>
             <td>${job.priority.lower()}</td>
-            <td><span alt="${job.finishTimeMs-job.startTimeMs}">${job.durationFormatted}</span></td>
+            <td>
+                <span alt="${job.finishTimeMs-job.startTimeMs}">
+                    % if job.is_retired:
+                        ${_('N/A')}
+                    % else:
+                        ${job.durationFormatted}
+                    % endif
+                </span>
+            </td>
             <td><span alt="${job.startTimeMs}">${job.startTimeFormatted}</span></td>
             <td>
+                % if not job.is_retired:
                 <a href="${url('jobbrowser.views.single_job', jobid=job.jobId)}" title="${_('View this job')}" data-row-selector="true">${_('View')}</a>
+                % endif
                 % if job.status.lower() == 'running' or job.status.lower() == 'pending':
                 % if request.user.is_superuser or request.user.username == job.user:
                 - <a href="#" title="${_('Kill this job')}" onclick="$('#kill-job').submit()">${_('Kill')}</a>
@@ -126,10 +159,12 @@ ${commonheader(_('Job Browser'), "jobbrowser")}
             "bLengthChange": false,
             "bFilter": false,
             "bInfo": false,
+            "aaSorting": [[ 0, "desc" ]],
             "aoColumns": [
                 null,
                 null,
                 null,
+                null,
                 { "sType": "alt-numeric" },
                 { "sType": "alt-numeric" },
                 null,

+ 9 - 10
apps/jobbrowser/src/jobbrowser/views.py

@@ -33,8 +33,8 @@ from django.utils.functional import wraps
 
 from desktop.log.access import access_warn, access_log_level
 from desktop.views import register_status_bar_view
-from hadoop.api.jobtracker.ttypes import ThriftJobPriority
-from hadoop.api.jobtracker.ttypes import TaskTrackerNotFoundException
+from hadoop.api.jobtracker.ttypes import ThriftJobPriority, TaskTrackerNotFoundException, ThriftJobState
+
 
 from jobbrowser import conf
 from jobbrowser.models import Job, JobLinkage, TaskList, Tracker, Cluster
@@ -469,25 +469,24 @@ def get_matching_jobs(request, check_permission=False, **kwargs):
 
   Filter by user ownership if check_permission is set to true.
   """
-  jobfunc = {"completed" : request.jt.completed_jobs,
+  jobfunc = {"completed" : (request.jt.completed_jobs, ThriftJobState.SUCCEEDED),
              # Succeeded and completed are synonyms here.
-             "succeeded" : request.jt.completed_jobs,
-             "running" : request.jt.running_jobs,
-             "failed" : request.jt.failed_jobs,
-             "killed" : request.jt.killed_jobs,
-             "all" : request.jt.all_jobs}
+             "succeeded" : (request.jt.completed_jobs, ThriftJobState.SUCCEEDED),
+             "running" : (request.jt.running_jobs, ThriftJobState.RUNNING),
+             "failed" : (request.jt.failed_jobs, ThriftJobState.FAILED),
+             "killed" : (request.jt.killed_jobs, ThriftJobState.KILLED),
+             "all" : (request.jt.all_jobs, None)}
   if 'state' in kwargs:
     selection = kwargs['state']
   else:
     selection = request.GET.get("state", "all")
 
-  joblist = jobfunc[selection]().jobs
+  joblist = jobfunc[selection][0]().jobs + request.jt.retired_jobs(jobfunc[selection][1]).jobs
 
   return [Job.from_thriftjob(request.jt, j)
           for j in _filter_jobs_by_req(joblist, request, **kwargs)
           if not check_permission or request.user.is_superuser or j.profile.user == request.user]
 
-
 def get_job_count_by_state(request, username):
   """
   Returns the number of comlpeted, running, and failed jobs for a user.

+ 2 - 0
desktop/libs/hadoop/README

@@ -3,3 +3,5 @@ the generated code by running regenerate-thrift.sh, in this
 directory.  Finally, checkin any files that were generated during the previous
 steps.
 
+If you need to install the Thrift compiler, go to http://thrift.apache.org/ and
+pick the version 0.7.0.

+ 7 - 0
desktop/libs/hadoop/gen-py/hadoop/api/jobtracker/Jobtracker-remote

@@ -27,6 +27,7 @@ if len(sys.argv) <= 1 or sys.argv[1] == '--help':
   print '  ThriftJobInProgress getJob(RequestContext ctx, ThriftJobID jobID)'
   print '  ThriftJobList getRunningJobs(RequestContext ctx)'
   print '  ThriftJobList getCompletedJobs(RequestContext ctx)'
+  print '  ThriftJobList getRetiredJobs(RequestContext ctx, ThriftJobState state)'
   print '  ThriftJobList getFailedJobs(RequestContext ctx)'
   print '  ThriftJobList getKilledJobs(RequestContext ctx)'
   print '  ThriftJobList getAllJobs(RequestContext ctx)'
@@ -131,6 +132,12 @@ elif cmd == 'getCompletedJobs':
     sys.exit(1)
   pp.pprint(client.getCompletedJobs(eval(args[0]),))
 
+elif cmd == 'getRetiredJobs':
+  if len(args) != 2:
+    print 'getRetiredJobs requires 2 args'
+    sys.exit(1)
+  pp.pprint(client.getRetiredJobs(eval(args[0]),eval(args[1]),))
+
 elif cmd == 'getFailedJobs':
   if len(args) != 1:
     print 'getFailedJobs requires 1 args'

+ 197 - 0
desktop/libs/hadoop/gen-py/hadoop/api/jobtracker/Jobtracker.py

@@ -75,6 +75,16 @@ class Iface(hadoop.api.common.HadoopServiceBase.Iface):
     """
     pass
 
+  def getRetiredJobs(self, ctx, state):
+    """
+    Get a list of retired jobs
+
+    Parameters:
+     - ctx
+     - state
+    """
+    pass
+
   def getFailedJobs(self, ctx):
     """
     Get a list of failed (due to error, not killed) jobs
@@ -462,6 +472,40 @@ class Client(hadoop.api.common.HadoopServiceBase.Client, Iface):
       return result.success
     raise TApplicationException(TApplicationException.MISSING_RESULT, "getCompletedJobs failed: unknown result");
 
+  def getRetiredJobs(self, ctx, state):
+    """
+    Get a list of retired jobs
+
+    Parameters:
+     - ctx
+     - state
+    """
+    self.send_getRetiredJobs(ctx, state)
+    return self.recv_getRetiredJobs()
+
+  def send_getRetiredJobs(self, ctx, state):
+    self._oprot.writeMessageBegin('getRetiredJobs', TMessageType.CALL, self._seqid)
+    args = getRetiredJobs_args()
+    args.ctx = ctx
+    args.state = state
+    args.write(self._oprot)
+    self._oprot.writeMessageEnd()
+    self._oprot.trans.flush()
+
+  def recv_getRetiredJobs(self, ):
+    (fname, mtype, rseqid) = self._iprot.readMessageBegin()
+    if mtype == TMessageType.EXCEPTION:
+      x = TApplicationException()
+      x.read(self._iprot)
+      self._iprot.readMessageEnd()
+      raise x
+    result = getRetiredJobs_result()
+    result.read(self._iprot)
+    self._iprot.readMessageEnd()
+    if result.success is not None:
+      return result.success
+    raise TApplicationException(TApplicationException.MISSING_RESULT, "getRetiredJobs failed: unknown result");
+
   def getFailedJobs(self, ctx):
     """
     Get a list of failed (due to error, not killed) jobs
@@ -1108,6 +1152,7 @@ class Processor(hadoop.api.common.HadoopServiceBase.Processor, Iface, TProcessor
     self._processMap["getJob"] = Processor.process_getJob
     self._processMap["getRunningJobs"] = Processor.process_getRunningJobs
     self._processMap["getCompletedJobs"] = Processor.process_getCompletedJobs
+    self._processMap["getRetiredJobs"] = Processor.process_getRetiredJobs
     self._processMap["getFailedJobs"] = Processor.process_getFailedJobs
     self._processMap["getKilledJobs"] = Processor.process_getKilledJobs
     self._processMap["getAllJobs"] = Processor.process_getAllJobs
@@ -1214,6 +1259,17 @@ class Processor(hadoop.api.common.HadoopServiceBase.Processor, Iface, TProcessor
     oprot.writeMessageEnd()
     oprot.trans.flush()
 
+  def process_getRetiredJobs(self, seqid, iprot, oprot):
+    args = getRetiredJobs_args()
+    args.read(iprot)
+    iprot.readMessageEnd()
+    result = getRetiredJobs_result()
+    result.success = self._handler.getRetiredJobs(args.ctx, args.state)
+    oprot.writeMessageBegin("getRetiredJobs", TMessageType.REPLY, seqid)
+    result.write(oprot)
+    oprot.writeMessageEnd()
+    oprot.trans.flush()
+
   def process_getFailedJobs(self, seqid, iprot, oprot):
     args = getFailedJobs_args()
     args.read(iprot)
@@ -2272,6 +2328,147 @@ class getCompletedJobs_result(object):
   def __ne__(self, other):
     return not (self == other)
 
+class getRetiredJobs_args(object):
+  """
+  Attributes:
+   - ctx
+   - state
+  """
+
+  thrift_spec = (
+    None, # 0
+    (1, TType.I32, 'state', None, None, ), # 1
+    None, # 2
+    None, # 3
+    None, # 4
+    None, # 5
+    None, # 6
+    None, # 7
+    None, # 8
+    None, # 9
+    (10, TType.STRUCT, 'ctx', (hadoop.api.common.ttypes.RequestContext, hadoop.api.common.ttypes.RequestContext.thrift_spec), None, ), # 10
+  )
+
+  def __init__(self, ctx=None, state=None,):
+    self.ctx = ctx
+    self.state = state
+
+  def read(self, iprot):
+    if iprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None and fastbinary is not None:
+      fastbinary.decode_binary(self, iprot.trans, (self.__class__, self.thrift_spec))
+      return
+    iprot.readStructBegin()
+    while True:
+      (fname, ftype, fid) = iprot.readFieldBegin()
+      if ftype == TType.STOP:
+        break
+      if fid == 10:
+        if ftype == TType.STRUCT:
+          self.ctx = hadoop.api.common.ttypes.RequestContext()
+          self.ctx.read(iprot)
+        else:
+          iprot.skip(ftype)
+      elif fid == 1:
+        if ftype == TType.I32:
+          self.state = iprot.readI32();
+        else:
+          iprot.skip(ftype)
+      else:
+        iprot.skip(ftype)
+      iprot.readFieldEnd()
+    iprot.readStructEnd()
+
+  def write(self, oprot):
+    if oprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and self.thrift_spec is not None and fastbinary is not None:
+      oprot.trans.write(fastbinary.encode_binary(self, (self.__class__, self.thrift_spec)))
+      return
+    oprot.writeStructBegin('getRetiredJobs_args')
+    if self.state is not None:
+      oprot.writeFieldBegin('state', TType.I32, 1)
+      oprot.writeI32(self.state)
+      oprot.writeFieldEnd()
+    if self.ctx is not None:
+      oprot.writeFieldBegin('ctx', TType.STRUCT, 10)
+      self.ctx.write(oprot)
+      oprot.writeFieldEnd()
+    oprot.writeFieldStop()
+    oprot.writeStructEnd()
+
+  def validate(self):
+    return
+
+
+  def __repr__(self):
+    L = ['%s=%r' % (key, value)
+      for key, value in self.__dict__.iteritems()]
+    return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+  def __eq__(self, other):
+    return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+  def __ne__(self, other):
+    return not (self == other)
+
+class getRetiredJobs_result(object):
+  """
+  Attributes:
+   - success
+  """
+
+  thrift_spec = (
+    (0, TType.STRUCT, 'success', (ThriftJobList, ThriftJobList.thrift_spec), None, ), # 0
+  )
+
+  def __init__(self, success=None,):
+    self.success = success
+
+  def read(self, iprot):
+    if iprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None and fastbinary is not None:
+      fastbinary.decode_binary(self, iprot.trans, (self.__class__, self.thrift_spec))
+      return
+    iprot.readStructBegin()
+    while True:
+      (fname, ftype, fid) = iprot.readFieldBegin()
+      if ftype == TType.STOP:
+        break
+      if fid == 0:
+        if ftype == TType.STRUCT:
+          self.success = ThriftJobList()
+          self.success.read(iprot)
+        else:
+          iprot.skip(ftype)
+      else:
+        iprot.skip(ftype)
+      iprot.readFieldEnd()
+    iprot.readStructEnd()
+
+  def write(self, oprot):
+    if oprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and self.thrift_spec is not None and fastbinary is not None:
+      oprot.trans.write(fastbinary.encode_binary(self, (self.__class__, self.thrift_spec)))
+      return
+    oprot.writeStructBegin('getRetiredJobs_result')
+    if self.success is not None:
+      oprot.writeFieldBegin('success', TType.STRUCT, 0)
+      self.success.write(oprot)
+      oprot.writeFieldEnd()
+    oprot.writeFieldStop()
+    oprot.writeStructEnd()
+
+  def validate(self):
+    return
+
+
+  def __repr__(self):
+    L = ['%s=%r' % (key, value)
+      for key, value in self.__dict__.iteritems()]
+    return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+  def __eq__(self, other):
+    return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+  def __ne__(self, other):
+    return not (self == other)
+
 class getFailedJobs_args(object):
   """
   Attributes:

+ 2 - 2
desktop/libs/hadoop/java/README

@@ -28,12 +28,12 @@ Compilation
 The compilation process creates a server org.apache.hadoop.thriftfs.HadooopThriftServer
 that implements the Thrift interface defined in if/hadoopfs.thrift.
 
-Th thrift compiler is used to generate API stubs in python, php, ruby,
+The thrift compiler is used to generate API stubs in python, php, ruby,
 cocoa, etc. The generated code is checked into the directories gen-*.
 The generated java API is checked into lib/hadoopthriftapi.jar.
 
 There is a sample python script hdfs.py in the scripts directory. This python 
 script, when invoked, creates a HadoopThriftServer in the background, and then
-communicates wth HDFS using the API. This script is for demonstration purposes
+communicates with HDFS using the API. This script is for demonstration purposes
 only.
 

+ 75 - 73
desktop/libs/hadoop/java/if/jobtracker.thrift

@@ -169,9 +169,9 @@ struct ThriftTaskStatus {
 
   11: ThriftGroupList counters
 
-	12: i64 shuffleFinishTime,
-	13: i64 sortFinishTime,
-	14: i64 mapFinishTime,
+  12: i64 shuffleFinishTime,
+  13: i64 sortFinishTime,
+  14: i64 mapFinishTime,
 }
 
 /**
@@ -245,11 +245,11 @@ enum JobTrackerState {
 
 /** Enum version of the ints in JobStatus */
 enum ThriftJobState {
-		 RUNNING = 1,
-		 SUCCEEDED = 2,
-		 FAILED = 3,
-		 PREP = 4,
-		 KILLED = 5
+     RUNNING = 1,
+     SUCCEEDED = 2,
+     FAILED = 3,
+     PREP = 4,
+     KILLED = 5
 }
 
 /** Status of a job */
@@ -292,17 +292,17 @@ struct ThriftJobInProgress {
   1: ThriftJobProfile profile
   2: ThriftJobStatus status
   3: ThriftJobID jobID
-  4: i32 desiredMaps
-  5: i32 desiredReduces
-  6: i32 finishedMaps
-  7: i32 finishedReduces
-  8: ThriftJobPriority priority
+  4: i32 desiredMaps                     /* N/A for a Retired job */
+  5: i32 desiredReduces                  /* N/A for a Retired job */
+  6: i32 finishedMaps                    /* N/A for a Retired job */
+  7: i32 finishedReduces                 /* N/A for a Retired job */
+  8: ThriftJobPriority priority          /* N/A for a Retired job, but present in status field */
 
   11: i64 startTime
-  12: i64 finishTime
-  13: i64 launchTime
+  12: i64 finishTime                     /* N/A for a Retired job */
+  13: i64 launchTime                     /* N/A for a Retired job */
 
-  23: ThriftTaskInProgressList tasks
+  23: ThriftTaskInProgressList tasks     /* N/A for a Retired job */
 }
 
 /** Container structure of a list of jobs, in case we ever want to add metadata */
@@ -352,7 +352,7 @@ struct ThriftClusterStatus {
   18: string hostname
   19: string identifier
 
-	20: i32 httpPort
+  20: i32 httpPort
 }
 
 /** Merely an indicator that job wasn't found. */
@@ -373,40 +373,43 @@ exception TaskTrackerNotFoundException {
 
 /** A proxy service onto a Jobtracker, exposing read-only methods for cluster monitoring */
 service Jobtracker extends common.HadoopServiceBase {
-	/** Get the name of the tracker exporting this service */
-	string getJobTrackerName(10: common.RequestContext ctx),
+  /** Get the name of the tracker exporting this service */
+  string getJobTrackerName(10: common.RequestContext ctx),
 
-	/** Get the current cluster status */
-	ThriftClusterStatus getClusterStatus(10: common.RequestContext ctx),
+  /** Get the current cluster status */
+  ThriftClusterStatus getClusterStatus(10: common.RequestContext ctx),
 
-	/** Get a list of job queues managed by this tracker */
-	ThriftJobQueueList getQueues(10: common.RequestContext ctx)
-				 	   throws(1: common.IOException err),
+  /** Get a list of job queues managed by this tracker */
+  ThriftJobQueueList getQueues(10: common.RequestContext ctx)
+      throws(1: common.IOException err),
 
-	/** Get a job by ID */
-        ThriftJobInProgress getJob(10: common.RequestContext ctx, 1: ThriftJobID jobID)
-                                  throws(1: JobNotFoundException err),
+  /** Get a job by ID */
+  ThriftJobInProgress getJob(10: common.RequestContext ctx, 1: ThriftJobID jobID)
+      throws(1: JobNotFoundException err),
 
-	/** Get a list of currently running jobs */
-	ThriftJobList getRunningJobs(10: common.RequestContext ctx),
+  /** Get a list of currently running jobs */
+  ThriftJobList getRunningJobs(10: common.RequestContext ctx),
 
-	/** Get a list of completed jobs */
-	ThriftJobList getCompletedJobs(10: common.RequestContext ctx),
+  /** Get a list of completed jobs */
+  ThriftJobList getCompletedJobs(10: common.RequestContext ctx),
 
-	/** Get a list of failed (due to error, not killed) jobs */
-	ThriftJobList getFailedJobs(10: common.RequestContext ctx),
+  /** Get a list of retired jobs */
+  ThriftJobList getRetiredJobs(10: common.RequestContext ctx, 1: ThriftJobState state),
 
-	/** Get a list of killed jobs */
-	ThriftJobList getKilledJobs(10: common.RequestContext ctx),
+  /** Get a list of failed (due to error, not killed) jobs */
+  ThriftJobList getFailedJobs(10: common.RequestContext ctx),
 
-	/** Get a list of all failed, completed and running jobs (could be expensive!) */
-	ThriftJobList getAllJobs(10: common.RequestContext ctx),
+  /** Get a list of killed jobs */
+  ThriftJobList getKilledJobs(10: common.RequestContext ctx),
 
-        /** Get the count of jobs by status for a given user */
-        ThriftUserJobCounts getUserJobCounts(1: common.RequestContext ctx, 2: string user),
+  /** Get a list of all failed, completed and running jobs (could be expensive!) */
+  ThriftJobList getAllJobs(10: common.RequestContext ctx),
 
-        /** Get a (possibly incomplete) list of tasks */
-        ThriftTaskInProgressList getTaskList(
+  /** Get the count of jobs by status for a given user */
+  ThriftUserJobCounts getUserJobCounts(1: common.RequestContext ctx, 2: string user),
+
+  /** Get a (possibly incomplete) list of tasks */
+  ThriftTaskInProgressList getTaskList(
                                       1: common.RequestContext ctx,
                                       2: ThriftJobID jobID,
                                       3: set<ThriftTaskType> types,
@@ -415,55 +418,55 @@ service Jobtracker extends common.HadoopServiceBase {
                                       6: i32 count,
                                       7: i32 offset) throws(1: JobNotFoundException err),
 
-        /** Get details of a task */
-        ThriftTaskInProgress getTask(1: common.RequestContext ctx,
-                                     2: ThriftTaskID taskID)
-                        throws(1: JobNotFoundException jnf, 2: TaskNotFoundException tnf),
+  /** Get details of a task */
+  ThriftTaskInProgress getTask(1: common.RequestContext ctx,
+                               2: ThriftTaskID taskID)
+      throws(1: JobNotFoundException jnf, 2: TaskNotFoundException tnf),
 
-        /**
-         * Get a list of groups of counters attached to the job with provided id.
-         * This returns the total counters
-         **/
-        ThriftGroupList getJobCounters(10: common.RequestContext ctx,
-                                        1: ThriftJobID jobID)
-                                  throws(1: JobNotFoundException err),
+  /**
+   * Get a list of groups of counters attached to the job with provided id.
+   * This returns the total counters
+   **/
+  ThriftGroupList getJobCounters(10: common.RequestContext ctx,
+                                 1: ThriftJobID jobID)
+      throws(1: JobNotFoundException err),
 
 
-        /** Return job counters rolled up by map, reduce, and total */
-        ThriftJobCounterRollups getJobCounterRollups(10: common.RequestContext ctx,
-                                                     1: ThriftJobID jobID)
-                                  throws(1: JobNotFoundException err),
+   /** Return job counters rolled up by map, reduce, and total */
+   ThriftJobCounterRollups getJobCounterRollups(10: common.RequestContext ctx,
+                                                1: ThriftJobID jobID)
+      throws(1: JobNotFoundException err),
 
 
-	/** Get all active trackers */
-	ThriftTaskTrackerStatusList getActiveTrackers(10: common.RequestContext ctx),
+  /** Get all active trackers */
+  ThriftTaskTrackerStatusList getActiveTrackers(10: common.RequestContext ctx),
 
-	/** Get all blacklisted trackers */
-	ThriftTaskTrackerStatusList getBlacklistedTrackers(10: common.RequestContext ctx),
+  /** Get all blacklisted trackers */
+  ThriftTaskTrackerStatusList getBlacklistedTrackers(10: common.RequestContext ctx),
 
-	/** Get all trackers */
-	ThriftTaskTrackerStatusList getAllTrackers(10: common.RequestContext ctx),
+  /** Get all trackers */
+  ThriftTaskTrackerStatusList getAllTrackers(10: common.RequestContext ctx),
 
-	/** Get a single task tracker by name */
-	ThriftTaskTrackerStatus getTracker(10: common.RequestContext ctx, 1: string name)
-	          throws(1: TaskTrackerNotFoundException tne),
+  /** Get a single task tracker by name */
+  ThriftTaskTrackerStatus getTracker(10: common.RequestContext ctx, 1: string name)
+      throws(1: TaskTrackerNotFoundException tne),
 
   /** Get the current time in ms according to the JT */
   i64 getCurrentTime(10: common.RequestContext ctx),
 
   /** Get the xml for a job's configuration, serialised from the local filesystem on the JT */
   string getJobConfXML(10: common.RequestContext ctx, 1: ThriftJobID jobID)
-            throws(1: common.IOException err),
+      throws(1: common.IOException err),
 
   /** Kill a job */
   void killJob(10: common.RequestContext ctx, 1: ThriftJobID jobID)
-                             throws(1: common.IOException err, 2: JobNotFoundException jne),
+      throws(1: common.IOException err, 2: JobNotFoundException jne),
 
   /** Kill a task attempt */
   void killTaskAttempt(10: common.RequestContext ctx, 1: ThriftTaskAttemptID attemptID)
-                                   throws(1: common.IOException err,
-                                          2: TaskAttemptNotFoundException tne,
-                                          3: JobNotFoundException jne),
+      throws(1: common.IOException err,
+             2: TaskAttemptNotFoundException tne,
+             3: JobNotFoundException jne),
 
   /** Set a job's priority */
   void setJobPriority(10: common.RequestContext ctx,
@@ -472,7 +475,6 @@ service Jobtracker extends common.HadoopServiceBase {
       throws(1: common.IOException err, 2: JobNotFoundException jne),
 
   /** Get an MR delegation token. */
-  common.ThriftDelegationToken getDelegationToken(10:common.RequestContext ctx, 1:string renewer) throws(1: common.IOException err)
+  common.ThriftDelegationToken getDelegationToken(10:common.RequestContext ctx, 1:string renewer)
+      throws(1: common.IOException err)
 }
-
-

+ 786 - 0
desktop/libs/hadoop/java/src/main/gen-java/org/apache/hadoop/thriftfs/jobtracker/api/Jobtracker.java

@@ -70,6 +70,14 @@ public class Jobtracker {
      */
     public ThriftJobList getCompletedJobs(org.apache.hadoop.thriftfs.api.RequestContext ctx) throws org.apache.thrift.TException;
 
+    /**
+     * Get a list of retired jobs
+     * 
+     * @param ctx
+     * @param state
+     */
+    public ThriftJobList getRetiredJobs(org.apache.hadoop.thriftfs.api.RequestContext ctx, ThriftJobState state) throws org.apache.thrift.TException;
+
     /**
      * Get a list of failed (due to error, not killed) jobs
      * 
@@ -231,6 +239,8 @@ public class Jobtracker {
 
     public void getCompletedJobs(org.apache.hadoop.thriftfs.api.RequestContext ctx, org.apache.thrift.async.AsyncMethodCallback<AsyncClient.getCompletedJobs_call> resultHandler) throws org.apache.thrift.TException;
 
+    public void getRetiredJobs(org.apache.hadoop.thriftfs.api.RequestContext ctx, ThriftJobState state, org.apache.thrift.async.AsyncMethodCallback<AsyncClient.getRetiredJobs_call> resultHandler) throws org.apache.thrift.TException;
+
     public void getFailedJobs(org.apache.hadoop.thriftfs.api.RequestContext ctx, org.apache.thrift.async.AsyncMethodCallback<AsyncClient.getFailedJobs_call> resultHandler) throws org.apache.thrift.TException;
 
     public void getKilledJobs(org.apache.hadoop.thriftfs.api.RequestContext ctx, org.apache.thrift.async.AsyncMethodCallback<AsyncClient.getKilledJobs_call> resultHandler) throws org.apache.thrift.TException;
@@ -434,6 +444,30 @@ public class Jobtracker {
       throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "getCompletedJobs failed: unknown result");
     }
 
+    public ThriftJobList getRetiredJobs(org.apache.hadoop.thriftfs.api.RequestContext ctx, ThriftJobState state) throws org.apache.thrift.TException
+    {
+      send_getRetiredJobs(ctx, state);
+      return recv_getRetiredJobs();
+    }
+
+    public void send_getRetiredJobs(org.apache.hadoop.thriftfs.api.RequestContext ctx, ThriftJobState state) throws org.apache.thrift.TException
+    {
+      getRetiredJobs_args args = new getRetiredJobs_args();
+      args.setCtx(ctx);
+      args.setState(state);
+      sendBase("getRetiredJobs", args);
+    }
+
+    public ThriftJobList recv_getRetiredJobs() throws org.apache.thrift.TException
+    {
+      getRetiredJobs_result result = new getRetiredJobs_result();
+      receiveBase(result, "getRetiredJobs");
+      if (result.isSetSuccess()) {
+        return result.success;
+      }
+      throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "getRetiredJobs failed: unknown result");
+    }
+
     public ThriftJobList getFailedJobs(org.apache.hadoop.thriftfs.api.RequestContext ctx) throws org.apache.thrift.TException
     {
       send_getFailedJobs(ctx);
@@ -1114,6 +1148,41 @@ public class Jobtracker {
       }
     }
 
+    public void getRetiredJobs(org.apache.hadoop.thriftfs.api.RequestContext ctx, ThriftJobState state, org.apache.thrift.async.AsyncMethodCallback<getRetiredJobs_call> resultHandler) throws org.apache.thrift.TException {
+      checkReady();
+      getRetiredJobs_call method_call = new getRetiredJobs_call(ctx, state, resultHandler, this, ___protocolFactory, ___transport);
+      this.___currentMethod = method_call;
+      ___manager.call(method_call);
+    }
+
+    public static class getRetiredJobs_call extends org.apache.thrift.async.TAsyncMethodCall {
+      private org.apache.hadoop.thriftfs.api.RequestContext ctx;
+      private ThriftJobState state;
+      public getRetiredJobs_call(org.apache.hadoop.thriftfs.api.RequestContext ctx, ThriftJobState state, org.apache.thrift.async.AsyncMethodCallback<getRetiredJobs_call> resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+        super(client, protocolFactory, transport, resultHandler, false);
+        this.ctx = ctx;
+        this.state = state;
+      }
+
+      public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException {
+        prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("getRetiredJobs", org.apache.thrift.protocol.TMessageType.CALL, 0));
+        getRetiredJobs_args args = new getRetiredJobs_args();
+        args.setCtx(ctx);
+        args.setState(state);
+        args.write(prot);
+        prot.writeMessageEnd();
+      }
+
+      public ThriftJobList getResult() throws org.apache.thrift.TException {
+        if (getState() != org.apache.thrift.async.TAsyncMethodCall.State.RESPONSE_READ) {
+          throw new IllegalStateException("Method call not finished!");
+        }
+        org.apache.thrift.transport.TMemoryInputTransport memoryTransport = new org.apache.thrift.transport.TMemoryInputTransport(getFrameBuffer().array());
+        org.apache.thrift.protocol.TProtocol prot = client.getProtocolFactory().getProtocol(memoryTransport);
+        return (new Client(prot)).recv_getRetiredJobs();
+      }
+    }
+
     public void getFailedJobs(org.apache.hadoop.thriftfs.api.RequestContext ctx, org.apache.thrift.async.AsyncMethodCallback<getFailedJobs_call> resultHandler) throws org.apache.thrift.TException {
       checkReady();
       getFailedJobs_call method_call = new getFailedJobs_call(ctx, resultHandler, this, ___protocolFactory, ___transport);
@@ -1760,6 +1829,7 @@ public class Jobtracker {
       processMap.put("getJob", new getJob());
       processMap.put("getRunningJobs", new getRunningJobs());
       processMap.put("getCompletedJobs", new getCompletedJobs());
+      processMap.put("getRetiredJobs", new getRetiredJobs());
       processMap.put("getFailedJobs", new getFailedJobs());
       processMap.put("getKilledJobs", new getKilledJobs());
       processMap.put("getAllJobs", new getAllJobs());
@@ -1885,6 +1955,22 @@ public class Jobtracker {
       }
     }
 
+    private static class getRetiredJobs<I extends Iface> extends org.apache.thrift.ProcessFunction<I, getRetiredJobs_args> {
+      public getRetiredJobs() {
+        super("getRetiredJobs");
+      }
+
+      protected getRetiredJobs_args getEmptyArgsInstance() {
+        return new getRetiredJobs_args();
+      }
+
+      protected getRetiredJobs_result getResult(I iface, getRetiredJobs_args args) throws org.apache.thrift.TException {
+        getRetiredJobs_result result = new getRetiredJobs_result();
+        result.success = iface.getRetiredJobs(args.ctx, args.state);
+        return result;
+      }
+    }
+
     private static class getFailedJobs<I extends Iface> extends org.apache.thrift.ProcessFunction<I, getFailedJobs_args> {
       public getFailedJobs() {
         super("getFailedJobs");
@@ -6063,6 +6149,706 @@ public class Jobtracker {
 
   }
 
+  public static class getRetiredJobs_args implements org.apache.thrift.TBase<getRetiredJobs_args, getRetiredJobs_args._Fields>, java.io.Serializable, Cloneable   {
+    private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("getRetiredJobs_args");
+
+    private static final org.apache.thrift.protocol.TField CTX_FIELD_DESC = new org.apache.thrift.protocol.TField("ctx", org.apache.thrift.protocol.TType.STRUCT, (short)10);
+    private static final org.apache.thrift.protocol.TField STATE_FIELD_DESC = new org.apache.thrift.protocol.TField("state", org.apache.thrift.protocol.TType.I32, (short)1);
+
+    public org.apache.hadoop.thriftfs.api.RequestContext ctx; // required
+    /**
+     * 
+     * @see ThriftJobState
+     */
+    public ThriftJobState state; // required
+
+    /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
+    public enum _Fields implements org.apache.thrift.TFieldIdEnum {
+      CTX((short)10, "ctx"),
+      /**
+       * 
+       * @see ThriftJobState
+       */
+      STATE((short)1, "state");
+
+      private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
+
+      static {
+        for (_Fields field : EnumSet.allOf(_Fields.class)) {
+          byName.put(field.getFieldName(), field);
+        }
+      }
+
+      /**
+       * Find the _Fields constant that matches fieldId, or null if its not found.
+       */
+      public static _Fields findByThriftId(int fieldId) {
+        switch(fieldId) {
+          case 10: // CTX
+            return CTX;
+          case 1: // STATE
+            return STATE;
+          default:
+            return null;
+        }
+      }
+
+      /**
+       * Find the _Fields constant that matches fieldId, throwing an exception
+       * if it is not found.
+       */
+      public static _Fields findByThriftIdOrThrow(int fieldId) {
+        _Fields fields = findByThriftId(fieldId);
+        if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
+        return fields;
+      }
+
+      /**
+       * Find the _Fields constant that matches name, or null if its not found.
+       */
+      public static _Fields findByName(String name) {
+        return byName.get(name);
+      }
+
+      private final short _thriftId;
+      private final String _fieldName;
+
+      _Fields(short thriftId, String fieldName) {
+        _thriftId = thriftId;
+        _fieldName = fieldName;
+      }
+
+      public short getThriftFieldId() {
+        return _thriftId;
+      }
+
+      public String getFieldName() {
+        return _fieldName;
+      }
+    }
+
+    // isset id assignments
+
+    public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;
+    static {
+      Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);
+      tmpMap.put(_Fields.CTX, new org.apache.thrift.meta_data.FieldMetaData("ctx", org.apache.thrift.TFieldRequirementType.DEFAULT, 
+          new org.apache.thrift.meta_data.StructMetaData(org.apache.thrift.protocol.TType.STRUCT, org.apache.hadoop.thriftfs.api.RequestContext.class)));
+      tmpMap.put(_Fields.STATE, new org.apache.thrift.meta_data.FieldMetaData("state", org.apache.thrift.TFieldRequirementType.DEFAULT, 
+          new org.apache.thrift.meta_data.EnumMetaData(org.apache.thrift.protocol.TType.ENUM, ThriftJobState.class)));
+      metaDataMap = Collections.unmodifiableMap(tmpMap);
+      org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(getRetiredJobs_args.class, metaDataMap);
+    }
+
+    public getRetiredJobs_args() {
+    }
+
+    public getRetiredJobs_args(
+      org.apache.hadoop.thriftfs.api.RequestContext ctx,
+      ThriftJobState state)
+    {
+      this();
+      this.ctx = ctx;
+      this.state = state;
+    }
+
+    /**
+     * Performs a deep copy on <i>other</i>.
+     */
+    public getRetiredJobs_args(getRetiredJobs_args other) {
+      if (other.isSetCtx()) {
+        this.ctx = new org.apache.hadoop.thriftfs.api.RequestContext(other.ctx);
+      }
+      if (other.isSetState()) {
+        this.state = other.state;
+      }
+    }
+
+    public getRetiredJobs_args deepCopy() {
+      return new getRetiredJobs_args(this);
+    }
+
+    @Override
+    public void clear() {
+      this.ctx = null;
+      this.state = null;
+    }
+
+    public org.apache.hadoop.thriftfs.api.RequestContext getCtx() {
+      return this.ctx;
+    }
+
+    public getRetiredJobs_args setCtx(org.apache.hadoop.thriftfs.api.RequestContext ctx) {
+      this.ctx = ctx;
+      return this;
+    }
+
+    public void unsetCtx() {
+      this.ctx = null;
+    }
+
+    /** Returns true if field ctx is set (has been assigned a value) and false otherwise */
+    public boolean isSetCtx() {
+      return this.ctx != null;
+    }
+
+    public void setCtxIsSet(boolean value) {
+      if (!value) {
+        this.ctx = null;
+      }
+    }
+
+    /**
+     * 
+     * @see ThriftJobState
+     */
+    public ThriftJobState getState() {
+      return this.state;
+    }
+
+    /**
+     * 
+     * @see ThriftJobState
+     */
+    public getRetiredJobs_args setState(ThriftJobState state) {
+      this.state = state;
+      return this;
+    }
+
+    public void unsetState() {
+      this.state = null;
+    }
+
+    /** Returns true if field state is set (has been assigned a value) and false otherwise */
+    public boolean isSetState() {
+      return this.state != null;
+    }
+
+    public void setStateIsSet(boolean value) {
+      if (!value) {
+        this.state = null;
+      }
+    }
+
+    public void setFieldValue(_Fields field, Object value) {
+      switch (field) {
+      case CTX:
+        if (value == null) {
+          unsetCtx();
+        } else {
+          setCtx((org.apache.hadoop.thriftfs.api.RequestContext)value);
+        }
+        break;
+
+      case STATE:
+        if (value == null) {
+          unsetState();
+        } else {
+          setState((ThriftJobState)value);
+        }
+        break;
+
+      }
+    }
+
+    public Object getFieldValue(_Fields field) {
+      switch (field) {
+      case CTX:
+        return getCtx();
+
+      case STATE:
+        return getState();
+
+      }
+      throw new IllegalStateException();
+    }
+
+    /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */
+    public boolean isSet(_Fields field) {
+      if (field == null) {
+        throw new IllegalArgumentException();
+      }
+
+      switch (field) {
+      case CTX:
+        return isSetCtx();
+      case STATE:
+        return isSetState();
+      }
+      throw new IllegalStateException();
+    }
+
+    @Override
+    public boolean equals(Object that) {
+      if (that == null)
+        return false;
+      if (that instanceof getRetiredJobs_args)
+        return this.equals((getRetiredJobs_args)that);
+      return false;
+    }
+
+    public boolean equals(getRetiredJobs_args that) {
+      if (that == null)
+        return false;
+
+      boolean this_present_ctx = true && this.isSetCtx();
+      boolean that_present_ctx = true && that.isSetCtx();
+      if (this_present_ctx || that_present_ctx) {
+        if (!(this_present_ctx && that_present_ctx))
+          return false;
+        if (!this.ctx.equals(that.ctx))
+          return false;
+      }
+
+      boolean this_present_state = true && this.isSetState();
+      boolean that_present_state = true && that.isSetState();
+      if (this_present_state || that_present_state) {
+        if (!(this_present_state && that_present_state))
+          return false;
+        if (!this.state.equals(that.state))
+          return false;
+      }
+
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      return 0;
+    }
+
+    public int compareTo(getRetiredJobs_args other) {
+      if (!getClass().equals(other.getClass())) {
+        return getClass().getName().compareTo(other.getClass().getName());
+      }
+
+      int lastComparison = 0;
+      getRetiredJobs_args typedOther = (getRetiredJobs_args)other;
+
+      lastComparison = Boolean.valueOf(isSetCtx()).compareTo(typedOther.isSetCtx());
+      if (lastComparison != 0) {
+        return lastComparison;
+      }
+      if (isSetCtx()) {
+        lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.ctx, typedOther.ctx);
+        if (lastComparison != 0) {
+          return lastComparison;
+        }
+      }
+      lastComparison = Boolean.valueOf(isSetState()).compareTo(typedOther.isSetState());
+      if (lastComparison != 0) {
+        return lastComparison;
+      }
+      if (isSetState()) {
+        lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.state, typedOther.state);
+        if (lastComparison != 0) {
+          return lastComparison;
+        }
+      }
+      return 0;
+    }
+
+    public _Fields fieldForId(int fieldId) {
+      return _Fields.findByThriftId(fieldId);
+    }
+
+    public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException {
+      org.apache.thrift.protocol.TField field;
+      iprot.readStructBegin();
+      while (true)
+      {
+        field = iprot.readFieldBegin();
+        if (field.type == org.apache.thrift.protocol.TType.STOP) { 
+          break;
+        }
+        switch (field.id) {
+          case 10: // CTX
+            if (field.type == org.apache.thrift.protocol.TType.STRUCT) {
+              this.ctx = new org.apache.hadoop.thriftfs.api.RequestContext();
+              this.ctx.read(iprot);
+            } else { 
+              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, field.type);
+            }
+            break;
+          case 1: // STATE
+            if (field.type == org.apache.thrift.protocol.TType.I32) {
+              this.state = ThriftJobState.findByValue(iprot.readI32());
+            } else { 
+              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, field.type);
+            }
+            break;
+          default:
+            org.apache.thrift.protocol.TProtocolUtil.skip(iprot, field.type);
+        }
+        iprot.readFieldEnd();
+      }
+      iprot.readStructEnd();
+
+      // check for required fields of primitive type, which can't be checked in the validate method
+      validate();
+    }
+
+    public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
+      validate();
+
+      oprot.writeStructBegin(STRUCT_DESC);
+      if (this.state != null) {
+        oprot.writeFieldBegin(STATE_FIELD_DESC);
+        oprot.writeI32(this.state.getValue());
+        oprot.writeFieldEnd();
+      }
+      if (this.ctx != null) {
+        oprot.writeFieldBegin(CTX_FIELD_DESC);
+        this.ctx.write(oprot);
+        oprot.writeFieldEnd();
+      }
+      oprot.writeFieldStop();
+      oprot.writeStructEnd();
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder sb = new StringBuilder("getRetiredJobs_args(");
+      boolean first = true;
+
+      sb.append("ctx:");
+      if (this.ctx == null) {
+        sb.append("null");
+      } else {
+        sb.append(this.ctx);
+      }
+      first = false;
+      if (!first) sb.append(", ");
+      sb.append("state:");
+      if (this.state == null) {
+        sb.append("null");
+      } else {
+        sb.append(this.state);
+      }
+      first = false;
+      sb.append(")");
+      return sb.toString();
+    }
+
+    public void validate() throws org.apache.thrift.TException {
+      // check for required fields
+    }
+
+    private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
+      try {
+        write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out)));
+      } catch (org.apache.thrift.TException te) {
+        throw new java.io.IOException(te);
+      }
+    }
+
+    private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
+      try {
+        read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in)));
+      } catch (org.apache.thrift.TException te) {
+        throw new java.io.IOException(te);
+      }
+    }
+
+  }
+
+  public static class getRetiredJobs_result implements org.apache.thrift.TBase<getRetiredJobs_result, getRetiredJobs_result._Fields>, java.io.Serializable, Cloneable   {
+    private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("getRetiredJobs_result");
+
+    private static final org.apache.thrift.protocol.TField SUCCESS_FIELD_DESC = new org.apache.thrift.protocol.TField("success", org.apache.thrift.protocol.TType.STRUCT, (short)0);
+
+    public ThriftJobList success; // required
+
+    /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
+    public enum _Fields implements org.apache.thrift.TFieldIdEnum {
+      SUCCESS((short)0, "success");
+
+      private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
+
+      static {
+        for (_Fields field : EnumSet.allOf(_Fields.class)) {
+          byName.put(field.getFieldName(), field);
+        }
+      }
+
+      /**
+       * Find the _Fields constant that matches fieldId, or null if its not found.
+       */
+      public static _Fields findByThriftId(int fieldId) {
+        switch(fieldId) {
+          case 0: // SUCCESS
+            return SUCCESS;
+          default:
+            return null;
+        }
+      }
+
+      /**
+       * Find the _Fields constant that matches fieldId, throwing an exception
+       * if it is not found.
+       */
+      public static _Fields findByThriftIdOrThrow(int fieldId) {
+        _Fields fields = findByThriftId(fieldId);
+        if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
+        return fields;
+      }
+
+      /**
+       * Find the _Fields constant that matches name, or null if its not found.
+       */
+      public static _Fields findByName(String name) {
+        return byName.get(name);
+      }
+
+      private final short _thriftId;
+      private final String _fieldName;
+
+      _Fields(short thriftId, String fieldName) {
+        _thriftId = thriftId;
+        _fieldName = fieldName;
+      }
+
+      public short getThriftFieldId() {
+        return _thriftId;
+      }
+
+      public String getFieldName() {
+        return _fieldName;
+      }
+    }
+
+    // isset id assignments
+
+    public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;
+    static {
+      Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);
+      tmpMap.put(_Fields.SUCCESS, new org.apache.thrift.meta_data.FieldMetaData("success", org.apache.thrift.TFieldRequirementType.DEFAULT, 
+          new org.apache.thrift.meta_data.StructMetaData(org.apache.thrift.protocol.TType.STRUCT, ThriftJobList.class)));
+      metaDataMap = Collections.unmodifiableMap(tmpMap);
+      org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(getRetiredJobs_result.class, metaDataMap);
+    }
+
+    public getRetiredJobs_result() {
+    }
+
+    public getRetiredJobs_result(
+      ThriftJobList success)
+    {
+      this();
+      this.success = success;
+    }
+
+    /**
+     * Performs a deep copy on <i>other</i>.
+     */
+    public getRetiredJobs_result(getRetiredJobs_result other) {
+      if (other.isSetSuccess()) {
+        this.success = new ThriftJobList(other.success);
+      }
+    }
+
+    public getRetiredJobs_result deepCopy() {
+      return new getRetiredJobs_result(this);
+    }
+
+    @Override
+    public void clear() {
+      this.success = null;
+    }
+
+    public ThriftJobList getSuccess() {
+      return this.success;
+    }
+
+    public getRetiredJobs_result setSuccess(ThriftJobList success) {
+      this.success = success;
+      return this;
+    }
+
+    public void unsetSuccess() {
+      this.success = null;
+    }
+
+    /** Returns true if field success is set (has been assigned a value) and false otherwise */
+    public boolean isSetSuccess() {
+      return this.success != null;
+    }
+
+    public void setSuccessIsSet(boolean value) {
+      if (!value) {
+        this.success = null;
+      }
+    }
+
+    public void setFieldValue(_Fields field, Object value) {
+      switch (field) {
+      case SUCCESS:
+        if (value == null) {
+          unsetSuccess();
+        } else {
+          setSuccess((ThriftJobList)value);
+        }
+        break;
+
+      }
+    }
+
+    public Object getFieldValue(_Fields field) {
+      switch (field) {
+      case SUCCESS:
+        return getSuccess();
+
+      }
+      throw new IllegalStateException();
+    }
+
+    /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */
+    public boolean isSet(_Fields field) {
+      if (field == null) {
+        throw new IllegalArgumentException();
+      }
+
+      switch (field) {
+      case SUCCESS:
+        return isSetSuccess();
+      }
+      throw new IllegalStateException();
+    }
+
+    @Override
+    public boolean equals(Object that) {
+      if (that == null)
+        return false;
+      if (that instanceof getRetiredJobs_result)
+        return this.equals((getRetiredJobs_result)that);
+      return false;
+    }
+
+    public boolean equals(getRetiredJobs_result that) {
+      if (that == null)
+        return false;
+
+      boolean this_present_success = true && this.isSetSuccess();
+      boolean that_present_success = true && that.isSetSuccess();
+      if (this_present_success || that_present_success) {
+        if (!(this_present_success && that_present_success))
+          return false;
+        if (!this.success.equals(that.success))
+          return false;
+      }
+
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      return 0;
+    }
+
+    public int compareTo(getRetiredJobs_result other) {
+      if (!getClass().equals(other.getClass())) {
+        return getClass().getName().compareTo(other.getClass().getName());
+      }
+
+      int lastComparison = 0;
+      getRetiredJobs_result typedOther = (getRetiredJobs_result)other;
+
+      lastComparison = Boolean.valueOf(isSetSuccess()).compareTo(typedOther.isSetSuccess());
+      if (lastComparison != 0) {
+        return lastComparison;
+      }
+      if (isSetSuccess()) {
+        lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.success, typedOther.success);
+        if (lastComparison != 0) {
+          return lastComparison;
+        }
+      }
+      return 0;
+    }
+
+    public _Fields fieldForId(int fieldId) {
+      return _Fields.findByThriftId(fieldId);
+    }
+
+    public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException {
+      org.apache.thrift.protocol.TField field;
+      iprot.readStructBegin();
+      while (true)
+      {
+        field = iprot.readFieldBegin();
+        if (field.type == org.apache.thrift.protocol.TType.STOP) { 
+          break;
+        }
+        switch (field.id) {
+          case 0: // SUCCESS
+            if (field.type == org.apache.thrift.protocol.TType.STRUCT) {
+              this.success = new ThriftJobList();
+              this.success.read(iprot);
+            } else { 
+              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, field.type);
+            }
+            break;
+          default:
+            org.apache.thrift.protocol.TProtocolUtil.skip(iprot, field.type);
+        }
+        iprot.readFieldEnd();
+      }
+      iprot.readStructEnd();
+
+      // check for required fields of primitive type, which can't be checked in the validate method
+      validate();
+    }
+
+    public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
+      oprot.writeStructBegin(STRUCT_DESC);
+
+      if (this.isSetSuccess()) {
+        oprot.writeFieldBegin(SUCCESS_FIELD_DESC);
+        this.success.write(oprot);
+        oprot.writeFieldEnd();
+      }
+      oprot.writeFieldStop();
+      oprot.writeStructEnd();
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder sb = new StringBuilder("getRetiredJobs_result(");
+      boolean first = true;
+
+      sb.append("success:");
+      if (this.success == null) {
+        sb.append("null");
+      } else {
+        sb.append(this.success);
+      }
+      first = false;
+      sb.append(")");
+      return sb.toString();
+    }
+
+    public void validate() throws org.apache.thrift.TException {
+      // check for required fields
+    }
+
+    private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
+      try {
+        write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out)));
+      } catch (org.apache.thrift.TException te) {
+        throw new java.io.IOException(te);
+      }
+    }
+
+    private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
+      try {
+        read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in)));
+      } catch (org.apache.thrift.TException te) {
+        throw new java.io.IOException(te);
+      }
+    }
+
+  }
+
   public static class getFailedJobs_args implements org.apache.thrift.TBase<getFailedJobs_args, getFailedJobs_args._Fields>, java.io.Serializable, Cloneable   {
     private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("getFailedJobs_args");
 

+ 360 - 0
desktop/libs/hadoop/java/src/main/gen-java/org/apache/hadoop/thriftfs/jobtracker/api/ThriftJobStatusList.java

@@ -0,0 +1,360 @@
+/**
+ * Autogenerated by Thrift Compiler (0.7.0)
+ *
+ * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+ */
+package org.apache.hadoop.thriftfs.jobtracker.api;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.EnumMap;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.EnumSet;
+import java.util.Collections;
+import java.util.BitSet;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Container structure of a list of job statuses
+ */
+public class ThriftJobStatusList implements org.apache.thrift.TBase<ThriftJobStatusList, ThriftJobStatusList._Fields>, java.io.Serializable, Cloneable {
+  private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("ThriftJobStatusList");
+
+  private static final org.apache.thrift.protocol.TField JOBS_FIELD_DESC = new org.apache.thrift.protocol.TField("jobs", org.apache.thrift.protocol.TType.LIST, (short)1);
+
+  public List<ThriftJobStatus> jobs; // required
+
+  /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
+  public enum _Fields implements org.apache.thrift.TFieldIdEnum {
+    JOBS((short)1, "jobs");
+
+    private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
+
+    static {
+      for (_Fields field : EnumSet.allOf(_Fields.class)) {
+        byName.put(field.getFieldName(), field);
+      }
+    }
+
+    /**
+     * Find the _Fields constant that matches fieldId, or null if its not found.
+     */
+    public static _Fields findByThriftId(int fieldId) {
+      switch(fieldId) {
+        case 1: // JOBS
+          return JOBS;
+        default:
+          return null;
+      }
+    }
+
+    /**
+     * Find the _Fields constant that matches fieldId, throwing an exception
+     * if it is not found.
+     */
+    public static _Fields findByThriftIdOrThrow(int fieldId) {
+      _Fields fields = findByThriftId(fieldId);
+      if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
+      return fields;
+    }
+
+    /**
+     * Find the _Fields constant that matches name, or null if its not found.
+     */
+    public static _Fields findByName(String name) {
+      return byName.get(name);
+    }
+
+    private final short _thriftId;
+    private final String _fieldName;
+
+    _Fields(short thriftId, String fieldName) {
+      _thriftId = thriftId;
+      _fieldName = fieldName;
+    }
+
+    public short getThriftFieldId() {
+      return _thriftId;
+    }
+
+    public String getFieldName() {
+      return _fieldName;
+    }
+  }
+
+  // isset id assignments
+
+  public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;
+  static {
+    Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);
+    tmpMap.put(_Fields.JOBS, new org.apache.thrift.meta_data.FieldMetaData("jobs", org.apache.thrift.TFieldRequirementType.DEFAULT, 
+        new org.apache.thrift.meta_data.ListMetaData(org.apache.thrift.protocol.TType.LIST, 
+            new org.apache.thrift.meta_data.StructMetaData(org.apache.thrift.protocol.TType.STRUCT, ThriftJobStatus.class))));
+    metaDataMap = Collections.unmodifiableMap(tmpMap);
+    org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(ThriftJobStatusList.class, metaDataMap);
+  }
+
+  public ThriftJobStatusList() {
+  }
+
+  public ThriftJobStatusList(
+    List<ThriftJobStatus> jobs)
+  {
+    this();
+    this.jobs = jobs;
+  }
+
+  /**
+   * Performs a deep copy on <i>other</i>.
+   */
+  public ThriftJobStatusList(ThriftJobStatusList other) {
+    if (other.isSetJobs()) {
+      List<ThriftJobStatus> __this__jobs = new ArrayList<ThriftJobStatus>();
+      for (ThriftJobStatus other_element : other.jobs) {
+        __this__jobs.add(new ThriftJobStatus(other_element));
+      }
+      this.jobs = __this__jobs;
+    }
+  }
+
+  public ThriftJobStatusList deepCopy() {
+    return new ThriftJobStatusList(this);
+  }
+
+  @Override
+  public void clear() {
+    this.jobs = null;
+  }
+
+  public int getJobsSize() {
+    return (this.jobs == null) ? 0 : this.jobs.size();
+  }
+
+  public java.util.Iterator<ThriftJobStatus> getJobsIterator() {
+    return (this.jobs == null) ? null : this.jobs.iterator();
+  }
+
+  public void addToJobs(ThriftJobStatus elem) {
+    if (this.jobs == null) {
+      this.jobs = new ArrayList<ThriftJobStatus>();
+    }
+    this.jobs.add(elem);
+  }
+
+  public List<ThriftJobStatus> getJobs() {
+    return this.jobs;
+  }
+
+  public ThriftJobStatusList setJobs(List<ThriftJobStatus> jobs) {
+    this.jobs = jobs;
+    return this;
+  }
+
+  public void unsetJobs() {
+    this.jobs = null;
+  }
+
+  /** Returns true if field jobs is set (has been assigned a value) and false otherwise */
+  public boolean isSetJobs() {
+    return this.jobs != null;
+  }
+
+  public void setJobsIsSet(boolean value) {
+    if (!value) {
+      this.jobs = null;
+    }
+  }
+
+  public void setFieldValue(_Fields field, Object value) {
+    switch (field) {
+    case JOBS:
+      if (value == null) {
+        unsetJobs();
+      } else {
+        setJobs((List<ThriftJobStatus>)value);
+      }
+      break;
+
+    }
+  }
+
+  public Object getFieldValue(_Fields field) {
+    switch (field) {
+    case JOBS:
+      return getJobs();
+
+    }
+    throw new IllegalStateException();
+  }
+
+  /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */
+  public boolean isSet(_Fields field) {
+    if (field == null) {
+      throw new IllegalArgumentException();
+    }
+
+    switch (field) {
+    case JOBS:
+      return isSetJobs();
+    }
+    throw new IllegalStateException();
+  }
+
+  @Override
+  public boolean equals(Object that) {
+    if (that == null)
+      return false;
+    if (that instanceof ThriftJobStatusList)
+      return this.equals((ThriftJobStatusList)that);
+    return false;
+  }
+
+  public boolean equals(ThriftJobStatusList that) {
+    if (that == null)
+      return false;
+
+    boolean this_present_jobs = true && this.isSetJobs();
+    boolean that_present_jobs = true && that.isSetJobs();
+    if (this_present_jobs || that_present_jobs) {
+      if (!(this_present_jobs && that_present_jobs))
+        return false;
+      if (!this.jobs.equals(that.jobs))
+        return false;
+    }
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    return 0;
+  }
+
+  public int compareTo(ThriftJobStatusList other) {
+    if (!getClass().equals(other.getClass())) {
+      return getClass().getName().compareTo(other.getClass().getName());
+    }
+
+    int lastComparison = 0;
+    ThriftJobStatusList typedOther = (ThriftJobStatusList)other;
+
+    lastComparison = Boolean.valueOf(isSetJobs()).compareTo(typedOther.isSetJobs());
+    if (lastComparison != 0) {
+      return lastComparison;
+    }
+    if (isSetJobs()) {
+      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.jobs, typedOther.jobs);
+      if (lastComparison != 0) {
+        return lastComparison;
+      }
+    }
+    return 0;
+  }
+
+  public _Fields fieldForId(int fieldId) {
+    return _Fields.findByThriftId(fieldId);
+  }
+
+  public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException {
+    org.apache.thrift.protocol.TField field;
+    iprot.readStructBegin();
+    while (true)
+    {
+      field = iprot.readFieldBegin();
+      if (field.type == org.apache.thrift.protocol.TType.STOP) { 
+        break;
+      }
+      switch (field.id) {
+        case 1: // JOBS
+          if (field.type == org.apache.thrift.protocol.TType.LIST) {
+            {
+              org.apache.thrift.protocol.TList _list51 = iprot.readListBegin();
+              this.jobs = new ArrayList<ThriftJobStatus>(_list51.size);
+              for (int _i52 = 0; _i52 < _list51.size; ++_i52)
+              {
+                ThriftJobStatus _elem53; // required
+                _elem53 = new ThriftJobStatus();
+                _elem53.read(iprot);
+                this.jobs.add(_elem53);
+              }
+              iprot.readListEnd();
+            }
+          } else { 
+            org.apache.thrift.protocol.TProtocolUtil.skip(iprot, field.type);
+          }
+          break;
+        default:
+          org.apache.thrift.protocol.TProtocolUtil.skip(iprot, field.type);
+      }
+      iprot.readFieldEnd();
+    }
+    iprot.readStructEnd();
+
+    // check for required fields of primitive type, which can't be checked in the validate method
+    validate();
+  }
+
+  public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
+    validate();
+
+    oprot.writeStructBegin(STRUCT_DESC);
+    if (this.jobs != null) {
+      oprot.writeFieldBegin(JOBS_FIELD_DESC);
+      {
+        oprot.writeListBegin(new org.apache.thrift.protocol.TList(org.apache.thrift.protocol.TType.STRUCT, this.jobs.size()));
+        for (ThriftJobStatus _iter54 : this.jobs)
+        {
+          _iter54.write(oprot);
+        }
+        oprot.writeListEnd();
+      }
+      oprot.writeFieldEnd();
+    }
+    oprot.writeFieldStop();
+    oprot.writeStructEnd();
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder("ThriftJobStatusList(");
+    boolean first = true;
+
+    sb.append("jobs:");
+    if (this.jobs == null) {
+      sb.append("null");
+    } else {
+      sb.append(this.jobs);
+    }
+    first = false;
+    sb.append(")");
+    return sb.toString();
+  }
+
+  public void validate() throws org.apache.thrift.TException {
+    // check for required fields
+  }
+
+  private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
+    try {
+      write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out)));
+    } catch (org.apache.thrift.TException te) {
+      throw new java.io.IOException(te);
+    }
+  }
+
+  private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
+    try {
+      read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in)));
+    } catch (org.apache.thrift.TException te) {
+      throw new java.io.IOException(te);
+    }
+  }
+
+}
+

+ 56 - 0
desktop/libs/hadoop/java/src/main/java/org/apache/hadoop/mapred/ThriftJobTrackerPlugin.java

@@ -31,6 +31,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -69,6 +70,7 @@ import org.apache.hadoop.thriftfs.jobtracker.api.ThriftJobCounterRollups;
 import org.apache.hadoop.thriftfs.jobtracker.api.ThriftJobID;
 import org.apache.hadoop.thriftfs.jobtracker.api.ThriftJobInProgress;
 import org.apache.hadoop.thriftfs.jobtracker.api.ThriftJobList;
+import org.apache.hadoop.thriftfs.jobtracker.api.ThriftJobStatusList;
 import org.apache.hadoop.thriftfs.jobtracker.api.ThriftJobPriority;
 import org.apache.hadoop.thriftfs.jobtracker.api.ThriftJobProfile;
 import org.apache.hadoop.thriftfs.jobtracker.api.ThriftJobQueueInfo;
@@ -256,6 +258,25 @@ public class ThriftJobTrackerPlugin extends JobTrackerPlugin implements Configur
             return ret;
         }
 
+        /**
+         * Gets as much information about a retired Job and converts it to its corresponding
+         * Thrift representation.
+         * @param jobProfile The profile of a job.
+         * @param jobStatus The status of a job.
+         */
+        public static ThriftJobInProgress toThrift(JobProfile jobProfile, JobStatus jobStatus) {
+            ThriftJobInProgress ret = new ThriftJobInProgress();
+
+            ret.setJobID(toThrift(jobProfile.getJobID()));
+            ret.setPriority(toThrift(jobStatus.getJobPriority()));
+            ret.setProfile(toThrift(jobProfile));
+
+            ret.setStatus(toThrift(jobStatus));
+
+            ret.setStartTime(jobStatus.getStartTime());
+
+            return ret;
+        }
 
         /**
          * There are always two setup tasks and two cleanup tasks by default
@@ -790,6 +811,41 @@ public class ThriftJobTrackerPlugin extends JobTrackerPlugin implements Configur
             });
         }
 
+        /** Returns all retired jobs (does not include task info, miss some fields) */
+        public ThriftJobList getRetiredJobs(RequestContext ctx, final ThriftJobState state) {
+            return assumeUserContextAndExecute(ctx, new PrivilegedAction<ThriftJobList>() {
+              public ThriftJobList run() {
+                JobStatus[] jobStatuses = null;
+                Set<JobID> jobsInProgressId = new HashSet<JobID>();
+
+                synchronized(jobTracker) {
+                    jobStatuses = jobTracker.getAllJobs();
+                    for (JobInProgress job : jobTracker.getRunningJobs()) {
+                        jobsInProgressId.add(job.getJobID());
+                    }
+                    for (JobInProgress job : jobTracker.failedJobs()) {
+                        jobsInProgressId.add(job.getJobID());
+                    }
+                    for (JobInProgress job : jobTracker.completedJobs()) {
+                        jobsInProgressId.add(job.getJobID());
+                    }
+                }
+
+                ArrayList<ThriftJobInProgress> ret = new ArrayList<ThriftJobInProgress>();
+
+                for (JobStatus jobStatus : jobStatuses) {
+                    JobID jobID = jobStatus.getJobID();
+                    if (!jobsInProgressId.contains(jobID) &&
+                        (state == null || state == JTThriftUtils.jobRunStateToThrift(jobStatus.getRunState()))) {
+                        // No need to lock
+                        ret.add(JTThriftUtils.toThrift(jobTracker.getJobProfile(jobID), jobStatus));
+                    }
+                }
+                return new ThriftJobList(ret);
+              }
+            });
+        }
+
         /** Returns all failed jobs (does not include task info) */
         public ThriftJobList getFailedJobs(RequestContext ctx) {
             return assumeUserContextAndExecute(ctx, new PrivilegedAction<ThriftJobList>() {

+ 13 - 0
desktop/libs/hadoop/src/hadoop/job_tracker.py

@@ -165,6 +165,9 @@ class LiveJobTracker(object):
     for taskstatus in tip.taskStatuses.values():
       self._fixup_taskstatus(taskstatus)
 
+  def _fixup_retired_job(self, job):
+    job.is_retired = True
+
   def setuser(self, user):
     # Hadoop determines the groups the user belongs to on the server side.
     self.thread_local.request_context = RequestContext()
@@ -271,6 +274,16 @@ class LiveJobTracker(object):
       self._fixup_job(job)
     return joblist
 
+  def retired_jobs(self, status=None):
+    """
+    Returns a ThriftJobStatusList (does not include task info)
+    """
+    joblist = self.client.getRetiredJobs(self.thread_local.request_context, status)
+    for job in joblist.jobs:
+      self._fixup_job(job)
+      self._fixup_retired_job(job)
+    return joblist
+
   def failed_jobs(self):
     """
     Returns a ThriftJobList (does not include task info)