[wiki:Tutorials Back] [[TOC(heading=Tutorial TOC, Tutorials, Tutorials/HelloWorld, depth=2)]] = Basic Tutorial: Hello World Example = == 1. Developing a Script for an Experiment == This tutorial presents a simple example, which shows you all the basic steps to quickly develop, run, and access the result of an experiment with OMF. Subsequent tutorials will build on this one to show you how to use other OMF features. If you are a new OMF users, you may want to read the short [http://mytestbed.net/projects/omf/wiki/An_Introduction_to_OMF OMF System Overview] and/or [http://mytestbed.net/projects/omf/wiki/UsageOverview the Experiment Life-cycle Overview]. To run an experiment on the ORBIT testbed, in addtion to all the steps from [wiki:Documentation/GettingStarted getting started], a user needs to first describe the experiment in a ''script''. This ''script/experiment description'' is then passed on to a '''Experiment Controller''', which will use it to run and control the experiment execution. This process is illustrated in figure 1. This script contains the experiment configuration and scenario. More precisely, it includes: * a description of the required resources (e.g. nodes on the testbed, applications to run), * their initializations and optional associations (e.g. nodeX is connected to NodeY, all nodes use channel 6 of 802.11g), * a description of their operation throughout the experiment duration (e.g. applicationX starts at T0 and run for 60sec, nodeY switch wireless configuration after 100sec). ORBIT experiment scripts are written using the Ruby scripting language. However, a user does '''not''' necessarily need to be familiar with Ruby to be able to write and run simple experiments. The only prerequisite skills are some prior basic knowledge and practice of any other scripting and/or programming languages (e.g. python, perl, c, java,...). Of course, to be able to develop more complex experiments a user will have to learn basics of Ruby and read about ORBIT-specific Ruby methods at some stage (more on these two issues at the end of this tutorial). [[Image(OMF-User-View.png)]] [[BR]]Figure 1. Execution of an Experiment from a User's point-of-view == 2. "Hello World" experiment == In this basic experiment, we define two groups of nodes: a ''sender'' group and a ''receiver'' group. Although a group of nodes can contain several nodes, in this simple case each group contains only one node. We also associate a traffic generator application to the ''sender'' group, and a traffic sink application to the ''receiver'' group. We then define the configuration of the wireless interfaces on the nodes within these groups. Finally, we describe the successive actions to execute in order to perform the experiment. The script for this experiment is shown below. {{{ # # Tutorial experiment # defProperty('duration', 60, "Duration of the experiment") baseTopo = Topology['system:topo:imaged'] st = defTopology("sender") do |t| t.addNode(baseTopo.getNodeByIndex(0)) end rt = defTopology("receiver") do |t| t.addNode(baseTopo.getNodeByIndex(1)) end defGroup('Sender', "sender") do |node| node.addApplication("test:app:otg2") do |app| app.setProperty('udp:local_host', '192.168.0.2') app.setProperty('udp:dst_host', '192.168.0.3') app.setProperty('udp:dst_port', 3000) app.measure('udp_out', :samples => 1) end node.net.w1.mode = "adhoc" node.net.w1.type = 'g' node.net.w1.channel = "6" node.net.w1.essid = "helloworld" node.net.w1.ip = "192.168.0.2" end defGroup('Receiver', "receiver") do |node| node.addApplication("test:app:otr2") do |app| app.setProperty('udp:local_host', '192.168.0.3') app.setProperty('udp:local_port', 3000) app.measure('udp_in', :samples => 1) end node.net.w1.mode = "adhoc" node.net.w1.type = 'g' node.net.w1.channel = "6" node.net.w1.essid = "helloworld" node.net.w1.ip = "192.168.0.3" end onEvent(:ALL_UP_AND_INSTALLED) do |event| info "This is my first OMF experiment" wait 15 allGroups.startApplications info "All my Applications are started now..." wait property.duration allGroups.stopApplications info "All my Applications are stopped now." Experiment.done end }}} == 3. Understanding the "Hello World" script == As you noticed the "Hello World" script is almost ''human-readable'', and is quite understandable to any computer-literate reader. As already mentioned above, deep Ruby knowledge is '''not''' a prerequisite to running experiments on the ORBIT testbed. * The first part of this script uses the ORBIT method ''defGroup'' to define a group of nodes called ''sender'', which contains a unique node ''[1,1]''. Next, we perform some specific configurations on the node(s) within the group ''sender''. These specific configurations are described in a ''block'' (e.g. curly braces) that directly follows the ''defGroup'' call. Within this ''block'', we first assign a particular application to the ''sender'' node(s). This application is a traffic generator and is accessed via a ''prototype'' which is called ''test:proto:sender'' in this example. A prototype can be viewed as a wrapper around an existing application. It defines some set of properties (i.e. "parameters"), which allows us to customize the wrapped application for the specific need of an experiment. For example in this case, through the prototype we can set the address of the sender, and various parameters of the traffic generator itself, such as packet size, rate, and the protocol over which to send the traffic. Prototypes are normally defined in separate files, and the ORBIT platform has a set of predefined prototypes for some basic applications. For example, the prototype "test:proto:sender" is a wrapper around the application "otg" (orbit traffic generator). Other tutorials (see [wiki:Tutorials main page]) describe how to write your own prototypes for your own or existing applications. The last line on this ''sender'' block configures the first wireless interface ''w0'' on the node(s) in this block into ''managed'' mode. {{{ # # A) Define the 'sender' group, which has the unique node [1,1] # defGroup('sender', [1,1]) {|node| # Assign the prototype "test:proto:sender" to the node(s) in this group node.prototype("test:proto:sender", { # Configure the properties for this prototype, i.e. the parameters for the wrapped application 'destinationHost' => '192.168.1.2', 'packetSize' => 1024, 'rate' => 300, 'protocol' => 'udp' }) # Configure the wireless interface "w0" of the node(s) in this group # Put the interface into "Managed" mode. node.net.w0.mode = "managed" } }}} * The second part of the script is very similar to the previous one. Here we define a 'receiver' group with a unique node, this time node [1,2]. Again we define some specific configuration for the 'receiver' nodes in a block following the ''defGroup'' call. In this case, we assign a traffic sink application to the node(s) via a prototype called "test:proto:receiver", we also set the 'protocol' property of this prototype. Finally, we configure the "w0" wireless interface of the node(s) into "master" mode. {{{ # # B) Define the 'receiver' group, which has the unique node [1,2] # defGroup('receiver', [1,2]) {|node| # Assign the prototype "test:proto:receiver" to the node(s) in this group node.prototype("test:proto:receiver" , { 'protocol' => 'udp' }) # Configure the wireless interface "w0" of the node(s) in this group node.net.w0.mode = "master" } }}} * The third part of the script presents an example on how to configure interfaces on all nodes in one place to ensure consistency. The command ''allGroups.net.w0'' describes the first wireless interface on all nodes in the experiment. The code inside the following ''block'' (e.g. curly braces) configures various parameters of these interfaces. In this specific example, we configure the interface as an 802.11b type, set the ''essid'' to a common string, and set it's IP address. We obviously do not want to set all the interfaces to the same IP address, thus any string beginning with a '%' is ''personalized'' for each node by replacing characters prefixed by a '%' with a local string. In this specific example, '%y' is replaced by the 'y' coordinate of the node. For this specific experiment setup, the IP address of node [1,1] will be 192.168.1.1, while node [1,2] will have 192.168.1.2 assigned. This part concludes the configuration phase of the experiment. {{{ # # C) Configure the wireless interfaces of All the Nodes in this experiment # allGroups.net.w0 { |w| w.type = 'b' w.channel = "6" w.essid = "helloworld" w.ip = "%192.168.%x.%y" } }}} * This final part of the script describes the operation to execute in order to perform the experiment. An ORBIT experiment script basically defines a state machine, or more precisely, what sequence of commands should be executed if the experiments enters a particular state. The only state we will use in this experiment is ''whenAllInstalled''. This state is reached when all the nodes are configured and all the requested applications are installed and ready to go. The sequence of commands to perform are given in a ''block'' following the ''whenAllInstalled'' call. The first command 'wait 30' will suspend the execution for 30 seconds to ensure that indeed everything has settled. The ''allGroups.startApplications'' will then send a command to all nodes to start the applications assigned to them (via the use of ''prototypes'') in the previous script parts. Thus in this example, this command will start a traffic generator on node [1,1] and a corresponding sink on node [1,2]. The different parameters for these applications are taken from the above definition as well. Finally, the next line 'wait 40' will suspend the control of the experiment for 40 seconds (during which the applications on the nodes '''will run''' and exchange traffic), before concluding the experiment with a call to ''Experiment.done''. {{{ # # D) When all the nodes are turned On and the all the applications # are installed and ready, we can start to perform the experiment # whenAllInstalled() {|node| # Wait 30sec wait 30 # Start all the Applications on all the nodes allGroups.startApplications # Wait for 40sec wait 40 # Stop the experiment execution Experiment.done } }}} * You can find detailed description of all the various experiment-specific scripting commands (such as ''defGroup'', ''allGroups'', ''whenAllInstalled'', etc...) in the '''Node Handler''' document page '''[wiki:Old/NodeHandler/Commands/defNodes here]''' [[BR]] [[BR]] == 4. Running the "Hello World" experiment == Before being able to run this experiment, you should: * Make sure you have followed the getting started procedure [wiki:Documentation/GettingStarted getting started] * Once you have proper reservation, login to the console. * Install a disk image which includes ''Ex'' on the nodes of this testbed. For example, you can use the latest ''baseline.ndz'' disk image. This disk image installation is done using the ''omf load'' command above. Detailed instructions on how to perform such disk image installation on the nodes on a testbed can be found [wiki:Tutorials/HowToImage here]. {{{ ssugrim@console.grid:/usr/bin$ omf help exec OMF Experiment Controller 5.4 (git c005675) Execute an experiment script Usage: exec [OPTIONS] ExperimentName [-- EXP_OPTIONS] ExperimentName is the filename of the experiment script [EXP_OPTIONS] are any options defined in the experiment script [OPTIONS] are any of the following: -a, --allow-missing Continue experiment even if some nodes did not check in -c, --config NAME Configuration section from the config file ('default' if omitted) -C, --configfile FILE File containing local configuration parameters -d, --debug Operate in debug mode -i, --interactive Run the experiment controller in interactive mode -l, --libraries LIST Comma separated list of libraries to load (defaults to [system:exp:stdlib,system:exp:eventlib,system:exp:winlib]) --log FILE File containing logging configuration information -m, --message MESSAGE Message to add to experiment trace -n, --just-print Print the commands that would be executed, but do not execute them -N, --no-am Don't use the Aggregate Manager (AM) -p, --print URI Print the contents of the experiment script -o, --output-result FILE File to write final state information to -e, --experiment-id EXPID Set the ID for this experiment, instead of the default standard ID -O, --output-app Display STDOUT & STDERR output from the executed applications -r, --reset If set, then reset (reboot) the nodes before the experiment -s, --shutdown If set, then shut down resources at the end of an experiment -S, --slice NAME Name of the Slice where this EC should operate -t, --tags TAGS Comma separated list of tags to add to experiment trace --oml-uri URI The URI to the OML server for this experiment -x, --extra-libs LIST Comma separated list of libraries to load in addition to [system:exp:stdlib,system:exp:eventlib,system:exp:winlib] --slave-mode EXPID Run in slave mode in disconnected experiment, EXPID is the exp. ID --slave-mode-resource NAME When in slave mode, NAME is the HRN of the resource for this EC -h, --help Show this message -v, --version Show the version }}} Once the nodes have been imaged, you can run the "Hello World" experiment with the ''omf exec'' command as follows: {{{ omf exec test:exp:tutorial:hello-world-wireless.rb }}} Normally an experiment is described in a script file, e.g. "my_experiment.rb", which is run by using the command "omf exec my_experiment". However in this particular case, the tutorial script is already stored in a repository known by the ''omf exec'' application. Thus the "test:exp:tutorial:hello-world-wireless.rb" option instructs the ''omf exec'' application to fetch that script and execute it. After running the above command, your console output should look like this: {{{ Test results go here: }}} '''Congratulations''' you just ran your first experiment script on the Orbit Testbed! You will find information on how to collect and interpret measurements and results from your experiment in the next parts of this basic tutorial, [wiki:Tutorials/CollectMeasurements here] and [wiki:Tutorials/AnalyzeResults here] [[BR]] [[BR]] == 5. Beyond the Basics == === 5.1. More on the Ruby Language and the ORBIT-specific Ruby methods === As mentioned previously, ORBIT experiment scripts are written in Ruby, an easily understood, extensible, dynamic, object-oriented scripting language. Ruby has been extended into the testbed user's domain with a number of methods. Besides its extensibility and object-orientation, Ruby is concise and consistent. An ORBIT script therefore is written in Ruby primarily using ORBIT-specific methods. Users are encouraged to take look at following resources: * [wiki:Tutorial/HowtoWriteScripts/RubyResources Ruby Resources] * [wiki:Tutorial/HowtoWriteScripts/OrbitRuby ORBIT Specific Ruby Methods] === 5.2. More Tutorials === Once you are done with the remaining parts of this basic tutorial (i.e. [wiki:Tutorials/CollectMeasurements collecting] and [wiki:Tutorials/AnalyzeResults interpreting] experiment measurements and results), you might want to have a look at the other tutorials on the [wiki:Tutorials main page] to further learn about the other ORBIT functions that can help you develop your own complex experiments. === 5.3. Definitions of the Prototypes and Application used in the "Hello World" script === * Orbit Traffic Generator (OTG) [[BR]] [wiki:Documentation/OTG/ScriptsRepository/ProtoDefSender sender.rb] is the file that contains the "test:proto:sender" prototype, which wraps around the [wiki:Documentation/OTG/ScriptsRepository/OtgAppDef otg.rb] application. * Orbit Traffic Receiver (OTR) [[BR]] [wiki:Documentation/OTG/ScriptsRepository/ProtoDefReceiver receiver.rb] is the file that contains the "test:proto:receiver" prototype, which wraps around the [wiki:Documentation/OTG/ScriptsRepository/OtrAppdef otr.rb] application.