-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathopenLavaActor.py
More file actions
327 lines (257 loc) · 9.5 KB
/
Copy pathopenLavaActor.py
File metadata and controls
327 lines (257 loc) · 9.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# Copyright 2009-2011 Mark Fiers
# The New Zealand Institute for Plant & Food Research
#
# This file is part of Moa - http://github.com/mfiers/Moa
#
# Licensed under the GPL license (see 'COPYING')
#
"""
**sgeActor** - Run jobs through SGE
-----------------------------------------------------------
"""
import os
import stat
import subprocess as sp
import sys
import tempfile
import jinja2
import moa.logger
import moa.ui
from moa.sysConf import sysConf
l = moa.logger.getLogger(__name__)
#l.setLevel(moa.logger.DEBUG)
def hook_defineCommandOptions(job, parser):
parser.add_argument('--ol', action='store_const', const='openlava',
dest='actorId', help='Use OpenLava as actor')
parser.add_argument('--olq', default='normal', dest='openlavaQueue',
help='The Openlava queue to submit this job to')
parser.add_argument('--olx', default='', dest='openlavaExtra',
help='Extra arguments for bsub')
parser.add_argument('--olmin', type=int, dest='openlavaProcsMin',
help='The minimum number of processors the job requires')
parser.add_argument('--olmax', type=int, dest='openlavaProcsMax',
help='The maximum number of processors the job allows')
parser.add_argument('--oldummy', default=False, dest='openlavaDummy',
action='store_true',
help='Do not execute - just create a script to run')
parser.add_argument('--olm', default="", dest='openlavaHost',
help='The host to use for openlava')
def _writeOlTmpFile(wd, _script):
#save the file
tmpdir = os.path.join(wd, '.moa', 'tmp')
if not os.path.exists(tmpdir):
os.makedirs(tmpdir)
tf = tempfile.NamedTemporaryFile(dir=tmpdir, prefix='openlava.',
delete=False, suffix='.sh')
if isinstance(_script, list):
tf.write("\n".join(_script))
else:
tf.write(str(_script))
tf.close()
os.chmod(tf.name, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
return tf.name
@moa.actor.async
def openlavaRunner(wd, cl, conf={}, **kwargs):
"""
Run the job using OPENLAVA
what does this function do?
- put env in the environment
- Execute the commandline (in cl)
- store stdout & stderr in log files
- return the rc
"""
#see if we can get a command
command = kwargs.get('command', 'unknown')
if command == 'unknown':
l.critical("runner should be called with a command")
sys.exit(-1)
l.debug("starting openlava actor for %s" % command)
# this is a trick to get the real path of the log dir - but not of
# any underlying directory - in case paths are mounted differently
# on different hosts
outDir = os.path.abspath(os.path.join(wd, '.moa', 'log.latest'))
outDir = outDir.rsplit('.moa', 1)[0] + '.moa' + \
os.path.realpath(outDir).rsplit('.moa', 1)[1]
sysConf.job.data.openlava.outDir = outDir
if not os.path.exists(outDir):
try:
os.makedirs(outDir)
except OSError:
pass
#expect the cl to be nothing more than a single script to execute
outfile = os.path.join(outDir, 'stdout')
errfile = os.path.join(outDir, 'stderr')
sysConf.job.data.openlava.outfile = outfile
sysConf.job.data.openlava.errfile = errfile
bsub_cl = ['bsub']
sc = []
def s(*cl):
sc.append(" ".join(map(str, cl)))
s("#!/bin/bash")
s("#BSUB -o %s" % outfile)
s("#BSUB -e %s" % errfile)
s("#BSUB -q %s" % sysConf.args.openlavaQueue)
# Only specify '-n' if required
if '--olmin' in sys.argv:
minProcs = sysConf.args.openlavaProcsMin
# Max also set?
if '--olmax' in sys.argv:
maxProcs = sysConf.args.openlavaProcsMax
minMaxProcs = "%d,%d" % (minProcs, maxProcs)
else:
minMaxProcs = "%d" % minProcs
s("#BSUB -n %s" % minMaxProcs)
if sysConf.args.openlavaExtra.strip():
s("#BSUB %s" % sysConf.args.openlavaExtra)
if '--olm' in sys.argv:
s("#BSUB -m %s" % sysConf.args.openlavaHost)
#bsub_cl.extend(["-m", sysConf.args.openlavaHost])
if command == 'run':
prep_jids = sysConf.job.data.openlava.jids.get('prepare', [])
#hold until the 'prepare' jobs are done
#l.critical("Prepare jids - wait for these! %s" % prep_jids)
for j in prep_jids:
s("#BSUB -w 'done(%d)'" % j)
#bsub_cl.extend(["-w", "'done(%d)'" % j])
elif command == 'finish':
run_jids = sysConf.job.data.openlava.jids.get('run', [])
#hold until the 'prepare' jobs are done
for j in run_jids:
s("#BSUB -w 'done(%d)'" % j)
#bsub_cl.extend(["-w", "'done(%d)'" % j])
#give it a reasonable name
jobname = ("%s_%s" % (wd.split('/')[-1], command[0]))
bsub_cl.extend(['-J', jobname])
s("#BSUB -J '%s'" % jobname)
#dump the configuration in the environment
s("")
s("## ensure we're in the correct directory")
s("cd", wd)
s("")
s("## Defining moa specific environment variables")
s("")
confkeys = sorted(conf.keys())
for k in confkeys:
# to prevent collusion, prepend all env variables
# with 'moa_'
if k[0] == '_' or k[:3] == 'moa':
outk = k
else:
outk = 'moa_' + k
v = conf[k]
#this should not happen:
if ' ' in outk:
continue
if isinstance(v, list):
s("%s='%s'" % (outk, " ".join(v)))
elif isinstance(v, dict):
continue
else:
s("%s='%s'" % (outk, v))
s("")
s("## Run the command")
s("")
s(*cl)
if sysConf.args.openlavaDummy:
# Dummy mode - do not execute - just write the script.
ii = 0
while True:
outFile = os.path.join(wd, 'openlava.%s.%d.bash' % (command, ii))
if not os.path.exists(outFile):
break
ii += 1
with open(outFile, 'w') as F:
F.write("\n".join(sc))
moa.ui.message("Created openlava submit script: %s" %
outFile.rsplit('/', 1)[1])
moa.ui.message("now run:")
moa.ui.message(" %s < %s" % ((" ".join(map(str, bsub_cl))),
outFile.rsplit('/', 1)[1]))
return 0
tmpfile = _writeOlTmpFile(wd, sc)
moa.ui.message("Running %s:" % " ".join(map(str, bsub_cl)))
moa.ui.message("(copy of) the bsub script: %s" % tmpfile)
p = sp.Popen(map(str, bsub_cl), cwd=wd, stdout=sp.PIPE, stdin=sp.PIPE)
o, e = p.communicate("\n".join(sc))
jid = int(o.split("<")[1].split(">")[0])
moa.ui.message("Submitted a job to openlava with id %d" % jid)
if not sysConf.job.data.openlava.jids.get(command):
sysConf.job.data.openlava.jids[command] = []
#moa.ui.message("submitted job with openlava job id %s " % jid)
#store the job id submitted
if not sysConf.job.data.openlava.jids.get(command):
sysConf.job.data.openlava.jids[command] = []
if not sysConf.job.data.openlava.get('alljids'):
sysConf.job.data.openlava.alljids = []
sysConf.job.data.openlava.jids[command].append(jid)
sysConf.job.data.openlava.alljids.append(jid)
l.debug("jids stored %s" % str(sysConf.job.data.openlava.jids))
return p.returncode
OnSuccessScript = """#!/bin/bash
#BSUB -o {{ job.data.openlava.outfile }}
#BSUB -w {{ job.data.openlava.errfile }}
#BSUB -q {{ args.openlavaQueue }}
#BSUB -J "{{ job.data.openlava.uid }}_Ok"
{% if args.openlavaHost -%}
#BSUB -m {{ args.openlavaHost }}
{%- endif %}
#BSUB -w '{%- for j in job.data.openlava.alljids -%}
{%- if loop.index0 > 0 %}&&{% endif -%}
done({{j}})
{%- endfor -%}'
cd {{ job.wd }}
echo "Openlava OnSuccess Start"
echo "Killing the OnError job"
bkill -J "{{ job.data.openlava.uid }}_Err"
moasetstatus success
"""
OnErrorScript = """#!/bin/bash
## only run this job if there is a single job
#BSUB -o {{ job.data.openlava.outfile }}
#BSUB -w {{ job.data.openlava.errfile }}
#BSUB -q {{ args.openlavaQueue }}
#BSUB -J "{{ job.data.openlava.uid }}_Err"
{% if args.openlavaHost -%}
#BSUB -m {{ args.openlavaHost }}
{%- endif %}
#BSUB -w '{%- for j in job.data.openlava.alljids -%}
{%- if loop.index0 > 0 %}||{% endif -%}
exit({{j}},!=0)
{%- endfor -%}
'
cd {{ job.wd }}
echo "Openlava OnError Start"
echo "Killing the all other jobs"
#killing all jobs
{% for j in job.data.openlava.alljids %}
bkill -s 9 {{ j }}
{% endfor %}
bkill -J "{{ job.data.openlava.uid }}_Ok"
moasetstatus error
"""
def hook_async_exit(job):
"""
Need to exit here, and reconvene once all jobs have executed
"""
#make sure that this is the correct actor
actor = moa.actor.getActor()
if actor.__name__ != 'openlavaRunner':
return
jidlist = sysConf.job.data.openlava.get('alljids', [])
if len(jidlist) == 0:
return
uid = "%s.%s" % (job.wd.split('/')[-1],max(jidlist))
sysConf.job.data.openlava.uid = uid
onsuccess = jinja2.Template(OnSuccessScript).render(sysConf)
onerror = jinja2.Template(OnErrorScript).render(sysConf)
with open('succ', 'w') as F:
F.write(onsuccess)
with open('onerr', 'w') as F:
F.write(onerror)
P = sp.Popen('bsub', stdin=sp.PIPE)
P.communicate(onsuccess)
P = sp.Popen('bsub', stdin=sp.PIPE)
P.communicate(onerror)
#register this actor globally
sysConf.actor.actors['openlava'] = openlavaRunner
sysConf.actor.openlava.jids = []