Changes between Initial Version and Version 1 of Old/Tutorials/MobilityFirst_ORBIT_Tutorial


Ignore:
Timestamp:
Sep 21, 2014, 2:08:09 AM (10 years ago)
Author:
wontoniii
Comment:

Legend:

Unmodified
Added
Removed
Modified
  • Old/Tutorials/MobilityFirst_ORBIT_Tutorial

    v1 v1  
     1= ORBIT Testbed Tutorial =
     2
     3== Pre-requisites ==
     4
     5* Start by registering for an account on the [https://www.orbit-lab.org ORBIT testbed site]
     6* If a first time user, follow the 'How to get started' guide to get familiar with the testbed basics
     7* Read up on the [[|MobilityFirst software release primer]] for a quick introduction to the software
     8
     9== Reserve Resources ==
     10
     11ORBIT testbed offers multiple domains for experimentation, including several small sized (between 2 and 12 nodes) sandbox environments, and a larger 20x20 two-dimensional grid of programmable radio nodes. Depending on the size of the experiment, you can pick and reserve an appropriate domain through the [https://www.orbit-lab.org/schedule testbed scheduler].
     12
     13In the following tutorial exercises, we will assume availability of a minimum of 4 nodes that can (at least) be interconnected in a linear topology.
     14
     15== Exercise 1: Simple MobilityFirst Network Deployment and Test ==
     16
     17=== Objective ===
     18
     19In this exercise we will establish a simple topology consisting of Mobilityfirst routers, hosts and applications, deploy the software components onto physical nodes, and run a end-to-end 'ping' application.
     20
     21=== Topology ===
     22
     23In this exercise we will start with 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:
     24
     25{{{
     26#!sh
     27Host1 ---- MFR1 ---- MFR2 ---- Host2
     28}}}
     29
     30=== MobilityFirst ORBIT Image ===
     31
     32The 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've had a chance to test compatibility. Once logged into the console of the reserved domain, the image can be loaded onto a node using the command line:
     33
     34{{{
     35#!sh
     36
     37omf load -i 'mf-release-latest.ndz' -t 'node1-1.sb9.orbit-lab.org'
     38}}}
     39
     40where the above assumes you are attempting to image node1-1 in the 'sb9.orbit-lab.org' domain (Sandbox 9). You could also just use 'node1-1' without the domain part - assumed from the console you issue the command from. So for installing the image across 4 nodes - node1-1, node1-2, node1-3, node1-4 - the following can be used:
     41
     42{{{
     43#!sh
     44omf load -i 'mf-release-latest.ndz' -t 'node1-1,node1-2,node1-3,node1-4'
     45}}}
     46
     47The imaging should be done for each node to be used in our network deployment, and can be done in a single shot by providing multiple comma separated hostnames within the '-t' topology argument above.
     48
     49=== Deploy Network ===
     50
     51Software 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 as shown below:
     52
     53{{{
     54#!sh
     55                 mfping                               mfping
     56Software         mfapi lib     gnrs      gnrs         mfapi lib
     57                 mfstack       click     click        mfstack
     58                                 
     59Topology         Host1 ------- MFR1 ---- MFR2 ------- Host2
     60
     61ORBIT Node       node1-1       node1-2   node1-3      node1-4
     62}}}
     63
     64The entire script is available as part of the tutorial package as orbit/tutorial/scripts/exercise1.rb
     65
     66The following sub-sections dissect the key parts of this script:
     67
     68==== Software Component Specification ====
     69
     70The following snippet shows the specification of the MobilityFirst router along with the required arguments:
     71
     72{{{
     73#!ruby
     74
     75defApplication('MF-Router', 'router') {|app|
     76    app.shortDescription = "Click-based MobilityFirst Router"
     77    app.path = ";/usr/local/bin/click"
     78
     79    # click options
     80    app.defProperty('num_threads', 'number of threads', "--threads",{:type =>; :integer, :mandatory => true, :default => 4, :order => 1})
     81    app.defProperty('ctrl_port', 'port for Click control socket', "--port",{:type => :integer, :order => 2})
     82
     83    # click config file
     84    app.defProperty('config_file', 'Click configuration file', nil,{:type => :string,:mandatory=> true})
     85
     86    # keyword parameters used in click config file
     87    app.defProperty('my_GUID', 'router GUID', "my_GUID",{:type => :string, :mandatory => true})
     88    app.defProperty('topo_file', 'path to topology file', "topo_file",{:type => :string, :mandatory => true})
     89    app.defProperty('core_dev', 'core network interface', "core_dev",{:type => :string,:mandatory=> true})
     90    app.defProperty('GNRS_server_ip', 'IP of local GNRS server', "GNRS_server_ip",{:type => :string,:mandatory=> true})
     91    app.defProperty('GNRS_server_port', 'Port of GNRS server', "GNRS_server_port",{:type => :string,:mandatory=> true})
     92    app.defProperty('GNRS_listen_ip', 'IP to listen for GNRS response', "GNRS_server_ip",{:type => :string,:default=> "0.0.0.0"})
     93    app.defProperty('GNRS_listen_port', 'port to listen for GNRS response', "GNRS_server_port",{:type => :string,:default=> 10001})
     94    app.defProperty('edge_dev', 'edge network interface', "edge_dev",{:type => :string,:mandatory=> true})
     95    app.defProperty('edge_dev_ip', 'IP assigned to edge interface', "edge_dev_ip",{:type => :string,:mandatory=> true})
     96}
     97}}}
     98
     99As seen above, the router is configured with both 'core' and 'edge' interfaces. The core interfaces connect routers towards the core of the network, while the edge interface enables hosts to connect and access the MobilityFirst network.
     100
     101Also seen above is the GNRS service related arguments that specify which server (IP and port) the router should use for in-network name resolution purpose, both for sending requests and to receive responses. By default it will listen on all interfaces and port 10001.
     102
     103==== Setting up the Software Node Groups ====
     104
     105The 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 and execute commands across all nodes in the group.
     106
     107{{{
     108#!ruby
     109for i in 1..num_routers
     110        defTopology("topo:router_#{i}") { |t|
     111                aNode = routersTopo.getNodeByIndex(i-1)
     112                t.addNode(aNode)
     113                print "Adding node: ", aNode, " router with GUID: #{i+num_hosts}\n"
     114        }
     115
     116        defGroup("router_#{i}", "topo:router_#{i}") {|node|
     117                node.addApplication('MF-Router') {|app|
     118                    app.setProperty('my_GUID', router_guid[i-1])
     119                    app.setProperty('topo_file', topo_file)
     120                    app.setProperty('conf_file', click_conf)
     121                    app.setProperty('core_dev', core_dev)
     122                    app.setProperty('gnrs_server_ip', router_gnrs_if_ip[i-1])
     123                    app.setProperty('gnrs_server_port', gnrs_server_port)
     124                    app.setProperty('edge_dev', edge_dev)
     125                    app.setProperty('edge_dev_ip', router_wifi_if_ip[i-1])
     126                }
     127
     128                node.addApplication('MF-GNRS') {|app|
     129                    app.setProperty('config_file', gnrs_conf_file)
     130                }
     131        }
     132end
     133}}}
     134
     135==== Configuring the Network Interfaces ====
     136
     137{{{
     138#!ruby
     139    # clean up and initialize networking for routers
     140    for i in 1..num_routers
     141        # click router cleanup
     142        group("router_#{i}").exec("killall -9 click")
     143        # gnrsd cleanup
     144        group("router_#{i}").exec("killall -9 java")
     145
     146        # bring up gnrs interface if required - in sb4 we use ctrl iface
     147        # group("router_#{i}").exec("ifconfig " + core_dev + " " + router_gnrs_if_ip + " netmask " + router_gnrs_if_netmask)
     148               
     149        #bring up edge wifi interface
     150        group("router_#{i}").exec("modprobe ath5k")
     151        group("router_#{i}").exec("ifconfig " + edge_dev + " " + router_wifi_if_ip + " netmask " + wifi_netmask)
     152    end         
     153                   
     154    #clean up and initialize networking for hosts
     155    for i in 1..num_hosts
     156        group("host_#{i}").exec("killall -9 mfstack")
     157
     158        #bring up edge wifi interface
     159        group("host_#{i}").exec("modprobe ath5k")
     160        group("host_#{i}").exec("ifconfig " + edge_dev + " " + host_wifi_if_ip + " netmask " + wifi_netmask)
     161    end
     162}}}
     163
     164==== Starting the MobilityFirst Components ====
     165
     166The following snippet shows the starting of the router software and the gnrs servers on the two router nodes:
     167
     168{{{
     169#!ruby
     170    # bring up the routers (along with gnrs servers)
     171    print "Bringing up routers...\n"
     172    for i in 1..num_routers
     173        group("router_#{i}").startApplications
     174    end
     175}}}
     176
     177=== Test The Network ===
     178
     179Once the host and router components are up, you can log in to the sender and receiver host nodes (two separate terminals) and run the 'mfping' application.
     180
     181Run the mfping 'server' specifying the application GUID:
     182
     183{{{
     184#!sh
     185mfping -S <my-GUID>
     186}}}
     187
     188To run the mfping 'client'
     189
     190{{{
     191#!sh
     192mfping -C <my-GUID> <server-GUID> -i <interval_secs> -c <count>
     193}}}
     194
     195You should see an out at the client similar to below:
     196
     197{{{
     198#!sh
     199
     200}}}
     201
     202== Exercise 2: Measuring Performance of a MobilityFirst Router ==
     203
     204=== Objective ===
     205
     206In this exercise, we will try to drive synthetic traffic through the router and measure key performance characteristics such as throughput and forwarding latency. Since MobilityFirst presents a hop-by-hop block data transport, we can vary the unit size of the data block and observe it's impact on the performance. We will also try to visualize the performance results using OMF's result service.
     207
     208=== Deploy the Network ===
     209
     210We will assume the network described and initialized in Exercise 1 is up and functional. Instead of the mfping application that was run manually, we will extend the OMF script to add the mfperf application that will drive the performance measurement of the router.
     211
     212The entire script is available as part of the tutorial package as orbit/tutorial/scripts/exercise2.rb
     213
     214The key extensions over previous script are briefly discussed below:
     215
     216==== Setting up the 'mfperf Application' ====
     217
     218The following snippet from the script shows the code added to set up the mfperf application and its arguments:
     219
     220{{{
     221#!ruby
     222defApplication('test:app:mf:mfperf', 'mfperf') do |a|
     223
     224  a.path = "/usr/local/bin/mfperf"
     225  app.appPackage = "http://mobilityfirst.winlab.rutgers.edu/mf-orbit-tutorial.tar"
     226  a.version(0, 9, 1)
     227  a.shortDescription = "MF protocol performance benchmark tool"
     228  a.description = "This is targeted to be similar to the iperf benchmarking tool available for IP protocol. It generates MobilityFirst block packet traffic via the MF socket API and can be used to benchmark performance of MF routers and protocol stack implementations."
     229
     230  # Define the properties that can be configured for this application
     231  #
     232  # syntax: defProperty(name = :mandatory, description = nil, parameter = nil, options = {})
     233  #
     234  a.defProperty('generator', 'Type of packet generator to use (cbr)', '-g', {:type => :string, :dynamic => false})
     235  a.defProperty('dst_GUID', 'GUID of the Destination', '--dst_GUID', {:type => :string, :dynamic => false})
     236  a.defProperty('my_GUID', 'GUID of this Source application', '--my_GUID', {:type => :string, :dynamic => false})
     237  a.defProperty("cbr:size", "Size of data block [bytes]", '--cbr:size', {:dynamic => true, :type => :integer})
     238  a.defProperty("cbr:rate", "Data rate of the flow [kbps]", '--cbr:rate', {:dynamic => true, :type => :integer})
     239
     240  # Define the Measurement Points and associated metrics that are available for this application
     241  #
     242  a.defMeasurement('msg_out') do |m|
     243    m.defMetric('ts',:float)
     244    m.defMetric('msg_no',:long)
     245    m.defMetric('msg_length',:long)
     246    m.defMetric('dst_GUID',:string)
     247  end
     248end
     249}
     250}}}
     251
     252As seen above, the configuration also covers the set up of OML measurement points that we will use to track and visualize the forwarding performance of MFRs.
     253
     254==== Running the Benchmark Application ====
     255
     256The following snippet shows how the exercise runs the mfperf application and also changes the block size dynamically
     257
     258==== Visualizing the Performance Data ====
     259
     260'''Method 1:''' OMF framework supports a result service that allows experimenters to query data stored using the OML measurement framework. The query is performed over the web and requires the you know the hostname and port where the result service runs, and the ''experiment ID'' associated with this experiment - this is obtained from the output following the execution of the control script.
     261
     262The result service supports either dumping of the entire database or a SQL-like querying option to selectively retrieve required measurement data. The below HTTP request shows an example query to retrieve certain fields of our above defined measurement point in the mfperf application:
     263
     264{{{
     265#!sh
     266wget "http://<hostname>:<port>/result/queryDatabase?expID=testing_slice-2010-09-03t09.41.43+10.00&format=csv&query=select ts,msg_no,msg_length from msg_out"  -O msg_out.csv
     267}}}
     268
     269Note that the URL used in wget, in particular the arguments, may require to be encoded to unambiguously represent special characters when using the HTTP protocol.
     270
     271The downloaded data can now be easily visualized using a tool such as gnuplot. You can find a helper script in the tutorial package that plots they key performance data downloaded using the above query.
     272
     273'''Method 2:''' Alternatively, the performance data may also be visualized using ''omf-web'', OMF's web-based visualization service. It also works in concert with the result service referenced in Method 1, and makes available a variety of graph widgets to visualize live-experiment data logged using OML. Detailed documentation on the installation and usage of omf-web can be found on the [https://github.com/mytestbed/omf_web omf-web github site].
     274
     275Since this is installed on all ORBIT domains, we will only concern ourselves with defining the widget configuration required to bring up the live graphs for the performance data we are logging. Below is the contents of the simple two widget configuration file available with this tutorial package:
     276
     277{{{
     278#!yaml
     279title: MobilityFirst Data Transfer Performance
     280
     281# Port number the omf-web service should run at
     282port: 4041
     283
     284# Root url of AM result2 service
     285result2_server: http://oml:5054
     286
     287# Define tabs, widgets for visualisation below
     288#
     289tabs:
     290  # Data transfer throughput
     291  mfperf_tput:
     292    # Line chart widget, need to define columns in mapping section.   
     293    - name: OML TS SERVER
     294      type: line_chart
     295      data: msg_out
     296      mapping:
     297        # x-axis, y-axis and group_by.
     298        x: ts
     299        y: kbytes_per_sec
     300        group_by: oml_sender_id
     301  mfperf_rtt:
     302    # Line chart widget, need to define columns in mapping section.   
     303    - name: OML TS SERVER
     304      type: line_chart
     305      data: msg_out
     306      mapping:
     307        # x-axis, y-axis and group_by.
     308        x: msg_length
     309        y: transfer_time
     310        group_by: oml_sender_id
     311}}}
     312
     313To bring up the visualization, start the basic omf-web service with the configuration file argument:
     314
     315{{{
     316#!sh
     317omf-web-basic -c <config_yaml_file> <experiment id>
     318}}}
     319
     320== Exercise 3: Socket Programming using New MobilityFirst NetAPI ==
     321
     322=== Objective: ===
     323
     324In this exercise we will learn to write, compile, and run a simple content distribution application using MobilityFirst's new socket API. We will then modify the program to utilize MobilityFirst's native support for point to multi-point delivery services such as anycast and multicast to enable more flexible delivery options.
     325
     326=== Develop Sender and Receiver Applications with MF Socket API ===
     327
     328The following Java application shows the key pieces of the sender application that sends a file to a receiver known to the sender by its GUID and to then receive a confirmation message from the receiver:
     329
     330{{{
     331#!java
     332//Simple class used to test the java api
     333
     334
     335//jmfapi needs to be in the classpath
     336import java.io.*;
     337import java.util.*;
     338import java.nio.file.*;
     339import edu.rutgers.winlab.jmfapi.*;
     340import edu.rutgers.winlab.jmfapi.GUID;
     341
     342class Sender{
     343    private static void usage(){
     344        System.out.println("Usage:");
     345        System.out.println("sender <my_GUID> <file_to_send> <dst_GUID>");
     346    }
     347    public static void main(String []argv){
     348        if(argv.length < 3){
     349            usage();
     350            return;
     351        }
     352        String scheme = "basic";
     353        GUID srcGUID = null, dstGUID;
     354        srcGUID = new GUID(Integer.parseInt(argv[0]));
     355        Path file = FileSystems.getDefault().getPath(argv[1]);
     356        dstGUID = new GUID(Integer.parseInt(argv[2]));
     357        JMFAPI sender = new JMFAPI();
     358        try{
     359            if(srcGUID!=null) sender.jmfopen(scheme, srcGUID);
     360            else sender.jmfopen(scheme);
     361            byte[] fileArray;
     362            try {
     363                fileArray = Files.readAllBytes(file);
     364            } catch (IOException e){
     365                System.out.println("ERROR");
     366                return;
     367            }
     368            byte[] tempArray;
     369            int ret, read = 0;
     370            while(fileArray.length - read>=1000000){
     371                tempArray = Arrays.copyOfRange(fileArray, 0, 999999);
     372                sender.jmfsend(tempArray,1000000, dstGUID);
     373            }
     374            tempArray = Arrays.copyOfRange(fileArray, 0, fileArray.length - read - 1);
     375            sender.jmfsend(tempArray,fileArray.length - read, dstGUID);
     376            sender.jmfclose();
     377            System.out.println("Transmitted file");
     378
     379                       //TODO receive confirmation
     380
     381            System.out.println("Received confirmation");
     382
     383        } catch (JMFException e){
     384            System.out.println(e.toString());
     385        }
     386    }
     387}
     388}}}
     389
     390The following shows the corresponding receiver code:
     391
     392{{{
     393#!java
     394//Simple class used to test the java api
     395
     396import java.io.*;
     397import java.util.*;
     398import java.nio.file.*;
     399import edu.rutgers.winlab.jmfapi.*;
     400
     401class Receiver{
     402    private static void usage(){
     403        System.out.println("Usage:");
     404        System.out.println("receiver [<my_GUID>]");
     405    }
     406    public static void main(String []argv){
     407        String scheme = "basic";
     408        GUID srcGUID = null;
     409        int i = 0;
     410        if(argv.length == 1) srcGUID = new GUID(Integer.parseInt(argv[0]));
     411        Path file = FileSystems.getDefault().getPath("temp.txt");
     412        try{
     413            Files.createFile(file);
     414        } catch(IOException e){
     415            try{
     416                Files.delete(file);
     417                Files.createFile(file);
     418            } catch(IOException e2){
     419                return;
     420            }
     421        }
     422        byte[] buf = new byte[1000000];
     423        int ret;
     424        JMFAPI receiver = new JMFAPI();
     425        try{
     426            if(srcGUID!=null) receiver.jmfopen(scheme, srcGUID);
     427            else receiver.jmfopen(scheme);
     428            while(i < 24954287){
     429                ret = receiver.jmfrecv_blk(null, buf, 1000000);
     430                try{
     431                    Files.write(file, buf, StandardOpenOption.APPEND);
     432                } catch (IOException e){
     433                    System.out.println(e.toString());
     434                }
     435                i += ret;
     436
     437            }
     438                System.out.println("Received file");
     439
     440                        //TODO send confirmation
     441
     442            receiver.jmfclose();
     443        } catch (JMFException e){
     444            System.out.println(e.toString());
     445        }
     446
     447    }
     448}
     449}}}
     450
     451==== Test Sender/Receiver Applications ====
     452
     453==== Add Second Receiver End Point to Topology ====
     454
     455==== Modify Delivery Service Option to Add Multi-point Delivery ====
     456
     457The following code snippet shows the modified portion of the code to request Multicast delivery option while transfering the file:
     458
     459{{{
     460#!java
     461
     462}}}