wiki:Tutorials/c0WiFi/Tutorial4

Wifi Tutorial: Data logging with OMF

This tutorial shows how to use OMF's data logging feature. The OEDL script used here is built on top of the script used for Access point with multiple clients with slight changes that allow each wifi client to initiate an iperf session on a staggered first-in first-out schedule.

Experiment Script

A brief description of the modify sections is described below.

#
# Tutorial experiment
#
#  omf exec test:exp:tutorial:wifi-ap-iperf2.rb -- --accesspoint node15-1 --client node17-1,node19-1 --interval 60
#
defProperty('accesspoint', 'node1-1', "node ID for access point")
defProperty('client', 'node1-2,node1-3,node1-4', "node ID for client nodes")
defProperty('interval', 60, "Time before starting next client")

defApplication('iperf', 'iperf-oml2') do |app|

  app.version(2, 10, 0)
  app.shortDescription = 'Iperf traffic generator and bandwidth measurement tool'
  app.description = %{Iperf is a traffic generator and bandwidth measurement
tool. It provides generators producing various forms of packet streams and port
for sending these packets via various transports, such as TCP and UDP.
}
  app.path = "/usr/bin/iperf-oml2"

  app.defProperty('interval', 'pause n seconds between periodic bandwidth reports', '-i',
                  :type => :double, :unit => "seconds", :default => '1.')
  app.defProperty('len', 'set length read/write buffer to n (default 8 KB)', '-l',
                  :type => :integer, :unit => "KiBytes")
  app.defProperty('print_mss', 'print TCP maximum segment size (MTU - TCP/IP header)', '-m',
                  :type => :boolean)
  app.defProperty('output', 'output the report or error message to this specified file', '-o',
                  :type => :string)
  app.defProperty('port', 'set server port to listen on/connect to to n (default 5001)', '-p',
                  :type => :integer)
  app.defProperty('udp', 'use UDP rather than TCP', '-u',
                  :type => :boolean,
                  :order => 2)
  app.defProperty('window', 'TCP window size (socket buffer size)', '-w',
                  :type => :integer, :unit => "Bytes")
  app.defProperty('bind', 'bind to <host>, an interface or multicast address', '-B',
                  :type => :string)
  app.defProperty('compatibility', 'for use with older versions does not sent extra msgs', '-C',
                  :type => :boolean)
  app.defProperty('mss', 'set TCP maximum segment size (MTU - 40 bytes)', '-M',
                  :type => :integer, :unit => "Bytes")
  app.defProperty('nodelay', 'set TCP no delay, disabling Nagle\'s Algorithm', '-N',
                  :type => :boolean)
  app.defProperty('IPv6Version', 'set the domain to IPv6', '-V',
                  :type => :boolean)
  app.defProperty('reportexclude', 'exclude C(connection) D(data) M(multicast) S(settings) V(server) reports', '-x',
                  :type => :string, :unit => "[CDMSV]")
  app.defProperty('reportstyle', 'C or c for CSV report, O or o for OML', '-y',
                  :type => :string, :unit => "[CcOo]", :default => "o") # Use OML reporting by default

  app.defProperty('oml-server', 'OML server for collecting data','--oml-server')
  app.defProperty('oml-id', 'ID for this oml client','--oml-id')
  app.defProperty('oml-exp-id', 'ID for this experiment','--oml-exp-id')

  app.defProperty('server', 'run in server mode', '-s',
                  :type => :boolean)

  app.defProperty('bandwidth', 'set target bandwidth to n bits/sec (default 1 Mbit/sec)', '-b',
                  :type => :string, :unit => "Mbps")
  app.defProperty('client', 'run in client mode, connecting to <host>', '-c',
                  :type => :string,
                  :order => 1)
  app.defProperty('dualtest', 'do a bidirectional test simultaneously', '-d',
                  :type => :boolean)
  app.defProperty('num', 'number of bytes to transmit (instead of -t)', '-n',
                  :type => :integer, :unit => "Bytes")
  app.defProperty('tradeoff', 'do a bidirectional test individually', '-r',
                  :type => :boolean)
  app.defProperty('time', 'time in seconds to transmit for (default 10 secs)', '-t',
                  :type => :integer, :unit => "seconds")
  app.defProperty('fileinput', 'input the data to be transmitted from a file', '-F',
                  :type => :string)
  app.defProperty('stdin', 'input the data to be transmitted from stdin', '-I',
                  :type => :boolean)
  app.defProperty('listenport', 'port to recieve bidirectional tests back on', '-L',
                  :type => :integer)
  app.defProperty('parallel', 'number of parallel client threads to run', '-P',
                  :type => :integer)
  app.defProperty('ttl', 'time-to-live, for multicast (default 1)', '-T',
                  :type => :integer,
                  :default => 1)
  app.defProperty('linux-congestion', 'set TCP congestion control algorithm (Linux only)', '-Z',
                  :type => :boolean)

  app.defMeasurement("application"){ |m|
      m.defMetric('pid', :integer, 'Main process identifier')
      m.defMetric('version', :string, 'Iperf version')
      m.defMetric('cmdline', :string, 'Iperf invocation command line')
      m.defMetric('starttime_s', :integer, 'Time the application was received (s)')
      m.defMetric('starttime_us', :integer, 'Time the application was received (us)')
  }

  app.defMeasurement("settings"){ |m|
      m.defMetric('pid', :integer, 'Main process identifier')
      m.defMetric('server_mode', :integer, '1 if in server mode, 0 otherwise')
      m.defMetric('bind_address', :string, 'Address to bind')
      m.defMetric('multicast', :integer, '1 if listening to a Multicast group')
      m.defMetric('multicast_ttl', :integer, 'Multicast TTL if relevant')
      m.defMetric('transport_protocol', :integer, 'Transport protocol (IANA number)')
      m.defMetric('window_size', :integer, 'TCP window size')
      m.defMetric('buffer_size', :integer, 'UDP buffer size')
  }

  app.defMeasurement("connection"){ |m|
      m.defMetric('pid', :integer, 'Main process identifier')
      m.defMetric('connection_id', :integer, 'Connection identifier (socket)')
      m.defMetric('local_address', :string, 'Local network address')
      m.defMetric('local_port', :integer, 'Local port')
      m.defMetric('remote_address', :string, 'Remote network address')
      m.defMetric('remote_port', :integer, 'Remote port')
  }

  app.defMeasurement("transfer"){ |m|
      m.defMetric('pid', :integer, 'Main process identifier')
      m.defMetric('connection_id', :integer, 'Connection identifier (socket)')
      m.defMetric('begin_interval', :double, 'Start of the averaging interval (Iperf timestamp)')
      m.defMetric('end_interval', :double, 'End of the averaging interval (Iperf timestamp)')
      m.defMetric('size', :uint64, 'Amount of transmitted data [Bytes]')
  }

  app.defMeasurement("losses"){ |m|
      m.defMetric('pid', :integer, 'Main process identifier')
      m.defMetric('connection_id', :integer, 'Connection identifier (socket)')
      m.defMetric('begin_interval', :double, 'Start of the averaging interval (Iperf timestamp)')
      m.defMetric('end_interval', :double, 'End of the averaging interval (Iperf timestamp)')
      m.defMetric('total_datagrams', :integer, 'Total number of datagrams')
      m.defMetric('lost_datagrams', :integer, 'Number of lost datagrams')
  }

  app.defMeasurement("jitter"){ |m|
      m.defMetric('pid', :integer, 'Main process identifier')
      m.defMetric('connection_id', :integer, 'Connection identifier (socket)')
      m.defMetric('begin_interval', :double, 'Start of the averaging interval (Iperf timestamp)')
      m.defMetric('end_interval', :double, 'End of the averaging interval (Iperf timestamp)')
      m.defMetric('jitter', :double, 'Average jitter [ms]')
}

