xpflow 0.1b

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.
Files changed (74) hide show
  1. data/bin/xpflow +96 -0
  2. data/lib/colorado.rb +198 -0
  3. data/lib/json/add/core.rb +243 -0
  4. data/lib/json/add/rails.rb +8 -0
  5. data/lib/json/common.rb +423 -0
  6. data/lib/json/editor.rb +1369 -0
  7. data/lib/json/ext.rb +28 -0
  8. data/lib/json/pure/generator.rb +442 -0
  9. data/lib/json/pure/parser.rb +320 -0
  10. data/lib/json/pure.rb +15 -0
  11. data/lib/json/version.rb +8 -0
  12. data/lib/json.rb +62 -0
  13. data/lib/mime/types.rb +881 -0
  14. data/lib/mime-types.rb +3 -0
  15. data/lib/restclient/abstract_response.rb +106 -0
  16. data/lib/restclient/exceptions.rb +193 -0
  17. data/lib/restclient/net_http_ext.rb +55 -0
  18. data/lib/restclient/payload.rb +235 -0
  19. data/lib/restclient/raw_response.rb +34 -0
  20. data/lib/restclient/request.rb +316 -0
  21. data/lib/restclient/resource.rb +169 -0
  22. data/lib/restclient/response.rb +24 -0
  23. data/lib/restclient.rb +174 -0
  24. data/lib/xpflow/bash.rb +341 -0
  25. data/lib/xpflow/bundle.rb +113 -0
  26. data/lib/xpflow/cmdline.rb +249 -0
  27. data/lib/xpflow/collection.rb +122 -0
  28. data/lib/xpflow/concurrency.rb +79 -0
  29. data/lib/xpflow/data.rb +393 -0
  30. data/lib/xpflow/dsl.rb +816 -0
  31. data/lib/xpflow/engine.rb +574 -0
  32. data/lib/xpflow/ensemble.rb +135 -0
  33. data/lib/xpflow/events.rb +56 -0
  34. data/lib/xpflow/experiment.rb +65 -0
  35. data/lib/xpflow/exts/facter.rb +30 -0
  36. data/lib/xpflow/exts/g5k.rb +931 -0
  37. data/lib/xpflow/exts/g5k_use.rb +50 -0
  38. data/lib/xpflow/exts/gui.rb +140 -0
  39. data/lib/xpflow/exts/model.rb +155 -0
  40. data/lib/xpflow/graph.rb +1603 -0
  41. data/lib/xpflow/graph_xpflow.rb +251 -0
  42. data/lib/xpflow/import.rb +196 -0
  43. data/lib/xpflow/library.rb +349 -0
  44. data/lib/xpflow/logging.rb +153 -0
  45. data/lib/xpflow/manager.rb +147 -0
  46. data/lib/xpflow/nodes.rb +1250 -0
  47. data/lib/xpflow/runs.rb +773 -0
  48. data/lib/xpflow/runtime.rb +125 -0
  49. data/lib/xpflow/scope.rb +168 -0
  50. data/lib/xpflow/ssh.rb +186 -0
  51. data/lib/xpflow/stat.rb +50 -0
  52. data/lib/xpflow/stdlib.rb +381 -0
  53. data/lib/xpflow/structs.rb +369 -0
  54. data/lib/xpflow/taktuk.rb +193 -0
  55. data/lib/xpflow/templates/ssh-config.basic +14 -0
  56. data/lib/xpflow/templates/ssh-config.inria +18 -0
  57. data/lib/xpflow/templates/ssh-config.proxy +13 -0
  58. data/lib/xpflow/templates/taktuk +6590 -0
  59. data/lib/xpflow/templates/utils/batch +4 -0
  60. data/lib/xpflow/templates/utils/bootstrap +12 -0
  61. data/lib/xpflow/templates/utils/hostname +3 -0
  62. data/lib/xpflow/templates/utils/ping +3 -0
  63. data/lib/xpflow/templates/utils/rsync +12 -0
  64. data/lib/xpflow/templates/utils/scp +17 -0
  65. data/lib/xpflow/templates/utils/scp_many +8 -0
  66. data/lib/xpflow/templates/utils/ssh +3 -0
  67. data/lib/xpflow/templates/utils/ssh-interactive +4 -0
  68. data/lib/xpflow/templates/utils/taktuk +19 -0
  69. data/lib/xpflow/threads.rb +187 -0
  70. data/lib/xpflow/utils.rb +569 -0
  71. data/lib/xpflow/visual.rb +230 -0
  72. data/lib/xpflow/with_g5k.rb +7 -0
  73. data/lib/xpflow.rb +349 -0
  74. metadata +135 -0
