Module: OmfEc::DSL

Defined in:
omf_ec/lib/omf_ec/dsl.rb

Overview

DSL methods to be used for OEDL scripts

Defined Under Namespace

Classes: OEDLArgumentException, OEDLCommandException, OEDLException, OEDLUnknownProperty

Instance Method Summary (collapse)

Instance Method Details

- (Object) after(time, &block)

Use EM timer to execute after certain time

Examples:

do something after 2 seconds


after 2.seconds { 'do something' }


59
60
61
# File 'omf_ec/lib/omf_ec/dsl.rb', line 59

def after(time, &block)
  OmfCommon.eventloop.after(time, &block)
end

- (Object) alias_event(new_name, name)

Create an alias name of an event



205
206
207
208
209
210
211
# File 'omf_ec/lib/omf_ec/dsl.rb', line 205

def alias_event(new_name, name)
  unless (event = OmfEc.experiment.event(name))
    raise RuntimeError, "Can not create alias for Event '#{name}' which is not defined"
  else
    event[:aliases] << new_name
  end
end

- (Object) all_equal(array, value = nil, &block)

Check if all elements in array equal the value provided



175
176
177
178
179
180
181
182
183
184
185
# File 'omf_ec/lib/omf_ec/dsl.rb', line 175

def all_equal(array, value = nil, &block)
  if array.empty?
    false
  else
    if value
      array.all? { |v| v.to_s == value.to_s }
    else
      array.all?(&block)
    end
  end
end

- (Object) all_groups(&block) Also known as: all_nodes!

Iterator for all defined groups



122
123
124
# File 'omf_ec/lib/omf_ec/dsl.rb', line 122

def all_groups(&block)
  OmfEc.experiment.each_group(&block)
end

- (Boolean) all_groups?(&block)

Returns:

  • (Boolean)


126
127
128
# File 'omf_ec/lib/omf_ec/dsl.rb', line 126

def all_groups?(&block)
  OmfEc.experiment.all_groups?(&block)
end

- (Object) def_application(name, &block)



72
73
74
75
76
# File 'omf_ec/lib/omf_ec/dsl.rb', line 72

def def_application(name, &block)
  app_def = OmfEc::AppDefinition.new(name)
  OmfEc.experiment.app_definitions[name] = app_def
  block.call(app_def) if block
end

- (Object) def_event(name, opts = {}, &trigger)

Define an event

Parameters:

  • name (Symbol)

    of the event

  • opts (Hash) (defaults to: {})

    additional options

Options Hash (opts):

  • :every (Fixnum)

    indicates non-reactive style event checking, i.e. trigger will be evaluated periodically with :every as interval

Raises:

  • (ArgumentError)


199
200
201
202
# File 'omf_ec/lib/omf_ec/dsl.rb', line 199

def def_event(name, opts = {}, &trigger)
  raise ArgumentError, 'Need a trigger callback' if trigger.nil?
  OmfEc.experiment.add_event(name, opts, trigger)
end

- (Object) def_graph(name = nil, &block)

Define a new graph widget showing experiment related measurements to be be used in a LabWiki column.

The block is called with an instance of the 'LabWiki::OMFBridge::GraphDescription' class. See that classes' documentation on the methods supported.

Parameters:

  • name (defaults to: nil)

    short/easy to remember name for this graph



236
237
238
239
240
241
242
# File 'omf_ec/lib/omf_ec/dsl.rb', line 236

def def_graph(name = nil, &block)
  if OmfEc.experiment.show_graph
    gd = OmfEc::Graph::GraphDescription.create(name)
    block.call(gd)
    gd._report
  end
end

- (Object) def_group(name, *members, &block)

Define a group, create a pubsub topic for the group

Examples:

add resource 'a', 'b' to group 'My_Pinger'


defGroup('My_Pinger', 'a', 'b') do |g|
  g.addApplication("ping") do |app|
    app.setProperty('target', 'mytestbed.net')
    app.setProperty('count', 3)
  end
end

# Or pass resources as an array

res_array = ['a', 'b']

defGroup('My_Pinger', res_array) do |g|
  g.addApplication("ping") do |app|
    app.setProperty('target', 'mytestbed.net')
    app.setProperty('count', 3)
  end
end

Parameters:

  • name (String)

    name of the group



102
103
104
105
106
107
108
# File 'omf_ec/lib/omf_ec/dsl.rb', line 102

def def_group(name, *members, &block)
  group = OmfEc::Group.new(name)
  OmfEc.experiment.add_group(group)
  group.add_resource(*members)

  block.call(group) if block
end

- (Object) def_property(name, default_value, description = nil, type = nil)

Define an experiment property which can be used to bind to application and other properties. Changing an experiment property should also change the bound properties, or trigger commands to change them.

Parameters:

  • name

    of property

  • default_value

    for this property

  • description (defaults to: nil)

    short text description of this property

  • type (defaults to: nil)

    of property



151
152
153
# File 'omf_ec/lib/omf_ec/dsl.rb', line 151

def def_property(name, default_value, description = nil, type = nil)
  OmfEc.experiment.add_property(name, default_value, description)
end

- (Object) def_query(query)

Define a query for measurements This requires that the EC was started with its JobService related parameters set (e.g. js_url or job_url) The EC contacts the JobService and: 1 - request the creation of a Measurement Point corresponding the query

parameter of this function.

2 - read the data generated by that query, and return it.

Parameters:

  • query

    a SQL query



346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
# File 'omf_ec/lib/omf_ec/dsl.rb', line 346

def def_query(query)
  raise "No valid URL to connect to the Job Service!" if OmfEc.experiment.job_url.nil?
  begin
    query = query.sql if query.kind_of? OmfEc::Graph::MSBuilder
    # Create a Measurement Point for that Job item
    unless OmfEc.experiment.job_mps.include?(query)
      mp = { name: "#{Time.now.to_i}", sql: query }
      u = URI.parse(OmfEc.experiment.job_url+'/measurement_points')
      req = Net::HTTP::Post.new(u.path, {'Content-Type' =>'application/json'})
      req.body = JSON.pretty_generate(mp)
      res = Net::HTTP.new(u.host, u.port).start {|http| http.request(req) }
      raise "Could not connect to the service providing measurements\n"+
            "Response #{res.code} #{res.message}:\n#{res.body}" unless res.kind_of? Net::HTTPSuccess
      mp = JSON.parse(res.body)
      raise "No valid URL to connect to the measurement point" if mp['href'].nil?
      OmfEc.experiment.job_mps[query] = mp['href']
    end
    # Read and format data from that Measurement Point
    u = URI.parse(OmfEc.experiment.job_mps[query]+'/data')
    res = Net::HTTP.get(u)
    raise "No valid data from the service providing measurements" if res.nil? || res.empty? || !(res.kind_of? String)
    resjson = JSON.parse(res)
    metrics = resjson['schema'].map { |e| e[0] }
    data = []
    resjson['data'].each do |a|
      row = Hashie::Mash.new
      a.each_index { |i| row[metrics[i].downcase.to_sym] = a[i] }
      data << row
    end
    return data
  rescue Exception => ex
    return nil if ex.kind_of? EOFError
    error "def_query - #{ex} (#{ex.class})"
    #error "def_query - #{ex.backtrace.join("\n\t")}"
    return nil
  end
end

- (Object) defPrototype(refName, name = nil, &block)

Define a new prototype. The supplied block is executed with the new Prototype instance as a single argument.

Parameters:

  • refName

    reference name for this property

  • name (defaults to: nil)

    optional, short/easy to remember name for this property



330
331
332
333
334
# File 'omf_ec/lib/omf_ec/dsl.rb', line 330

def defPrototype(refName, name = nil, &block)
  p = Prototype.create(refName)
  p.name = name
  block.call(p)
end

- (Object) deprecated_load_oedl(location)



304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'omf_ec/lib/omf_ec/dsl.rb', line 304

def deprecated_load_oedl(location)
  warn "Loading OEDL Library using DEPRECATED syntax. Please use proper URI syntax"
  begin
    require location
    info "Loaded built-in OEDL library '#{location}'"
  rescue LoadError
    begin
      file = Tempfile.new("oedl-#{Time.now.to_i}")
      open(location) { |io| file.write(io.read) }
      file.close
      OmfEc.experiment.archive_oedl(file.path)
      load(file.path)
      file.unlink
      info "Loaded external OEDL library '#{location}'"
    rescue Exception => e
      error "Fail loading external OEDL library '#{location}': #{e}"
    end
  rescue Exception => e
    error "Fail loading built-in OEDL library '#{location}': #{e}"
  end
end

- (Object) done! Also known as: done

Exit the experiment

See Also:



135
136
137
# File 'omf_ec/lib/omf_ec/dsl.rb', line 135

def done!
  OmfEc::Experiment.done
end

- (Object) ensure_property(name, default_value, description = nil, type = nil)

Check if a property exist, if not then define it Take the same parameter as def_property



163
164
165
166
167
168
169
# File 'omf_ec/lib/omf_ec/dsl.rb', line 163

def ensure_property(name, default_value, description = nil, type = nil)
  begin
    property[name]
  rescue
    def_property(name, default_value, description, type)
  end
end

- (Object) every(time, &block)

Use EM periodic timer to execute after certain time

Examples:

do something every 2 seconds


every 2.seconds { 'do something' }


68
69
70
# File 'omf_ec/lib/omf_ec/dsl.rb', line 68

def every(time, &block)
  OmfCommon.eventloop.every(time, &block)
end

- (Object) get_resources

Query a Slice Service to get back the list of resources which were previously provisioned for the slice within which this EC is operating. Return either an empty array or an array of Hash (actually Hashie::Mash) Require that the EC was provided an URL to a slice service (option –slice-service) and the name of the slice (option –slice).



410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
# File 'omf_ec/lib/omf_ec/dsl.rb', line 410

def get_resources
  begin
    #slice_url = "http://bleeding.mytestbed.net:8006/slices/"
    raise "No slice service URL, use '--slice-service' option" if OmfEc.experiment.ss_url.nil?
    raise "No slice name, use '--slice' option" if OmfEc.experiment.sliceID.nil?
    u = URI.parse(OmfEc.experiment.ss_url+'/slices/'+OmfEc.experiment.sliceID+'/resources')
    res = Net::HTTP.get(u)
    raise "Could not retrieve a valid list of resources from '#{u}'" if res.nil? || res.empty? || !(res.kind_of? String)
    Hashie::Mash.new(JSON.parse(res)).values
  rescue Exception => ex
    error "get_resources - #{ex} (#{ex.class}) - URI: '#{u}'"
    #error "get_resources - #{ex.backtrace.join("\n\t")}"
    return []
  end
end

- (Object) group(name, &block)

Get a group instance

Parameters:

  • name (String)

    name of the group

Raises:

  • (RuntimeError)


113
114
115
116
117
118
119
# File 'omf_ec/lib/omf_ec/dsl.rb', line 113

def group(name, &block)
  group = OmfEc.experiment.group(name)
  raise RuntimeError, "Group #{name} not found" if group.nil?

  block.call(group) if block
  group
end

- (Object) load_oedl(location, opts = {})

Load an additional OEDL script referenced by a URI

The supported URI schemes are:

  • file:///foo/bar.rb , which loads the file located at '/foo/bar.rb' on the local filesystem

  • system:///foo/bar.rb , which loads the file located at 'foo/bar.rb' in the default Ruby path of this EC

  • foo.com/bar.rb , which loads the file located at the URL 'foo.com/bar.rb'

If an optional has of key/value is provided, then define an OMF Experiment Property for each keys and assigne them the values.

Parameters:

  • uri

    URI for the OEDL script to load

  • opts (defaults to: {})

    optional hash of key/values for extra Experiment Property to define



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'omf_ec/lib/omf_ec/dsl.rb', line 257

def load_oedl(location, opts = {})
  begin
    u = URI(location.downcase)
  rescue Exception => e
    warn "Unsupported OEDL library location '#{location}'"
    return
  end

  # Define the additional properties from opts
  opts.each { |k,v| def_property(k, v,) }

  # Keep the old syntax around for a while, warn users to use the new URI syntax
  # TODO: remove this in a couple of EC versions
  if u.scheme.nil? || u.scheme.empty?
    deprecated_load_oedl(location)
    return
  end

  # Find out which type of location this is and deal with it accordingly
  case u.scheme.downcase.to_sym
  when :system
    begin
      u.path[0]='' # get rid of first '/'
      require u.path
      info "Loaded built-in OEDL library '#{location}'"
    rescue Exception => e
      error "Fail loading built-in OEDL library '#{location}': #{e}"
    end
  when :file, :http, :https
    begin
      file = Tempfile.new("oedl-#{Time.now.to_i}")
      # see: http://stackoverflow.com/questions/7578898
      open(u.to_s.sub(%r{^file:}, '')) { |io| file.write(io.read) }
      file.close
      OmfEc.experiment.archive_oedl(file.path)
      load(file.path)
      file.unlink
      info "Loaded external OEDL library '#{location}'"
    rescue Exception => e
      error "Fail loading external OEDL library '#{location}': #{e}"
    end
  else
    warn "Unsupported scheme for OEDL library location '#{location}'"
    return
  end
end

- (Object) ms(ms_name)

Define a query for measurements, using the Sequel Syntax Refer to the def_query method above. In this variant, the query is defined using the Sequel Syntax against a Measurement Stream which must have been previously defined in the OEDl experiment (e.g. app.measure('foo') in a addApplication block)

this query

Parameters:

  • ms_name

    the name of the existing measurement stream on which to run



393
394
395
396
397
398
399
400
401
402
# File 'omf_ec/lib/omf_ec/dsl.rb', line 393

def ms(ms_name)
  db = Sequel.postgres
  db.instance_variable_set('@server_version', 90105)
  if (table_name = OmfEc.experiment.mp_table_names[ms_name])
    msb = OmfEc::Graph::MSBuilder.new(db[table_name.to_sym])
  else
    warn "Measurement point '#{ms_name}' NOT defined"
  end
  msb
end

- (Object) on_event(name, opts = { consume_event: true }, &callback)

Define an event callback

Parameters:

  • name (Symbol)

    of the event

  • opts (Hash) (defaults to: { consume_event: true })

    additional options

Options Hash (opts):

  • :consume_event (Boolean)

    indicates if event callback will triggered ONLY once when condition met. Default true.



219
220
221
222
223
224
225
226
227
# File 'omf_ec/lib/omf_ec/dsl.rb', line 219

def on_event(name, opts = { consume_event: true }, &callback)
  unless (event = OmfEc.experiment.event(name))
    raise RuntimeError, "Event '#{name}' not defined"
  else
    event[:callbacks] ||= []
    event[:callbacks] << callback
    event[:consume_event] = opts[:consume_event]
  end
end

- (Object) one_equal(array, value)

Check if any elements in array equals the value provided



189
190
191
# File 'omf_ec/lib/omf_ec/dsl.rb', line 189

def one_equal(array, value)
  !array.any? ? false : array.any? { |v| v.to_s == value.to_s }
end

- (Object) property Also known as: prop

Return the context for setting experiment wide properties



156
157
158
# File 'omf_ec/lib/omf_ec/dsl.rb', line 156

def property
  return OmfEc.experiment.property
end