app.defMeasurement("packets"){ |m|
    m.defMetric('pid', :integer, 'Main process identifier')
    m.defMetric('connection_id', :integer, 'Connection identifier (socket)')
    m.defMetric('packet_id', :integer, 'Packet sequence number for datagram-oriented protocols')
    m.defMetric('packet_size', :integer, 'Packet size')
    m.defMetric('packet_time_s', :integer, 'Time the packet was processed (s)')
    m.defMetric('packet_time_us', :integer, 'Time the packet was processed (us)')
    m.defMetric('packet_sent_time_s', :integer, 'Time the packet was sent (s) for datagram-oriented protocols')
    m.defMetric('packet_sent_time_us', :integer, 'Time the packet was sent (us) for datagram-oriented protocols')
}

end

defGroup('AP', property.accesspoint) do |node|
  node.addApplication("iperf") do |app|
    app.setProperty('server', true)
  end
  node.net.w0.mode = "master"
  node.net.w0.type = 'g'
  node.net.w0.channel = "7"
  node.net.w0.essid = "TEST1234"
  node.net.w0.ip = "192.168.0.254"
end


res = property.client.value.split(',')
res.each_with_index do |v,i|
  defGroup("client#{i+1}", v) do |node|
    node.addApplication("iperf") do |app|
      app.setProperty('client', "192.168.0.254")
      app.setProperty('time', 99999)
      app.setProperty('interval', 5)
      app.setProperty('reportstyle','O')
      app.setProperty('oml-server', "oml:3003")
      app.setProperty('oml-id', "#{Experiment.ID}")
      app.setProperty('oml-exp-id', "#{Experiment.ID}")
    end
    node.net.w0.mode = "managed"
    node.net.w0.type = 'g'
    node.net.w0.channel = "7"
    node.net.w0.essid = "TEST1234"
    node.net.w0.ip = "192.168.0.#{i+1}"
  end
