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.
- data/bin/xpflow +96 -0
- data/lib/colorado.rb +198 -0
- data/lib/json/add/core.rb +243 -0
- data/lib/json/add/rails.rb +8 -0
- data/lib/json/common.rb +423 -0
- data/lib/json/editor.rb +1369 -0
- data/lib/json/ext.rb +28 -0
- data/lib/json/pure/generator.rb +442 -0
- data/lib/json/pure/parser.rb +320 -0
- data/lib/json/pure.rb +15 -0
- data/lib/json/version.rb +8 -0
- data/lib/json.rb +62 -0
- data/lib/mime/types.rb +881 -0
- data/lib/mime-types.rb +3 -0
- data/lib/restclient/abstract_response.rb +106 -0
- data/lib/restclient/exceptions.rb +193 -0
- data/lib/restclient/net_http_ext.rb +55 -0
- data/lib/restclient/payload.rb +235 -0
- data/lib/restclient/raw_response.rb +34 -0
- data/lib/restclient/request.rb +316 -0
- data/lib/restclient/resource.rb +169 -0
- data/lib/restclient/response.rb +24 -0
- data/lib/restclient.rb +174 -0
- data/lib/xpflow/bash.rb +341 -0
- data/lib/xpflow/bundle.rb +113 -0
- data/lib/xpflow/cmdline.rb +249 -0
- data/lib/xpflow/collection.rb +122 -0
- data/lib/xpflow/concurrency.rb +79 -0
- data/lib/xpflow/data.rb +393 -0
- data/lib/xpflow/dsl.rb +816 -0
- data/lib/xpflow/engine.rb +574 -0
- data/lib/xpflow/ensemble.rb +135 -0
- data/lib/xpflow/events.rb +56 -0
- data/lib/xpflow/experiment.rb +65 -0
- data/lib/xpflow/exts/facter.rb +30 -0
- data/lib/xpflow/exts/g5k.rb +931 -0
- data/lib/xpflow/exts/g5k_use.rb +50 -0
- data/lib/xpflow/exts/gui.rb +140 -0
- data/lib/xpflow/exts/model.rb +155 -0
- data/lib/xpflow/graph.rb +1603 -0
- data/lib/xpflow/graph_xpflow.rb +251 -0
- data/lib/xpflow/import.rb +196 -0
- data/lib/xpflow/library.rb +349 -0
- data/lib/xpflow/logging.rb +153 -0
- data/lib/xpflow/manager.rb +147 -0
- data/lib/xpflow/nodes.rb +1250 -0
- data/lib/xpflow/runs.rb +773 -0
- data/lib/xpflow/runtime.rb +125 -0
- data/lib/xpflow/scope.rb +168 -0
- data/lib/xpflow/ssh.rb +186 -0
- data/lib/xpflow/stat.rb +50 -0
- data/lib/xpflow/stdlib.rb +381 -0
- data/lib/xpflow/structs.rb +369 -0
- data/lib/xpflow/taktuk.rb +193 -0
- data/lib/xpflow/templates/ssh-config.basic +14 -0
- data/lib/xpflow/templates/ssh-config.inria +18 -0
- data/lib/xpflow/templates/ssh-config.proxy +13 -0
- data/lib/xpflow/templates/taktuk +6590 -0
- data/lib/xpflow/templates/utils/batch +4 -0
- data/lib/xpflow/templates/utils/bootstrap +12 -0
- data/lib/xpflow/templates/utils/hostname +3 -0
- data/lib/xpflow/templates/utils/ping +3 -0
- data/lib/xpflow/templates/utils/rsync +12 -0
- data/lib/xpflow/templates/utils/scp +17 -0
- data/lib/xpflow/templates/utils/scp_many +8 -0
- data/lib/xpflow/templates/utils/ssh +3 -0
- data/lib/xpflow/templates/utils/ssh-interactive +4 -0
- data/lib/xpflow/templates/utils/taktuk +19 -0
- data/lib/xpflow/threads.rb +187 -0
- data/lib/xpflow/utils.rb +569 -0
- data/lib/xpflow/visual.rb +230 -0
- data/lib/xpflow/with_g5k.rb +7 -0
- data/lib/xpflow.rb +349 -0
- metadata +135 -0
@@ -0,0 +1,369 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
#
|
4
|
+
# Various structures/classes used everywhere.
|
5
|
+
#
|
6
|
+
|
7
|
+
module XPFlow
|
8
|
+
|
9
|
+
module Operations
|
10
|
+
def ==(x); XPFlow::BinaryOp.new('==', self, x) end
|
11
|
+
def +(x); XPFlow::BinaryOp.new('+', self, x) end
|
12
|
+
def *(x); XPFlow::BinaryOp.new('*', self, x) end
|
13
|
+
def /(x); XPFlow::BinaryOp.new('/', self, x) end
|
14
|
+
def -(x); XPFlow::BinaryOp.new('-', self, x) end
|
15
|
+
def <(x); XPFlow::BinaryOp.new('<', self, x) end
|
16
|
+
def >(x); XPFlow::BinaryOp.new('>', self, x) end
|
17
|
+
def <=(x); XPFlow::BinaryOp.new('<=', self, x) end
|
18
|
+
def >=(x); XPFlow::BinaryOp.new('>=', self, x) end
|
19
|
+
def -@; XPFlow::UnaryOp.new('-@', self) end
|
20
|
+
def &(x); XPFlow::BinaryOp.new('&', self, x) end
|
21
|
+
def |(x); XPFlow::BinaryOp.new('|', self, x) end
|
22
|
+
def not; XPFlow::NegOp.new(self) end
|
23
|
+
|
24
|
+
def index(i); XPFlow::IndexOp.new(self, i) end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
module Meta
|
29
|
+
|
30
|
+
def meta
|
31
|
+
if @frame
|
32
|
+
return @frame
|
33
|
+
else
|
34
|
+
return Frame.new('<unknown>', '<unknown>')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def collect_meta(skip = 0)
|
39
|
+
original = caller(skip).first
|
40
|
+
x = /^(.+):(\d+)/.match(original)
|
41
|
+
file, line = x.captures
|
42
|
+
@frame = Frame.new(file, line.to_i, self)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
class Visiter
|
48
|
+
|
49
|
+
attr_reader :values
|
50
|
+
|
51
|
+
def initialize
|
52
|
+
@values = []
|
53
|
+
end
|
54
|
+
|
55
|
+
def collect(x)
|
56
|
+
@values.push(x)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
module Traverse
|
61
|
+
|
62
|
+
def _visit(ctx, &block)
|
63
|
+
# puts self.class
|
64
|
+
results = __children__.map { |x| x._visit(ctx, &block) }
|
65
|
+
return ctx.instance_exec(self, results, &block)
|
66
|
+
end
|
67
|
+
|
68
|
+
def visit(&block)
|
69
|
+
ctx = Visiter.new
|
70
|
+
_visit(ctx, &block)
|
71
|
+
return ctx.values
|
72
|
+
end
|
73
|
+
|
74
|
+
def object_key
|
75
|
+
return @key
|
76
|
+
end
|
77
|
+
|
78
|
+
def _workflow(o)
|
79
|
+
h = { :type => o.class.to_s.split("::").last }
|
80
|
+
key = o.object_key()
|
81
|
+
h[:key] = key unless key.nil?
|
82
|
+
if o.respond_to?(:workflow_value)
|
83
|
+
return o.workflow_value
|
84
|
+
end
|
85
|
+
o.__children_hash__.each do |k, v|
|
86
|
+
if h.key?(k)
|
87
|
+
raise "Child '#{k}' exists in workflow. Please change it."
|
88
|
+
end
|
89
|
+
if v.respond_to?(:workflow)
|
90
|
+
h[k] = v.workflow
|
91
|
+
elsif v.is_a?(Array)
|
92
|
+
h[k] = v.map { |x| _workflow(x) }
|
93
|
+
else
|
94
|
+
h[k] = "Undefined for class #{v.class}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
return h
|
98
|
+
end
|
99
|
+
|
100
|
+
def workflow
|
101
|
+
return _workflow(self)
|
102
|
+
end
|
103
|
+
|
104
|
+
def vars_uses
|
105
|
+
# returns a hash mapping a variable in the workflow
|
106
|
+
# to list of nodes that use it
|
107
|
+
uses = visit do |node, children|
|
108
|
+
vs = children.select { |x| x.is_a?(XPVariable) }
|
109
|
+
vs = Hash[vs.map { |x| [ x.key, node ] }]
|
110
|
+
collect(vs) if vs.length > 0
|
111
|
+
node
|
112
|
+
end
|
113
|
+
# uses contains an array of hashes that must be merged
|
114
|
+
# every [k,v] is [variable, node that uses it]
|
115
|
+
summary = {}
|
116
|
+
uses.each do |h|
|
117
|
+
h.each do |key, node|
|
118
|
+
summary[key] = [] unless summary.key?(key)
|
119
|
+
summary[key].push(node)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
summary
|
123
|
+
end
|
124
|
+
|
125
|
+
def vars
|
126
|
+
# returns variables used in that workflow
|
127
|
+
return vars_uses.keys
|
128
|
+
end
|
129
|
+
|
130
|
+
def declarations
|
131
|
+
# returns a hash that maps all variables
|
132
|
+
# to the nodes that define them
|
133
|
+
decls = visit do |node, children|
|
134
|
+
collect({ node.key => node }) if node.is_a?(AbstractRun)
|
135
|
+
collect(node.__declarations__) if node.respond_to?(:__declarations__)
|
136
|
+
end
|
137
|
+
return decls.reduce({}) { |x, y| x.merge(y) }
|
138
|
+
end
|
139
|
+
|
140
|
+
def vars_uses_declarations
|
141
|
+
# like 'declarations' but only shows
|
142
|
+
# declarations of variables that are used in that workflow
|
143
|
+
vs = vars()
|
144
|
+
ds = declarations()
|
145
|
+
# iteration variables will show up as nils
|
146
|
+
return Hash[ vs.map { |x| [x, ds[x]] } ]
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
class ActivityList
|
152
|
+
|
153
|
+
include Traverse
|
154
|
+
children :activities
|
155
|
+
constructor :activities
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
class Frame
|
160
|
+
|
161
|
+
attr_reader :file
|
162
|
+
attr_reader :line
|
163
|
+
attr_reader :obj
|
164
|
+
|
165
|
+
def initialize(file, line, obj = nil)
|
166
|
+
@file = file
|
167
|
+
@line = line
|
168
|
+
@obj = obj
|
169
|
+
end
|
170
|
+
|
171
|
+
def location
|
172
|
+
return '%s:%s' % [ @file, @line ]
|
173
|
+
end
|
174
|
+
|
175
|
+
def location_long
|
176
|
+
return '%s at line %s' % [ @file, @line ]
|
177
|
+
end
|
178
|
+
|
179
|
+
def to_s
|
180
|
+
return "<Frame: #{location}>"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
class FakeScope
|
185
|
+
# a faked scope for evaluate_offline
|
186
|
+
end
|
187
|
+
|
188
|
+
class XPValue
|
189
|
+
|
190
|
+
include Operations
|
191
|
+
include Traverse
|
192
|
+
|
193
|
+
def self.flatten(obj)
|
194
|
+
return obj.flatten if obj.is_a?(XPOp)
|
195
|
+
return obj if obj.is_a?(XPValue)
|
196
|
+
raise if obj.is_a?(AbstractRun)
|
197
|
+
return XPList.new(obj.map { |x| self.flatten(x) }) if obj.is_a?(Array)
|
198
|
+
return XPHash.new(obj.map { |k, v| [ k, self.flatten(v) ] }) if obj.is_a?(Hash)
|
199
|
+
return XPVariable.new(obj.key) if obj.is_a?(DSLVariable)
|
200
|
+
return XPConst.new(obj)
|
201
|
+
end
|
202
|
+
|
203
|
+
def evaluate(scope); raise end
|
204
|
+
|
205
|
+
def evaluate_offline
|
206
|
+
begin
|
207
|
+
return self.evaluate(FakeScope.new)
|
208
|
+
rescue NoMethodError => e
|
209
|
+
return nil
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
|
216
|
+
class XPConst < XPValue
|
217
|
+
|
218
|
+
constructor :obj
|
219
|
+
children
|
220
|
+
|
221
|
+
def evaluate(scope)
|
222
|
+
o = @obj
|
223
|
+
if o.is_a?(String)
|
224
|
+
o = DSLVariable.replace(o, scope)
|
225
|
+
o = XPOp.replace(o, scope)
|
226
|
+
end
|
227
|
+
return o
|
228
|
+
end
|
229
|
+
|
230
|
+
def workflow_value # TODO, handle strings
|
231
|
+
return @obj
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
class XPList < XPValue
|
237
|
+
|
238
|
+
constructor :obj
|
239
|
+
children :obj
|
240
|
+
|
241
|
+
def evaluate(scope)
|
242
|
+
@obj.map { |x| x.evaluate(scope) }
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
246
|
+
|
247
|
+
class XPHash < XPValue
|
248
|
+
|
249
|
+
constructor :obj
|
250
|
+
|
251
|
+
def evaluate(scope)
|
252
|
+
o = Hash[@obj]
|
253
|
+
Hash[o.map { |k, v| [k, v.evaluate(scope) ]}]
|
254
|
+
end
|
255
|
+
|
256
|
+
def __children__
|
257
|
+
return @obj.map { |k, v| v }
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
261
|
+
|
262
|
+
class XPVariable < XPValue
|
263
|
+
|
264
|
+
attr_reader :key
|
265
|
+
constructor :key
|
266
|
+
children
|
267
|
+
|
268
|
+
def evaluate(scope)
|
269
|
+
scope[@key]
|
270
|
+
end
|
271
|
+
|
272
|
+
def workflow_value
|
273
|
+
return "var(#{@key})"
|
274
|
+
end
|
275
|
+
|
276
|
+
end
|
277
|
+
|
278
|
+
|
279
|
+
|
280
|
+
class XPOp < XPValue
|
281
|
+
|
282
|
+
@@instances = {} # TODO: this may consume memory
|
283
|
+
|
284
|
+
def flatten
|
285
|
+
raise
|
286
|
+
end
|
287
|
+
|
288
|
+
def self.replace(s, scope)
|
289
|
+
out = s.gsub(/Op\[-@@@@-(\d+)-@@@@-\]/) do |m|
|
290
|
+
identifier = $1.to_i
|
291
|
+
@@instances[identifier].evaluate(scope)
|
292
|
+
end
|
293
|
+
return out
|
294
|
+
end
|
295
|
+
|
296
|
+
def to_s
|
297
|
+
@@instances[object_id()] = self
|
298
|
+
return "Op[-@@@@-#{object_id()}-@@@@-]"
|
299
|
+
end
|
300
|
+
|
301
|
+
end
|
302
|
+
|
303
|
+
class NegOp < XPOp
|
304
|
+
|
305
|
+
constructor :arg
|
306
|
+
children :arg
|
307
|
+
|
308
|
+
def evaluate(scope)
|
309
|
+
return !@arg.evaluate(scope)
|
310
|
+
end
|
311
|
+
|
312
|
+
def flatten
|
313
|
+
return NegOp.new(XPValue.flatten(@arg))
|
314
|
+
end
|
315
|
+
|
316
|
+
end
|
317
|
+
|
318
|
+
class UnaryOp < XPOp
|
319
|
+
|
320
|
+
constructor :type, :arg
|
321
|
+
children :arg
|
322
|
+
|
323
|
+
def evaluate(scope)
|
324
|
+
x = @arg.evaluate(scope)
|
325
|
+
return x.send(@type)
|
326
|
+
end
|
327
|
+
|
328
|
+
def flatten
|
329
|
+
return UnaryOp.new(@type, XPValue.flatten(@arg))
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
333
|
+
|
334
|
+
class BinaryOp < XPOp
|
335
|
+
|
336
|
+
constructor :type, :left, :right
|
337
|
+
children :left, :right
|
338
|
+
|
339
|
+
def evaluate(scope)
|
340
|
+
a = @left.evaluate(scope)
|
341
|
+
b = @right.evaluate(scope)
|
342
|
+
return a.send(@type, b)
|
343
|
+
end
|
344
|
+
|
345
|
+
def flatten
|
346
|
+
return BinaryOp.new(@type, XPValue.flatten(@left), XPValue.flatten(@right))
|
347
|
+
end
|
348
|
+
|
349
|
+
end
|
350
|
+
|
351
|
+
class IndexOp < XPOp
|
352
|
+
|
353
|
+
constructor :arg, :index
|
354
|
+
children :arg
|
355
|
+
|
356
|
+
def evaluate(scope)
|
357
|
+
v = @arg.evaluate(scope)
|
358
|
+
i = @index.evaluate(scope)
|
359
|
+
return v[i]
|
360
|
+
end
|
361
|
+
|
362
|
+
def flatten
|
363
|
+
return IndexOp.new(XPValue.flatten(@arg), XPValue.flatten(@index))
|
364
|
+
end
|
365
|
+
|
366
|
+
end
|
367
|
+
|
368
|
+
end
|
369
|
+
|
@@ -0,0 +1,193 @@
|
|
1
|
+
|
2
|
+
require 'thread'
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
module XPFlow
|
6
|
+
|
7
|
+
class TakTukRun
|
8
|
+
|
9
|
+
@@mutex = Mutex.new
|
10
|
+
@@counter = 0
|
11
|
+
@@hash = SecureRandom.hex(16)
|
12
|
+
|
13
|
+
attr_reader :filename
|
14
|
+
|
15
|
+
def initialize(taktuk, nodes, opts = {})
|
16
|
+
@taktuk = taktuk
|
17
|
+
@nodes = nodes
|
18
|
+
@opts = { :escape => 1 }.merge(opts)
|
19
|
+
_write_run_file()
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.get_uniq
|
23
|
+
@@mutex.synchronize do
|
24
|
+
@@counter += 1
|
25
|
+
@@counter
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def _ensure_file(key)
|
30
|
+
@opts[key] = %(mktemp).strip unless @opts.key?(key)
|
31
|
+
return @opts[key]
|
32
|
+
end
|
33
|
+
|
34
|
+
def filename
|
35
|
+
return _ensure_file(:filename)
|
36
|
+
end
|
37
|
+
|
38
|
+
def stdout
|
39
|
+
return _ensure_file(:stdout)
|
40
|
+
end
|
41
|
+
|
42
|
+
def stderr
|
43
|
+
return _ensure_file(:stderr)
|
44
|
+
end
|
45
|
+
|
46
|
+
def grouped_nodes
|
47
|
+
# gives a hash of lists that contain all nodes
|
48
|
+
# merged together at each key - they will be together in taktuk execution
|
49
|
+
# for better locality etc.
|
50
|
+
h = Hash.new { |h, k| h[k] = [] }
|
51
|
+
@nodes.each { |x| h[x.group].push(x) }
|
52
|
+
return h
|
53
|
+
end
|
54
|
+
|
55
|
+
def nodes_mapping
|
56
|
+
# gives a mapping from .userhost to a list of nodes
|
57
|
+
# this handles the duplicated nodes
|
58
|
+
h = Hash.new { |h, k| h[k] = [] }
|
59
|
+
@nodes.each { |x| h[x.userhost].push(x) }
|
60
|
+
return h
|
61
|
+
end
|
62
|
+
|
63
|
+
def _write_run_file
|
64
|
+
File.open(filename, "w") do |f|
|
65
|
+
f.puts("#!/bin/bash")
|
66
|
+
f.puts("set -eu")
|
67
|
+
f.puts("TAKTUK=${TAKTUK:-#{@taktuk}}")
|
68
|
+
f.puts("$TAKTUK #{@opts[:propagate] ? '-s' : ''} \\")
|
69
|
+
grouped_nodes().each_pair do |group, ns|
|
70
|
+
# f.puts("-b \\") # -b and -e cause bugs!!! grrrh!! TakTuk hangs!
|
71
|
+
ns.each { |x| f.puts(" -m #{x.userhost} \\") }
|
72
|
+
# f.puts("-e \\")
|
73
|
+
end
|
74
|
+
f.puts('"$@"')
|
75
|
+
end
|
76
|
+
return self
|
77
|
+
end
|
78
|
+
|
79
|
+
def execute_raw(cmd, opts = {})
|
80
|
+
# execute the command at the lowest level
|
81
|
+
# return stdout & stderr, but throw an exception if taktuk failed
|
82
|
+
original_cmd = cmd
|
83
|
+
opts = @opts.merge(opts)
|
84
|
+
opts[:escape].times { cmd = Shellwords.escape(cmd) }
|
85
|
+
|
86
|
+
real_command = "bash #{filename} synchronize broadcast exec [ #{cmd} ]"
|
87
|
+
|
88
|
+
output = %x(#{real_command} < /dev/null 1> #{stdout} 2> #{stderr})
|
89
|
+
|
90
|
+
raise ExecutionError.new("Command '#{original_cmd}' returned error (see #{stdout} and #{stderr})!") if $?.exitstatus != 0
|
91
|
+
|
92
|
+
return [ stdout, stderr ]
|
93
|
+
end
|
94
|
+
|
95
|
+
def execute_shell(cmd, opts = {})
|
96
|
+
# executes 'cmd' in a shell, so shell variables and redirections are possible
|
97
|
+
|
98
|
+
out, err = execute_raw(cmd, opts)
|
99
|
+
|
100
|
+
# parsing of the output
|
101
|
+
# EXAMPLE: root@172.16.0.3-2: hostname > /tmp/nazwa (3825): status > Exited with status 0
|
102
|
+
|
103
|
+
status_exp = /^(\S+)-(\d+): .+\((\d+)\): status > Exited with status (\d+)$/
|
104
|
+
output_exp = /^(\S+)-(\d+): .+\((\d+)\): (output|error) > (.+)$/
|
105
|
+
|
106
|
+
outputs = Hash.new { |h, k| h[k] = [] }
|
107
|
+
errputs = Hash.new { |h, k| h[k] = [] }
|
108
|
+
results = []
|
109
|
+
|
110
|
+
File.open(out).each_line do |line|
|
111
|
+
m = line.strip.match(output_exp)
|
112
|
+
if m
|
113
|
+
_, rank, _, stream, text = m.captures
|
114
|
+
(stream == "output" ? outputs : errputs )[rank.to_i].push(text)
|
115
|
+
next
|
116
|
+
end
|
117
|
+
m = line.strip.match(status_exp)
|
118
|
+
if m
|
119
|
+
node, rank, ident, status = m.captures
|
120
|
+
results.push({ :name => node, :rank => rank.to_i, :ident => ident.to_i, :status => status.to_i })
|
121
|
+
next
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
names = nodes_mapping()
|
126
|
+
results.each do |r|
|
127
|
+
r[:stdout] = outputs[r[:rank]].join("\n")
|
128
|
+
r[:stderr] = errputs[r[:rank]].join("\n")
|
129
|
+
r[:node] = names[r[:name]].pop()
|
130
|
+
raise "Fatal error" if r[:node].nil?
|
131
|
+
end
|
132
|
+
|
133
|
+
return results
|
134
|
+
end
|
135
|
+
|
136
|
+
def execute(cmd, opts = {})
|
137
|
+
original_cmd = cmd
|
138
|
+
results = execute_shell(cmd, opts)
|
139
|
+
return _split_results(results)
|
140
|
+
end
|
141
|
+
|
142
|
+
def execute_remote(cmd, opts = {})
|
143
|
+
# executes a command which results will be stored remotely
|
144
|
+
original_cmd = cmd
|
145
|
+
prefix = "/tmp/.taktuk--#{@@hash}--#{TakTukRun.get_uniq}"
|
146
|
+
out_file, err_file = proc { |x| "#{prefix}--out--#{x}" }, proc { |x| "#{prefix}--err--#{x}" }
|
147
|
+
out, err = out_file.call("$TAKTUK_RANK"), err_file.call("$TAKTUK_RANK")
|
148
|
+
|
149
|
+
real_cmd = "#{cmd} 1> #{out} 2> #{err}"
|
150
|
+
results = execute_shell(real_cmd, opts)
|
151
|
+
|
152
|
+
results.each do |r|
|
153
|
+
rank = r[:rank]
|
154
|
+
r[:stdout_file] = out_file.call(rank)
|
155
|
+
r[:stderr_file] = err_file.call(rank)
|
156
|
+
end
|
157
|
+
|
158
|
+
return _split_results(results)
|
159
|
+
end
|
160
|
+
|
161
|
+
def put(src, dest, opts = {})
|
162
|
+
# distributes a file over all nodes
|
163
|
+
# returns md5's for all nodes
|
164
|
+
|
165
|
+
real_command = "bash #{filename} synchronize broadcast put [ #{src} ] [ #{dest} ]"
|
166
|
+
output = %x(#{real_command} 1> /dev/null 2> /dev/null)
|
167
|
+
|
168
|
+
succ, fail = execute("md5sum #{dest}")
|
169
|
+
|
170
|
+
if fail.length != 0
|
171
|
+
raise "Some nodes failed (try #{real_command})."
|
172
|
+
end
|
173
|
+
|
174
|
+
succ.each { |x| x[:hash] = x[:stdout].split.first }
|
175
|
+
|
176
|
+
return succ
|
177
|
+
end
|
178
|
+
|
179
|
+
def _split_results(results)
|
180
|
+
return [
|
181
|
+
results.select { |r| r[:status] == 0 },
|
182
|
+
results.select { |r| r[:status] != 0 }
|
183
|
+
]
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
|
191
|
+
if $0 == __FILE__
|
192
|
+
|
193
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
# Grid5000 template
|
3
|
+
|
4
|
+
LogLevel quiet
|
5
|
+
UserKnownHostsFile /dev/null
|
6
|
+
StrictHostKeyChecking no
|
7
|
+
BatchMode yes
|
8
|
+
ConnectTimeout 15
|
9
|
+
ForwardAgent yes
|
10
|
+
|
11
|
+
Host proxy
|
12
|
+
User <%= g5k_user %>
|
13
|
+
Hostname <%= gw %>
|
14
|
+
|
15
|
+
Host this
|
16
|
+
User <%= user %>
|
17
|
+
Hostname <%= host %>
|
18
|
+
ProxyCommand ssh -F <%= path %>/ssh-config proxy 'nc <%= host %> 22'
|