rbbt-util 5.25.7 → 5.25.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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