end


onEvent(:ALL_UP_AND_INSTALLED) do |event|
  info "Wifi Multi Client Iperf Experiment"
  wait 10

  info "Start access point"
  group('AP').startApplications


  res.each_with_index do |v,i|
  info "Start client #{i+1} ==> #{v}"
    group("client#{i+1}").startApplications
    wait property.interval
  end

  res.each_with_index do |v,i|
  info "Stop client #{i+1} ==> #{v}"
    group("client#{i+1}").stopApplications
    wait property.interval
  end

  info "Stop access point"
  group('AP').stopApplications

  info "All my Applications are stopped now."
  Experiment.done
end
  • In the defApplication block we define several measurement points (defMeasurement) along with the relevant metrics (defMetric) that the iperf application measures. These measurement points and metrics are already defined.
    app.defMeasurement("packets"){ |m|
        m.defMetric('pid', :integer, 'Main process identifier')
        m.defMetric('connection_id', :integer, 'Connection identifier (socket)')
        m.defMetric('packet_id', :integer, 'Packet sequence number for datagram-oriented protocols')
        m.defMetric('packet_size', :integer, 'Packet size')
        m.defMetric('packet_time_s', :integer, 'Time the packet was processed (s)')
        m.defMetric('packet_time_us', :integer, 'Time the packet was processed (us)')
        m.defMetric('packet_sent_time_s', :integer, 'Time the packet was sent (s) for datagram-oriented protocols')
        m.defMetric('packet_sent_time_us', :integer, 'Time the packet was sent (us) for datagram-oriented protocols')
    }
    
    In the above measurement point definition, packets is a measurement point that's already defined in the iperf application. Next the metrics are defined with a name (already defined in the iperf app) along with it's object type.
  • Since each client starts an iperf session at staggered time intervals, we define multiple client groups with only one node associated in each group. This will allow us to start iperf on a per group basis.
    res = property.client.value.split(',')
    res.each_with_index do |v,i|
      defGroup("client#{i+1}", v) do |node|
        node.addApplication("iperf") do |app|
          app.setProperty('client', "192.168.0.254")
          app.setProperty('time', 99999)
          app.setProperty('interval', 5)
          app.setProperty('reportstyle','O')
          app.setProperty('oml-server', "oml:3003")
          app.setProperty('oml-id', "#{Experiment.ID}")
          app.setProperty('oml-exp-id', "#{Experiment.ID}")
        end
        node.net.w0.mode = "managed"
        node.net.w0.type = 'g'
        node.net.w0.channel = "7"
        node.net.w0.essid = "TEST1234"
        node.net.w0.ip = "192.168.0.#{i+1}"
      end
    end
    
    Note that the original group definition has been wrapped around with some Ruby code:
    1. Each defGroup is given a unique identifier (ie. client1, client2, etc…)
    2. The ip address is assigned with a Ruby variable.

