pwrake 0.9.3

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.
@@ -0,0 +1,367 @@
1
+ module Pwrake
2
+
3
+ START_TIME = Time.now
4
+
5
+ module Option
6
+
7
+ DEFAULT_CONFFILES = ["pwrake_conf.yaml","PwrakeConf.yaml"]
8
+
9
+ def option_data
10
+ [
11
+ 'DRYRUN',
12
+ 'IGNORE_SYSTEM',
13
+ 'IGNORE_DEPRECATE',
14
+ 'LOAD_SYSTEM',
15
+ 'NOSEARCH',
16
+ 'RAKELIB',
17
+ 'SHOW_PREREQS',
18
+ 'SILENT',
19
+ 'TRACE',
20
+ 'TRACE_RULES',
21
+
22
+ 'FILESYSTEM',
23
+ 'SSH_OPTION',
24
+ 'PASS_ENV',
25
+ 'GNU_TIME',
26
+ 'HALT_QUEUE_WHILE_SEARCH',
27
+ 'SHOW_CONF',
28
+
29
+ ['HOSTFILE','HOSTS'],
30
+ ['LOGFILE','LOG',
31
+ proc{|v|
32
+ if v
33
+ # turn trace option on
34
+ Rake.application.options.trace = true
35
+ if v == "" || !v.kind_of?(String)
36
+ v = "Pwrake%Y%m%d-%H%M%S_%$.log"
37
+ end
38
+ START_TIME.strftime(v).sub("%$",Process.pid.to_s)
39
+ end
40
+ }],
41
+ ['PROFILE',
42
+ proc{|v|
43
+ if v
44
+ if v == "" || !v.kind_of?(String)
45
+ v = "Pwrake%Y%m%d-%H%M%S_%$.csv"
46
+ end
47
+ START_TIME.strftime(v).sub("%$",Process.pid.to_s)
48
+ end
49
+ }],
50
+ ['NUM_THREADS', proc{|v| v && v.to_i}],
51
+ ['DISABLE_AFFINITY', proc{|v| v || ENV['AFFINITY']=='off'}],
52
+ ['GFARM_BASEDIR', proc{|v| v || '/tmp'}],
53
+ ['GFARM_PREFIX', proc{|v| v || "pwrake_#{ENV['USER']}"}],
54
+ ['GFARM_SUBDIR', proc{|v| v || '/'}],
55
+ ['MASTER_HOSTNAME', proc{|v| v || `hostname -f`.chomp}],
56
+ ['WORK_DIR',proc{|v|
57
+ v ||= '$HOME/%CWD_RELATIVE_TO_HOME'
58
+ v.sub('%CWD_RELATIVE_TO_HOME',cwd_relative_to_home)
59
+ }]
60
+ ]
61
+ end
62
+
63
+
64
+ # ----- init -----
65
+
66
+ def init_option
67
+ @host_group = []
68
+ init_options
69
+ init_pass_env
70
+ init_logger
71
+ if @opts['SHOW_CONF']
72
+ require "yaml"
73
+ YAML.dump(@opts,$stdout)
74
+ exit
75
+ end
76
+ @counter = Counter.new
77
+ end
78
+
79
+ attr_reader :core_list
80
+ attr_reader :counter
81
+ attr_reader :logfile
82
+ attr_reader :queue_class
83
+ attr_reader :shell_class
84
+
85
+
86
+ def pwrake_options
87
+ @opts
88
+ end
89
+
90
+ def init_options
91
+ # Read pwrake_conf
92
+ @pwrake_conf = Rake.application.options.pwrake_conf
93
+
94
+ if @pwrake_conf
95
+ if !File.exist?(@pwrake_conf)
96
+ raise "Configuration file not found: #{@pwrake_conf}"
97
+ end
98
+ else
99
+ @pwrake_conf = DEFAULT_CONFFILES.find{|fn| File.exist?(fn)}
100
+ end
101
+
102
+ if @pwrake_conf.nil?
103
+ @yaml = {}
104
+ else
105
+ Log.debug "@pwrake_conf=#{@pwrake_conf}"
106
+ require "yaml"
107
+ @yaml = open(@pwrake_conf){|f| YAML.load(f) }
108
+ end
109
+
110
+ @opts = {'PWRAKE_CONF' => @pwrake_conf}
111
+
112
+ option_data.each do |a|
113
+ prc = nil
114
+ keys = []
115
+ case a
116
+ when String
117
+ keys << a
118
+ when Array
119
+ a.each do |x|
120
+ case x
121
+ when String
122
+ keys << x
123
+ when Proc
124
+ prc = x
125
+ end
126
+ end
127
+ end
128
+ key = keys[0]
129
+ val = search_opts(keys)
130
+ val = prc.call(val) if prc
131
+ @opts[key] = val if !val.nil?
132
+ instance_variable_set("@"+key.downcase, val)
133
+ end
134
+
135
+ feedback_options [
136
+ 'DRYRUN',
137
+ 'IGNORE_SYSTEM',
138
+ 'IGNORE_DEPRECATE',
139
+ 'LOAD_SYSTEM',
140
+ 'NOSEARCH',
141
+ 'RAKELIB',
142
+ 'SHOW_PREREQS',
143
+ 'SILENT',
144
+ 'TRACE',
145
+ 'TRACE_RULES']
146
+ Rake.verbose(true) if Rake.application.options.trace
147
+ Rake.verbose(false) if Rake.application.options.silent
148
+ end
149
+
150
+ def feedback_options(a)
151
+ a.each do |k|
152
+ if v=@opts[k]
153
+ m = (k.downcase+"=").to_sym
154
+ Rake.application.options.send(m,v)
155
+ end
156
+ end
157
+ end
158
+
159
+ # Option order:
160
+ # command_option > ENV > pwrake_conf > DEFAULT_OPTIONS
161
+ def search_opts(keys)
162
+ val = Rake.application.options.send(keys[0].downcase.to_sym)
163
+ return val if !val.nil?
164
+ #
165
+ keys.each do |k|
166
+ val = ENV[k.upcase]
167
+ return val if !val.nil?
168
+ end
169
+ #
170
+ return nil if !@yaml
171
+ keys.each do |k|
172
+ val = @yaml[k.upcase]
173
+ return val if !val.nil?
174
+ end
175
+ nil
176
+ end
177
+
178
+ def cwd_relative_to_home
179
+ Pathname.pwd.relative_path_from(Pathname.new(ENV['HOME'])).to_s
180
+ end
181
+
182
+ def cwd_relative_if_under_home
183
+ home = Pathname.new(ENV['HOME']).realpath
184
+ path = pwd = Pathname.pwd.realpath
185
+ while path != home
186
+ if path.root?
187
+ return pwd.to_s
188
+ end
189
+ path = path.parent
190
+ end
191
+ return pwd.relative_path_from(home).to_s
192
+ end
193
+
194
+ def init_pass_env
195
+ if envs = @opts['PASS_ENV']
196
+ pass_env = {}
197
+
198
+ case envs
199
+ when Array
200
+ envs.each do |k|
201
+ k = k.to_s
202
+ if v = ENV[k]
203
+ pass_env[k] = v
204
+ end
205
+ end
206
+ when Hash
207
+ envs.each do |k,v|
208
+ k = k.to_s
209
+ if v = ENV[k] || v
210
+ pass_env[k] = v
211
+ end
212
+ end
213
+ else
214
+ raise "invalid option for PASS_ENV in pwrake_conf.yaml"
215
+ end
216
+
217
+ if pass_env.empty?
218
+ @opts.delete('PASS_ENV')
219
+ else
220
+ @opts['PASS_ENV'] = pass_env
221
+ end
222
+ end
223
+ end
224
+
225
+ def init_logger
226
+ if Rake.application.options.debug
227
+ Log.level = Log::DEBUG
228
+ elsif Rake.verbose.kind_of? TrueClass
229
+ Log.level = Log::INFO
230
+ else
231
+ Log.level = Log::WARN
232
+ end
233
+
234
+ if @logfile
235
+ logdir = File.dirname(@logfile)
236
+ if !File.directory?(logdir)
237
+ mkdir_p logdir
238
+ end
239
+ Log.open(@logfile)
240
+ # turn trace option on
241
+ #Rake.application.options.trace = true
242
+ else
243
+ Log.open($stdout)
244
+ end
245
+ end
246
+
247
+ # ----- setup -----
248
+
249
+ def setup_option
250
+ set_hosts
251
+ set_filesystem
252
+ end
253
+
254
+ def set_hosts
255
+ if @hostfile && @num_threads
256
+ raise "Cannot set `hostfile' and `num_threads' simultaneously"
257
+ end
258
+ if @hostfile
259
+ require "socket"
260
+ tmplist = []
261
+ File.open(@hostfile) do |f|
262
+ while l = f.gets
263
+ l = $1 if /^([^#]*)#/ =~ l
264
+ host, ncore, group = l.split
265
+ if host
266
+ begin
267
+ host = Socket.gethostbyname(host)[0]
268
+ rescue
269
+ Log.info "FQDN not resoved : #{host}"
270
+ end
271
+ ncore = (ncore || 1).to_i
272
+ group = (group || 0).to_i
273
+ tmplist << ([host] * ncore.to_i)
274
+ @host_group[group] ||= []
275
+ @host_group[group] << host
276
+ end
277
+ end
278
+ end
279
+ #
280
+ @core_list = []
281
+ begin # alternative order
282
+ sz = 0
283
+ tmplist.each do |a|
284
+ @core_list << a.shift if !a.empty?
285
+ sz += a.size
286
+ end
287
+ end while sz>0
288
+ @num_threads = @core_list.size
289
+ else
290
+ @num_threads = 1 if !@num_threads
291
+ @core_list = ['localhost'] * @num_threads
292
+ end
293
+ end
294
+
295
+
296
+ def set_filesystem
297
+ if fn = @opts["PROFILE"]
298
+ Shell.profiler.open(fn,@opts['GNU_TIME'])
299
+ end
300
+
301
+ @shell_opt = {
302
+ :work_dir => @opts['WORK_DIR'],
303
+ :pass_env => @opts['PASS_ENV'],
304
+ :ssh_opt => @opts['SSH_OPTION']
305
+ }
306
+
307
+ if @filesystem.nil?
308
+ case mount_type
309
+ when /gfarm2fs/
310
+ @opts['FILESYSTEM'] = @filesystem = 'gfarm'
311
+ end
312
+ end
313
+
314
+ case @filesystem
315
+ when 'gfarm'
316
+ require "pwrake/locality_aware_queue"
317
+ require "pwrake/gfarm_feature"
318
+ GfarmPath.subdir = @opts['GFARM_SUBDIR']
319
+ @filesystem = 'gfarm'
320
+ @shell_class = GfarmShell
321
+ @shell_opt.merge!({
322
+ :work_dir => Dir.pwd,
323
+ :disable_steal => @opts['DISABLE_STEAL'],
324
+ :single_mp => @opts['GFARM_SINGLE_MP'],
325
+ :basedir => @opts['GFARM_BASEDIR'],
326
+ :prefix => @opts['GFARM_PREFIX']
327
+ })
328
+ @queue_class = GfarmQueue
329
+ # @queue_class = TaskQueue
330
+ else
331
+ @filesystem = 'nfs'
332
+ @shell_class = Shell
333
+ @queue_class = TaskQueue
334
+ end
335
+ end
336
+
337
+ def mount_type(d=nil)
338
+ mtab = '/etc/mtab'
339
+ if File.exist?(mtab)
340
+ d ||= mountpoint_of_cwd
341
+ open(mtab,'r') do |f|
342
+ f.each_line do |l|
343
+ if /#{d} (?:type )?(\S+)/o =~ l
344
+ return $1
345
+ end
346
+ end
347
+ end
348
+ end
349
+ nil
350
+ end
351
+
352
+ def mountpoint_of_cwd
353
+ d = Pathname.pwd
354
+ while !d.mountpoint?
355
+ d = d.parent
356
+ end
357
+ d
358
+ end
359
+
360
+ # ----- finish -----
361
+
362
+ def finish_option
363
+ Log.close
364
+ end
365
+
366
+ end
367
+ end
@@ -0,0 +1,88 @@
1
+ module Pwrake
2
+
3
+ class Profiler
4
+
5
+ def initialize
6
+ @lock = Mutex.new
7
+ @separator = ","
8
+ @gnu_time = false
9
+ @id = 0
10
+ @io = nil
11
+ end
12
+
13
+ attr_accessor :separator, :gnu_time
14
+
15
+ def open(file,gnu_time)
16
+ @gnu_time = gnu_time
17
+ @lock.synchronize do
18
+ @io.close if @io != nil
19
+ @io = File.open(file,"w")
20
+ end
21
+ _puts table_header
22
+ end
23
+
24
+ def close
25
+ @lock.synchronize do
26
+ @io.close if @io != nil
27
+ @io = nil
28
+ end
29
+ end
30
+
31
+ def _puts(s)
32
+ @lock.synchronize do
33
+ @io.puts(s) if @io
34
+ end
35
+ end
36
+
37
+ def table_header
38
+ a = %w[id task command start end elap status]
39
+ if @gnu_time
40
+ a.concat %w[realtime systime usrtime maxrss averss memsz
41
+ datasz stcksz textsz pagesz majflt minflt nswap ncswinv
42
+ ncswvol ninp nout msgrcv msgsnd signum]
43
+ end
44
+ a.join(@separator)
45
+ end
46
+
47
+ def command(cmd,terminator)
48
+ if @gnu_time
49
+ if /\*|\?|\{|\}|\[|\]|<|>|\(|\)|\~|\&|\||\\|\$|;|`|\n/ =~ cmd
50
+ cmd = cmd.gsub(/'/,"'\"'\"'")
51
+ cmd = "sh -c '#{cmd}'"
52
+ end
53
+ f = %w[%x %e %S %U %M %t %K %D %p %X %Z %F %R %W %c %w %I %O %r
54
+ %s %k].join(@separator)
55
+ "/usr/bin/time -o /dev/stdout -f '#{terminator}:#{f}' #{cmd}"
56
+ else
57
+ "#{cmd}\necho '#{terminator}':$? "
58
+ end
59
+ end
60
+
61
+ def format_time(t)
62
+ t.utc.strftime("%F %T.%L").inspect
63
+ end
64
+
65
+ def profile(task, cmd, start_time, end_time, status)
66
+ id = @lock.synchronize do
67
+ id = @id
68
+ @id += 1
69
+ id
70
+ end
71
+ if @io
72
+ _puts [id, task && task.name.inspect,
73
+ cmd.inspect,
74
+ format_time(start_time),
75
+ format_time(end_time),
76
+ "%.3f" % (end_time-start_time),
77
+ status].join(@separator)
78
+ end
79
+ if @gnu_time
80
+ /^([^,]*),/ =~ status
81
+ Integer($1)
82
+ else
83
+ Integer(status)
84
+ end
85
+ end
86
+
87
+ end
88
+ end
@@ -0,0 +1,14 @@
1
+ module Rake
2
+
3
+ class Task
4
+ include Pwrake::TaskAlgorithm
5
+
6
+ alias invoke_orig :invoke
7
+
8
+ def invoke(*args)
9
+ invoke_modify(*args)
10
+ end
11
+
12
+ end
13
+
14
+ end # module Rake
@@ -0,0 +1,151 @@
1
+ module Pwrake
2
+
3
+ class Shell
4
+ CHARS='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
5
+ TLEN=32
6
+
7
+ OPEN_LIST={}
8
+
9
+ def self.nice=(nice)
10
+ @@nice=nice
11
+ end
12
+
13
+ def self.reset_id
14
+ @@current_id = 0
15
+ end
16
+
17
+ @@nice = "nice"
18
+ @@shell = "sh"
19
+ @@current_id = 0
20
+ @@profiler = Profiler.new
21
+
22
+ def self.profiler
23
+ @@profiler
24
+ end
25
+
26
+ def initialize(host,opt={})
27
+ @host = host || 'localhost'
28
+ @lock = Mutex.new
29
+ @@current_id += 1
30
+ @id = @@current_id
31
+ @option = opt
32
+ @work_dir = @option[:work_dir] || Dir.pwd
33
+ @pass_env = @option[:pass_env]
34
+ @ssh_opt = @option[:ssh_opt]
35
+ @terminator = ""
36
+ TLEN.times{ @terminator << CHARS[rand(CHARS.length)] }
37
+ end
38
+
39
+ attr_reader :id
40
+ attr_accessor :current_task
41
+
42
+ def system_cmd(*arg)
43
+ if ['localhost','localhost.localdomain','127.0.0.1'].include? @host
44
+ [@@nice,@@shell].join(' ')
45
+ else
46
+ "ssh -x -T -q #{@ssh_opt} #{@host} #{@@nice} #{@@shell}"
47
+ end
48
+ end
49
+
50
+ def start
51
+ open(system_cmd)
52
+ cd_work_dir
53
+ end
54
+
55
+ def open(cmd,path=nil)
56
+ if path.nil?
57
+ path = ENV['PATH']
58
+ end
59
+ @io = IO.popen( cmd, "r+" )
60
+ OPEN_LIST[__id__] = self
61
+ _system "export PATH='#{path}'"
62
+ if @pass_env
63
+ @pass_env.each do |k,v|
64
+ _system "export #{k}='#{v}'"
65
+ end
66
+ end
67
+ end
68
+
69
+ attr_reader :host, :status, :profile
70
+
71
+ def finish
72
+ close
73
+ end
74
+
75
+ def close
76
+ @lock.synchronize do
77
+ if !@io.closed?
78
+ @io.puts("exit")
79
+ @io.close
80
+ end
81
+ OPEN_LIST.delete(__id__)
82
+ end
83
+ end
84
+
85
+ def backquote(*command)
86
+ command = command.join(' ')
87
+ @lock.synchronize do
88
+ a = []
89
+ _execute(command){|x| a << x}
90
+ a.join("\n")
91
+ end
92
+ end
93
+
94
+ def system(*command)
95
+ command = command.join(' ')
96
+ Log.debug "--- command=#{command.inspect}"
97
+ @lock.synchronize do
98
+ _execute(command){|x| LOCK.synchronize{puts x}}
99
+ end
100
+ end
101
+
102
+ def cd_work_dir
103
+ _system("cd #{@work_dir}")
104
+ end
105
+
106
+ def cd(dir="")
107
+ _system("cd #{dir}")
108
+ end
109
+
110
+
111
+ END {
112
+ OPEN_LIST.map do |k,v|
113
+ v.close
114
+ end
115
+ Shell.profiler.close
116
+ }
117
+
118
+ private
119
+
120
+ def _system(cmd)
121
+ raise "@io is closed" if @io.closed?
122
+ @lock.synchronize do
123
+ @io.puts(cmd+"\necho '#{@terminator}':$? ")
124
+ status = io_read_loop{}
125
+ Integer(status) == 0
126
+ end
127
+ end
128
+
129
+ def _execute(cmd,quote=nil,&block)
130
+ raise "@io is closed" if @io.closed?
131
+ start_time = Time.now
132
+ @io.puts @@profiler.command(cmd,@terminator)
133
+ status = io_read_loop(&block)
134
+ end_time = Time.now
135
+ @status = @@profiler.profile(@current_task, cmd,
136
+ start_time, end_time, status)
137
+ @status == 0
138
+ end
139
+
140
+ def io_read_loop
141
+ while x = @io.gets
142
+ x.chomp!
143
+ if x[0,TLEN] == @terminator
144
+ return x[TLEN+1..-1]
145
+ end
146
+ yield x
147
+ end
148
+ end
149
+ end
150
+
151
+ end # module Pwrake