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

Changes between Initial Version and Version 1 of OverSimDevelopOld


Ignore:
Timestamp:
Apr 17, 2009, 3:54:48 PM (15 years ago)
Author:
heep
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • OverSimDevelopOld

    v1 v1  
     1On this page we offer a guide how to add your own overlay protocol implementation
     2to !OverSim.
     3
     4Other good starting point you could look at:
     5
     6   * The source code of the Chord protocol in [source:src/overlay/chord/Chord.cc]
     7
     8----
     9== '''Be careful: ==
     10==     This documentation covers only OMNeT++-3.3 compatible versions of OverSim !! ==
     11==     It is still under development and may be partly incorrect or outdated !! ==
     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 doxygen documentation. 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
     22[[Image(SimpleOverlayHost.png)]]
     23
     24The 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.
     25
     26== 2.1 Module declaration ==
     27
     28For 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.
     29
     30Following is a declaration for an example overlay module called ''!MyOverlay'' in myOverlay.ned:
     31
     32{{{
     33simple MyOverlay
     34
     35    parameters:
     36
     37        myParam1 : int,     
     38        myParam2 : string, 
     39
     40        debugOutput: bool;  // obligatory!
     41
     42    gates:
     43
     44        in: from_udp;    // gate from the UDP layer
     45        out: to_udp;     // gate to the UDP layer
     46        in: from_app;    // gate from the application
     47        out: to_app;     // gate to the application
     48
     49        in: direct_in;   // gate for sendDirect
     50
     51endsimple
     52}}}
     53
     54The 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. The direct_in gate is used for e.g. internal RPCs.
     55
     56Modules 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 3). 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.
     57
     58An example compound module is as follows:
     59
     60{{{
     61module MyOverlayContainer
     62
     63    gates:
     64
     65        in: from_udp;   // gate from the UDP layer
     66        out: to_udp;    // gate to the UDP layer
     67        in: from_app;   // gate from the application
     68        out: to_app;    // gate to the application
     69
     70   submodules:
     71
     72        innerOverlay: MyOverlay;
     73
     74    connections nocheck:  // connect our gates with the inner module
     75
     76                from_udp --> innerOveray.from_udp;
     77                to_udp <-- innerOverlay.to_udp;
     78                from_app --> innerOverlay.from_app;
     79                to_app <-- innerOverlay.to_app;
     80
     81endmodule
     82}}}
     83
     84== 2.2 Setting parameters ==
     85
     86The 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 4). 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:
     87
     88{{{
     89SimpleOverlay.overlayTerminal[5].overlay.myProt.myParam1 = 1024
     90}}}
     91
     92In 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:
     93
     94{{{
     95*.overlayTerminal[5].overlay.myProt.myParam1 = 1024
     96
     97**.overlay.myProt.myParam1 = 1024
     98}}}
     99
     100
     101
     102* 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).
     103
     104Should 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.
     105
     106== 3. Implementation using !BaseOverlay ==
     107
     108Overlay modules are implemented in C++ and should derive from the class BaseOverlay, which contains the necessary interface to work with other !OverSim modules.
     109
     110== 3.1 Important attributes ==
     111
     112{{{
     113cModule *thisTerminal;
     114}}}
     115
     116Pointer to the overlay module.
     117
     118{{{
     119NodeHandle thisNode;
     120}}}
     121
     122Information about the overlay node (IP address, port and overlay key).[[BR]]
     123
     124{{{
     125BootstrapOracle* bootstrapOracle;
     126}}}
     127
     128Pointer to a database of registered overlay nodes, to be used for bootstrapping.[[BR]]
     129
     130{{{
     131NotificationBoard* notificationBoard;
     132}}}
     133
     134Pointer to the notification board, which is used to generate node events.[[BR]]
     135
     136== 3.2 Initialization and finalization ==
     137
     138When 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.
     139
     140{{{
     141void initialize(int stage);
     142}}}
     143
     144First 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().
     145
     146If 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.
     147
     148{{{
     149void initializeOverlay(int stage);
     150}}}
     151
     152To be overriden by the overlay, this is the implementable overlay initialization function.
     153
     154{{{
     155void join(OverlayKey nodeID);
     156}}}
     157
     158Begins the bootstrapping. This function needs only to be manually called when joinOnApplicationRequest is true. When finished it calls joinOverlay().
     159
     160{{{
     161void joinOverlay();
     162}}}
     163
     164To 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).
     165
     166{{{
     167void setOverlayReady(bool ready);
     168}}}
     169
     170The overlay should call this function when it has finished bootstrapping and is in a ready state (or inversely, when it leaves that state).
     171
     172{{{
     173void finishOverlay();
     174}}}
     175
     176To 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.
     177
     178== 3.3 Messages ==
     179
     180The 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.
     181
     182{{{
     183void sendMessageToUDP(const TransportAddress& dest, cMessage* msg);
     184}}}
     185
     186Sends the given message to address dest.
     187
     188{{{
     189void handleUDPMessage(BaseOverlayMessage* msg);
     190}}}
     191
     192Called when a non-RPC/non-BaseRouteMessage message from UDP arrives. May be overriden by the overlay.
     193
     194{{{
     195void sendMessageToApp(cMessage *msg);
     196}}}
     197
     198Sends the given message to the application module (TBI)
     199
     200{{{
     201void handleAppMessage(cMessage* msg);
     202}}}
     203
     204Called when a non-RPC/non-CommonAPI message from the application arrives. May be overriden by the overlay.
     205
     206== 3.4 Key Based Routing (KBR) ==
     207
     208To 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.
     209
     210{{{
     211void sendToKey(const OverlayKey& key, BaseOverlayMessage* message,
     212               int numSiblings = 1,
     213               const std::vector<TransportAddress>& sourceRoute = TransportAddress::UNSPECIFIED_NODES,
     214               RoutingType routingType = DEFAULT_ROUTING);
     215}}}
     216
     217Sends the given message to the overlay key.
     218
     219sourceRoute 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.
     220
     221routingType specifies how the message will be routed.
     222
     223{{{
     224NodeVector* findNode(const OverlayKey& key, int numRedundantNodes, int numSiblings, BaseOverlayMessage* msg = NULL);
     225}}}
     226
     227Must be overriden by the overlay, it returns the numSiblings closest nodes known to key in the routing topology. 
     228
     229{{{
     230bool isSiblingFor(const NodeHandle& node, const OverlayKey& key, int numSiblings, bool* err);
     231}}}
     232
     233Must 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.
     234
     235{{{
     236bool handleFailedNode(const TransportAddress& failed);
     237}}}
     238
     239Called whenever a node given by findNode was unreachable. May be overriden by the overlay.
     240
     241== 3.5 Remote Procedure Calls ==
     242
     243RPCs 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.
     244
     245== 3.5.1 Sending Remote Procedure Calls ==
     246
     247{{{
     248inline uint32_t sendUdpRpcCall(const TransportAddress& dest,
     249                               BaseCallMessage* msg,
     250                               cPolymorphic* context = NULL,
     251                               simtime_t timeout = -1,
     252                               int retries = 0, int rpcId = -1,
     253                               RpcListener* rpcListener = NULL);
     254}}}
     255
     256Sends the RPC message msg through UDP the address dest. 
     257Context is a pointer to an arbitrary object which can be used to store additional state information.
     258Timeout is the time to wait until a call is declared as lost, retries is the amount of times to retry a lost call.
     259!RpcId is an RPC identifier to differentiate between calls.
     260!RpcListener is a listener object that will be notified for responses and timout events.
     261
     262{{{
     263inline uint32_t sendRouteRpcCall(CompType destComp,
     264                                 const TransportAddress& dest,
     265                                 const OverlayKey& destKey,
     266                                 BaseCallMessage* msg,
     267                                 cPolymorphic* context = NULL,
     268                                 RoutingType routingType = DEFAULT_ROUTING,
     269                                 simtime_t timeout = -1,
     270                                 int retries = 0,
     271                                 int rpcId = -1,
     272                                 RpcListener* rpcListener = NULL);
     273}}}
     274
     275Sends the RPC message through the overlay to the key destKey.
     276
     277!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().
     278
     279Context is a pointer to an arbitrary object which can be used to store additional state information.
     280!RoutingType determines the routing algorithm.
     281Timeout is the time to wait until a call is declared as lost, retries is the amount of times to retry a lost call.
     282!RpcId is an RPC identifier to differentiate between calls.
     283!RpcListener is a listener object that will be notified for responses and timout events.
     284
     285{{{
     286inline uint32_t sendInternalRpcCall(CompType destComp,
     287                                    BaseCallMessage* msg,
     288                                    cPolymorphic* context = NULL,
     289                                    simtime_t timeout = -1,
     290                                    int retries = 0,
     291                                    int rpcId = -1,
     292                                    RpcListener* rpcListener = NULL);
     293}}}
     294
     295Sends the RPC message to the same node but the tier destComp.
     296
     297!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().
     298Timeout is the time to wait until a call is declared as lost, retries is the amount of times to retry a lost call.
     299!RpcId is an RPC identifier to differentiate between calls.
     300!RpcListener is a listener object that will be notified for responses and timout events.
     301
     302== 3.5.2 Receiving Remote Procedure Calls ==
     303
     304{{{
     305bool handleRpc(BaseCallMessage* msg);
     306}}}
     307
     308To be overriden by the overlay, it is called whenever an RPC is received.
     309An 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:
     310
     311{{{
     312    RPC_SWITCH_START( msg )
     313
     314    RPC_DELEGATE( Join, rpcJoin );
     315
     316    RPC_DELEGATE( Notify, rpcNotify );
     317
     318    RPC_SWITCH_END( )
     319}}}
     320
     321In 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.
     322
     323{{{
     324void handleRpcTimeout(BaseCallMessage* msg, const TransportAddress& dest, int rpcId, const OverlayKey& destKey);
     325}}}
     326
     327To be overriden by the overlay, it is called when an RPC times out.
     328
     329
     330== 3.5.3 Replying to Remote Procedure Calls ==
     331
     332{{{
     333void sendRpcResponse(BaseCallMessage* call, BaseResponseMessage* response);
     334}}}
     335
     336Must be called by the overlay to respond to a given RPC.
     337
     338{{{
     339void handleRpcResponse( BaseResponseMessage* msg, int rpcId, simtime_t rtt );
     340}}}
     341
     342To be overriden by the overlay, it is called whenever an RPC response is received.
     343
     344== 3.5.4 Ping RPC Calls ==
     345
     346Ping RPC calls are convenience functions already implemented.
     347
     348{{{
     349void pingNode(const TransportAddress& dest,
     350              simtime_t timeout = -1,
     351              int retries = 0,
     352              cPolymorphic* context = NULL,
     353              const char* caption = "PING",
     354              RpcListener* rpcListener = NULL,
     355              int rpcId = -1,
     356              TransportType transportType = INVALID_TRANSPORT,
     357              bool overrideCache = false);
     358}}}
     359
     360Pings the node dest. When a node replies, the callback function pingResponse is invoked.
     361
     362Parameters timeout, retries, rpcListener and rpcId are the same as sendRouteRpcCall.
     363!OverrideCache determines whether the RTT value of the ping call should be cached. 
     364
     365{{{
     366void pingResponse(PingResponse* response, cPolymorphic* context, int rpcId, simtime_t rtt);
     367}}}
     368
     369To be overriden by the overlay, it is called when a ping response arrives.
     370Response is the RPC reply message.  Context, rpcIdare the same parameters as the calling pingNode. The rtt parameter returns the RTT value.
     371
     372{{{
     373void pingTimeout(PingCall* call, const TransportAddress& dest, cPolymorphic* context, int rpcId);
     374}}}
     375
     376To be overriden by the overlay, it is called after a ping RPC times out.
     377Call is the RPC call message.  Context, rpcIdare the same parameters as the calling pingNode.
     378
     379== 4. Setting up the network: the configuration file ==
     380
     381Now 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.
     382
     383The first line of the configuration file is the inclusion of the default values. That is done by adding the following line:
     384
     385{{{
     386include ./default.ini
     387}}}
     388
     389Each 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.
     390
     391Now, 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:
     392
     393{{{
     394[Run 1]
     395
     396network = SimpleNetwork
     397
     398# The following applies to SimpleNetwork
     399*.underlayConfigurator.churnGeneratorTypes = "LifetimeChurn"  // one node type
     400
     401# Since we only have one churn generator, the following applies to SimpleNetwork.churnGenerator[0]
     402**.numTiers = 1                         // only one application tier
     403**.tier1Type = "MyAppModule"            // module name of the application tier
     404**.overlayType = "MyOverlay"            // module name of the overlay
     405**.lifetimeMean = 10000                 // mean session time in seconds
     406**.targetOverlayTerminalNum=10          // target number of nodes
     407}}}
     408
     409
     410
     411The 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.
     412
     413
     414
     415{{{
     416[Run 2]
     417
     418*.underlayConfigurator.churnGeneratorTypes = "LifetimeChurn NoChurn"  // two churn generators
     419
     420# First churn
     421*.churnGenerator[0].tier1Type = "MyClientModule"  // module name of the application tier
     422*.churnGenerator[0].overlayType = "MyOverlay"     // module name of the overlay
     423
     424# Second churn
     425*.churnGenerator[1].tier1Type = "MyServerModule"  // module name of the application tier
     426*.churnGenerator[1].overlayType = "MyOverlay"     // module name of the overlay
     427}}}
     428
     429
     430
     431== 5. Compiling and running ==
     432
     433In order for !OverSim to find your files you need to to make the following changes:
     434Edit makemakefiles in the root folder, and add an include to your directory in ALL_OVERSIM_INCLUDES.
     435Change the “all:” section accordingly by adding your folder to the list.
     436
     437Now run {{{ ./makemake }}} and {{{ make }}} in the root folder. That should compile your new modules.
     438
     439In order to run it, you need to setup your simulation run in omnetpp.ini as explained in Section 4. 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 :
     440
     441{{{
     442../bin/OverSim [-f customConfigFile] [-r runNumber]
     443}}}
     444
     445If 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.
     446
     447
     448
     449== Have fun! ==