A few more parameters are passed to the iperf application to enable recording all the defined measurement points:

  1. The oml-server is set to oml:3003. This is send all the measurements to the OML server running on the console.
  2. The oml-id and oml-exp-id is associated with the ID of the experiment so the results service can fetch the recorded data.
  3. The reportstyle is set for OML recording.
  • In the last block of the experiment script, all the groups are started and stopped individually by referencing the previously defined group names.
    onEvent(:ALL_UP_AND_INSTALLED) do |event|
      info "Wifi Multi Client Iperf Experiment"
      wait 10
    
      info "Start access point"
      group('AP').startApplications
    
    
      res.each_with_index do |v,i|
      info "Start client #{i+1} ==> #{v}"
        group("client#{i+1}").startApplications
        wait property.interval
      end
    
      res.each_with_index do |v,i|
      info "Stop client #{i+1} ==> #{v}"
        group("client#{i+1}").stopApplications
        wait property.interval
      end
    
      info "Stop access point"
      group('AP').stopApplications
    
      info "All my Applications are stopped now."
      Experiment.done
    end
    
    The client groups are referenced with the additional Ruby code to allow starting each iperf session and wait before starting the next. The iperf sessions are stopped in a similar way as well.

Executing the Experiment Script

An overview on running experiment scripts on the orbit testbed can be found on the Getting Started Page. In order to run this experiment script, load baseline.ndz image on nodes with wifi cards. Check the Status Page' on the Control Panel to select any particular type of wifi card.

nilanjan@console.grid:~/EXP$ omf load -i baseline.ndz -t node15-1,node16-1,node17-1,node19-1,node20-1

After the nodes have finished imaging, turn the nodes on. The topology used here is system:topo:imaged which selected the prior set of node that were successfully imaged.

nilanjan@console.grid:~/EXP$ omf tell -a on -t system:topo:imaged

To run this experiment with 2 clients nodes:

nilanjan@console.grid:~/EXP$ omf exec test:exp:tutorial:wifi-ap-iperf2.rb -- --accesspoint node15-1 --client node17-1,node19-1 --interval 60

The output of this script should be similar to the following:

 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-15t12.08.29.982-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 wifi-ap-exercise3.rb
 INFO property.accesspoint: accesspoint = "node15-1" (String)
 INFO property.client: client = "node17-1,node19-1" (String)
 INFO property.interval: interval = 60 (Fixnum)
 INFO ALL_UP_AND_INSTALLED: Event triggered. Starting the associated tasks.
 INFO exp: Wifi Multi Client Iperf Experiment
 INFO exp: Request from Experiment Script: Wait for 10s....
 INFO node17-1.grid.orbit-lab.org: Device 'net/w0' reported Not-Associated
 INFO node19-1.grid.orbit-lab.org: Device 'net/w0' reported Not-Associated
 INFO exp: Start access point
 INFO exp: Start client 1 ==> node17-1
 INFO exp: Request from Experiment Script: Wait for 60s....
 INFO node17-1.grid.orbit-lab.org: Device 'net/w0' reported 00:60:B3:AC:2B:D5
 INFO node19-1.grid.orbit-lab.org: Device 'net/w0' reported 00:60:B3:AC:2B:D5
 INFO exp: Start client 2 ==> node19-1
 INFO exp: Request from Experiment Script: Wait for 60s....
 INFO exp: Stop client 1 ==> node17-1
 INFO exp: Request from Experiment Script: Wait for 60s....
 INFO exp: Stop client 2 ==> node19-1
 INFO exp: Request from Experiment Script: Wait for 60s....
 INFO exp: Stop access point
 INFO exp: All my Applications are stopped now.
 INFO EXPERIMENT_DONE: Event triggered. Starting the associated tasks.
 INFO NodeHandler:
 INFO NodeHandler: Shutting down experiment, please wait...
 INFO NodeHandler:
 INFO run: Experiment default_slice-2014-10-15t12.08.29.982-04.00 finished after 4:20

Experiment Results

The detailed output from the experiment script is stored in the default slice*.log file under the /tmp directory of the console. However in this case the iperf results are store separately in an OML database that is accessible via the Result Service. Please check here for more details to access the database contents.

Last modified 3 years ago Last modified on 10/22/14 12:03:10