rbbt-util 5.32.4 → 5.32.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,311 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rbbt-util'
4
+ require 'rbbt/util/simpleopt'
5
+ require 'rbbt/hpc'
6
+
7
+ #$0 = "rbbt #{$previous_commands*""} #{ File.basename(__FILE__) }" if $previous_commands
8
+
9
+ options = SOPT.setup <<EOF
10
+
11
+ Queue a job in Marenostrum
12
+
13
+ $ rbbt slurm list [options]
14
+
15
+ -h--help Print this help
16
+ -d--done Done jobs only
17
+ -e--error Error jobs only
18
+ -a--aborted SLURM aboted jobs
19
+ -r--running Running jobs only
20
+ -q--queued Queued jobs only
21
+ -j--job* Job ids
22
+ -s--search* Regular expression
23
+ -t--tail* Show the last lines of the STDERR
24
+ -p--progress Report progress of job and the dependencies
25
+ -BP--batch_parameters show batch parameters
26
+ -BPP--batch_procpath show Procpath performance summary
27
+ -sacct--sacct_peformance show sacct performance summary
28
+ -bs--batch_system* Batch system to use: auto, lsf, slurm (default is auto-detect)
29
+ EOF
30
+
31
+ if options[:help]
32
+ if defined? rbbt_usage
33
+ rbbt_usage
34
+ else
35
+ puts SOPT.doc
36
+ end
37
+ exit 0
38
+ end
39
+
40
+ batch_system = options.delete :batch_system
41
+ batch_system ||= 'auto'
42
+
43
+ HPC::BATCH_MODULE = HPC.batch_system batch_system
44
+
45
+ raise ParameterException.new("Could not detect batch_system: #{Misc.fingerprint batch_system}") if HPC::BATCH_MODULE.nil?
46
+
47
+ batch_system = HPC::BATCH_MODULE.to_s.split("::").last.downcase
48
+
49
+ done, error, running, queued, aborted, jobid, search, tail, progress = options.values_at :done, :error, :running, :queued, :aborted, :job, :search, :tail, :progress
50
+
51
+ workdir = File.expand_path('~/rbbt-batch')
52
+ Path.setup(workdir)
53
+
54
+ running_jobs = begin
55
+ squeue_txt = HPC::BATCH_MODULE.job_status
56
+ squeue_txt.split("\n").collect{|l| l.to_i.to_s}
57
+ rescue
58
+ Log.warn "Cannot determine if jobs are running, they will seem to be all alive (Job ID in green)"
59
+ squeue_txt = nil
60
+ $norunningjobs = true
61
+ []
62
+ end
63
+
64
+ if squeue_txt
65
+ job_nodes = {}
66
+ squeue_txt.split("\n").each do |line|
67
+ parts = line.strip.split(/\s+/)
68
+ job_nodes[parts.first] = parts.last.split(",")
69
+ end
70
+ else
71
+ job_nodes = nil
72
+ end
73
+
74
+ count = 0
75
+ workdir.glob("**/command.batch").sort_by{|f| File.mtime(f)}.each do |fcmd|
76
+ dir = File.dirname(fcmd)
77
+ command_txt = Open.read(fcmd)
78
+
79
+ if m = command_txt.match(/#CMD: (.*)/)
80
+ cmd = m[1]
81
+ else
82
+ cmd = nil
83
+ end
84
+
85
+ if m = command_txt.match(/^export BATCH_SYSTEM=(.*)/)
86
+ job_batch_system = m[1].downcase
87
+ else
88
+ job_batch_system = nil
89
+ end
90
+
91
+ different_system = job_batch_system != batch_system
92
+
93
+ if m = command_txt.match(/#MANIFEST: (.*)/)
94
+ manifest = m[1]
95
+ else
96
+ manifest = nil
97
+ end
98
+
99
+ if m = command_txt.match(/#STEP_PATH: (.*)/)
100
+ step_path = m[1]
101
+ else
102
+ step_path = nil
103
+ end
104
+
105
+ if m = command_txt.match(/#EXEC_CMD: (.*)/)
106
+ exe = m[1]
107
+ else
108
+ exe = nil
109
+ end
110
+
111
+ if m = command_txt.match(/^CONTAINER_DIR=(.*)/)
112
+ container_home = m[1]
113
+ else
114
+ container_home = nil
115
+ end
116
+
117
+ if File.exists?(fid = File.join(dir, 'job.id'))
118
+ id = Open.read(fid).chomp
119
+ else
120
+ id = nil
121
+ end
122
+
123
+ if File.exists?(fstatus = File.join(dir, 'exit.status'))
124
+ exit_status = Open.read(fstatus).to_i
125
+ else
126
+ exit_status = nil
127
+ end
128
+
129
+ if File.exists?(fstatus = File.join(dir, 'job.status'))
130
+ fstatus_txt = Open.read(fstatus)
131
+ begin
132
+ if job_batch_system == "lsf"
133
+ nodes = Open.read(fstatus).split("\n").last.split(/\s+/)[5].split(",")
134
+ else
135
+ nodes = Open.read(fstatus).split("\n").last.split(/\s+/).last.split(",")
136
+ end
137
+ rescue
138
+ nodes = []
139
+ end
140
+ elsif job_nodes[id]
141
+ nodes = job_nodes[id].reject{|n| n.include? "("}
142
+ else
143
+ nodes = []
144
+ end
145
+
146
+ if File.exists?(File.join(dir, 'exit.status'))
147
+ now = File.ctime(File.join(dir, 'exit.status'))
148
+ else
149
+ now = Time.now
150
+ end
151
+
152
+ if File.exists?(File.join(dir, 'std.out'))
153
+ cerrt = File.ctime File.join(dir, 'std.err')
154
+ coutt = File.ctime File.join(dir, 'std.out')
155
+ outt = File.mtime File.join(dir, 'std.out')
156
+ errt = File.mtime File.join(dir, 'std.err')
157
+ time_diff = now - [outt, errt].max
158
+ time_elapsed = now - [cerrt, coutt].min
159
+ end
160
+
161
+ fdep = File.join(dir, 'dependencies.list')
162
+ deps = Open.read(fdep).split("\n") if File.exists?(fdep)
163
+
164
+ fcadep = File.join(dir, 'canfail_dependencies.list')
165
+ cadeps = Open.read(fcadep).split("\n") if File.exists?(fcadep)
166
+
167
+ if done || error || aborted || running || queued || jobid
168
+ select = false
169
+ select = true if done && exit_status == 0
170
+ select = true if error && exit_status && exit_status != 0
171
+ select = true if aborted && (exit_status.nil? && ! running_jobs.include?(id))
172
+ is_running = exit_status.nil? && ( (running_jobs.include?(id) && (!deps || (running_jobs & deps).empty?)) || different_system )
173
+ select = true if queued && deps && (running_jobs & deps).any? || queued && is_running && nodes.empty?
174
+ select = true if running && nodes.any? && (exit_status.nil? && running_jobs.include?(id)) && (!deps || (running_jobs & deps).empty?)
175
+ select = true if jobid && jobid.split(",").include?(id)
176
+ select = select && cmd.match(/#{search}/) if search
177
+ next unless select
178
+ elsif search
179
+ select = false
180
+ select = true if search && cmd.match(/#{search}/)
181
+ next unless select
182
+ end
183
+
184
+
185
+ puts Log.color :blue, dir
186
+ puts Log.color(:magenta, "Creation: ") << File.mtime(File.join(dir, 'command.batch')).to_s
187
+ puts Log.color(:magenta, "Started: ") << File.ctime(File.join(dir, 'std.err')).to_s if File.exist?(File.join(dir, 'std.err'))
188
+ puts Log.color(:magenta, "Manifest: ") << Log.color(:yellow, manifest)
189
+ puts Log.color(:magenta, "Step path: ") << Log.color(:yellow, step_path)
190
+ puts Log.color(:magenta, "Done: ") << File.mtime(File.join(dir, 'exit.status')).to_s if File.exist?(File.join(dir, 'exit.status'))
191
+ puts Log.color(:magenta, "Exec: ") << (exe || "Missing")
192
+ puts Log.color(:magenta, "CMD: ") << (Log.color(:yellow, cmd) || "Missing")
193
+ puts Log.color(:magenta, "HOME: ") << Log.color(:yellow, container_home) if container_home
194
+ if different_system
195
+ puts Log.color(:magenta, "Job ID (#{Log.color(:red, job_batch_system)}): ") << (exit_status ? (exit_status == 0 ? Log.color(:green, "Done") : Log.color(:red, "Error")) + " (#{ id })" : Log.color(:green, id) )
196
+ else
197
+ puts Log.color(:magenta, "Job ID: ") << (exit_status ? (exit_status == 0 ? Log.color(:green, "Done") : Log.color(:red, "Error")) + " (#{ id })" : (running_jobs.include?(id) || $norunningjobs ? Log.color(:green, id) : Log.color(:red, id) ))
198
+ end
199
+ puts Log.color(:magenta, "Dependencies: ") << deps * ", " if deps
200
+ puts Log.color(:magenta, "Dependencies (can fail): ") << cadeps * ", " if cadeps
201
+ puts Log.color(:magenta, "Nodes: ") << nodes * ", "
202
+ puts Log.color(:magenta, "Time elapsed: ") << Misc.format_seconds(time_elapsed) if time_elapsed
203
+ puts Log.color(:magenta, "Output: ") << File.exists?(File.join(dir, 'std.out')).to_s << (id.nil? || File.exists?(File.join(dir, 'exit.status')) ? "" : " (last update " + Misc.format_seconds(time_diff) + " ago)")
204
+
205
+ if options[:batch_parameters]
206
+ puts Log.color(:magenta, "BATCH parameters: ")
207
+ case job_batch_system
208
+ when 'slurm'
209
+ text = CMD.cmd('grep "^#SBATCH" |tail -n +5', :in => Open.read(fcmd)).read.strip
210
+ when 'lsf'
211
+ text = CMD.cmd('grep "^#BSUB" |tail -n +5', :in => Open.read(fcmd)).read.strip
212
+ else
213
+ text = ""
214
+ end
215
+ lines = text.split("\n").collect{|line| header, _sep, value = line.partition(/\s+/); Log.color(:yellow, header + ": ") + value}
216
+ puts Log.color :yellow, lines * "\n"
217
+ end
218
+
219
+ fprocpath = File.join(dir, 'procpath.sqlite3')
220
+ if options[:batch_procpath] && Open.exists?(fprocpath)
221
+ puts Log.color(:magenta, "Procpath summary: ")
222
+ require 'rbbt/tsv/csv'
223
+ meta = TSV.csv(CMD.cmd("sqlite3 -header -csv #{fprocpath} 'select * from meta;' "))
224
+ perf = TSV.csv(CMD.cmd("sqlite3 -header -csv #{fprocpath} 'select * from record;' "))
225
+
226
+ page_size = meta["page_size"].first.to_f
227
+ clock_ticks = meta["clock_ticks"].first.to_f
228
+
229
+ cpu_average = {}
230
+ rss_average = {}
231
+ perf.through :key, ["ts", 'stat_pid', "stat_utime", "stat_stime", "stat_cutime", "stat_cstime", "stat_rss"] do |k, values|
232
+ time, stat_pid, ucpu, scpu, ccpu, cscpu, rss = values
233
+ time = time.to_f
234
+
235
+ cpu = Misc.sum([ucpu, scpu].collect{|v| v.to_f})
236
+ cpu_average[stat_pid] ||= {}
237
+ cpu_average[stat_pid][time] ||= []
238
+ cpu_average[stat_pid][time] << cpu.to_f
239
+ rss_average[time] ||= []
240
+ rss_average[time] << rss.to_f * page_size
241
+ end
242
+
243
+ ticks = 0
244
+ cpu_average.each do |stat_pid, cpu_average_pid|
245
+ start = cpu_average_pid.keys.sort.first
246
+ eend = cpu_average_pid.keys.sort.last
247
+ ticks += Misc.sum(cpu_average_pid[eend]) - Misc.sum(cpu_average_pid[start])
248
+ end
249
+ start = rss_average.keys.sort.first
250
+ eend = rss_average.keys.sort.last
251
+ time_elapsed = eend - start
252
+ ticks = 1 if ticks == 0
253
+ time_elapsed = 1 if time_elapsed == 0
254
+ puts Log.color(:yellow, "CPU average: ") + "%.2f" % ( ticks / clock_ticks / time_elapsed * 100).to_s
255
+ puts Log.color(:yellow, "RSS average: ") + "%.2f GB" % Misc.mean(rss_average.collect{|t,l| Misc.sum(l) / (1024 * 1024 * 1024)}).to_s
256
+ puts Log.color(:yellow, "Time: ") + Misc.format_seconds((eend - start))
257
+
258
+ end
259
+
260
+ if options[:sacct_peformance]
261
+ begin
262
+ raise "sacct not supported for LSF" unless batch_system == 'slurm'
263
+ tsv = TSV.open(CMD.cmd("sacct -j #{id} -o 'jobid,AveRSS,MaxRSS,MaxDiskRead,MaxDiskWrite' -P|grep 'JobID\\|\.batch'"), :header_hash => '', :sep => "|", :type => :list)
264
+ values = tsv[tsv.keys.first]
265
+ if values.compact.any?
266
+ puts Log.color(:magenta, "SACCT performance: ")
267
+ puts values.zip(values.fields).collect{|v,t| Log.color(:yellow, t + ": ") + v.to_s } * "\n"
268
+ end
269
+ rescue
270
+ Log.warn $!.message
271
+ end
272
+ end
273
+
274
+
275
+ if tail && File.exists?(File.join(dir, 'std.err'))
276
+ if exit_status && exit_status != 0
277
+ puts Log.color(:magenta, "First error or exception found: ")
278
+ puts CMD.cmd("grep -i -w 'error\\|[a-z]*exception' #{File.join(dir, 'std.err')} -A #{tail.to_i} |head -n #{tail.to_i}", :no_fail => true).read
279
+ elsif exit_status
280
+ puts Log.color(:magenta, "Completed jobs: ")
281
+ puts CMD.cmd("grep -i -w 'Completed step' #{File.join(dir, 'std.err')} | grep -v 'Retrying dep.' | tail -n #{tail.to_i}", :no_fail => true).read
282
+ else
283
+ puts Log.color(:magenta, "Log tail: ")
284
+ puts CMD.cmd(" cat #{File.join(dir, 'std.err')} | grep -v '^[^\\s:]*\\[3.m' | tail -n #{tail.to_i} ").read
285
+ end
286
+ end
287
+
288
+ if options[:progress]
289
+ step_line = Open.read(fcmd).split("\n").select{|line| line =~ /^#STEP_PATH:/}.first
290
+ if step_line
291
+ require 'rbbt/workflow'
292
+ step_path = step_line.split(": ").last.strip
293
+ step = Step.new step_path
294
+ step.load_dependencies_from_info
295
+ (step.rec_dependencies + [step]).reverse.each do |j|
296
+ next if j.done?
297
+ next unless j.file(:progress).exists?
298
+ bar = Log::ProgressBar.new
299
+ bar.load(j.file(:progress).yaml)
300
+ puts Log.color(:magenta, "Progress: ") + bar.report_msg + " " + Log.color(:yellow, j.task_signature)
301
+ end
302
+ end
303
+ end
304
+
305
+ count += 1
306
+
307
+ end
308
+
309
+ puts
310
+ puts "Found #{count} jobs"
311
+
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rbbt/util/simpleopt'
4
+ require 'rbbt/workflow'
5
+ require 'rbbt/workflow/usage'
6
+ require 'rbbt/hpc'
7
+ require 'rbbt/hpc/orchestrate'
8
+ require 'time'
9
+
10
+ $slurm_options = SOPT.get <<EOF
11
+ -dr--dry_run Print only the template
12
+ -cj--clean_job Clean job
13
+ --drbbt* Use development version of rbbt
14
+ -sing--singularity Use Singularity
15
+ -si--singularity_img* Singularity image to use
16
+ -ug--user_group* Use alternative user group for group project directory
17
+ -c--contain* Contain in directory (using Singularity)
18
+ -s--sync* Contain in directory and sync jobs
19
+ -e--exclusive Make exclusive use of the node
20
+ -hm--highmem Make use of highmem cores
21
+ -wc--wipe_container* Wipe the jobs from the contain directory
22
+ -CS--contain_and_sync Contain and sync to default locations
23
+ -ci--copy_image When using a container directory, copy image there
24
+ -t--tail Tail the logs
25
+ -BPP--batch_procpath* Save Procpath performance for batch job; specify only options
26
+ -q--queue* Queue
27
+ -t--task_cpus* Tasks
28
+ -tm--time* Time
29
+ -lin--licenses* SLURM licenses
30
+ -cons--constraint* SLURM constraint
31
+ -W--workflows* Additional workflows
32
+ -OR--orchestration_rules* Orchestration rules
33
+ -rmb--remove_batch_basedir Remove the SLURM working directory (command, STDIN, exit status, ...)
34
+ EOF
35
+
36
+ batch_system = $slurm_options.delete :batch_system
37
+ batch_system ||= 'auto'
38
+
39
+ HPC::BATCH_MODULE = HPC.batch_system batch_system
40
+
41
+ raise ParameterException.new("Could not detect batch_system: #{Misc.fingerprint batch_system}") if HPC::BATCH_MODULE.nil?
42
+
43
+ class Step
44
+ def run(*args)
45
+ if done?
46
+ self.load
47
+ else
48
+ begin
49
+ Log.debug "Issuing SLURM job for #{self.path}"
50
+ HPC::BATCH_MODULE.orchestrate_job(self, SOPT::GOT_OPTIONS.merge($slurm_options))
51
+ rescue HPC::SBATCH
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ ARGV.concat ["-W", $slurm_options[:workflows], '--detach'] if $slurm_options[:workflows]
58
+ load Rbbt.share.rbbt_commands.workflow.task.find
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rbbt-util'
4
+ require 'rbbt/util/simpleopt'
5
+ require 'rbbt/hpc'
6
+
7
+ #$0 = "rbbt #{$previous_commands*""} #{ File.basename(__FILE__) }" if $previous_commands
8
+
9
+ options = SOPT.setup <<EOF
10
+
11
+ Queue a job in Marenostrum
12
+
13
+ $ rbbt slurm tail <directory> [options]
14
+
15
+ -h--help Print this help
16
+ -d--done Done jobs only
17
+ -e--error Error jobs only
18
+ -a--aborted SLURM aboted jobs
19
+ -r--running Running jobs only
20
+ -q--queued Queued jobs only
21
+ -j--job* Job ids
22
+ -s--search* Regular expression
23
+ -t--tail* Show the last lines of the STDERR
24
+ -p--progress Report progress of job and the dependencies
25
+ -SBP--sbatch_parameters show sbatch parameters
26
+ -PERF--procpath_performance show Procpath performance summary
27
+ -sacct--sacct_peformance show sacct performance summary
28
+ -bs--batch_system* Batch system to use: auto, lsf, slurm (default is auto-detect)
29
+ EOF
30
+
31
+ if options[:help]
32
+ if defined? rbbt_usage
33
+ rbbt_usage
34
+ else
35
+ puts SOPT.doc
36
+ end
37
+ exit 0
38
+ end
39
+
40
+ batch_system = options.delete :batch_system
41
+ batch_system ||= 'auto'
42
+
43
+ HPC::BATCH_MODULE = HPC.batch_system batch_system
44
+
45
+ raise ParameterException.new("Could not detect batch_system: #{Misc.fingerprint batch_system}") if HPC::BATCH_MODULE.nil?
46
+
47
+ directory = ARGV.shift
48
+
49
+ raise ParameterException if directory.nil?
50
+
51
+ directory = File.dirname(directory) unless File.directory?(directory)
52
+
53
+ require 'rbbt/hpc/slurm'
54
+
55
+ HPC::BATCH_MODULE.follow_job directory, true
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rbbt/util/simpleopt'
4
+ require 'rbbt/workflow'
5
+ require 'rbbt/workflow/usage'
6
+ require 'rbbt/hpc'
7
+ require 'time'
8
+
9
+ $slurm_options = SOPT.get <<EOF
10
+ -dr--dry_run Print only the template
11
+ -cj--clean_job Clean job
12
+ --drbbt* Use development version of rbbt
13
+ -sing--singularity Use Singularity
14
+ -si--singularity_img* Singularity image to use
15
+ -ug--user_group* Use alternative user group for group project directory
16
+ -c--contain* Contain in directory (using Singularity)
17
+ -s--sync* Contain in directory and sync jobs
18
+ -e--exclusive Make exclusive use of the node
19
+ -hm--highmem Make use of highmem cores
20
+ -wc--wipe_container* Wipe the jobs from the contain directory
21
+ -CS--contain_and_sync Contain and sync to default locations
22
+ -ci--copy_image When using a container directory, copy image there
23
+ -t--tail Tail the logs
24
+ -BPP--batch_procpath* Save Procpath performance for batch job; specify only options
25
+ -q--queue* Queue
26
+ -t--task_cpus* Tasks
27
+ -tm--time* Time
28
+ -lin--licenses* SLURM licenses
29
+ -cons--constraint* SLURM constraint
30
+ -W--workflows* Additional workflows
31
+ -rmb--remove_batch_dir Remove the batch working directory (command, STDIN, exit status, ...)
32
+ -bs--batch_system* Batch system to use: auto, lsf, slurm (default is auto-detect)
33
+ EOF
34
+
35
+ batch_system = $slurm_options.delete :batch_system
36
+ batch_system ||= 'auto'
37
+
38
+ HPC::BATCH_MODULE = HPC.batch_system batch_system
39
+
40
+ raise ParameterException.new("Could not detect batch_system: #{Misc.fingerprint batch_system}") if HPC::BATCH_MODULE.nil?
41
+
42
+ class Step
43
+ def run(*args)
44
+ if done?
45
+ self.load
46
+ else
47
+ begin
48
+ Log.debug "Issuing SLURM job for #{self.path}"
49
+ HPC::BATCH_MODULE.run_job(self, SOPT::GOT_OPTIONS.merge($slurm_options))
50
+ rescue HPC::SBATCH
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ ARGV.concat ["-W", $slurm_options[:workflows]] if $slurm_options[:workflows]
57
+ load Rbbt.share.rbbt_commands.workflow.task.find