jkr 0.0.1 → 0.1.0

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,5 @@
1
+
2
+ module Jkr
3
+ class ParameterError < RuntimeError
4
+ end
5
+ end
@@ -0,0 +1,28 @@
1
+
2
+ class Integer
3
+ def to_hstr
4
+ if self < 2**10
5
+ self.to_s
6
+ elsif self < 2**20
7
+ sprintf('%dK', self / 2**10)
8
+ elsif self < 2**30
9
+ sprintf('%dM', self / 2**20)
10
+ else
11
+ sprintf('%dG', self / 2**30)
12
+ end
13
+ end
14
+ end
15
+
16
+ class Float
17
+ def to_hstr
18
+ if self < 2**10
19
+ sprintf('%.2f', self)
20
+ elsif self < 2**20
21
+ sprintf('%.2fK', self / 2**10)
22
+ elsif self < 2**30
23
+ sprintf('%.2fM', self / 2**20)
24
+ else
25
+ sprintf('%.2fG', self / 2**30)
26
+ end
27
+ end
28
+ end
@@ -1,51 +1,153 @@
1
1
 
2
2
  require 'jkr/utils'
3
+ require 'jkr/error'
3
4
  require 'tempfile'
5
+ require 'net/http'
6
+ require 'fileutils'
4
7
 
5
- class Jkr
8
+ module Jkr
6
9
  class Plan
7
10
  attr_accessor :title
8
11
  attr_accessor :desc
9
-
12
+ attr_accessor :short_desc
13
+ attr_accessor :plan_name
14
+
10
15
  attr_accessor :params
11
16
  attr_accessor :vars
12
-
17
+ attr_accessor :metastore
18
+
19
+ attr_accessor :base_plan
20
+ attr_accessor :plan_search_path
21
+ attr_accessor :script_search_path
22
+ attr_accessor :used_scripts
23
+
24
+ attr_accessor :resultset_dir
25
+
13
26
  # Proc's
14
27
  attr_accessor :prep
15
28
  attr_accessor :cleanup
16
29
  attr_accessor :routine
30
+ attr_accessor :routine_nr_run
17
31
  attr_accessor :analysis
32
+ attr_accessor :param_filters
33
+ attr_accessor :exec_time_estimate
18
34
 
19
35
  attr_accessor :src
20
36
 
21
- attr_reader :file_path
37
+ attr_accessor :file_path
22
38
  attr_reader :jkr_env
23
39
 
24
- def initialize(jkr_env, plan_file_path = nil)
40
+ private_class_method :new
41
+ def initialize(jkr_env)
42
+ @base_plan = nil
43
+ @used_scripts = []
25
44
  @jkr_env = jkr_env
26
- @file_path = plan_file_path || jkr_env.next_plan
27
- return nil unless @file_path
45
+ @metastore = Hash.new
28
46
 
29
47
  @title = "no title"
30
48
  @desc = "no desc"
49
+ @short_desc = nil
50
+ @plan_name = plan_name
31
51
 
32
52
  @params = {}
33
53
  @vars = {}
34
- @routine = lambda do |_|
35
- raise NotImplementedError.new("A routine of experiment '#{@title}' is not implemented")
54
+ @routine = nil
55
+ @routine_nr_run = 1
56
+ @prep = nil
57
+ @cleanup = nil
58
+ @param_filters = []
59
+
60
+ @src = nil
61
+ end
62
+
63
+ def self.create_by_name(jkr_env, plan_name, options = {})
64
+ plan = new(jkr_env)
65
+
66
+ if options[:plan_search_path]
67
+ plan.plan_search_path = options[:plan_search_path]
68
+ else
69
+ plan.plan_search_path = [plan.jkr_env.jkr_plan_dir]
36
70
  end
37
- @prep = lambda do |_|
38
- raise NotImplementedError.new("A prep of experiment '#{@title}' is not implemented")
71
+
72
+ if options[:script_search_path]
73
+ plan.script_search_path = options[:script_search_path]
74
+ else
75
+ plan.script_search_path = [plan.jkr_env.jkr_script_dir]
39
76
  end
40
- @cleanup = lambda do |_|
41
- raise NotImplementedError.new("A cleanup of experiment '#{@title}' is not implemented")
77
+
78
+ finder = PlanFinder.new(jkr_env)
79
+ plan.file_path = finder.find_by_name(plan_name,
80
+ :plan_search_path => plan.plan_search_path)
81
+ unless plan.file_path
82
+ raise ArgumentError.new("No such plan: #{plan_name}")
42
83
  end
43
84
 
44
- @src = nil
85
+ PlanLoader.load_plan(plan)
86
+
87
+ plan
88
+ end
89
+
90
+ def self.create_by_result_id(jkr_env, ret_id, options = {})
91
+ plan = new(jkr_env)
92
+ finder = PlanFinder.new(jkr_env)
93
+ plan.file_path = finder.find_by_result_id(ret_id)
94
+
95
+ plan.plan_search_path = [File.expand_path("../plan", plan.file_path)]
96
+ plan.script_search_path = [File.expand_path("../script", plan.file_path)]
97
+
98
+ unless plan.file_path
99
+ raise ArgumentError.new("Not valid result ID: #{ret_id}")
100
+ end
101
+
102
+ PlanLoader.load_plan(plan)
103
+
104
+ plan
105
+ end
106
+
107
+ def param_names
108
+ @params.keys
109
+ end
110
+
111
+ def var_names
112
+ @vars.keys
113
+ end
114
+
115
+ def do_prep(plan = self)
116
+ if self.prep.nil?
117
+ self.base_plan.do_prep(plan)
118
+ else
119
+ self.prep.call(plan)
120
+ end
121
+ end
122
+
123
+ def do_routine(plan, params)
124
+ if self.routine.nil?
125
+ self.base_plan.do_routine(plan, params)
126
+ else
127
+ self.routine.call(plan, params)
128
+ end
129
+ end
45
130
 
46
- PlanLoader.load_plan(self)
131
+ def do_cleanup(plan = self)
132
+ if self.cleanup.nil?
133
+ self.base_plan.do_cleanup(plan)
134
+ else
135
+ self.cleanup.call(plan)
136
+ end
47
137
  end
48
138
 
139
+ def do_analysis(plan = self)
140
+ if self.analysis.nil?
141
+ self.base_plan.resultset_dir = self.resultset_dir
142
+ self.base_plan.do_analysis(plan)
143
+ else
144
+ Jkr::AnalysisUtils.define_analysis_utils(resultset_dir, self)
145
+ ret = self.analysis.call(plan)
146
+ Jkr::AnalysisUtils.undef_analysis_utils(self)
147
+
148
+ ret
149
+ end
150
+ end
49
151
 
50
152
  class PlanLoader
51
153
  class PlanParams
@@ -56,7 +158,7 @@ class Jkr
56
158
  @vars = {}
57
159
  @params = {}
58
160
  end
59
-
161
+
60
162
  def [](key)
61
163
  @params[key]
62
164
  end
@@ -65,44 +167,143 @@ class Jkr
65
167
  @params[key] = val
66
168
  end
67
169
  end
68
-
69
- include Jkr::PlanUtils
70
170
 
71
171
  def initialize(plan)
72
172
  @plan = plan
73
173
  @params = nil
74
174
  end
75
-
175
+
76
176
  def self.load_plan(plan)
77
177
  plan_loader = self.new(plan)
78
178
  plan.src = File.open(plan.file_path, "r").read
79
179
  plan_loader.instance_eval(plan.src, plan.file_path, 1)
80
180
  plan
81
181
  end
82
-
182
+
83
183
  ## Functions for describing plans in '.plan' files below
84
184
  def plan
85
185
  @plan
86
186
  end
87
187
 
188
+ def use_script(name)
189
+ # find script file
190
+ if name.is_a? Symbol
191
+ name = name.to_s + ".rb"
192
+ elsif ! (name =~ /\.rb$/)
193
+ name += ".rb"
194
+ end
195
+
196
+ path = nil
197
+ search_dirs = @plan.script_search_path
198
+ while ! search_dirs.empty?
199
+ dir = search_dirs.shift
200
+ path = File.expand_path(name, dir)
201
+
202
+ if File.exists?(path)
203
+ break
204
+ end
205
+ end
206
+
207
+ if path
208
+ load path
209
+ @plan.used_scripts.push(path)
210
+ else
211
+ raise RuntimeError.new("Cannot use script: #{name}")
212
+ end
213
+ end
214
+
215
+ def extend(base_plan_name)
216
+ base_plan = Plan.create_by_name(self.plan.jkr_env, base_plan_name.to_s,
217
+ :plan_search_path => @plan.plan_search_path,
218
+ :script_search_path => @plan.script_search_path)
219
+ self.plan.base_plan = base_plan
220
+
221
+ @plan.params.merge!(base_plan.params)
222
+ @plan.vars.merge!(base_plan.vars)
223
+ end
224
+
88
225
  def title(plan_title)
89
226
  @plan.title = plan_title.to_s
90
227
  end
91
-
228
+
92
229
  def description(plan_desc)
93
230
  @plan.desc = plan_desc.to_s
94
231
  end
95
-
232
+
233
+ def short_desc(short_desc)
234
+ @plan.short_desc = short_desc.gsub(/ /, '_').gsub(/\//, '!')
235
+ end
236
+
96
237
  def def_parameters(&proc)
97
238
  @params = PlanParams.new
98
239
  proc.call()
240
+
241
+ consts = @params.params
242
+ vars = @params.vars
243
+
244
+ if @plan.base_plan
245
+ consts.keys.each do |const|
246
+ if ! @plan.params.include?(const)
247
+ raise Jkr::ParameterError.new("#{const} is not defined in base plan: #{@plan.base_plan.title}")
248
+ end
249
+ end
250
+
251
+ vars.keys.each do |var|
252
+ if ! @plan.vars.include?(var)
253
+ raise Jkr::ParameterError.new("#{var} is not defined in base plan: #{@plan.base_plan.title}")
254
+ end
255
+ end
256
+ end
257
+
99
258
  @plan.params.merge!(@params.params)
100
259
  @plan.vars.merge!(@params.vars)
101
260
  end
102
-
103
- def def_routine(&proc)
261
+
262
+ def def_routine(options = {}, &proc)
263
+ if options[:nr_run]
264
+ @plan.routine_nr_run = options[:nr_run]
265
+ end
104
266
  @plan.routine = proc
105
267
  end
268
+
269
+ # call routine of super plan
270
+ def super_routine(plan, params)
271
+ if @plan.base_plan == nil
272
+ RuntimeError.new("No super plan.")
273
+ else
274
+ @plan.base_plan.do_routine(plan, params)
275
+ end
276
+ end
277
+
278
+ def super_prep(plan)
279
+ if @plan.base_plan == nil
280
+ RuntimeError.new("No super plan.")
281
+ else
282
+ @plan.base_plan.do_prep(plan)
283
+ end
284
+ end
285
+
286
+ def super_cleanup(plan)
287
+ if @plan.base_plan == nil
288
+ RuntimeError.new("No super plan.")
289
+ else
290
+ @plan.base_plan.do_cleanup(plan)
291
+ end
292
+ end
293
+
294
+ def super_analysis(plan)
295
+ if @plan.base_plan == nil
296
+ RuntimeError.new("No super plan.")
297
+ else
298
+ @plan.base_plan.resultset_dir = @plan.resultset_dir
299
+ @plan.base_plan.do_analysis(plan)
300
+ end
301
+ end
302
+
303
+ def exec_time_estimate(&proc)
304
+ @plan.exec_time_estimate = proc
305
+ end
306
+
106
307
  def def_prep(&proc)
107
308
  @plan.prep = proc
108
309
  end
@@ -116,17 +317,107 @@ class Jkr
116
317
  def parameter(arg = nil)
117
318
  if arg.is_a? Hash
118
319
  # set param
119
- @params.params.merge!(arg)
320
+ $stderr.puts("'parameter' is deprecated. use 'constant' instead.")
321
+ constant(arg)
120
322
  else
121
323
  @params
122
324
  end
123
325
  end
124
326
 
327
+ def constant(arg = nil)
328
+ if arg.is_a? Hash
329
+ arg.keys.each do |const_name|
330
+ if @params.vars.keys.include?(const_name)
331
+ raise Jkr::ParameterError.new("#{const_name} is already defined as variable")
332
+ end
333
+ end
334
+
335
+ # set param
336
+ @params.params.merge!(arg)
337
+ else
338
+ raise ArgumentError.new
339
+ end
340
+ end
341
+
342
+ def jkr_env
343
+ self.plan.jkr_env
344
+ end
345
+
125
346
  def variable(arg = nil)
126
347
  if arg.is_a? Hash
348
+ arg.keys.each do |var_name|
349
+ if @params.params.keys.include?(var_name)
350
+ raise Jkr::ParameterError.new("#{var_name} is already defined as constant")
351
+ end
352
+ end
353
+
127
354
  @params.vars.merge!(arg)
128
355
  else
129
- @params
356
+ raise ArgumentError.new
357
+ end
358
+ end
359
+
360
+ def param_filter(&proc)
361
+ @plan.param_filters.push(proc)
362
+ end
363
+
364
+ # utility functions
365
+ def send_mail(subject, addrs, body, files = [])
366
+ attach_option = files.map{|file| "-a #{file}"}.join(" ")
367
+ IO.popen("mutt #{addrs.join(' ')} -s #{subject.inspect} #{attach_option}", "w+") do |io|
368
+ io.puts body
369
+ end
370
+ end
371
+
372
+ def notify_im_kayac(username, message)
373
+ Net::HTTP.post_form(URI.parse("http://im.kayac.com/api/post/#{username}"),
374
+ {'message'=>message})
375
+ end
376
+
377
+ def sh(*args)
378
+ puts "sh: #{args.join(' ')}"
379
+ return system(*args)
380
+ end
381
+
382
+ def sh!(*args)
383
+ puts "sh!: #{args.join(' ')}"
384
+ unless system(*args)
385
+ raise RuntimeError.new(args.join(" "))
386
+ end
387
+ true
388
+ end
389
+
390
+ alias :system_ :sh!
391
+
392
+ def su_sh(*args)
393
+ puts "su_sh: #{args.join(' ')}"
394
+ su_cmd = File.expand_path("../su_cmd", __FILE__)
395
+ system_(su_cmd, args.join(' '))
396
+ end
397
+
398
+ def sudo_sh(*args)
399
+ puts "sudo_sh: #{args.join(' ')}"
400
+ system_((["sudo"] + args).join(' '))
401
+ end
402
+
403
+ def drop_caches()
404
+ su_sh('echo 1 > /proc/sys/vm/drop_caches')
405
+ end
406
+
407
+ def checkout_git(dir, repo_url, branch)
408
+ if ! File.directory?(dir)
409
+ sh! "git clone #{repo_url} #{dir}"
410
+ end
411
+
412
+ Dir.chdir(dir) do
413
+ if ! File.exists?(".git")
414
+ sh! "git clone #{repo_url} ."
415
+ end
416
+
417
+ sh "git checkout -t origin/#{branch}"
418
+ sh! "git pull"
419
+
420
+ sh! "git rev-parse HEAD > #{@plan.resultset_dir + '/git-commit.log'}"
130
421
  end
131
422
  end
132
423
  end