From f00fdcecec484d28b029c73bd08f88a8b9a6bc86 Mon Sep 17 00:00:00 2001 From: xlqiang-learn <61980447+xlqiang-learn@users.noreply.github.com> Date: Thu, 23 Mar 2023 21:17:02 +0800 Subject: [PATCH 01/29] Create job_cputime.py An example shows how to get job's cpu time usage. --- examples/job_cputime.py | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 examples/job_cputime.py diff --git a/examples/job_cputime.py b/examples/job_cputime.py new file mode 100644 index 0000000..2a29398 --- /dev/null +++ b/examples/job_cputime.py @@ -0,0 +1,43 @@ +from pythonlsf import lsf + +def print_job_cputime(jobid): + + if lsf.lsb_init("job_info") > 0: + print("failed to initialise the api") + return + + num_jobs_found = lsf.lsb_openjobinfo(jobid, "", "all", "", "", 0x2000) + print('num_jobs_found: {}'.format(num_jobs_found)) + + if num_jobs_found == -1: + print("no job was found") + return + + int_ptr = lsf.new_intp() + lsf.intp_assign(int_ptr, num_jobs_found) + + job_info = lsf.lsb_readjobinfo(int_ptr) + + lsf.lsb_closejobinfo() + + print('jobId: {}'.format(job_info.jobId)) + print('jobStatus: {}'.format(job_info.status)) + + # Get CPU time from run Rusage + time_sum = float(job_info.runRusage.utime) + float(job_info.runRusage.stime) + if time_sum > 0: + print('The CPU time used is: {} seconds'.format(time_sum)) + + # Get CPU time from host Rusage of each execution host + if job_info.numhRusages > 0: + hRusages = job_info.hostRusage + for i in range(0,job_info.numhRusages): + hRusage = lsf.hRusageArray_getitem(hRusages, i) + cpu_time = float(hRusage.utime) + float(hRusage.stime) + print('HOST: {}, CPU_TIME: {} seconds'.format(hRusage.name, cpu_time)) + + return + +if __name__ == "__main__": + id = input("Enter a running job id:\n") + print_job_cputime(int(id)) From e0be8002ea9df3196f70406080e2ff16e7a7a350 Mon Sep 17 00:00:00 2001 From: xlqiang-learn <61980447+xlqiang-learn@users.noreply.github.com> Date: Thu, 23 Mar 2023 21:21:30 +0800 Subject: [PATCH 02/29] Update lsf.i Add array function for hRusage. --- pythonlsf/lsf.i | 1 + 1 file changed, 1 insertion(+) diff --git a/pythonlsf/lsf.i b/pythonlsf/lsf.i index 9238fb0..5bdae18 100644 --- a/pythonlsf/lsf.i +++ b/pythonlsf/lsf.i @@ -52,6 +52,7 @@ int fclose(FILE *f); %array_functions(guaranteedResourcePoolEnt, guaranteedResourcePoolEntArray) %array_functions(struct rsvInfoEnt, rsvInfoEntArray) %array_functions(struct hostRsvInfoEnt, hostRsvInfoEntArray) +%array_functions(struct hRusage, hRusageArray) //helper function for transforming char** to python list %inline %{ From 57319c10983399a1e9bdc817dfc1c513c6c82a7d Mon Sep 17 00:00:00 2001 From: Xiaolin Qiang <61980447+xlqiang-learn@users.noreply.github.com> Date: Fri, 5 May 2023 18:59:51 +0800 Subject: [PATCH 03/29] Add files via upload --- read_JOB_FINISH_submitExt.py | 67 ++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 read_JOB_FINISH_submitExt.py diff --git a/read_JOB_FINISH_submitExt.py b/read_JOB_FINISH_submitExt.py new file mode 100644 index 0000000..fba8299 --- /dev/null +++ b/read_JOB_FINISH_submitExt.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 + +from pythonlsf import lsf +import sys + +# Define function to get key and pair values from the eventrec.eventLog.jobFinishLog.submitExt +def get_pair_from_submit_ext(submit_ext, id): + if submit_ext is None: + return None + + + values_as_list = lsf.string_array_to_pylist(submit_ext.values, submit_ext.num) + for i in range(submit_ext.num): + key = lsf.intArray_getitem(submit_ext.keys, i) + if key == id: + return values_as_list[i] + + return None + + +def display(eventrec): + """ + display event record, this example to get the user group (-G) value from the JOB_FINISH record + """ + if eventrec.type == lsf.EVENT_JOB_FINISH: + jobid = eventrec.eventLog.jobFinishLog.jobId + fromHost = eventrec.eventLog.jobFinishLog.fromHost + submit_ext=eventrec.eventLog.jobFinishLog.submitExt + userGroup = get_pair_from_submit_ext(submit_ext,lsf.JDATA_EXT_USRGROUP) +# jobgroup = eventrec.eventLog.jobFinishLog.jgroup +# jobgroup = eventrec.eventLog.jobFinishLog.submitExt.values[1075] + print("EVENT_JOB_FINISH jobid<%d>, fromHost<%s>, to jobgroup <%s>" %(jobid, fromHost, userGroup)) + else: + print("event type is %d" %(eventrec.type)) + +def read_eventrec(path): + """ + read lsb.events + """ + lineNum = lsf.new_intp() + lsf.intp_assign(lineNum, 0) + fp = lsf.fopen(path, "r") + if fp is None: + print("The file %s does not exist." % path) + sys.exit(1) + + flag = 1 + + if lsf.lsb_init("test") > 0: + exit(1) + + while flag > 0: + log = lsf.lsb_geteventrec(fp, lineNum) + if log: + display(log) + else: + flag = 0 + + +if __name__ == '__main__': + if len(sys.argv) == 1: + print("Usage: %s full_path_lsb.events_file" % (sys.argv[0])) + sys.exit(0) + + print("LSF Clustername is :", lsf.ls_getclustername()) + #read_eventrec("/opt/lsf8.0.1/work/cluster1/logdir/lsb.events") + read_eventrec(sys.argv[1]) From 8cc363b5c7383e951459809d9ac934b26950b4ca Mon Sep 17 00:00:00 2001 From: Yan Li Date: Wed, 7 Jun 2023 10:01:55 +0800 Subject: [PATCH 04/29] add example code for limcontrol and mbd restart --- examples/limcontrol.py | 18 ++++++++++++++++++ examples/restartMbd.py | 15 +++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 examples/limcontrol.py create mode 100644 examples/restartMbd.py diff --git a/examples/limcontrol.py b/examples/limcontrol.py new file mode 100644 index 0000000..ca5a61e --- /dev/null +++ b/examples/limcontrol.py @@ -0,0 +1,18 @@ +from pythonlsf import lsf + + +# the hostname you want to operate lim running on +host = "your_hostname" + +# set opCode to 1 if you need to reboot lim +#set to 2 if you want to shutdown it +opCode = 1 + +if lsf.lsb_init("test") > 0: + print("failed to initialize") + exit +if lsf.ls_limcontrol(host, opCode) == 0 : + print("host operated successfully") +else : + print("host operated failed") + diff --git a/examples/restartMbd.py b/examples/restartMbd.py new file mode 100644 index 0000000..dc6a29f --- /dev/null +++ b/examples/restartMbd.py @@ -0,0 +1,15 @@ +from pythonlsf import lsf + +req = lsf.mbdCtrlReq() +req.opCode = 0 +req.message = "" +req.name = "mbd" + +if lsf.lsb_init("test") > 0 : + print("failed to initialize") + exit +if lsf.lsb_reconfig(req) == 0 : + print("mbd restarted successfully") +else : + print("failed to restart mbd") + From 809cd065b848cc83b54e301c60eb58ea2984a192 Mon Sep 17 00:00:00 2001 From: Yan Li Date: Wed, 7 Jun 2023 11:15:01 +0800 Subject: [PATCH 05/29] add example of submitting GPU job --- examples/submit_gpu_job.py | 69 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100755 examples/submit_gpu_job.py diff --git a/examples/submit_gpu_job.py b/examples/submit_gpu_job.py new file mode 100755 index 0000000..395843f --- /dev/null +++ b/examples/submit_gpu_job.py @@ -0,0 +1,69 @@ +from pythonlsf import lsf + + +def run_job(command): + """ + Run a job... + """ + submitreq = lsf.submit() + submitreq.command = command + #submitreq.options = 0 + submitreq.options2 = 0 + + limits = [] + for i in range(0, lsf.LSF_RLIM_NLIMITS): + limits.append(lsf.DEFAULT_RLIMIT) + + submitreq.rLimits = limits + + submitreq.beginTime = 0 + submitreq.termTime = 0 + submitreq.numProcessors = 1 + submitreq.maxNumProcessors = 1 + + # below 2 lines section is for using -R rusage to request GPU + #submitreq.resReq = "rusage[ngpus_physical=2:gmodel=K80#12G:nvlink=yes]" + #submitreq.options = lsf.SUB_RES_REQ + + # below section is for using -gpu to request GPU + submit_ext = lsf.submit_ext() + submitreq.options2 = lsf.SUB2_MODIFY_PEND_JOB + submitreq.options4 = lsf.SUB4_GPU_REQ + gpuOpt = {} + gpuOpt[lsf.JDATA_EXT_GPU_NUM] = "2" + gpuOpt[lsf.JDATA_EXT_GPU_MODE] = "3" + #gpuOpt[lsf.JDATA_EXT_GPU_MPS_ENABLE] = "-1" + #gpuOpt[lsf.JDATA_EXT_GPU_JOB_EXCLUSIVE] = "-1" + #gpuOpt[lsf.JDATA_EXT_GPU_MODEL] = "" + #gpuOpt[lsf.JDATA_EXT_GPU_MEM] = "" + #gpuOpt[lsf.JDATA_EXT_GPU_TILE] = "" + #gpuOpt[lsf.JDATA_EXT_GPU_AFFBIND] = "-1" + #gpuOpt[lsf.JDATA_EXT_GPU_BLOCK] = "-1" + #gpuOpt[lsf.JDATA_EXT_GPU_GPACK] = "-1" + #gpuOpt[lsf.JDATA_EXT_GPU_GVENDOR] = "-1" + #gpuOpt[lsf.JDATA_EXT_GPU_RSRC_TYPE] = "0" + #gpuOpt[lsf.JDATA_EXT_GPU_GI_SLICE] = "-1" + #gpuOpt[lsf.JDATA_EXT_GPU_CI_SLICE] = "-1" + + submit_ext.keys = lsf.new_intArray(len(gpuOpt)) + submit_ext.values = lsf.new_stringArray(len(gpuOpt)) + for i in range(len(gpuOpt)): + lsf.intArray_setitem(submit_ext.keys, i, gpuOpt.keys()[i]) + lsf.stringArray_setitem(submit_ext.values, i, gpuOpt.values()[i]) + submit_ext.num = len(gpuOpt) + submitreq.submitExt = submit_ext + print(submitreq.submitExt.num) + + # submit the job request + submitreply = lsf.submitReply() + + if lsf.lsb_init("test") > 0: + exit(1) + + job_id = lsf.lsb_submit(submitreq, submitreply) + return job_id + + +if __name__ == '__main__': + print("LSF Clustername is :", lsf.ls_getclustername()) + print(run_job("/bin/sleep 10")) From 8d27628b5134715a916fe922f40a9d07a6764975 Mon Sep 17 00:00:00 2001 From: Yan Li Date: Fri, 9 Jun 2023 10:58:08 +0800 Subject: [PATCH 06/29] add example for query multiple jobs' info --- examples/get_jobs_info.py | 60 +++++++++++++++++++++++++++++++++++++++ pythonlsf/lsf.i | 14 +++++++++ 2 files changed, 74 insertions(+) create mode 100755 examples/get_jobs_info.py diff --git a/examples/get_jobs_info.py b/examples/get_jobs_info.py new file mode 100755 index 0000000..84aeaea --- /dev/null +++ b/examples/get_jobs_info.py @@ -0,0 +1,60 @@ +from pythonlsf import lsf +import sys + +def get_job_info(list) : + if lsf.lsb_init("test") > 0: + print("failed to initialize") + return + if len(list) == 0 : + print("no valid job id given") + return + print("request below job's info: {}".format(list)) + clusterName = lsf.ls_getclustername() + print("retrieve cluster name : {}".format(clusterName)) + query = "jobIds=(" + for l in list: + query += l +"," + query = query[:-1] + query += ")jobSouceClusterNames=(" + for i in range(len(list)): + query += clusterName + "," + query = query[:-1] + query += ") options=" + str(lsf.ALL_JOB) + " " + + jobQuery = lsf.jobInfoQuery() + jobQuery.nCols = 10 + jobQuery.colIndexs = lsf.buildQueryColIndexs() + + jobQuery.query = query + jobQuery.submitExt = lsf.submit_ext() + more = lsf.new_intp() + print("request: {}".format(jobQuery.query)) + jobInfoPtr = lsf.jobInfoHeadExt() + jobInfoPtr = lsf.lsb_queryjobinfo_ext_2(jobQuery, clusterName) + foundJob = False + if jobInfoPtr != None : + if jobInfoPtr.jobInfoHead != None : + foundJob = True + if not foundJob : + print("faild to query jobs") + else : + print("found job number : {}".format(jobInfoPtr.jobInfoHead.numJobs)) + if jobInfoPtr.jobInfoHead.numJobs > 0 : + job = lsf.jobInfoEnt() + job = lsf.lsb_fetchjobinfo(more, jobQuery.nCols, jobQuery.colIndexs, jobQuery.query) + if job == None: + print("no job found") + while job != None: + print("job <{}> from user ({}) status is {}".format(lsf.lsb_jobid2str(job.jobId),job.user, job.status)) + job = lsf.lsb_fetchjobinfo(more, jobQuery.nCols, jobQuery.colIndexs, jobQuery.query) + + +if __name__ == "__main__": + joblist = [] + if len(sys.argv) < 2: + joblist = ['0'] + else: + for jobid in sys.argv[1:]: + joblist.append(jobid) + joblist = ['3530','3531'] + get_job_info(joblist) diff --git a/pythonlsf/lsf.i b/pythonlsf/lsf.i index ba1e52e..ac6af86 100755 --- a/pythonlsf/lsf.i +++ b/pythonlsf/lsf.i @@ -712,4 +712,18 @@ PyObject * get_pids_from_stream(struct jRusage * jrusage) { return result; } +long * buildQueryColIndexs() { + long * colIndexs = NULL; + int i = 0; + + colIndexs = calloc(113, sizeof(long)); + for (i= 0; i < 113; i++) { + colIndexs[i] = i; + } + colIndexs[1] = 2; + colIndexs[2] = 1; + colIndexs[9] = 10; + return colIndexs; +} + %} From 322936ff07ead51105ded908d7880f550cbed8b5 Mon Sep 17 00:00:00 2001 From: Yan Li Date: Fri, 9 Jun 2023 11:08:35 +0800 Subject: [PATCH 07/29] restore latest change before re-commit --- examples/get_jobs_info.py | 60 --------------------------------------- pythonlsf/lsf.i | 14 --------- 2 files changed, 74 deletions(-) delete mode 100755 examples/get_jobs_info.py diff --git a/examples/get_jobs_info.py b/examples/get_jobs_info.py deleted file mode 100755 index 84aeaea..0000000 --- a/examples/get_jobs_info.py +++ /dev/null @@ -1,60 +0,0 @@ -from pythonlsf import lsf -import sys - -def get_job_info(list) : - if lsf.lsb_init("test") > 0: - print("failed to initialize") - return - if len(list) == 0 : - print("no valid job id given") - return - print("request below job's info: {}".format(list)) - clusterName = lsf.ls_getclustername() - print("retrieve cluster name : {}".format(clusterName)) - query = "jobIds=(" - for l in list: - query += l +"," - query = query[:-1] - query += ")jobSouceClusterNames=(" - for i in range(len(list)): - query += clusterName + "," - query = query[:-1] - query += ") options=" + str(lsf.ALL_JOB) + " " - - jobQuery = lsf.jobInfoQuery() - jobQuery.nCols = 10 - jobQuery.colIndexs = lsf.buildQueryColIndexs() - - jobQuery.query = query - jobQuery.submitExt = lsf.submit_ext() - more = lsf.new_intp() - print("request: {}".format(jobQuery.query)) - jobInfoPtr = lsf.jobInfoHeadExt() - jobInfoPtr = lsf.lsb_queryjobinfo_ext_2(jobQuery, clusterName) - foundJob = False - if jobInfoPtr != None : - if jobInfoPtr.jobInfoHead != None : - foundJob = True - if not foundJob : - print("faild to query jobs") - else : - print("found job number : {}".format(jobInfoPtr.jobInfoHead.numJobs)) - if jobInfoPtr.jobInfoHead.numJobs > 0 : - job = lsf.jobInfoEnt() - job = lsf.lsb_fetchjobinfo(more, jobQuery.nCols, jobQuery.colIndexs, jobQuery.query) - if job == None: - print("no job found") - while job != None: - print("job <{}> from user ({}) status is {}".format(lsf.lsb_jobid2str(job.jobId),job.user, job.status)) - job = lsf.lsb_fetchjobinfo(more, jobQuery.nCols, jobQuery.colIndexs, jobQuery.query) - - -if __name__ == "__main__": - joblist = [] - if len(sys.argv) < 2: - joblist = ['0'] - else: - for jobid in sys.argv[1:]: - joblist.append(jobid) - joblist = ['3530','3531'] - get_job_info(joblist) diff --git a/pythonlsf/lsf.i b/pythonlsf/lsf.i index 490dc4e..5bdae18 100644 --- a/pythonlsf/lsf.i +++ b/pythonlsf/lsf.i @@ -715,20 +715,6 @@ PyObject * get_pids_from_stream(struct jRusage * jrusage) { return result; } -long * buildQueryColIndexs() { - long * colIndexs = NULL; - int i = 0; - - colIndexs = calloc(113, sizeof(long)); - for (i= 0; i < 113; i++) { - colIndexs[i] = i; - } - colIndexs[1] = 2; - colIndexs[2] = 1; - colIndexs[9] = 10; - return colIndexs; -} - PyObject * get_host_info_all() { struct hostInfoEnt *hostinfo; char **hosts = NULL; From a4400da483c82fb082febf1ef690e70165bedd5c Mon Sep 17 00:00:00 2001 From: Yan Li Date: Fri, 9 Jun 2023 11:09:32 +0800 Subject: [PATCH 08/29] add example for query multiple jobs info --- examples/get_jobs_info.py | 60 +++++++++++++++++++++++++++++++++++++++ pythonlsf/lsf.i | 14 +++++++++ 2 files changed, 74 insertions(+) create mode 100755 examples/get_jobs_info.py diff --git a/examples/get_jobs_info.py b/examples/get_jobs_info.py new file mode 100755 index 0000000..84aeaea --- /dev/null +++ b/examples/get_jobs_info.py @@ -0,0 +1,60 @@ +from pythonlsf import lsf +import sys + +def get_job_info(list) : + if lsf.lsb_init("test") > 0: + print("failed to initialize") + return + if len(list) == 0 : + print("no valid job id given") + return + print("request below job's info: {}".format(list)) + clusterName = lsf.ls_getclustername() + print("retrieve cluster name : {}".format(clusterName)) + query = "jobIds=(" + for l in list: + query += l +"," + query = query[:-1] + query += ")jobSouceClusterNames=(" + for i in range(len(list)): + query += clusterName + "," + query = query[:-1] + query += ") options=" + str(lsf.ALL_JOB) + " " + + jobQuery = lsf.jobInfoQuery() + jobQuery.nCols = 10 + jobQuery.colIndexs = lsf.buildQueryColIndexs() + + jobQuery.query = query + jobQuery.submitExt = lsf.submit_ext() + more = lsf.new_intp() + print("request: {}".format(jobQuery.query)) + jobInfoPtr = lsf.jobInfoHeadExt() + jobInfoPtr = lsf.lsb_queryjobinfo_ext_2(jobQuery, clusterName) + foundJob = False + if jobInfoPtr != None : + if jobInfoPtr.jobInfoHead != None : + foundJob = True + if not foundJob : + print("faild to query jobs") + else : + print("found job number : {}".format(jobInfoPtr.jobInfoHead.numJobs)) + if jobInfoPtr.jobInfoHead.numJobs > 0 : + job = lsf.jobInfoEnt() + job = lsf.lsb_fetchjobinfo(more, jobQuery.nCols, jobQuery.colIndexs, jobQuery.query) + if job == None: + print("no job found") + while job != None: + print("job <{}> from user ({}) status is {}".format(lsf.lsb_jobid2str(job.jobId),job.user, job.status)) + job = lsf.lsb_fetchjobinfo(more, jobQuery.nCols, jobQuery.colIndexs, jobQuery.query) + + +if __name__ == "__main__": + joblist = [] + if len(sys.argv) < 2: + joblist = ['0'] + else: + for jobid in sys.argv[1:]: + joblist.append(jobid) + joblist = ['3530','3531'] + get_job_info(joblist) diff --git a/pythonlsf/lsf.i b/pythonlsf/lsf.i index 5bdae18..490dc4e 100644 --- a/pythonlsf/lsf.i +++ b/pythonlsf/lsf.i @@ -715,6 +715,20 @@ PyObject * get_pids_from_stream(struct jRusage * jrusage) { return result; } +long * buildQueryColIndexs() { + long * colIndexs = NULL; + int i = 0; + + colIndexs = calloc(113, sizeof(long)); + for (i= 0; i < 113; i++) { + colIndexs[i] = i; + } + colIndexs[1] = 2; + colIndexs[2] = 1; + colIndexs[9] = 10; + return colIndexs; +} + PyObject * get_host_info_all() { struct hostInfoEnt *hostinfo; char **hosts = NULL; From 2b9c23a6b47429e847da1e6202f8b7ca3fb39ed8 Mon Sep 17 00:00:00 2001 From: Vassilis Vassiliadis Date: Fri, 9 Jun 2023 09:41:21 +0100 Subject: [PATCH 09/29] fix: iterate gpuOpt items when crafting submit_ext in submit_gpu_job.py example Signed-off-by: Vassilis Vassiliadis --- examples/submit_gpu_job.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/submit_gpu_job.py b/examples/submit_gpu_job.py index 395843f..b67ae0e 100755 --- a/examples/submit_gpu_job.py +++ b/examples/submit_gpu_job.py @@ -47,9 +47,9 @@ def run_job(command): submit_ext.keys = lsf.new_intArray(len(gpuOpt)) submit_ext.values = lsf.new_stringArray(len(gpuOpt)) - for i in range(len(gpuOpt)): - lsf.intArray_setitem(submit_ext.keys, i, gpuOpt.keys()[i]) - lsf.stringArray_setitem(submit_ext.values, i, gpuOpt.values()[i]) + for i, (key, value) in enumerate(gpuOpt.items()): + lsf.intArray_setitem(submit_ext.keys, i, key) + lsf.stringArray_setitem(submit_ext.values, i, value) submit_ext.num = len(gpuOpt) submitreq.submitExt = submit_ext print(submitreq.submitExt.num) From 4a1519c6337e1bd63d08e08c7be8594b9bacda6d Mon Sep 17 00:00:00 2001 From: Yan Li Date: Fri, 11 Aug 2023 15:20:13 +0800 Subject: [PATCH 10/29] add example for checking job pending reason get_job_pendreason.py, and enh jobid input for get_jobs_info.py --- examples/get_job_pendreason.py | 106 +++++++++++++++++++++++++++++++++ examples/get_jobs_info.py | 12 ++-- 2 files changed, 113 insertions(+), 5 deletions(-) create mode 100644 examples/get_job_pendreason.py diff --git a/examples/get_job_pendreason.py b/examples/get_job_pendreason.py new file mode 100644 index 0000000..ac6b3b9 --- /dev/null +++ b/examples/get_job_pendreason.py @@ -0,0 +1,106 @@ +from pythonlsf import lsf +import sys + +def showJobStat(stat) : + if stat == "" or stat == lsf.JOB_STAT_NULL : + return "NULL" + elif stat == lsf.JOB_STAT_PEND : + return "PEND" + elif stat == lsf.JOB_STAT_PSUSP : + return "PSUSP" + elif stat == lsf.JOB_STAT_RUN : + return "RUN" + elif stat == lsf.JOB_STAT_SSUSP : + return "SSUSP" + elif stat == lsf.JOB_STAT_USUSP : + return "USUSP" + elif stat == lsf.JOB_STAT_EXIT : + return "EXIT" + elif stat == lsf.JOB_STAT_DONE or stat == (lsf.JOB_STAT_DONE + lsf.JOB_STAT_PDONE) or \ +stat == (lsf.JOB_STAT_DONE + lsf.JOB_STAT_PERR) : + return "DONE" + elif stat == lsf.JOB_STAT_UNKWN : + return "UNKWN" + elif stat == (lsf.JOB_STAT_RUN + lsf.JOB_STAT_PROV) : + return "PROV" + else : + return "ERROR" + + +def get_job_info(list) : + if lsf.lsb_init("test") > 0: + print("failed to initialize") + return + if len(list) > 0 : + print("request below job's info: {}".format(list)) + else : + print("request all job's info") + clusterName = lsf.ls_getclustername() + print("retrieve cluster name : {}".format(clusterName)) + reasonLevel = 3 + more = lsf.new_intp() + jobInfoPtr = lsf.jobInfoHeadExt() + req = lsf.jobInfoReq() + req.reasonLevel = reasonLevel + req.sourceClusterName = clusterName + req.user = "all" + req.options = lsf.ALL_JOB + if len(list) > 1 : + req.jobId = 0 + jobList = "" + cluList = "" + for j in range(len(list)): + jobList += list[j] + "," + cluList += clusterName + "," + req.submitExt = lsf.submit_ext() + submitDict = {} + submitDict[lsf.JDATA_EXT_JOBIDS] = jobList + submitDict[lsf.JDATA_EXT_SOURCECLUSTERS] = cluList + submitDict[lsf.JDATA_EXT_AFFINITYINFO] = "" + submitDict[lsf.JDATA_EXT_DATAINFO] = "" + submitDict[lsf.JDATA_EXT_EST_RESULTS] = "" + submitDict[lsf.JDATA_EXT_APS_DETAIL] = "" + req.submitExt.keys = lsf.new_intArray(6) + req.submitExt.values = lsf.new_stringArray(6) + for p, (key, value) in enumerate(submitDict.items()): + lsf.intArray_setitem(req.submitExt.keys, p, key) + lsf.stringArray_setitem(req.submitExt.values, p, value) + req.submitExt.num = 6 + elif len(list) == 1 : + req.jobId = int(list[0]) + else : + req.jobId = 0 + jobInfoPtr = lsf.lsb_openjobinfo_req(req) + foundJob = False + if jobInfoPtr != None : + if jobInfoPtr.jobInfoHead != None : + foundJob = True + if not foundJob : + print("failed to query jobs") + else : + print("found job number : {}".format(jobInfoPtr.jobInfoHead.numJobs)) + if jobInfoPtr.jobInfoHead.numJobs > 0 : + job = lsf.jobInfoEnt() + job = lsf.lsb_readjobinfo_cond(more, jobInfoPtr); + if job == None: + print("no job found") + while job != None: + if job.status != lsf.JOB_STAT_PEND and job.status != lsf.JOB_STAT_PSUSP : + print("job <{}> from user ({}) status is <{}>".format(lsf.lsb_jobid2str(job.jobId),job.user, showJobStat(job.status))) + else : + pendreasons = lsf.lsb_pendreason_ex(reasonLevel, job, jobInfoPtr.jobInfoHead, job.clusterId) + print("job <{}> from user ({}) pending in status <{}>\n with pending reason:\n {}".format(lsf.lsb_jobid2str(job.jobId),job.user, showJobStat(job.status), pendreasons)) + + job = lsf.lsb_readjobinfo_cond(more, jobInfoPtr); + + +if __name__ == "__main__": + joblist = [] + if len(sys.argv) >= 2: + for jobid in sys.argv[1:]: + joblist.append(jobid) + if len(joblist) > 0 : + print("get job pending reason for: ", joblist) + else : + print("get all job pending reason info") + get_job_info(joblist) diff --git a/examples/get_jobs_info.py b/examples/get_jobs_info.py index 84aeaea..50b54e7 100755 --- a/examples/get_jobs_info.py +++ b/examples/get_jobs_info.py @@ -36,7 +36,7 @@ def get_job_info(list) : if jobInfoPtr.jobInfoHead != None : foundJob = True if not foundJob : - print("faild to query jobs") + print("failed to query jobs") else : print("found job number : {}".format(jobInfoPtr.jobInfoHead.numJobs)) if jobInfoPtr.jobInfoHead.numJobs > 0 : @@ -51,10 +51,12 @@ def get_job_info(list) : if __name__ == "__main__": joblist = [] - if len(sys.argv) < 2: - joblist = ['0'] - else: + if len(sys.argv) >= 2: for jobid in sys.argv[1:]: joblist.append(jobid) - joblist = ['3530','3531'] + if len(joblist) == 0 : + print("usage: \n {} job1\n {} job1 job2 job3 ...\n".format(sys.argv[0],sys.argv[0])) + exit() + print("get job info for: ", joblist) get_job_info(joblist) + From c33408fb1e78e162253fc0d56cef44866f6106ee Mon Sep 17 00:00:00 2001 From: Yan Li Date: Thu, 17 Aug 2023 11:14:05 +0800 Subject: [PATCH 11/29] add example for restart local sbd --- examples/restartSBD.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100755 examples/restartSBD.py diff --git a/examples/restartSBD.py b/examples/restartSBD.py new file mode 100755 index 0000000..d1a6462 --- /dev/null +++ b/examples/restartSBD.py @@ -0,0 +1,42 @@ +from pythonlsf import lsf +import sys + + +def restartSBD(opCode, message, hosts) : + if lsf.lsb_init("test") > 0: + print("failed to initialize") + return + req = lsf.hostCtrlReq() + req.opCode = opCode + req.message = message + if len(hosts) > 0 : + for h in hosts: + print("restarting sbatchd daemon on host <{}> ...".format(h)) + req.host = h + cc = lsf.lsb_hostcontrol(req) + if cc == 0 : + print("sbatchd daemon restarted successfully.") + elif cc == -1 : + print("ERROR: sbatchd daemon failed to restart.") + else : + print("ERROR: return {} while trying to restart sbatchd.".format(cc)) + else : + print("restarting sbatchd daemon on local host ...") + cc = lsf.lsb_hostcontrol(req) + if cc == 0 : + print("sbatchd daemon restarted successfully.") + elif cc == -1 : + print("ERROR: sbatchd daemon failed to restart.") + else : + print("ERROR: return {} while trying to restart sbatchd.".format(cc)) + + +if __name__ == "__main__": + opCode = lsf.HOST_REBOOT + message = "reboot according to python-api" + if len(sys.argv) > 1 : + hosts = sys.argv[1:] + else : + hosts = [] + restartSBD(opCode, message, hosts) + From a392c837a0a5823c903461c4f3ade42912536e55 Mon Sep 17 00:00:00 2001 From: Yan Li Date: Mon, 11 Sep 2023 17:26:09 +0800 Subject: [PATCH 12/29] add example for restarting RES daemons on specified 1 or multiple hosts --- examples/restartRES.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 examples/restartRES.py diff --git a/examples/restartRES.py b/examples/restartRES.py new file mode 100644 index 0000000..7556a3b --- /dev/null +++ b/examples/restartRES.py @@ -0,0 +1,28 @@ +import sys,socket +from pythonlsf import lsf + +def restartres(hostlist): + if lsf.ls_initdebug("resrestart") < 0: + print("ls_initdebug failed!") + return -1 + num_port = len(hostlist) * 2 + if lsf.ls_initrex(num_port, 0) < num_port : + lsf.ls_perror("ls_initrex") + return -1 + for host in hostlist: + rc=lsf.ls_rescontrol(host, lsf.RES_CMD_REBOOT, 0) + + if rc < 0: + lsf.ls_perror("lsf.ls_rescontrol") + print("failed restart res on {}".format(host)) + else : + print("res on {} restarted".format(host)) + return + +if __name__ == '__main__': + if len(sys.argv) > 1 : + hostlist = sys.argv[1:] + else : + hostlist = [socket.gethostname()] + restartres(hostlist) + From 5781f798aee582f2626602d73b10855d3fa35705 Mon Sep 17 00:00:00 2001 From: Xiaolin Qiang <61980447+xlqiang-learn@users.noreply.github.com> Date: Mon, 22 Jan 2024 10:35:25 +0800 Subject: [PATCH 13/29] Add array function for hostInfoArray Add an array function for hostInfoArray to retrieve information from ls_gethostinfo. --- pythonlsf/lsf.i | 1 + 1 file changed, 1 insertion(+) diff --git a/pythonlsf/lsf.i b/pythonlsf/lsf.i index 490dc4e..3c36f2c 100644 --- a/pythonlsf/lsf.i +++ b/pythonlsf/lsf.i @@ -53,6 +53,7 @@ int fclose(FILE *f); %array_functions(struct rsvInfoEnt, rsvInfoEntArray) %array_functions(struct hostRsvInfoEnt, hostRsvInfoEntArray) %array_functions(struct hRusage, hRusageArray) +%array_functions(struct hostInfo, hostInfoArray) //helper function for transforming char** to python list %inline %{ From 79e81c40ad66c6181fc1fcfe7246f70c09619e46 Mon Sep 17 00:00:00 2001 From: Xiaolin Qiang <61980447+xlqiang-learn@users.noreply.github.com> Date: Mon, 29 Jan 2024 21:37:52 +0800 Subject: [PATCH 14/29] Update get_job_pendreason.py Correct the field from req.user to req.userName at line 46. --- examples/get_job_pendreason.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/get_job_pendreason.py b/examples/get_job_pendreason.py index ac6b3b9..a1319e9 100644 --- a/examples/get_job_pendreason.py +++ b/examples/get_job_pendreason.py @@ -43,7 +43,7 @@ def get_job_info(list) : req = lsf.jobInfoReq() req.reasonLevel = reasonLevel req.sourceClusterName = clusterName - req.user = "all" + req.userName = "all" req.options = lsf.ALL_JOB if len(list) > 1 : req.jobId = 0 From 86ff69ca58da397c54839ad4ed3ae1406dbef541 Mon Sep 17 00:00:00 2001 From: Andretility <159141307+Andretility@users.noreply.github.com> Date: Tue, 6 Feb 2024 16:48:30 +0100 Subject: [PATCH 15/29] Added 0 to open('{}/lsf.conf'.... to prevent ValueError: zero length field name in format [lsf-python-api]# python setup.py build Traceback (most recent call last): File "setup.py", line 81, in set_gccflag_lsf_version() File "setup.py", line 54, in set_gccflag_lsf_version with open('{}/lsf.conf'.format(_lsf_envdir), 'r') as f: ValueError: zero length field name in format --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9650790..ef5e503 100755 --- a/setup.py +++ b/setup.py @@ -51,7 +51,7 @@ def is_keyvalue_defined(lsbatch_h_path): def set_gccflag_lsf_version(): global gccflag_lsfversion _lsf_envdir = os.environ['LSF_ENVDIR'] - with open('{}/lsf.conf'.format(_lsf_envdir), 'r') as f: + with open('{0}/lsf.conf'.format(_lsf_envdir), 'r') as f: _lsf_version = re.search('LSF_VERSION=(.*)', f.read()).group(1).strip() if _lsf_version == '10.1' : gccflag_lsfversion= '-DLSF_VERSION_101' From e4d424a80d0621a7a3737a1d9addf11210a88713 Mon Sep 17 00:00:00 2001 From: Xiaolin Qiang <61980447+xlqiang-learn@users.noreply.github.com> Date: Thu, 7 Mar 2024 07:44:20 +0800 Subject: [PATCH 16/29] Aadd an example for bsub -pack --- examples/pack_submit.py | 51 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 examples/pack_submit.py diff --git a/examples/pack_submit.py b/examples/pack_submit.py new file mode 100644 index 0000000..e05956c --- /dev/null +++ b/examples/pack_submit.py @@ -0,0 +1,51 @@ +from pythonlsf import lsf + +def sub_pack_job(): + + + limits1 = [] + for i in range(0, lsf.LSF_RLIM_NLIMITS): + limits1.append(lsf.DEFAULT_RLIMIT) + + submitreq1 = lsf.submit() + submitreq1.command = "sleep 10" + submitreq1.options = 0 + submitreq1.options2 = 0 + submitreq1.rLimits = limits1 + + limits2 = [] + for i in range(0, lsf.LSF_RLIM_NLIMITS): + limits2.append(lsf.DEFAULT_RLIMIT) + + submitreq2 = lsf.submit() + submitreq2.command = "sleep 20" + submitreq2.options = 0 + submitreq2.options2 = 0 + submitreq2.rLimits = limits2 + + + pack_submitreq = lsf.packSubmit() + pack_submitreq.num = 2 + submits = lsf.new_submitArray(2) + lsf.submitArray_setitem(submits, 0, submitreq1) + lsf.submitArray_setitem(submits, 1, submitreq2) + pack_submitreq.reqs = submits + + pack_submitreply = lsf.packSubmitReply() + intp_acceptedNum = lsf.copy_intp(0) + intp_rejectedNum = lsf.copy_intp(0) + + + if lsf.lsb_init("test") > 0: + exit(1) + + result = lsf.lsb_submitPack(pack_submitreq, pack_submitreply, intp_acceptedNum, intp_rejectedNum) + print(result) + print(lsf.intp_value(intp_acceptedNum)) + print(lsf.intp_value(intp_rejectedNum)) + + return result + +if __name__ == '__main__': + print("LSF Clustername is :", lsf.ls_getclustername()) + sub_pack_job() From e92ec03bae5b455059de9fb1f3d2b511e65c5b2c Mon Sep 17 00:00:00 2001 From: Xiaolin Qiang <61980447+xlqiang-learn@users.noreply.github.com> Date: Thu, 7 Mar 2024 07:47:53 +0800 Subject: [PATCH 17/29] Add an array funciton of submit * --- pythonlsf/lsf.i | 1 + 1 file changed, 1 insertion(+) diff --git a/pythonlsf/lsf.i b/pythonlsf/lsf.i index 3c36f2c..d380025 100644 --- a/pythonlsf/lsf.i +++ b/pythonlsf/lsf.i @@ -54,6 +54,7 @@ int fclose(FILE *f); %array_functions(struct hostRsvInfoEnt, hostRsvInfoEntArray) %array_functions(struct hRusage, hRusageArray) %array_functions(struct hostInfo, hostInfoArray) +%array_functions(struct submit*, submitArray) //helper function for transforming char** to python list %inline %{ From 36e6f61afe9567e63d2d0c081397fa5d8e0afcb4 Mon Sep 17 00:00:00 2001 From: Yan Li Date: Tue, 26 Mar 2024 17:28:15 +0800 Subject: [PATCH 18/29] update readme and steup.py to support python3.12 --- README.md | 9 +++++++-- setup.py | 5 ++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bfccf47..63c9875 100755 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Please note you must use lsf.lsb_init before any other LSBLIB library routine in Supported operating systems: Linux 2.6 glibc 2.3 x86 64 bit: RHEL 6.2, RHEL6.4, RHEL6.5, RHEL6.8 - Linux 3.10 glibc 2.17 x86 64 bit: Red Hat 7.4, 7.5 + Linux 3.10 glibc 2.17 x86 64 bit: Red Hat 7.4, 7.5, 8.8, 8.9 Linux for Power Systems Servers 8 Little Endian (Linux 3.10, glibc 2.17): RHEL 7.4 Linux for Power Systems Servers 9 Little Endian (Linux 4.14, glibc 2.17): RHEL 7.5 @@ -58,7 +58,7 @@ Please note you must use lsf.lsb_init before any other LSBLIB library routine in Python2 and Python3 are all supported. The following versions are tested: - Python 2.6.6, 2.7.15, 3.0, 3.6.0, 3.7.0 + Python 2.6.6, 2.7.15, 3.0, 3.6.0, 3.7.0, 3.12.2 ## Compatibility @@ -74,6 +74,10 @@ Before compiling the library, set the LSF environment variables: `$ source profile.lsf` +If using python version higher than 3.10, distutils has been deprecated, install setuptools: + +`$ pip3 install setuptools` + To compile and install the library, go to the main source directory and type: @@ -90,6 +94,7 @@ or `$ python3 setup.py bdist_rpm` Resulting RPMs will be placed in the dist directory + ## Release Notes ### Release 1.0.6 diff --git a/setup.py b/setup.py index ef5e503..57992b7 100755 --- a/setup.py +++ b/setup.py @@ -8,7 +8,10 @@ # import os, sys, re import time -from distutils.core import setup, Extension +if sys.version_info >= (3,10): + from setuptools import setup, Extension +else: + from distutils.core import setup, Extension from distutils.command.bdist_rpm import bdist_rpm from distutils.command.install import INSTALL_SCHEMES From 32f498fda26bd8c980aa415ad0308423ccfbce8a Mon Sep 17 00:00:00 2001 From: Yan Li Date: Tue, 26 Mar 2024 18:15:09 +0800 Subject: [PATCH 19/29] update readme for installing SWIG3.0.12 in pip install --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 63c9875..19ef0c7 100755 --- a/README.md +++ b/README.md @@ -48,10 +48,13 @@ Please note you must use lsf.lsb_init before any other LSBLIB library routine in - SWIG - SWIG version 2.0, or later + SWIG version 2.0, or 3.0 The following versions are tested: SWIG: 2.0.10, 3.0.12 + + If isntalling SWIG with pip or pip3, please specify 3.0.12 version. + `$ pip3 install swig==3.0.12` - Python From adc66fee13c5f7fbd51083c21e6b58acaf055574 Mon Sep 17 00:00:00 2001 From: Peter Kang Date: Tue, 25 Feb 2025 10:16:11 -0800 Subject: [PATCH 20/29] add str2GpuJobData --- pythonlsf/lsf.i | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pythonlsf/lsf.i b/pythonlsf/lsf.i index d380025..25253a4 100644 --- a/pythonlsf/lsf.i +++ b/pythonlsf/lsf.i @@ -25,6 +25,7 @@ int fclose(FILE *f); #include "lsf.h" #include "lsbatch.h" #include "lib.table.h" +extern struct gpuJobData* str2GpuJobData(char *str); %} %pointer_functions(int, intp) @@ -47,6 +48,10 @@ int fclose(FILE *f); %array_functions(struct shareAcctInfoEnt, shareAcctInfoEntArray) #ifdef LSF_VERSION_101 %array_functions(struct gpuRusage, gpuRusageArray) +%array_functions(struct gpuJobHostData, gpuJobHostDataArray) +%array_functions(struct gpuTaskData, gpuTaskDataArray) +%array_functions(struct gpuData *, gpuDataArray) +%array_functions(struct migData, migDataArray) #endif %array_functions(LS_LONG_INT, LS_LONG_INTArray) %array_functions(guaranteedResourcePoolEnt, guaranteedResourcePoolEntArray) @@ -703,6 +708,9 @@ int get_lsb_errno() { char * get_lsb_sysmsg() { return lsb_sysmsg(); } +struct gpuJobData* get_str2GpuJobData(char *str) { + return str2GpuJobData(str); +} PyObject * get_pids_from_stream(struct jRusage * jrusage) { struct pidInfo *pidInfo; From 34e5b2dfbe3cd74bb4ad39b44f226b81ae6fc049 Mon Sep 17 00:00:00 2001 From: Yu-Ching Chen Date: Tue, 29 Apr 2025 08:37:40 -0700 Subject: [PATCH 21/29] 1335 update for compression and off_t type --- pythonlsf/Makefile | 2 +- pythonlsf/lsf.i | 1 + setup.py | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pythonlsf/Makefile b/pythonlsf/Makefile index b48b893..7df6577 100755 --- a/pythonlsf/Makefile +++ b/pythonlsf/Makefile @@ -14,7 +14,7 @@ PROJECT = _lsf.so OBJECTS = lsf_wrap.o CFLAGS = -m64 -fPIC -I$(PYTHON_INCLUDE) -I$(LSF_INCLUDE) -LDFLAGS = $(LSF_LIBDIR)/liblsf.a $(LSF_LIBDIR)/libbat.a $(LSF_LIBDIR)/libfairshareadjust.so $(LSF_LIBDIR)/liblsbstream.so -lc -lnsl +LDFLAGS = $(LSF_LIBDIR)/liblsf.a $(LSF_LIBDIR)/libbat.a $(LSF_LIBDIR)/libfairshareadjust.so $(LSF_LIBDIR)/liblsbstream.so -lc -lnsl -lz all: $(PROJECT) diff --git a/pythonlsf/lsf.i b/pythonlsf/lsf.i index 25253a4..18771e6 100644 --- a/pythonlsf/lsf.i +++ b/pythonlsf/lsf.i @@ -27,6 +27,7 @@ int fclose(FILE *f); #include "lib.table.h" extern struct gpuJobData* str2GpuJobData(char *str); %} +typedef long off_t; %pointer_functions(int, intp) %pointer_functions(float, floatp) diff --git a/setup.py b/setup.py index 57992b7..1476f9c 100755 --- a/setup.py +++ b/setup.py @@ -66,7 +66,7 @@ def set_gccflag_lsf_version(): xlc_path = os.path.join(path, 'xlc') if os.access(xlc_path, os.F_OK): found_xlc = True - os.environ["LDSHARED"] = "%s -pthread -shared -Wl,-z,relro" % xlc_path + os.environ["LDSHARED"] = "%s -pthread -shared -Wl,-z,-lz,relro" % xlc_path break if found_xlc == False: print(''' @@ -90,11 +90,11 @@ def set_gccflag_lsf_version(): if os.access(LSF_LIBDIR + "/liblsbstream.a", os.F_OK): lsf_static_lib = [ LSF_LIBDIR + '/liblsbstream.a'] - lsf_dynamic_lib = ['c', 'nsl', 'rt'] + lsf_dynamic_lib = ['c', 'nsl', 'rt', 'z'] warning_msg = "" else: lsf_static_lib = [] - lsf_dynamic_lib = ['c', 'nsl', 'lsbstream', 'lsf', 'bat', 'rt'] + lsf_dynamic_lib = ['c', 'nsl', 'lsbstream', 'lsf', 'bat', 'rt', 'z'] warning_msg = ''' Warning: The compatibility of the LSF Python API package is not guaranteed if you update LSF at a later time. This is because your current From 0f1e25ba5206cad5fbc2eb87680b71a6033e6ec9 Mon Sep 17 00:00:00 2001 From: Yu-Ching Chen Date: Tue, 29 Apr 2025 09:50:44 -0700 Subject: [PATCH 22/29] add RUN_RUSAGE for readstream.py with pid and pgids --- examples/readstream.py | 12 +++++++++++- pythonlsf/lsf.i | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/examples/readstream.py b/examples/readstream.py index 5ba9b57..60a4722 100644 --- a/examples/readstream.py +++ b/examples/readstream.py @@ -27,7 +27,17 @@ def display(eventrec): for i in range(0,numhosts): hoststr += lsf.stringArray_getitem(exechosts, i) + "" print("EVENT_JOB_FORCE jobid<%d>, execHost<%s>, username<%s>" %(jobid, hoststr, username)) - + elif eventrec.type == lsf.EVENT_JOB_RUN_RUSAGE: + jobid = eventrec.eventLog.jobRunRusageLog.jobid; + numpgids = eventrec.eventLog.jobRunRusageLog.jrusage.npgids; + for i in range(0,numpgids): + pgids = str(lsf.intArray_getitem(eventrec.eventLog.jobRunRusageLog.jrusage.pgid, i)) + " " + pgids = pgids[:-1] + numpids = eventrec.eventLog.jobRunRusageLog.jrusage.npids; + for i in range(0,numpids): + pids = str(lsf.pidInfoArray_getitem(eventrec.eventLog.jobRunRusageLog.jrusage.pidInfo, i).pid) + " "; + pids = pids[:-1] + print("EVENT_JOB_RUN_RUSAGE jobid<%d> pgids<%s> pids<%s>" %(jobid, pgids, pids)) else: print("event type is %d" %(eventrec.type)) diff --git a/pythonlsf/lsf.i b/pythonlsf/lsf.i index 18771e6..859437a 100644 --- a/pythonlsf/lsf.i +++ b/pythonlsf/lsf.i @@ -53,6 +53,7 @@ typedef long off_t; %array_functions(struct gpuTaskData, gpuTaskDataArray) %array_functions(struct gpuData *, gpuDataArray) %array_functions(struct migData, migDataArray) +%array_functions(struct pidInfo, pidInfoArray) #endif %array_functions(LS_LONG_INT, LS_LONG_INTArray) %array_functions(guaranteedResourcePoolEnt, guaranteedResourcePoolEntArray) From cb3321d2a072d88685cec3fb5bfa17f02eee0a17 Mon Sep 17 00:00:00 2001 From: Yu-Ching Chen Date: Wed, 6 Aug 2025 08:15:44 -0700 Subject: [PATCH 23/29] change build to be platform agnostic --- pythonlsf/Makefile | 2 +- setup.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pythonlsf/Makefile b/pythonlsf/Makefile index b48b893..1e19d9b 100755 --- a/pythonlsf/Makefile +++ b/pythonlsf/Makefile @@ -13,7 +13,7 @@ LSF_INCLUDE = $(LSF_LIBDIR)/../../include/lsf/ PROJECT = _lsf.so OBJECTS = lsf_wrap.o -CFLAGS = -m64 -fPIC -I$(PYTHON_INCLUDE) -I$(LSF_INCLUDE) +CFLAGS = -fPIC -I$(PYTHON_INCLUDE) -I$(LSF_INCLUDE) LDFLAGS = $(LSF_LIBDIR)/liblsf.a $(LSF_LIBDIR)/libbat.a $(LSF_LIBDIR)/libfairshareadjust.so $(LSF_LIBDIR)/liblsbstream.so -lc -lnsl all: $(PROJECT) diff --git a/setup.py b/setup.py index 57992b7..2f1ef6b 100755 --- a/setup.py +++ b/setup.py @@ -134,14 +134,13 @@ def set_gccflag_lsf_version(): # '-DLSF_SIMULATOR', '-DOS_HAS_THREAD -D_REENTRANT', gccflag_keyvaluet, gccflag_lsfversion], - extra_compile_args=['-m64', + extra_compile_args=[ '-I' + LSF_LIBDIR + '/../../include/lsf/', '-Wno-strict-prototypes', gccflag_keyvaluet, gccflag_lsfversion, '-DOS_HAS_THREAD -D_REENTRANT', #For multi-thread lib, lserrno '-Wp,-U_FORTIFY_SOURCE', #The flag needs -O option. Undefine it for warning. '-O0'], - extra_link_args=['-m64'], extra_objects=lsf_static_lib, libraries=lsf_dynamic_lib)], py_modules=['pythonlsf.lsf'], From 194891726fab351783b389220f07c4b8700a3abb Mon Sep 17 00:00:00 2001 From: Peter Kang Date: Tue, 14 Oct 2025 07:02:55 -0700 Subject: [PATCH 24/29] add hostGpuInfoArray, hostGpuInfoArray, hostGpuLoadArray --- examples/disp_gpu_host.py | 23 +++++++++++++++++++++++ pythonlsf/lsf.i | 5 +++++ 2 files changed, 28 insertions(+) create mode 100644 examples/disp_gpu_host.py diff --git a/examples/disp_gpu_host.py b/examples/disp_gpu_host.py new file mode 100644 index 0000000..c39cf97 --- /dev/null +++ b/examples/disp_gpu_host.py @@ -0,0 +1,23 @@ +from pythonlsf import lsf +if __name__ == '__main__': + if lsf.lsb_init("test") > 0: + exit -1; + num_hosts = lsf.new_intp() + lsf.intp_assign(num_hosts, 0) + hosts = lsf.ls_gethostgpuinfo(None, num_hosts, None, 0, 0) + hostGpuInfos = lsf.hostGpuInfoArray_frompointer(hosts) + for i in range(0, lsf.intp_value(num_hosts)): + hostGpuInfo = hostGpuInfos[i] + print("Host: {}". format(hostGpuInfo.hostName)) + numGpus = hostGpuInfos[i].gpuC + if numGpus <= 0: + continue + gpuAttrData = hostGpuInfo.gpuAttrV + gpuLoadData = hostGpuInfo.gpuLoadV + gpuAttrs = lsf.hostGpuAttrArray_frompointer(gpuAttrData) + gpuLoads = lsf.hostGpuLoadArray_frompointer(gpuLoadData) + print(" gBrand gModel gBusId gMode gUsedMem gStatus") + for j in range(0, numGpus): + print(" {} {} {} {} {} {} ". \ + format(gpuAttrs[j].gBrand, gpuAttrs[j].gModel, gpuAttrs[j].gBusId, + gpuLoads[j].gMode, gpuLoads[j].gUsedMem, gpuLoads[j].gStatus)) diff --git a/pythonlsf/lsf.i b/pythonlsf/lsf.i index 859437a..9052da7 100644 --- a/pythonlsf/lsf.i +++ b/pythonlsf/lsf.i @@ -200,6 +200,11 @@ static void stringArray_setitem(char * *ary, size_t index, char * value) { %array_class(struct _limitItem, limitItemArray) %array_class(struct _limitConsumer, limitConsumerArray) %array_class(struct _limitResource, limitResourceArray) +#ifdef LSF_VERSION_101 +%array_class(struct hostGpuInfo, hostGpuInfoArray) +%array_class(struct hostGpuAttr, hostGpuAttrArray) +%array_class(struct hostGpuLoad, hostGpuLoadArray) +#endif // handle int arrays %typemap(in) int [ANY] (int temp[$1_dim0]) { From 5addfaa00f899e00859c30f02c19b507b5ae93ba Mon Sep 17 00:00:00 2001 From: Peter Kang Date: Wed, 18 Feb 2026 11:51:39 -0800 Subject: [PATCH 25/29] set_limit_filter_flag() added for lsb_limitInfo() --- examples/disp_limit.py | 1 + pythonlsf/lsf.i | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/examples/disp_limit.py b/examples/disp_limit.py index c7bcead..ab715e8 100644 --- a/examples/disp_limit.py +++ b/examples/disp_limit.py @@ -28,4 +28,5 @@ def printLimit(): if __name__ == '__main__': print("LSF Clustername is : {}".format(lsf.ls_getclustername())) + lsf.set_limit_filter_flag(True) printLimit() diff --git a/pythonlsf/lsf.i b/pythonlsf/lsf.i index 9052da7..a18028d 100644 --- a/pythonlsf/lsf.i +++ b/pythonlsf/lsf.i @@ -26,6 +26,8 @@ int fclose(FILE *f); #include "lsbatch.h" #include "lib.table.h" extern struct gpuJobData* str2GpuJobData(char *str); +typedef int bool_t; +extern void setbConfigInfoFlag4Lib(bool_t bConfigInfo); %} typedef long off_t; @@ -247,6 +249,16 @@ static void stringArray_setitem(char * *ary, size_t index, char * value) { $1 = 0; } +%typemap(in) bool_t { + if (PyBool_Check($input)) { + $1 = (bool_t)(($input == Py_True) ? 1 : 0); + } else if (PyLong_Check($input)) { + $1 = (bool_t)PyLong_AsLong($input); + } else { + $1 = (bool_t)0; + } +} + /* The following routines are not wrapped because SWIG has issues generating proper code for them @@ -765,4 +777,8 @@ PyObject * get_host_info_all() { return result; } +void set_limit_filter_flag(bool_t bConfigInfo) { + setbConfigInfoFlag4Lib(bConfigInfo); +} + %} From e808d4ce1f87646f19b721d51261f10d35268834 Mon Sep 17 00:00:00 2001 From: Peter Kang Date: Thu, 26 Feb 2026 05:42:01 -0800 Subject: [PATCH 26/29] move *Array_frompointer() to frompointer() --- examples/disp_gpu.py | 5 ++++- examples/disp_gpu_host.py | 10 +++++++--- examples/disp_limit_all.py | 9 ++++++--- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/examples/disp_gpu.py b/examples/disp_gpu.py index 096d711..892e1e2 100644 --- a/examples/disp_gpu.py +++ b/examples/disp_gpu.py @@ -7,11 +7,14 @@ num_hosts = lsf.new_intp() lsf.intp_assign(num_hosts, 0) host_data = lsf.lsb_hostinfo_ex(host_names, num_hosts, "", 0) - all_host_data = lsf.hostInfoEntArray_frompointer(host_data) + all_host_data = lsf.hostInfoEntArray(lsf.intp_value(num_hosts)) + all_host_data = all_host_data.frompointer(host_data) for i in range(0, lsf.intp_value(num_hosts)): hostname = all_host_data[i].host print(hostname) gpudata = all_host_data[i].gpuData + if gpudata is None: + continue print("ngpus avail_shared_gpus avail_excl_gpus") print(" {} {} {}". \ format(gpudata.ngpus, gpudata.avail_shared_ngpus, gpudata.avail_excl_ngpus)) diff --git a/examples/disp_gpu_host.py b/examples/disp_gpu_host.py index c39cf97..cae2e3c 100644 --- a/examples/disp_gpu_host.py +++ b/examples/disp_gpu_host.py @@ -5,7 +5,9 @@ num_hosts = lsf.new_intp() lsf.intp_assign(num_hosts, 0) hosts = lsf.ls_gethostgpuinfo(None, num_hosts, None, 0, 0) - hostGpuInfos = lsf.hostGpuInfoArray_frompointer(hosts) + + hostGpuInfos = lsf.hostGpuInfoArray(lsf.intp_value(num_hosts)) + hostGpuInfos = hostGpuInfos.frompointer(hosts) for i in range(0, lsf.intp_value(num_hosts)): hostGpuInfo = hostGpuInfos[i] print("Host: {}". format(hostGpuInfo.hostName)) @@ -14,8 +16,10 @@ continue gpuAttrData = hostGpuInfo.gpuAttrV gpuLoadData = hostGpuInfo.gpuLoadV - gpuAttrs = lsf.hostGpuAttrArray_frompointer(gpuAttrData) - gpuLoads = lsf.hostGpuLoadArray_frompointer(gpuLoadData) + gpuAttrs = lsf.hostGpuAttrArray(numGpus) + gpuAttrs = gpuAttrs.frompointer(gpuAttrData) + gpuLoads = lsf.hostGpuLoadArray(numGpus) + gpuLoads = gpuLoads.frompointer(gpuLoadData) print(" gBrand gModel gBusId gMode gUsedMem gStatus") for j in range(0, numGpus): print(" {} {} {} {} {} {} ". \ diff --git a/examples/disp_limit_all.py b/examples/disp_limit_all.py index cc2fa2a..56ea773 100644 --- a/examples/disp_limit_all.py +++ b/examples/disp_limit_all.py @@ -3,12 +3,14 @@ def printLimitItem(name, item): print(name+' :') print(' consumerC : {}'.format(item.consumerC)) - consumers = lsf.limitConsumerArray_frompointer(item.consumerV) + consumers = lsf.limitConsumerArray(item.consumerC) + consumers = consumers.frompointer(item.consumerV) for j in range (item.consumerC) : print(' [{}] type : {}'.format(j, consumers[j].type)) print(' [{}] name : {}'.format(j, consumers[j].name)) print(' resourceC : {}'.format(item.resourceC)) - resources = lsf.limitResourceArray_frompointer(item.resourceV) + resources = lsf.limitResourceArray(item.resourceC) + resources= resources.frompointer(item.resourceV) for j in range (item.resourceC) : print(' [{}] name : {}'.format(j, resources[j].name)) print(' [{}] type : {}'.format(j, resources[j].type)) @@ -47,7 +49,8 @@ def printLimit(): # print usageC in the limit print('usageC : {}'.format(ent.usageC)) # print usageInfo in the limit - all_usageInfo = lsf.limitItemArray_frompointer(ent.usageInfo) + all_usageInfo = lsf.limitItemArray(ent.usageC) + all_usageInfo = all_usageInfo.frompointer(ent.usageInfo) for j in range (ent.usageC) : printLimitItem('usageInfo', all_usageInfo[j]) From bb5ef1b306048f49dbd3d99f595cc127c7bed956 Mon Sep 17 00:00:00 2001 From: Rob Davies Date: Tue, 21 Jan 2025 13:39:27 +0000 Subject: [PATCH 27/29] Added build wrapper so build_ext runs before build_py SWIG, run by build_ext, generates a shared object file and a python library. The latter needs to be generated before build_py is run so that build_py finds it. Unfortunately the default in setuptools is to run build_py before build_ext. The solution is to override the build class so that the build order can be changed. With this change, "pip install" works better, including when lsf-python-api is a dependency of another module. See: https://bugs.python.org/issue2624 https://bugs.python.org/issue1016626 https://stackoverflow.com/questions/12491328/python-distutils-not-include-the-swig-generated-module https://stackoverflow.com/questions/50239473/building-a-module-with-setuptools-and-swig Signed-off-by: Robert Davies --- setup.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3e6d6bc..b3da42e 100755 --- a/setup.py +++ b/setup.py @@ -13,6 +13,7 @@ else: from distutils.core import setup, Extension from distutils.command.bdist_rpm import bdist_rpm +from distutils.command.build import build from distutils.command.install import INSTALL_SCHEMES class bdist_rpm_custom(bdist_rpm): @@ -28,6 +29,21 @@ def finalize_package_data (self): self.no_autoreq = 1 bdist_rpm.finalize_package_data(self) +# Build extensions before python modules, so the generated pythonlsf/lsf.py +# file is created before attempting to install it. This makes building with +# "pip" easier. +# See: +# https://bugs.python.org/issue2624 +# https://bugs.python.org/issue1016626 +# https://stackoverflow.com/questions/12491328/python-distutils-not-include-the-swig-generated-module +# https://stackoverflow.com/questions/50239473/building-a-module-with-setuptools-and-swig + +class build_ext_first(build): + def finalize_options(self): + super().finalize_options() + new_order = list(filter(lambda x: x[0] == 'build_ext', self.sub_commands)) + list(filter(lambda x: x[0] != 'build_ext', self.sub_commands)) + self.sub_commands[:] = new_order + def get_lsf_libdir(): try: _lsf_envdir = os.environ['LSF_ENVDIR'] @@ -144,7 +160,8 @@ def set_gccflag_lsf_version(): extra_objects=lsf_static_lib, libraries=lsf_dynamic_lib)], py_modules=['pythonlsf.lsf'], - cmdclass = { 'bdist_rpm': bdist_rpm_custom }, + cmdclass = { 'bdist_rpm': bdist_rpm_custom, + 'build': build_ext_first }, classifiers=["Development Status :: 2 - Pre-Alpha", "License :: OSI Approved :: Eclipse Public License", "Operating System :: OS Independent", From 600ef95891dfde3e9fc4aeefce9e8a807c2e5acd Mon Sep 17 00:00:00 2001 From: philip r brenan Date: Tue, 19 Aug 2025 23:16:33 +0100 Subject: [PATCH 28/29] Update README.md Corrected spelling of development --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 19ef0c7..3f744f4 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # LSF Python API -These python wrappers allow customers to submit and control jobs and obtain status of queues, hosts and other LSF attributes from Python directly. They work with various versions of LSF and are maintained by LSF developement, though we take contributions from the Open Source community. +These python wrappers allow customers to submit and control jobs and obtain status of queues, hosts and other LSF attributes from Python directly. They work with various versions of LSF and are maintained by LSF development, though we take contributions from the Open Source community. If you plan or would like to contribute to the library, you must follow the DCO process in the attached [DCO Readme file](https://github.com/IBMSpectrumComputing/platform-python-lsf-api/blob/master/IBMDCO.md) in the root of this repository. It essentially requires you to provide a Sign Off line in the notes of your pull request stating that the work is clear of infinging work by others. Again, for more details, please see the DCO Readme file. @@ -179,3 +179,4 @@ IBM(R), the IBM logo and ibm.com(R) are trademarks of International Business Mac registered in many jurisdictions worldwide. Other product and service names might be trademarks of IBM or other companies. A current list of IBM trademarks is available on the Web at "Copyright and trademark information" at www.ibm.com/legal/copytrade.shtml. + From ff16de1baef8a8edc626e3ea70e02e73b6b32c54 Mon Sep 17 00:00:00 2001 From: philip r brenan Date: Tue, 19 Aug 2025 23:18:46 +0100 Subject: [PATCH 29/29] Update README.md Fixed spelling of "installing" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3f744f4..a643d81 100755 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Please note you must use lsf.lsb_init before any other LSBLIB library routine in The following versions are tested: SWIG: 2.0.10, 3.0.12 - If isntalling SWIG with pip or pip3, please specify 3.0.12 version. + If installing SWIG with pip or pip3, please specify 3.0.12 version. `$ pip3 install swig==3.0.12` - Python