wiki:Old/Tutorials/MobilityFirst_ORBIT_Tutorial

ORBIT Testbed Tutorial

Pre-requisites

Reserve Resources

ORBIT 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 testbed scheduler.

In the following tutorial exercises, we will assume availability of a minimum of 4 nodes that can (at least) be interconnected in a linear topology.

Exercise 1: Simple MobilityFirst Network Deployment and Test

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 a end-to-end 'ping' application.

Topology

In 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:

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'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:

omf load -i 'mf-release-latest.ndz' -t 'node1-1.sb9.orbit-lab.org'

where 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:

omf load -i 'mf-release-latest.ndz' -t 'node1-1,node1-2,node1-3,node1-4'

The 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.

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 as shown below:

                 mfping                               mfping 
Software         mfapi lib     gnrs      gnrs         mfapi lib
                 mfstack       click     click        mfstack
                                 
Topology         Host1 ------- MFR1 ---- MFR2 ------- Host2

ORBIT Node       node1-1       node1-2   node1-3      node1-4

The entire script is available as part of the tutorial package as orbit/tutorial/scripts/exercise1.rb

The following sub-sections dissect the key parts of this script:

Software Component Specification

The following snippet shows the specification of the MobilityFirst router along with the required arguments:

defApplication('MF-Router', 'router') {|app|
    app.shortDescription = "Click-based MobilityFirst Router"
    app.path = ";/usr/local/bin/click"

    # click options
    app.defProperty('num_threads', 'number of threads', "--threads",{:type =>; :integer, :mandatory => true, :default => 4, :order => 1})
    app.defProperty('ctrl_port', 'port for Click control socket', "--port",{:type => :integer, :order => 2})

    # click config file 
    app.defProperty('config_file', 'Click configuration file', nil,{:type => :string,:mandatory=> true})

    # keyword parameters used in click config file
    app.defProperty('my_GUID', 'router GUID', "my_GUID",{:type => :string, :mandatory => true})
    app.defProperty('topo_file', 'path to topology file', "topo_file",{:type => :string, :mandatory => true})
    app.defProperty('core_dev', 'core network interface', "core_dev",{:type => :string,:mandatory=> true})
    app.defProperty('GNRS_server_ip', 'IP of local GNRS server', "GNRS_server_ip",{:type => :string,:mandatory=> true})
    app.defProperty('GNRS_server_port', 'Port of GNRS server', "GNRS_server_port",{:type => :string,:mandatory=> true})
    app.defProperty('GNRS_listen_ip', 'IP to listen for GNRS response', "GNRS_server_ip",{:type => :string,:default=> "0.0.0.0"})
    app.defProperty('GNRS_listen_port', 'port to listen for GNRS response', "GNRS_server_port",{:type => :string,:default=> 10001})
    app.defProperty('edge_dev', 'edge network interface', "edge_dev",{:type => :string,:mandatory=> true})
    app.defProperty('edge_dev_ip', 'IP assigned to edge interface', "edge_dev_ip",{:type => :string,:mandatory=> true})
}

As 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.

Also 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.

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 and execute commands across all nodes in the group.

for i in 1..num_routers
        defTopology("topo:router_#{i}") { |t|
                aNode = routersTopo.getNodeByIndex(i-1)
                t.addNode(aNode)
                print "Adding node: ", aNode, " router with GUID: #{i+num_hosts}\n"
        }

        defGroup("router_#{i}", "topo:router_#{i}") {|node|
                node.addApplication('MF-Router') {|app|
                    app.setProperty('my_GUID', router_guid[i-1])
                    app.setProperty('topo_file', topo_file)
                    app.setProperty('conf_file', click_conf)
                    app.setProperty('core_dev', core_dev)
                    app.setProperty('gnrs_server_ip', router_gnrs_if_ip[i-1])
                    app.setProperty('gnrs_server_port', gnrs_server_port)
                    app.setProperty('edge_dev', edge_dev)
                    app.setProperty('edge_dev_ip', router_wifi_if_ip[i-1])
                }

                node.addApplication('MF-GNRS') {|app|
                    app.setProperty('config_file', gnrs_conf_file)
                }
        }
end

Configuring the Network Interfaces

    # 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")

        # bring up gnrs interface if required - in sb4 we use ctrl iface
        # group("router_#{i}").exec("ifconfig " + core_dev + " " + router_gnrs_if_ip + " netmask " + router_gnrs_if_netmask)
                
        #bring up edge wifi interface 
        group("router_#{i}").exec("modprobe ath5k")
        group("router_#{i}").exec("ifconfig " + edge_dev + " " + router_wifi_if_ip + " netmask " + wifi_netmask)
    end         
                    
    #clean up and initialize networking for hosts
    for i in 1..num_hosts
        group("host_#{i}").exec("killall -9 mfstack")

        #bring up edge wifi interface 
        group("host_#{i}").exec("modprobe ath5k")
        group("host_#{i}").exec("ifconfig " + edge_dev + " " + host_wifi_if_ip + " netmask " + wifi_netmask)
    end

Starting the MobilityFirst Components

The following snippet shows the starting of the router software and the gnrs servers on the two router nodes:

    # bring up the routers (along with gnrs servers)
    print "Bringing up routers...\n"
    for i in 1..num_routers
        group("router_#{i}").startApplications
    end

Test The Network

Once 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.

Run the mfping 'server' specifying the application GUID:

mfping -S <my-GUID>

To run the mfping 'client'

mfping -C <my-GUID> <server-GUID> -i <interval_secs> -c <count>

You should see an out at the client similar to below:

Exercise 2: Measuring Performance of a MobilityFirst Router

Objective

In 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.

Deploy the Network

We 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.

The entire script is available as part of the tutorial package as orbit/tutorial/scripts/exercise2.rb

The key extensions over previous script are briefly discussed below:

Setting up the 'mfperf Application'

The following snippet from the script shows the code added to set up the mfperf application and its arguments:

defApplication('test:app:mf:mfperf', 'mfperf') do |a|

  a.path = "/usr/local/bin/mfperf" 
  app.appPackage = "http://mobilityfirst.winlab.rutgers.edu/mf-orbit-tutorial.tar" 
  a.version(0, 9, 1)
  a.shortDescription = "MF protocol performance benchmark tool" 
  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." 

  # Define the properties that can be configured for this application
  # 
  # syntax: defProperty(name = :mandatory, description = nil, parameter = nil, options = {})
  #
  a.defProperty('generator', 'Type of packet generator to use (cbr)', '-g', {:type => :string, :dynamic => false})
  a.defProperty('dst_GUID', 'GUID of the Destination', '--dst_GUID', {:type => :string, :dynamic => false})
  a.defProperty('my_GUID', 'GUID of this Source application', '--my_GUID', {:type => :string, :dynamic => false})
  a.defProperty("cbr:size", "Size of data block [bytes]", '--cbr:size', {:dynamic => true, :type => :integer})
  a.defProperty("cbr:rate", "Data rate of the flow [kbps]", '--cbr:rate', {:dynamic => true, :type => :integer})

  # Define the Measurement Points and associated metrics that are available for this application
  #
  a.defMeasurement('msg_out') do |m|
    m.defMetric('ts',:float)
    m.defMetric('msg_no',:long)
    m.defMetric('msg_length',:long)
    m.defMetric('dst_GUID',:string)
  end
end
}

As 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.

Running the Benchmark Application

The following snippet shows how the exercise runs the mfperf application and also changes the block size dynamically

Visualizing the Performance Data

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.

The 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:

wget "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

Note 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.

The 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.

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 omf-web github site.

Since 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:

title: MobilityFirst Data Transfer Performance

# Port number the omf-web service should run at
port: 4041

# Root url of AM result2 service
result2_server: http://oml:5054

# Define tabs, widgets for visualisation below
#
tabs:
  # Data transfer throughput
  mfperf_tput:
    # Line chart widget, need to define columns in mapping section.   
    - name: OML TS SERVER
      type: line_chart
      data: msg_out
      mapping:
        # x-axis, y-axis and group_by.
        x: ts
        y: kbytes_per_sec
        group_by: oml_sender_id
  mfperf_rtt:
    # Line chart widget, need to define columns in mapping section.   
    - name: OML TS SERVER
      type: line_chart
      data: msg_out
      mapping:
        # x-axis, y-axis and group_by.
        x: msg_length
        y: transfer_time
        group_by: oml_sender_id

To bring up the visualization, start the basic omf-web service with the configuration file argument:

omf-web-basic -c <config_yaml_file> <experiment id>

Exercise 3: Socket Programming using New MobilityFirst NetAPI

Objective:

In 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.

Develop Sender and Receiver Applications with MF Socket API

The 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:

//Simple class used to test the java api


//jmfapi needs to be in the classpath
import java.io.*;
import java.util.*;
import java.nio.file.*;
import edu.rutgers.winlab.jmfapi.*;
import edu.rutgers.winlab.jmfapi.GUID;

class Sender{
    private static void usage(){
        System.out.println("Usage:");
        System.out.println("sender <my_GUID> <file_to_send> <dst_GUID>");
    }
    public static void main(String []argv){
        if(argv.length < 3){
            usage();
            return;
        }
        String scheme = "basic";
        GUID srcGUID = null, dstGUID;
        srcGUID = new GUID(Integer.parseInt(argv[0]));
        Path file = FileSystems.getDefault().getPath(argv[1]);
        dstGUID = new GUID(Integer.parseInt(argv[2]));
        JMFAPI sender = new JMFAPI();
        try{
            if(srcGUID!=null) sender.jmfopen(scheme, srcGUID);
            else sender.jmfopen(scheme);
            byte[] fileArray;
            try {
                fileArray = Files.readAllBytes(file);
            } catch (IOException e){
                System.out.println("ERROR");
                return;
            }
            byte[] tempArray;
            int ret, read = 0;
            while(fileArray.length - read>=1000000){
                tempArray = Arrays.copyOfRange(fileArray, 0, 999999);
                sender.jmfsend(tempArray,1000000, dstGUID);
            }
            tempArray = Arrays.copyOfRange(fileArray, 0, fileArray.length - read - 1);
            sender.jmfsend(tempArray,fileArray.length - read, dstGUID);
            sender.jmfclose();
            System.out.println("Transmitted file");

                       //TODO receive confirmation

            System.out.println("Received confirmation");

        } catch (JMFException e){
            System.out.println(e.toString());
        }
    }
}

The following shows the corresponding receiver code:

//Simple class used to test the java api

import java.io.*;
import java.util.*;
import java.nio.file.*;
import edu.rutgers.winlab.jmfapi.*;

class Receiver{
    private static void usage(){
        System.out.println("Usage:");
        System.out.println("receiver [<my_GUID>]");
    }
    public static void main(String []argv){
        String scheme = "basic";
        GUID srcGUID = null;
        int i = 0;
        if(argv.length == 1) srcGUID = new GUID(Integer.parseInt(argv[0]));
        Path file = FileSystems.getDefault().getPath("temp.txt");
        try{
            Files.createFile(file);
        } catch(IOException e){
            try{
                Files.delete(file);
                Files.createFile(file);
            } catch(IOException e2){
                return;
            }
        }
        byte[] buf = new byte[1000000];
        int ret;
        JMFAPI receiver = new JMFAPI();
        try{
            if(srcGUID!=null) receiver.jmfopen(scheme, srcGUID);
            else receiver.jmfopen(scheme);
            while(i < 24954287){
                ret = receiver.jmfrecv_blk(null, buf, 1000000);
                try{
                    Files.write(file, buf, StandardOpenOption.APPEND);
                } catch (IOException e){
                    System.out.println(e.toString());
                }
                i += ret;

            }
                System.out.println("Received file");

                        //TODO send confirmation

            receiver.jmfclose();
        } catch (JMFException e){
            System.out.println(e.toString());
        }

    }
}

Test Sender/Receiver Applications

Add Second Receiver End Point to Topology

Modify Delivery Service Option to Add Multi-point Delivery

The following code snippet shows the modified portion of the code to request Multicast delivery option while transfering the file:

Last modified 10 years ago Last modified on Sep 21, 2014, 2:08:09 AM
Note: See TracWiki for help on using the wiki.