Răsfoiți Sursa

HUE-612 [jb] Get and display a retired job

Fix bug showing PREP job as retired
Add a get_retired_job() to the plugin API
Hue get_job() will try get_retired_job() if no regular jobs are found
Jobs/Job pages are customized a bit depending on job retirement status
Romain Rigaux 13 ani în urmă
părinte
comite
39a97e4

+ 10 - 3
apps/jobbrowser/src/jobbrowser/models.py

@@ -30,8 +30,10 @@ import urllib2
 from django.utils.translation import ugettext as _
 
 import hadoop.api.jobtracker.ttypes as ttypes
+from hadoop.api.jobtracker.ttypes import JobNotFoundException
 
 from django.utils.translation import ugettext as _
+from desktop.lib.django_util import PopupException
 
 LOGGER = logging.getLogger(__name__)
 
@@ -76,9 +78,14 @@ class Job(JobLinkage):
       Returns a Job instance given a job tracker interface and an id. The job tracker interface is typically
       located in request.jt.
     """
-    thriftjob = jt.get_job(jt.thriftjobid_from_string(jobid))
-    if not thriftjob:
-      raise Exception(_("could not find job with id %(jobid)s") % {'jobid': jobid})
+    try:
+      thriftjob = jt.get_job(jt.thriftjobid_from_string(jobid))
+    except JobNotFoundException:
+      try:
+        thriftjob = jt.get_retired_job(jt.thriftjobid_from_string(jobid))
+      except JobNotFoundException, e:
+        raise PopupException(_("Could not find job with id %(jobid)s") % {'jobid': jobid}, detail=e)
+
     return Job(jt, thriftjob)
 
   @staticmethod

+ 19 - 4
apps/jobbrowser/src/jobbrowser/templates/job.mako

@@ -99,6 +99,10 @@ ${commonheader(_('Job: %(jobId)s - Job Browser') % dict(jobId=job.jobId), "jobbr
                             % else:
                                 <span class="label">${job.status.lower()}</span>
                             % endif
+
+                            % if job.is_retired:
+                                <span class="label label-warning">${ _('retired') }</span>
+                            % endif
                     </li>
                     % if job.status.lower() == 'running' or job.status.lower() == 'pending':
                             <li class="nav-header">${_('Kill Job')}</li>
@@ -131,8 +135,15 @@ ${commonheader(_('Job: %(jobId)s - Job Browser') % dict(jobId=job.jobId), "jobbr
 
             <div class="tab-content">
                 <div class="tab-pane active" id="tasks">
-                    <strong>${_('Maps:')}</strong> ${comps.mr_graph_maps(job)}
-                    <strong>${_('Reduces:')}</strong> ${comps.mr_graph_reduces(job)}
+                    % if not job.is_retired:
+	                    <strong>${_('Maps:')}</strong> ${comps.mr_graph_maps(job)}
+	                    <strong>${_('Reduces:')}</strong> ${comps.mr_graph_reduces(job)}
+	                % else:
+	                   ${ _('This jobs is ')} <span class="label label-warning">${ _('retired') }</span> ${ _(' and so has little information available.') }
+                       <br/>
+                       <br/>
+                    % endif
+
                     %if failed_tasks:
                             <div>
                                 <h3>
@@ -163,8 +174,8 @@ ${commonheader(_('Job: %(jobId)s - Job Browser') % dict(jobId=job.jobId), "jobbr
                     </div>
                     <table id="metadataTable" class="table table-striped table-condensed">
                         <thead>
-                        <th>${_('Name')}</th>
-                        <th>${_('Value')}</th>
+	                        <th>${_('Name')}</th>
+	                        <th>${_('Value')}</th>
                         </thead>
                         <tbody>
                         <tr>
@@ -175,6 +186,7 @@ ${commonheader(_('Job: %(jobId)s - Job Browser') % dict(jobId=job.jobId), "jobbr
                             <td>${_('User')}</td>
                             <td>${job.user}</td>
                         </tr>
+                        % if not job.is_retired:
                         <tr>
                             <td>${_('Maps')}</td>
                             <td>${job.finishedMaps} of ${job.desiredMaps}</td>
@@ -183,10 +195,12 @@ ${commonheader(_('Job: %(jobId)s - Job Browser') % dict(jobId=job.jobId), "jobbr
                             <td>${_('Reduces')}</td>
                             <td>${job.finishedReduces} of ${job.desiredReduces}</td>
                         </tr>
+                        % endif
                         <tr>
                             <td>${_('Started')}</td>
                             <td>${job.startTimeFormatted}</td>
                         </tr>
+                        % if not job.is_retired:
                         <tr>
                             <td>${_('Ended')}</td>
                             <td>${job.finishTimeFormatted}</td>
@@ -195,6 +209,7 @@ ${commonheader(_('Job: %(jobId)s - Job Browser') % dict(jobId=job.jobId), "jobbr
                             <td>${_('Duration')}</td>
                             <td>${job.duration}</td>
                         </tr>
+                        % endif
                         <tr>
                             <td>${_('Status')}</td>
                             <td>${job.status}</td>

+ 11 - 4
apps/jobbrowser/src/jobbrowser/templates/jobbrowser_components.mako

@@ -62,10 +62,17 @@
                 job_count = counter.get('job', 0)
             %>
             <tr>
-                <td>${format_counter_name(counter.get('displayName', 'n/a'))}</td>
-                <td>${map_count}</td>
-                <td>${reduce_count}</td>
-                <td>${map_count + reduce_count + job_count}</td>
+                % if not job.is_retired:
+	                <td>${format_counter_name(counter.get('displayName', 'n/a'))}</td>
+	                <td>${map_count}</td>
+	                <td>${reduce_count}</td>
+	                <td>${map_count + reduce_count + job_count}</td>
+                % else:
+	                <td>N/A</td>
+	                <td>N/A</td>
+	                <td>N/A</td>
+	                <td>N/A</td>
+                % endif
             </tr>
             % endfor
         </tbody>

+ 0 - 2
apps/jobbrowser/src/jobbrowser/templates/jobs.mako

@@ -120,9 +120,7 @@ ${commonheader(_('Job Browser'), "jobbrowser")}
             </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>

+ 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 '  ThriftJobInProgress getRetiredJob(RequestContext ctx, ThriftJobID jobID)'
   print '  ThriftJobList getRetiredJobs(RequestContext ctx, ThriftJobState state)'
   print '  ThriftJobList getFailedJobs(RequestContext ctx)'
   print '  ThriftJobList getKilledJobs(RequestContext ctx)'
@@ -132,6 +133,12 @@ elif cmd == 'getCompletedJobs':
     sys.exit(1)
   pp.pprint(client.getCompletedJobs(eval(args[0]),))
 
+elif cmd == 'getRetiredJob':
+  if len(args) != 2:
+    print 'getRetiredJob requires 2 args'
+    sys.exit(1)
+  pp.pprint(client.getRetiredJob(eval(args[0]),eval(args[1]),))
+
 elif cmd == 'getRetiredJobs':
   if len(args) != 2:
     print 'getRetiredJobs requires 2 args'

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

@@ -75,6 +75,16 @@ class Iface(hadoop.api.common.HadoopServiceBase.Iface):
     """
     pass
 
+  def getRetiredJob(self, ctx, jobID):
+    """
+    Get a retired job
+
+    Parameters:
+     - ctx
+     - jobID
+    """
+    pass
+
   def getRetiredJobs(self, ctx, state):
     """
     Get a list of retired jobs
@@ -472,6 +482,42 @@ class Client(hadoop.api.common.HadoopServiceBase.Client, Iface):
       return result.success
     raise TApplicationException(TApplicationException.MISSING_RESULT, "getCompletedJobs failed: unknown result");
 
+  def getRetiredJob(self, ctx, jobID):
+    """
+    Get a retired job
+
+    Parameters:
+     - ctx
+     - jobID
+    """
+    self.send_getRetiredJob(ctx, jobID)
+    return self.recv_getRetiredJob()
+
+  def send_getRetiredJob(self, ctx, jobID):
+    self._oprot.writeMessageBegin('getRetiredJob', TMessageType.CALL, self._seqid)
+    args = getRetiredJob_args()
+    args.ctx = ctx
+    args.jobID = jobID
+    args.write(self._oprot)
+    self._oprot.writeMessageEnd()
+    self._oprot.trans.flush()
+
+  def recv_getRetiredJob(self, ):
+    (fname, mtype, rseqid) = self._iprot.readMessageBegin()
+    if mtype == TMessageType.EXCEPTION:
+      x = TApplicationException()
+      x.read(self._iprot)
+      self._iprot.readMessageEnd()
+      raise x
+    result = getRetiredJob_result()
+    result.read(self._iprot)
+    self._iprot.readMessageEnd()
+    if result.success is not None:
+      return result.success
+    if result.err is not None:
+      raise result.err
+    raise TApplicationException(TApplicationException.MISSING_RESULT, "getRetiredJob failed: unknown result");
+
   def getRetiredJobs(self, ctx, state):
     """
     Get a list of retired jobs
