module OmfEc::DSL

DSL methods to be used for OEDL scripts

Public Instance Methods

after(time, &block) click to toggle source

Use EM timer to execute after certain time

@example do something after 2 seconds

after 2.seconds { 'do something' }
# File lib/omf_ec/dsl.rb, line 59
def after(time, &block)
  OmfCommon.eventloop.after(time, &block)
end
alias_event(new_name, name) click to toggle source

Create an alias name of an event

# File 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
all_equal(array, value = nil, &block) click to toggle source

Check if all elements in array equal the value provided

# File 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
all_groups(&block) click to toggle source

Iterator for all defined groups

# File lib/omf_ec/dsl.rb, line 122
def all_groups(&block)
  OmfEc.experiment.each_group(&block)
end
Also aliased as: all_nodes!
all_groups?(&block) click to toggle source
# File lib/omf_ec/dsl.rb, line 126
def all_groups?(&block)
  OmfEc.experiment.all_groups?(&block)
end
all_nodes!(&block)
Alias for: all_groups
defPrototype(refName, name = nil, &block) click to toggle source

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

@param refName reference name for this property @param name optional, short/easy to remember name for this property

# File lib/omf_ec/dsl.rb, line 330
def defPrototype(refName, name = nil, &block)
  p = Prototype.create(refName)
  p.name = name
  block.call(p)
end
def_application(name, &block) click to toggle source
# File 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
def_event(name, opts = {}, &trigger) click to toggle source

Define an event

@param [Symbol] name of the event

@param [Hash] opts additional options @option opts [Fixnum] :every indicates non-reactive style event checking, i.e. trigger will be evaluated periodically with :every as interval

# File 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
def_graph(name = nil, &block) click to toggle source

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.

@param name short/easy to remember name for this graph

# File 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
def_group(name, *members, &block) click to toggle source

Define a group, create a pubsub topic for the group

@param [String] name name of the group

@example 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
# File 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
def_property(name, default_value, description = nil, type = nil) click to toggle source

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.

@param name of property @param default_value for this property @param description short text description of this property @param type of property

# File 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
def_query(query) click to toggle source

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.

@param query a SQL query

# File 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
deprecated_load_oedl(location) click to toggle source
# File 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
done()
Alias for: done!
done!() click to toggle source

Exit the experiment

@see OmfEc::Experiment.done

# File lib/omf_ec/dsl.rb, line 135
def done!
  OmfEc::Experiment.done
end
Also aliased as: done
ensure_property(name, default_value, description = nil, type = nil) click to toggle source

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

# File 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
every(time, &block) click to toggle source

Use EM periodic timer to execute after certain time

@example do something every 2 seconds

every 2.seconds { 'do something' }
# File lib/omf_ec/dsl.rb, line 68
def every(time, &block)
  OmfCommon.eventloop.every(time, &block)
end
get_resources() click to toggle source

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

# File 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
group(name, &block) click to toggle source

Get a group instance

@param [String] name name of the group

# File 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
load_oedl(location, opts = {}) click to toggle source

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.

@param uri URI for the OEDL script to load @param opts optional hash of key/values for extra Experiment Property to define

# File 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
ms(ms_name) click to toggle source

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)

@param ms_name the name of the existing measurement stream on which to run this query

# File 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
on_event(name, opts = { consume_event: true }, &callback) click to toggle source

Define an event callback

@param [Symbol] name of the event

@param [Hash] opts additional options @option opts [Boolean] :consume_event indicates if event callback will triggered ONLY once when condition met. Default true.

# File 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
one_equal(array, value) click to toggle source

Check if any elements in array equals the value provided

# File 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
prop()
Alias for: property
property() click to toggle source

Return the context for setting experiment wide properties

# File lib/omf_ec/dsl.rb, line 156
def property
  return OmfEc.experiment.property
end
Also aliased as: prop