Belle II Software development
HTCondor.HTCondorResult Class Reference
Inheritance diagram for HTCondor.HTCondorResult:
Result

Public Member Functions

 __init__ (self, job, job_id)
 
 update_status (self)
 
 ready (self)
 
 get_exit_code_from_file (self)
 

Public Attributes

 job_id = job_id
 job id given by HTCondor
 
 job = job
 Job object for result.
 
 time_to_wait_for_exit_code_file = timedelta(minutes=5)
 After our first attempt to view the exit code file once the job is 'finished', how long should we wait for it to exist before timing out?
 
 exit_code_file_initial_time = None
 Time we started waiting for the exit code file to appear.
 

Static Public Attributes

dict backend_code_to_status
 HTCondor statuses mapped to Job statuses.
 

Protected Member Functions

 _update_result_status (self, condor_q_output)
 

Protected Attributes

bool _is_ready = False
 Quicker way to know if it's ready once it has already been found.
 

Detailed Description

Simple class to help monitor status of jobs submitted by HTCondor Backend.

You pass in a `Job` object and job id from a condor_submit command.
When you call the `ready` method it runs condor_q and, if needed, ``condor_history``
to see whether or not the job has finished.

Definition at line 2022 of file backends.py.

Constructor & Destructor Documentation

◆ __init__()

__init__ ( self,
job,
job_id )
Pass in the job object and the job id to allow the result to do monitoring and perform
post processing of the job.

Definition at line 2041 of file backends.py.

2041 def __init__(self, job, job_id):
2042 """
2043 Pass in the job object and the job id to allow the result to do monitoring and perform
2044 post processing of the job.
2045 """
2046 super().__init__(job)
2047
2048 self.job_id = job_id
2049

Member Function Documentation

◆ _update_result_status()

_update_result_status ( self,
condor_q_output )
protected
In order to be slightly more efficient we pass in a previous call to condor_q to see if it can work.
If it is there we update the job's status. If not we are forced to start calling condor_q and, if needed,
``condor_history``, etc.

Parameters:
    condor_q_output (dict): The JSON output of a previous call to `HTCondor.condor_q` which we can reuse to find the
        status of this job if it was active when that command ran.

Definition at line 2063 of file backends.py.

2063 def _update_result_status(self, condor_q_output):
2064 """
2065 In order to be slightly more efficient we pass in a previous call to condor_q to see if it can work.
2066 If it is there we update the job's status. If not we are forced to start calling condor_q and, if needed,
2067 ``condor_history``, etc.
2068
2069 Parameters:
2070 condor_q_output (dict): The JSON output of a previous call to `HTCondor.condor_q` which we can reuse to find the
2071 status of this job if it was active when that command ran.
2072 """
2073 B2DEBUG(29, f"Calling {self.job}.result._update_result_status()")
2074 jobs_info = []
2075 for job_record in condor_q_output["JOBS"]:
2076 job_id = job_record["GlobalJobId"].split("#")[1]
2077 if job_id == self.job_id:
2078 B2DEBUG(29, f"Found {self.job_id} in condor_q_output.")
2079 jobs_info.append(job_record)
2080
2081 # Let's look for the exit code file where we expect it
2082 if not jobs_info:
2083 try:
2084 exit_code = self.get_exit_code_from_file()
2085 except FileNotFoundError:
2086 waiting_time = datetime.now() - self.exit_code_file_initial_time
2087 if self.time_to_wait_for_exit_code_file > waiting_time:
2088 B2ERROR(f"Exit code file for {self.job} missing and we can't wait longer. Setting exit code to 1.")
2089 exit_code = 1
2090 else:
2091 B2WARNING(f"Exit code file for {self.job} missing, will wait longer.")
2092 return
2093 if exit_code:
2094 jobs_info = [{"JobStatus": 6, "HoldReason": None}] # Set to failed
2095 else:
2096 jobs_info = [{"JobStatus": 4, "HoldReason": None}] # Set to completed
2097
2098 # If this job wasn't in the passed in condor_q output, let's try our own with the specific job_id
2099 if not jobs_info:
2100 jobs_info = HTCondor.condor_q(job_id=self.job_id, class_ads=["JobStatus", "HoldReason"])["JOBS"]
2101
2102 # If no job information is returned then the job already left the queue
2103 # check in the history to see if it succeeded or failed
2104 if not jobs_info:
2105 try:
2106 jobs_info = HTCondor.condor_history(job_id=self.job_id, class_ads=["JobStatus", "HoldReason"])["JOBS"]
2107 except KeyError:
2108 hold_reason = "No Reason Known"
2109
2110 # Still no record of it after waiting for the exit code file?
2111 if not jobs_info:
2112 jobs_info = [{"JobStatus": 6, "HoldReason": None}] # Set to failed
2113
2114 job_info = jobs_info[0]
2115 backend_status = job_info["JobStatus"]
2116 # if job is held (backend_status = 5) then report why then keep waiting
2117 if backend_status == 5:
2118 hold_reason = job_info.get("HoldReason", None)
2119 B2WARNING(f"{self.job} on hold because of {hold_reason}. Keep waiting.")
2120 backend_status = 2
2121 try:
2122 new_job_status = self.backend_code_to_status[backend_status]
2123 except KeyError as err:
2124 raise BackendError(f"Unidentified backend status found for {self.job}: {backend_status}") from err
2125 if new_job_status != self.job.status:
2126 self.job.status = new_job_status
2127
2128 @classmethod

