Class: Laborantin::Scenario

Inherits:
Object
  • Object
show all
Extended by:
Metaprog::Describable, Metaprog::Hookable, Metaprog::MultiName
Includes:
Metaprog::Configurable, Metaprog::Datable, Metaprog::Dependencies, Metaprog::Exports, Metaprog::Selector
Defined in:
lib/laborantin/core/scenario.rb

Overview

A Scenario represents a measurement done in a given environment. Some of its parameters will change, and we are interested in varying these parameters and then study their impact.

An user will usually creates a Scenario subklass which represents such a measurement. For that he must defines a run method that will yield consecutive lines added to the raw result file. Then this file can be processed to give intermediary or final results.

Like the Environment, all the subklasses will be stored in a @@all class variable for convenience purpose.

Constant Summary collapse

@@all =
[]

Constants included from Metaprog::MultiName

Metaprog::MultiName::AVAILABLE_NAMES

Class Attribute Summary collapse

Instance Attribute Summary collapse

Attributes included from Metaprog::Describable

#description

Attributes included from Metaprog::Hookable

#hooks

Attributes included from Metaprog::MultiName

#cli_name, #fs_name

Attributes included from Metaprog::Selector

#environments, #scenarii

Attributes included from Metaprog::Configurable

#config

Attributes included from Metaprog::Datable

#date

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Metaprog::Hookable

setup, teardown

Methods included from Metaprog::MultiName

set_name

Methods included from Metaprog::Exports

#export, #exports, #load_exports, #plots, #save_exports

Methods included from Metaprog::Dependencies

included

Methods included from Metaprog::Selector

included, #load_environments, #load_prior_results, #load_scenarii, #select_instance?

Methods included from Metaprog::Configurable

#load_config!, #save_config

Methods included from Metaprog::Datable

#date_str

Constructor Details

#initialize(env, params = {}) ⇒ Scenario

Initializes a new instance contains in the env Environment, and for the parameter set params. Sets the date to Time.now for unicity (with 1sec granularity) Sets the rundir accessor in the directory. Does NOT create any directory, so the accessors can be overwritten if needed.



167
168
169
170
171
172
173
# File 'lib/laborantin/core/scenario.rb', line 167

def initialize(env, params={})
  @environment = env
  @params = params
  @date = Time.now
  @config = {}
  @rundir = File.join(self.class.scenardir(environment), date_str)
end

Class Attribute Details

.parametersObject

The set of parameters that will vary for this Scenario.



97
98
99
# File 'lib/laborantin/core/scenario.rb', line 97

def parameters
  @parameters
end

.productsObject

Some special products that are done after an analysis on a measurement scenario. The intended way is to store raw results (e.g. a command output) in a file and then parse them and store the parsed result in another file etc. TODO : products that compares scenarii



103
104
105
# File 'lib/laborantin/core/scenario.rb', line 103

def products
  @products
end

Instance Attribute Details

#environmentObject

The environment in which we run this scenario.



156
157
158
# File 'lib/laborantin/core/scenario.rb', line 156

def environment
  @environment
end

#paramsObject

A hash of parameters for this run.



153
154
155
# File 'lib/laborantin/core/scenario.rb', line 153

def params
  @params
end

#rundirObject

An attribute that holds the directory where the config and the results are stored. Can be overridden (e.g. Scenario.scan_env does that).



160
161
162
# File 'lib/laborantin/core/scenario.rb', line 160

def rundir
  @rundir
end

Class Method Details

.allObject

Returns all the known subklasses of Scenario.



139
140
141
# File 'lib/laborantin/core/scenario.rb', line 139

def all
  @@all
end

.inherited(klass) ⇒ Object

Prepares attributes’ default values whenever a subclass is created.



106
107
108
109
110
111
112
113
# File 'lib/laborantin/core/scenario.rb', line 106

def inherited(klass)
  klass.parameters = ParameterHash.new.replace(self.parameters || {})
  klass.description = "#{self.description} (child of #{self})"
  klass.products = self.products ? self.products.dup : []
  klass.hooks = Hash.new.replace(self.hooks || {:setup => [], :teardown => []})
  klass.selectors = Hash.new.replace(self.selectors || {})
  @@all << klass
end

.new_loading_from_dir(env, path) ⇒ Object



85
86
87
88
89
90
91
92
93
# File 'lib/laborantin/core/scenario.rb', line 85

def self.new_loading_from_dir(env, path)
  obj = self.new(env)
  yml_path = File.join(path, 'config.yaml')
  tst, params = obj.load_config!(yml_path)
  obj.params = params
  obj.date = tst
  obj.rundir = path
  obj
end

.parameter(name, &blk) ⇒ Object

Defines a new ParameterRange instance for this Scenario. A block should be passed that will be evaluated in this ParameterRange instance’s context.

parameter(:size) do
  values 10, 20, 30
  describe "We expect a linear RTT increase with the size"
end

Raises:

  • (ArgumentError)


124
125
126
127
128
129
# File 'lib/laborantin/core/scenario.rb', line 124

def parameter(name, &blk)
  raise ArgumentError.new("Parameter #{name} already exists") if self.parameters[name]
  param = ParameterRange.new(name)
  param.instance_eval &blk
  self.parameters[name] = param
end

.produces(*args) ⇒ Object

Defines the products names. IMPORTANT: products are built in the provided order, and they must be valid instance methods name for a Scenario object (hence user defined).



134
135
136
# File 'lib/laborantin/core/scenario.rb', line 134

def produces(*args)
  self.products = [*args].flatten
end

.scan_env(env) ⇒ Object

Scans the env’s envdir (should be an Environment) for scenarii results. It will set their configuration (i.e. run date and parameters hash) according to the stored config.yaml in YAML format. Returns an array of such built scenarii.



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/laborantin/core/scenario.rb', line 69

def self.scan_env(env)
  list = []
  Dir.entries(env.rundir).each do |s|
    scklass = Laborantin::Scenario.all.find{|t| t.fs_name == s}
    if scklass
      Dir.entries(scklass.scenardir(env)).each do |r|
        if r =~ /\d+-\w+-\d+_\d+-\d+-\d+/
          scenar = scklass.new_loading_from_dir(env, File.join(scklass.scenardir(env), r))
          list << scenar
        end
      end
    end
  end
  list
end

.scenardir(env = nil) ⇒ Object

Returns the path where the results of the instances of this Scenario will be stored given that we are in the env Environment. If env is nil, will use ‘.’ as rootdir for the Scenario results.



146
147
148
149
# File 'lib/laborantin/core/scenario.rb', line 146

def scenardir(env=nil)
  envdir = env.rundir || '.'
  File.join(envdir, self.fs_name)
end

Instance Method Details

#analyze!Object

For each product define with Scenario.produces, and in its order, create a file with a canonic name in the scenario rundir. Call the instance method which has the product name. Appends each yielded line from this method.



224
225
226
227
228
229
230
231
232
233
234
# File 'lib/laborantin/core/scenario.rb', line 224

def analyze!
  self.class.products.each do |name|
    log "(#{name})"
    product_file(name.to_s, 'w') do |f|
      send(name) do |l|
        f.puts l
      end
    end
  end
  save_exports
end

#commandObject



295
296
297
# File 'lib/laborantin/core/scenario.rb', line 295

def command
  environment.command
end

#config_pathObject

The path to the config.yaml file that holds the scenario parameters.



258
259
260
# File 'lib/laborantin/core/scenario.rb', line 258

def config_path
  product_path('config.yaml', true)
end

#export_file(mode = 'r', &blk) ⇒ Object



283
284
285
# File 'lib/laborantin/core/scenario.rb', line 283

def export_file(mode='r', &blk)
  product_file('exports.yaml', mode, true, &blk) 
end

#export_pathObject



287
288
289
# File 'lib/laborantin/core/scenario.rb', line 287

def export_path
  product_path('exports.yaml', true)
end

#log(*args) ⇒ Object



291
292
293
# File 'lib/laborantin/core/scenario.rb', line 291

def log(*args)
  environment.log *args
end

#perform!Object

In the following order:

  • Calls the setup hooks

  • Logs some info

  • Creates the raw result file

  • Calls the run method (user defined)

  • … for each yielded line, store it into the raw result file

  • once completed (or on error) closes the raw result file

  • Logs some info

  • Calls the teardown hooks



207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/laborantin/core/scenario.rb', line 207

def perform!
  call_hooks :setup
  log "Starting measurement"
  raw_result_file('w') do |f|
    run do |l|
      f.puts l
    end
  end
  log "Measurement finished"
  call_hooks :teardown
  save_exports
end

#prepare!Object

In the following order:

  • Log some info in the environment

  • Creates the rundir to store the result and the config

  • Stores the configuration as well as the run date

BEWARE : currently does not ensure unicity of rundir, so wait one sec between several runs of same Scenario



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/laborantin/core/scenario.rb', line 182

def prepare!
  log "Loading prior results:" 
  load_prior_results
  log "Got #{scenarii.size} scenarios in #{environments.size} environments"
  log "Description:"
  log(self.class.description || 'no description', :info) 
  log "Parameters:"
  log self.params.inspect, :info
  log "Preparing directory #{rundir}"
  FileUtils.mkdir_p(rundir)  #TODO: ensure unicity
  environment.record_scenario_dir(rundir, true)
  log "Storing configuration in YAML format"
  @config = [date, params]
  save_config
end

#product_file(resultname, mode = 'r', brutname = false) ⇒ Object

Yields an open file for a given product, will make sure it is closed. mode is the mode in which the file is opened (you should leave it to ‘r’) see the doc for product_path to understand the role of brutname



251
252
253
254
255
# File 'lib/laborantin/core/scenario.rb', line 251

def product_file(resultname, mode='r', brutname=false)
  File.open(product_path(resultname, brutname), mode) do |f|
    yield f
  end
end

#product_path(resultname, brutname = false) ⇒ Object

Returns the absolute path to a product file (see File.join) If brutname is true, then resultname is appended to the rundir of the scenario. If brutname is false, then before being appended to the rundir of the scenario, the name is surronded by result.<resultname>.txt

The idea behind this is to avoid name collisions between simple users of Laborantin, and people developping extensions or modules.



243
244
245
246
# File 'lib/laborantin/core/scenario.rb', line 243

def product_path(resultname, brutname=false)
  resultname = "result.#{resultname}.txt" unless brutname
  File.join(rundir, resultname)
end

#raw_result_file(mode = 'r') ⇒ Object

Yield the opened raw result file, will close it afterwards. mode is the mode in which the file is opened (default to ‘r’) never open the file in another mode, unless you know what you’re doing, because this file most likely contains the value of your work, i.e., your data.



273
274
275
276
277
# File 'lib/laborantin/core/scenario.rb', line 273

def raw_result_file(mode='r')
  product_file('result.raw', mode, true) do |f| 
    yield f
  end
end

#raw_result_pathObject

The path to the “raw result”, i.e. the one built when you yield in the run method.



264
265
266
# File 'lib/laborantin/core/scenario.rb', line 264

def raw_result_path
  product_path('result.raw', true)
end

#runnerObject



299
300
301
# File 'lib/laborantin/core/scenario.rb', line 299

def runner
  environment.runner
end

#table(name, struct) ⇒ Object



279
280
281
# File 'lib/laborantin/core/scenario.rb', line 279

def table(name, struct)
  Table.new(name, struct, self.product_path(name))
end