Changes between Version 11 and Version 12 of Tutorials/c0WiFi/Tutorial3


Ignore:
Timestamp:
May 25, 2016, 8:57:29 PM (9 years ago)
Author:
jkol
Comment:

Legend:

Unmodified
Added
Removed
Modified
  • Tutorials/c0WiFi/Tutorial3

    v11 v12  
    1313=== Experiment Script ===
    1414
    15 The experiment script is shown below. Near the top of the script we define an application header for iperf.  After which we define two groups - the first group (AP) consists of a single node to be configured as an access point. The second group (client) can be a single or multiple node(s) that will be configured as !WiFi clients. Both groups add the iperf application and configure the wireless interfaces accordingly. At the bottom of the script, the nodes in all the defined groups are brought up and the applications are executed.
    16 
    17 {{{
    18      1  #
    19      2  # Tutorial experiment
    20      3  #
    21      4  defProperty('accesspoint', 'node1-1', "node ID for access point")
    22      5  defProperty('client', 'node1-2', "node ID for client nodes")
    23      6  defProperty('duration', 60, "Seconds to run the application.")
    24      7
    25      8  defApplication('iperf', 'iperf-oml2') do |app|
    26      9
    27     10    app.version(2, 10, 0)
    28     11    app.shortDescription = 'Iperf traffic generator and bandwidth measurement tool'
    29     12    app.description = %{Iperf is a traffic generator and bandwidth measurement
    30     13  tool. It provides generators producing various forms of packet streams and port
    31     14  for sending these packets via various transports, such as TCP and UDP.
    32     15  }
    33     16    app.path = "/usr/bin/iperf-oml2"
    34     17
    35     18    app.defProperty('interval', 'pause n seconds between periodic bandwidth reports', '-i',
    36     19                    :type => :double, :unit => "seconds", :default => '1.')
    37     20    app.defProperty('len', 'set length read/write buffer to n (default 8 KB)', '-l',
    38     21                    :type => :integer, :unit => "KiBytes")
    39     22    app.defProperty('print_mss', 'print TCP maximum segment size (MTU - TCP/IP header)', '-m',
    40     23                    :type => :boolean)
    41     24    app.defProperty('output', 'output the report or error message to this specified file', '-o',
    42     25                    :type => :string)
    43     26    app.defProperty('port', 'set server port to listen on/connect to to n (default 5001)', '-p',
    44     27                    :type => :integer)
    45     28    app.defProperty('udp', 'use UDP rather than TCP', '-u',
    46     29                    :type => :boolean,
    47     30                    :order => 2)
    48     31    app.defProperty('window', 'TCP window size (socket buffer size)', '-w',
    49     32                    :type => :integer, :unit => "Bytes")
    50     33    app.defProperty('bind', 'bind to <host>, an interface or multicast address', '-B',
    51     34                    :type => :string)
    52     35    app.defProperty('compatibility', 'for use with older versions does not sent extra msgs', '-C',
    53     36                    :type => :boolean)
    54     37    app.defProperty('mss', 'set TCP maximum segment size (MTU - 40 bytes)', '-M',
    55     38                    :type => :integer, :unit => "Bytes")
    56     39    app.defProperty('nodelay', 'set TCP no delay, disabling Nagle\'s Algorithm', '-N',
    57     40                    :type => :boolean)
    58     41    app.defProperty('IPv6Version', 'set the domain to IPv6', '-V',
    59     42                    :type => :boolean)
    60     43    app.defProperty('reportexclude', 'exclude C(connection) D(data) M(multicast) S(settings) V(server) reports', '-x',
    61     44                    :type => :string, :unit => "[CDMSV]")
    62     45    app.defProperty('reportstyle', 'C or c for CSV report, O or o for OML', '-y',
    63     46                    :type => :string, :unit => "[CcOo]", :default => "o") # Use OML reporting by default
    64     47
    65     48    app.defProperty('oml-server', 'OML server for collecting data','--oml-server')
    66     49    app.defProperty('oml-id', 'ID for this oml client','--oml-id')
    67     50    app.defProperty('oml-exp-id', 'ID for this experiment','--oml-exp-id')
    68     51
    69     52    app.defProperty('server', 'run in server mode', '-s',
    70     53                    :type => :boolean)
    71     54
    72     55    app.defProperty('bandwidth', 'set target bandwidth to n bits/sec (default 1 Mbit/sec)', '-b',
    73     56                    :type => :string, :unit => "Mbps")
    74     57    app.defProperty('client', 'run in client mode, connecting to <host>', '-c',
    75     58                    :type => :string,
    76     59                    :order => 1)
    77     60    app.defProperty('dualtest', 'do a bidirectional test simultaneously', '-d',
    78     61                    :type => :boolean)
    79     62    app.defProperty('num', 'number of bytes to transmit (instead of -t)', '-n',
    80     63                    :type => :integer, :unit => "Bytes")
    81     64    app.defProperty('tradeoff', 'do a bidirectional test individually', '-r',
    82     65                    :type => :boolean)
    83     66    app.defProperty('time', 'time in seconds to transmit for (default 10 secs)', '-t',
    84     67                    :type => :integer, :unit => "seconds")
    85     68    app.defProperty('fileinput', 'input the data to be transmitted from a file', '-F',
    86     69                    :type => :string)
    87     70    app.defProperty('stdin', 'input the data to be transmitted from stdin', '-I',
    88     71                    :type => :boolean)
    89     72    app.defProperty('listenport', 'port to recieve bidirectional tests back on', '-L',
    90     73                    :type => :integer)
    91     74    app.defProperty('parallel', 'number of parallel client threads to run', '-P',
    92     75                    :type => :integer)
    93     76    app.defProperty('ttl', 'time-to-live, for multicast (default 1)', '-T',
    94     77                    :type => :integer,
    95     78                    :default => 1)
    96     79    app.defProperty('linux-congestion', 'set TCP congestion control algorithm (Linux only)', '-Z',
    97     80                    :type => :boolean)
    98     81
    99     82  app.defMeasurement("application"){ |m|
    100     83      m.defMetric('pid', :integer, 'Main process identifier')
    101     84      m.defMetric('version', :string, 'Iperf version')
    102     85      m.defMetric('cmdline', :string, 'Iperf invocation command line')
    103     86      m.defMetric('starttime_s', :integer, 'Time the application was received (s)')
    104     87      m.defMetric('starttime_us', :integer, 'Time the application was received (us)')
    105     88  }
    106     89
    107     90  app.defMeasurement("settings"){ |m|
    108     91      m.defMetric('pid', :integer, 'Main process identifier')
    109     92      m.defMetric('server_mode', :integer, '1 if in server mode, 0 otherwise')
    110     93      m.defMetric('bind_address', :string, 'Address to bind')
    111     94      m.defMetric('multicast', :integer, '1 if listening to a Multicast group')
    112     95      m.defMetric('multicast_ttl', :integer, 'Multicast TTL if relevant')
    113     96      m.defMetric('transport_protocol', :integer, 'Transport protocol (IANA number)')
    114     97      m.defMetric('window_size', :integer, 'TCP window size')
    115     98      m.defMetric('buffer_size', :integer, 'UDP buffer size')
    116     99  }
    117    100
    118    101  app.defMeasurement("connection"){ |m|
    119    102      m.defMetric('pid', :integer, 'Main process identifier')
    120    103      m.defMetric('connection_id', :integer, 'Connection identifier (socket)')
    121    104      m.defMetric('local_address', :string, 'Local network address')
    122    105      m.defMetric('local_port', :integer, 'Local port')
    123    106      m.defMetric('remote_address', :string, 'Remote network address')
    124    107      m.defMetric('remote_port', :integer, 'Remote port')
    125    108  }
    126    109
    127    110  app.defMeasurement("transfer"){ |m|
    128    111      m.defMetric('pid', :integer, 'Main process identifier')
    129    112      m.defMetric('connection_id', :integer, 'Connection identifier (socket)')
    130    113      m.defMetric('begin_interval', :double, 'Start of the averaging interval (Iperf timestamp)')
    131    114      m.defMetric('end_interval', :double, 'End of the averaging interval (Iperf timestamp)')
    132    115      m.defMetric('size', :uint64, 'Amount of transmitted data [Bytes]')
    133    116  }
    134    117
    135    118  app.defMeasurement("losses"){ |m|
    136    119      m.defMetric('pid', :integer, 'Main process identifier')
    137    120      m.defMetric('connection_id', :integer, 'Connection identifier (socket)')
    138    121      m.defMetric('begin_interval', :double, 'Start of the averaging interval (Iperf timestamp)')
    139    122      m.defMetric('end_interval', :double, 'End of the averaging interval (Iperf timestamp)')
    140    123      m.defMetric('total_datagrams', :integer, 'Total number of datagrams')
    141    124      m.defMetric('lost_datagrams', :integer, 'Number of lost datagrams')
    142    125  }
    143    126
    144    127  app.defMeasurement("jitter"){ |m|
    145    128      m.defMetric('pid', :integer, 'Main process identifier')
    146    129      m.defMetric('connection_id', :integer, 'Connection identifier (socket)')
    147    130      m.defMetric('begin_interval', :double, 'Start of the averaging interval (Iperf timestamp)')
    148    131      m.defMetric('end_interval', :double, 'End of the averaging interval (Iperf timestamp)')
    149    132      m.defMetric('jitter', :double, 'Average jitter [ms]')
    150    133  }
    151    134
    152    135  app.defMeasurement("packets"){ |m|
    153    136      m.defMetric('pid', :integer, 'Main process identifier')
    154    137      m.defMetric('connection_id', :integer, 'Connection identifier (socket)')
    155    138      m.defMetric('packet_id', :integer, 'Packet sequence number for datagram-oriented protocols')
    156    139      m.defMetric('packet_size', :integer, 'Packet size')
    157    140      m.defMetric('packet_time_s', :integer, 'Time the packet was processed (s)')
    158    141      m.defMetric('packet_time_us', :integer, 'Time the packet was processed (us)')
    159    142      m.defMetric('packet_sent_time_s', :integer, 'Time the packet was sent (s) for datagram-oriented protocols')
    160    143      m.defMetric('packet_sent_time_us', :integer, 'Time the packet was sent (us) for datagram-oriented protocols')
    161    144  }
    162    145
    163    146  end
    164    147
    165    148
    166    149
    167    150  defGroup('AP', property.accesspoint) do |node|
    168    151    node.addApplication("iperf") do |app|
    169    152      app.setProperty('server', true)
    170    153    end
    171    154    node.net.w0.mode = "master"
    172    155    node.net.w0.type = 'g'
    173    156    node.net.w0.channel = "6"
    174    157    node.net.w0.essid = "TEST1234"
    175    158    node.net.w0.ip = "192.168.0.254"
    176    159  end
    177    160
    178    161  defGroup('client', property.client) do |node|
    179    162    node.addApplication("iperf") do |app|
    180    163      app.setProperty('client', "192.168.0.254")
    181    164      app.setProperty('time', 20)
    182    165      app.setProperty('interval', 5)
    183    166      #app.setProperty('reportstyle','O')
    184    167      #app.setProperty('oml-server', "oml:3003")
    185    168      #app.setProperty('oml-id', "#{Experiment.ID}")
    186    169      #app.setProperty('oml-exp-id', "#{Experiment.ID}")
    187    170    end
    188    171    node.net.w0.mode = "managed"
    189    172    node.net.w0.type = 'g'
    190    173    node.net.w0.channel = "6"
    191    174    node.net.w0.essid = "TEST1234"
    192    175    node.net.w0.ip = "192.168.0.%index%"
    193    176  end
    194    177
    195    178  onEvent(:ALL_UP_AND_INSTALLED) do |event|
    196    179    info "Wifi Multi Client Iperf Experiment"
    197    180    wait 10
    198    181    allGroups.startApplications
    199    182    info "All my Applications are started now..."
    200    183    wait property.duration
    201    184    allGroups.stopApplications
    202    185    info "All my Applications are stopped now."
    203    186    Experiment.done
    204    187  end
    205 }}}
    206 
    207 Here's brief run down with some details from top to bottom.
    208 
    209 * In Lines 4-6 we use ''defProperty'' to define a few experiment properties along with default values which allows the script to be executed with arguments passed from the command line.
    210 
    211 * From Lines 8 - 146 we use ''defApplication'' to define the application header for iperf.
    212 
    213  Here we defined a reference name ''iperf'' that can be used later in the script.
     15The experiment script is available below for reference. It is already accessible from the console of the testbed you will be working on, so there is no need download it there.
     16
     17* [attachment:wifi-ap-iperf.rb Experiment Script]
     18
     19=== Dissecting the Experiment ===
     20Below you will find a step by step explanation of how the experiment script is structured and what each section does.
     21
     22'''1. Define experiment properties:'''
     23
     24 [http://www.orbit-lab.org/attachment/wiki/Tutorials/c0WiFi/Tutorial3/wifi-ap-iperf.rb#L4 Lines 4 - 6]
     25
     26 We use ''defProperty'' to define a few experiment properties along with their default values which allows the script to be executed with arguments passed from the command line.
     27{{{
     28defProperty('accesspoint', 'node1-1', "node ID for access point")
     29defProperty('client', 'node1-2', "node ID for client nodes")
     30defProperty('duration', 60, "Seconds to run the application.")
     31}}}
     32
     33'''2. Define application that will run on nodes:'''
     34
     35 [http://www.orbit-lab.org/attachment/wiki/Tutorials/c0WiFi/Tutorial3/wifi-ap-iperf.rb#L8 Lines 8 - 146]
     36
     37 We use ''defApplication'' to define the application header for iperf. This creates a reference name ''iperf'' that can be used later in the script.
    21438{{{
    21539defApplication('iperf', 'iperf-oml2') do |app|
    21640}}}
    21741
    218  The actual path to the iperf application in the node is defined.
     42 The actual path to the iperf application in the node is defined as follows.
    21943{{{
    22044app.path = "/usr/bin/iperf-oml2"
    22145}}}
    22246
    223  The numerous ''defProperty'' that follows defines a reference to the various arguments for the application. In the following definition the reference name ''interval'' is mapped to the application argument specified with the ''-i'' flag. The default value and the other properties can also be specified.
     47 The numerous ''defProperty'' that follow define references to the various arguments for the application. For example, in the following definition, the reference name ''interval'' is mapped to the application argument specified with the ''-i'' flag. The default value and the other properties can also be specified.
    22448{{{
    22549app.defProperty('interval', 'pause n seconds between periodic bandwidth reports', '-i',
     
    22751}}}
    22852
    229 * We define a reference name ''AP'' for the access point node using ''defGroup''
    230 {{{
    231 defGroup('AP', property.accespoint) do |node|
    232   node.addApplication("iperf") do |app|
    233     app.setProperty('server', true)
    234   end
    235   node.net.w0.mode = "master"
    236   node.net.w0.type = 'g'
    237   node.net.w0.channel = "6"
    238   node.net.w0.essid = "TEST1234"
    239   node.net.w0.ip = "192.168.0.254"
     53'''3. Define properties for group of nodes that will act as !WiFi APs:'''
     54
     55 [http://www.orbit-lab.org/attachment/wiki/Tutorials/c0WiFi/Tutorial3/wifi-ap-iperf.rb#L150 Lines 150 - 159]
     56
     57 The first group (AP) consists of a single node to be configured as an access point. In practice, these property definitions can be applied to multiple nodes (as we will see in the next section). However, for the purposes of this tutorial we will write this particular set of properties assuming it will apply to a single node.
     58
     59 We first define a group name ''AP'' using ''defGroup'' which will apply the subsequent properties to the node specified inthe ''property.accesspoint'' reference.
     60{{{
     61defGroup('AP', property.accesspoint) do |node|
     62}}}
     63
     64 Now we define various properties that will be applied to the AP node.
     65
     66 First, ''node.addApplication'' connects the previously defined ''iperf'' application to this node with the specified arguments and values using ''setProperty''. On the AP node, the iperf application will be configured as a server.
     67{{{
     68node.addApplication("iperf") do |app|
     69  app.setProperty('server', true)
    24070end
    24171}}}
    242  Several things are happening here:
    243  1. The group name ''AP'' references the node specified in ''property.master''. This group only consists of a single node.
    244  2. ''node.addApplication'' connects the previously defined ''iperf'' application to this node with the specified arguments and values using ''setProperty''. The iperf application is configured as a server.
    245  3. The wireless card properties are set using with the ''node.net.w0'' parameters. Here we set ''node.net.w0.mode'' to ''master'' to configure the wireless card as an access point. Other relevant parameters are also set here as well.
    246 
    247 * Likewise we use ''defGroup'' again to define a reference name ''client'' for the client node(s).
     72
     73 Next, The wireless card properties are set with the ''node.net.w0'' parameters. Here we set ''node.net.w0.mode'' to ''master'' to configure the 1st wireless card in the node as an access point. Other relevant parameters are also set here as well, such as channel and SSID.
     74{{{
     75node.net.w0.mode = "master"
     76node.net.w0.type = 'g'
     77node.net.w0.channel = "6"
     78node.net.w0.essid = "TEST1234"
     79node.net.w0.ip = "192.168.0.254"
     80}}}
     81
     82'''4. Define properties for group of nodes that will act as !WiFi clients:'''
     83
     84 [http://www.orbit-lab.org/attachment/wiki/Tutorials/c0WiFi/Tutorial3/wifi-ap-iperf.rb#L161 Lines 161 - 176]
     85
     86 The second group (client) consists of a single or multiple node(s) that will be configured as !WiFi clients.
     87
     88 Like the AP group, we use ''defGroup'' again to define a reference name ''client'' for the client node(s) specified in ''property.client'' reference.
    24889{{{
    24990defGroup('client', property.client) do |node|
    250   node.addApplication("iperf") do |app|
    251     app.setProperty('client', "192.168.0.254")
    252     app.setProperty('time', 20)
    253     app.setProperty('interval', 5)
    254   end
    255   node.net.w0.mode = "managed"
    256   node.net.w0.type = 'g'
    257   node.net.w0.channel = "6"
    258   node.net.w0.essid = "TEST1234"
    259   node.net.w0.ip = "192.168.0.%index%"
     91}}}
     92
     93 Now we configure iperf for the client node(s), but this time iperf will be running as a client connecting to the previously defined iperf server with the appropriate properties set accordingly.
     94{{{
     95node.addApplication("iperf") do |app|
     96  app.setProperty('client', "192.168.0.254")
     97  app.setProperty('time', 20)
     98  app.setProperty('interval', 5)
    26099end
    261100}}}
    262  1. The group name ''client'' references the node(s) specified in ''property.client''. This group can have multiple nodes.
    263  2. ''node.addApplication'' connects the previously defined ''iperf'' application to the node(s). ''iperf'' is configured as a client connection to the specified ip address.
    264  3. The wireless card properties are set using with the ''node.net.w0'' parameters. Note that we set ''node.net.w0.mode'' to ''managed'' to configure the wireless card as client. Other relevant parameters are also set here as well. Since we can have multiple nodes in a group we specify ''node.net.w0.ip'' with the last byte of the IP address as %index%. In this way each node will have a unique address.
    265 
    266 * At the end of the script once all the nodes are at the same point and all applications have been initialized, an event is triggered to continue and execute the remaining steps. The 10 second delay before the call to ''startApplications'', this allows time for the clients to establish a !WiFi connection with the access point before continuing. Iperf is started on all the nodes, execution of experiment script is delayed again giving time to collect experiment data. Finally all applications are stopped and the experiment exits out.
     101
     102 The wireless card properties are then set with the ''node.net.w0'' parameters. Note that we set ''node.net.w0.mode'' to ''managed'' to configure the wireless card as a client. Other relevant parameters are also set here as well. Since we can have multiple nodes in a group we specify ''node.net.w0.ip'' with the last byte of the IP address as ''%index%''. This way, each node will automatically be assigned a unique address.
     103{{{
     104node.net.w0.mode = "managed"
     105node.net.w0.type = 'g'
     106node.net.w0.channel = "6"
     107node.net.w0.essid = "TEST1234"
     108node.net.w0.ip = "192.168.0.%index%"
     109}}}
     110
     111'''5. Define event that will occur once all nodes are setup and ready:'''
     112
     113 [http://www.orbit-lab.org/attachment/wiki/Tutorials/c0WiFi/Tutorial3/wifi-ap-iperf.rb#L178 Lines 178 - 187]
     114
     115 At the end of the script, once all the nodes are at the same point and all applications have been initialized, an event is triggered to continue and execute the remaining steps.
    267116{{{
    268117onEvent(:ALL_UP_AND_INSTALLED) do |event|
    269   info "Wifi Multi Client Iperf Experiment"
    270   wait 10
    271   allGroups.startApplications
    272   info "All my Applications are started now..."
    273   wait property.duration
    274   allGroups.stopApplications
    275   info "All my Applications are stopped now."
    276   Experiment.done
    277 end
     118}}}
     119
     120 The remaining commands execute the actual experiment. The 10 second delay before the call to ''startApplications'' allows time for the clients to establish a !WiFi connection with the access point before continuing. Then iperf is started on all the nodes (AP and clients). Execution of the experiment script is once delayed again, this time for a period defined by ''property.duration'', giving time to collect experiment data. Finally all applications are stopped and the experiment exits out.
     121{{{
     122info "Wifi Multi Client Iperf Experiment"
     123wait 10
     124allGroups.startApplications
     125info "All my Applications are started now..."
     126wait property.duration
     127allGroups.stopApplications
     128info "All my Applications are stopped now."
     129Experiment.done
    278130}}}
    279131
     
    281133An overview on running experiment scripts on the orbit testbed can be found on the [wiki:Documentation/CGettingStarted Getting Started Page].
    282134
    283 In order to run this experiment script, load the ''baseline.ndz'' image onto nodes with !WiFi cards. Check the ''Status Page' in the [https://www.orbit-lab.org/cPanel/controlPanel/start Control Panel] to find nodes with a !WiFi card.
    284 {{{
    285 user@console:~$ omf load -i baseline.ndz -t node1-1,node1-2
    286 }}}
    287 
    288 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.
     135In order to run this experiment script, connect to the console of the domain for which you have an active and approved reservation for and load the ''baseline.ndz'' image onto nodes with !WiFi cards. Check the ''Status Page'' in the [https://www.orbit-lab.org/cPanel/controlPanel/start Control Panel] to find nodes with a !WiFi card.
     136{{{
     137user@console:~$ omf load -i baseline.ndz -t node1-1,node1-2,node1-3
     138}}}
     139
     140After the nodes have finished imaging, turn the nodes on. The topology used here is ''system:topo:imaged'' which selects the prior set of nodes that were successfully imaged.
    289141{{{
    290142user@console:~$ omf tell -a on -t system:topo:imaged
    291143}}}
    292144
    293 To run this experiment with 2 clients nodes:
    294 {{{
    295 user@console:~$ omf exec test:exp:tutorial:wifi-ap-iperf.rb -- --accesspoint node15-1 --client node16-1,node17-1 --duration 60
     145To run this experiment with 2 clients nodes (assuming the domain has more than 2 compatible nodes):
     146{{{
     147user@console:~$ omf exec test:exp:tutorial:wifi-ap-iperf.rb -- --accesspoint node1-1 --client node1-2,node1-3 --duration 60
    296148}}}
    297149
     
    330182}}}
    331183
    332 A detailed output of the experiment  along with iperf's output are aggregated in a log file: /tmp/default_slice-2014-10-09t10.15.22.101-04.00.log  or in general /tmp/''Experiment ID''.log.  Partial contents of this file is shown below:
     184=== Retrieving Results ===
     185
     186The experiment script generates a lot of data that contains not only results, but also information useful for debugging. This detailed output of the experiment along with iperf's output are aggregated on the console in a log file: /tmp/''Experiment ID''.log
     187
     188For this example, the file would be: /tmp/default_slice-2014-10-09t10.15.22.101-04.00.log
     189
     190Partial contents of this file (specifically iperf results) are shown below:
    333191{{{
    3341922014-10-09 10:15:38 INFO nodeHandler::exp: All my Applications are started now...
     
    3762342014-10-09 10:16:38 DEBUG nodeHandler::nodeSetPath: Stop all applications
    377235}}}
     236
     237=== Troubleshooting ===
     238
     239* When you login to the domain's console at the start of your approved time slot, do not assume the nodes you will be using have a working image on them. Load the appropriate image onto the nodes you will be using before attempting your experiment.
     240
     241* Make sure the nodes that you are trying to run the experiment on are ''actually'' powered on. You can use the `omf stat` command or view the Status page in the Control Panel to check this. Please note that the Status page is only periodically refreshed and may not show immediate changes in node power state.
     242
     243* Make sure the nodes (and domain) you are using for this experiment have compatible !WiFi cards. Refer to the Status page in the Control Panel to find nodes that have compatible !Wifi cards (either ath9k or ath5k).
     244
     245* If you are attempting this tutorial with more than 1 client node, please make a reservation on Grid or SB4. All other compatible domains only have 2 nodes, which will only allow for 1 AP and 1 client node.
     246
     247* It is not recommended to attempt this tutorial on SB4 unless you have completed the tutorial on configuring the attenuator matrix. The experiment will fail or produce errors if the attenuators are set beyond certain values.
     248
     249* If you are trying this tutorial on SB9, you are going to have a bad time because that sandbox does not have ANY wireless devices.
     250
     251=== Final Observations ===
     252
     253You have now, hopefully, succeeded in executing the experiment script in this tutorial. Try to run this experiment again but with more client nodes. Additionally, if you are trying this tutorial on the ORBIT grid, try using nodes in different locations to see those effects on performance.