== Exercise 1: Simple MobilityFirst Network Deployment and Test == [[TOC(../oMF*)]] === Objective === In this exercise we will establish a simple topology consisting of MobilityFirst routers, hosts and applications, deploy the software components onto physical nodes, and run an end-to-end 'ping' application. At the end of the exercise, you should expect to acquire a general knowledge of the Mobilityfirst software components and how to utilize them using the Orbit testbed. === Topology === In this exercise we will use a simple linear topology consisting of two MobilityFirst routers (MFR) that interconnect two hosts: Host1 will initiate a 'ping' communication and Host2 will respond to the ping request: {{{ #!sh Host1 ---- MFR1 ---- MFR2 ---- Host2 }}} === MobilityFirst ORBIT Image === The complete set of components from the latest release of MobilityFirst software is available as a compressed image within the ORBIT testbed for imaging nodes using the 'omf' tool. The current pre-prepared image is built over Ubuntu 12.04 LTS distribution and will be moved to newer distributions as they become available and we have had a chance to test compatibility. A typical Orbit experiment requires the following six steps: 1. [[CollapsibleStart(Create resource reservation)]][[Include(Documentation/Short/CreateRes)]][[CollapsibleEnd]] 2. [[CollapsibleStart(Login into reserved domain: "ssh username@sb1.orbit-lab.org")]][[Include(Documentation/Short/Login)]][[CollapsibleEnd]] 3. [[CollapsibleStart(Load an image on the nodes: "omf load -i baseline.ndz -t all")]] [[Include(Documentation/Short/LoadImage)]][[CollapsibleEnd]] 4. [[CollapsibleStart(Turn the nodes on: "omf tell -a on -t all")]][[Include(Documentation/Short/TellOn)]][[CollapsibleEnd]] 5. [[CollapsibleStart(Execute the experiment: "omf exec test:exp:tutorial:hello-world-wireless -- --res1 node1-1.sb1.orbit-lab.org --res2 node1-2.sb1.orbit-lab.org")]][[Include(Software/cOMF/aExec)]][[CollapsibleEnd]] 6. Analyze the results While, most of the experiments follow the presented structure, for this specific tutorial some simplifications have been applied. From now on, the following assumptions are considered: * You will be working with resources belonging to the Orbid grid. * You have been assigned a group number. While for this experiment we are using the grid, it is not a strict a requirement and for the successful execution of the experiment any sandbox with at least 4 nodes could be employed. Once logged into the grid console: {{{ #!sh ssh username@console.grid.orbit-lab.org }}} From the console we will start by loading the Mobilityfirst image into the four nodes that have been assigned to you: {{{ #!sh omf load -i 'mf-release-latest.ndz' -t system:topo:mf-groupX }}} ''system:topo:mf-groupX'' represents the group of 4 nodes and ''mf-groupX'' has to be replaced by the group id assigned to you. For example, ''mf-group1'' will load the image on nodes 'node1-1,node1-2,node2-1,node2-2' If the output of your console looks similar to: {{{ #!sh INFO exp: ----------------------------- INFO exp: Imaging Process Done INFO exp: 4 nodes successfully imaged - Topology saved in '/tmp/pxe_slice-2014-10-15t02.10.16.594-04.00-topo-success.rb' INFO exp: ----------------------------- INFO EXPERIMENT_DONE: Event triggered. Starting the associated tasks. INFO NodeHandler: INFO NodeHandler: Shutting down experiment, please wait... INFO NodeHandler: INFO NodeHandler: Shutdown flag is set - Turning Off the resources INFO run: Experiment pxe_slice-2014-10-15t02.10.16.594-04.00 finished after 1:50 }}} your nodes have been imaged correctly. === Deploy Network === Software and experiment control in the ORBIT testbed can be automated greatly using the OMF framework. An OMF control script is written in Ruby and allows the experimenter to specify the set of nodes, their network configuration, to specify software components and arguments, and to control their execution on one or more nodes. We will use an OMF script to bring up 4 ORBIT nodes to host our topology, with corresponding software components. We will first introduce the main details of the scripts that will be run and then we will step to the execution process itself. ==== Software Component Specification ==== The following snippet shows the specification of the MobilityFirst components along with the required arguments: {{{ #!ruby #Application definition of a MobilityFirst access router defApplication('MF-Router', 'router') {|app| app.shortDescription = "Click-based MobilityFirst Access Router" app.path = "/usr/local/src/mobilityfirst/eval/orbit/tutorial/scripts/ARWrapper.sh" # click options app.defProperty('num_threads', 'number of threads', "-t",{:type => :integer, :mandatory => true, :default => 4, :order => 1}) app.defProperty('ctrl_port', 'port for Click control socket', "-c",{:type => :integer, :order => 2}) # click config file app.defProperty('config_file', 'Click configuration file', "-C",{:type => :string,:mandatory=> true}) # keyword parameters used in click config file app.defProperty('my_GUID', 'router GUID', "-m",{:type => :string, :mandatory => true}) app.defProperty('topo_file', 'path to topology file', "-f",{:type => :string, :mandatory => true}) app.defProperty('core_dev', 'core network interface', "-d",{:type => :string,:mandatory => true}) app.defProperty('GNRS_server_ip', 'IP of local GNRS server', "-s",{:type => :string,:mandatory => true}) app.defProperty('GNRS_server_port', 'Port of GNRS server', "-p",{:type => :string,:mandatory => true}) app.defProperty('GNRS_listen_ip', 'IP to listen for GNRS response', "-i",{:type => :string,:default => "0.0.0.0"}) app.defProperty('GNRS_listen_port', 'port to listen for GNRS response', "-P",{:type => :string,:default => "10001"}) app.defProperty('edge_dev', 'edge network interface', "-D",{:type => :string,:mandatory => true}) app.defProperty('edge_dev_ip', 'IP assigned to edge interface', "-I",{:type => :string,:mandatory => true}) } #Application definition of a GNRS server defApplication('MF-GNRS', 'gnrs') {|app| app.shortDescription = "GNRS Server" app.path = "/usr/local/src/mobilityfirst/eval/orbit/tutorial/scripts/GNRSWrapper.sh" app.defProperty('log4j_config_file', 'log 4j configuration file', "-d",{:type => :string, :order => 1}) app.defProperty('jar_file', 'server jar file with all dependencies', "-j" ,{:type => :string, :mandatory=> true, :default => "/usr/local/src/mobilityfirst/gnrs/jserver/target/gnrs-server-1.0.0-SNAPSHOT-jar-with-dependencies.jar", :order => 2}) app.defProperty('config_file', 'server configuration file', "-c",{:type => :string, :mandatory=> true, :order => 3}) } #Application definition of the client network protocol stack defApplication('MF-HostStack', 'hoststack') {|app| app.shortDescription = "MF host network stack" app.path = "/usr/local/bin/mfstack" app.defProperty('log_level', 'log level', nil,{:type => :string, :mandatory => true, :order => 1, :default => "-e"}) # default is 'error' app.defProperty('config_file', 'stack configuration file', nil,{:type => :string, :mandatory => true, :order => 2}) } }}} A few considerations on the defined applications: * As seen above, the router is configured with both 'core' and 'edge' interfaces. Different router configurations are available depending on the required functionality. In this case we use what we call a MobilityFirst Access Router, which has the particularity of having the core interfaces connected towards the core of the network, while the edge interface enables hosts to connect and access the MobilityFirst network. * For this basic setup, the GNRS has been configured to be running as a single instance, but in a larger experiment, it is designed to be a distributed system deployed at different locations. * Most of the client settings are located in a configuration file pre-loaded on the Orbit image. ==== Setting up the Software Node Groups ==== The following shows how the node groups for the routers are setup in the OMF control scripts. Node groups allows experimenters to use single statements to set configuration (e.g. network interfaces) and execute commands across all nodes in the group. {{{ #!ruby #Create router groups for i in 1..num_routers #Create a topology with a single router in it defTopology("topo:router_#{i}") { |t| aNode = routersTopo.getNodeByIndex(i-1) t.addNode(aNode) info aNode, " assigned role of router with GUID: #{i}" } #Through the group definition we set up the applications to run defGroup("router_#{i}", "topo:router_#{i}") {|node| node.addApplication('MF-Router') {|app| app.setProperty('num_threads', router_threads) app.setProperty('config_file', click_conf) app.setProperty('my_GUID', router_guid[i-1]) app.setProperty('topo_file', rtr_topo_file) app.setProperty('core_dev', core_dev) app.setProperty('GNRS_server_ip', GNRS_server_ip) app.setProperty('GNRS_server_port', GNRS_server_port) app.setProperty('GNRS_listen_ip', "192.168.100.#{i}") app.setProperty('GNRS_listen_port', GNRS_listen_port) app.setProperty('edge_dev', edge_dev) app.setProperty('edge_dev_ip', router_ether_if_ip[i-1]) } #If it is the first router add the GNRS if i == 1 aNode = routersTopo.getNodeByIndex(i-1) info aNode, " will also host the GNRS server" node.addApplication('MF-GNRS') {|app| app.setProperty('log4j_config_file', GNRS_log_file) app.setProperty('jar_file', GNRS_jar_file) app.setProperty('config_file', GNRS_conf_file) } end #Setup the node interfaces #The first ethernet interface is used as the core interface node.net.e0.ip = "192.168.100.#{i}" node.net.e0.netmask = '255.255.255.0' #The first wireless interface is used to give access to clients node.net.w0.mode = "adhoc" node.net.w0.type = 'g' node.net.w0.channel = "11" node.net.w0.essid = "SSID_group_#{i}" node.net.w0.ip = "192.168.#{i}.1" } end #Create host groups for i in 1..num_hosts #Create a topology with a single router in it defTopology("topo:host_#{i}") { |t| aNode = hostsTopo.getNodeByIndex(i-1) t.addNode(aNode) info aNode, " assigned role of client with GUID: #{100 + i}" } #Through the group definition we set up the applications to run defGroup("host_#{i}", "topo:host_#{i}") {|node| node.addApplication('MF-HostStack') {|app| app.setProperty('config_file', hoststack_conf_file[i-1]) app.setProperty('log_level', log_level) } #The first wifi interface is used to connect to the Access Router node.net.w0.mode = "adhoc" node.net.w0.type = 'g' node.net.w0.channel = "11" node.net.w0.essid = "SSID_group_#{i}" node.net.w0.ip = "192.168.#{i}.2" } end }}} As can be seen above, properties which were defined in the MF-Router, MF-GNRS and MF-HostStack applications have been set here. Moreover, node interfaces have been set up, and IP addresses have been assigned to them. As we discussed earlier the router is configured with both edge and core interfaces. The ethernet interface is used to connect to the core of the network, and the wireless interface is for connection to the clients. On the other side, the client is equipped with wifi interface to connect to the access router. ==== Starting the MobilityFirst Components ==== The following snippet shows the starting of the router software and the gnrs servers on the two router nodes: {{{ #!ruby onEvent(:ALL_UP_AND_INSTALLED) do |event| info "This is my first MobilityFirst experiment" info "Initializing resources" # clean up and initialize networking for routers for i in 1..num_routers # click router cleanup group("router_#{i}").exec("killall -9 click") # gnrsd cleanup group("router_#{i}").exec("killall -9 java") end #clean up and initialize networking for hosts for i in 1..num_hosts group("host_#{i}").exec("killall -9 mfstack") end wait 20 # bring up routers (along with gnrs servers) info "Bringing up routers..." for i in 1..num_routers group("router_#{i}").startApplications end wait 5 info "Bringing up host stacks..." for i in 1..num_hosts group("host_#{i}").startApplications end info "Access the nodes to run a program" wait 10000 Experiment.done end }}} To make sure, our experiment will not conflict with any prior running processes on the node groups, killall command is used to terminate all processes associated with routers, GNRS and host stack. After waiting for a reasonable time, first the routers are brought up and we start the applications on them, and then the hosts are brought up, starting the applications on them. ==== Executing the script ==== First of all you will first need to turn your assigned nodes on: {{{ #!sh omf tell -a on -t system:topo:imaged }}} In order to execute the just described script, download it to your console home folder copying and pasting the following command: {{{ #!sh wget www.winlab.rutgers.edu/~bronzino/downlaods/exercise1.rb }}} Once the file has been downloaded, execute it with the following command: {{{ #!sh omf exec exercise1.rb }}} The obtained output should resamble the follwoing snippet: {{{ #!sh INFO NodeHandler: OMF Experiment Controller 5.4 (git 3fb37b9) INFO NodeHandler: Reading configuration file /etc/omf-expctl-5.4/services.yaml INFO NodeHandler: Add domain http - http://internal1.orbit-lab.org:5054/ INFO NodeHandler: Add domain http - http://repository1.orbit-lab.org:5054/ INFO NodeHandler: Slice ID: default_slice (default) INFO NodeHandler: Experiment ID: default_slice-2014-10-15t02.12.19.869-04.00 INFO NodeHandler: Message authentication is disabled INFO Experiment: load system:exp:stdlib INFO property.resetDelay: resetDelay = 230 (Fixnum) INFO property.resetTries: resetTries = 1 (Fixnum) INFO Experiment: load system:exp:eventlib INFO Experiment: load system:exp:winlib INFO Experiment: load exercise1.rb INFO Topology: Loaded topology '/tmp/pxe_slice-2014-10-15t02.10.16.594-04.00-topo-success'. INFO Topology: Loaded topology 'system:topo:imaged'. INFO exp: node19-1.grid.orbit-lab.org assigned role of router with GUID: 1 INFO exp: node19-1.grid.orbit-lab.org will also host the GNRS server INFO exp: node19-2.grid.orbit-lab.org assigned role of router with GUID: 2 INFO exp: node20-1.grid.orbit-lab.org assigned role of client with GUID: 101 INFO exp: node20-2.grid.orbit-lab.org assigned role of client with GUID: 102 INFO exp: Definition of resources completed INFO stdlib: Waiting for nodes (Up/Down/Total): 0/4/4 - (still down: node1-2.grid.orbit-lab.org,node2-1.grid.orbit-lab.org,node1-1.grid.orbit-lab.org) [0 sec.] INFO stdlib: Waiting for nodes (Up/Down/Total): 0/4/4 - (still down: node1-2.grid.orbit-lab.org,node2-1.grid.orbit-lab.org,node1-1.grid.orbit-lab.org) [10 sec.] INFO stdlib: Waiting for nodes (Up/Down/Total): 0/4/4 - (still down: node1-2.grid.orbit-lab.org,node2-1.grid.orbit-lab.org,node1-1.grid.orbit-lab.org) [20 sec.] INFO stdlib: Waiting for nodes (Up/Down/Total): 0/4/4 - (still down: node1-2.grid.orbit-lab.org,node2-1.grid.orbit-lab.org,node1-1.grid.orbit-lab.org) [31 sec.] INFO stdlib: Waiting for nodes (Up/Down/Total): 0/4/4 - (still down: node1-2.grid.orbit-lab.org,node2-1.grid.orbit-lab.org,node1-1.grid.orbit-lab.org) [41 sec.] INFO stdlib: Waiting for nodes (Up/Down/Total): 0/4/4 - (still down: node1-2.grid.orbit-lab.org,node2-1.grid.orbit-lab.org,node1-1.grid.orbit-lab.org) [51 sec.] INFO node2-2.grid.orbit-lab.org: Device 'net/w0' reported Not-Associated INFO node1-2.grid.orbit-lab.org: Device 'net/w0' reported Not-Associated INFO stdlib: Waiting for nodes (Up/Down/Total): 2/2/4 - (still down: node2-1.grid.orbit-lab.org,node1-1.grid.orbit-lab.org) [61 sec.] INFO node2-2.grid.orbit-lab.org: Device 'net/w0' reported 76:01:22:6E:DB:FD INFO ALL_UP: Event triggered. Starting the associated tasks. INFO exp: This is my first MobilityFirst experiment INFO exp: Initializing resources INFO exp: Request from Experiment Script: Wait for 20s.... INFO node1-2.grid.orbit-lab.org: Device 'net/w0' reported 76:5D:54:9F:2E:AE INFO node2-1.grid.orbit-lab.org: Device 'net/w0' reported 76:01:22:6E:DB:FD INFO node1-1.grid.orbit-lab.org: Device 'net/w0' reported 76:5D:54:9F:2E:AE INFO exp: Bringing up routers... INFO exp: Request from Experiment Script: Wait for 5s.... INFO exp: Bringing up host stacks... INFO exp: Access the nodes to run a program INFO exp: Request from Experiment Script: Wait for 10000s.... }}} A few comments on the obtained output: * The experiment ID can be seen on one of the first lines, it will be useful for using OML to retrieve the output for OML-enabled experiments. (this experiment does not have any outputs.) * The GUIDs assigned to the nodes can be seen. The routers (nodes19-1 and 19-2 on the grid) have GUID 1 and 2, and the hosts (nodes20-1 and 20-2 on the grid) have GUID 101 and 102. Since we have a simple topology in this experiment, only a single instance of GNRS server is running, which as shown above is hosted by router with GUID 1. === Test The Network === Once the host and router components are up, you can log in to the sender (host identified by GUID 101) and receiver (host identified by GUID 102) host nodes (two separate terminals) and run the 'mfping' application. Run the mfping 'server' specifying the application GUID: {{{ #!sh mfping -s -m 102 -o 101 }}} where "-s" specifies that the host is running as server, "-m" specifies the source guid and "-o" the destination one To run the mfping 'client' {{{ #!sh mfping -c -m 101 -o 102 -n 10 }}} Where "-c" specifies the client is running and "-n" specifies the number of pings between the two nodes. If successfully executed, the client will display some information similar to the following snippet {{{ #!sh root@node1-1:~# mfping -c -m 101 -o 102 -n 10 64 bytes received: seq_n=0, time=25.1470 msec 64 bytes received: seq_n=1, time=23.7070 msec 64 bytes received: seq_n=2, time=20.0559 msec 64 bytes received: seq_n=3, time=24.0371 msec 64 bytes received: seq_n=4, time=23.1831 msec 64 bytes received: seq_n=5, time=20.3069 msec 64 bytes received: seq_n=6, time=24.1379 msec 64 bytes received: seq_n=7, time=19.6230 msec 64 bytes received: seq_n=8, time=20.3931 msec 64 bytes received: seq_n=9, time=20.2239 msec }}} === References === For more information regarding the MobilityFirst project, visit the project page: http://mobilityfirst.winlab.rutgers.edu/ For more information regarding the prototype design and updated status, visit the wiki page: https://mobilityfirst.orbit-lab.org/