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.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +81 -0
- data/Guardfile +9 -0
- data/LICENSE.txt +674 -0
- data/{README.txt → README.rdoc} +0 -0
- data/Rakefile +4 -5
- data/bin/console +7 -0
- data/etc/example.plan +32 -0
- data/etc/zsh-comp.sh +82 -0
- data/exe/jkr +6 -0
- data/jkr.gemspec +31 -0
- data/lib/jkr.rb +13 -4
- data/lib/jkr/analysis.rb +5 -14
- data/lib/jkr/analytics.rb +75 -0
- data/lib/jkr/array.rb +47 -0
- data/lib/jkr/blktrace.rb +131 -0
- data/lib/jkr/cli.rb +110 -0
- data/lib/jkr/cpu_usage.rb +81 -0
- data/lib/jkr/cpufreq.rb +201 -0
- data/lib/jkr/dirlock.rb +9 -0
- data/lib/jkr/env.rb +17 -17
- data/lib/jkr/error.rb +5 -0
- data/lib/jkr/numeric.rb +28 -0
- data/lib/jkr/plan.rb +317 -26
- data/lib/jkr/planfinder.rb +40 -0
- data/lib/jkr/plot.rb +626 -0
- data/lib/jkr/stat.rb +2 -0
- data/lib/jkr/stat/kmeans-1d.rb +94 -0
- data/lib/jkr/su_cmd +163 -0
- data/lib/jkr/sysinfo.rb +34 -0
- data/lib/jkr/trial.rb +91 -16
- data/lib/jkr/userutils.rb +300 -22
- data/lib/jkr/utils.rb +38 -314
- data/lib/jkr/version.rb +3 -0
- data/sample-jkr.plan +52 -0
- metadata +171 -63
- data/bin/jkr +0 -224
- data/test/test_jkr.rb +0 -8
data/lib/jkr/error.rb
ADDED
data/lib/jkr/numeric.rb
ADDED
@@ -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
|
data/lib/jkr/plan.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
37
|
+
attr_accessor :file_path
|
22
38
|
attr_reader :jkr_env
|
23
39
|
|
24
|
-
|
40
|
+
private_class_method :new
|
41
|
+
def initialize(jkr_env)
|
42
|
+
@base_plan = nil
|
43
|
+
@used_scripts = []
|
25
44
|
@jkr_env = jkr_env
|
26
|
-
@
|
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 =
|
35
|
-
|
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
|
-
|
38
|
-
|
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
|
-
|
41
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|