Class: OmfEc::Experiment

Inherits:
Object
  • Object
show all
Includes:
MonitorMixin, Singleton
Defined in:
lib/omf_ec/experiment.rb

Overview

Experiment class to hold relevant state information

Defined Under Namespace

Classes: MetaData

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeExperiment

Returns a new instance of Experiment.



32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/omf_ec/experiment.rb', line 32

def initialize
  super
  @id = Time.now.utc.iso8601(3)
  @state ||= [] #TODO: we need to keep history of all the events and not ovewrite them
  @groups ||= []
  @nodes ||= []
  @events ||= []
  @app_definitions ||= Hash.new
  @sub_groups ||= []
  @cmdline_properties ||= Hash.new
  @show_graph = false
end

Instance Attribute Details

#app_definitionsObject

Returns the value of attribute app_definitions.



19
20
21
# File 'lib/omf_ec/experiment.rb', line 19

def app_definitions
  @app_definitions
end

#cmdline_propertiesObject

Returns the value of attribute cmdline_properties.



19
20
21
# File 'lib/omf_ec/experiment.rb', line 19

def cmdline_properties
  @cmdline_properties
end

#groupsObject (readonly)

Returns the value of attribute groups.



20
21
22
# File 'lib/omf_ec/experiment.rb', line 20

def groups
  @groups
end

#nameObject

Returns the value of attribute name.



19
20
21
# File 'lib/omf_ec/experiment.rb', line 19

def name
  @name
end

#nodesObject

Returns the value of attribute nodes.



19
20
21
# File 'lib/omf_ec/experiment.rb', line 19

def nodes
  @nodes
end

#oml_uriObject

Returns the value of attribute oml_uri.



19
20
21
# File 'lib/omf_ec/experiment.rb', line 19

def oml_uri
  @oml_uri
end

#propertyObject

Returns the value of attribute property.



19
20
21
# File 'lib/omf_ec/experiment.rb', line 19

def property
  @property
end

#show_graphObject

Returns the value of attribute show_graph.



19
20
21
# File 'lib/omf_ec/experiment.rb', line 19

def show_graph
  @show_graph
end

#stateObject (readonly)

Returns the value of attribute state.



20
21
22
# File 'lib/omf_ec/experiment.rb', line 20

def state
  @state
end

#sub_groupsObject (readonly)

Returns the value of attribute sub_groups.



20
21
22
# File 'lib/omf_ec/experiment.rb', line 20

def sub_groups
  @sub_groups
end

Class Method Details

.disconnectObject



227
228
229
230
231
232
233
234
# File 'lib/omf_ec/experiment.rb', line 227

def disconnect
  info "Disconnecting in 5 sec from experiment: #{OmfEc.experiment.id}"
  info "Run the EC again to reattach"
  OmfCommon.el.after(5) do
    OmfCommon.comm.disconnect
    OmfCommon.eventloop.stop
  end
end

.doneObject

Disconnect communicator, try to delete any XMPP affiliations



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/omf_ec/experiment.rb', line 203

def done
  info "Experiment: #{OmfEc.experiment.id} finished"
  info "Release applications and network interfaces"
  info "Exit in 15 seconds..."

  OmfCommon.el.after(10) do
    allGroups do |g|
      g.resources[type: 'application'].release unless g.app_contexts.empty?
      g.resources[type: 'net'].release unless g.net_ifs.find_all { |v| v.conf[:type] == 'net' }.empty?
      g.resources[type: 'wlan'].release unless g.net_ifs.find_all { |v| v.conf[:type] == 'wlan' }.empty?
      g.resources.membership = { leave: g.address }
    end

    OmfCommon.el.after(4) do
      info "OMF Experiment Controller #{OmfEc::VERSION} - Exit."
      OmfCommon.el.after(1) do
        OmfCommon.comm.disconnect
        OmfCommon.eventloop.stop
      end
    end
  end
  OmfEc.experiment.("state", "finished")
end

.leave_membershipsObject

Ask the resources which joined the groups I created to leave



255
256
257
258
259
# File 'lib/omf_ec/experiment.rb', line 255

def leave_memberships
  all_groups do |g|
    g.resources.membership = { leave: g.address }
  end
end

.startObject



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/omf_ec/experiment.rb', line 236

def start
  info "Experiment: #{OmfEc.experiment.id} starts"
  OmfEc.experiment.("state", "running")

  allGroups do |g|
    g.members.each do |key, value|
      OmfEc.subscribe_and_monitor(key) do |res|
        info "Configure '#{key}' to join '#{g.name}'"
        g.synchronize do
          g.members[key] = res.address
        end
        res.configure(membership: g.address, :res_index => OmfEc.experiment.nodes.index(key))
      end
    end
  end
end

Instance Method Details

#add_event(name, trigger) ⇒ Object



147
148
149
150
151
152
153
# File 'lib/omf_ec/experiment.rb', line 147

def add_event(name, trigger)
  self.synchronize do
    warn "Event '#{name}' has already been defined. Overwriting it now." if event(name)
    @events.delete_if { |e| e[:name] == name }
    @events << { name: name, trigger: trigger, aliases: [] }
  end
end

#add_group(group) ⇒ Object



124
125
126
127
128
129
# File 'lib/omf_ec/experiment.rb', line 124

def add_group(group)
  self.synchronize do
    raise ArgumentError, "Expect Group object, got #{group.inspect}" unless group.kind_of? OmfEc::Group
    @groups << group unless group(group.name)
  end
end

#add_or_update_resource_state(name, opts = {}) ⇒ Object Also known as: add_resource



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/omf_ec/experiment.rb', line 65

def add_or_update_resource_state(name, opts = {})
  self.synchronize do
    res = resource_state(name)
    if res
      opts.each do |key, value|
        if value.class == Array
          # Merge array values
          res[key] ||= []
          res[key] += value
          res[key].uniq!
        elsif value.kind_of? Hash
          # Merge hash values
          res[key] ||= {}
          res[key].merge(value)
        else
          # Overwrite otherwise
          res[key] = value
        end
      end
    else
      info "Newly discovered resource >> #{name}"
      res = Hashie::Mash.new({ address: name }).merge(opts)
      @state << res

      # Re send membership configure
      planned_groups = groups_by_res(res[:address])

      unless planned_groups.empty?
        OmfEc.subscribe_and_monitor(name) do |res|
          info "Config #{name} to join #{planned_groups.map(&:name).join(', ')}"
          res.configure(membership: planned_groups.map(&:address))
        end
      end
    end
  end
end

#add_property(name, value = nil, description = nil) ⇒ Object



49
50
51
52
53
# File 'lib/omf_ec/experiment.rb', line 49

def add_property(name, value = nil, description = nil)
  override_value = @cmdline_properties[name.to_s.to_sym]
  value = override_value unless override_value.nil?
  ExperimentProperty.create(name, value, description)
end

#add_sub_group(name) ⇒ Object



114
115
116
117
118
# File 'lib/omf_ec/experiment.rb', line 114

def add_sub_group(name)
  self.synchronize do
    @sub_groups << name unless @sub_groups.include?(name)
  end
end

#all_groups?(&block) ⇒ Boolean

Returns:

  • (Boolean)


139
140
141
# File 'lib/omf_ec/experiment.rb', line 139

def all_groups?(&block)
  !groups.empty? && groups.all? { |g| block ? block.call(g) : g }
end

#archive_oedl(script_name) ⇒ Object

Archive OEDL content to OML db



192
193
194
195
196
197
198
# File 'lib/omf_ec/experiment.rb', line 192

def archive_oedl(script_name)
  (
    script_name,
    Base64.encode64(Zlib::Deflate.deflate(File.read(script_name))),
    "oedl_content"
  )
end

#each_group(&block) ⇒ Object



131
132
133
134
135
136
137
# File 'lib/omf_ec/experiment.rb', line 131

def each_group(&block)
  if block
    groups.each { |g| block.call(g) }
  else
    groups
  end
end

#event(name) ⇒ Object



143
144
145
# File 'lib/omf_ec/experiment.rb', line 143

def event(name)
  @events.find { |v| v[:name] == name || v[:aliases].include?(name) }
end

#group(name) ⇒ Object



120
121
122
# File 'lib/omf_ec/experiment.rb', line 120

def group(name)
  groups.find { |v| v.name == name }
end

#groups_by_res(res_addr) ⇒ Object

Find all groups a given resource belongs to



106
107
108
# File 'lib/omf_ec/experiment.rb', line 106

def groups_by_res(res_addr)
  groups.find_all { |g| g.members.values.include?(res_addr) }
end

#idObject

Unique experiment id



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

def id
  @name || @id
end

#log_metadata(key, value, domain = 'sys') ⇒ Object



186
187
188
189
# File 'lib/omf_ec/experiment.rb', line 186

def (key, value, domain = 'sys')
  #MetaData.inject_metadata(key.to_s, value.to_s)
  MetaData.inject(domain.to_s, key.to_s, value.to_s)
end

#mp_table_namesObject



178
179
180
181
182
183
184
# File 'lib/omf_ec/experiment.rb', line 178

def mp_table_names
  {}.tap do |m_t_n|
    groups.map(&:app_contexts).flatten.map(&:mp_table_names).each do |v|
      m_t_n.merge!(v)
    end
  end
end

#process_eventsObject

Parsing user defined events, checking conditions against internal state, and execute callbacks if triggered



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/omf_ec/experiment.rb', line 161

def process_events
  self.synchronize do
    @events.find_all { |v| v[:callbacks] && !v[:callbacks].empty? }.each do |event|
      if event[:trigger].call(@state)
        @events.delete(event) if event[:consume_event]
        event_names = ([event[:name]] + event[:aliases]).join(', ')
        info "Event triggered: '#{event_names}'"

        # Last in first serve callbacks
        event[:callbacks].reverse.each do |callback|
          callback.call
        end
      end
    end
  end
end

#resource_by_hrn(hrn) ⇒ Object



61
62
63
# File 'lib/omf_ec/experiment.rb', line 61

def resource_by_hrn(hrn)
  @state.find { |v| v[:hrn].to_s == hrn.to_s }
end

#resource_state(address) ⇒ Object Also known as: resource



55
56
57
# File 'lib/omf_ec/experiment.rb', line 55

def resource_state(address)
  @state.find { |v| v[:address].to_s == address.to_s }
end

#sub_group(name) ⇒ Object



110
111
112
# File 'lib/omf_ec/experiment.rb', line 110

def sub_group(name)
  @sub_groups.find { |v| v == name }
end