jkr 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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