rbbt-util 5.31.13 → 5.32.2
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 +4 -4
- data/lib/rbbt/hpc/batch.rb +35 -3
- data/lib/rbbt/hpc/lsf.rb +7 -4
- data/lib/rbbt/hpc/orchestrate.rb +8 -1
- data/lib/rbbt/hpc/slurm.rb +7 -3
- data/lib/rbbt/persist/tsv/adapter.rb +1 -1
- data/lib/rbbt/tsv/csv.rb +5 -0
- data/lib/rbbt/tsv/parser.rb +2 -2
- data/lib/rbbt/tsv/stream.rb +1 -0
- data/lib/rbbt/util/misc/pipes.rb +1 -1
- data/lib/rbbt/util/open.rb +1 -1
- data/lib/rbbt/workflow.rb +10 -6
- data/lib/rbbt/workflow/definition.rb +1 -0
- data/lib/rbbt/workflow/doc.rb +18 -3
- data/lib/rbbt/workflow/step.rb +4 -0
- data/lib/rbbt/workflow/step/accessor.rb +1 -0
- data/lib/rbbt/workflow/util/archive.rb +1 -1
- data/lib/rbbt/workflow/util/trace.rb +132 -90
- data/share/rbbt_commands/hpc/clean +1 -27
- data/share/rbbt_commands/hpc/list +4 -28
- data/share/rbbt_commands/hpc/orchestrate +1 -27
- data/share/rbbt_commands/hpc/tail +1 -27
- data/share/rbbt_commands/hpc/task +1 -27
- data/share/rbbt_commands/workflow/forget_deps +17 -5
- data/share/rbbt_commands/workflow/trace +9 -195
- data/share/rbbt_commands/workflow/write_info +17 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 612e1472bae3839b68f143d639025a9bb19294525ecc0e95df99702883af31d3
|
4
|
+
data.tar.gz: bea8c5ec09525363447c5bdc140f899f8a22ccb311d225da061b01d6f68bcf10
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0aef55d55b97f40011aeaa3f09450dd623ad6d682b4b3ef5dc3f2df603efeeede2e50b3be2f4f586e502adb6a4a0fd898113d3c5c892212170edb4cde7b7f6a7
|
7
|
+
data.tar.gz: 81261ef8e8da6ada5c1d8e672fdbd31ff6dc67a7a08edb4b6a3dcee10cfc6ed3f7abacf0c3628d604839e2743de043793c5169a147bd9b528c2afd614dca62b9
|
data/lib/rbbt/hpc/batch.rb
CHANGED
@@ -6,6 +6,36 @@ module HPC
|
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
+
def self.batch_system(batch_system = 'auto')
|
10
|
+
case batch_system.to_s.downcase
|
11
|
+
when 'slurm'
|
12
|
+
HPC::SLURM
|
13
|
+
when 'lsf'
|
14
|
+
HPC::LSF
|
15
|
+
when 'auto'
|
16
|
+
case $previous_commands.last
|
17
|
+
when 'slurm'
|
18
|
+
HPC::SLURM
|
19
|
+
when 'lsf'
|
20
|
+
HPC::LSF
|
21
|
+
else
|
22
|
+
case Rbbt::Config.get(:batch_system, :batch, :batch_system, :hpc, :HPC, :BATCH).to_s.downcase
|
23
|
+
when 'slurm'
|
24
|
+
HPC::SLURM
|
25
|
+
when 'lsf'
|
26
|
+
HPC::LSF
|
27
|
+
else
|
28
|
+
case ENV["BATCH_SYSTEM"].to_s.downcase
|
29
|
+
when 'slurm'
|
30
|
+
HPC::SLURM
|
31
|
+
when 'lsf'
|
32
|
+
HPC::LSF
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
9
39
|
module TemplateGeneration
|
10
40
|
def exec_cmd(job, options = {})
|
11
41
|
env_cmd = Misc.process_options options, :env_cmd
|
@@ -30,12 +60,12 @@ module HPC
|
|
30
60
|
-B "/.singularity_ruby_inline":"#{contain}/.singularity_ruby_inline":rw
|
31
61
|
-B "#{options[:batch_dir]}" \
|
32
62
|
-B /scratch/tmp \
|
33
|
-
#{ group != user_group ? "-B /gpfs/projects/#{user_group}" : "" } \
|
63
|
+
#{ group != user_group ? "-B /gpfs/projects/#{user_group}" : "" } \
|
34
64
|
-B #{scratch_group_dir} \
|
35
65
|
-B #{projects_group_dir} \
|
36
66
|
-B /apps/ \
|
37
67
|
-B ~/git:"#{contain}/git":ro \
|
38
|
-
#{Open.exists?('~/.rbbt/software/opt/')? '-B ~/.rbbt/software/opt/:"/opt/":ro' : '' } \
|
68
|
+
#{Open.exists?('~/.rbbt/software/opt/')? '-B ~/.rbbt/software/opt/:"/opt/":ro' : '' } \
|
39
69
|
-B ~/.rbbt:"#{contain}/home/":ro)
|
40
70
|
end
|
41
71
|
|
@@ -74,7 +104,7 @@ module HPC
|
|
74
104
|
[name, dep.path] * "="
|
75
105
|
end * ","
|
76
106
|
|
77
|
-
options[:override_deps] = override_deps
|
107
|
+
options[:override_deps] = override_deps unless override_deps.empty?
|
78
108
|
end
|
79
109
|
|
80
110
|
# Save inputs into inputs_dir
|
@@ -216,6 +246,7 @@ EOF
|
|
216
246
|
:fexit => File.join(batch_dir, 'exit.status'),
|
217
247
|
:fsync => File.join(batch_dir, 'sync.log'),
|
218
248
|
:fsexit => File.join(batch_dir, 'sync.status'),
|
249
|
+
:fenv => File.join(batch_dir, 'env.vars'),
|
219
250
|
:fcmd => File.join(batch_dir, 'command.batch')
|
220
251
|
|
221
252
|
batch_options
|
@@ -441,6 +472,7 @@ exit $exit_status
|
|
441
472
|
|
442
473
|
# #{Log.color :green, "1. Prepare environment"}
|
443
474
|
#{prepare_environment}
|
475
|
+
env > #{batch_options[:fenv]}
|
444
476
|
|
445
477
|
# #{Log.color :green, "2. Execute"}
|
446
478
|
#{execute}
|
data/lib/rbbt/hpc/lsf.rb
CHANGED
@@ -7,10 +7,13 @@ module HPC
|
|
7
7
|
|
8
8
|
def self.batch_system_variables
|
9
9
|
<<-EOF
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
let TOTAL_PROCESORS="$(cat /proc/cpuinfo|grep ^processor |wc -l)"
|
11
|
+
let MAX_MEMORY_DEFAULT="$(grep MemTotal /proc/meminfo|grep -o "[[:digit:]]*") / ( (1024 * $TOTAL_PROCESORS) / $LSB_MAX_NUM_PROCESSORS )"
|
12
|
+
[ ! -z $LSB_MAX_MEM_RUSAGE ] && let MAX_MEMORY="$LSB_MAX_MEM_RUSAGE" || MAX_MEMORY="$MAX_MEMORY_DEFAULT"
|
13
|
+
export MAX_MEMORY_DEFAULT
|
14
|
+
export MAX_MEMORY
|
15
|
+
export BATCH_JOB_ID=$LSF_JOBID
|
16
|
+
export BATCH_SYSTEM=LSF
|
14
17
|
EOF
|
15
18
|
end
|
16
19
|
|
data/lib/rbbt/hpc/orchestrate.rb
CHANGED
@@ -145,6 +145,8 @@ module HPC
|
|
145
145
|
job_rules.delete :workflow
|
146
146
|
|
147
147
|
|
148
|
+
option_config_keys = options[:config_keys]
|
149
|
+
|
148
150
|
job_options = IndiferentHash.setup(options.merge(job_rules).merge(:batch_dependencies => dep_ids))
|
149
151
|
job_options.delete :orchestration_rules
|
150
152
|
|
@@ -154,6 +156,11 @@ module HPC
|
|
154
156
|
job_options[:config_keys] = job_options[:config_keys] ? config_keys + "," + job_options[:config_keys] : config_keys
|
155
157
|
end
|
156
158
|
|
159
|
+
if option_config_keys
|
160
|
+
option_config_keys = option_config_keys.gsub(/,\s+/,',')
|
161
|
+
job_options[:config_keys] = job_options[:config_keys] ? job_options[:config_keys] + "," + option_config_keys : option_config_keys
|
162
|
+
end
|
163
|
+
|
157
164
|
if options[:piggyback]
|
158
165
|
manifest = options[:piggyback].uniq
|
159
166
|
manifest += [job]
|
@@ -165,7 +172,7 @@ module HPC
|
|
165
172
|
new_config_keys = self.job_rules(rules, job)[:config_keys]
|
166
173
|
if new_config_keys
|
167
174
|
new_config_keys = new_config_keys.gsub(/,\s+/,',')
|
168
|
-
job_options[:config_keys] = job_options[:config_keys] ?
|
175
|
+
job_options[:config_keys] = job_options[:config_keys] ? job_options[:config_keys] + "," + new_config_keys : new_config_keys
|
169
176
|
end
|
170
177
|
|
171
178
|
job_options.delete :piggyback
|
data/lib/rbbt/hpc/slurm.rb
CHANGED
@@ -8,9 +8,13 @@ module HPC
|
|
8
8
|
|
9
9
|
def self.batch_system_variables
|
10
10
|
<<-EOF
|
11
|
-
let
|
12
|
-
|
13
|
-
|
11
|
+
let TOTAL_PROCESORS="$(cat /proc/cpuinfo|grep ^processor |wc -l)"
|
12
|
+
let MAX_MEMORY_DEFAULT="$(grep MemTotal /proc/meminfo|grep -o "[[:digit:]]*") / ( (1024 * $TOTAL_PROCESORS) / $SLURM_CPUS_PER_TASK )"
|
13
|
+
[ ! -z $SLURM_MEM_PER_CPU ] && let MAX_MEMORY="$SLURM_MEM_PER_CPU * $SLURM_CPUS_PER_TASK" || MAX_MEMORY="$MAX_MEMORY_DEFAULT"
|
14
|
+
export MAX_MEMORY_DEFAULT
|
15
|
+
export MAX_MEMORY
|
16
|
+
export BATCH_JOB_ID=$SLURM_JOB_ID
|
17
|
+
export BATCH_SYSTEM=SLURM
|
14
18
|
EOF
|
15
19
|
end
|
16
20
|
|
data/lib/rbbt/tsv/csv.rb
CHANGED
@@ -8,6 +8,7 @@ module TSV
|
|
8
8
|
noheaders = ! headers
|
9
9
|
|
10
10
|
type = options.delete :type
|
11
|
+
cast = options.delete :cast
|
11
12
|
merge = options.delete :merge
|
12
13
|
key_field = options.delete :key_field
|
13
14
|
fields = options.delete :fields
|
@@ -46,6 +47,10 @@ module TSV
|
|
46
47
|
else
|
47
48
|
key, *values = row
|
48
49
|
end
|
50
|
+
|
51
|
+
if cast
|
52
|
+
values = values.collect{|v| v.send cast }
|
53
|
+
end
|
49
54
|
|
50
55
|
case type
|
51
56
|
when :double, :flat
|
data/lib/rbbt/tsv/parser.rb
CHANGED
@@ -40,7 +40,7 @@ module TSV
|
|
40
40
|
# Process fields line
|
41
41
|
|
42
42
|
preamble << line if line
|
43
|
-
while line
|
43
|
+
while line && (TrueClass === @header_hash || (String === @header_hash && Misc.fixutf8(line) =~ /^#{@header_hash}/ ))
|
44
44
|
@fields = line.split(@sep, -1)
|
45
45
|
@key_field = @fields.shift
|
46
46
|
@key_field = @key_field[(0 + header_hash.length)..-1] if String === @header_hash
|
@@ -49,7 +49,7 @@ module TSV
|
|
49
49
|
line = (@header_hash != "" ? stream.gets : nil)
|
50
50
|
line = Misc.fixutf8 line.chomp if line
|
51
51
|
preamble << line if line
|
52
|
-
@header_hash = false if TrueClass === @header_hash
|
52
|
+
@header_hash = false if TrueClass === @header_hash || @header_hash == ""
|
53
53
|
end
|
54
54
|
|
55
55
|
@preamble = preamble[0..-3] * "\n"
|
data/lib/rbbt/tsv/stream.rb
CHANGED
data/lib/rbbt/util/misc/pipes.rb
CHANGED
data/lib/rbbt/util/open.rb
CHANGED
@@ -11,7 +11,7 @@ module Open
|
|
11
11
|
class OpenGzipError < StandardError; end
|
12
12
|
|
13
13
|
REMOTE_CACHEDIR = File.join(ENV["HOME"], "/tmp/open_cache")
|
14
|
-
FileUtils.mkdir_p REMOTE_CACHEDIR unless File.exist? REMOTE_CACHEDIR
|
14
|
+
#FileUtils.mkdir_p REMOTE_CACHEDIR unless File.exist? REMOTE_CACHEDIR
|
15
15
|
|
16
16
|
GREP_CMD = begin
|
17
17
|
if ENV["GREP_CMD"]
|
data/lib/rbbt/workflow.rb
CHANGED
@@ -187,17 +187,21 @@ module Workflow
|
|
187
187
|
clean_name = wf_name.sub(/::.*/,'')
|
188
188
|
Log.info{"Looking for '#{wf_name}' in '#{clean_name}'"}
|
189
189
|
require_workflow clean_name
|
190
|
-
|
190
|
+
workflow = Misc.string2const Misc.camel_case(wf_name)
|
191
|
+
workflow.load_documentation
|
192
|
+
return workflow
|
191
193
|
end
|
192
194
|
|
193
195
|
Log.high{"Loading workflow #{wf_name}"}
|
194
196
|
require_local_workflow(wf_name) or
|
195
197
|
(Workflow.autoinstall and `rbbt workflow install #{Misc.snake_case(wf_name)} || rbbt workflow install #{wf_name}` and require_local_workflow(wf_name)) or raise("Workflow not found or could not be loaded: #{ wf_name }")
|
196
|
-
begin
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
198
|
+
workflow = begin
|
199
|
+
Misc.string2const Misc.camel_case(wf_name)
|
200
|
+
rescue
|
201
|
+
Workflow.workflows.last || true
|
202
|
+
end
|
203
|
+
workflow.load_documentation
|
204
|
+
workflow
|
201
205
|
end
|
202
206
|
|
203
207
|
attr_accessor :description
|
@@ -74,6 +74,7 @@ module Workflow
|
|
74
74
|
def dep_task(name, workflow, oname, *rest, &block)
|
75
75
|
dep(workflow, oname, *rest, &block)
|
76
76
|
extension workflow.tasks[oname].extension if workflow.tasks.include?(oname) unless @extension
|
77
|
+
returns workflow.tasks[oname].result_description if workflow.tasks.include?(oname) unless @result_description
|
77
78
|
task name do
|
78
79
|
raise RbbtException, "dependency not found in dep_task" if dependencies.empty?
|
79
80
|
dep = dependencies.last
|
data/lib/rbbt/workflow/doc.rb
CHANGED
@@ -45,10 +45,25 @@ module Workflow
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def load_documentation
|
48
|
-
@documentation
|
48
|
+
return if @documentation
|
49
|
+
@documentation ||= Workflow.parse_workflow_doc documentation_markdown
|
49
50
|
@documentation[:tasks].each do |task, description|
|
50
|
-
|
51
|
-
|
51
|
+
if task.include? "#"
|
52
|
+
workflow, task = task.split("#")
|
53
|
+
workflow = begin
|
54
|
+
Kernel.const_get workflow
|
55
|
+
rescue
|
56
|
+
next
|
57
|
+
end
|
58
|
+
else
|
59
|
+
workflow = self
|
60
|
+
end
|
61
|
+
|
62
|
+
if workflow.tasks.include? task.to_sym
|
63
|
+
workflow.tasks[task.to_sym].description = description
|
64
|
+
else
|
65
|
+
Log.low "Documentation for #{ task }, but not a #{ workflow.to_s } task"
|
66
|
+
end
|
52
67
|
end
|
53
68
|
end
|
54
69
|
|
data/lib/rbbt/workflow/step.rb
CHANGED
@@ -458,6 +458,10 @@ class Step
|
|
458
458
|
end
|
459
459
|
|
460
460
|
def clean
|
461
|
+
if ! Open.exists?(info_file)
|
462
|
+
Log.high "Refusing to clean step with no .info file: #{path}"
|
463
|
+
return self
|
464
|
+
end
|
461
465
|
status = []
|
462
466
|
status << "dirty" if done? && dirty?
|
463
467
|
status << "not running" if ! done? && ! running?
|
@@ -97,6 +97,7 @@ class Step
|
|
97
97
|
Open.ln_s(value.path, path)
|
98
98
|
when type.to_s == "file"
|
99
99
|
if String === value && File.exists?(value)
|
100
|
+
value = File.expand_path(value)
|
100
101
|
Open.ln_s(value, path)
|
101
102
|
else
|
102
103
|
value = value.collect{|v| v = "#{v}" if Path === v; v }if Array === value
|
@@ -1,12 +1,8 @@
|
|
1
1
|
require 'rbbt/util/R'
|
2
2
|
|
3
3
|
module Workflow
|
4
|
-
def self.
|
5
|
-
|
6
|
-
jobs = []
|
7
|
-
seed_jobs.each{|j| jobs << j; jobs += j.rec_dependencies}
|
8
|
-
|
9
|
-
data = TSV.setup({}, "Job~Workflow,Task,Start,End#:type=:list")
|
4
|
+
def self.trace_job_times(jobs, fix_gap = false)
|
5
|
+
data = TSV.setup({}, "Job~Code,Workflow,Task,Start,End#:type=:list")
|
10
6
|
min_start = nil
|
11
7
|
max_done = nil
|
12
8
|
jobs.each do |job|
|
@@ -14,10 +10,10 @@ module Workflow
|
|
14
10
|
started = job.info[:started]
|
15
11
|
ddone = job.info[:done]
|
16
12
|
|
17
|
-
code = [job.workflow, job.task_name].compact.collect{|s| s.to_s} * "
|
18
|
-
code =
|
13
|
+
code = [job.workflow, job.task_name].compact.collect{|s| s.to_s} * " · "
|
14
|
+
code = job.name + " - " + code
|
19
15
|
|
20
|
-
data[
|
16
|
+
data[job.path] = [code,job.workflow.to_s, job.task_name, started, ddone]
|
21
17
|
if min_start.nil?
|
22
18
|
min_start = started
|
23
19
|
else
|
@@ -39,7 +35,7 @@ module Workflow
|
|
39
35
|
value["End"] - min_start
|
40
36
|
end
|
41
37
|
|
42
|
-
if
|
38
|
+
if fix_gap
|
43
39
|
ranges = []
|
44
40
|
data.through do |k,values|
|
45
41
|
start, eend = values.values_at "Start.second", "End.second"
|
@@ -67,115 +63,161 @@ module Workflow
|
|
67
63
|
gap = Misc.sum(gaps.select{|pos,size| pos < values["Start.second"]}.collect{|pos,size| size})
|
68
64
|
value - gap
|
69
65
|
end
|
66
|
+
|
67
|
+
total_gaps = Misc.sum(gaps.collect{|k,v| v})
|
68
|
+
Log.info "Total gaps: #{total_gaps} seconds"
|
70
69
|
end
|
71
70
|
|
71
|
+
start = data.column("Start.second").values.flatten.collect{|v| v.to_f}.min
|
72
|
+
eend = data.column("End.second").values.flatten.collect{|v| v.to_f}.max
|
73
|
+
total = eend - start unless eend.nil? || start.nil?
|
74
|
+
Log.info "Total time elapsed: #{total} seconds" if total
|
75
|
+
|
76
|
+
data
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.plot_trace_job_times(data, plot, width=800, height=800)
|
80
|
+
data.R <<-EOF, [:svg]
|
81
|
+
rbbt.require('tidyverse')
|
82
|
+
rbbt.require('ggplot2')
|
83
|
+
|
84
|
+
names(data) <- make.names(names(data))
|
85
|
+
data$id = data$Code
|
86
|
+
data$content = data$Task
|
87
|
+
data$start = data$Start
|
88
|
+
data$end = data$End
|
89
|
+
data$Project = data$Workflow
|
90
|
+
|
91
|
+
tasks = data
|
92
|
+
|
93
|
+
#theme_gantt <- function(base_size=11, base_family="Source Sans Pro Light") {
|
94
|
+
theme_gantt <- function(base_size=11, base_family="Sans Serif") {
|
95
|
+
ret <- theme_bw(base_size, base_family) %+replace%
|
96
|
+
theme(panel.background = element_rect(fill="#ffffff", colour=NA),
|
97
|
+
axis.title.x=element_text(vjust=-0.2), axis.title.y=element_text(vjust=1.5),
|
98
|
+
title=element_text(vjust=1.2, family="Source Sans Pro Semibold"),
|
99
|
+
panel.border = element_blank(), axis.line=element_blank(),
|
100
|
+
panel.grid.minor=element_blank(),
|
101
|
+
panel.grid.major.y = element_blank(),
|
102
|
+
panel.grid.major.x = element_line(size=0.5, colour="grey80"),
|
103
|
+
axis.ticks=element_blank(),
|
104
|
+
legend.position="bottom",
|
105
|
+
axis.title=element_text(size=rel(1.2), family="Source Sans Pro Semibold"),
|
106
|
+
strip.text=element_text(size=rel(1.5), family="Source Sans Pro Semibold"),
|
107
|
+
strip.background=element_rect(fill="#ffffff", colour=NA),
|
108
|
+
panel.spacing.y=unit(1.5, "lines"),
|
109
|
+
legend.key = element_blank())
|
110
|
+
|
111
|
+
ret
|
112
|
+
}
|
113
|
+
|
114
|
+
tasks.long <- tasks %>%
|
115
|
+
gather(date.type, task.date, -c(Code,Project, Task, id, Start.second, End.second)) %>%
|
116
|
+
arrange(date.type, task.date) %>%
|
117
|
+
mutate(id = factor(id, levels=rev(unique(id)), ordered=TRUE))
|
118
|
+
|
119
|
+
x.breaks <- seq(length(tasks$Task) + 0.5 - 3, 0, by=-3)
|
120
|
+
|
121
|
+
timeline <- ggplot(tasks.long, aes(y=id, yend=id, x=Start.second, xend=End.second, colour=Task)) +
|
122
|
+
geom_segment() +
|
123
|
+
geom_vline(xintercept=x.breaks, colour="grey80", linetype="dotted") +
|
124
|
+
guides(colour=guide_legend(title=NULL)) +
|
125
|
+
labs(x=NULL, y=NULL) +
|
126
|
+
theme_gantt() + theme(axis.text.x=element_text(angle=45, hjust=1))
|
127
|
+
|
128
|
+
rbbt.png_plot('#{plot}', 'plot(timeline)', width=#{width}, height=#{height}, pointsize=6)
|
129
|
+
EOF
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.trace_job_summary(jobs, report_keys = [])
|
72
133
|
tasks_info = {}
|
73
134
|
|
135
|
+
report_keys = report_keys.collect{|k| k.to_s}
|
136
|
+
|
74
137
|
jobs.each do |dep|
|
75
138
|
next unless dep.info[:done]
|
76
139
|
task = [dep.workflow, dep.task_name].compact.collect{|s| s.to_s} * "#"
|
77
|
-
info = tasks_info[task] ||= {}
|
140
|
+
info = tasks_info[task] ||= IndiferentHash.setup({})
|
141
|
+
dep_info = IndiferentHash.setup(dep.info)
|
78
142
|
|
79
|
-
time =
|
143
|
+
time = dep_info[:done] - dep_info[:started]
|
80
144
|
info[:time] ||= []
|
81
145
|
info[:time] << time
|
82
146
|
|
83
|
-
|
84
|
-
|
85
|
-
|
147
|
+
report_keys.each do |key|
|
148
|
+
info[key] = dep_info[key]
|
149
|
+
end
|
150
|
+
|
86
151
|
dep.info[:config_keys].select do |kinfo|
|
87
152
|
key, value, tokens = kinfo
|
88
|
-
key = key.to_s
|
89
|
-
cpus = value if key.include? 'cpu'
|
90
|
-
spark = value if key == 'spark'
|
91
|
-
shard = value if key == 'shard'
|
92
|
-
end
|
93
153
|
|
94
|
-
|
95
|
-
|
96
|
-
info[:shard] = shard
|
154
|
+
info[key.to_s] = value if report_keys.include? key.to_s
|
155
|
+
end
|
97
156
|
end
|
98
157
|
|
99
|
-
|
158
|
+
summary = TSV.setup({}, "Task~Calls,Avg. Time,Total Time#:type=:list")
|
100
159
|
|
101
160
|
tasks_info.each do |task, info|
|
102
|
-
time_lists
|
103
|
-
avg_time = Misc.mean(time_lists)
|
104
|
-
total_time = Misc.sum(time_lists)
|
161
|
+
time_lists = info[:time]
|
162
|
+
avg_time = Misc.mean(time_lists).to_i
|
163
|
+
total_time = Misc.sum(time_lists).to_i
|
105
164
|
calls = time_lists.length
|
106
|
-
|
165
|
+
summary[task] = [calls, avg_time, total_time]
|
107
166
|
end
|
108
167
|
|
109
|
-
|
168
|
+
report_keys.each do |key|
|
169
|
+
summary.add_field Misc.humanize(key) do |task|
|
170
|
+
tasks_info[task][key]
|
171
|
+
end
|
172
|
+
end if Array === report_keys && report_keys.any?
|
110
173
|
|
111
|
-
|
112
|
-
|
113
|
-
total = eend - start
|
114
|
-
Log.info "Total time elapsed: #{total} seconds"
|
174
|
+
summary
|
175
|
+
end
|
115
176
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
177
|
+
def self.trace(seed_jobs, options = {})
|
178
|
+
jobs = []
|
179
|
+
seed_jobs.each do |step|
|
180
|
+
jobs += step.rec_dependencies + [step]
|
181
|
+
step.info[:archived_info].each do |path,ainfo|
|
182
|
+
archived_step = Step.new path
|
183
|
+
|
184
|
+
archived_step.define_singleton_method :info do
|
185
|
+
ainfo
|
186
|
+
end
|
187
|
+
|
188
|
+
#class << archived_step
|
189
|
+
# self
|
190
|
+
#end.define_method :info do
|
191
|
+
# ainfo
|
192
|
+
#end
|
193
|
+
|
194
|
+
jobs << archived_step
|
195
|
+
end if step.info[:archived_info]
|
120
196
|
|
121
|
-
plot, width, height = options.values_at :plot, :width, :height
|
122
|
-
if plot
|
123
|
-
data.R <<-EOF, [:svg]
|
124
|
-
rbbt.require('tidyverse')
|
125
|
-
rbbt.require('ggplot2')
|
126
|
-
|
127
|
-
names(data) <- make.names(names(data))
|
128
|
-
data$id = rownames(data)
|
129
|
-
data$content = data$Task
|
130
|
-
data$start = data$Start
|
131
|
-
data$end = data$End
|
132
|
-
data$Project = data$Workflow
|
133
|
-
|
134
|
-
tasks = data
|
135
|
-
|
136
|
-
#theme_gantt <- function(base_size=11, base_family="Source Sans Pro Light") {
|
137
|
-
theme_gantt <- function(base_size=11, base_family="Sans Serif") {
|
138
|
-
ret <- theme_bw(base_size, base_family) %+replace%
|
139
|
-
theme(panel.background = element_rect(fill="#ffffff", colour=NA),
|
140
|
-
axis.title.x=element_text(vjust=-0.2), axis.title.y=element_text(vjust=1.5),
|
141
|
-
title=element_text(vjust=1.2, family="Source Sans Pro Semibold"),
|
142
|
-
panel.border = element_blank(), axis.line=element_blank(),
|
143
|
-
panel.grid.minor=element_blank(),
|
144
|
-
panel.grid.major.y = element_blank(),
|
145
|
-
panel.grid.major.x = element_line(size=0.5, colour="grey80"),
|
146
|
-
axis.ticks=element_blank(),
|
147
|
-
legend.position="bottom",
|
148
|
-
axis.title=element_text(size=rel(1.2), family="Source Sans Pro Semibold"),
|
149
|
-
strip.text=element_text(size=rel(1.5), family="Source Sans Pro Semibold"),
|
150
|
-
strip.background=element_rect(fill="#ffffff", colour=NA),
|
151
|
-
panel.spacing.y=unit(1.5, "lines"),
|
152
|
-
legend.key = element_blank())
|
153
|
-
|
154
|
-
ret
|
155
|
-
}
|
156
|
-
|
157
|
-
tasks.long <- tasks %>%
|
158
|
-
gather(date.type, task.date, -c(Project, Task, id, Start.second, End.second)) %>%
|
159
|
-
arrange(date.type, task.date) %>%
|
160
|
-
mutate(id = factor(id, levels=rev(unique(id)), ordered=TRUE))
|
161
|
-
|
162
|
-
x.breaks <- seq(length(tasks$Task) + 0.5 - 3, 0, by=-3)
|
163
|
-
|
164
|
-
timeline <- ggplot(tasks.long, aes(y=id, yend=id, x=Start.second, xend=End.second, colour=Task)) +
|
165
|
-
geom_segment() +
|
166
|
-
geom_vline(xintercept=x.breaks, colour="grey80", linetype="dotted") +
|
167
|
-
guides(colour=guide_legend(title=NULL)) +
|
168
|
-
labs(x=NULL, y=NULL) +
|
169
|
-
theme_gantt() + theme(axis.text.x=element_text(angle=45, hjust=1))
|
170
|
-
|
171
|
-
rbbt.png_plot('#{plot}', 'plot(timeline)', width=#{width}, height=#{height}, pointsize=6)
|
172
|
-
EOF
|
173
197
|
end
|
174
198
|
|
199
|
+
jobs = jobs.uniq.sort_by{|job| t = job.info[:started]; t || Open.mtime(job.path) || Time.now }
|
200
|
+
|
201
|
+
data = trace_job_times(jobs, options[:fix_gap])
|
202
|
+
|
203
|
+
report_keys = options[:report_keys] || ""
|
204
|
+
report_keys = report_keys.split(/,\s*/) if String === report_keys
|
205
|
+
summary = trace_job_summary(jobs, report_keys)
|
206
|
+
|
207
|
+
raise "No jobs to process" if data.size == 0
|
208
|
+
|
209
|
+
plot, size, width, height = options.values_at :plot, :size, :width, :height
|
210
|
+
|
211
|
+
size = 800 if size.nil?
|
212
|
+
width = size.to_i * 2 if width.nil?
|
213
|
+
height = size if height.nil?
|
214
|
+
|
215
|
+
plot_trace_job_times(data, plot, width, height) if plot
|
216
|
+
|
175
217
|
if options[:plot_data]
|
176
218
|
data
|
177
219
|
else
|
178
|
-
|
220
|
+
summary
|
179
221
|
end
|
180
222
|
|
181
223
|
end
|
@@ -36,33 +36,7 @@ end
|
|
36
36
|
batch_system = options.delete :batch_system
|
37
37
|
batch_system ||= 'auto'
|
38
38
|
|
39
|
-
HPC::BATCH_MODULE =
|
40
|
-
when 'slurm'
|
41
|
-
HPC::SLURM
|
42
|
-
when 'lsf'
|
43
|
-
HPC::LSF
|
44
|
-
when 'auto'
|
45
|
-
case $previous_commands.last
|
46
|
-
when 'slurm'
|
47
|
-
HPC::SLURM
|
48
|
-
when 'lsf'
|
49
|
-
HPC::LSF
|
50
|
-
else
|
51
|
-
case Rbbt::Config.get(:batch_system, :batch, :batch_system, :hpc, :HPC, :BATCH).to_s.downcase
|
52
|
-
when 'slurm'
|
53
|
-
HPC::SLURM
|
54
|
-
when 'lsf'
|
55
|
-
HPC::LSF
|
56
|
-
else
|
57
|
-
case ENV["BATCH_SYSTEM"].to_s.downcase
|
58
|
-
when 'slurm'
|
59
|
-
HPC::SLURM
|
60
|
-
when 'lsf'
|
61
|
-
HPC::LSF
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
39
|
+
HPC::BATCH_MODULE = HPC.batch_system batch_system
|
66
40
|
|
67
41
|
raise ParameterException.new("Could not detect batch_system: #{Misc.fingerprint batch_system}") if HPC::BATCH_MODULE.nil?
|
68
42
|
|
@@ -40,33 +40,7 @@ end
|
|
40
40
|
batch_system = options.delete :batch_system
|
41
41
|
batch_system ||= 'auto'
|
42
42
|
|
43
|
-
HPC::BATCH_MODULE =
|
44
|
-
when 'slurm'
|
45
|
-
HPC::SLURM
|
46
|
-
when 'lsf'
|
47
|
-
HPC::LSF
|
48
|
-
when 'auto'
|
49
|
-
case $previous_commands.last
|
50
|
-
when 'slurm'
|
51
|
-
HPC::SLURM
|
52
|
-
when 'lsf'
|
53
|
-
HPC::LSF
|
54
|
-
else
|
55
|
-
case Rbbt::Config.get(:batch_system, :batch, :batch_system, :hpc, :HPC, :BATCH).to_s.downcase
|
56
|
-
when 'slurm'
|
57
|
-
HPC::SLURM
|
58
|
-
when 'lsf'
|
59
|
-
HPC::LSF
|
60
|
-
else
|
61
|
-
case ENV["BATCH_SYSTEM"].to_s.downcase
|
62
|
-
when 'slurm'
|
63
|
-
HPC::SLURM
|
64
|
-
when 'lsf'
|
65
|
-
HPC::LSF
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
43
|
+
HPC::BATCH_MODULE = HPC.batch_system batch_system
|
70
44
|
|
71
45
|
raise ParameterException.new("Could not detect batch_system: #{Misc.fingerprint batch_system}") if HPC::BATCH_MODULE.nil?
|
72
46
|
|
@@ -108,7 +82,7 @@ workdir.glob("**/command.batch").sort_by{|f| File.mtime(f)}.each do |fcmd|
|
|
108
82
|
cmd = nil
|
109
83
|
end
|
110
84
|
|
111
|
-
if m = command_txt.match(/^BATCH_SYSTEM=(.*)/)
|
85
|
+
if m = command_txt.match(/^export BATCH_SYSTEM=(.*)/)
|
112
86
|
job_batch_system = m[1].downcase
|
113
87
|
else
|
114
88
|
job_batch_system = nil
|
@@ -235,6 +209,8 @@ workdir.glob("**/command.batch").sort_by{|f| File.mtime(f)}.each do |fcmd|
|
|
235
209
|
text = CMD.cmd('grep "^#SBATCH" |tail -n +5', :in => Open.read(fcmd)).read.strip
|
236
210
|
when 'lsf'
|
237
211
|
text = CMD.cmd('grep "^#BSUB" |tail -n +5', :in => Open.read(fcmd)).read.strip
|
212
|
+
else
|
213
|
+
text = ""
|
238
214
|
end
|
239
215
|
lines = text.split("\n").collect{|line| header, _sep, value = line.partition(/\s+/); Log.color(:yellow, header + ": ") + value}
|
240
216
|
puts Log.color :yellow, lines * "\n"
|
@@ -34,33 +34,7 @@ EOF
|
|
34
34
|
batch_system = $slurm_options.delete :batch_system
|
35
35
|
batch_system ||= 'auto'
|
36
36
|
|
37
|
-
HPC::BATCH_MODULE =
|
38
|
-
when 'slurm'
|
39
|
-
HPC::SLURM
|
40
|
-
when 'lsf'
|
41
|
-
HPC::LSF
|
42
|
-
when 'auto'
|
43
|
-
case $previous_commands.last
|
44
|
-
when 'slurm'
|
45
|
-
HPC::SLURM
|
46
|
-
when 'lsf'
|
47
|
-
HPC::LSF
|
48
|
-
else
|
49
|
-
case Rbbt::Config.get(:batch_system, :batch, :batch_system, :hpc, :HPC, :BATCH).to_s.downcase
|
50
|
-
when 'slurm'
|
51
|
-
HPC::SLURM
|
52
|
-
when 'lsf'
|
53
|
-
HPC::LSF
|
54
|
-
else
|
55
|
-
case ENV["BATCH_SYSTEM"].to_s.downcase
|
56
|
-
when 'slurm'
|
57
|
-
HPC::SLURM
|
58
|
-
when 'lsf'
|
59
|
-
HPC::LSF
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
37
|
+
HPC::BATCH_MODULE = HPC.batch_system batch_system
|
64
38
|
|
65
39
|
raise ParameterException.new("Could not detect batch_system: #{Misc.fingerprint batch_system}") if HPC::BATCH_MODULE.nil?
|
66
40
|
|
@@ -40,33 +40,7 @@ end
|
|
40
40
|
batch_system = options.delete :batch_system
|
41
41
|
batch_system ||= 'auto'
|
42
42
|
|
43
|
-
HPC::BATCH_MODULE =
|
44
|
-
when 'slurm'
|
45
|
-
HPC::SLURM
|
46
|
-
when 'lsf'
|
47
|
-
HPC::LSF
|
48
|
-
when 'auto'
|
49
|
-
case $previous_commands.last
|
50
|
-
when 'slurm'
|
51
|
-
HPC::SLURM
|
52
|
-
when 'lsf'
|
53
|
-
HPC::LSF
|
54
|
-
else
|
55
|
-
case Rbbt::Config.get(:batch_system, :batch, :batch_system, :hpc, :HPC, :BATCH).to_s.downcase
|
56
|
-
when 'slurm'
|
57
|
-
HPC::SLURM
|
58
|
-
when 'lsf'
|
59
|
-
HPC::LSF
|
60
|
-
else
|
61
|
-
case ENV["BATCH_SYSTEM"].to_s.downcase
|
62
|
-
when 'slurm'
|
63
|
-
HPC::SLURM
|
64
|
-
when 'lsf'
|
65
|
-
HPC::LSF
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
43
|
+
HPC::BATCH_MODULE = HPC.batch_system batch_system
|
70
44
|
|
71
45
|
raise ParameterException.new("Could not detect batch_system: #{Misc.fingerprint batch_system}") if HPC::BATCH_MODULE.nil?
|
72
46
|
|
@@ -33,33 +33,7 @@ EOF
|
|
33
33
|
batch_system = $slurm_options.delete :batch_system
|
34
34
|
batch_system ||= 'auto'
|
35
35
|
|
36
|
-
HPC::BATCH_MODULE =
|
37
|
-
when 'slurm'
|
38
|
-
HPC::SLURM
|
39
|
-
when 'lsf'
|
40
|
-
HPC::LSF
|
41
|
-
when 'auto'
|
42
|
-
case $previous_commands.last
|
43
|
-
when 'slurm'
|
44
|
-
HPC::SLURM
|
45
|
-
when 'lsf'
|
46
|
-
HPC::LSF
|
47
|
-
else
|
48
|
-
case Rbbt::Config.get(:batch_system, :batch, :batch_system, :hpc, :HPC, :BATCH).to_s.downcase
|
49
|
-
when 'slurm'
|
50
|
-
HPC::SLURM
|
51
|
-
when 'lsf'
|
52
|
-
HPC::LSF
|
53
|
-
else
|
54
|
-
case ENV["BATCH_SYSTEM"].to_s.downcase
|
55
|
-
when 'slurm'
|
56
|
-
HPC::SLURM
|
57
|
-
when 'lsf'
|
58
|
-
HPC::LSF
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
36
|
+
HPC::BATCH_MODULE = HPC.batch_system batch_system
|
63
37
|
|
64
38
|
raise ParameterException.new("Could not detect batch_system: #{Misc.fingerprint batch_system}") if HPC::BATCH_MODULE.nil?
|
65
39
|
|
@@ -8,9 +8,11 @@ $0 = "rbbt #{$previous_commands*" "} #{ File.basename(__FILE__) }" if $previous_
|
|
8
8
|
|
9
9
|
options = SOPT.setup <<EOF
|
10
10
|
|
11
|
-
Make a job forget
|
11
|
+
Make a job forget its dependencies and archive their meta-data
|
12
12
|
|
13
|
-
$ #{$0} [options] <job_path>
|
13
|
+
$ #{$0} [options] <job_path> [<task_name>|<workflow>#<task_name>] ...
|
14
|
+
|
15
|
+
Specific dependencies can be specified as . Otherwise, all are forgoten.
|
14
16
|
|
15
17
|
-h--help Print this help
|
16
18
|
-p--purge Purge dependencies
|
@@ -26,7 +28,7 @@ if options[:help]
|
|
26
28
|
exit 0
|
27
29
|
end
|
28
30
|
|
29
|
-
path = ARGV
|
31
|
+
path, *remove = ARGV
|
30
32
|
|
31
33
|
raise ParameterException, "No path given" if path.nil?
|
32
34
|
step = Workflow.load_step path
|
@@ -34,10 +36,20 @@ step = Workflow.load_step path
|
|
34
36
|
step.archive_deps
|
35
37
|
step.copy_files_dir
|
36
38
|
dependencies = step.dependencies
|
37
|
-
step.set_info :dependencies, []
|
38
39
|
|
39
|
-
if
|
40
|
+
if remove && remove.any?
|
41
|
+
remove_paths = dependencies.select do |dep|
|
42
|
+
remove.include?(dep.task_name) || remove.include?([dep.workflow.to_s, dep.task_name] * "#")
|
43
|
+
end.collect{|dep| dep.path }
|
44
|
+
else
|
45
|
+
remove_paths = dependencies.collect{|dep| dep.path }
|
46
|
+
end
|
47
|
+
|
48
|
+
step.set_info :dependencies, step.info[:dependencies].reject{|info| remove_paths.include? info.last}
|
49
|
+
|
50
|
+
if options[:purge] || options[:recursive_purge]
|
40
51
|
dependencies.each do |dependency|
|
52
|
+
next unless remove_paths.include? dependency.path
|
41
53
|
Step.purge(dependency.path, options[:recursive_purge])
|
42
54
|
end
|
43
55
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'rbbt/workflow'
|
4
|
+
require 'rbbt/workflow/util/trace'
|
4
5
|
|
5
6
|
require 'rbbt-util'
|
6
7
|
require 'fileutils'
|
@@ -16,30 +17,26 @@ require 'rbbt/util/R'
|
|
16
17
|
$0 = "rbbt #{$previous_commands*""} #{ File.basename(__FILE__) }" if $previous_commands
|
17
18
|
|
18
19
|
options = SOPT.setup <<EOF
|
19
|
-
Examine the
|
20
|
+
Examine the execution trace of a job or set of jobs
|
20
21
|
|
21
22
|
$ rbbt workflow trace <job-result>
|
22
23
|
|
23
24
|
-h--help Help
|
25
|
+
-fg--fix_gap Remove execution gaps
|
26
|
+
-rk--report_keys* Config keys and info fields to report
|
27
|
+
-p--plot* Plot file
|
24
28
|
-w--width* Image Width
|
25
29
|
-h--height* Image Height
|
26
|
-
-p--plot* Plot file
|
27
30
|
-s--size* Image Size (Height and Width)
|
28
|
-
-fg--fix_gap Remove execution gaps
|
29
31
|
-pd--plot_data Print plot data
|
30
32
|
EOF
|
31
33
|
|
32
34
|
SOPT.usage if options[:help]
|
33
35
|
|
36
|
+
|
34
37
|
files = ARGV
|
35
38
|
plot = options[:plot]
|
36
39
|
|
37
|
-
width, height, size = options.values_at :width, :height, :size
|
38
|
-
|
39
|
-
size = 800 if size.nil?
|
40
|
-
width = size if width.nil?
|
41
|
-
height = size if height.nil?
|
42
|
-
|
43
40
|
def get_step(file)
|
44
41
|
file = File.expand_path(file)
|
45
42
|
file = file.sub(/\.(info|files)/,'')
|
@@ -47,191 +44,8 @@ def get_step(file)
|
|
47
44
|
end
|
48
45
|
|
49
46
|
jobs = []
|
50
|
-
files.
|
51
|
-
|
52
|
-
|
53
|
-
jobs += step.rec_dependencies + [step]
|
54
|
-
|
55
|
-
step.info[:archived_info].each do |path,ainfo|
|
56
|
-
archived_step = Step.new path
|
57
|
-
class << archived_step
|
58
|
-
self
|
59
|
-
end.define_method :info do
|
60
|
-
ainfo
|
61
|
-
end
|
62
|
-
jobs << archived_step
|
63
|
-
end if step.info[:archived_info]
|
64
|
-
end
|
65
|
-
|
66
|
-
jobs = jobs.select{|job| job.info[:done]}.sort_by{|job| job.info[:started]}
|
67
|
-
|
68
|
-
data = TSV.setup({}, "Job~Workflow,Task,Start,End#:type=:list")
|
69
|
-
min_start = nil
|
70
|
-
max_done = nil
|
71
|
-
jobs.each do |job|
|
72
|
-
next unless job.info[:done]
|
73
|
-
started = job.info[:started]
|
74
|
-
ddone = job.info[:done]
|
75
|
-
|
76
|
-
code = [job.workflow, job.task_name].compact.collect{|s| s.to_s} * "."
|
77
|
-
code = code + '.' + job.name
|
78
|
-
|
79
|
-
data[code] = [job.workflow.to_s, job.task_name, started, ddone]
|
80
|
-
if min_start.nil?
|
81
|
-
min_start = started
|
82
|
-
else
|
83
|
-
min_start = started if started < min_start
|
84
|
-
end
|
85
|
-
|
86
|
-
if max_done.nil?
|
87
|
-
max_done = ddone
|
88
|
-
else
|
89
|
-
max_done = ddone if ddone > max_done
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
data.add_field "Start.second" do |k,value|
|
94
|
-
value["Start"] - min_start
|
95
|
-
end
|
96
|
-
|
97
|
-
data.add_field "End.second" do |k,value|
|
98
|
-
value["End"] - min_start
|
99
|
-
end
|
100
|
-
|
101
|
-
if options[:fix_gap]
|
102
|
-
ranges = []
|
103
|
-
data.through do |k,values|
|
104
|
-
start, eend = values.values_at "Start.second", "End.second"
|
105
|
-
|
106
|
-
ranges << (start..eend)
|
107
|
-
end
|
108
|
-
|
109
|
-
gaps = {}
|
110
|
-
last = nil
|
111
|
-
Misc.collapse_ranges(ranges).each do |range|
|
112
|
-
start = range.begin
|
113
|
-
eend = range.end
|
114
|
-
if last
|
115
|
-
gaps[last] = start - last
|
116
|
-
end
|
117
|
-
last = eend
|
118
|
-
end
|
119
|
-
|
120
|
-
data.process "End.second" do |value,k,values|
|
121
|
-
gap = Misc.sum(gaps.select{|pos,size| pos < values["Start.second"]}.collect{|pos,size| size})
|
122
|
-
value - gap
|
123
|
-
end
|
124
|
-
|
125
|
-
data.process "Start.second" do |value,k,values|
|
126
|
-
gap = Misc.sum(gaps.select{|pos,size| pos < values["Start.second"]}.collect{|pos,size| size})
|
127
|
-
value - gap
|
128
|
-
end
|
47
|
+
jobs = files.collect do |file|
|
48
|
+
get_step file
|
129
49
|
end
|
130
50
|
|
131
|
-
|
132
|
-
|
133
|
-
jobs.each do |dep|
|
134
|
-
next unless dep.info[:done]
|
135
|
-
task = [dep.workflow, dep.task_name].compact.collect{|s| s.to_s} * "#"
|
136
|
-
info = tasks_info[task] ||= {}
|
137
|
-
|
138
|
-
time = dep.info[:done] - dep.info[:started]
|
139
|
-
info[:time] ||= []
|
140
|
-
info[:time] << time
|
141
|
-
|
142
|
-
cpus = nil
|
143
|
-
spark = false
|
144
|
-
shard = false
|
145
|
-
dep.info[:config_keys].select do |kinfo|
|
146
|
-
key, value, tokens = kinfo
|
147
|
-
key = key.to_s
|
148
|
-
cpus = value if key.include? 'cpu'
|
149
|
-
spark = value if key == 'spark'
|
150
|
-
shard = value if key == 'shard'
|
151
|
-
end
|
152
|
-
|
153
|
-
info[:cpus] = cpus || 1
|
154
|
-
info[:spark] = spark
|
155
|
-
info[:shard] = shard
|
156
|
-
end
|
157
|
-
|
158
|
-
stats = TSV.setup({}, "Task~Calls,Avg. Time,Total Time,Cpus,Spark,Shard#:type=:list")
|
159
|
-
|
160
|
-
tasks_info.each do |task, info|
|
161
|
-
time_lists, cpus, spark, shard = info.values_at :time, :cpus, :spark, :shard
|
162
|
-
avg_time = Misc.mean(time_lists).to_i
|
163
|
-
total_time = Misc.sum(time_lists)
|
164
|
-
calls = time_lists.length
|
165
|
-
stats[task] = [calls, avg_time, total_time, cpus, spark, shard]
|
166
|
-
end
|
167
|
-
|
168
|
-
raise "No jobs to process" if data.size == 0
|
169
|
-
|
170
|
-
start = data.column("Start.second").values.flatten.collect{|v| v.to_i}.min
|
171
|
-
eend = data.column("End.second").values.flatten.collect{|v| v.to_i}.max
|
172
|
-
total = eend - start
|
173
|
-
Log.info "Total time elapsed: #{total} seconds"
|
174
|
-
|
175
|
-
if options[:fix_gap]
|
176
|
-
total_gaps = Misc.sum(gaps.collect{|k,v| v})
|
177
|
-
Log.info "Total gaps: #{total_gaps} seconds"
|
178
|
-
end
|
179
|
-
|
180
|
-
if options[:plot_data]
|
181
|
-
puts data.to_s
|
182
|
-
else
|
183
|
-
puts stats.to_s
|
184
|
-
end
|
185
|
-
|
186
|
-
if plot
|
187
|
-
data.R <<-EOF, [:svg]
|
188
|
-
rbbt.require('tidyverse')
|
189
|
-
rbbt.require('ggplot2')
|
190
|
-
|
191
|
-
names(data) <- make.names(names(data))
|
192
|
-
data$id = rownames(data)
|
193
|
-
data$content = data$Task
|
194
|
-
data$start = data$Start
|
195
|
-
data$end = data$End
|
196
|
-
data$Project = data$Workflow
|
197
|
-
|
198
|
-
tasks = data
|
199
|
-
|
200
|
-
#theme_gantt <- function(base_size=11, base_family="Source Sans Pro Light") {
|
201
|
-
theme_gantt <- function(base_size=11, base_family="Sans Serif") {
|
202
|
-
ret <- theme_bw(base_size, base_family) %+replace%
|
203
|
-
theme(panel.background = element_rect(fill="#ffffff", colour=NA),
|
204
|
-
axis.title.x=element_text(vjust=-0.2), axis.title.y=element_text(vjust=1.5),
|
205
|
-
title=element_text(vjust=1.2, family="Source Sans Pro Semibold"),
|
206
|
-
panel.border = element_blank(), axis.line=element_blank(),
|
207
|
-
panel.grid.minor=element_blank(),
|
208
|
-
panel.grid.major.y = element_blank(),
|
209
|
-
panel.grid.major.x = element_line(size=0.5, colour="grey80"),
|
210
|
-
axis.ticks=element_blank(),
|
211
|
-
legend.position="bottom",
|
212
|
-
axis.title=element_text(size=rel(1.2), family="Source Sans Pro Semibold"),
|
213
|
-
strip.text=element_text(size=rel(1.5), family="Source Sans Pro Semibold"),
|
214
|
-
strip.background=element_rect(fill="#ffffff", colour=NA),
|
215
|
-
panel.spacing.y=unit(1.5, "lines"),
|
216
|
-
legend.key = element_blank())
|
217
|
-
|
218
|
-
ret
|
219
|
-
}
|
220
|
-
|
221
|
-
tasks.long <- tasks %>%
|
222
|
-
gather(date.type, task.date, -c(Project, Task, id, Start.second, End.second)) %>%
|
223
|
-
arrange(date.type, task.date) %>%
|
224
|
-
mutate(id = factor(id, levels=rev(unique(id)), ordered=TRUE))
|
225
|
-
|
226
|
-
x.breaks <- seq(length(tasks$Task) + 0.5 - 3, 0, by=-3)
|
227
|
-
|
228
|
-
timeline <- ggplot(tasks.long, aes(y=id, yend=id, x=Start.second, xend=End.second, colour=Task)) +
|
229
|
-
geom_segment() +
|
230
|
-
geom_vline(xintercept=x.breaks, colour="grey80", linetype="dotted") +
|
231
|
-
guides(colour=guide_legend(title=NULL)) +
|
232
|
-
labs(x=NULL, y=NULL) +
|
233
|
-
theme_gantt() + theme(axis.text.x=element_text(angle=45, hjust=1))
|
234
|
-
|
235
|
-
rbbt.png_plot('#{plot}', 'plot(timeline)', width=#{width}, height=#{height}, pointsize=6)
|
236
|
-
EOF
|
237
|
-
end
|
51
|
+
puts Workflow.trace(jobs, options)
|
@@ -46,7 +46,21 @@ pid = step.info[:pid]
|
|
46
46
|
host = step.info[:pid_hostname]
|
47
47
|
|
48
48
|
step.rec_dependencies.each do |dep|
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
begin
|
50
|
+
dep.set_info key, value if (force || ! dep.info.include?(key)) && (!check_pid || dep.info[:pid].to_s == pid and dep.info[:pid_hostname] == host)
|
51
|
+
rescue
|
52
|
+
Log.warn "Could no set info #{key} for #{dep.path}: #{$!.message}"
|
53
|
+
end
|
52
54
|
end if recursive
|
55
|
+
|
56
|
+
if recursive && step.info[:archived_info]
|
57
|
+
ad = step.info[:archived_info]
|
58
|
+
ad.each do |d,info|
|
59
|
+
begin
|
60
|
+
info[key] = value if (force || ! info.include?(key)) && (!check_pid || info[:pid].to_s == pid and info[:pid_hostname] == host)
|
61
|
+
rescue
|
62
|
+
Log.warn "Could no set info #{key} for archived_dep #{info[:path]}: #{$!.message}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
step.set_info :archived_info, ad
|
66
|
+
end
|
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.
|
4
|
+
version: 5.32.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Miguel Vazquez
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-04-
|
11
|
+
date: 2021-04-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|