Previous: , Up: First Steps with GNUnet   [Contents]


3.5 Starting Peers Using the Testbed Service

GNUnet’s testbed service is used for testing scenarios where a number of peers are to be started. The testbed can manage peers on a single host or on multiple hosts in a distributed fashion. On a single affordable computer, it should be possible to run around tens of peers without drastically increasing the load on the system.

The testbed service can be access through its API include/gnunet\_testbed\_service.h. The API provides many routines for managing a group of peers. It also provides a helper function GNUNET\_TESTBED\_test\_run() to quickly setup a minimalistic testing environment on a single host.

This function takes a configuration file which will be used as a template configuration for the peers. The testbed takes care of modifying relevant options in the peers’ configuration such as SERVICEHOME, PORT, UNIXPATH to unique values so that peers run without running into conflicts. It also checks and assigns the ports in configurations only if they are free.

Additionally, the testbed service also reads its options from the same configuration file. Various available options and details about them can be found in the testbed default configuration file src/testbed/testbed.conf.

With the testbed API, a sample test case can be structured as follows:

#include <unistd.h>
#include <gnunet/platform.h>
#include <gnunet/gnunet_util_lib.h>
#include <gnunet/gnunet_testbed_service.h>
#include <gnunet/gnunet_dht_service.h>

#define NUM_PEERS 20

static struct GNUNET_TESTBED_Operation *dht_op;

static struct GNUNET_DHT_Handle *dht_handle;


struct MyContext
{
  int ht_len;
} ctxt;


static int result;


static void
shutdown_task (void *cls)
{
  if (NULL != dht_op)
  {
    GNUNET_TESTBED_operation_done (dht_op);
    dht_op = NULL;
    dht_handle = NULL;
  }
  result = GNUNET_OK;
}


static void
service_connect_comp (void *cls,
                      struct GNUNET_TESTBED_Operation *op,
                      void *ca_result,
                      const char *emsg)
{
  GNUNET_assert (op == dht_op);
  dht_handle = ca_result;
  // Do work here...
  GNUNET_SCHEDULER_shutdown ();
}


static void *
dht_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
{
  struct MyContext *ctxt = cls;

  dht_handle = GNUNET_DHT_connect (cfg, ctxt->ht_len);
  return dht_handle;
}


static void
dht_da (void *cls, void *op_result)
{
  struct MyContext *ctxt = cls;

  GNUNET_DHT_disconnect ((struct GNUNET_DHT_Handle *) op_result);
  dht_handle = NULL;
}


static void
test_master (void *cls,
             struct GNUNET_TESTBED_RunHandle *h,
             unsigned int num_peers,
             struct GNUNET_TESTBED_Peer **peers,
             unsigned int links_succeeded,
             unsigned int links_failed)
{
  ctxt.ht_len = 10;
  dht_op = GNUNET_TESTBED_service_connect
      (NULL, peers[0], "dht",
       &service_connect_comp, NULL,
       &dht_ca, &dht_da, &ctxt);
  GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
}


int
main (int argc, char **argv)
{
  int ret;

  result = GNUNET_SYSERR;
  ret = GNUNET_TESTBED_test_run
      ("awesome-test", "template.conf",
       NUM_PEERS, 0LL,
       NULL, NULL, &test_master, NULL);
  if ( (GNUNET_OK != ret) || (GNUNET_OK != result) )
    return 1;
  return 0;
}

The source code for the above listing can be found at https://git.gnunet.org/gnunet.git/tree/doc/documentation/testbed_test.c or in the doc/ folder of your repository check-out. After installing GNUnet, the above source code can be compiled as:

$ export CPPFLAGS="-I/path/to/gnunet/headers"
$ export LDFLAGS="-L/path/to/gnunet/libraries"
$ gcc $CPPFLAGS $LDFLAGS -o testbed-test testbed_test.c \
 -lgnunettestbed -lgnunetdht -lgnunetutil
# Generate (empty) configuration
$ touch template.conf
# run it (press CTRL-C to stop)
$ ./testbed-test

The CPPFLAGS and LDFLAGS are necessary if GNUnet is installed into a different directory other than /usr/local.

All of testbed API’s peer management functions treat management actions as operations and return operation handles. It is expected that the operations begin immediately, but they may get delayed (to balance out load on the system). The program using the API then has to take care of marking the operation as “done” so that its associated resources can be freed immediately and other waiting operations can be executed. Operations will be canceled if they are marked as “done” before their completion.

An operation is treated as completed when it succeeds or fails. Completion of an operation is either conveyed as events through controller event callback or through respective operation completion callbacks. In functions which support completion notification through both controller event callback and operation completion callback, first the controller event callback will be called. If the operation is not marked as done in that callback or if the callback is given as NULL when creating the operation, the operation completion callback will be called. The API documentation shows which event are to be expected in the controller event notifications. It also documents any exceptional behaviour.

Once the peers are started, test cases often need to connect some of the peers’ services. Normally, opening a connect to a peer’s service requires the peer’s configuration. While using testbed, the testbed automatically generates per-peer configuration. Accessing those configurations directly through file system is discouraged as their locations are dynamically created and will be different among various runs of testbed. To make access to these configurations easy, testbed API provides the function GNUNET\_TESTBED\_service\_connect(). This function fetches the configuration of a given peer and calls the Connect Adapter. In the example code, it is the dht\_ca. A connect adapter is expected to open the connection to the needed service by using the provided configuration and return the created service connection handle. Successful connection to the needed service is signaled through service\_connect\_comp\_cb.

A dual to connect adapter is the Disconnect Adapter. This callback is called after the connect adapter has been called when the operation from GNUNET\_TESTBED\_service\_connect() is marked as “done”. It has to disconnect from the service with the provided service handle (op\_result).

Exercise: Find out how many peers you can run on your system.

Exercise: Find out how to create a 2D torus topology by changing the options in the configuration file. See The GNUnet Reference Documentation in The GNUnet Reference Documentation, then use the DHT API to store and retrieve values in the network.


Previous: , Up: First Steps with GNUnet   [Contents]