Go back --> [wiki:Tutorial Tutorials] [[TOC(heading=Tutorial TOC, Tutorial, depth=2)]] = How to use execute an Application via a Prototype = === Prerequisite === This feature is only available in NodeHandler v4 and above. Before going through this tutorial, you should know the basics of writing and running an experiment script (see [wiki:Tutorial/HelloWorld here] and [wiki:Tutorial/HowtoWriteScripts here]). You should also have installed a disk image with Node Agent v4 on the nodes of the testbed you are using (e.g. "baseline.ndz"). See [wiki:Tutorial/HowToImage "How to install a disk image"]. === Goal === In this tutorial, you will learn a method to execute an application using a prototype. This method is complementary to the two previous methods presented [wiki:Tutorial/HowToCommand here] and [wiki:Tutorial/HowToApplication here]. It is the recommended method if you have an application, which you would like to use in the long term with different (possibly dynamic) parameters. For example, this would be the case of a traffic generator that can generate multiple type of traffic depending on the parameters given by the experiment script. Using a prototype to execute this generator within your experiment allows you to: * run the application with different initial parameters without having to re-write any modified "wrapper" code, * change any eventual dynamic parameters during the application execution (e.g. change the rate of the generated traffic after 60sec) This method involves three steps: * the definition of a "wrapper" around your original application. This step would correspond to the creation of a generic Node Handler-compatible version of your application. * the definition of a so-called prototype. This step would correspond to the creation of a specific type or instance of your application. * the definition of an experiment script, which would use your application via its prototype. In the context of the above traffic generator example: * first step: you create an Node Handler-compatible application definition, which is a "wrapper" around your original application. This application definition will provide an interface to all the available parameters of your original application. * second step: you specialize the previous application definition in a prototype definition. For example you decide to define an instance of your application (i.e. a prototype) which will only generate a given traffic model (e.g. pareto) but which still accepts different parameter (e.g. packet size, rate, etc...). You may also decide to define a second prototype, which will generate another type of traffic. * third step: you define an experiment which will use this specialized application. The remainder of this tutorial describes these three steps and the execution of the related experiment script === Step 1: Application Definition === In this step, we define an application definition (i.e. a "wrapper") for our original application. In this example, we take "ping" as our original application. The following code should be saved into a file "pingWrapper.rb" in the same directory as the experiment scripts that would use it: {{{ # Define a new application # The following declaration defines a new application which as a URI 'pingWrapper' # and the name 'pingApp' defApplication('pingWrapper', 'pingApp') {|app| app.shortDescription = "This is a simple wrapper application around tcpdump" app.path="/bin/ping" # Here we define the "properties" of this application, i.e. the parameters that # it supports and which we would like to access # Ping has many parameters, however in this example, we are only interested in # the following four ones. # defProperty has the following signature: # defProperty ( name, description, mnemonic, options ) # - 'name' is interpreted as the long parameter for the command (e.g. "--help") # - 'mnemonic' is the short parameter call (e.g. ?h will result in "-h") # - 'options' is a Hash of options for this parameter: # :type => # is the parameter types, any of ':string' or ':integer' # or ':boolean' or 'nil' # :dynamic => true | false # true property can be changed at run-time # :use_name => true | false # if false then print only the parameter value and do # not print either 'name' or 'mnemonic' when calling the command # :order => integer # Order the property according to integer when calling the command # This is the ping destination. It is a string and its value is not prefixed by any # "--options" or "-X", thus we set ':use_name' to false # app.defProperty("dst", "Destination Address or Name", nil, {:dynamic => false, :type => :string, :use_name => false}) # This is the number of ping packet to send. It is an integer, and on the command line # it should be prefixed by "-c", hence the use of '?c' # app.defProperty("count", "Number of probe packet to send", ?c, {:dynamic => false, :type => :integer}) # similar as previous comment... app.defProperty("flood", "Flood packets", ?f, {:dynamic => false, :type => :boolean}) app.defProperty("interval", "Time interval between packets in a flood", ?i, {:dynamic => false, :type => :integer}) } }}} === Step 2: Prototype Definition === In this step, we define a prototype that would use and specialize the above "pingWrapper" application. For the purpose of this example, we will define two independent different prototype. The first one is "agressivePing". It specializes the "pingWrapper" application into one that sends by default a fixed high amount of probes with minimal interval between them, thus effectively flooding the destination. This application could be used to get a coarse quick estimation of the packet drop rate on a link. The second one is "gentlePing". It specializes the "pingWrapper" application into one that sends by default 5 probes with 1s interval (default ping interval). This application could be used to test reachability between two nodes with minimal link disruption. The following two code blocks should be saved into two separate files "agressivePing.rb" and "gentlePing.rb" in the same directory as the previous application definition: {{{ # Define a new prototype # This prototype is an instance/specialization of the above 'pingWrapper' application # defPrototype("aggressivePing") { |proto| proto.name = "aggressivePing" proto.description = "A node that flood packets to a destination to quiclky determine packet drop rates" # Here we specify which property of the base application we would like to use # AND what are the default value we would like to give them. # 'destination' is a mandatory parameter of ping, thus we do not set any default value proto.defProperty('destination', 'Name or IP address of the destination') # This is a agressive ping, so we set the default probe number to 1000, we also set the # flooding behavior as 'true' by default, along with an interval of 0 # proto.defProperty('numPacket', 'Number of probe packets to flood', 1000) proto.defProperty('enableFlood', 'Enable packet flooding', true) proto.defProperty('pktInterval', 'Time interval between packets in a flood', 0) # Here we bind this prototype with the "pingWrapper" application definition # And we also bind the properties that we decided to use and gave default values to # proto.addApplication("pingApp", "pingWrapper") { |listener| listener.bindProperty('dst', 'destination') listener.bindProperty('count', 'numPacket') listener.bindProperty('flood', 'enableFlood') listener.bindProperty('interval', 'pktInterval') } } }}} and {{{ # Define another prototype # This prototype is another instance/specialization of the above 'pingWrapper' application # defPrototype("gentlePing") { |proto| proto.name = "gentlePing" proto.description = "A node that pings a destination to quiclky assess its reachability" # This is a gentle ping, so we set the default probe number to 5, we also set the # flooding behavior as 'false' by default, and we ignore the 'interval' parameter # proto.defProperty('destination', 'Name or IP address of the destination') proto.defProperty('numPacket', 'Number of probe packets to flood', 5) proto.defProperty('enableFlood', 'Enable packet flooding', false) proto.addApplication("pingApp", "pingWrapper") { |listener| listener.bindProperty('dst', 'destination') listener.bindProperty('count', 'numPacket') listener.bindProperty('flood', 'enableFlood') } } }}} === Step 3: The Experiment Script === In this step, we describe the experiment script that demonstrates the use of the above two prototypes. This experiment involves an ad-hoc network of two group of nodes, each containing a single node. The first group "firstWorker" contains node [1,1], which will use the "gentlePing" prototype/application to assert that it can reach node [1,2]. The second group "secondWorker" contains node [1,2], which will use the "agressivePing" prototype/application to get a coarse estimate of the packet drop rate on its link to node [1,1]. The following code should be saved into a file "tut_app_3.rb" in the same directory as the above application and prototype definitions: {{{ # Define the first group # The node(s) within this group will run the "gentlePing" prototype/application # defGroup('firstWorker', [1,1]) {|node| # Associate the prototype to use with this group # and configure any eventual parameters # node.prototype("gentlePing", { 'destination' => "192.168.0.2" }) # Configure the wireless interface on the node(s) in this set node.net.w0.mode = "ad-hoc" node.net.w0.type = "g" node.net.w0.essid = "tutorial" node.net.w0.ip = "192.168.0.1" } # Define the second group # The node(s) within this group will run the "aggressivePing" prototype/application # defGroup('secondWorker', [1,2]) {|node| # Here we decide that the default 1000 probes are not enough in our case # and we give a specific value of 4000 to the 'numPacket' parameter of # the "aggressivePing" prototype # node.prototype("aggressivePing", { 'destination' => "192.168.0.1", 'numPacket' => 4000 }) # Configure the wireless interface on the node(s) in this set node.net.w0.mode = "ad-hoc" node.net.w0.type = "g" node.net.w0.essid = "tutorial" node.net.w0.ip = "192.168.0.2" } # When all the applications on all the nodes in this experiment are ready # whenAllInstalled() {|node| # Wait 10 sec to make sure that the wireless interfaces are all # configured wait 10 # Start all the applications allGroups.startApplications # Wait 10 sec, this will be the experiment duration time wait 10 # Stop the experiment Experiment.done } }}} === Final Step! === To run this example script, use the following command: {{{ orbit exec tut_app_3 }}} === The Results === The experiment screen output should then look like [attachment:sb5_2007_12_04_22_44_34-Output.txt this]. And the experiment log file should look like [attachment:sb5_2007_12_04_22_44_34.log this]. === More... ===