laborantin 0.0.14 → 0.0.21
Sign up to get free protection for your applications and to get access to all the features.
- data/INFO +1 -0
- data/README +148 -4
- data/Rakefile +73 -27
- data/TODO +27 -6
- data/bin/labor +2 -404
- data/lib/laborantin.rb +13 -26
- data/lib/laborantin/core/analysis.rb +231 -0
- data/lib/laborantin/core/command.rb +234 -0
- data/lib/laborantin/core/completeable.rb +30 -0
- data/lib/laborantin/core/configurable.rb +28 -0
- data/lib/laborantin/core/datable.rb +13 -0
- data/lib/laborantin/core/describable.rb +11 -0
- data/lib/laborantin/core/environment.rb +90 -52
- data/lib/laborantin/core/hookable.rb +20 -0
- data/lib/laborantin/core/monkey_patches.rb +0 -1
- data/lib/laborantin/core/multi_name.rb +25 -0
- data/lib/laborantin/core/parameter.rb +5 -12
- data/lib/laborantin/core/parameter_hash.rb +6 -2
- data/lib/laborantin/core/scenario.rb +93 -70
- data/lib/laborantin/core/table.rb +84 -0
- data/lib/laborantin/extra/commands/git.rb +40 -0
- data/lib/laborantin/extra/commands/git/check.rb +25 -0
- data/lib/laborantin/extra/commands/git/run.rb +100 -0
- data/lib/laborantin/extra/vectorial_product.rb +31 -0
- data/lib/laborantin/runner.rb +247 -0
- data/lib/laborantin/runner/commands/analyze.rb +58 -0
- data/lib/laborantin/runner/commands/cleanup.rb +40 -0
- data/lib/laborantin/runner/commands/complete.rb +111 -0
- data/lib/laborantin/runner/commands/config.rb +169 -0
- data/lib/laborantin/runner/commands/create.rb +61 -0
- data/lib/laborantin/runner/commands/describe.rb +215 -0
- data/lib/laborantin/runner/commands/find.rb +82 -0
- data/lib/laborantin/runner/commands/load_classes.rb +75 -0
- data/lib/laborantin/runner/commands/load_results.rb +143 -0
- data/lib/laborantin/runner/commands/note.rb +35 -0
- data/lib/laborantin/runner/commands/replay.rb +89 -0
- data/lib/laborantin/runner/commands/rm.rb +107 -0
- data/lib/laborantin/runner/commands/run.rb +131 -0
- data/lib/laborantin/runner/commands/scan.rb +77 -0
- metadata +45 -13
- data/bin/files/README.erb +0 -29
- data/bin/files/TODO.erb +0 -2
- data/bin/files/config/ftp.yaml.erb +0 -6
- data/bin/files/config/xmpp.yaml.erb +0 -7
- data/bin/files/environments/environment.rb.erb +0 -10
- data/bin/files/scenarii/scenario.rb.erb +0 -13
data/README
CHANGED
@@ -1,5 +1,149 @@
|
|
1
|
-
Laborantin
|
2
|
-
|
3
|
-
|
1
|
+
= Laborantin (cheaper than an intern!)
|
2
|
+
|
3
|
+
|
4
|
+
== Purpose
|
5
|
+
|
6
|
+
Laborantin is a framework intended to help people that performs a lot of experiments that can be scripted (e.g., scientists).
|
7
|
+
It has two main abstractions: Environment and Scenario.
|
8
|
+
* Environment: represents what cannot be changed easily, like the version of an operating system, or an hardware component.
|
9
|
+
* Scenario: represents an experiment, with parameters that you will vary to study their impact.
|
10
|
+
|
11
|
+
Laborantin ships with an executable script that allows you to perform recurring tasks like running, finding experiments with filtering on parameters sets.
|
12
|
+
The experiments reports are stored in a directory structure such that you don't have to bother about where to store which paramters.
|
13
|
+
Environments and Scenarios are also accessible as regular libraries (unless you don't want to use Ruby at all), such that you can use all the stored data without too much efforts.
|
14
|
+
|
15
|
+
=== Benefits
|
16
|
+
|
17
|
+
Laborantin enables you to focus on your experiments and not on the silly issues that come with scientific rigor, like logging what happened, organizing your scripts, or writing an argument parser for the N-th time.
|
18
|
+
|
19
|
+
=== Current Features
|
20
|
+
|
21
|
+
* library with
|
22
|
+
* handy logging
|
23
|
+
* hook points before/after experiments
|
24
|
+
* easy selection of experiments for post-analysis
|
25
|
+
* command line interface
|
26
|
+
* to run, retrieve, destroy, analyze experiments
|
27
|
+
* easily extendable (e.g. for HTML reporting)
|
28
|
+
|
29
|
+
=== Next Improvements
|
30
|
+
|
31
|
+
* verify that we're actually in a laborantin directory
|
32
|
+
* run identifier
|
33
|
+
* per-scenario logfile
|
34
|
+
* inherit from parameters, products and hooks
|
35
|
+
* better analysis meta-data for automated reporting
|
36
|
+
* better Git integration
|
37
|
+
* better use of the configurations
|
38
|
+
|
39
|
+
=== Future
|
40
|
+
|
41
|
+
The future of Laborantin mainly depends on you, what are your needs? can you patch it?
|
42
|
+
We envision four main branches for the future of Laborantin:
|
43
|
+
* *Develop* a way for everyone to define its prefered directory structure, especially heretics that do not want to use Ruby and Laborantin's libraries to read the experiments reports.
|
44
|
+
* *Develop* roles in experiments having each Laborantin be a cooperating agent (maybe a P2P one).
|
45
|
+
|
46
|
+
Also, Laborantin was written super quickly, and there certainly is room for a lot of code and design improvements, feel free to participate (see contacts below).
|
47
|
+
== Workflow
|
48
|
+
|
49
|
+
Working with Laborantin is not straightforward, but it's not too much of a pain neither. You can follow the tutorial (http://dicioccio.fr/laborantin) if you want the short overview on how to use it.
|
50
|
+
|
51
|
+
=== The labor script commands
|
52
|
+
|
53
|
+
==== help
|
54
|
+
Simply shows a list of available commands, if an argument is passed, details the command.
|
55
|
+
|
56
|
+
- <tt>labor help</tt>
|
57
|
+
- <tt>labor create --help</tt>
|
58
|
+
|
59
|
+
==== create
|
60
|
+
Initializes a workbench with the correct directory structure. You can directly create empty stub files for environments and scenarii if you know what you will do.
|
61
|
+
|
62
|
+
- <tt>labor create workbench [--envs=foo1,foo2] [-s bar1]</tt>
|
63
|
+
|
64
|
+
==== describe
|
65
|
+
Describes a workbench Environments and Scenarii, that's why it's important to fill the descriptions.
|
66
|
+
|
67
|
+
- <tt>labor describe</tt>
|
68
|
+
|
69
|
+
==== run
|
70
|
+
Runs matching -e Environments and -s Scenarii experiments, with parameters given in -p. With the -c flag, you can set it to skip experiments which already have a successful record in the database.
|
71
|
+
|
72
|
+
- <tt>labor run [-e foo] [-p "{:bar => [1,2,3]}"] [-s bar1]</tt>
|
73
|
+
|
74
|
+
==== scan
|
75
|
+
Scans the result directory and counts, for each Scenario and Environment class, the number of performed instances.
|
76
|
+
|
77
|
+
- <tt>labor scan</tt>
|
78
|
+
|
79
|
+
==== find
|
80
|
+
Finds in the result dir the performed instances, and prints their name. The --successful or --failed flags allow you to display only the experiments that went right.
|
81
|
+
|
82
|
+
- <tt>labor find [-p "{:foo => [1,2,3]}"]</tt>
|
83
|
+
|
84
|
+
==== replay
|
85
|
+
Produces the scenario's product for scenarios that matching the -e, -s, and -p options (like in the run command).
|
86
|
+
|
87
|
+
- <tt>labor replay [-s foo,bar] [-p "{:baz => [1,2,3]}"]</tt>
|
88
|
+
- <tt>labor replay [-m meth1,meth2]</tt>
|
89
|
+
|
90
|
+
==== analyze
|
91
|
+
Analyses the result to produce what you want (e.g., a plot). The -a option allows you to select only a subset of the analyses.
|
92
|
+
- <tt>labor analyze [-a foo,bar]</tt>
|
93
|
+
|
94
|
+
==== rm
|
95
|
+
Removes the result from the database, the syntax is very similar to labor find. Simply: what would be printed on the screen with the find command will be destroyed.
|
96
|
+
|
97
|
+
==== note
|
98
|
+
Adds a record to the BOOKNOTE file, its only purpose is for you to keep track of oddities, bugs, or ideas during the long process of doing experimentations.
|
99
|
+
|
100
|
+
==== config set|get|del
|
101
|
+
Currently unused, will serve later.
|
102
|
+
|
103
|
+
=== Typical workflow
|
104
|
+
|
105
|
+
==== Ideal world
|
106
|
+
The following list lacks many details, hopefully I'll find time to write more doc.
|
107
|
+
|
108
|
+
* create a project directory with <tt>labor create <name></tt>.
|
109
|
+
* cd <name>
|
110
|
+
* edit ./environments/<env>
|
111
|
+
* edit ./scenarii/<scenario> (NB: the "run method in your scenarios must take at least 1sec., sleep if necessary")
|
112
|
+
* verify that things seems correct with <tt>labor describe</tt>
|
113
|
+
* run things with <tt>labor run</tt> (and wait a bit)
|
114
|
+
* ensure that we have the results we need with <tt>labor scan</tt>
|
115
|
+
* produces per-scenario intermediary results with <tt>labor replay</tt>
|
116
|
+
* edit ./analyses/<analysis>
|
117
|
+
* analyse your results with <tt>labor analyze</tt>
|
118
|
+
|
119
|
+
Moreover, at any point, you can take notes with <tt>labor note "some note"</tt>.
|
120
|
+
|
121
|
+
==== Real (cursed) world
|
122
|
+
Often, experiments crash. Sometimes, you are bothered by the presence of an old result which forces you to add filter to the command line.
|
123
|
+
|
124
|
+
Since last version, Laborantin provides support to handle these cases:
|
125
|
+
|
126
|
+
Let's say you had a crash during a run of 1000 experiments. <tt>labor scan</tt> will show that you only completed 200 of them. The thing is that you have a lot of parameters, and little time to figure-out which combinations of parameters are missing. Your Laborantin will do that for you, just re-run the same command that crashed (after fixing the source of the bug), and append it the -c option, and it will re-run only what crashed or is missing.
|
127
|
+
|
128
|
+
Then if you're not interested in the failed results, you can use <tt>labor rm --failed</tt>.
|
129
|
+
|
130
|
+
== Hints
|
131
|
+
|
132
|
+
=== Super classes ?
|
133
|
+
You can benefit from object orientation of Scenarii both in Environment and Scenarii, just make sure you don't run them (for Environment, set a dummy verification which is always false).
|
134
|
+
|
135
|
+
Unfortunately, subclasses of Environment or Scenario do not automatically inheritates from parent's parameter/products/setup/teardown information.
|
136
|
+
|
137
|
+
=== Environment or Scenario ?
|
138
|
+
Sometimes it is hard to know what should go inside the code of the Environment and what should go inside the code of the Scenario. Indeed, you can compare two environments like you would compare two scenariis. There is no rule that fits all purposes, but I suggest that what can be changed easily should go to the environment. Also, all code which is related to sending commands to distant computers fit well in environments. Meanwhile, the content of the command is build inside the scenario.
|
139
|
+
|
140
|
+
=== Parameter or not ?
|
141
|
+
The same kind of ambivalence exists between Parameters and Scenarii. Sometimes the design of the same scenario with a different parameter makes more sense with a subclass (when you start having a lot of `if params[:foo] == :bar'. It's up to you to decide afterwards, how you'll be filtering on results and perform your analysis.
|
142
|
+
|
143
|
+
== Contributors / Contacts
|
144
|
+
|
145
|
+
* Lucas Di Cioccio (http://dicioccio.fr)
|
146
|
+
|
147
|
+
== Licence
|
148
|
+
This library and tools are licensed under the GPL version 3 (http://www.gnu.org/licenses).
|
4
149
|
|
5
|
-
Next, it will ease the reprocessing of measurements, synchronization to send reports to a backup server, and maybe provide some coordination with distant nodes.
|
data/Rakefile
CHANGED
@@ -1,46 +1,92 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake/gempackagetask'
|
3
3
|
|
4
|
+
$LOAD_PATH.unshift('lib')
|
5
|
+
require 'lib/laborantin'
|
6
|
+
|
4
7
|
spec = Gem::Specification.new do |s|
|
8
|
+
|
5
9
|
s.name = 'laborantin'
|
6
|
-
s.
|
7
|
-
s.
|
10
|
+
s.rubyforge_project = 'laborantin'
|
11
|
+
s.version = Laborantin::VERSION
|
12
|
+
s.author = Laborantin::AUTHORS.first
|
13
|
+
s.homepage = Laborantin::WEBSITE
|
8
14
|
s.summary = "A measurement batch facilitator"
|
9
|
-
|
10
|
-
s.author = "Di Cioccio Lucas"
|
11
15
|
s.email = "lucas.dicioccio<@nospam@>frihd.net"
|
12
|
-
s.
|
13
|
-
s.homepage = 'http://rubyforge.org/projects/laborantin'
|
14
|
-
|
15
|
-
s.files = ['README', 'LICENSE', 'gpl-3.0.txt',
|
16
|
-
'Rakefile', 'TODO',
|
17
|
-
'bin/labor',
|
18
|
-
'bin/files/README.erb',
|
19
|
-
'bin/files/TODO.erb',
|
20
|
-
'bin/files/config/ftp.yaml.erb',
|
21
|
-
'bin/files/config/xmpp.yaml.erb',
|
22
|
-
'bin/files/environments/environment.rb.erb',
|
23
|
-
'bin/files/scenarii/scenario.rb.erb',
|
24
|
-
'lib/laborantin.rb',
|
25
|
-
'lib/laborantin/core/environment.rb',
|
26
|
-
'lib/laborantin/core/parameter.rb',
|
27
|
-
'lib/laborantin/core/parameter_hash.rb',
|
28
|
-
'lib/laborantin/core/scenario.rb',
|
29
|
-
'lib/laborantin/core/monkey_patches.rb',
|
30
|
-
]
|
16
|
+
s.platform = Gem::Platform::RUBY
|
31
17
|
|
32
|
-
s.
|
33
|
-
|
34
|
-
|
18
|
+
s.files = [
|
19
|
+
'README',
|
20
|
+
'LICENSE',
|
21
|
+
'gpl-3.0.txt',
|
22
|
+
'Rakefile',
|
23
|
+
'TODO',
|
24
|
+
'INFO',
|
25
|
+
'bin/labor',
|
26
|
+
'lib/laborantin.rb',
|
27
|
+
'lib/laborantin/core/environment.rb',
|
28
|
+
'lib/laborantin/core/parameter.rb',
|
29
|
+
'lib/laborantin/core/parameter_hash.rb',
|
30
|
+
'lib/laborantin/core/scenario.rb',
|
31
|
+
'lib/laborantin/core/monkey_patches.rb',
|
32
|
+
'lib/laborantin/core/analysis.rb',
|
33
|
+
'lib/laborantin/core/command.rb',
|
34
|
+
'lib/laborantin/core/datable.rb',
|
35
|
+
'lib/laborantin/core/completeable.rb',
|
36
|
+
'lib/laborantin/core/describable.rb',
|
37
|
+
'lib/laborantin/core/hookable.rb',
|
38
|
+
'lib/laborantin/core/configurable.rb',
|
39
|
+
'lib/laborantin/core/multi_name.rb',
|
40
|
+
'lib/laborantin/core/table.rb',
|
41
|
+
'lib/laborantin/runner.rb',
|
42
|
+
'lib/laborantin/runner/commands/complete.rb',
|
43
|
+
'lib/laborantin/runner/commands/create.rb',
|
44
|
+
'lib/laborantin/runner/commands/describe.rb',
|
45
|
+
'lib/laborantin/runner/commands/run.rb',
|
46
|
+
'lib/laborantin/runner/commands/analyze.rb',
|
47
|
+
'lib/laborantin/runner/commands/load_classes.rb',
|
48
|
+
'lib/laborantin/runner/commands/load_results.rb',
|
49
|
+
'lib/laborantin/runner/commands/replay.rb',
|
50
|
+
'lib/laborantin/runner/commands/find.rb',
|
51
|
+
'lib/laborantin/runner/commands/scan.rb',
|
52
|
+
'lib/laborantin/runner/commands/note.rb',
|
53
|
+
'lib/laborantin/runner/commands/cleanup.rb',
|
54
|
+
'lib/laborantin/runner/commands/rm.rb',
|
55
|
+
'lib/laborantin/runner/commands/config.rb',
|
56
|
+
'lib/laborantin/extra/commands/git.rb',
|
57
|
+
'lib/laborantin/extra/commands/git/check.rb',
|
58
|
+
'lib/laborantin/extra/commands/git/run.rb',
|
59
|
+
'lib/laborantin/extra/vectorial_product.rb'
|
60
|
+
]
|
35
61
|
|
62
|
+
s.require_path = 'lib'
|
63
|
+
s.bindir = 'bin'
|
64
|
+
s.executables = ['labor']
|
36
65
|
s.has_rdoc = true
|
37
66
|
end
|
38
67
|
|
68
|
+
file 'INFO' => spec.files - ['INFO'] do
|
69
|
+
print "trying to gather source revision for build ... "
|
70
|
+
File.open('INFO', 'w') do |f|
|
71
|
+
begin
|
72
|
+
require 'git'
|
73
|
+
g = Git.open('.')
|
74
|
+
sha1 = g.revparse(g.current_branch)
|
75
|
+
f.puts sha1
|
76
|
+
puts "OK"
|
77
|
+
rescue Exception => err
|
78
|
+
puts "KO (not fatal)"
|
79
|
+
f.puts "could not determine sha1 at packaging time"
|
80
|
+
puts "#{err}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
39
85
|
Rake::GemPackageTask.new(spec) do |pkg|
|
40
86
|
pkg.need_tar = true
|
41
87
|
end
|
42
88
|
|
43
|
-
task :gem => "pkg/#{spec.name}-#{spec.version}.gem" do
|
89
|
+
task :gem => ["pkg/#{spec.name}-#{spec.version}.gem", 'INFO'] do
|
44
90
|
puts "generated #{spec.version}"
|
45
91
|
end
|
46
92
|
|
data/TODO
CHANGED
@@ -1,6 +1,27 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
new data structure:
|
2
|
+
in config files (both env and scenarii):
|
3
|
+
reference every rundir
|
4
|
+
reference the class type
|
5
|
+
reference the success/failure status (not in scenarii for now)
|
6
|
+
reference the date (not in env for now, badly done for scenarii)
|
7
|
+
reference the laborantin version somewhere
|
8
|
+
relax 1sec constraint for uniqueness with an ID field instead
|
9
|
+
make such that load / save methods pass through an adapter (database)
|
10
|
+
migrate command that will take a legacy dir and make a new one from it by loading the old way; fixing the data model; saving in the new way
|
11
|
+
commands:
|
12
|
+
report
|
13
|
+
publish
|
14
|
+
migrate (from version to version, and dir structure to dir structure)
|
15
|
+
check (a sort of run but that you can abbreviate,
|
16
|
+
and that writes in a tempdir)
|
17
|
+
optimize (given a goal)
|
18
|
+
estimate (cost, duration,
|
19
|
+
whatever defined by the user, only total sum, products?)
|
20
|
+
infos (gives info about the running environment)
|
21
|
+
config dir:
|
22
|
+
extra libraries to require
|
23
|
+
general:
|
24
|
+
move runner into core/ and cli_runner into base/
|
25
|
+
move runner/commands in another directory (e.g. /commands or /base/commands or /core/commands)
|
26
|
+
roles for multi-machines
|
27
|
+
git integration (from create to everything)
|
data/bin/labor
CHANGED
@@ -1,408 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require 'rubygems'
|
4
3
|
require 'laborantin'
|
5
|
-
|
6
|
-
require 'optparse'
|
7
|
-
require 'fileutils'
|
8
|
-
require 'find'
|
9
|
-
require 'erb'
|
10
|
-
require 'yaml'
|
11
|
-
require 'net/ftp'
|
12
|
-
require 'logger'
|
4
|
+
require 'laborantin/runner'
|
13
5
|
|
14
|
-
|
15
|
-
attr_accessor :name, :description, :parser, :block, :options, :args
|
16
|
-
|
17
|
-
@@all = []
|
18
|
-
|
19
|
-
def find_and_require(dir, filter=[])
|
20
|
-
Find.find(dir) do |f|
|
21
|
-
test = if filter.empty?
|
22
|
-
File.extname(f) == '.rb'
|
23
|
-
else
|
24
|
-
filter.include? File.basename(f)
|
25
|
-
end
|
26
|
-
require f if test
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def initialize(name, description='')
|
31
|
-
@@all << self
|
32
|
-
@name = name
|
33
|
-
@description = description
|
34
|
-
@options = {}
|
35
|
-
@args = []
|
36
|
-
yield self
|
37
|
-
end
|
38
|
-
|
39
|
-
def self.all
|
40
|
-
@@all
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.for_name(name)
|
44
|
-
@@all.find{|c| c.name == name}
|
45
|
-
end
|
46
|
-
|
47
|
-
def opts(&argblk)
|
48
|
-
@parser = OptionParser.new &argblk
|
49
|
-
script_name = File.basename($0)
|
50
|
-
@parser.summary_indent = "\t"
|
51
|
-
@parser.banner = %{Usage: #{script_name} #{name} [OPTIONS] [env1 [env2 ..]]}
|
52
|
-
@parser.banner << %{\n#{description}} unless description.empty?
|
53
|
-
end
|
54
|
-
|
55
|
-
def parse!(args)
|
56
|
-
begin
|
57
|
-
@args = @parser.parse args
|
58
|
-
rescue OptionParser::InvalidOption => err
|
59
|
-
puts err
|
60
|
-
puts @parser
|
61
|
-
exit
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def execute(&blk)
|
66
|
-
@block = blk
|
67
|
-
end
|
68
|
-
|
69
|
-
def execute!
|
70
|
-
self.instance_eval &@block if @block
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
# Create
|
75
|
-
|
76
|
-
Command.new('create', 'prepares a dir for laborantin measurements') do |c|
|
77
|
-
c.options[:scenarii] = []
|
78
|
-
c.options[:environments] = []
|
79
|
-
c.options[:force] = false
|
80
|
-
c.opts do |o|
|
81
|
-
o.on('-s', '--scenarii=OPTIONAL', 'scenarii to build, comma separated', Array) {|val| c.options[:scenarii] = val}
|
82
|
-
o.on('-e', '--environments=OPTIONAL', 'environments to prepare, comma separated', Array) {|val| c.options[:environments] = val}
|
83
|
-
o.on_tail('-f', '--force', 'force overwrite') {|val| c.options[:force] = val}
|
84
|
-
o.on_tail('-h', '--help', 'shows this help and exit') {puts o; exit}
|
85
|
-
end
|
86
|
-
|
87
|
-
c.execute do
|
88
|
-
dir = args.shift
|
89
|
-
p dir #not for debug
|
90
|
-
unless c.options[:force]
|
91
|
-
begin
|
92
|
-
FileUtils.mkdir dir
|
93
|
-
rescue Errno::EEXIST => err
|
94
|
-
puts "Directory already exists, use -f/--force to overwrite (will overwrite all generated files, use at your own risk)."
|
95
|
-
exit
|
96
|
-
end
|
97
|
-
end
|
98
|
-
%w{environments scenarii results config scripts}.each do |w|
|
99
|
-
path = File.join( dir, w )
|
100
|
-
p path #not for debug
|
101
|
-
FileUtils.mkdir_p path
|
102
|
-
end
|
103
|
-
%w{README TODO config/ftp.yaml config/xmpp.yaml}.each do |w|
|
104
|
-
res_path = File.join(dir, w)
|
105
|
-
p res_path #not for debug
|
106
|
-
File.open(res_path, 'w') do |f|
|
107
|
-
tpl = ''
|
108
|
-
tpl_path = (File.join(File.dirname(__FILE__), 'files', "#{w}.erb"))
|
109
|
-
File.open(tpl_path) do |tf|
|
110
|
-
tpl = tf.read
|
111
|
-
end
|
112
|
-
erb = ERB.new tpl
|
113
|
-
erb.filename = res_path
|
114
|
-
f.puts erb.result(binding)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
@options[:scenarii].each do |s|
|
118
|
-
res_path = File.join(dir, 'scenarii', "#{s}.rb")
|
119
|
-
p res_path #not for debug
|
120
|
-
File.open(res_path, 'w') do |f|
|
121
|
-
tpl = ''
|
122
|
-
tpl_path = (File.join(File.dirname(__FILE__), 'files', 'scenarii', 'scenario.rb.erb'))
|
123
|
-
File.open(tpl_path) do |tf|
|
124
|
-
tpl = tf.read
|
125
|
-
end
|
126
|
-
erb = ERB.new tpl
|
127
|
-
erb.filename = res_path
|
128
|
-
f.puts erb.result(binding)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
@options[:environments].each do |e|
|
132
|
-
res_path = File.join(dir, 'environments', "#{e}.rb")
|
133
|
-
p res_path #not for debug
|
134
|
-
File.open(res_path, 'w') do |f|
|
135
|
-
tpl = ''
|
136
|
-
tpl_path = (File.join(File.dirname(__FILE__), 'files', 'environments', 'environment.rb.erb'))
|
137
|
-
File.open(tpl_path) do |tf|
|
138
|
-
tpl = tf.read
|
139
|
-
end
|
140
|
-
erb = ERB.new tpl
|
141
|
-
erb.filename = res_path
|
142
|
-
f.puts erb.result(binding)
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
# Describe
|
149
|
-
|
150
|
-
Command.new('describe', "describes a laborantin's work") do |c|
|
151
|
-
c.options[:environments] = []
|
152
|
-
c.options[:scenarii] = []
|
153
|
-
c.opts do |o|
|
154
|
-
o.on('-s', '--scenarii=OPTIONAL', 'scenarii to build, comma separated', Array) {|val| c.options[:scenarii] = val}
|
155
|
-
o.on('-e', '--environments=OPTIONAL', 'environments to prepare, comma separated', Array) {|val| c.options[:environments] = val}
|
156
|
-
o.on_tail('-h', '--help', 'shows this help and exit') {puts o; exit}
|
157
|
-
end
|
158
|
-
c.execute do
|
159
|
-
FileUtils.cd(args.first || '.')
|
160
|
-
loaded_envs = c.options[:environments].map{|e| "#{e}.rb"}
|
161
|
-
loaded_scii = c.options[:scenarii].map{|e| "#{e}.rb"}
|
162
|
-
find_and_require('environments', loaded_envs)
|
163
|
-
find_and_require('scenarii', loaded_scii)
|
164
|
-
|
165
|
-
Laborantin::Environment.all.each {|e| p e}
|
166
|
-
Laborantin::Scenario.all.each {|s| p s}
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
# Scan
|
171
|
-
|
172
|
-
Command.new('scan', "scan a laborantin's dir for results") do |c|
|
173
|
-
c.options[:filter] = ''
|
174
|
-
c.options[:environments] = []
|
175
|
-
c.options[:scenarii] = []
|
176
|
-
c.opts do |o|
|
177
|
-
o.on('-s', '--scenarii=OPTIONAL', 'scenarii to build, comma separated', Array) {|val| c.options[:scenarii] = val}
|
178
|
-
o.on('-e', '--environments=OPTIONAL', 'environments to prepare, comma separated', Array) {|val| c.options[:environments] = val}
|
179
|
-
o.on('-p', '--parameters=OPTIONAL', 'filter parameters (ruby hash format)', String) {|val| c.options[:filter] = val}
|
180
|
-
o.on_tail('-h', '--help', 'shows this help and exit') {puts o; exit}
|
181
|
-
end
|
182
|
-
c.execute do
|
183
|
-
FileUtils.cd(args.first || '.')
|
184
|
-
loaded_envs = c.options[:environments].map{|e| "#{e}.rb"}
|
185
|
-
loaded_scii = c.options[:scenarii].map{|e| "#{e}.rb"}
|
186
|
-
find_and_require('environments', loaded_envs)
|
187
|
-
find_and_require('scenarii', loaded_scii)
|
188
|
-
|
189
|
-
envs = Laborantin::Environment.scan_resdir('results')
|
190
|
-
|
191
|
-
scs = envs.map{|env| env.populate }.flatten
|
192
|
-
|
193
|
-
puts "Laborantin' summary:"
|
194
|
-
Laborantin::Environment.all.each do |envklass|
|
195
|
-
puts "#{envklass.name.duck_case} => #{envs.count{|e| e.is_a? envklass}}"
|
196
|
-
Laborantin::Scenario.all.each do |scklass|
|
197
|
-
puts "\t#{scklass.name.duck_case} => #{scs.count{|s| s.is_a? scklass and s.environment.is_a? envklass}}"
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
# Find
|
205
|
-
|
206
|
-
Command.new('find', "find the results dir for a set of params/env") do |c|
|
207
|
-
c.options[:filter] = ''
|
208
|
-
c.options[:environments] = []
|
209
|
-
c.options[:scenarii] = []
|
210
|
-
c.opts do |o|
|
211
|
-
o.on('-s', '--scenarii=OPTIONAL', 'scenarii to build, comma separated', Array) {|val| c.options[:scenarii] = val}
|
212
|
-
o.on('-e', '--environments=OPTIONAL', 'environments to prepare, comma separated', Array) {|val| c.options[:environments] = val}
|
213
|
-
o.on('-p', '--parameters=OPTIONAL', 'filter parameters (ruby hash format)', String) {|val| c.options[:filter] = val}
|
214
|
-
o.on_tail('-h', '--help', 'shows this help and exit') {puts o; exit}
|
215
|
-
end
|
216
|
-
c.execute do
|
217
|
-
|
218
|
-
FileUtils.cd(args.first || '.')
|
219
|
-
loaded_envs = c.options[:environments].map{|e| "#{e}.rb"}
|
220
|
-
loaded_scii = c.options[:scenarii].map{|e| "#{e}.rb"}
|
221
|
-
find_and_require('environments', loaded_envs)
|
222
|
-
find_and_require('scenarii', loaded_scii)
|
223
|
-
|
224
|
-
envs = Laborantin::Environment.scan_resdir('results')
|
225
|
-
|
226
|
-
params = eval @options[:filter] unless @options[:filter].empty?
|
227
|
-
params.each_key{|k| params[k] = [params[k]].flatten} if params
|
228
|
-
params ||= {}
|
229
|
-
|
230
|
-
scs = envs.map{|env| env.populate }.flatten
|
231
|
-
|
232
|
-
scs.each do |sc|
|
233
|
-
to_filter = params.keys.find{|k| not params[k].include?(sc.params[k])}
|
234
|
-
unless to_filter
|
235
|
-
puts sc.rundir
|
236
|
-
puts sc.params.inspect
|
237
|
-
end
|
238
|
-
end
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
# Replay
|
243
|
-
|
244
|
-
Command.new('replay', 'scan existing runs, keep their raw results but rebuild all the products') do |c|
|
245
|
-
c.options[:scenarii] = []
|
246
|
-
c.options[:environments] = []
|
247
|
-
c.options[:methods] = []
|
248
|
-
c.options[:filter] = ''
|
249
|
-
c.opts do |o|
|
250
|
-
o.on('-m', '--methods=OPTIONAL', 'list of methods to call, else all the products will, you can provide non products methods as well, so beware the typo', Array) {|val| c.options[:methods] = val}
|
251
|
-
o.on('-s', '--scenarii=OPTIONAL', 'scenarii to build, comma separated', Array) {|val| c.options[:scenarii] = val}
|
252
|
-
o.on('-e', '--environments=OPTIONAL', 'environments to prepare, comma separated', Array) {|val| c.options[:environments] = val}
|
253
|
-
o.on('-p', '--parameters=OPTIONAL', 'filter parameters (ruby hash format)', String) {|val| c.options[:filter] = val}
|
254
|
-
o.on_tail('-h', '--help', 'shows this help and exit') {puts o; exit}
|
255
|
-
end
|
256
|
-
c.execute do
|
257
|
-
|
258
|
-
FileUtils.cd(args.first || '.')
|
259
|
-
loaded_envs = c.options[:environments].map{|e| "#{e}.rb"}
|
260
|
-
loaded_scii = c.options[:scenarii].map{|e| "#{e}.rb"}
|
261
|
-
find_and_require('environments', loaded_envs)
|
262
|
-
find_and_require('scenarii', loaded_scii)
|
263
|
-
|
264
|
-
envs = Laborantin::Environment.scan_resdir('results')
|
265
|
-
envs.each{|env| env.loggers << Logger.new(STDOUT)}
|
266
|
-
|
267
|
-
params = eval @options[:filter] unless @options[:filter].empty?
|
268
|
-
params.each_key{|k| params[k] = [params[k]].flatten} if params
|
269
|
-
params ||= {}
|
270
|
-
|
271
|
-
scs = envs.map{|env| env.populate }.flatten
|
272
|
-
|
273
|
-
scs.each do |sc|
|
274
|
-
to_filter = params.keys.find{|k| not params[k].include?(sc.params[k])}
|
275
|
-
unless to_filter
|
276
|
-
sc.environment.log "Replaying products #{sc.params.inspect}"
|
277
|
-
if c.options[:methods].empty?
|
278
|
-
sc.analyze!
|
279
|
-
else
|
280
|
-
c.options[:methods].each do |meth|
|
281
|
-
sc.send meth
|
282
|
-
end
|
283
|
-
end
|
284
|
-
end
|
285
|
-
end
|
286
|
-
end
|
287
|
-
end
|
288
|
-
|
289
|
-
# Run
|
290
|
-
|
291
|
-
Command.new('run', 'actually perform measurements') do |c|
|
292
|
-
c.options[:scenarii] = []
|
293
|
-
c.options[:environments] = []
|
294
|
-
c.options[:filter] = ''
|
295
|
-
c.opts do |o|
|
296
|
-
o.on('-s', '--scenarii=OPTIONAL', 'scenarii to build, comma separated', Array) {|val| c.options[:scenarii] = val}
|
297
|
-
o.on('-e', '--environments=OPTIONAL', 'environments to prepare, comma separated', Array) {|val| c.options[:environments] = val}
|
298
|
-
o.on('-p', '--parameters=OPTIONAL', 'filter parameters (ruby hash format)', String) {|val| c.options[:filter] = val}
|
299
|
-
o.on_tail('-h', '--help', 'shows this help and exit') {puts o; exit}
|
300
|
-
end
|
301
|
-
c.execute do
|
302
|
-
dir = args.first || '.'
|
303
|
-
FileUtils.cd(dir)
|
304
|
-
Find.find('environments', 'scenarii') do |f|
|
305
|
-
require f if File.extname(f) == '.rb'
|
306
|
-
end
|
307
|
-
|
308
|
-
envs = if c.options[:environments].empty?
|
309
|
-
Laborantin::Environment.all
|
310
|
-
else
|
311
|
-
c.options[:environments].map!{|e| e.camelize}
|
312
|
-
Laborantin::Environment.all.select{|e| c.options[:environments].include? e.name}
|
313
|
-
end
|
314
|
-
|
315
|
-
scii = if c.options[:scenarii].empty?
|
316
|
-
Laborantin::Scenario.all
|
317
|
-
else
|
318
|
-
c.options[:scenarii].map!{|e| e.camelize}
|
319
|
-
Laborantin::Scenario.all.select{|e| c.options[:scenarii].include? e.name}
|
320
|
-
end
|
321
|
-
|
322
|
-
params = eval @options[:filter] unless @options[:filter].empty?
|
323
|
-
params.each_key{|k| params[k] = [params[k]].flatten} if params
|
324
|
-
|
325
|
-
envs.each do |eklass|
|
326
|
-
env = eklass.new
|
327
|
-
if env.valid?
|
328
|
-
env.prepare!
|
329
|
-
env.log "Running matching scenarii", :info #TODO: pass the logging+running in the env
|
330
|
-
scii.each do |sklass|
|
331
|
-
sklass.parameters.merge! params if params
|
332
|
-
env.log sklass.parameters.inspect
|
333
|
-
sklass.parameters.each_config do |cfg|
|
334
|
-
sc = sklass.new(env, cfg)
|
335
|
-
sc.prepare!
|
336
|
-
sc.perform!
|
337
|
-
sc.analyze!
|
338
|
-
end
|
339
|
-
end
|
340
|
-
env.teardown!
|
341
|
-
env.log "Scenarii performed", :info
|
342
|
-
end
|
343
|
-
end
|
344
|
-
end
|
345
|
-
end
|
346
|
-
|
347
|
-
# Run missing
|
348
|
-
# TODO scan existing params + filter
|
349
|
-
|
350
|
-
# FTP
|
351
|
-
|
352
|
-
Command.new('ftp-up', 'upload results via FTP') do |c|
|
353
|
-
c.opts do |o|
|
354
|
-
o.on_tail('-h', '--help', 'shows this help and exit') {puts o; exit}
|
355
|
-
end
|
356
|
-
c.execute do
|
357
|
-
dir = args.first || '.'
|
358
|
-
FileUtils.cd(dir)
|
359
|
-
cfg_file = File.join('config', 'ftp.yaml')
|
360
|
-
puts "Reading config file from #{cfg_file}"
|
361
|
-
cfg = YAML.load_file cfg_file
|
362
|
-
if cfg[:enable]
|
363
|
-
#TODO: port
|
364
|
-
# optimize that
|
365
|
-
ftp = Net::FTP.open(cfg[:host], cfg[:nick], cfg[:pass]) do |ftp|
|
366
|
-
Find.find('results') do |f|
|
367
|
-
if File.directory? f
|
368
|
-
ftp.chdir '/'
|
369
|
-
begin
|
370
|
-
ftp.mkdir f
|
371
|
-
rescue Net::FTPPermError
|
372
|
-
end
|
373
|
-
ftp.chdir f
|
374
|
-
elsif File.file? f
|
375
|
-
begin
|
376
|
-
ftp.putbinaryfile f
|
377
|
-
rescue Net::FTPPermError
|
378
|
-
end
|
379
|
-
end
|
380
|
-
end
|
381
|
-
end #ftp
|
382
|
-
else
|
383
|
-
p "Disabled, please edit #{cfg_file}"
|
384
|
-
end
|
385
|
-
end
|
386
|
-
end
|
387
|
-
|
388
|
-
# Actually run the commands
|
389
|
-
|
390
|
-
cmd = Command.for_name ARGV.first
|
391
|
-
|
392
|
-
if cmd
|
393
|
-
cmd.parse!(ARGV.dup[1 .. -1])
|
394
|
-
cmd.execute!
|
395
|
-
else
|
396
|
-
if ARGV.empty?
|
397
|
-
puts %{#{$0} v.#{Laborantin::VERSION}: No command given
|
398
|
-
|
399
|
-
Usage: #{$0} <command> [options ...]
|
400
|
-
|
401
|
-
<command> among: #{Command.all.map{|c| c.name}.join(", ")}
|
402
|
-
|
403
|
-
use #{$0} <command> --help for help on a specific command
|
404
|
-
}
|
405
|
-
else
|
406
|
-
puts "Unknown command #{ARGV.first}. \nKnown commands: #{Command.all.map{|c| c.name}.join(', ')} "
|
407
|
-
end
|
408
|
-
end
|
6
|
+
Laborantin::CliRunner.instance.run_argv(ARGV)
|