omf_ec 6.0.0.pre.4 → 6.0.0.pre.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/omf_ec +75 -57
- data/example/engine_oedl.rb +20 -20
- data/example/engine_test.rb +51 -73
- data/example/test_exp/test00.rb +25 -5
- data/example/test_exp/test01.rb +4 -5
- data/example/test_exp/test02.rb +2 -5
- data/example/test_exp/test03.rb +15 -28
- data/lib/omf_ec/app_definition.rb +3 -3
- data/lib/omf_ec/backward/default_events.rb +13 -8
- data/lib/omf_ec/backward/dsl.rb +4 -36
- data/lib/omf_ec/backward/exp/testbed.rb +7 -7
- data/lib/omf_ec/backward/group.rb +23 -9
- data/lib/omf_ec/context/app_context.rb +40 -9
- data/lib/omf_ec/context/group_context.rb +18 -47
- data/lib/omf_ec/context/net_context.rb +3 -3
- data/lib/omf_ec/dsl.rb +55 -40
- data/lib/omf_ec/experiment.rb +112 -20
- data/lib/omf_ec/experiment_property.rb +222 -0
- data/lib/omf_ec/group.rb +54 -104
- data/lib/omf_ec/version.rb +1 -1
- data/lib/omf_ec.rb +59 -7
- data/omf_ec.gemspec +4 -2
- data/test/omf_ec/dsl_spec.rb +53 -0
- data/test/omf_ec/experiment_property_spec.rb +81 -0
- data/test/omf_ec/experiment_spec.rb +41 -0
- data/test/omf_ec/group_spec.rb +45 -2
- metadata +35 -19
- data/example/test_exp/test04.rb +0 -105
- data/example/test_exp/test05.rb +0 -87
data/lib/omf_ec/backward/dsl.rb
CHANGED
@@ -22,51 +22,19 @@ module OmfEc
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
def defApplication(uri,name,&block)
|
25
|
+
def defApplication(uri, name=nil ,&block)
|
26
26
|
# URI parameter was used by previous OMF5 EC, for now we
|
27
27
|
# do nothing with it in OMF6
|
28
|
+
name = uri if name.nil?
|
28
29
|
def_application(name,&block)
|
29
30
|
end
|
30
31
|
|
31
32
|
def defGroup(name, *members, &block)
|
32
33
|
group = OmfEc::Group.new(name)
|
33
|
-
OmfEc.
|
34
|
-
|
35
|
-
members.each do |m|
|
36
|
-
m_group = OmfEc.exp.groups.find { |v| v.name == m }
|
37
|
-
if m_group
|
38
|
-
group.members += m_group.members
|
39
|
-
else
|
40
|
-
group.members << m
|
41
|
-
end
|
42
|
-
end
|
34
|
+
OmfEc.experiment.add_group(group)
|
35
|
+
group.add_resource(*members)
|
43
36
|
|
44
37
|
block.call(group) if block
|
45
|
-
|
46
|
-
OmfEc.comm.subscribe(group.id, create_if_non_existent: true) do |m|
|
47
|
-
unless m.error?
|
48
|
-
members.each do |m|
|
49
|
-
group.add_resource(m)
|
50
|
-
end
|
51
|
-
|
52
|
-
rg = OmfEc.comm.get_topic(group.id)
|
53
|
-
|
54
|
-
rg.on_message lambda {|m| m.operation == :inform && m.read_content('inform_type') == 'FAILED' && m.context_id.nil? } do |i|
|
55
|
-
warn "RC reports failure: '#{i.read_content("reason")}'"
|
56
|
-
end
|
57
|
-
|
58
|
-
rg.on_message lambda {|m| m.operation == :inform && m.read_content('inform_type') == 'STATUS' && m.context_id.nil? } do |i|
|
59
|
-
r = OmfEc.exp.state.find { |v| v[:uid] == i.read_property(:uid) }
|
60
|
-
unless r.nil?
|
61
|
-
i.each_property do |p|
|
62
|
-
key = p.attr('key').to_sym
|
63
|
-
r[key] = i.read_property(key)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
Experiment.instance.process_events
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
38
|
end
|
71
39
|
|
72
40
|
# Wait for some time before issuing more commands
|
@@ -1,4 +1,4 @@
|
|
1
|
-
comm =
|
1
|
+
comm = OmfCommon.comm
|
2
2
|
|
3
3
|
testbed_topic = comm.get_topic('testbed')
|
4
4
|
|
@@ -6,11 +6,11 @@ msgs = {
|
|
6
6
|
create: comm.create_message([type: 'application']),
|
7
7
|
req_platform: comm.request_message([:platform]),
|
8
8
|
conf_path: comm.configure_message([binary_path: @cmd]),
|
9
|
-
run_application: comm.configure_message([state: :
|
9
|
+
run_application: comm.configure_message([state: :running])
|
10
10
|
}
|
11
11
|
|
12
|
-
msgs[:create].
|
13
|
-
app_topic = comm.get_topic(message.
|
12
|
+
msgs[:create].on_inform_creation_ok do |message|
|
13
|
+
app_topic = comm.get_topic(message.res_id)
|
14
14
|
app_topic.subscribe do
|
15
15
|
msgs[:req_platform].publish app_topic.id
|
16
16
|
sleep 1
|
@@ -21,16 +21,16 @@ msgs[:create].on_inform_created do |message|
|
|
21
21
|
|
22
22
|
app_topic.on_message do |m|
|
23
23
|
if m.operation == :inform
|
24
|
-
case m.read_content("
|
24
|
+
case m.read_content("itype")
|
25
25
|
when 'STATUS'
|
26
26
|
if m.read_property("status_type") == 'APP_EVENT'
|
27
27
|
after (2) { comm.disconnect } if m.read_property("event") =~ /DONE.(OK|ERROR)/
|
28
28
|
puts m.read_property("msg")
|
29
29
|
end
|
30
30
|
when 'ERROR'
|
31
|
-
logger.error m.read_content('reason') if m.read_content("
|
31
|
+
logger.error m.read_content('reason') if m.read_content("itype") == 'ERROR'
|
32
32
|
when 'WARN'
|
33
|
-
logger.warn m.read_content('reason') if m.read_content("
|
33
|
+
logger.warn m.read_content('reason') if m.read_content("itype") == 'WARN'
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
@@ -5,6 +5,10 @@ module OmfEc
|
|
5
5
|
module Group
|
6
6
|
# The following are ODEL 5 methods
|
7
7
|
|
8
|
+
def resource_group(type)
|
9
|
+
"#{self.id}_#{type.to_s}"
|
10
|
+
end
|
11
|
+
|
8
12
|
# Create an application for the group and start it
|
9
13
|
#
|
10
14
|
def exec(name)
|
@@ -14,25 +18,35 @@ module OmfEc
|
|
14
18
|
|
15
19
|
e_name = "#{self.name}_application_#{name}_created_#{e_uid}"
|
16
20
|
|
21
|
+
resource_group_name = "#{self.id}_application"
|
22
|
+
|
17
23
|
def_event e_name do |state|
|
18
|
-
state.find_all { |v| v[:hrn] == name }.size >= self.members.uniq.size
|
24
|
+
state.find_all { |v| v[:hrn] == name && v[:membership] && v[:membership].include?(resource_group_name)}.size >= self.members.uniq.size
|
19
25
|
end
|
20
26
|
|
21
27
|
on_event e_name do
|
22
|
-
resources[type: 'application', name: name].state = :
|
28
|
+
resources[type: 'application', name: name].state = :running
|
23
29
|
end
|
24
30
|
end
|
25
31
|
|
26
32
|
def startApplications
|
27
|
-
|
33
|
+
if self.app_contexts.empty?
|
34
|
+
warn "No applications defined in group #{self.name}. Nothing to start"
|
35
|
+
else
|
36
|
+
resources[type: 'application'].state = :running
|
37
|
+
end
|
28
38
|
end
|
29
39
|
|
30
40
|
def stopApplications
|
31
|
-
|
41
|
+
if self.app_contexts.empty?
|
42
|
+
warn "No applications defined in group #{self.name}. Nothing to stop"
|
43
|
+
else
|
44
|
+
resources[type: 'application'].state = :stopped
|
45
|
+
end
|
32
46
|
end
|
33
47
|
|
34
48
|
def addApplication(name, &block)
|
35
|
-
app_cxt = OmfEc::Context::AppContext.new(name)
|
49
|
+
app_cxt = OmfEc::Context::AppContext.new(name,self)
|
36
50
|
block.call(app_cxt) if block
|
37
51
|
self.app_contexts << app_cxt
|
38
52
|
end
|
@@ -49,16 +63,16 @@ module OmfEc
|
|
49
63
|
|
50
64
|
def method_missing(name, *args, &block)
|
51
65
|
if name =~ /w(\d+)/
|
52
|
-
net = self.net_ifs.find { |v| v.conf[:
|
66
|
+
net = self.net_ifs.find { |v| v.conf[:if_name] == "wlan#{$1}" }
|
53
67
|
if net.nil?
|
54
|
-
net = OmfEc::Context::NetContext.new(:type => 'wlan', :
|
68
|
+
net = OmfEc::Context::NetContext.new(:type => 'wlan', :if_name => "wlan#{$1}", :index => $1)
|
55
69
|
self.net_ifs << net
|
56
70
|
end
|
57
71
|
net
|
58
72
|
elsif name =~ /e(\d+)/
|
59
|
-
net = self.net_ifs.find { |v| v.conf[:
|
73
|
+
net = self.net_ifs.find { |v| v.conf[:if_name] == "eth#{$1}" }
|
60
74
|
if net.nil?
|
61
|
-
net = OmfEc::Context::NetContext.new(:type => 'net', :
|
75
|
+
net = OmfEc::Context::NetContext.new(:type => 'net', :if_name => "eth#{$1}", :index => $1)
|
62
76
|
self.net_ifs << net
|
63
77
|
end
|
64
78
|
net
|
@@ -8,44 +8,75 @@ module OmfEc::Context
|
|
8
8
|
# values for each. Thus we need to distinguish these different context
|
9
9
|
@@context_count = Hash.new
|
10
10
|
|
11
|
-
def initialize(name)
|
12
|
-
if OmfEc.
|
13
|
-
self.app_def = OmfEc.
|
11
|
+
def initialize(name, group)
|
12
|
+
if OmfEc.experiment.app_definitions.key?(name)
|
13
|
+
self.app_def = OmfEc.experiment.app_definitions[name]
|
14
14
|
self.param_values = Hash.new
|
15
15
|
self.oml_collections = Array.new
|
16
16
|
@@context_count[name] = 0 unless @@context_count.key?(name)
|
17
17
|
id = @@context_count[name]
|
18
18
|
@@context_count[name] += 1
|
19
19
|
self.name = "#{name}_cxt_#{id}"
|
20
|
+
@group = group
|
20
21
|
self
|
21
22
|
else
|
22
23
|
raise RuntimeError, "Cannot create context for unknwon application '#{name}'"
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
26
|
-
def setProperty(key,
|
27
|
-
|
27
|
+
def setProperty(key, property_value)
|
28
|
+
app_def_param = app_def.properties.parameters
|
29
|
+
raise OEDLUnknownProperty.new(key, "Unknown parameter '#{key}' for application "+
|
30
|
+
"definition '#{app_def.name}'") if app_def_param.nil? || !app_def_param.key?(key)
|
31
|
+
if property_value.kind_of?(ExperimentProperty)
|
32
|
+
@param_values[key] = property_value.value
|
33
|
+
# if this app parameter has its dynamic attribute set to true, then
|
34
|
+
# we register a callback block to the ExperimentProperty, which will
|
35
|
+
# be called each time the property changes its value
|
36
|
+
if app_def_param[key][:dynamic]
|
37
|
+
info "Binding dynamic parameter '#{key}' to the property '#{property_value.name}'"
|
38
|
+
property_value.on_change do |new_value|
|
39
|
+
info "Updating dynamic app parameter '#{key}' with value: '#{new_value}'"
|
40
|
+
OmfEc.subscribe_and_monitor(@group.resource_group(:application)) do |topic|
|
41
|
+
p = properties
|
42
|
+
p[:parameters][key.to_sym][:value] = property_value.value
|
43
|
+
topic.configure(p, { guard: { hrn: @name} } )
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
else
|
48
|
+
@param_values[key] = property_value
|
49
|
+
end
|
28
50
|
self
|
29
51
|
end
|
30
52
|
|
31
53
|
# For now this follows v5.4 syntax...
|
32
54
|
# We have not yet finalised an OML syntax inside OEDL for v6
|
33
55
|
def measure(mp,filters)
|
34
|
-
collection = {:url => OmfEc.
|
56
|
+
collection = {:url => OmfEc.experiment.oml_uri, :streams => [] }
|
35
57
|
stream = { :mp => mp , :filters => [] }.merge(filters)
|
36
58
|
collection[:streams] << stream
|
37
59
|
@oml_collections << collection
|
38
60
|
end
|
39
61
|
|
40
62
|
def properties
|
41
|
-
# deep copy the properties from the
|
63
|
+
# deep copy the properties from the app definition
|
42
64
|
original = Marshal.load(Marshal.dump(app_def.properties))
|
65
|
+
# now build the properties for this context
|
66
|
+
# - use the properties from app definition as the base
|
67
|
+
# - if this context's param_values has a property which also exists in
|
68
|
+
# the app def and if that property has an assigned value, then
|
69
|
+
# use that value for the properties of this context
|
43
70
|
p = original.merge({:type => 'application'})
|
44
|
-
@param_values.each
|
71
|
+
@param_values.each do |k,v|
|
72
|
+
if p[:parameters].key?(k)
|
73
|
+
p[:parameters][k][:value] = v.kind_of?(ExperimentProperty) ? v.value : v
|
74
|
+
end
|
75
|
+
end
|
45
76
|
if @oml_collections.size > 0
|
46
77
|
p[:use_oml] = true
|
47
78
|
p[:oml][:id] = @name
|
48
|
-
p[:oml][:experiment] = OmfEc.
|
79
|
+
p[:oml][:experiment] = OmfEc.experiment.id
|
49
80
|
p[:oml][:collection] = @oml_collections
|
50
81
|
end
|
51
82
|
p
|
@@ -28,60 +28,31 @@ module OmfEc::Context
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def send_message(name, value = nil, &block)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
op_name = self.operation == :release ? "request_message" : "#{self.operation}_message"
|
36
|
-
|
37
|
-
o_m = OmfEc.comm.__send__(op_name, send_to) do |m|
|
38
|
-
m.element(:guard) do |g|
|
39
|
-
self.guard.each_pair do |k, v|
|
40
|
-
g.property(k, v)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
unless self.operation == :release
|
45
|
-
m.property(name, value)
|
46
|
-
end
|
47
|
-
|
48
|
-
unless self.operation == :configure
|
49
|
-
m.property(:uid)
|
50
|
-
m.property(:hrn)
|
51
|
-
end
|
31
|
+
if self.guard[:type]
|
32
|
+
topic = self.group.resource_topic(self.guard[:type])
|
33
|
+
else
|
34
|
+
topic = self.group.topic
|
52
35
|
end
|
53
36
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
release_m.on_inform_released do |m|
|
65
|
-
info "#{m.resource_id} released"
|
66
|
-
r = OmfEc.exp.state.find { |v| v[:uid] == m.resource_id }
|
67
|
-
r[:released] = true unless r.nil?
|
68
|
-
block.call if block
|
69
|
-
Experiment.instance.process_events
|
37
|
+
case self.operation
|
38
|
+
when :configure
|
39
|
+
topic.configure({ name => value }, { guard: self.guard })
|
40
|
+
when :request
|
41
|
+
topic.request([:uid, :hrn, name], { guard: self.guard })
|
42
|
+
when :release
|
43
|
+
topics_to_release = OmfEc.experiment.state.find_all do |res_state|
|
44
|
+
all_equal(self.guard.keys) do |k|
|
45
|
+
res_state[k] == self.guard[k]
|
70
46
|
end
|
71
47
|
end
|
72
48
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
49
|
+
topics_to_release.each do |res_state|
|
50
|
+
OmfEc.subscribe_and_monitor(res_state.uid) do |child_topic|
|
51
|
+
OmfEc.subscribe_and_monitor(self.group.id) do |group_topic|
|
52
|
+
group_topic.release(child_topic) if child_topic
|
53
|
+
end
|
78
54
|
end
|
79
55
|
end
|
80
|
-
Experiment.instance.process_events
|
81
|
-
end
|
82
|
-
|
83
|
-
o_m.on_inform_failed do |i|
|
84
|
-
warn "RC reports failure: '#{i.read_content("reason")}'"
|
85
56
|
end
|
86
57
|
end
|
87
58
|
end
|
@@ -27,7 +27,7 @@ module OmfEc::Context
|
|
27
27
|
else
|
28
28
|
net_prop
|
29
29
|
end
|
30
|
-
self.conf.merge!(net_prop => args[0])
|
30
|
+
self.conf.merge!(net_prop => args[0].to_s)
|
31
31
|
else
|
32
32
|
super
|
33
33
|
end
|
@@ -35,10 +35,10 @@ module OmfEc::Context
|
|
35
35
|
|
36
36
|
def map_channel_freq
|
37
37
|
if self.conf[:channel] && self.conf[:frequency].nil?
|
38
|
-
self.conf[:frequency] = FREQUENCY[self.conf[:channel].to_i]
|
38
|
+
self.conf[:frequency] = FREQUENCY[self.conf[:channel].to_s.to_i]
|
39
39
|
end
|
40
40
|
if self.conf[:channel].nil? && self.conf[:frequency]
|
41
|
-
self.conf[:channel] = FREQUENCY.keys.find { |k| FREQUENCY[k] == self.conf[:frequency].
|
41
|
+
self.conf[:channel] = FREQUENCY.keys.find { |k| FREQUENCY[k] == self.conf[:frequency].to_sto_i }
|
42
42
|
end
|
43
43
|
self
|
44
44
|
end
|
data/lib/omf_ec/dsl.rb
CHANGED
@@ -4,13 +4,49 @@ require 'eventmachine'
|
|
4
4
|
module OmfEc
|
5
5
|
# DSL methods to be used for OEDL scripts
|
6
6
|
module DSL
|
7
|
+
|
8
|
+
# Define OEDL-specific exceptions. These are the Exceptions that might be
|
9
|
+
# raised when the OMF EC is processing an OEDL experiment scripts
|
10
|
+
#
|
11
|
+
# The base exception is OEDLException
|
12
|
+
class OEDLException < Exception; end
|
13
|
+
|
14
|
+
class OEDLArgumentException < OEDLException
|
15
|
+
attr_reader :cmd, :arg
|
16
|
+
def initialize(cmd, arg, msg = nil)
|
17
|
+
@cmd = cmd
|
18
|
+
@arg = arg
|
19
|
+
msg ||= "Illegal value for argument '#{arg}' in command '#{cmd}'"
|
20
|
+
super(msg)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class OEDLCommandException < OEDLException
|
25
|
+
attr_reader :cmd
|
26
|
+
def initialize(cmd, msg = nil)
|
27
|
+
@cmd = cmd
|
28
|
+
msg ||= "Illegal command '#{cmd}' unsupported by OEDL"
|
29
|
+
super(msg)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class OEDLUnknownProperty < OEDLException
|
34
|
+
attr_reader :cmd
|
35
|
+
def initialize(name, msg = nil)
|
36
|
+
@name = name
|
37
|
+
msg ||= "Unknown property '#{name}', not previously defined in your OEDL experiment"
|
38
|
+
super(msg)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
7
43
|
# Use EM timer to execute after certain time
|
8
44
|
#
|
9
45
|
# @example do something after 2 seconds
|
10
46
|
#
|
11
47
|
# after 2.seconds { 'do something' }
|
12
48
|
def after(time, &block)
|
13
|
-
|
49
|
+
OmfCommon.eventloop.after(time, &block)
|
14
50
|
end
|
15
51
|
|
16
52
|
# Use EM periodic timer to execute after certain time
|
@@ -19,12 +55,12 @@ module OmfEc
|
|
19
55
|
#
|
20
56
|
# every 2.seconds { 'do something' }
|
21
57
|
def every(time, &block)
|
22
|
-
|
58
|
+
OmfCommon.eventloop.every(time, &block)
|
23
59
|
end
|
24
60
|
|
25
61
|
def def_application(name,&block)
|
26
62
|
app_def = OmfEc::AppDefinition.new(name)
|
27
|
-
OmfEc.
|
63
|
+
OmfEc.experiment.app_definitions[name] = app_def
|
28
64
|
block.call(app_def) if block
|
29
65
|
end
|
30
66
|
|
@@ -39,47 +75,29 @@ module OmfEc
|
|
39
75
|
#
|
40
76
|
# @see OmfEc::Backward::DSL#defGroup
|
41
77
|
def def_group(name, &block)
|
42
|
-
group = OmfEc::Group.new(name)
|
43
|
-
OmfEc.
|
44
|
-
|
45
|
-
OmfEc.comm.subscribe(group.id, create_if_non_existent: true) do |m|
|
46
|
-
unless m.error?
|
47
|
-
block.call group if block
|
48
|
-
|
49
|
-
rg = OmfEc.comm.get_topic(group.id)
|
50
|
-
|
51
|
-
rg.on_message lambda {|m| m.operation == :inform && m.read_content('inform_type') == 'FAILED' && m.context_id.nil? } do |i|
|
52
|
-
warn "RC reports failure: '#{i.read_content("reason")}'"
|
53
|
-
end
|
54
|
-
|
55
|
-
rg.on_message lambda {|m| m.operation == :inform && m.read_content('inform_type') == 'STATUS' && m.context_id.nil? } do |i|
|
56
|
-
r = OmfEc.exp.state.find { |v| v[:uid] == i.read_property(:uid) }
|
57
|
-
unless r.nil?
|
58
|
-
i.each_property do |p|
|
59
|
-
key = p.attr('key').to_sym
|
60
|
-
r[key] = i.read_property(key)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
Experiment.instance.process_events
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
78
|
+
group = OmfEc::Group.new(name, &block)
|
79
|
+
OmfEc.experiment.add_group(group)
|
80
|
+
group
|
67
81
|
end
|
68
82
|
|
69
83
|
# Get a group instance
|
70
84
|
#
|
71
85
|
# @param [String] name name of the group
|
72
86
|
def group(name, &block)
|
73
|
-
group = OmfEc.
|
87
|
+
group = OmfEc.experiment.group(name)
|
88
|
+
raise RuntimeError, "Group #{name} not found" if group.nil?
|
89
|
+
|
74
90
|
block.call(group) if block
|
75
91
|
group
|
76
92
|
end
|
77
93
|
|
78
94
|
# Iterator for all defined groups
|
79
95
|
def all_groups(&block)
|
80
|
-
OmfEc.
|
81
|
-
|
82
|
-
|
96
|
+
OmfEc.experiment.each_group(&block)
|
97
|
+
end
|
98
|
+
|
99
|
+
def all_groups?(&block)
|
100
|
+
OmfEc.experiment.all_groups?(&block)
|
83
101
|
end
|
84
102
|
|
85
103
|
alias_method :all_nodes!, :all_groups
|
@@ -103,12 +121,12 @@ module OmfEc
|
|
103
121
|
# @param description short text description of this property
|
104
122
|
#
|
105
123
|
def def_property(name, default_value, description = nil)
|
106
|
-
OmfEc.
|
124
|
+
OmfEc.experiment.add_property(name, default_value, description)
|
107
125
|
end
|
108
126
|
|
109
127
|
# Return the context for setting experiment wide properties
|
110
128
|
def property
|
111
|
-
OmfEc.
|
129
|
+
return OmfEc.experiment.property
|
112
130
|
end
|
113
131
|
|
114
132
|
alias_method :prop, :property
|
@@ -135,16 +153,13 @@ module OmfEc
|
|
135
153
|
|
136
154
|
# Define an event
|
137
155
|
def def_event(name, &trigger)
|
138
|
-
|
139
|
-
|
140
|
-
else
|
141
|
-
OmfEc.exp.events << { name: name, trigger: trigger }
|
142
|
-
end
|
156
|
+
raise ArgumentError, 'Need a trigger callback' if trigger.nil?
|
157
|
+
OmfEc.experiment.add_event(name, trigger)
|
143
158
|
end
|
144
159
|
|
145
160
|
# Define an event callback
|
146
161
|
def on_event(name, consume_event = true, &callback)
|
147
|
-
event = OmfEc.
|
162
|
+
event = OmfEc.experiment.event(name)
|
148
163
|
if event.nil?
|
149
164
|
raise RuntimeError, "Event '#{name}' not defined"
|
150
165
|
else
|
data/lib/omf_ec/experiment.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'hashie'
|
2
2
|
require 'singleton'
|
3
|
+
require 'monitor'
|
3
4
|
|
4
5
|
module OmfEc
|
5
6
|
# Experiment class to hold relevant state information
|
@@ -7,17 +8,112 @@ module OmfEc
|
|
7
8
|
class Experiment
|
8
9
|
include Singleton
|
9
10
|
|
10
|
-
|
11
|
+
include MonitorMixin
|
12
|
+
|
13
|
+
attr_accessor :name, :oml_uri, :app_definitions, :property, :cmdline_properties
|
14
|
+
attr_reader :groups, :sub_groups, :state
|
11
15
|
|
12
16
|
def initialize
|
13
17
|
@id = Time.now.utc.iso8601
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
@state ||= [] #TODO: we need to keep history of all the events and not ovewrite them
|
19
|
+
@groups ||= []
|
20
|
+
@events ||= []
|
21
|
+
@app_definitions ||= Hash.new
|
22
|
+
@sub_groups ||= []
|
23
|
+
@cmdline_properties ||= Hash.new
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
def property
|
28
|
+
return ExperimentProperty
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_property(name, value = nil, description = nil)
|
32
|
+
override_value = @cmdline_properties[name.to_s.to_sym]
|
33
|
+
value = override_value unless override_value.nil?
|
34
|
+
ExperimentProperty.create(name, value, description)
|
35
|
+
end
|
36
|
+
|
37
|
+
def resource_state(id)
|
38
|
+
@state.find { |v| v[:uid].to_s == id.to_s }
|
39
|
+
end
|
40
|
+
|
41
|
+
alias_method :resource, :resource_state
|
42
|
+
|
43
|
+
def resource_by_hrn(hrn)
|
44
|
+
@state.find { |v| v[:hrn].to_s == hrn.to_s }
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_or_update_resource_state(name, opts = {})
|
48
|
+
self.synchronize do
|
49
|
+
res = resource_state(name)
|
50
|
+
if res
|
51
|
+
opts.each do |key, value|
|
52
|
+
if value.class == Array
|
53
|
+
# Merge array values
|
54
|
+
res[key] ||= []
|
55
|
+
res[key] += value
|
56
|
+
res[key].uniq!
|
57
|
+
elsif value.kind_of? Hash
|
58
|
+
# Merge hash values
|
59
|
+
res[key] ||= {}
|
60
|
+
res[key].merge(value)
|
61
|
+
else
|
62
|
+
# Overwrite otherwise
|
63
|
+
res[key] = value
|
64
|
+
end
|
65
|
+
end
|
66
|
+
else
|
67
|
+
info "Newly discovered resource >> #{opts[:uid]}"
|
68
|
+
@state << Hashie::Mash.new({ uid: name }.merge(opts))
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
alias_method :add_resource, :add_or_update_resource_state
|
74
|
+
|
75
|
+
def sub_group(name)
|
76
|
+
@sub_groups.find { |v| v == name }
|
77
|
+
end
|
78
|
+
|
79
|
+
def add_sub_group(name)
|
80
|
+
self.synchronize do
|
81
|
+
@sub_groups << name unless @sub_groups.include?(name)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def group(name)
|
86
|
+
groups.find { |v| v.name == name }
|
87
|
+
end
|
88
|
+
|
89
|
+
def add_group(group)
|
90
|
+
self.synchronize do
|
91
|
+
raise ArgumentError, "Expect Group object, got #{group.inspect}" unless group.kind_of? OmfEc::Group
|
92
|
+
@groups << group unless group(group.name)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def each_group(&block)
|
97
|
+
if block
|
98
|
+
groups.each { |g| block.call(g) }
|
99
|
+
else
|
100
|
+
groups
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def all_groups?(&block)
|
105
|
+
!groups.empty? && groups.all? { |g| block ? block.call(g) : g }
|
106
|
+
end
|
107
|
+
|
108
|
+
def event(name)
|
109
|
+
@events.find { |v| v[:name] == name }
|
110
|
+
end
|
111
|
+
|
112
|
+
def add_event(name, trigger)
|
113
|
+
self.synchronize do
|
114
|
+
raise RuntimeError, "Event '#{name}' has been defined" if event(name)
|
115
|
+
@events << { name: name, trigger: trigger }
|
116
|
+
end
|
21
117
|
end
|
22
118
|
|
23
119
|
# Unique experiment id
|
@@ -27,11 +123,11 @@ module OmfEc
|
|
27
123
|
|
28
124
|
# Parsing user defined events, checking conditions against internal state, and execute callbacks if triggered
|
29
125
|
def process_events
|
30
|
-
|
31
|
-
|
32
|
-
if event[:trigger].call(
|
126
|
+
self.synchronize do
|
127
|
+
@events.find_all { |v| v[:callbacks] && !v[:callbacks].empty? }.each do |event|
|
128
|
+
if event[:trigger].call(@state)
|
33
129
|
info "Event triggered: '#{event[:name]}'"
|
34
|
-
|
130
|
+
@events.delete(event) if event[:consume_event]
|
35
131
|
|
36
132
|
# Last in first serve callbacks
|
37
133
|
event[:callbacks].reverse.each do |callback|
|
@@ -46,9 +142,9 @@ module OmfEc
|
|
46
142
|
class << self
|
47
143
|
# Disconnect communicator, try to delete any XMPP affiliations
|
48
144
|
def done
|
49
|
-
info "Exit in up to
|
145
|
+
info "Exit in up to 15 seconds..."
|
50
146
|
|
51
|
-
|
147
|
+
OmfCommon.eventloop.after(10) do
|
52
148
|
info "Release applications and network interfaces"
|
53
149
|
|
54
150
|
allGroups do |g|
|
@@ -57,12 +153,8 @@ module OmfEc
|
|
57
153
|
g.resources[type: 'wlan'].release unless g.net_ifs.find_all { |v| v.conf[:type] == 'wlan' }.empty?
|
58
154
|
end
|
59
155
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
OmfEc.comm.add_timer(5) do
|
64
|
-
OmfEc.comm.disconnect
|
65
|
-
end
|
156
|
+
OmfCommon.eventloop.after(5) do
|
157
|
+
OmfCommon.comm.disconnect
|
66
158
|
end
|
67
159
|
end
|
68
160
|
end
|