@@ -0,0 +1,341 @@
1
+
2
+ #
3
+ # Features a cool class to interface with Bash.
4
+ #
5
+
6
+ # exceptionally the requirements are present since
7
+ # this module can be nicely used separately
8
+
9
+ require 'digest'
10
+ require 'open3'
11
+
12
+ module Bash
13
+
14
+ class BashError < StandardError; end
15
+ class BashTimeout < BashError; end
16
+ class BashPaddingError < BashError; end
17
+
18
+ class StatusError < BashError
19
+
20
+ attr_reader :status
21
+ attr_reader :output
22
+
23
+ def initialize(cmd, status, output)
24
+ super("'#{cmd}' returned with status = #{status}")
25
+ @status = status
26
+ @cmd = cmd
27
+ @output = output
28
+ end
29
+
30
+ end
31
+
32
+ class Bash
33
+
34
+ def initialize(stdin, stdout, opts = {})
35
+ @stdin = stdin
36
+ @stdout = stdout
37
+ @opts = {
38
+ :debug => false,
39
+ :timeout => 120.0
40
+ }.merge(opts)
41
+ @buff = ''
42
+ @debug = @opts[:debug]
43
+ @timeout = @opts[:timeout]
44
+ end
45
+
46
+ def _loop
47
+ while true do
48
+ x = IO::select([@stdout], [], [], @timeout)
49
+ raise BashTimeout.new if x.nil?
50
+ bytes = @stdout.sysread(1024)
51
+ $stderr.write("\nBASH IN: #{bytes}\n") if @debug
52
+ @buff << bytes
53
+ break if yield
54
+ end
55
+ end
56
+
57
+ def parse(&block)
58
+ return self.instance_exec(&block)
59
+ end
60
+
61
+ def _nonce
62
+ randee = 4.times.map { rand().to_s }.join('|')
63
+ return Digest::SHA512.hexdigest(randee).to_s
64
+ end
65
+
66
+ def _run(cmd, opts)
67
+ # it's a kind of magic
68
+ $stderr.write("\nBASH CMD: #{cmd}\n") if @debug
69
+ nonce = _nonce()
70
+ @stdin.write("#{cmd}; printf '%04d#{nonce}' $?\n")
71
+ @stdin.flush
72
+ _loop do
73
+ @buff.include?(nonce)
74
+ end
75
+ raise BashPaddingError.new if !@buff.end_with?(nonce)
76
+ output = @buff
77
+ @buff = ''
78
+ # treat the output
79
+ output.slice!(-nonce.length..-1)
80
+ status = output.slice!(-4..-1)
81
+ raise "Status #{status} > 255?" if status.slice(0..0) != '0'
82
+ return output, status.to_i
83
+ end
84
+
85
+ def _run_block(cmd, opts)
86
+ @stdin.write("#{cmd}; printf '%04d#{nonce}' $?\n")
87
+ end
88
+
89
+ def _extend(path, suffix)
90
+ path = path.chomp('/') if path.end_with?('/')
91
+ return path + suffix
92
+ end
93
+
94
+ def _unlines(s)
95
+ return s.lines.map { |l| l.chomp("\n") }
96
+ end
97
+
98
+ def _escape(args)
99
+ return args.map { |x| "'#{x}'" }.join(' ')
100
+ end
101
+
102
+ def export(name, value)
103
+ run("export #{name}=#{value}")
104
+ end
105
+
106
+ def run(cmd, opts = {})
107
+ out, status = _run(cmd, opts)
108
+ raise StatusError.new(cmd, status, out) if status != 0
109
+ return out
110
+ end
111
+
112
+ def run_status(cmd, opts = {})
113
+ out, status = _run(cmd, opts)
114
+ return status
115
+ end
116
+
117
+ def cd(path)
118
+ run("cd #{path}")
119
+ end
120
+
121
+ def ls
122
+ run("ls -1")
123
+ end
124
+
125
+ def sleep(t)
126
+ run("sleep #{t.to_i}")
127
+ end
128
+
129
+ def pwd
130
+ run("pwd").strip
131
+ end
132
+
133
+ def untar(name, where = nil)
134
+ if where.nil?
135
+ run("tar xvf #{name}")
136
+ else
137
+ run("tar -C #{where} -xvf #{name}")
138
+ end
139
+ end
140
+
141
+ def echo(text)
142
+ run("echo #{text}")
143
+ end
144
+
145
+ def bc(text)
146
+ run("echo '#{text}' | bc").strip
147
+ end
148
+
149
+ def cp(a, b)
150
+ run("cp #{a} #{b}")
151
+ end
152
+
153
+ def mv(a, b)
154
+ run("mv #{a} #{b}")
155
+ end
156
+
157
+ def rm(*args)
158
+ run("rm #{_escape(args)}")
159
+ end
160
+
161
+ def remove_dirs(path)
162
+ rm "-rf", path
163
+ end
164
+
165
+ def build
166
+ # builds a standard Unix software
167
+ run("./configure")
168
+ run("make")
169
+ end
170
+
171
+ def abspath(path)
172
+ run("readlink -f #{path}").strip
173
+ end
174
+
175
+ def build_tarball(tarball, path)
176
+ # builds a tarball containing a std Unix software
177
+ tarball = abspath(tarball)
178
+ path = abspath(path)
179
+ remove_dirs(path)
180
+ tmp = _extend(path, '-tmp')
181
+ remove_dirs(tmp)
182
+ make_dirs(tmp)
183
+ untar(tarball, tmp)
184
+ cd tmp
185
+ # we are in the temp dir
186
+ if exists('./configure')
187
+ cd '/'
188
+ mv tmp, path
189
+ else
190
+ ds = dirs()
191
+ raise 'Too many dirs?' if ds.length != 1
192
+ mv ds.first, path
193
+ cd '/'
194
+ remove_dirs(tmp)
195
+ end
196
+ cd path
197
+ build
198
+ end
199
+
200
+ def tmp_file
201
+ return run('mktemp').strip
202
+ end
203
+
204
+ def save_machines(machines, path = nil)
205
+ path = tmp_file if path.nil?
206
+ append_lines(path, machines.map { |m| m.to_s })
207
+ return path
208
+ end
209
+
210
+ def mpirun(machines, params)
211
+ machines = save_machines(machines) if machines.is_a?(Array)
212
+ return run("mpirun --mca btl ^openib -machinefile #{machines} #{params}")
213
+ end
214
+
215
+ def join(*args)
216
+ return File.join(*args)
217
+ end
218
+
219
+ def exists(path)
220
+ run_status("[[ -e #{path} ]]") == 0
221
+ end
222
+
223
+ def make_dirs(path)
224
+ run("mkdir -p #{path}")
225
+ end
226
+
227
+ def mkdir(path)
228
+ run("mkdir #{path}") unless exists(path)
229
+ end
230
+
231
+ def files(ignore = true, type = 'f')
232
+ fs = run("find . -maxdepth 1 -type #{type}")
233
+ fs = _unlines(fs).reject { |f| f == '.' }.map { |f| f[2..-1] }
234
+ fs = fs.reject { |f| f.end_with?('~') or f.start_with?('.') } if ignore
235
+ return fs
236
+ end
237
+
238
+ def which(prog)
239
+ return run("which #{prog}").strip
240
+ end
241
+
242
+ def dirs(ignore = true)
243
+ return files(ignore, 'd')
244
+ end
245
+
246
+ def get_type(name)
247
+ return :dir if run_status("[[ -d #{name} ]]") == 0
248
+ return :file if run_status("[[ -f #{name} ]]") == 0
249
+ raise "'#{name}' is neither file nor directory"
250
+ end
251
+
252
+ def expand_path(path)
253
+ return run("echo #{path}").strip
254
+ end
255
+
256
+ def append_line(path, line)
257
+ return run("echo '#{line}' >> #{path}")
258
+ end
259
+
260
+ def append_lines(path, lines)
261
+ lines.each { |line|
262
+ append_line(path, line)
263
+ }
264
+ end
265
+
266
+ def packages
267
+ list = run("dpkg -l")
268
+ list = _unlines(list).map do |p|
269
+ s, n, v = p.split
270
+ { :status => s, :name => n, :version => v }
271
+ end
272
+ end
273
+
274
+ def aptget(*args)
275
+ raise 'Command not given' if args.length == 0
276
+ cmd = args.first.to_sym
277
+ args = args.map { |x| x.to_s }.join(' ')
278
+ status = run_status("DEBIAN_FRONTEND=noninteractive apt-get -y #{args}")
279
+ if cmd == :purge and status == 100
280
+ return # ugly hack for the case when the package is not installed
281
+ end
282
+ raise StatusError.new('aptget', status, 'none') if status != 0
283
+ end
284
+
285
+ def contents(name)
286
+ run("cat #{name}")
287
+ end
288
+
289
+ alias cat contents
290
+
291
+ def hostname
292
+ return run('hostname').strip
293
+ end
294
+
295
+ def touch(path)
296
+ run("touch #{path}")
297
+ end
298
+
299
+ def trunc(path)
300
+ run("cat /dev/null > #{path}")
301
+ end
302
+
303
+ def distribute(path, dest, *nodes)
304
+ # distributes a file to the given nodes
305
+ nodes.flatten.each { |node|
306
+ run("scp -o 'StrictHostKeyChecking no' #{path} #{node}:#{dest}")
307
+ }
308
+ end
309
+
310
+ def glob(pattern)
311
+ out, status = _run("ls -1 #{pattern}", {})
312
+ return [] if status != 0
313
+ return out.strip.lines.map { |x| x.strip }
314
+ end
315
+
316
+ end
317
+
318
+
319
+ def self.bash(cmd = 'bash', opts = {}, &block)
320
+ if not block_given?
321
+ sin, sout, serr, thr = Open3.popen3(cmd)
322
+ return Bash.new(sin, sout, opts)
323
+ end
324
+ # run bash interpreter using this command
325
+ result = nil
326
+ Open3.popen3(cmd) do |sin, sout, serr, thr|
327
+ dsl = Bash.new(sin, sout, opts)
328
+ dsl.cd('~') # go to the home dir
329
+ result = dsl.parse(&block)
330
+ end
331
+ return result
332
+ end
333
+
334
+ end
335
+
336
+ if __FILE__ == $0
337
+ Bash.bash("ssh localhost bash") do
338
+ cd '/tmp'
339
+ puts glob("/tmp/*.gz").inspect
340
+ end
341
+ end
@@ -0,0 +1,113 @@
1
+
2
+ #
3
+ # builds a one-file-bundle of xpflow
4
+ #
5
+ # TODO: it's not finished:
6
+ # * it won't bundle various external files (e.g., node templates)
7
+ # * __FILE__ usage should be standarized somehow to make everything work
8
+ #
9
+ # We roughly do the following (bundle method):
10
+ #
11
+ # - take all files from lib directory
12
+ # - discard some files that are not used
13
+ # - pull ../xpflow (entry point for the library) as well
14
+ # - remove "require" uses all over the place, checking if
15
+ # we know about them
16
+ # - then merge them all and wrap in an envelope that
17
+ # temporarily replaces $0 (so that inline code won't run)
18
+ # - for interested parties, we set $bundled = true
19
+ # - finally we append model.rb (TODO: it needs fixing)
20
+ # - the order of files *must* not matter!
21
+ #
22
+ # For executable method, we also:
23
+ #
24
+ # - pull main bin/xpflow script
25
+ # - replace a specially marked region with the result
26
+ # of bundle() method
27
+ #
28
+ # Therefore, a todo list:
29
+ # 1) Fix __FILE__ uses so that model.rb works
30
+ # 2) Bundle external files as well (pack them, serialize
31
+ # and then extract them on-the-fly?)
32
+ # 3) make everything more robust and less hackish
33
+ #
34
+
35
+ module XPFlow
36
+
37
+ class Bundler
38
+
39
+ def initialize
40
+ basic = [ '../colorado', 'utils', 'structs', 'scope', 'library' ]
41
+ discard = [ 'ensemble', 'ssh', 'bash' ]
42
+ files = basic + (__get_my_requires__.sort - basic - discard)
43
+ @here = File.dirname(__FILE__)
44
+ files += [ '../xpflow']
45
+
46
+ @files = files.map { |x| File.join(@here, "#{x}.rb") }
47
+ end
48
+
49
+ def bundle
50
+ ignored = [ "xpflow/exts/g5k", "colorado" ]
51
+ libs = [ "thread", "monitor", "pp", "digest", "open3", "tmpdir",
52
+ "fileutils", "monitor", "erb", "ostruct", "yaml", "shellwords",
53
+ "etc", "optparse", "pathname" ] # TODO: g5k should be pulled in
54
+ parts = []
55
+ puts "Bundling files:"
56
+ @files.each do |f|
57
+ puts " - #{f}"
58
+ contents = IO.read(f)
59
+ new_cont = contents.gsub(/require\s+(\S+)/) do |req|
60
+ lib = req.split("'")[1]
61
+ if (libs + ignored).include?(lib) or lib == "xpflow"
62
+ ""
63
+ else
64
+ puts "Warning: untreated library (#{lib})"
65
+ end
66
+ end
67
+ parts.push(new_cont)
68
+ end
69
+
70
+ s = parts.join("\n")
71
+
72
+ lib_lines = libs.map { |x| "require('#{x}')" }
73
+ model = File.join(@here, "exts", "model.rb")
74
+
75
+ final = ([ "# encoding: UTF-8" ] + lib_lines + [
76
+ "$tmp_0 = $0; $0 = 'faked_name'; $bundled = true",
77
+ s,
78
+ "$0 = $tmp_0",
79
+ IO.read(model)
80
+ ]).join("\n")
81
+
82
+ return final
83
+ end
84
+
85
+ def executable
86
+ b = bundle()
87
+ main_file = File.join(@here, "..", "..", "bin", "xpflow")
88
+ contents = IO.read(main_file)
89
+
90
+ exp = Regexp.new("(\#XSTART.+\#XEND)", Regexp::MULTILINE)
91
+ main = contents.gsub(exp, b)
92
+
93
+ return main
94
+ end
95
+
96
+ end
97
+ end
98
+
99
+ if __FILE__ == $0
100
+ require 'xpflow'
101
+
102
+ bundle = ARGV.first
103
+
104
+ if bundle.nil?
105
+ raise "Please provide an output file."
106
+ end
107
+
108
+ bundler = XPFlow::Bundler.new
109
+ output = bundler.executable()
110
+
111
+ IO.write(bundle, output)
112
+
113
+ end
@@ -0,0 +1,249 @@
1
+ # encoding: UTF-8
2
+
3
+ #
4
+ # Takes care of command line interface.
5
+ #
6
+
7
+ module XPFlow
8
+
9
+ class RunInfo
10
+
11
+ attr_reader :name
12
+ attr_reader :args
13
+
14
+ def initialize(name, *args)
15
+ @name = name
16
+ @args = args
17
+ end
18
+
19
+ end
20
+
21
+ class CmdlineError < Exception
22
+
23
+ def ignore?
24
+ return false
25
+ end
26
+
27
+ end
28
+
29
+ class UnknownCommandError < CmdlineError
30
+
31
+ def initialize(cmd)
32
+ super("unknown command `#{cmd}'")
33
+ end
34
+
35
+ end
36
+
37
+ class CmdlineNoFileError < CmdlineError
38
+
39
+ def initialize(path)
40
+ super("file `#{path}' does not exist")
41
+ end
42
+ end
43
+
44
+ class CmdlineIgnoreError < CmdlineError
45
+
46
+ def ignore?
47
+ return true
48
+ end
49
+ end
50
+
51
+ class Options
52
+
53
+ attr_reader :args
54
+ attr_reader :config
55
+ attr_reader :includes
56
+ attr_reader :command
57
+ attr_reader :entry
58
+
59
+ def initialize(args)
60
+ @args = args.clone
61
+ @command = nil
62
+ @config = Options.defaults
63
+ @includes = []
64
+ @entry = nil
65
+ @verbose = false
66
+ parse
67
+ end
68
+
69
+ def verbose?
70
+ return @verbose
71
+ end
72
+
73
+ def self.defaults
74
+ # Define a sane default configuration
75
+ {
76
+ :labels => [],
77
+ :ignore_checkpoints => false,
78
+ :checkpoint => nil,
79
+ :activity => nil,
80
+ :instead => [],
81
+ :after => [],
82
+ :vars => {}
83
+ }
84
+ end
85
+
86
+ def load_config(conffile)
87
+ if !File.exist?(conffile)
88
+ raise CmdlineNoFileError.new(conffile)
89
+ end
90
+ type = File.extname(conffile).downcase
91
+ contents = File.read(conffile)
92
+
93
+ data = case type
94
+ when '.json' then JSON.parse(contents)
95
+ when '.yaml' then YAML.load(contents)
96
+ else raise CmdlineError.new("unknown config file type (#{type})")
97
+ end
98
+
99
+ raise CmdlineError.new("bad config file format") unless data.is_a?(Hash)
100
+
101
+ return data
102
+ end
103
+
104
+ # Parses options from the command line
105
+
106
+ def parse
107
+ parser = OptionParser.new do |opts|
108
+ opts.banner = [
109
+ "Usage: #{$0} <command> [options] ARGUMENTS",
110
+ "",
111
+ "Available commands:",
112
+ "",
113
+ " * help -- show help",
114
+ " * run <files*> -- run a workflow from a file",
115
+ " * workflow <files*> -o <output> -- generate a workflow from a file",
116
+ "",
117
+ "Options:"
118
+ ].join("\n")
119
+
120
+ opts.on("-v", "--verbose", "Run verbosely") do
121
+ @config[:labels] += [ :verbose ]
122
+ @verbose = true
123
+ end
124
+ opts.on("-q", "--quiet", "Run quietly") do
125
+ @config[:labels] = [ :normal ]
126
+ end
127
+ opts.on("-p", "--paranoiac", "Run paranoically") do
128
+ @config[:labels] += [ :verbose, :paranoic ]
129
+ end
130
+ opts.on("-l", "--labels LABELS", "Log messages labeled with LABELS") do |labels|
131
+ @config[:labels] += labels.split(',').map { |x| x.downcase.to_sym }
132
+ end
133
+
134
+ opts.on("-I", "--ignore-checkpoints", "Ignore automatically saved checkpoints") do
135
+ @config[:ignore_checkpoints] = true
136
+ end
137
+ # opts.on("-C", "--checkpoint NAME", "Jump to checkpoint NAME (if exists)") do |name|
138
+ # @config[:checkpoint] = name
139
+ # end
140
+ # opts.on("-c", "--list-checkpoints", "List available checkpoints") do
141
+ # @config[:instead] += [ RunInfo.new(:list_checkpoints) ]
142
+ # end
143
+ # opts.on("-i", "--info NAME", "Show information NAME") do |name|
144
+ # @config[:instead] += [ RunInfo.new(name.to_sym) ]
145
+ # end
146
+ # opts.on("-L", "--list", "List declared activities") do
147
+ # @config[:instead] += [ RunInfo.new(:activities) ]
148
+ # end
149
+ opts.on("-g", "--gantt", "Show Gantt diagram after execution") do
150
+ @config[:after] += [ RunInfo.new(:show_gantt) ]
151
+ end
152
+ opts.on("-G", "--save-gantt FILE", "Save Gantt diagram information to FILE") do |name|
153
+ @config[:after] += [ RunInfo.new(:save_gantt, name) ]
154
+ end
155
+ opts.on("-V", "--vars SPEC", "Set variables from the cmdline") do |spec|
156
+ pairs = spec.split(',').map { |x| x.split('=') }
157
+ unless pairs.all? { |x| x.length == 2 }
158
+ raise CmdlineError.new("Wrong vars syntax - <name>=<value> expected.")
159
+ end
160
+ @config[:vars].merge!(Hash[*pairs.flatten])
161
+ end
162
+ opts.on("-f", "--file FILE", "Set variables from a YAML file") do |file|
163
+ params = load_config(file)
164
+ @config[:vars].merge!(params)
165
+ end
166
+ opts.on("-o", "--output FILE", "Set output file for some commands") do |file|
167
+ @config[:output] = file
168
+ end
169
+ end
170
+
171
+ parser.parse!(@args)
172
+
173
+ @config[:labels] += [ :normal ]
174
+ @config[:labels].uniq!
175
+
176
+ @command = @args.first
177
+ @params = @args.tail
178
+
179
+ if command == "help" || command.nil?
180
+ show_usage(parser)
181
+ raise CmdlineIgnoreError.new
182
+ elsif command == "version"
183
+ raise CmdlineError.new("Unsupported.")
184
+ elsif command == "run"
185
+ load_files()
186
+ elsif command == "workflow"
187
+ if @config[:output].nil?
188
+ raise CmdlineError.new("output file must be provided with -o switch")
189
+ end
190
+ load_files()
191
+ else
192
+ # try to parse command as a filename
193
+ begin
194
+ @entry = parse_activity_spec(command)
195
+ rescue CmdlineNoFileError
196
+ raise UnknownCommandError.new(command)
197
+ end
198
+ @command = "run"
199
+ @params = @args
200
+ load_files()
201
+ end
202
+ end
203
+
204
+ def load_files
205
+ # loads files from command line
206
+ if @params.length == 0
207
+ raise CmdlineError.new("at least one file must be given")
208
+ end
209
+ @entry = parse_activity_spec(@params.first)
210
+ @params.each do |spec|
211
+ x = parse_activity_spec(spec)
212
+ @includes.push(x.first)
213
+ end
214
+ end
215
+
216
+ def parse_activity_spec(path)
217
+ parts = path.split(":", 2)
218
+ activity = nil
219
+ if parts.length == 1
220
+ activity = "main" # by default use 'main' activity
221
+ else
222
+ path, activity = parts
223
+ end
224
+ if !File.exist?(path)
225
+ raise CmdlineNoFileError.new(path)
226
+ end
227
+ return [ path, activity ]
228
+ end
229
+
230
+ def vars
231
+ return @config[:vars]
232
+ end
233
+
234
+ def dispatch(obj)
235
+ if @command == "run"
236
+ return obj.execute_run(*@entry)
237
+ elsif @command == "workflow"
238
+ return obj.execute_workflow(*@entry)
239
+ end
240
+ end
241
+
242
+ def show_usage(parser)
243
+ Kernel.puts(parser.banner)
244
+ Kernel.puts(parser.summarize); Kernel.puts
245
+ end
246
+
247
+ end
248
+
249
+ end