◆ get_exit_code_from_file()

get_exit_code_from_file ( self)
inherited
Read the exit code file to discover the exit status of the job command. Useful fallback if the job is no longer
known to the job database (batch system purged it for example). Since some backends may take time to download
the output files of the job back to the working directory we use a time limit on how long to wait.

Definition at line 909 of file backends.py.

909 def get_exit_code_from_file(self):
910 """
911 Read the exit code file to discover the exit status of the job command. Useful fallback if the job is no longer
912 known to the job database (batch system purged it for example). Since some backends may take time to download
913 the output files of the job back to the working directory we use a time limit on how long to wait.
914 """
915 if not self.exit_code_file_initial_time:
916 self.exit_code_file_initial_time = datetime.now()
917 exit_code_path = Path(self.job.working_dir, Backend.exit_code_file)
918 with open(exit_code_path) as f:
919 exit_code = int(f.read().strip())
920 B2DEBUG(29, f"Exit code from file for {self.job} was {exit_code}")
921 return exit_code
922
923

◆ ready()

ready ( self)
inherited
Returns whether or not this job result is known to be ready. Doesn't actually change the job status. Just changes
the 'readiness' based on the known job status.

Definition at line 888 of file backends.py.

888 def ready(self):
889 """
890 Returns whether or not this job result is known to be ready. Doesn't actually change the job status. Just changes
891 the 'readiness' based on the known job status.
892 """
893 B2DEBUG(29, f"Calling {self.job}.result.ready()")
894 if self._is_ready:
895 return True
896 elif self.job.status in self.job.exit_statuses:
897 self._is_ready = True
898 return True
899 else:
900 return False
901

◆ update_status()

update_status ( self)
Update the job's (or subjobs') status by calling condor_q.

Reimplemented from Result.

Definition at line 2050 of file backends.py.

2050 def update_status(self):
2051 """
2052 Update the job's (or subjobs') status by calling condor_q.
2053 """
2054 B2DEBUG(29, f"Calling {self.job.name}.result.update_status()")
2055 # Get all jobs info and reuse it for each status update to minimise tie spent on this updating.
2056 condor_q_output = HTCondor.condor_q()
2057 if self.job.subjobs:
2058 for subjob in self.job.subjobs.values():
2059 subjob.result._update_result_status(condor_q_output)
2060 else:
2061 self._update_result_status(condor_q_output)
2062

Member Data Documentation

◆ _is_ready

bool _is_ready = False
protectedinherited

Quicker way to know if it's ready once it has already been found.

Saves a lot of calls to batch system commands.

Definition at line 881 of file backends.py.

◆ backend_code_to_status

dict backend_code_to_status
static
Initial value:
= {0: "submitted",
1: "submitted",
2: "running",
3: "failed",
4: "completed",
5: "submitted",
6: "failed"
}

HTCondor statuses mapped to Job statuses.

Definition at line 2032 of file backends.py.

◆ exit_code_file_initial_time

exit_code_file_initial_time = None
inherited

Time we started waiting for the exit code file to appear.

Definition at line 886 of file backends.py.

◆ job

job = job
inherited

Job object for result.

Definition at line 879 of file backends.py.

◆ job_id

job_id = job_id

job id given by HTCondor

Definition at line 2048 of file backends.py.

◆ time_to_wait_for_exit_code_file

time_to_wait_for_exit_code_file = timedelta(minutes=5)
inherited

After our first attempt to view the exit code file once the job is 'finished', how long should we wait for it to exist before timing out?

Definition at line 884 of file backends.py.


The documentation for this class was generated from the following file: