rbbt-util 5.25.7 → 5.25.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b343e64d8efd008218b6566a0ab462490d4edc56
4
- data.tar.gz: 173df6bdb2479c4febe612d2ad7b4b71ab078008
3
+ metadata.gz: 3a8edeae60507177567648ab41bb5566d8707a31
4
+ data.tar.gz: 9e5188a78754f01da5c96eee01e0448bebb34a1c
5
5
  SHA512:
6
- metadata.gz: c4af721ae27c0ba29988b8abc7ca02158e8aeecd5a21a77ea741794bea7a2ffee33be92a8f0349c902cbfed086cdced582d0ab94e08b203794605b9a5438a69f
7
- data.tar.gz: d2dd2aa67c59d937811d0e8f0e7a4cd72392b022a3cf3499a5d6b36caa660d03cd80fee029c6dfca5824eace43d556c5df6c418c874ae755823e0d1a4130d92f
6
+ metadata.gz: 593de7cb23b2afd5562bc72cf44efc6d3a9138da692009aa8d15e54996ea31f3dfb9ac75cf0412b0e861c8014be62ff0f2b8ffd6ce74b28c1caa46e0dff53d83
7
+ data.tar.gz: dd4f867fa4e20a6a8eaf8e53d8cdd8a782f5181e2fb5863004c028f1a8ce8bcf82c2d67b5469e5f8d9c3a437417af4a053a05c80db85f55b0db61a44a58dbd84
@@ -4,119 +4,361 @@ require 'rbbt/util/cmd'
4
4
  module Marenostrum
5
5
  SERVER='mn1'
6
6
  module SLURM
7
- TEMPLATE=<<-EOF
8
7
 
8
+ def self.template(args, options = {})
9
9
 
10
- EOF
11
- def self.issue(cmd, name = nil, time = nil, options = {})
12
- name = "rbbt-job-" << rand(10000).to_s if name.nil?
13
- time = "00:00:10" if time.nil?
14
- workdir = "rbbt-workdir/" << name
10
+ dry_run = options.delete :dry_run
11
+ development = options.delete :drbbt
12
+ contain = options.delete :contain
13
+ sync = options.delete :sync
14
+ contain_and_sync = options.delete :contain_and_sync
15
+ wipe_container = options.delete :wipe_container
16
+ copy_image = options.delete :copy_image
17
+ exclusive = options.delete :exclusive
18
+ highmem = options.delete :highmem
19
+
20
+ if contain_and_sync
21
+ contain = "/scratch/tmp/rbbt" if contain.nil?
22
+ sync = "~/.rbbt/var/jobs" if sync.nil?
23
+ end
24
+
25
+ contain = File.expand_path(contain) if contain
26
+
27
+ singularity = true if contain || ! development
28
+
29
+
30
+ name = options[:name] ||= Misc.obj2digest({:options => options.collect{|k,v| [k,v]}.sort_by{|k,v| k.to_s }, :args => args})
31
+ workdir = options[:workdir] ||= File.expand_path(File.join('~/rbbt-workdir', name)) if workdir.nil?
32
+
33
+ rbbt_cmd = args.reject{|e| e == '--' }.collect{|e| e.include?(" ")? '"' + e + '"' : e } * " "
34
+
35
+ queue = options[:queue] || 'bsc_ls'
36
+ tasks = options[:tasks] || 1
37
+ time = options[:time] || "0:00:10"
38
+
39
+ time = Misc.format_seconds Misc.timespan(time) unless time.include? ":"
15
40
 
16
- new_options = Misc.add_defaults options, "job-name" => name, "workdir" => workdir,
17
- "output" => "log",
18
- "error" => "log.err",
19
- "ntasks" => "1",
20
- "time" => time.to_s,
21
- "user" => ENV["USER"],
22
- "key_file" => File.join(ENV['HOME'], '.ssh/id_rsa.pub')
23
41
 
24
- options.merge!(new_options)
25
- IndiferentHash.setup(options)
42
+ #{{{ PREPARE LOCAL LOGFILES
26
43
 
27
- key_file, user = options.values_at :key_file, :user
44
+ Open.mkdir workdir
28
45
 
29
- template = <<-EOF
46
+ fout = File.join(workdir, 'std.out')
47
+ ferr = File.join(workdir, 'std.err')
48
+ fjob = File.join(workdir, 'job.id')
49
+ fexit = File.join(workdir, 'exit.status')
50
+ fsync = File.join(workdir, 'sync.log')
51
+ fcmd = File.join(workdir, 'command.slurm')
52
+
53
+ #{{{ GENERATE TEMPLATE
54
+
55
+ # HEADER
56
+ header =<<-EOF
30
57
  #!/bin/bash
31
- #{options.collect do |name,value|
32
- next if name == 'user'
33
- next if name == 'key_file'
34
- name = "--" << name.to_s.sub(/^--/,'')
35
- [name, '"' << value << '"'] * "="
36
- end.compact.collect{|str| "#SBATCH #{str}"} * "\n"
37
- }
38
- #{ cmd }
58
+ #SBATCH --qos="#{queue}"
59
+ #SBATCH --job-name="#{name}"
60
+ #SBATCH --workdir="#{Dir.pwd}"
61
+ #SBATCH --output="#{fout}"
62
+ #SBATCH --error="#{ferr}"
63
+ #SBATCH --ntasks="#{tasks}"
64
+ #SBATCH --time="#{time}"
39
65
  EOF
40
66
 
41
- TmpFile.with_file do |slurm|
42
- res = nil
43
- begin
44
- Open.write(slurm, template)
45
- Log.medium("Issuing job:\n" + template)
46
-
47
- cmd = "ssh -i #{key_file} #{ user }@#{ SERVER } mkdir -p '#{ workdir }'; scp -i #{key_file} #{ slurm } #{ user }@#{ SERVER }:'#{ workdir }/#{ name }.slurm'; ssh -i #{key_file} #{ user }@#{ SERVER } sbatch #{ workdir }/#{ name }.slurm"
48
- Log.debug cmd
49
- res = CMD.cmd(cmd).read
50
- rescue
51
- raise "Could not issue job"
67
+ if highmem
68
+ header +=<<-EOF
69
+ #SBATCH --constraint=highmem
70
+ EOF
71
+ end
72
+
73
+ if exclusive
74
+ header +=<<-EOF
75
+ #SBATCH --exclusive
76
+ EOF
77
+ end
78
+
79
+ header +=<<-EOF
80
+ #CMD: #{rbbt_cmd}
81
+ EOF
82
+
83
+ # ENV
84
+ env = ""
85
+ env +=<<-EOF
86
+ # Prepare env
87
+ [[ -f ~/config/load.sh ]] && source ~/config/load.sh
88
+ module load java
89
+ EOF
90
+
91
+ if singularity
92
+ env +=<<-EOF
93
+ module load intel/2018.1
94
+ module load singularity
95
+ module load samtools
96
+ SINGULARITY_IMG="$HOME/projects/rbbt.singularity.img"
97
+ SINGULARITY_RUBY_INLINE="$HOME/.singularity_ruby_inline"
98
+ mkdir -p "$SINGULARITY_RUBY_INLINE"
99
+ EOF
100
+ end
101
+
102
+ if contain
103
+ env +=<<-EOF
104
+ CONTAINER_DIR="#{contain}"
105
+ mkdir -p $CONTAINER_DIR/.rbbt/etc/
106
+ for tmpd in persist_locks produce_locks R_sockets sensiblewrite sensiblewrite_locks step_info_locks tsv_open_locks; do
107
+ mkdir -p $CONTAINER_DIR/.rbbt/tmp/$tmpd
108
+ done
109
+ cp ~/.rbbt/etc/environment $CONTAINER_DIR/.rbbt/etc/
110
+ echo "rbbt_user: /home/rbbt/.rbbt/{TOPLEVEL}/{SUBPATH}" > $CONTAINER_DIR/.rbbt/etc/search_paths
111
+ echo "home: $CONTAINER_DIR/home/{TOPLEVEL}/{SUBPATH}" >> $CONTAINER_DIR/.rbbt/etc/search_paths
112
+ echo "group_projects: $CONTAINER_DIR/projects/{PKGDIR}/{TOPLEVEL}/{SUBPATH}" >> $CONTAINER_DIR/.rbbt/etc/search_paths
113
+ echo "group_scratch: $CONTAINER_DIR/scratch/{PKGDIR}/{TOPLEVEL}/{SUBPATH}" >> $CONTAINER_DIR/.rbbt/etc/search_paths
114
+ echo "user_projects: $CONTAINER_DIR/projects/#{ENV['USER']}/{PKGDIR}/{TOPLEVEL}/{SUBPATH}" >> $CONTAINER_DIR/.rbbt/etc/search_paths
115
+ echo "user_scratch: $CONTAINER_DIR/scratch/#{ENV['USER']}/{PKGDIR}/{TOPLEVEL}/{SUBPATH}" >> $CONTAINER_DIR/.rbbt/etc/search_paths
116
+ EOF
117
+
118
+ if copy_image
119
+ env +=<<EOF
120
+ rsync -avz "$SINGULARITY_IMG" "$CONTAINER_DIR/rbbt.singularity.img"
121
+ SINGULARITY_IMG="$CONTAINER_DIR/rbbt.singularity.img"
122
+ EOF
52
123
  end
53
124
 
54
- res.scan(/\d+/).first
125
+ if wipe_container == "pre" || wipe_container == "both"
126
+ env +=<<-EOF
127
+ singularity exec -e -C -H "$CONTAINER_DIR" "$SINGULARITY_IMG" rm -Rfv .rbbt/var/jobs &>> #{fsync}
128
+ singularity exec -e -C -H "$CONTAINER_DIR" "$SINGULARITY_IMG" rbbt system clean -f &>> #{fsync}
129
+ EOF
130
+ end
55
131
  end
56
- end
57
132
 
58
- def self.query(id, options = {})
59
- key_file, user = options.values_at :key_file, :user
133
+ # RUN
134
+ run = ""
135
+
136
+ if singularity
137
+ if contain
138
+ group = File.basename(File.dirname(ENV['HOME']))
139
+ scratch_group_dir = File.join('/gpfs/scratch/', group)
140
+ projects_group_dir = File.join('/gpfs/projects/', group)
141
+ exec_cmd = %(singularity exec -e -C -H "$CONTAINER_DIR" -B "$SINGULARITY_RUBY_INLINE":"$CONTAINER_DIR/.ruby_inline":rw -B ~/git:"$CONTAINER_DIR/git":ro -B #{scratch_group_dir}:"$CONTAINER_DIR/scratch":ro -B ~/.rbbt/software/opt/:"/opt/":ro -B ~/.rbbt:"$CONTAINER_DIR/home/":ro -B #{projects_group_dir}:"$CONTAINER_DIR/projects":ro "$SINGULARITY_IMG" env TMPDIR="$CONTAINER_DIR/.rbbt/tmp" rbbt)
142
+ else
143
+ exec_cmd = %(singularity exec -e -B "$SINGULARITY_RUBY_INLINE":"$HOME/.ruby_inline":rw "$SINGULARITY_IMG" rbbt)
144
+ end
145
+
146
+ if development
147
+ exec_cmd += ' --dev=git'
148
+ end
149
+ else
150
+ exec_cmd = %(~/git/rbbt-util/bin/rbbt --dev=~/git/)
151
+ end
152
+
153
+
154
+ cmd =<<-EOF
155
+ #{exec_cmd} \\
156
+ #{rbbt_cmd}
157
+ EOF
60
158
 
61
- res = nil
62
- begin
63
- cmd = "ssh -i #{key_file} #{ user }@#{ SERVER } squeue |grep '#{id}\\|JOBID'"
64
- Log.debug cmd
65
- res = CMD.cmd(cmd).read
66
- rescue
67
- raise "Could not query job: #{ id }" << $!.message
159
+ run +=<<-EOF
160
+
161
+ # Run command
162
+ #{cmd}
163
+
164
+ # Save exit status
165
+ echo $? > #{fexit}
166
+
167
+ # Clean job.id, since we are done
168
+ rm #{fjob}
169
+ EOF
170
+
171
+ # CODA
172
+ coda = ""
173
+ if sync
174
+ if singularity
175
+ coda +=<<-EOF
176
+ singularity exec -e -C -H "$CONTAINER_DIR" "$SINGULARITY_IMG" rbbt system clean all -q &>> #{fsync}
177
+ EOF
178
+ end
179
+
180
+ coda +=<<-EOF
181
+ rsync -avt "#{File.join(File.expand_path(contain), '.rbbt/var/jobs')}/" "#{File.expand_path(sync)}/" &>> #{fsync}
182
+ EOF
183
+
184
+ if contain && (wipe_container == "post" || wipe_container == "both")
185
+ coda +=<<-EOF
186
+ sync_es="$?"
187
+ singularity exec -e -C -H "$CONTAINER_DIR" "$SINGULARITY_IMG" rbbt system clean -f &>> #{fsync}
188
+ singularity exec -e -C -H "$CONTAINER_DIR" "$SINGULARITY_IMG" rm -v /dev/shm/sem.*.{in,out,process} /dev/shm/sem.Session-PID.*.sem 2> /dev/null >> #{fsync}
189
+ if [ $sync_es == '0' ]; then
190
+ singularity exec -e -C -H "$CONTAINER_DIR" "$SINGULARITY_IMG" rm -Rfv .rbbt/var/jobs &>> #{fsync}
191
+ else
192
+ echo "WARNING: Results could not sync correctly. Job directory not purged"
193
+ fi
194
+ unset sync_es
195
+ EOF
196
+ end
68
197
  end
69
198
 
70
- res
199
+ template = [header, env, run, coda] * "\n"
200
+
201
+ template
71
202
  end
203
+
204
+ def self.issue_template(template, options = {})
72
205
 
73
- def self.done?(id, options = {})
74
- ! query(id, options).include? id
206
+ workdir = options[:workdir]
207
+ Open.mkdir workdir
208
+ fout = File.join(workdir, 'std.out')
209
+ ferr = File.join(workdir, 'std.err')
210
+ fjob = File.join(workdir, 'job.id')
211
+ fexit = File.join(workdir, 'exit.status')
212
+ fsync = File.join(workdir, 'sync.log')
213
+ fcmd = File.join(workdir, 'command.slurm')
214
+
215
+ job = nil
216
+ if options[:clean_job]
217
+ [fcmd, fjob, fout, ferr, fsync, fexit].each do |file|
218
+ Open.rm file if Open.exists? file
219
+ end
220
+ end
221
+
222
+ return if Open.exists?(fexit)
223
+
224
+ STDERR.puts Log.color(:magenta, "Issuing SLURM file: #{fcmd}")
225
+ STDERR.puts template
226
+
227
+ Open.write(fcmd, template) unless File.exists? fcmd
228
+ if File.exists?(fjob)
229
+ job = Open.read(fjob).to_i
230
+ else
231
+ if File.exists?(fout)
232
+ return
233
+ else
234
+ Open.rm fsync
235
+ Open.rm fexit
236
+ Open.rm fout
237
+ Open.rm ferr
238
+ job = CMD.cmd("sbatch '#{fcmd}'").read.scan(/\d+/).first.to_i
239
+ Open.write(fjob, job.to_s)
240
+ end
241
+ end
75
242
  end
76
243
 
77
- def self.gather(id, file = nil, options = {})
78
- key_file, user, workdir, output = options.values_at :key_file, :user, :workdir, :output
244
+ def self.follow_job(workdir, tail = true)
245
+ fjob = File.join(workdir, 'job.id')
246
+ fout = File.join(workdir, 'std.out')
247
+ ferr = File.join(workdir, 'std.err')
248
+ fstatus = File.join(workdir, 'job.status')
249
+
250
+ job = Open.read(fjob) if Open.exists?(fjob)
79
251
 
80
- TmpFile.with_file do |result|
252
+ if job
253
+ status_txt = CMD.cmd("squeue --job #{job}").read
254
+ STDERR.puts Log.color(:magenta, "Status [#{job.to_i}]:")
255
+ STDERR.puts status_txt
256
+ lines = status_txt.split("\n").length
257
+ end
81
258
 
259
+ if tail
260
+ while ! File.exists? fout
261
+ if job
262
+ STDERR.puts
263
+ Log.clear_line(STDERR)
264
+ STDERR.write Log.color(:magenta, "Waiting for Output")
265
+ 3.times do
266
+ STDERR.write Log.color(:magenta, ".")
267
+ sleep 1
268
+ end
269
+ status_txt = CMD.cmd("squeue --job #{job}").read
270
+ lines.times do
271
+ Log.clear_line(STDERR)
272
+ end
273
+ Log.clear_line(STDERR)
274
+ STDERR.puts Log.color(:magenta, "Status [#{job.to_i}]:")
275
+ STDERR.puts status_txt
276
+ lines = status_txt.split("\n").length
277
+ end
278
+ end
279
+ STDERR.puts
280
+ Log.clear_line(STDERR)
281
+ STDERR.puts Log.color(:magenta, "Output:")
82
282
  begin
83
- cmd = "scp -i #{key_file} #{ user }@#{ SERVER }:'#{workdir}/#{output}' '#{ result }' "
84
- Log.debug cmd
85
- CMD.cmd(cmd)
86
- if file.nil?
87
- Open.read(result)
88
- else
89
- Misc.sensiblewrite(result, file)
283
+ CMD.cmd("squeue --job #{job} > #{fstatus}")
284
+ out = CMD.cmd("tail -f '#{fout}'", :pipe => true) if File.exists? fout
285
+ err = CMD.cmd("tail -f '#{ferr}'", :pipe => true) if File.exists? ferr
286
+
287
+ Misc.consume_stream(err, true, STDERR) if err
288
+ Misc.consume_stream(out, true, STDOUT) if out
289
+
290
+ sleep 3 while CMD.cmd("squeue --job #{job}").read.include? job.to_s
291
+ ensure
292
+ begin
293
+ err.close if err
294
+ err.join if err
295
+ rescue Exception
296
+ end
297
+
298
+ begin
299
+ out.close if out
300
+ out.join if out
301
+ rescue Exception
90
302
  end
91
- rescue
92
- raise "Could not gather job: #{ id }: " << $!.message
93
303
  end
94
304
  end
95
305
  end
96
306
 
97
- def self.run(cmd, name = nil, time = nil, options = {})
98
- sleep_time = Misc.process_options options, :sleep
307
+ def self.wait_for_job(workdir, time = 1)
308
+ fexit = File.join(workdir, 'exit.status')
309
+ fjob = File.join(workdir, 'job.id')
310
+ job = Open.read(fjob) if Open.exists?(fjob)
99
311
 
100
- id = issue(cmd, name, time, options)
312
+ status_txt = CMD.cmd("squeue --job #{job}").read
313
+ STDERR.puts Log.color(:magenta, "Status [#{job.to_i}]:")
314
+ STDERR.puts status_txt
315
+ lines = status_txt.split("\n").length
101
316
 
102
- if sleep_time.nil?
103
- times = [1,2,3,5,10,30]
104
- else
105
- times = Array === sleep_time ? sleep_time.dup : [sleep_time]
317
+ while ! Open.exists?(fexit)
318
+ STDERR.puts
319
+ Log.clear_line(STDERR)
320
+ STDERR.write Log.color(:magenta, "Waiting for end")
321
+ 3.times do
322
+ STDERR.write Log.color(:magenta, ".")
323
+ sleep 1
324
+ end
325
+ status_txt = CMD.cmd("squeue --job #{job}").read
326
+ lines.times do
327
+ Log.clear_line(STDERR)
328
+ end
329
+ Log.clear_line(STDERR)
330
+ STDERR.puts Log.color(:magenta, "Status [#{job.to_i}]:")
331
+ STDERR.puts status_txt
332
+ lines = status_txt.split("\n").length
333
+ sleep time
106
334
  end
335
+ end
107
336
 
108
- while not done?(id, options)
109
- Log.debug "Waiting on #{ id }"
110
- sleep_time = times.shift || sleep_time
111
- sleep sleep_time
337
+ def self.run_job(job, options = {})
338
+ workflow = job.workflow
339
+ task = job.task_name
340
+ name = job.clean_name
341
+ TmpFile.with_file(nil, false) do |tmp_directory|
342
+ workdir = options[:workdir] ||= File.join(tmp_directory, 'workdir')
343
+ inputs_dir = File.join(tmp_directory, 'inputs_dir')
344
+ Step.save_job_inputs(job, inputs_dir)
345
+ cmd = ['workflow', 'task', workflow.to_s, task.to_s, '-pf', '-jn', name, '--load_inputs', inputs_dir, '--log', (options[:log] || Log.severity).to_s]
346
+ template = self.template(cmd, options)
347
+ self.issue_template(template, options)
348
+ self.wait_for_job(workdir)
349
+ path = Open.read(File.join(workdir, 'std.out'))
350
+ if job.path != path
351
+ Log.medium "Path of SLURM job #{path} is different from original job #{job.path}. Stablishing link."
352
+ Open.ln path, job.path
353
+ end
112
354
  end
113
-
114
- gather(id, nil, options)
115
355
  end
116
356
  end
117
357
  end
118
358
 
119
- Log.severity = 0 if __FILE__ == $0
120
- iii Marenostrum::SLURM.run('ls', nil, nil, :qos => "debug", :user => 'bsc26892') if __FILE__ == $0
359
+ if __FILE__ == $0
360
+ Log.severity = 0
361
+ iii Marenostrum::SLURM.run('ls', nil, nil, :qos => "debug", :user => 'bsc26892') if __FILE__ == $0
362
+ end
121
363
 
122
364
 
@@ -71,13 +71,15 @@ module Misc
71
71
  def self.timespan(str, default = "s")
72
72
  tokens = {
73
73
  "s" => (1),
74
+ "sec" => (1),
75
+ "m" => (60),
74
76
  "min" => (60),
75
77
  "''" => (1),
76
78
  "'" => (60),
77
79
  "h" => (60 * 60),
78
80
  "d" => (60 * 60 * 24),
79
81
  "w" => (60 * 60 * 24 * 7),
80
- "m" => (60 * 60 * 24 * 30),
82
+ "mo" => (60 * 60 * 24 * 30),
81
83
  "y" => (60 * 60 * 24 * 365),
82
84
  }
83
85
 
@@ -80,6 +80,29 @@ class Step
80
80
  end
81
81
  end
82
82
 
83
+ def self.save_job_inputs(job, dir)
84
+
85
+ task_name = job.task_name
86
+ task_info = job.workflow.task_info(task_name)
87
+ input_types = task_info[:input_types]
88
+ task_inputs = task_info[:inputs]
89
+ job.recursive_inputs.zip(job.recursive_inputs.fields).each do |value,name|
90
+ next unless task_inputs.include? name.to_sym
91
+ next if value.nil?
92
+ path = File.join(dir, name.to_s)
93
+ type = input_types[name]
94
+ Log.debug "Saving job input #{name} (#{type}) into #{path}"
95
+ case
96
+ when Array === value
97
+ Open.write(path, value * "\n")
98
+ when IO === value
99
+ Open.write(path, value)
100
+ else
101
+ Open.write(path, value.to_s)
102
+ end
103
+ end
104
+ end
105
+
83
106
  def name
84
107
  @name ||= path.sub(/.*\/#{Regexp.quote task_name.to_s}\/(.*)/, '\1')
85
108
  end
@@ -108,7 +131,7 @@ class Step
108
131
  #Lockfile.new path, :refresh => false, :dont_use_lock_id => true
109
132
  Lockfile.new path
110
133
  end if @info_lock.nil?
111
- @info_lock
134
+ @info_lock
112
135
  end
113
136
 
114
137
  def status_lock
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbbt-util
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.25.7
4
+ version: 5.25.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miguel Vazquez
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-12-18 00:00:00.000000000 Z
11
+ date: 2018-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake