How to define Events and associated tasks to execute¶
1. Prerequisites¶
- Make sure that you understand how OMF works from a user's point of view.
- Make sure that you have completed and understood most of the basic tutorial.
2. Goal¶
- This tutorial shows you how to define Events in your experiment and how to associate tasks to execute when these events occur.
- The OMF Experiment Controller (EC) uses an Event-driven mechanism to execute the different steps of an experiment. This mechanism involves:
- the definition on an Event:
- an OMF Event is a test which will be periodically performed by the EC (default = every 5sec)
- this test is up to you to write and could check for whatever you want to. For example, it can check for: a state being reached by some resources or the experiment itself, a value from one of the collected measurements, a key pressed by you, etc...
- when you write this test, you decide on the conditions that allows your event to fire (= trigger)
- by default an event is not re-armed after it has fired (i.e. detect the event only once), but you can decide otherwise
- the association of tasks to that Event:
- when your Event has been fired the tasks defined here will be executed
- you can define multiple blocks of tasks to associate to the Event. They will be put in a stack and executed in the last-in first-out order.
- one of these blocks of tasks can be configured to consume the event, i.e. the other blocks after it will never be executed
- the definition on an Event:
3. A simple Event with a single block of tasks associated to it¶
- Here is an example of a simple experiment with a custom event: (download: using-event-1.rb)
1 defProperty('startTime', Time.now, "The experiment start time")
2 defGroup('Actor', 'omf.nicta.node8')
3
4 defEvent(:MY_FIRST_EVENT) do |event|
5 seconds = Time.now - property.startTime.value
6 info "Number of seconds gone by: #{seconds}"
7 if seconds > 15
8 event.fire
9 end
10 end
11
12 onEvent(:MY_FIRST_EVENT) do |event|
13 info " "
14 info "My event has happened!"
15 info " "
16 Experiment.done
17 end
- Line 4-10: define the new Event named
:MY_FIRST_EVENT- line 6: computes how many seconds have elapsed since the start o the Experiment
- line 7-9: if more than 15 seconds have gone by, then fire the event!
- by default, the EC will perform this test every 5 second
- Line 12-17: define a single block of tasks to execute when the Event
:MY_FIRST_EVENTfires:- line 13-15: prints some information for you to see
- line 16: terminate this Experiment
- When running that simple experiment with
omf-5.3 exec simple.rb, you should see:
1 INFO NodeHandler: OMF Experiment Controller 5.3.662
2 INFO NodeHandler: Slice ID: testing_slice (default)
3 INFO NodeHandler: Experiment ID: testing_slice-2010-09-24t04.29.44+00.00
4 INFO NodeHandler: Message authentication is disabled
5 INFO NodeHandler: Web interface available at: http://10.0.0.200:4000
6 INFO Experiment: load system:exp:stdlib
7 INFO property.resetDelay: value = 210 (Fixnum)
8 INFO property.resetTries: value = 1 (Fixnum)
9 INFO Experiment: load system:exp:eventlib
10 INFO Experiment: load test01
11 INFO property.startTime: value = Fri Sep 24 04:29:46 +0000 2010 (Time)
12 INFO Topology: Loading topology 'omf.nicta.node8'.
13 INFO exp: Number of seconds gone by: 1.486059
14 INFO exp: Number of seconds gone by: 6.488447
15 INFO exp: Number of seconds gone by: 11.526995
16 INFO exp: Number of seconds gone by: 16.610224
17 INFO MY_FIRST_EVENT: Event triggered. Starting the associated tasks.
18 INFO exp:
19 INFO exp: My event has happened!
20 INFO exp:
21 INFO EXPERIMENT_DONE: Event triggered. Starting the associated tasks.
22 INFO NodeHandler:
23 INFO NodeHandler: Shutting down experiment, please wait...
24 INFO NodeHandler:
25 INFO run: Experiment testing_slice-2010-09-24t04.29.44+00.00 finished after 0:26
- line 11: notice the value of the Experiment Property
startTimeas we defined it in the above Experiment - line 13-16: the number of elapsed seconds, which is displayed by our test in our Event definition
- line 17: the EC informs us that our Event has fired (= has been triggered)!
- line 18-20: the tasks that we defined in our unique block of tasks associated to our Event
- Notes:
- you may notice line 21-24, which gives a hint that OMF's "end of experiment" is actually implemented as an Event itself.
- you may also have notice the use of
onEvent(:ALL_UP_AND_INSTALLED) ...in the basic "Hello World" tutorial - OMF provides a set of default events, already defined for you, which you can use in your experiment. The Events
:EXPERIMENT_DONEand:ALL_UP_AND_INSTALLEDare parts of these default Events, which will be described further below.
4. Syntax of defEvent and onEvent¶
- defEvent:
1 syntax: defEvent(name, interval, rearm) do |event| 2 ... 3 if ... 4 event.fire(params) 5 end 6 end
- name : new name for this event, usually something like :MY_OWN_EVENT
- interval : optional - the interval in second at which the test for this event should be performed, default is 5 sec.
- rearm : optional - (true/false) should this event be re-armed after it has fired once, i.e. do you want this event to be
checked for again after it has fired and its tasks have been executed once, default value is false.
- params : optional - a Hash of user-defined parameters, which should be passed to the blocks of tasks associated to this event
example: event.fire(:foo => 'abcd', :bar => 1234)
NOTE: when you decide to allow an event to be 'rearmed', be careful to note that if the condition(s) on
which your event has been previously triggered have not changed, then your rearmed event will directly
re-trigger... if not used correctly this may end up resulting in infinite loops in your experiment.
- onEvent:
1 syntax: onEvent(name, consume) do |event| 2 ... 3 variable = event[:parameter] 4 ... 5 end
- name : the name of the event to associate the block of tasks with
- consume : optional - (true/false) should this block of tasks consume this event or not, if true then other blocks of tasks
defined after this one for the same event will never be executed, default is false
- parameter : optional - the name of a parameter to access, and which was passed to this block of tasks by the event when it fired
example: var = event[:foo]
which would in this example set the string 'abcd' into the variable 'var'
5. More examples of Events:¶
5.1. Events that have multiple associated blocks of tasks:¶
- You can associate multiple block of tasks top a single event
- These blocks will be executed sequentially in a "last defined is first executed" order
- Example:
- when running this example, you should see the message from "Block 3" and Block 2" in that order
- "block 2" is set to consume the Event, and thus "block 1" will never be executed
1 defProperty('startTime', Time.now, "The experiment start time")
2 defGroup('Actor', 'omf.nicta.node8')
3
4 defEvent(:MY_FIRST_EVENT) do |event|
5 event.fire if (Time.now - property.startTime.value) > 15
6 end
7
8 onEvent(:MY_FIRST_EVENT) do |event|
9 info "My event has happened! - Block 1"
10 end
11
12 onEvent(:MY_FIRST_EVENT, true) do |event|
13 info "My event has happened! - Block 2"
14 Experiment.done
15 end
16
17 onEvent(:MY_FIRST_EVENT) do |event|
18 info "My event has happened! - Block 3"
19 end
5.2. Events that check if a give state is reached¶
- The EC holds and updates an XML tree of states about the running experiments and the involved resources
- this XML tree with these states are saved by the EC in a file at the end of your experiment
- an example of such XML state file is given at the bottom of the "Hello World" page
- You can define Events, which will query that XML tree during your experiment runtime to detect if a state has been reached
- such a query is done using the XPATH language (a good tutorial here: http://www.w3schools.com/xpath)
- Example 1 - This Event fires when all the resources have checked-in with the EC:
1 defEvent(:ALL_UP) do |event|
2 node_status = allGroups.state("status/@value")
3 event.fire if allEqual(node_status, "UP")
4 end
- Example 2 - This Event fires when all the resources have checked-in with the EC and all their associated applications have been installed:
1 defEvent(:ALL_UP_AND_INSTALLED) do |event|
2 node_status = allGroups.state("status/@value")
3 app_status = allGroups.state("apps/app/status/@value")
4 if allEqual(node_status, "UP") && allEqual(app_status, "INSTALLED.OK")
5 event.fire
6 end
7 end
- Example 3 - This Event fires when the Experiment has reached the state "DONE"
1 defEvent(:EXPERIMENT_DONE) do |event|
2 exp_status = Experiment.state("status/text()")
3 event.fire if allEqual(exp_status, "DONE")
4 end
- Example 4 - This Event fires when one of the applications 'bar' running on resources in the group 'Foo' writes a text containing the word 'example' on its standard out
1 defEvent(:STDOUT_EVENT_FROM_BAR) do |event|
2 app_status = group('Foo').state("apps/app[@name='bar']/io/out/line")
3 if !app_status.nil?
4 app_status.each do |element|
5 event.fire if element.include?('example')
6 end
7 end
8 end
- Command Syntax:
Syntax: allGroups.state(xpath)
For all the involved resources (e.g. node), returns an Array with all the XML elements matching a given XPATH
- xpath : the XPATH to match
Syntax: Experiment.state(xpath)
For the Experiment, returns an Array with all the XML elements matching a given XPATH
- xpath : the XPATH to match
Syntax: allEqual(array, value)
Return true if all the elements of the given array are equal to the defined value
- array : an Array returned by allGroups.state(...)
- value : the value to match
Syntax: oneEqual(array, value)
Return true, if at least one element of the given array is equal to the defined value
- A good tutorial on XPATH can be found here: http://www.w3schools.com/xpath
- An example of the EC XML state is given at the bottom of the "Hello World" page
5.3. Events that check if a given collected measurement has reached a given value¶
- The EC allows you to access any collected measurements while your application is running
- This capability can be used to:
- draw dynamic graphs while your experiment is running (see the "How to draw graphs" tutorial)
- define Events based on measurements
- Defining the Event:
1 defEvent(:MY_OWN_EVENT, 1) do |event|
2 myMeasurementStream = ms('udp_in')
3 myMeasuremenStream.project(:pkt_length).each do |row|
4 value = row.tuple[0]
5 event.fire if value > PKTSIZE
6 end
7 end
- Line 1: defines our custom event, for which a test will be done every 1 second
- Line 2: we get a reference to an existing Measurement Stream (MS)
- Here the MS is produced by the Measurement Point
udp_in(provided by the application OTR, see the "Hello World" tutorial) - We use
msto access that measurement stream
- Here the MS is produced by the Measurement Point
- Line 3-6: we retrieve and test the measurements of
pkt_lengthfrom the MS generated byudp_in:- line 3: we use
projectto get the values of the metricpkt_lengthfrom the MPprojectreturns a table of all the values ofpkt_lengthup to now, each row of this table is a tuple
- line 4: for each row (tuple) of the table, we retrieve the first value which is the
pkt_lengthmeasurement - line 5: we test if that value is above a given threshold (
PKTSIZE), if so we fire the Event
- line 3: we use
- Command Syntax:
Syntax: ms(name)
Return a Measurement Stream which has been produced by a Measurement Point
- name : the name of the Measurement Point to get the stream from
This Measurement Point has to be defined previously in an Application
Definition
Syntax: myStream.project(:metric1, :metric2, :metric3, ...)
Return a table of measurements for the specified metrics
- myStream : the Measurement Stream from which the measurements should be retrieved
- :metricX : the name of the metric inside the Measurement Stream for which the measurements
should be retrieved. Note the use of ":" to prefix the metric name
- Refer to the following tutorials to learn more about Measurement Points and Stream, how to collect measurements, how to find out the names of Measurement Points or metrics:
- Now, this is a modified "Hello World" experiment using this measurement-based event:
- refer to the set of basic tutorials if you do not understand this experiment description
1 PKTSIZE = 256
2 defProperty('res1', "omf.nicta.node8", "ID of a node")
3 defProperty('res2', "omf.nicta.node9", "ID of a node")
4 defProperty('packetsize', PKTSIZE, "Packet size (byte) from the sender node")
5
6 defGroup('Sender', property.res1) {|node|
7 node.addApplication("test:app:otg2") {|app|
8 app.setProperty('udp:local_host', '%net.w0.ip%')
9 app.setProperty('udp:broadcast', 1)
10 app.setProperty('udp:dst_host', '192.168.255.255')
11 app.setProperty('udp:dst_port', 3000)
12 app.setProperty('cbr:rate', 2048)
13 app.setProperty('cbr:size', property.packetsize)
14 app.measure('udp_out', :samples => 1)
15 }
16 }
17
18 defGroup('Receiver', property.res2) {|node|
19 node.addApplication("test:app:otr2") {|app|
20 app.setProperty('udp:local_host', '192.168.255.255')
21 app.setProperty('udp:local_port', 3000)
22 app.measure('udp_in', :samples => 1)
23 }
24 }
25
26 allGroups.net.w0 do |interface|
27 interface.mode = "adhoc"
28 interface.type = 'g'
29 interface.channel = "6"
30 interface.essid = "helloworld"
31 interface.ip = "192.168.0.%index%"
32 end
33
34 defEvent(:MY_OWN_EVENT, 1) do |event|
35 myMeasurementStream = ms('udp_in')
36 myMeasurementStream.project(:pkt_length).each do |row|
37 value = row.tuple[0]
38 event.fire if value > PKTSIZE
39 end
40 end
41
42 onEvent(:MY_OWN_EVENT) do |event|
43 info " "
44 info " #{Time.now} - We just received large packets!"
45 info " "
46 end
47
48 onEvent(:ALL_UP_AND_INSTALLED) do |event|
49 wait 10
50 allGroups.startApplications
51 wait 20
52 info "#{Time.now} - Packet Size before: #{property.packetsize}"
53 property.packetsize = 512
54 info "#{Time.now} - Packet Size after: #{property.packetsize}"
55 wait 20
56 allGroups.stopApplications
57 Experiment.done
58 end
- The output:
1 INFO NodeHandler: OMF Experiment Controller 5.3.662
2 INFO NodeHandler: Slice ID: testing_slice (default)
3 INFO NodeHandler: Experiment ID: testing_slice-2010-09-24t06.27.20+00.00
4 INFO NodeHandler: Message authentication is disabled
5 INFO NodeHandler: Web interface available at: http://10.0.0.200:4000
6 INFO Experiment: load system:exp:stdlib
7 INFO property.resetDelay: value = 210 (Fixnum)
8 INFO property.resetTries: value = 1 (Fixnum)
9 INFO Experiment: load system:exp:eventlib
10 INFO Experiment: load using-event-2.rb
11 INFO property.res1: value = "omf.nicta.node8" (String)
12 INFO property.res2: value = "omf.nicta.node9" (String)
13 INFO property.packetsize: value = 256 (Fixnum)
14 INFO Topology: Loading topology 'omf.nicta.node8'.
15 INFO Topology: Loading topology 'omf.nicta.node9'.
16 INFO ALL_UP_AND_INSTALLED: Event triggered. Starting the associated tasks.
17 INFO exp: Request from Experiment Script: Wait for 10s....
18 INFO omf.nicta.node8: Device 'net/w0' reported 06:0B:6B:0A:83:7E
19 INFO omf.nicta.node9: Device 'net/w0' reported 06:0B:6B:0A:83:7E
20 INFO exp: Request from Experiment Script: Wait for 20s....
21 INFO exp: Fri Sep 24 06:28:05 +0000 2010 - Packet Size before: 256
22 INFO property.packetsize: value = 512 (Fixnum)
23 INFO exp: Fri Sep 24 06:28:05 +0000 2010 - Packet Size after: 512
24 INFO exp: Request from Experiment Script: Wait for 20s....
25 INFO MY_OWN_EVENT: Event triggered. Starting the associated tasks.
26 INFO exp:
27 INFO exp: Fri Sep 24 06:28:09 +0000 2010 - We just received large packets!
28 INFO exp:
29 INFO EXPERIMENT_DONE: Event triggered. Starting the associated tasks.
30 INFO NodeHandler:
31 INFO NodeHandler: Shutting down experiment, please wait...
32 INFO NodeHandler:
33 INFO run: Experiment testing_slice-2010-09-24t06.27.20+00.00 finished after 1:15
- Line 23: we just increased the packet size as specified on line 53 of our experiment description
- Line 25-28: 4 seconds after that, our custom event
MY_OWN_EVENTfired and our custom set of tasks (here printing text) is executed - Note that the delay of 4 second might be due to many factors, e.g. the time it takes for the application OTG to react to the request of increasing the packet size, the time it takes for measurements to be collected at the receiver, etc...
6. List of predefined Events bundled with OMF¶
- Some Events have already been defined for you in OMF
- If you want to use them, you just have to define the set of tasks to associate to them
- Some of these Events have a default set of task associated to them
- these default tasks will be executed after your own custom ones, if you defined any
- to prevent them from being executed, you should set the
consumeflag to true when defining your own tasks
Predefined Events and their default tasks if any
- :ALL_UP_AND_INSTALLED
- Triggered when:
- all the resources in your experiments have checked in with the EC
- AND all the applications associated with these resources have been installed
- Default tasks: none
- Triggered when:
- :ALL_UP
- Triggered when:
- all the resources in your experiments have checked in with the EC
- Default tasks: none
- Triggered when:
- :ALL_INTERFACE_UP
- Triggered when:
- all the interfaces of all the resources in your experiments have been correctly configured
- Default tasks: none
- Triggered when:
- :EXPERIMENT_DONE
- Triggered when:
- the command
Experiment.doneis called by your experiment
- the command
- Default tasks:
- stop all activities and reset the resources of your experiments
- cleanly close the EC's log and XML state files
- clean anything created by the EC for the experiment run (e.g. communication pub/sub groups)
- stop the experiment run
- Triggered when:
- :INTERRUPT
- Triggered when:
- the user press "CTRL-C" on the console running the EC (or send a SIGINT to the EC process)
- Default tasks:
- calls the
Experiment.donecommand, thus triggering the :EXPERIMENT_DONE Event
- calls the
- Triggered when:
- :NO_USER_DEFINED_EVENTS
- Triggered when:
- no Events have been defined in your experiment
- Default tasks:
- prints a warning message informing you that your experiment has no event defined, and thus nothing will happen from now on
- Triggered when: