omf_ec 6.0.0.pre.4 → 6.0.0.pre.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|