Skip to content

Commit 0d5e23a

Browse files
authored
Return job steps from slurmdb_jobs.get #167 (#168)
* add # cython: language_level=2 * Return job steps from slurmdb_jobs.get() #167 * Add test #167
1 parent f21fbd9 commit 0d5e23a

File tree

4 files changed

+170
-13
lines changed

4 files changed

+170
-13
lines changed

examples/listdb_jobs.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
List all jobs in Slurm, similar to `sacct`
44
"""
55
import time
6+
import datetime
67

78
import pyslurm
89

@@ -15,11 +16,11 @@ def job_display(job):
1516

1617
if __name__ == "__main__":
1718
try:
18-
end = time.time()
19-
start = end - (30 * 24 * 60 * 60)
20-
print("start={}, end={}".format(start, end))
19+
start = (datetime.datetime.utcnow() - datetime.timedelta(days=1)).strftime("%Y-%m-%dT00:00:00")
20+
end = (datetime.datetime.utcnow() + datetime.timedelta(days=1)).strftime("%Y-%m-%dT00:00:00")
21+
2122
jobs = pyslurm.slurmdb_jobs()
22-
jobs_dict = jobs.get(starttime=start, endtime=end)
23+
jobs_dict = jobs.get(starttime=start.encode('utf-8'), endtime=end.encode('utf-8'))
2324
if jobs_dict:
2425
for key, value in jobs_dict.items():
2526
print("{} Job: {}".format("{", key))

pyslurm/pyslurm.pyx

Lines changed: 99 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# cython: embedsignature=True
22
# cython: profile=False
3+
# cython: language_level=2
34
import os
45
import re
56
import sys
@@ -5434,8 +5435,8 @@ cdef class slurmdb_jobs:
54345435
if job is not NULL:
54355436
jobid = job.jobid
54365437
JOBS_info[u'account'] = slurm.stringOrNone(job.account, '')
5437-
JOBS_info[u'allocated_gres'] = slurm.stringOrNone(job.alloc_gres, '')
5438-
JOBS_info[u'allocated_nodes'] = job.alloc_nodes
5438+
JOBS_info[u'alloc_gres'] = slurm.stringOrNone(job.alloc_gres, '')
5439+
JOBS_info[u'alloc_nodes'] = job.alloc_nodes
54395440
JOBS_info[u'array_job_id'] = job.array_job_id
54405441
JOBS_info[u'array_max_tasks'] = job.array_max_tasks
54415442
JOBS_info[u'array_task_id'] = job.array_task_id
@@ -5448,7 +5449,7 @@ cdef class slurmdb_jobs:
54485449
JOBS_info[u'elapsed'] = job.elapsed
54495450
JOBS_info[u'eligible'] = job.eligible
54505451
JOBS_info[u'end'] = job.end
5451-
JOBS_info[u'exit_code'] = job.exitcode
5452+
JOBS_info[u'exitcode'] = job.exitcode
54525453
JOBS_info[u'gid'] = job.gid
54535454
JOBS_info[u'jobid'] = job.jobid
54545455
JOBS_info[u'jobname'] = slurm.stringOrNone(job.jobname, '')
@@ -5466,11 +5467,101 @@ cdef class slurmdb_jobs:
54665467
JOBS_info[u'show_full'] = job.show_full
54675468
JOBS_info[u'start'] = job.start
54685469
JOBS_info[u'state'] = job.state
5469-
JOBS_info[u'state_str'] = slurm.slurm_job_state_string(job.state)
5470+
JOBS_info[u'state_str'] = slurm.stringOrNone(slurm.slurm_job_state_string(job.state), '')
5471+
5472+
# TRES are reported as strings in the format `TRESID=value` where TRESID is one of:
5473+
# TRES_CPU=1, TRES_MEM=2, TRES_ENERGY=3, TRES_NODE=4, TRES_BILLING=5, TRES_FS_DISK=6, TRES_VMEM=7, TRES_PAGES=8
5474+
# Example: '1=0,2=745472,3=0,6=1949,7=7966720,8=0'
5475+
JOBS_info[u'stats'] = {}
5476+
stats = JOBS_info[u'stats']
5477+
stats[u'act_cpufreq'] = job.stats.act_cpufreq
5478+
stats[u'consumed_energy'] = job.stats.consumed_energy
5479+
stats[u'tres_usage_in_max'] = slurm.stringOrNone(job.stats.tres_usage_in_max, '')
5480+
stats[u'tres_usage_in_max_nodeid'] = slurm.stringOrNone(job.stats.tres_usage_in_max_nodeid, '')
5481+
stats[u'tres_usage_in_max_taskid'] = slurm.stringOrNone(job.stats.tres_usage_in_max_taskid, '')
5482+
stats[u'tres_usage_in_min'] = slurm.stringOrNone(job.stats.tres_usage_in_min, '')
5483+
stats[u'tres_usage_in_min_nodeid'] = slurm.stringOrNone(job.stats.tres_usage_in_min_nodeid, '')
5484+
stats[u'tres_usage_in_min_taskid'] = slurm.stringOrNone(job.stats.tres_usage_in_min_taskid, '')
5485+
stats[u'tres_usage_in_tot'] = slurm.stringOrNone(job.stats.tres_usage_in_tot, '')
5486+
stats[u'tres_usage_out_ave'] = slurm.stringOrNone(job.stats.tres_usage_out_ave, '')
5487+
stats[u'tres_usage_out_max'] = slurm.stringOrNone(job.stats.tres_usage_out_max, '')
5488+
stats[u'tres_usage_out_max_nodeid'] = slurm.stringOrNone(job.stats.tres_usage_out_max_nodeid, '')
5489+
stats[u'tres_usage_out_max_taskid'] = slurm.stringOrNone(job.stats.tres_usage_out_max_taskid, '')
5490+
stats[u'tres_usage_out_min'] = slurm.stringOrNone(job.stats.tres_usage_out_min, '')
5491+
stats[u'tres_usage_out_min_nodeid'] = slurm.stringOrNone(job.stats.tres_usage_out_min_nodeid, '')
5492+
stats[u'tres_usage_out_min_taskid'] = slurm.stringOrNone(job.stats.tres_usage_out_min_taskid, '')
5493+
stats[u'tres_usage_out_tot'] = slurm.stringOrNone(job.stats.tres_usage_out_tot, '')
5494+
5495+
# add job steps
5496+
JOBS_info[u'steps'] = {}
5497+
step_dict = JOBS_info[u'steps']
5498+
5499+
stepsNum = slurm.slurm_list_count(job.steps)
5500+
stepsIter = slurm.slurm_list_iterator_create(job.steps)
5501+
for i in range(stepsNum):
5502+
step = <slurm.slurmdb_step_rec_t *>slurm.slurm_list_next(stepsIter)
5503+
step_info = {}
5504+
if step is not NULL:
5505+
step_id = step.stepid
5506+
5507+
step_info[u'elapsed'] = step.elapsed
5508+
step_info[u'end'] = step.end
5509+
step_info[u'exitcode'] = step.exitcode
5510+
5511+
# Don't add this unless you want to create an endless recursive structure
5512+
# step_info[u'job_ptr'] = JOBS_Info # job's record
5513+
5514+
step_info[u'nnodes'] = step.nnodes
5515+
step_info[u'nodes'] = slurm.stringOrNone(step.nodes, '')
5516+
step_info[u'ntasks'] = step.ntasks
5517+
step_info[u'pid_str'] = slurm.stringOrNone(step.pid_str, '')
5518+
step_info[u'req_cpufreq_min'] = step.req_cpufreq_min
5519+
step_info[u'req_cpufreq_max'] = step.req_cpufreq_max
5520+
step_info[u'req_cpufreq_gov'] = step.req_cpufreq_gov
5521+
step_info[u'requid'] = step.requid
5522+
step_info[u'start'] = step.start
5523+
step_info[u'state'] = step.state
5524+
step_info[u'state_str'] = slurm.stringOrNone(slurm.slurm_job_state_string(step.state), '')
5525+
5526+
# TRES are reported as strings in the format `TRESID=value` where TRESID is one of:
5527+
# TRES_CPU=1, TRES_MEM=2, TRES_ENERGY=3, TRES_NODE=4, TRES_BILLING=5, TRES_FS_DISK=6, TRES_VMEM=7, TRES_PAGES=8
5528+
# Example: '1=0,2=745472,3=0,6=1949,7=7966720,8=0'
5529+
step_info[u'stats'] = {}
5530+
stats = step_info[u'stats']
5531+
stats[u'act_cpufreq'] = step.stats.act_cpufreq
5532+
stats[u'consumed_energy'] = step.stats.consumed_energy
5533+
stats[u'tres_usage_in_max'] = slurm.stringOrNone(step.stats.tres_usage_in_max, '')
5534+
stats[u'tres_usage_in_max_nodeid'] = slurm.stringOrNone(step.stats.tres_usage_in_max_nodeid, '')
5535+
stats[u'tres_usage_in_max_taskid'] = slurm.stringOrNone(step.stats.tres_usage_in_max_taskid, '')
5536+
stats[u'tres_usage_in_min'] = slurm.stringOrNone(step.stats.tres_usage_in_min, '')
5537+
stats[u'tres_usage_in_min_nodeid'] = slurm.stringOrNone(step.stats.tres_usage_in_min_nodeid, '')
5538+
stats[u'tres_usage_in_min_taskid'] = slurm.stringOrNone(step.stats.tres_usage_in_min_taskid, '')
5539+
stats[u'tres_usage_in_tot'] = slurm.stringOrNone(step.stats.tres_usage_in_tot, '')
5540+
stats[u'tres_usage_out_ave'] = slurm.stringOrNone(step.stats.tres_usage_out_ave, '')
5541+
stats[u'tres_usage_out_max'] = slurm.stringOrNone(step.stats.tres_usage_out_max, '')
5542+
stats[u'tres_usage_out_max_nodeid'] = slurm.stringOrNone(step.stats.tres_usage_out_max_nodeid, '')
5543+
stats[u'tres_usage_out_max_taskid'] = slurm.stringOrNone(step.stats.tres_usage_out_max_taskid, '')
5544+
stats[u'tres_usage_out_min'] = slurm.stringOrNone(step.stats.tres_usage_out_min, '')
5545+
stats[u'tres_usage_out_min_nodeid'] = slurm.stringOrNone(step.stats.tres_usage_out_min_nodeid, '')
5546+
stats[u'tres_usage_out_min_taskid'] = slurm.stringOrNone(step.stats.tres_usage_out_min_taskid, '')
5547+
stats[u'tres_usage_out_tot'] = slurm.stringOrNone(step.stats.tres_usage_out_tot, '')
5548+
5549+
step_info[u'stepid'] = step_id
5550+
step_info[u'stepname'] = slurm.stringOrNone(step.stepname, '')
5551+
step_info[u'suspended'] = step.suspended
5552+
step_info[u'sys_cpu_sec'] = step.sys_cpu_sec
5553+
step_info[u'sys_cpu_usec'] = step.sys_cpu_usec
5554+
step_info[u'task_dist'] = step.task_dist
5555+
step_info[u'tot_cpu_sec'] = step.tot_cpu_sec
5556+
step_info[u'tot_cpu_usec'] = step.tot_cpu_usec
5557+
step_info[u'tres_alloc_str'] = slurm.stringOrNone(step.tres_alloc_str, '')
5558+
step_info[u'user_cpu_sec'] = step.user_cpu_sec
5559+
step_info[u'user_cpu_usec'] = step.user_cpu_usec
5560+
5561+
step_dict[step_id] = step_info
5562+
5563+
slurm.slurm_list_iterator_destroy(stepsIter)
54705564

5471-
JOBS_info[u'stat_actual_cpufreq'] = job.stats.act_cpufreq
5472-
5473-
JOBS_info[u'steps'] = "Not filled, string should be handled"
54745565
JOBS_info[u'submit'] = job.submit
54755566
JOBS_info[u'suspended'] = job.suspended
54765567
JOBS_info[u'sys_cpu_sec'] = job.sys_cpu_sec
@@ -5488,6 +5579,7 @@ cdef class slurmdb_jobs:
54885579
JOBS_info[u'user_cpu_sec'] = job.user_cpu_usec
54895580
JOBS_info[u'wckey'] = slurm.stringOrNone(job.wckey, '')
54905581
JOBS_info[u'wckeyid'] = job.wckeyid
5582+
JOBS_info[u'work_dir'] = slurm.stringOrNone(job.work_dir, '')
54915583
J_dict[jobid] = JOBS_info
54925584

54935585
slurm.slurm_list_iterator_destroy(iters)

pyslurm/slurm.pxd

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2781,7 +2781,34 @@ cdef extern from 'slurm/slurmdb.h' nogil:
27812781
List tres_list
27822782
List user_list
27832783

2784-
# ctypedef struct slurmdb_step_rec_t
2784+
ctypedef struct slurmdb_step_rec_t:
2785+
uint32_t elapsed
2786+
time_t end
2787+
int32_t exitcode
2788+
slurmdb_job_rec_t *job_ptr # job's record
2789+
uint32_t nnodes
2790+
char *nodes
2791+
uint32_t ntasks
2792+
char *pid_str
2793+
uint32_t req_cpufreq_min
2794+
uint32_t req_cpufreq_max
2795+
uint32_t req_cpufreq_gov
2796+
uint32_t requid
2797+
time_t start
2798+
uint32_t state
2799+
slurmdb_stats_t stats
2800+
uint32_t stepid # job's step number
2801+
char *stepname
2802+
uint32_t suspended
2803+
uint32_t sys_cpu_sec
2804+
uint32_t sys_cpu_usec
2805+
uint32_t task_dist
2806+
uint32_t tot_cpu_sec
2807+
uint32_t tot_cpu_usec
2808+
char *tres_alloc_str
2809+
uint32_t user_cpu_sec
2810+
uint32_t user_cpu_usec
2811+
27852812
# ctypedef struct slurmdb_res_cond_t
27862813
# ctypedef struct slurmdb_res_rec_t
27872814
# ctypedef struct slurmdb_txn_cond_t

tests/test-slurmdb.py renamed to tests/test_slurmdb.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import datetime
21
import pwd
32
import subprocess
3+
import datetime
4+
import time
5+
import json
46

57
from nose.tools import assert_equals
68

@@ -62,6 +64,41 @@ def test_slurmdb_jobs_get():
6264
njobs_sacct = njobs_sacct_jobs(starttime, endtime)
6365
assert_equals(njobs_pyslurm, njobs_sacct)
6466

67+
def test_slurmdb_jobs_get_steps():
68+
"""
69+
Slurmdb: Get jobs with steps for all users
70+
"""
71+
job = {
72+
"wrap": """
73+
srun hostname
74+
srun sleep 1
75+
""",
76+
"job_name": "pyslurm_test_job_steps",
77+
"ntasks": 1,
78+
"cpus_per_task": 1,
79+
}
80+
job_id = pyslurm.job().submit_batch_job(job)
81+
82+
# wait for job to finish
83+
time.sleep(10)
84+
85+
# get `sacct` jobs
86+
start = (datetime.datetime.now() - datetime.timedelta(days=1)).strftime("%Y-%m-%dT00:00:00")
87+
end = (datetime.datetime.now() + datetime.timedelta(days=1)).strftime("%Y-%m-%dT00:00:00")
88+
jobs = pyslurm.slurmdb_jobs().get(starttime=start.encode('utf-8'), endtime=end.encode('utf-8'))
89+
90+
# make sure results are valid json
91+
assert json.dumps(jobs, sort_keys=True, indent=4)
92+
93+
# we should get our job in the results
94+
assert jobs.get(job_id, None)
95+
96+
# and it should have steps
97+
assert jobs[job_id]["steps"]
98+
99+
# and 3 steps, 1 batch + 2 srun
100+
assert 3 == len(jobs[job_id]["steps"])
101+
65102

66103
def test_slurmdb_jobs_get_byuser():
67104
"""

0 commit comments

Comments
 (0)