@@ -63,6 +63,7 @@ cdef class Jobs(MultiClusterMap):
6363
6464 def __init__ (self , jobs = None , frozen = False ):
6565 self .frozen = frozen
66+ self .stats = JobStatistics()
6667 super ().__init__(data = jobs,
6768 typ = " Jobs" ,
6869 val_type = Job,
@@ -161,15 +162,48 @@ cdef class Jobs(MultiClusterMap):
161162 Pending Jobs will be ignored, since they don't have any Steps yet.
162163
163164 Raises:
164- RPCError: When retrieving the Job information for all the Steps
165- failed.
165+ RPCError: When retrieving the information for all the Steps failed.
166166 """
167167 cdef dict steps = JobSteps.load_all()
168168 for job in self .values():
169169 jid = job.id
170170 if jid in steps:
171171 job.steps = steps[jid]
172172
173+ def load_stats (self ):
174+ """ Load realtime stats for this collection of Jobs.
175+
176+ This function additionally fills in the `stats` attribute for all Jobs
177+ in the collection, and also populates its own `stats` attribute.
178+ Implicitly calls `load_steps()`.
179+
180+ !!! note
181+
182+ Pending Jobs will be ignored, since they don't have any Stats yet.
183+
184+ Returns:
185+ (JobStatistics): The statistics of this job collection.
186+
187+ Raises:
188+ RPCError: When retrieving the stats for all the Jobs failed.
189+
190+ Examples:
191+ >>> import pyslurm
192+ >>> jobs = pyslurm.Jobs.load()
193+ >>> stats = jobs.load_stats()
194+ >>>
195+ >>> # Print the CPU Time Used
196+ >>> print(stats.total_cpu_time)
197+ """
198+ self .load_steps()
199+ stats = JobStatistics()
200+ for job in self .values():
201+ job.load_stats()
202+ stats.add(job.stats)
203+
204+ self .stats = stats
205+ return self .stats
206+
173207 @property
174208 def memory (self ):
175209 return xcollections.sum_property(self , Job.memory)
@@ -183,7 +217,7 @@ cdef class Jobs(MultiClusterMap):
183217 return xcollections.sum_property(self , Job.ntasks)
184218
185219 @property
186- def cpu_time (self ):
220+ def elapsed_cpu_time (self ):
187221 return xcollections.sum_property(self , Job.cpu_time)
188222
189223
@@ -199,6 +233,8 @@ cdef class Job:
199233 self .groups = {}
200234 cstr.fmalloc(& self .ptr.cluster, LOCAL_CLUSTER)
201235 self .steps = JobSteps()
236+ self .stats = JobStatistics()
237+ self .pids = {}
202238
203239 def _alloc_impl (self ):
204240 if not self .ptr:
@@ -225,7 +261,7 @@ cdef class Job:
225261 !!! note
226262
227263 If the Job is not pending, the related Job steps will also be
228- loaded.
264+ loaded. Job statistics are however not loaded automatically.
229265
230266 Args:
231267 job_id (int):
@@ -276,6 +312,8 @@ cdef class Job:
276312 wrap.passwd = {}
277313 wrap.groups = {}
278314 wrap.steps = JobSteps.__new__ (JobSteps)
315+ wrap.stats = JobStatistics()
316+ wrap.pids = {}
279317 memcpy(wrap.ptr, in_ptr, sizeof(slurm_job_info_t))
280318 return wrap
281319
@@ -297,6 +335,8 @@ cdef class Job:
297335 """
298336 cdef dict out = instance_to_dict(self )
299337 out[" steps" ] = self .steps.to_dict()
338+ out[" stats" ] = self .stats.to_dict()
339+ out[" pids" ] = self .pids
300340 return out
301341
302342 def send_signal (self , signal , steps = " children" , hurry = False ):
@@ -516,6 +556,49 @@ cdef class Job:
516556 """
517557 verify_rpc(slurm_notify_job(self .id, msg))
518558
559+ def load_stats (self ):
560+ """ Load realtime statistics for a Job and its steps.
561+
562+ Calling this function returns the Job statistics, and additionally
563+ populates the `stats` and `pids` attribute of the instance.
564+
565+ Returns:
566+ (JobStatistics): The statistics of the job.
567+
568+ Raises:
569+ RPCError: When receiving the Statistics was not successful.
570+
571+ Examples:
572+ >>> import pyslurm
573+ >>> job = pyslurm.Job.load(9999)
574+ >>> stats = job.load_stats()
575+ >>>
576+ >>> # Print the CPU Time Used
577+ >>> print(stats.total_cpu_time)
578+ >>>
579+ >>> # Print the Process-IDs for the whole Job, organized by hostname
580+ >>> print(job.pids)
581+ """
582+ if not self .steps:
583+ job = Job.load(self .id)
584+ self .steps = job.steps
585+
586+ all_pids = {}
587+ for step in self .steps.values():
588+ step.load_stats()
589+ self .stats._sum_steps(step.stats)
590+
591+ for node, pids in step.pids.items():
592+ if node not in all_pids:
593+ all_pids[node] = []
594+
595+ all_pids[node].extend(pids)
596+
597+ self .stats.elapsed_cpu_time = self .run_time * self .cpus
598+
599+ self .pids = all_pids
600+ return self .stats
601+
519602 def get_batch_script (self ):
520603 """ Return the content of the script for a Batch-Job.
521604
@@ -1181,7 +1264,7 @@ cdef class Job:
11811264 return cstr.to_unicode(self .ptr.cronspec)
11821265
11831266 @property
1184- def cpu_time (self ):
1267+ def elapsed_cpu_time (self ):
11851268 return self .cpus * self .run_time
11861269
11871270 @property
0 commit comments