laborantin 0.0.14 → 0.0.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/INFO +1 -0
  2. data/README +148 -4
  3. data/Rakefile +73 -27
  4. data/TODO +27 -6
  5. data/bin/labor +2 -404
  6. data/lib/laborantin.rb +13 -26
  7. data/lib/laborantin/core/analysis.rb +231 -0
  8. data/lib/laborantin/core/command.rb +234 -0
  9. data/lib/laborantin/core/completeable.rb +30 -0
  10. data/lib/laborantin/core/configurable.rb +28 -0
  11. data/lib/laborantin/core/datable.rb +13 -0
  12. data/lib/laborantin/core/describable.rb +11 -0
  13. data/lib/laborantin/core/environment.rb +90 -52
  14. data/lib/laborantin/core/hookable.rb +20 -0
  15. data/lib/laborantin/core/monkey_patches.rb +0 -1
  16. data/lib/laborantin/core/multi_name.rb +25 -0
  17. data/lib/laborantin/core/parameter.rb +5 -12
  18. data/lib/laborantin/core/parameter_hash.rb +6 -2
  19. data/lib/laborantin/core/scenario.rb +93 -70
  20. data/lib/laborantin/core/table.rb +84 -0
  21. data/lib/laborantin/extra/commands/git.rb +40 -0
  22. data/lib/laborantin/extra/commands/git/check.rb +25 -0
  23. data/lib/laborantin/extra/commands/git/run.rb +100 -0
  24. data/lib/laborantin/extra/vectorial_product.rb +31 -0
  25. data/lib/laborantin/runner.rb +247 -0
  26. data/lib/laborantin/runner/commands/analyze.rb +58 -0
  27. data/lib/laborantin/runner/commands/cleanup.rb +40 -0
  28. data/lib/laborantin/runner/commands/complete.rb +111 -0
  29. data/lib/laborantin/runner/commands/config.rb +169 -0
  30. data/lib/laborantin/runner/commands/create.rb +61 -0
  31. data/lib/laborantin/runner/commands/describe.rb +215 -0
  32. data/lib/laborantin/runner/commands/find.rb +82 -0
  33. data/lib/laborantin/runner/commands/load_classes.rb +75 -0
  34. data/lib/laborantin/runner/commands/load_results.rb +143 -0
  35. data/lib/laborantin/runner/commands/note.rb +35 -0
  36. data/lib/laborantin/runner/commands/replay.rb +89 -0
  37. data/lib/laborantin/runner/commands/rm.rb +107 -0
  38. data/lib/laborantin/runner/commands/run.rb +131 -0
  39. data/lib/laborantin/runner/commands/scan.rb +77 -0
  40. metadata +45 -13
  41. data/bin/files/README.erb +0 -29
  42. data/bin/files/TODO.erb +0 -2
  43. data/bin/files/config/ftp.yaml.erb +0 -6
  44. data/bin/files/config/xmpp.yaml.erb +0 -7
  45. data/bin/files/environments/environment.rb.erb +0 -10
  46. data/bin/files/scenarii/scenario.rb.erb +0 -13
data/INFO ADDED
@@ -0,0 +1 @@
1
+ could not determine sha1 at packaging time
data/README CHANGED
@@ -1,5 +1,149 @@
1
- Laborantin is a measurement batch facilitator.
2
- When you need to perform a lot of measurements varying some parameters, Laborantin helps you to stay organized.
3
- It provides an abstraction of environments and scenarii, vary all possible parameters and store everything in directories.
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.version = '0.0.14'
7
- s.platform = Gem::Platform::RUBY
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.rubyforge_project = 'laborantin'
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.require_path = 'lib'
33
- s.bindir = 'bin'
34
- s.executables = ['labor']
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
- replay / pause / restart
2
-
3
- => interaction with rubydrill?
4
- => or direct stats
5
-
6
- creation of a script that doesn't depends on laborantin for export?
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
- #require '../lib/laborantin'
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
- class Command
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)