@@ -1152,6 +1198,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["getRetiredJob"] = Processor.process_getRetiredJob
     self._processMap["getRetiredJobs"] = Processor.process_getRetiredJobs
     self._processMap["getFailedJobs"] = Processor.process_getFailedJobs
     self._processMap["getKilledJobs"] = Processor.process_getKilledJobs
@@ -1259,6 +1306,20 @@ class Processor(hadoop.api.common.HadoopServiceBase.Processor, Iface, TProcessor
     oprot.writeMessageEnd()
     oprot.trans.flush()
 
+  def process_getRetiredJob(self, seqid, iprot, oprot):
+    args = getRetiredJob_args()
+    args.read(iprot)
+    iprot.readMessageEnd()
+    result = getRetiredJob_result()
+    try:
+      result.success = self._handler.getRetiredJob(args.ctx, args.jobID)
+    except JobNotFoundException, err:
+      result.err = err
+    oprot.writeMessageBegin("getRetiredJob", TMessageType.REPLY, seqid)
+    result.write(oprot)
+    oprot.writeMessageEnd()
+    oprot.trans.flush()
+
   def process_getRetiredJobs(self, seqid, iprot, oprot):
     args = getRetiredJobs_args()
     args.read(iprot)
@@ -2328,6 +2389,161 @@ class getCompletedJobs_result(object):
   def __ne__(self, other):
     return not (self == other)
 
+class getRetiredJob_args(object):
+  """
+  Attributes:
+   - ctx
+   - jobID
+  """
+
+  thrift_spec = (
+    None, # 0
+    (1, TType.STRUCT, 'jobID', (ThriftJobID, ThriftJobID.thrift_spec), 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, jobID=None,):
+    self.ctx = ctx
+    self.jobID = jobID
+
+  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.STRUCT:
+          self.jobID = ThriftJobID()
+          self.jobID.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('getRetiredJob_args')
+    if self.jobID is not None:
+      oprot.writeFieldBegin('jobID', TType.STRUCT, 1)
+      self.jobID.write(oprot)
+      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 getRetiredJob_result(object):
+  """
+  Attributes:
+   - success
+   - err
+  """
+
+  thrift_spec = (
+    (0, TType.STRUCT, 'success', (ThriftJobInProgress, ThriftJobInProgress.thrift_spec), None, ), # 0
+    (1, TType.STRUCT, 'err', (JobNotFoundException, JobNotFoundException.thrift_spec), None, ), # 1
+  )
+
+  def __init__(self, success=None, err=None,):
+    self.success = success
+    self.err = err
+
+  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 = ThriftJobInProgress()
+          self.success.read(iprot)
+        else:
+          iprot.skip(ftype)
+      elif fid == 1:
+        if ftype == TType.STRUCT:
+          self.err = JobNotFoundException()
+          self.err.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('getRetiredJob_result')
+    if self.success is not None:
+      oprot.writeFieldBegin('success', TType.STRUCT, 0)
+      self.success.write(oprot)
+      oprot.writeFieldEnd()
+    if self.err is not None:
+      oprot.writeFieldBegin('err', TType.STRUCT, 1)
+      self.err.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_args(object):
   """
   Attributes:

+ 4 - 0
desktop/libs/hadoop/java/if/jobtracker.thrift

@@ -393,6 +393,10 @@ service Jobtracker extends common.HadoopServiceBase {
   /** Get a list of completed jobs */
   ThriftJobList getCompletedJobs(10: common.RequestContext ctx),
 
+  /** Get a retired job */
+  ThriftJobInProgress getRetiredJob(10: common.RequestContext ctx, 1: ThriftJobID jobID)
+      throws(1: JobNotFoundException err),
+
   /** Get a list of retired jobs */
   ThriftJobList getRetiredJobs(10: common.RequestContext ctx, 1: ThriftJobState state),
 

+ 867 - 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 retired job
+     * 
+     * @param ctx
+     * @param jobID
+     */
+    public ThriftJobInProgress getRetiredJob(org.apache.hadoop.thriftfs.api.RequestContext ctx, ThriftJobID jobID) throws JobNotFoundException, org.apache.thrift.TException;
+
     /**
      * Get a list of retired jobs
      * 
@@ -239,6 +247,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 getRetiredJob(org.apache.hadoop.thriftfs.api.RequestContext ctx, ThriftJobID jobID, org.apache.thrift.async.AsyncMethodCallback<AsyncClient.getRetiredJob_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;
@@ -444,6 +454,33 @@ public class Jobtracker {
       throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "getCompletedJobs failed: unknown result");
     }
 
+    public ThriftJobInProgress getRetiredJob(org.apache.hadoop.thriftfs.api.RequestContext ctx, ThriftJobID jobID) throws JobNotFoundException, org.apache.thrift.TException
+    {
+      send_getRetiredJob(ctx, jobID);
+      return recv_getRetiredJob();
+    }
+
+    public void send_getRetiredJob(org.apache.hadoop.thriftfs.api.RequestContext ctx, ThriftJobID jobID) throws org.apache.thrift.TException
+    {
+      getRetiredJob_args args = new getRetiredJob_args();
+      args.setCtx(ctx);
+      args.setJobID(jobID);
+      sendBase("getRetiredJob", args);
+    }
+
+    public ThriftJobInProgress recv_getRetiredJob() throws JobNotFoundException, org.apache.thrift.TException
+    {
+      getRetiredJob_result result = new getRetiredJob_result();
+      receiveBase(result, "getRetiredJob");
+      if (result.isSetSuccess()) {
+        return result.success;
+      }
+      if (result.err != null) {
+        throw result.err;
+      }
+      throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "getRetiredJob failed: unknown result");
+    }
+
     public ThriftJobList getRetiredJobs(org.apache.hadoop.thriftfs.api.RequestContext ctx, ThriftJobState state) throws org.apache.thrift.TException
     {
       send_getRetiredJobs(ctx, state);
@@ -1148,6 +1185,41 @@ public class Jobtracker {
       }
     }
 
+    public void getRetiredJob(org.apache.hadoop.thriftfs.api.RequestContext ctx, ThriftJobID jobID, org.apache.thrift.async.AsyncMethodCallback<getRetiredJob_call> resultHandler) throws org.apache.thrift.TException {
+      checkReady();
+      getRetiredJob_call method_call = new getRetiredJob_call(ctx, jobID, resultHandler, this, ___protocolFactory, ___transport);
+      this.___currentMethod = method_call;
+      ___manager.call(method_call);
+    }
+
+    public static class getRetiredJob_call extends org.apache.thrift.async.TAsyncMethodCall {
+      private org.apache.hadoop.thriftfs.api.RequestContext ctx;
+      private ThriftJobID jobID;
+      public getRetiredJob_call(org.apache.hadoop.thriftfs.api.RequestContext ctx, ThriftJobID jobID, org.apache.thrift.async.AsyncMethodCallback<getRetiredJob_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.jobID = jobID;
+      }
+
+      public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException {
+        prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("getRetiredJob", org.apache.thrift.protocol.TMessageType.CALL, 0));
+        getRetiredJob_args args = new getRetiredJob_args();
+        args.setCtx(ctx);
+        args.setJobID(jobID);
+        args.write(prot);
+        prot.writeMessageEnd();
+      }
+
+      public ThriftJobInProgress getResult() throws JobNotFoundException, 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_getRetiredJob();
+      }
+    }
+
     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);
@@ -1829,6 +1901,7 @@ public class Jobtracker {
       processMap.put("getJob", new getJob());
       processMap.put("getRunningJobs", new getRunningJobs());
       processMap.put("getCompletedJobs", new getCompletedJobs());
+      processMap.put("getRetiredJob", new getRetiredJob());
       processMap.put("getRetiredJobs", new getRetiredJobs());
       processMap.put("getFailedJobs", new getFailedJobs());
       processMap.put("getKilledJobs", new getKilledJobs());
@@ -1955,6 +2028,26 @@ public class Jobtracker {
       }
     }
 
+    private static class getRetiredJob<I extends Iface> extends org.apache.thrift.ProcessFunction<I, getRetiredJob_args> {
+      public getRetiredJob() {
+        super("getRetiredJob");
+      }
+
+      protected getRetiredJob_args getEmptyArgsInstance() {
+        return new getRetiredJob_args();
+      }
+
+      protected getRetiredJob_result getResult(I iface, getRetiredJob_args args) throws org.apache.thrift.TException {
+        getRetiredJob_result result = new getRetiredJob_result();
+        try {
+          result.success = iface.getRetiredJob(args.ctx, args.jobID);
+        } catch (JobNotFoundException err) {
+          result.err = err;
+        }
+        return result;
+      }
+    }
+
     private static class getRetiredJobs<I extends Iface> extends org.apache.thrift.ProcessFunction<I, getRetiredJobs_args> {
       public getRetiredJobs() {
         super("getRetiredJobs");
@@ -6149,6 +6242,780 @@ public class Jobtracker {
 
   }
 
+  public static class getRetiredJob_args implements org.apache.thrift.TBase<getRetiredJob_args, getRetiredJob_args._Fields>, java.io.Serializable, Cloneable   {
+    private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("getRetiredJob_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 JOB_ID_FIELD_DESC = new org.apache.thrift.protocol.TField("jobID", org.apache.thrift.protocol.TType.STRUCT, (short)1);
+
+    public org.apache.hadoop.thriftfs.api.RequestContext ctx; // required
+    public ThriftJobID jobID; // 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"),
+      JOB_ID((short)1, "jobID");
+
+      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: // JOB_ID
+            return JOB_ID;
+          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.JOB_ID, new org.apache.thrift.meta_data.FieldMetaData("jobID", org.apache.thrift.TFieldRequirementType.DEFAULT, 
+          new org.apache.thrift.meta_data.StructMetaData(org.apache.thrift.protocol.TType.STRUCT, ThriftJobID.class)));
+      metaDataMap = Collections.unmodifiableMap(tmpMap);
+      org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(getRetiredJob_args.class, metaDataMap);
+    }
+
+    public getRetiredJob_args() {
+    }
+
+    public getRetiredJob_args(
+      org.apache.hadoop.thriftfs.api.RequestContext ctx,
+      ThriftJobID jobID)
+    {
+      this();
+      this.ctx = ctx;
+      this.jobID = jobID;
+    }
+
+    /**
+     * Performs a deep copy on <i>other</i>.
+     */
+    public getRetiredJob_args(getRetiredJob_args other) {
+      if (other.isSetCtx()) {
+        this.ctx = new org.apache.hadoop.thriftfs.api.RequestContext(other.ctx);
+      }
+      if (other.isSetJobID()) {
+        this.jobID = new ThriftJobID(other.jobID);
+      }
+    }
+
+    public getRetiredJob_args deepCopy() {
+      return new getRetiredJob_args(this);
+    }
+
+    @Override
+    public void clear() {
+      this.ctx = null;
+      this.jobID = null;
+    }
+
+    public org.apache.hadoop.thriftfs.api.RequestContext getCtx() {
+      return this.ctx;
+    }
+
+    public getRetiredJob_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;
+      }
+    }
+
+    public ThriftJobID getJobID() {
+      return this.jobID;
+    }
+
+    public getRetiredJob_args setJobID(ThriftJobID jobID) {
+      this.jobID = jobID;
+      return this;
+    }
+
+    public void unsetJobID() {
+      this.jobID = null;
+    }
+
+    /** Returns true if field jobID is set (has been assigned a value) and false otherwise */
+    public boolean isSetJobID() {
+      return this.jobID != null;
+    }
+
+    public void setJobIDIsSet(boolean value) {
+      if (!value) {
+        this.jobID = 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 JOB_ID:
+        if (value == null) {
+          unsetJobID();
+        } else {
+          setJobID((ThriftJobID)value);
+        }
+        break;
+
+      }
+    }
+
+    public Object getFieldValue(_Fields field) {
+      switch (field) {
+      case CTX:
+        return getCtx();
+
+      case JOB_ID:
+        return getJobID();
+
+      }
+      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 JOB_ID:
+        return isSetJobID();
+      }
+      throw new IllegalStateException();
+    }
+
+    @Override
+    public boolean equals(Object that) {
+      if (that == null)
+        return false;
+      if (that instanceof getRetiredJob_args)
+        return this.equals((getRetiredJob_args)that);
+      return false;
+    }
+
+    public boolean equals(getRetiredJob_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_jobID = true && this.isSetJobID();
+      boolean that_present_jobID = true && that.isSetJobID();
+      if (this_present_jobID || that_present_jobID) {
+        if (!(this_present_jobID && that_present_jobID))
+          return false;
+        if (!this.jobID.equals(that.jobID))
+          return false;
+      }
+
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      return 0;
+    }
+
+    public int compareTo(getRetiredJob_args other) {
+      if (!getClass().equals(other.getClass())) {
+        return getClass().getName().compareTo(other.getClass().getName());
+      }
+
+      int lastComparison = 0;
+      getRetiredJob_args typedOther = (getRetiredJob_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(isSetJobID()).compareTo(typedOther.isSetJobID());
+      if (lastComparison != 0) {
+        return lastComparison;
+      }
+      if (isSetJobID()) {
+        lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.jobID, typedOther.jobID);
+        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: // JOB_ID
+            if (field.type == org.apache.thrift.protocol.TType.STRUCT) {
+              this.jobID = new ThriftJobID();
+              this.jobID.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 {
+      validate();
+
+      oprot.writeStructBegin(STRUCT_DESC);
+      if (this.jobID != null) {
+        oprot.writeFieldBegin(JOB_ID_FIELD_DESC);
+        this.jobID.write(oprot);
+        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("getRetiredJob_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("jobID:");
+      if (this.jobID == null) {
+        sb.append("null");
+      } else {
+        sb.append(this.jobID);
+      }
+      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 getRetiredJob_result implements org.apache.thrift.TBase<getRetiredJob_result, getRetiredJob_result._Fields>, java.io.Serializable, Cloneable   {
+    private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("getRetiredJob_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);
+    private static final org.apache.thrift.protocol.TField ERR_FIELD_DESC = new org.apache.thrift.protocol.TField("err", org.apache.thrift.protocol.TType.STRUCT, (short)1);
+
+    public ThriftJobInProgress success; // required
+    public JobNotFoundException err; // 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"),
+      ERR((short)1, "err");
+
+      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;
+          case 1: // ERR
+            return ERR;
+          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, ThriftJobInProgress.class)));
+      tmpMap.put(_Fields.ERR, new org.apache.thrift.meta_data.FieldMetaData("err", org.apache.thrift.TFieldRequirementType.DEFAULT, 
+          new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRUCT)));
+      metaDataMap = Collections.unmodifiableMap(tmpMap);
+      org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(getRetiredJob_result.class, metaDataMap);
+    }
+
+    public getRetiredJob_result() {
+    }
+
+    public getRetiredJob_result(
+      ThriftJobInProgress success,
+      JobNotFoundException err)
+    {
+      this();
+      this.success = success;
+      this.err = err;
+    }
+
+    /**
+     * Performs a deep copy on <i>other</i>.
+     */
+    public getRetiredJob_result(getRetiredJob_result other) {
+      if (other.isSetSuccess()) {
+        this.success = new ThriftJobInProgress(other.success);
+      }
+      if (other.isSetErr()) {
+        this.err = new JobNotFoundException(other.err);
+      }
+    }
+
+    public getRetiredJob_result deepCopy() {
+      return new getRetiredJob_result(this);
+    }
+
+    @Override
+    public void clear() {
+      this.success = null;
+      this.err = null;
+    }
+
+    public ThriftJobInProgress getSuccess() {
+      return this.success;
+    }
+
+    public getRetiredJob_result setSuccess(ThriftJobInProgress 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 JobNotFoundException getErr() {
+      return this.err;
+    }
+
+    public getRetiredJob_result setErr(JobNotFoundException err) {
+      this.err = err;
+      return this;
+    }
+
+    public void unsetErr() {
+      this.err = null;
+    }
+
+    /** Returns true if field err is set (has been assigned a value) and false otherwise */
+    public boolean isSetErr() {
+      return this.err != null;
+    }
+
+    public void setErrIsSet(boolean value) {
+      if (!value) {
+        this.err = null;
+      }
+    }
+
+    public void setFieldValue(_Fields field, Object value) {
+      switch (field) {
+      case SUCCESS:
+        if (value == null) {
+          unsetSuccess();
+        } else {
+          setSuccess((ThriftJobInProgress)value);
+        }
+        break;
+
+      case ERR:
+        if (value == null) {
+          unsetErr();
+        } else {
+          setErr((JobNotFoundException)value);
+        }
+        break;
+
+      }
+    }
+
+    public Object getFieldValue(_Fields field) {
+      switch (field) {
+      case SUCCESS:
+        return getSuccess();
+
+      case ERR:
+        return getErr();
+
+      }
+      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();
+      case ERR:
+        return isSetErr();
+      }
+      throw new IllegalStateException();
+    }
+
+    @Override
+    public boolean equals(Object that) {
+      if (that == null)
+        return false;
+      if (that instanceof getRetiredJob_result)
+        return this.equals((getRetiredJob_result)that);
+      return false;
+    }
+
+    public boolean equals(getRetiredJob_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;
+      }
+
+      boolean this_present_err = true && this.isSetErr();
+      boolean that_present_err = true && that.isSetErr();
+      if (this_present_err || that_present_err) {
+        if (!(this_present_err && that_present_err))
+          return false;
+        if (!this.err.equals(that.err))
+          return false;
+      }
+
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      return 0;
+    }
+
+    public int compareTo(getRetiredJob_result other) {
+      if (!getClass().equals(other.getClass())) {
+        return getClass().getName().compareTo(other.getClass().getName());
+      }
+
+      int lastComparison = 0;
+      getRetiredJob_result typedOther = (getRetiredJob_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;
+        }
+      }
+      lastComparison = Boolean.valueOf(isSetErr()).compareTo(typedOther.isSetErr());
+      if (lastComparison != 0) {
+        return lastComparison;
+      }
+      if (isSetErr()) {
+        lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.err, typedOther.err);
+        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 ThriftJobInProgress();
+              this.success.read(iprot);
+            } else { 
+              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, field.type);
+            }
+            break;
+          case 1: // ERR
+            if (field.type == org.apache.thrift.protocol.TType.STRUCT) {
+              this.err = new JobNotFoundException();
+              this.err.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();
+      } else if (this.isSetErr()) {
+        oprot.writeFieldBegin(ERR_FIELD_DESC);
+        this.err.write(oprot);
+        oprot.writeFieldEnd();
+      }
+      oprot.writeFieldStop();
+      oprot.writeStructEnd();
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder sb = new StringBuilder("getRetiredJob_result(");
+      boolean first = true;
+
+      sb.append("success:");
+      if (this.success == null) {
+        sb.append("null");
+      } else {
+        sb.append(this.success);
+      }
+      first = false;
+      if (!first) sb.append(", ");
+      sb.append("err:");
+      if (this.err == null) {
+        sb.append("null");
+      } else {
+        sb.append(this.err);
+      }
+      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_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");
 

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

@@ -811,6 +811,27 @@ public class ThriftJobTrackerPlugin extends JobTrackerPlugin implements Configur
             });
         }
 
+        /** Returns a retired job (does not include task info, miss some fields) */
+        public ThriftJobInProgress getRetiredJob(final RequestContext ctx, final ThriftJobID jobID) throws JobNotFoundException {
+            final JobID jid = JTThriftUtils.fromThrift(jobID);
+
+            final JobStatus jobStatus = assumeUserContextAndExecute(ctx, new PrivilegedAction<JobStatus>() {
+              public JobStatus run() {
+                return jobTracker.getJobStatus(jid);
+              }
+            });
+
+            if (jobStatus == null) {
+              throw new JobNotFoundException();
+            }
+
+            return assumeUserContextAndExecute(ctx, new PrivilegedAction<ThriftJobInProgress>() {
+              public ThriftJobInProgress run() {
+                return JTThriftUtils.toThrift(jobTracker.getJobProfile(jid), jobStatus);
+              }
+            });
+        }
+
         /** 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>() {
@@ -829,6 +850,9 @@ public class ThriftJobTrackerPlugin extends JobTrackerPlugin implements Configur
                     for (JobInProgress job : jobTracker.completedJobs()) {
                         jobsInProgressId.add(job.getJobID());
                     }
+                    for (JobStatus job : jobTracker.jobsToComplete()) {
+                      jobsInProgressId.add(job.getJobID());
+                    }
                 }
 
                 ArrayList<ThriftJobInProgress> ret = new ArrayList<ThriftJobInProgress>();

+ 14 - 1
desktop/libs/hadoop/src/hadoop/job_tracker.py

@@ -274,9 +274,22 @@ class LiveJobTracker(object):
       self._fixup_job(job)
     return joblist
 
+  def get_retired_job(self, jobid):
+    """
+    Returns a ThriftJobInProgress (does not include task info and most of the job information)
+    """
+    try:
+      job = self.client.getRetiredJob(self.thread_local.request_context, jobid)
+    except JobNotFoundException, e:
+        e.response_data = dict(code="JT_JOB_NOT_FOUND", message="Could not find job %s on JobTracker." % jobid.asString, data=jobid)
+        raise
+    self._fixup_job(job)
+    self._fixup_retired_job(job)
+    return job
+
   def retired_jobs(self, status=None):
     """
-    Returns a ThriftJobStatusList (does not include task info)
+    Returns a ThriftJobStatusList (does not include task info and most of the job information)
     """
     joblist = self.client.getRetiredJobs(self.thread_local.request_context, status)
     for job in joblist.jobs: