Class: Laborantin::Scenario

Inherits:
Object
  • Object
show all
Extended by:
Metaprog::Describable, Metaprog::Hookable, Metaprog::MultiName
Includes:
Metaprog::Configurable, Metaprog::Datable
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::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::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.



160
161
162
163
164
165
166
# File 'lib/laborantin/core/scenario.rb', line 160

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.



91
92
93
# File 'lib/laborantin/core/scenario.rb', line 91

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



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

def products
  @products
end

Instance Attribute Details

#environmentObject

The environment in which we run this scenario.



149
150
151
# File 'lib/laborantin/core/scenario.rb', line 149

def environment
  @environment
end

#paramsObject

A hash of parameters for this run.



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

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).



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

def rundir
  @rundir
end

Class Method Details

.allObject

Returns all the known subklasses of Scenario.



132
133
134
# File 'lib/laborantin/core/scenario.rb', line 132

def all
  @@all
end

.inherited(klass) ⇒ Object

Prepares attributes’ default values whenever a subclass is created.



100
101
102
103
104
105
106
# File 'lib/laborantin/core/scenario.rb', line 100

def inherited(klass)
  klass.parameters = ParameterHash.new
  klass.description = ''
  klass.products = []
  klass.hooks = {:setup => [], :teardown => []}
  @@all << klass
end

.new_loading_from_dir(env, path) ⇒ Object



79
80
81
82
83
84
85
86
87
# File 'lib/laborantin/core/scenario.rb', line 79

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)


117
118
119
120
121
122
# File 'lib/laborantin/core/scenario.rb', line 117

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).



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

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.



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/laborantin/core/scenario.rb', line 63

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.



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

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.



211
212
213
214
215
216
217
218
219
220
# File 'lib/laborantin/core/scenario.rb', line 211

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
end

#config_pathObject

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



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

def config_path
  product_path('config.yaml', true)
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



195
196
197
198
199
200
201
202
203
204
205
# File 'lib/laborantin/core/scenario.rb', line 195

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
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



175
176
177
178
179
180
181
182
183
184
# File 'lib/laborantin/core/scenario.rb', line 175

def prepare!
  log(self.class.description, :info) unless self.class.description.empty?
  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



237
238
239
240
241
# File 'lib/laborantin/core/scenario.rb', line 237

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.



229
230
231
232
# File 'lib/laborantin/core/scenario.rb', line 229

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.



259
260
261
262
263
# File 'lib/laborantin/core/scenario.rb', line 259

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.



250
251
252
# File 'lib/laborantin/core/scenario.rb', line 250

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

#table(name, struct) ⇒ Object



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

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