2 This file is part of GNUnet
3 (C) 2008, 2009 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file testing/testing_group.c
23 * @brief convenience API for writing testcases for GNUnet
24 * @author Christian Grothoff
27 #include "gnunet_arm_service.h"
28 #include "gnunet_testing_lib.h"
30 #define VERBOSE_TESTING GNUNET_YES
33 * Lowest port used for GNUnet testing. Should be high enough to not
34 * conflict with other applications running on the hosts but be low
35 * enough to not conflict with client-ports (typically starting around
38 #define LOW_PORT 10000
41 * Highest port used for GNUnet testing. Should be low enough to not
42 * conflict with the port range for "local" ports (client apps; see
43 * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
45 #define HIGH_PORT 32000
47 #define CONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
50 * Data we keep per peer.
55 * (Initial) configuration of the host.
56 * (initial because clients could change
57 * it and we would not know about those
60 struct GNUNET_CONFIGURATION_Handle *cfg;
63 * Handle for controlling the daemon.
65 struct GNUNET_TESTING_Daemon *daemon;
70 * Data we keep per host.
80 * Lowest port that we have not yet used
88 * Handle to a group of GNUnet peers.
90 struct GNUNET_TESTING_PeerGroup
95 struct GNUNET_SCHEDULER_Handle *sched;
98 * Configuration template.
100 const struct GNUNET_CONFIGURATION_Handle *cfg;
103 * Function to call on each started daemon.
105 GNUNET_TESTING_NotifyDaemonRunning cb;
113 * Function to call on each topology connection created
115 GNUNET_TESTING_NotifyConnection notify_connection;
118 * Callback for notify_connection
120 void *notify_connection_cls;
123 * NULL-terminated array of information about
126 struct HostData *hosts;
129 * Array of "total" peers.
131 struct PeerData *peers;
134 * Number of peers in this group.
143 struct GNUNET_CONFIGURATION_Handle *ret;
148 * Function to iterate over options. Copies
149 * the options to the target configuration,
150 * updating PORT values as needed.
153 * @param section name of the section
154 * @param option name of the option
155 * @param value value of the option
158 update_config (void *cls,
159 const char *section, const char *option, const char *value)
161 struct UpdateContext *ctx = cls;
165 if ((0 == strcmp (option, "PORT")) && (1 == sscanf (value, "%u", &ival)))
167 GNUNET_snprintf (cval, sizeof (cval), "%u", ctx->nport++);
170 GNUNET_CONFIGURATION_set_value_string (ctx->ret, section, option, value);
175 * Create a new configuration using the given configuration
176 * as a template; however, each PORT in the existing cfg
177 * must be renumbered by incrementing "*port". If we run
178 * out of "*port" numbers, return NULL.
180 * @param cfg template configuration
181 * @param port port numbers to use, update to reflect
182 * port numbers that were used
183 * @return new configuration, NULL on error
185 static struct GNUNET_CONFIGURATION_Handle *
186 make_config (const struct GNUNET_CONFIGURATION_Handle *cfg, uint16_t * port)
188 struct UpdateContext uc;
193 uc.ret = GNUNET_CONFIGURATION_create ();
194 GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
195 if (uc.nport >= HIGH_PORT)
198 GNUNET_CONFIGURATION_destroy (uc.ret);
201 *port = (uint16_t) uc.nport;
206 create_clique (struct GNUNET_TESTING_PeerGroup *pg)
208 unsigned int outer_count;
209 unsigned int inner_count;
210 int connect_attempts;
212 connect_attempts = 0;
214 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
216 for (inner_count = outer_count + 1; inner_count < pg->total;
220 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
221 "Connecting peer %d to peer %d\n",
222 outer_count, inner_count);
224 GNUNET_TESTING_daemons_connect (pg->peers[outer_count].daemon,
225 pg->peers[inner_count].daemon,
227 pg->notify_connection,
228 pg->notify_connection_cls);
233 return connect_attempts;
238 * Takes a peer group and attempts to create a topology based on the
239 * one specified in the configuration file. Returns the number of connections
240 * that will attempt to be created, but this will happen asynchronously(?) so
241 * the caller will have to keep track (via the callback) of whether or not
242 * the connection actually happened.
244 * @param pg the peer group struct representing the running peers
248 GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg)
250 /* Put stuff at home in here... */
251 unsigned long long topology_num;
254 GNUNET_assert (pg->notify_connection != NULL);
257 GNUNET_CONFIGURATION_get_value_number (pg->cfg, "testing", "topology",
260 switch (topology_num)
262 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
264 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
265 _("Creating clique topology (may take a bit!)\n"));
266 ret = create_clique (pg);
268 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
269 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
270 _("Creating small world topology (may take a bit!)\n"));
274 GNUNET_REMOTE_connect_small_world_ring (&totalConnections,
276 list_as_array, dotOutFile,
277 percentage, logNModifier);
280 case GNUNET_TESTING_TOPOLOGY_RING:
282 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
283 _("Creating ring topology (may take a bit!)\n"));
286 ret = GNUNET_REMOTE_connect_ring (&totalConnections, head, dotOutFile);
290 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
292 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
293 _("Creating 2d torus topology (may take a bit!)\n"));
297 GNUNET_REMOTE_connect_2d_torus (&totalConnections, number_of_daemons,
298 list_as_array, dotOutFile);
302 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
304 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
305 _("Creating Erdos-Renyi topology (may take a bit!)\n"));
308 GNUNET_REMOTE_connect_erdos_renyi (&totalConnections, percentage,
313 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
315 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
316 _("Creating InterNAT topology (may take a bit!)\n"));
320 GNUNET_REMOTE_connect_nated_internet (&totalConnections, percentage,
321 number_of_daemons, head,
326 case GNUNET_TESTING_TOPOLOGY_NONE:
336 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
337 _("No topology specified, was one intended?\n"));
344 * Start count gnunetd processes with the same set of transports and
345 * applications. The port numbers (any option called "PORT") will be
346 * adjusted to ensure that no two peers running on the same system
347 * have the same port(s) in their respective configurations.
349 * @param sched scheduler to use
350 * @param cfg configuration template to use
351 * @param total number of daemons to start
352 * @param cb function to call on each daemon that was started
353 * @param cb_cls closure for cb
354 * @param connect_callback function to call each time two hosts are connected
355 * @param connect_callback_cls closure for connect_callback
356 * @param hostnames space-separated list of hostnames to use; can be NULL (to run
357 * everything on localhost).
358 * @return NULL on error, otherwise handle to control peer group
360 struct GNUNET_TESTING_PeerGroup *
361 GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
362 const struct GNUNET_CONFIGURATION_Handle *cfg,
364 GNUNET_TESTING_NotifyDaemonRunning cb,
366 GNUNET_TESTING_NotifyConnection
367 connect_callback, void *connect_callback_cls,
368 const char *hostnames)
370 struct GNUNET_TESTING_PeerGroup *pg;
374 const char *hostname;
375 char *baseservicehome;
376 char *newservicehome;
377 struct GNUNET_CONFIGURATION_Handle *pcfg;
379 unsigned int hostcnt;
388 pg = GNUNET_malloc (sizeof (struct GNUNET_TESTING_PeerGroup));
393 pg->notify_connection = connect_callback;
394 pg->notify_connection_cls = connect_callback_cls;
396 pg->peers = GNUNET_malloc (total * sizeof (struct PeerData));
397 if (NULL != hostnames)
400 /* skip leading spaces */
401 while ((0 != *hostnames) && (isspace (*hostnames)))
404 while ('\0' != *rpos)
410 pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
412 start = GNUNET_strdup (hostnames);
419 if (strlen (start) > 0)
421 pg->hosts[off].minport = LOW_PORT;
422 pg->hosts[off++].hostname = start;
428 if (strlen (start) > 0)
430 pg->hosts[off].minport = LOW_PORT;
431 pg->hosts[off++].hostname = start;
436 GNUNET_free (pg->hosts);
440 minport = 0; /* make gcc happy */
447 for (off = 0; off < total; off++)
451 hostname = pg->hosts[off % hostcnt].hostname;
452 pcfg = make_config (cfg, &pg->hosts[off % hostcnt].minport);
457 pcfg = make_config (cfg, &minport);
461 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
463 ("Could not create configuration for peer number %u on `%s'!\n"),
464 off, hostname == NULL ? "localhost" : hostname);
469 GNUNET_CONFIGURATION_get_value_string (pcfg, "PATHS", "SERVICEHOME",
472 tempsize = snprintf (NULL, 0, "%s/%d/", baseservicehome, off) + 1;
473 newservicehome = GNUNET_malloc (tempsize);
474 snprintf (newservicehome, tempsize, "%s/%d/", baseservicehome, off);
478 tempsize = snprintf (NULL, 0, "%s/%d/", "/tmp/gnunet-testing-test-test", off) + 1; /* FIXME: set a default path, or read the TMPDIR variable or something */
479 newservicehome = GNUNET_malloc (tempsize);
480 snprintf (newservicehome, tempsize, "%s/%d/",
481 "/tmp/gnunet-testing-test-test", off);
483 GNUNET_CONFIGURATION_set_value_string (pcfg,
485 "SERVICEHOME", newservicehome);
487 pg->peers[off].cfg = pcfg;
488 pg->peers[off].daemon = GNUNET_TESTING_daemon_start (sched,
492 if (NULL == pg->peers[off].daemon)
493 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
494 _("Could not start peer number %u!\n"), off);
501 * Shutdown all peers started in the given group.
503 * @param pg handle to the peer group
506 GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg)
510 for (off = 0; off < pg->total; off++)
512 /* FIXME: should we wait for our
513 continuations to be called here? This
514 would require us to take a continuation
516 if (NULL != pg->peers[off].daemon)
517 GNUNET_TESTING_daemon_stop (pg->peers[off].daemon, NULL, NULL);
518 if (NULL != pg->peers[off].cfg)
519 GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg);
521 GNUNET_free (pg->peers);
522 if (NULL != pg->hosts)
524 GNUNET_free (pg->hosts[0].hostname);
525 GNUNET_free (pg->hosts);
531 /* end of testing_group.c */