close Warning: BrowserModule failed with ConfigurationError: Look in the Trac log for more information.

Changes between Version 3 and Version 4 of OverSimDevelop


Ignore:
Timestamp:
Aug 15, 2008, 11:57:40 AM (16 years ago)
Author:
heep
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • OverSimDevelop

    v3 v4  
    1 On this page we'll soon add a guide how to add your own overlay protocol implementation
     1On this page we offer a guide how to add your own overlay protocol implementation
    22to !OverSim.
    33
    4 As starting point you could look at:
     4== '''Be careful: This documentation is still under develpment and may be partly incorrect or outdated!! ==
     5
     6Other good starting point you could look at:
    57
    68   * The source code of the Chord protocol in [source:Overlay/Chord/Chord.cc]
    79   * The [http://www.omnetpp.org/doc/manual/usman.html OMNeT++ user manual]
     10   * The [http://www.omnetpp.org/external/ned.php NED language specification]
     11
     12----
     13
     14== 1. Implementing new overlay modules in !OverSim ==
     15
     16This is a guide to implement new overlay modules for !OverSim. It is aimed to be an introductory guide, for more in-depth information you should consult the links provided in Section 1. This guide is divided as follows. In order to understand the place an overlay module takes inside nodes and how it interacts with them, first the module hierarchy for overlay hosts is described. Then we proceed to explain how overlay modules should declared using the NED language. Next, the basics for the module implementation are described using the base class BaseOverlay. Finally we explain how to make !OverSim compile and run your module.
     17
     18== 2. !OverSim nodes ==
     19
     20!OverSim nodes are the equivalent of individual terminals in the simulation environment. Nodes are based on a multi-tiered module hierarchy similar to OSI network layers (e.g. transport, overlay, application). The most used node type is called SimpleOverlayHost, and its tiers are structured as follows:
     21
     22The UDP module is an implementation of the transport layer, and is in charge of communication between nodes. Above it is the overlay (KBR) tier, where the overlay module will be located. It communicates with other overlay nodes through the UDP layer, and exposes its services to the application layer above it. On the third tier is the application layer, which uses the services provided by the overlay module. Application modules may also use UDP directly if necessary. Other tiers may be activated above the application if required, these connect to the tier below and to the UDP module.
     23
     24== 2.1 Module declaration ==
     25
     26For module declarations, !OverSim uses the NED language, a topology description language. Modules should be declared in their own NED files (with extension .ned), and the file name should match the module name.
     27
     28Following is a declaration for an example overlay module called ''!MyOverlay'' in myOverlay.ned:
     29
     30{{{
     31simple MyOverlay
     32    parameters:
     33        myParam1 : int,     
     34        myParam2 : string, 
     35          debugOutput: bool;  // obligatory!
     36    gates:
     37        in: from_udp;    // gate from the UDP layer
     38        out: to_udp;     // gate to the UDP layer
     39        in: from_app;    // gate from the application
     40        out: to_app;     // gate to the application
     41endsimple
     42}}}
     43
     44The module declaration is divided in two subsections: parameters and gates. The parameters subsection contains custom parameters established by the user, while the gates subsection establishes the connections to the other layers: from_udp and to_udp to the UDP layer, and from_app and to_app to the application layer.
     45
     46Modules can be nested inside one another. Modules without any inner modules are called simple modules, and are declared using the keyword simple. Only simple modules can have its behaviour customized with C++ (see Section 4). On the other hand, modules with inner nested modules are called compound modules, and act only as containers; they are declared with the keyword module.
     47
     48An example compound module is as follows:
     49
     50{{{
     51module MyOverlayContainer
     52    gates:
     53        in: from_udp;   // gate from the UDP layer
     54        out: to_udp;    // gate to the UDP layer
     55        in: from_app;   // gate from the application
     56        out: to_app;    // gate to the application
     57   submodules:
     58        innerOverlay: MyOverlay;
     59    connections nocheck:  // connect our gates with the inner module
     60                from_udp --> innerOveray.from_udp;
     61                to_udp <-- innerOverlay.to_udp;
     62                from_app --> innerOverlay.from_app;
     63                to_app <-- innerOverlay.to_app;
     64endmodule
     65}}}
     66
     67== 2.2 Setting parameters ==
     68
     69The user can set custom values for module parameters in the file omnetpp.ini, or in default.ini for general default values, both located in the Simulation folder (see Section 5). Parameters are hierarchic, separated by dots for each layer. For example, setting a parameter for a specific overlay module in a node can be done as follows:
     70
     71{{{ SimpleOverlay.overlayTerminal[5].overlay.myParam1 = 1024 }}}
     72
     73In this example, we are working with the network SimpleOverlay. From there, we need node number 5, and then its overlay module. For that module, we'll set the parameter myParam1 to 1024.  This case, however, is too specific. We may not always work with the SimpleOverlay network. Or we may need that parameter to be set for all nodes. For those cases the wildcards “*” and “**” are of use. For example:
     74
     75{{{ *.overlayTerminal[5].overlay.myParam1 = 1024 }}}[[BR]]
     76{{{ **.overlay.myParam1 = 1024 }}}
     77
     78“*” replaces exactly one step of the hierarchy (or a part of a name), while “**” replaces any number of steps. In the first case, “*” means that the parameter is set for any network (first step). In the second case, “**” means the parameter is set for any network, and any node in it (first and second steps). Wildcards should be used sparingly, since it makes complicated for other users to calculate the scope, and may end up causing unexpected results (including rewriting other parameters).
     79
     80Should a module parameter not be set in either omnetpp.ini or default.ini, or match any wildcard, !OverSim will prompt the user to enter a value for each instance of the module. For simulations with a big amount of nodes, setting each parameter individually quickly becomes overwhelming. Therefore, it is recommended that every module parameter be assigned a default value in default.ini.
     81
     82== 3. Implementation using !BaseOverlay ==
     83
     84Overlay modules are implemented in C++ and should derive from the class BaseOverlay, which contains the necessary interface to work with other !OverSim modules.
     85
     86== 3.1 Important attributes ==
     87
     88{{{ cModule *thisTerminal; }}}: Pointer to the overlay module.[[BR]]
     89{{{ NodeHandle thisNode; }}}: Information about the overlay node (IP address, port and overlay key).[[BR]]
     90{{{ BootstrapOracle* bootstrapOracle; }}} : Pointer to a database of registered overlay nodes, to be used for bootstrapping.[[BR]]
     91{{{ NotificationBoard* notificationBoard; }}} : Pointer to the notification board, which is used to generate node events.[[BR]]
     92
     93== 3.2 Initialization and finalization ==
     94
     95When the module has been created, the first function to be called is initialize(). This starts the internals of the module, and in turn calls initializeOverlay() which initializes the overlay. These initialization functions should only be used to start internal variables, like timers and statistic vectors, as there are no guarantees that module creation has been finished, or that any other overlay node (if any) has been created already. For that reason, bootstrapping should be first attempted after joinOverlay() has been called. When the overlay module is about to be destroyed or the simulation finishes, the overlay can use finishOverlay() to finalize itself.
     96
     97{{{
     98void initialize(int stage);
     99}}}
     100First function to be called, it initializes the bare bones of the overlay module. Fills in necessary parameters, initializes RPCs, sets watches and statistic vectors. When it's done it calls initializeOverlay().
     101If the joinOnApplicationRequest parameter is not set, it automatically calls join() with a random key. Else the application should manually call the join() function to start the joining process.
     102
     103{{{
     104void initializeOverlay(int stage);
     105}}}
     106To be overriden by the overlay, this is the implementable overlay initialization function.
     107
     108{{{
     109void join(OverlayKey nodeID);
     110}}}
     111Begins the bootstrapping. This function needs only to be manually called when joinOnApplicationRequest is true. When finished it calls joinOverlay().
     112
     113{{{
     114void joinOverlay();
     115}}}
     116To be overriden by the overlay, to start bootstrapping. An overlay can obtain information about other nodes for bootstrapping through the bootstrapOracle functions getBootstrapNode() and getRandomNode(). When bootstrapping is finished the overlay should call setOverlayReady(true).
     117
     118{{{
     119void setOverlayReady(bool ready);
     120}}}
     121The overlay should call this function when it has finished bootstrapping and is in a ready state (or inversely, when it leaves that state).
     122
     123{{{
     124void finishOverlay();
     125}}}
     126To be overriden by the overlay, this function is called when the module is about to be destroyed, in order for the overlay to finalize itself.
     127
     128== 3.3 Messages ==
     129
     130The main way to communicate to other nodes will be through packets. To do that use the sendMessageToUDP, with the needed transport address (IP address plus port number) and message as parameters. To receive UDP messages the overlay needs to override handleUDPMessage. For communication with the application module, the functions sendMessageToApp and handleAppMessage can be used in a similar way.
     131
     132{{{
     133sendMessageToUDP(const TransportAddress& dest, BaseOverlayMessage* msg);
     134}}}
     135Sends the given message to address dest.
     136
     137{{{
     138void handleUDPMessage(BaseOverlayMessage* msg);
     139}}}
     140Called when a message from UDP arrives. May be overriden by the overlay.
     141
     142{{{
     143void sendMessageToApp(cMessage *msg);
     144}}}
     145Sends the given message to the application module (TBI)
     146
     147{{{
     148void handleAppMessage(cMessage* msg);
     149}}}
     150Called when a message from the application arrives.May be overriden by the overlay.
     151
     152
     153== 3.4 Key Based Routing (KBR) ==
     154
     155To send a key through the overlay the function sendToKey is called. It uses a generic routing algorithm, using the results from findNode, to search for the corresponding node. The function findNode, the center of the KBR system, must be implemented by the overlay, and returns a list of nodes close to the given overlay key. handleFailedNode is called whenever a node given by findNode could not be reached.
     156
     157{{{
     158void sendToKey(const OverlayKey& key, BaseOverlayMessage* message,
     159               int numSiblings = 1,
     160               const std::vector<TransportAddress>& sourceRoute = TransportAddress::UNSPECIFIED_NODES,
     161               RoutingType routingType = DEFAULT_ROUTING);
     162}}}
     163Sends the given message to the overlay key.
     164sourceRoute determines the route that the message will follow. If not specified, it sends the message using a generic routing algorithm using the node vector given by findNode.
     165routingType specifies how the message will be routed.
     166
     167{{{
     168NodeVector* findNode( const OverlayKey& key, int numRedundantNodes, int numSiblings, BaseOverlayMessage* msg = NULL);
     169}}}
     170Must be overriden by the overlay, it returns the numSiblings closest nodes known to key in the routing topology. 
     171
     172{{{
     173bool isSiblingFor(const NodeHandle& node, const OverlayKey& key, int numSiblings, bool* err);
     174}}}
     175
     176Must be overriden by the overlay, it determines whether the node parameter is among the numSiblings closest nodes to key. If numSiblings equals 1, then it answers whether the node is the closest to key. Note that this function should be consistent with findNode: if isSiblingFor returns true, an equivalent call to findNode should return the node parameter as part of the vector. The err parameter returns whether an error happened.
     177
     178{{{
     179bool handleFailedNode(const TransportAddress& failed);
     180}}}
     181Called whenever a node given by findNode was unreachable. May be overriden by the overlay.
     182
     183
     184== 3.5 Remote Procedure Calls ==
     185
     186RPCs are remote procedure calls which are used by nodes to ask for information to each other. Two calls can be used to initiate an RPC query: sendRouteRpcCall, which sends an RPC to the given key, sendUdpRpcCall, which sends it to the given transport address, and sendInternalRpcCall, which sends it to the same node but a different tier. A node receiving an RPC manages it through handleRpc, and responds to it using sendRpcResponse(). In turn, the starting overlay node can use handleRpcResponse to manage returning RPC responses. handleRpcTimeout is called whenever an RPC could not be delivered. See Common/BaseRpc.h for a detailed explanation of the parameters.
     187
     188== 3.5.1 Sending Remote Procedure Calls ==
     189
     190{{{
     191inline uint32_t sendUdpRpcCall(const TransportAddress& dest,
     192                                   BaseCallMessage* msg,
     193                                   cPolymorphic* context = NULL,
     194                                   simtime_t timeout = -1,
     195                                   int retries = 0, int rpcId = -1,
     196                                   RpcListener* rpcListener = NULL);
     197}}}
     198Sends the RPC message msg through UDP the address dest. 
     199Context is a pointer to an arbitrary object which can be used to store additional state information.
     200Timeout is the time to wait until a call is declared as lost, retries is the amount of times to retry a lost call.
     201!RpcId is an RPC identifier to differentiate between calls.
     202!RpcListener is a listener that will be notified for call events.
     203
     204{{{
     205inline uint32_t sendRouteRpcCall(CompType destComp,
     206                                 const TransportAddress& dest,
     207                                 const OverlayKey& destKey,
     208                                 BaseCallMessage* msg,
     209                                 cPolymorphic* context = NULL,
     210                                 RoutingType routingType = DEFAULT_ROUTING,
     211                                 simtime_t timeout = -1,
     212                                 int retries = 0,
     213                                 int rpcId = -1,
     214                                 RpcListener* rpcListener = NULL);
     215}}}
     216
     217Sends the RPC message through the overlay to the key destKey.
     218!DestComp specifies the destination tier, and can be OVERLAY_COMP for the overlay, TIER1_COMP for the first application tier, TIER2_COMP and so on. The tier of the calling node can be obtained with getThisCompType().
     219Context is a pointer to an arbitrary object which can be used to store additional state information.
     220!RoutingType determines the routing algorithm.
     221Timeout is the time to wait until a call is declared as lost, retries is the amount of times to retry a lost call.
     222!RpcId is an RPC identifier to differentiate between calls.
     223!RpcListener is a listener that will be notified for call events.
     224
     225{{{
     226inline uint32_t sendInternalRpcCall(CompType destComp,
     227                                    BaseCallMessage* msg,
     228                                    cPolymorphic* context = NULL,
     229                                    simtime_t timeout = -1,
     230                                    int retries = 0,
     231                                    int rpcId = -1,
     232                                    RpcListener* rpcListener = NULL);
     233}}}
     234Sends the RPC message to the same node but the tier destComp.
     235!DestComp specifies the destination tier, and can be OVERLAY_COMP for the overlay, TIER1_COMP for the first application tier, TIER2_COMP and so on. The tier of the calling node can be obtained with getThisCompType().
     236Timeout is the time to wait until a call is declared as lost, retries is the amount of times to retry a lost call.
     237!RpcId is an RPC identifier to differentiate between calls.
     238!RpcListener is a listener that will be notified for call events.
     239
     240== 3.5.2 Receiving Remote Procedure Calls ==
     241
     242{{{
     243bool handleRpc(BaseCallMessage* msg);
     244}}}
     245To be overriden by the overlay, it is called whenever an RPC is received.
     246An alternative to using a switch to manage incoming RPCs is given by the macros in Common/RpcMacros.h, and can be used the following way:
     247
     248{{{
     249    RPC_SWITCH_START( msg )
     250    RPC_DELEGATE( Join, rpcJoin );
     251    RPC_DELEGATE( Notify, rpcNotify );
     252    RPC_SWITCH_END( )
     253}}}
     254In this example, RPC_SWITCH_START inits the switch. RPC_DELEGATE casts the message to a structure with "Call" appended to the end of the first parameter (in these cases, !JoinCall and !NotifyCall) and sends it to the function in the second parameter (rpcJoin() and rpcNotifiy()). RPC_SWITCH_END ends the switch. RPC_HANDLED can be queried at any moment to see if the RPC has been already handled.
     255
     256{{{
     257void handleRpcTimeout( BaseCallMessage* msg, const TransportAddress& dest, int rpcId, const OverlayKey& destKey);
     258}}}
     259To be overriden by the overlay, it is called when an RPC times out.
     260
     261
     262== 3.5.3 Replying to Remote Procedure Calls ==
     263
     264{{{
     265void sendRpcResponse(BaseCallMessage* call, BaseResponseMessage* response);
     266}}}
     267Must be called by the overlay to respond to a given RPC.
     268
     269{{{
     270void handleRpcResponse( BaseResponseMessage* msg, int rpcId, simtime_t rtt );
     271}}}
     272To be overriden by the overlay, it is called whenever an RPC response is received.
     273
     274== 3.5.4 Ping RPC Calls ==
     275
     276Ping RPC calls are convenience functions already implemented.
     277
     278{{{
     279void pingNode(const TransportAddress& dest,
     280              simtime_t timeout = -1,
     281              int retries = 0,
     282              cPolymorphic* context = NULL,
     283              const char* caption = "PING",
     284              RpcListener* rpcListener = NULL,
     285              int rpcId = -1,
     286              TransportType transportType = INVALID_TRANSPORT,
     287              bool overrideCache = false);
     288}}}
     289Pings the node dest. When a node replies, the callback function pingResponse is invoked.
     290Parameters timeout, retries, rpcListener and rpcId are the same as sendRouteRpcCall.
     291!OverrideCache determines whether the RTT value of the ping call should be cached. 
     292
     293{{{
     294void pingResponse(PingResponse* response, cPolymorphic* context, int rpcId, simtime_t rtt);
     295}}}
     296To be overriden by the overlay, it is called when a ping response arrives.
     297Response is the RPC reply message.  Context, rpcIdare the same parameters as the calling pingNode. The rtt parameter returns the RTT value.
     298
     299{{{
     300void pingTimeout(PingCall* call, const TransportAddress& dest, cPolymorphic* context, int rpcId);
     301}}}
     302To be overriden by the overlay, it is called after a ping RPC times out.
     303Call is the RPC call message.  Context, rpcIdare the same parameters as the calling pingNode.
     304
     305
     306== 4. Setting up the network: the configuration file ==
     307
     308Now that the overlay module is implemented, we still need to set up a network in order to run that overlay. To do that, we need to edit the omnetpp.ini file, or set up a custom configuration file. The configuration file should be located in the Simulation folder.
     309
     310The first line of the configuration file is the inclusion of the default values. That is done by adding the following line:
     311
     312{{{ include ./default.ini }}}[[BR]]
     313
     314Each simulation environment class is defined as a run. A configuration file can contain different amounts of runs, each with a different number. We need to set up the network in that run. There are two base networks: SimpleUnderlay and Ipv4Underlay. !SimpleUnderlay is a simplified flat network where each node is assigned coordinates, packet latencies are calculated based on the distance of the source and destination node coordinates, and each nodes are directly connected to one another. Ipv4Underlay emulates real-life networks and contain hierarchies of nodes, routers, and backbones.  The network type is set with the first-tier parameter network. Network modules can be accessed under the names of !SimpleNetwork for !SimpleUnderlay and Ipv4Network for Ipv4Underlay.  The module in charge of building the network is called underlayConfigurator.
     315
     316Now, once the network type has been set, now we need to declare the node classes. We can declare each node to be made of the same components and attributes, or create different classes each with its own attributes. Each class is represented by a churnGenerator. For example, we can create a network with one type of node (see also Section 2.2):
     317
     318{{{
     319[Run 1]
     320
     321network = SimpleNetwork
     322
     323# The following applies to SimpleNetwork
     324*.underlayConfigurator.churnGeneratorTypes = "RandomChurn"  // one node type
     325
     326# Since we only have one churn generator, the following applies to SimpleNetwork.churnGenerator[0]
     327**.numTiers = 1                         // only one application tier
     328**.tier1Type = "MyAppModule"            // module name of the application tier
     329**.overlayType = "MyOverlay"            // module name of the overlay
     330**.churnChangeInterval=0                // time to wait between churn changes (creation, elimination, ...)
     331**.targetOverlayTerminalNum=10          // maximal number of nodes
     332**.creationProbability=0.5              // probability for each churn change to create a node
     333**.migrationProbability=0.0             // probability for each churn change to migrate a node
     334**.removalProbability=0.5               // probability for each churn change to eliminate a node
     335}}}
     336
     337The following applies for a network with 2 node classes: a server and a client. All other values are the default ones set in default.ini.
     338
     339{{{
     340[Run 2]
     341
     342*.underlayConfigurator.churnGeneratorTypes = "RandomChurn RandomChurn"  // two churn generators
     343
     344# First churn
     345*.churnGenerator[0].tier1Type = "MyClientModule"        // module name of the application tier
     346*.churnGenerator[0].overlayType = "MyOverlay"   // module name of the overlay
     347
     348# Second churn
     349*.churnGenerator[1].tier1Type = "MyServerModule"        // module name of the application tier
     350*.churnGenerator[1].overlayType = "MyOverlay"   // module name of the overlay
     351}}}
     352
     353== 5. Compiling and running ==
     354
     355In order for !OverSim to find your files you need to to make the following changes:
     356Edit makemakefiles in the root folder, and add an include to your directory in ALL_OVERSIM_INCLUDES.
     357Change the “all:” section accordingly by adding your folder to the list.
     358
     359Now run “./makemake” and “make” in the root folder. That should compile your new modules.
     360
     361In order to run it, you need to setup your simulation run in omnetpp.ini as explained in Section 5. Make sure that you selected default values for all parameters in default.ini, or you'll be prompted for a value when the simulation begins - for each instance of the parameter. To start !OverSim, enter the directory Simulations and run :
     362
     363{{{ ../bin/OverSim [-f customConfigFile] [-r runNumber] }}}[[BR]]
     364
     365If you don't select a run number, if the GUI is enabled, you'll be prompted for a run number. If not, all the runs in omnetpp.ini (or the given custom config file) will be run.