testing changes, beginning of topology stuff
[oweals/gnunet.git] / src / testing / testing_group.c
1 /*
2       This file is part of GNUnet
3       (C) 2008, 2009 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19  */
20
21 /**
22  * @file testing/testing_group.c
23  * @brief convenience API for writing testcases for GNUnet
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_arm_service.h"
28 #include "gnunet_testing_lib.h"
29
30 #define VERBOSE_TESTING GNUNET_YES
31
32 /**
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
36  * 32k).
37  */
38 #define LOW_PORT 10000
39
40 /**
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).
44  */
45 #define HIGH_PORT 32000
46
47 #define CONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
48
49 /**
50  * Data we keep per peer.
51  */
52 struct PeerData
53 {
54   /**
55    * (Initial) configuration of the host.
56    * (initial because clients could change
57    *  it and we would not know about those
58    *  updates).
59    */
60   struct GNUNET_CONFIGURATION_Handle *cfg;
61
62   /**
63    * Handle for controlling the daemon.
64    */
65   struct GNUNET_TESTING_Daemon *daemon;
66 };
67
68
69 /**
70  * Data we keep per host.
71  */
72 struct HostData
73 {
74   /**
75    * Name of the host.
76    */
77   char *hostname;
78
79   /**
80    * Lowest port that we have not yet used
81    * for GNUnet.
82    */
83   uint16_t minport;
84 };
85
86
87 /**
88  * Handle to a group of GNUnet peers.
89  */
90 struct GNUNET_TESTING_PeerGroup
91 {
92   /**
93    * Our scheduler.
94    */
95   struct GNUNET_SCHEDULER_Handle *sched;
96
97   /**
98    * Configuration template.
99    */
100   const struct GNUNET_CONFIGURATION_Handle *cfg;
101
102   /**
103    * Function to call on each started daemon.
104    */
105   GNUNET_TESTING_NotifyDaemonRunning cb;
106
107   /**
108    * Closure for cb.
109    */
110   void *cb_cls;
111
112   /*
113    * Function to call on each topology connection created
114    */
115   GNUNET_TESTING_NotifyConnection notify_connection;
116
117   /*
118    * Callback for notify_connection
119    */
120   void *notify_connection_cls;
121
122   /**
123    * NULL-terminated array of information about
124    * hosts.
125    */
126   struct HostData *hosts;
127
128   /**
129    * Array of "total" peers.
130    */
131   struct PeerData *peers;
132
133   /**
134    * Number of peers in this group.
135    */
136   unsigned int total;
137
138 };
139
140
141 struct UpdateContext
142 {
143   struct GNUNET_CONFIGURATION_Handle *ret;
144   unsigned int nport;
145 };
146
147 /**
148  * Function to iterate over options.  Copies
149  * the options to the target configuration,
150  * updating PORT values as needed.
151  *
152  * @param cls closure
153  * @param section name of the section
154  * @param option name of the option
155  * @param value value of the option
156  */
157 static void
158 update_config (void *cls,
159                const char *section, const char *option, const char *value)
160 {
161   struct UpdateContext *ctx = cls;
162   unsigned int ival;
163   char cval[12];
164
165   if ((0 == strcmp (option, "PORT")) && (1 == sscanf (value, "%u", &ival)))
166     {
167       GNUNET_snprintf (cval, sizeof (cval), "%u", ctx->nport++);
168       value = cval;
169     }
170   GNUNET_CONFIGURATION_set_value_string (ctx->ret, section, option, value);
171 }
172
173
174 /**
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. 
179  * 
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
184  */
185 static struct GNUNET_CONFIGURATION_Handle *
186 make_config (const struct GNUNET_CONFIGURATION_Handle *cfg, uint16_t * port)
187 {
188   struct UpdateContext uc;
189   uint16_t orig;
190
191   orig = *port;
192   uc.nport = *port;
193   uc.ret = GNUNET_CONFIGURATION_create ();
194   GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
195   if (uc.nport >= HIGH_PORT)
196     {
197       *port = orig;
198       GNUNET_CONFIGURATION_destroy (uc.ret);
199       return NULL;
200     }
201   *port = (uint16_t) uc.nport;
202   return uc.ret;
203 }
204
205 static int
206 create_clique (struct GNUNET_TESTING_PeerGroup *pg)
207 {
208   unsigned int outer_count;
209   unsigned int inner_count;
210   int connect_attempts;
211
212   connect_attempts = 0;
213
214   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
215     {
216       for (inner_count = outer_count + 1; inner_count < pg->total;
217            inner_count++)
218         {
219 #if VERBOSE_TESTING
220           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
221                       "Connecting peer %d to peer %d\n",
222                       outer_count, inner_count);
223 #endif
224           GNUNET_TESTING_daemons_connect (pg->peers[outer_count].daemon,
225                                           pg->peers[inner_count].daemon,
226                                           CONNECT_TIMEOUT,
227                                           pg->notify_connection,
228                                           pg->notify_connection_cls);
229           connect_attempts++;
230         }
231     }
232
233   return connect_attempts;
234 }
235
236
237 /*
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.
243  *
244  * @param pg the peer group struct representing the running peers
245  *
246  */
247 int
248 GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg)
249 {
250   /* Put stuff at home in here... */
251   unsigned long long topology_num;
252   int ret;
253
254   GNUNET_assert (pg->notify_connection != NULL);
255   ret = 0;
256   if (GNUNET_YES ==
257       GNUNET_CONFIGURATION_get_value_number (pg->cfg, "testing", "topology",
258                                              &topology_num))
259     {
260       switch (topology_num)
261         {
262         case GNUNET_TESTING_TOPOLOGY_CLIQUE:
263 #if VERBOSE_TESTING
264           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
265                       _("Creating clique topology (may take a bit!)\n"));
266           ret = create_clique (pg);
267           break;
268         case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
269           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
270                       _("Creating small world topology (may take a bit!)\n"));
271 #endif
272           ret = GNUNET_SYSERR;
273 /*        ret =
274           GNUNET_REMOTE_connect_small_world_ring (&totalConnections,
275                                                   number_of_daemons,
276                                                   list_as_array, dotOutFile,
277                                                   percentage, logNModifier);
278                                                   */
279           break;
280         case GNUNET_TESTING_TOPOLOGY_RING:
281 #if VERBOSE_TESTING
282           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
283                       _("Creating ring topology (may take a bit!)\n"));
284 #endif
285           /*
286              ret = GNUNET_REMOTE_connect_ring (&totalConnections, head, dotOutFile);
287            */
288           ret = GNUNET_SYSERR;
289           break;
290         case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
291 #if VERBOSE_TESTING
292           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
293                       _("Creating 2d torus topology (may take a bit!)\n"));
294 #endif
295           /*
296              ret =
297              GNUNET_REMOTE_connect_2d_torus (&totalConnections, number_of_daemons,
298              list_as_array, dotOutFile);
299            */
300           ret = GNUNET_SYSERR;
301           break;
302         case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
303 #if VERBOSE_TESTING
304           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
305                       _("Creating Erdos-Renyi topology (may take a bit!)\n"));
306 #endif
307           /* ret =
308              GNUNET_REMOTE_connect_erdos_renyi (&totalConnections, percentage,
309              head, dotOutFile);
310            */
311           ret = GNUNET_SYSERR;
312           break;
313         case GNUNET_TESTING_TOPOLOGY_INTERNAT:
314 #if VERBOSE_TESTING
315           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
316                       _("Creating InterNAT topology (may take a bit!)\n"));
317 #endif
318           /*
319              ret =
320              GNUNET_REMOTE_connect_nated_internet (&totalConnections, percentage,
321              number_of_daemons, head,
322              dotOutFile);
323            */
324           ret = GNUNET_SYSERR;
325           break;
326         case GNUNET_TESTING_TOPOLOGY_NONE:
327           ret = 0;
328           break;
329         default:
330           ret = GNUNET_SYSERR;
331           break;
332         }
333     }
334   else
335     {
336       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
337                   _("No topology specified, was one intended?\n"));
338     }
339
340   return ret;
341 }
342
343 /**
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.
348  *
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
359  */
360 struct GNUNET_TESTING_PeerGroup *
361 GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
362                               const struct GNUNET_CONFIGURATION_Handle *cfg,
363                               unsigned int total,
364                               GNUNET_TESTING_NotifyDaemonRunning cb,
365                               void *cb_cls,
366                               GNUNET_TESTING_NotifyConnection
367                               connect_callback, void *connect_callback_cls,
368                               const char *hostnames)
369 {
370   struct GNUNET_TESTING_PeerGroup *pg;
371   const char *rpos;
372   char *pos;
373   char *start;
374   const char *hostname;
375   char *baseservicehome;
376   char *newservicehome;
377   struct GNUNET_CONFIGURATION_Handle *pcfg;
378   unsigned int off;
379   unsigned int hostcnt;
380   uint16_t minport;
381   int tempsize;
382
383   if (0 == total)
384     {
385       GNUNET_break (0);
386       return NULL;
387     }
388   pg = GNUNET_malloc (sizeof (struct GNUNET_TESTING_PeerGroup));
389   pg->sched = sched;
390   pg->cfg = cfg;
391   pg->cb = cb;
392   pg->cb_cls = cb_cls;
393   pg->notify_connection = connect_callback;
394   pg->notify_connection_cls = connect_callback_cls;
395   pg->total = total;
396   pg->peers = GNUNET_malloc (total * sizeof (struct PeerData));
397   if (NULL != hostnames)
398     {
399       off = 2;
400       /* skip leading spaces */
401       while ((0 != *hostnames) && (isspace (*hostnames)))
402         hostnames++;
403       rpos = hostnames;
404       while ('\0' != *rpos)
405         {
406           if (isspace (*rpos))
407             off++;
408           rpos++;
409         }
410       pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
411       off = 0;
412       start = GNUNET_strdup (hostnames);
413       pos = start;
414       while ('\0' != *pos)
415         {
416           if (isspace (*pos))
417             {
418               *pos = '\0';
419               if (strlen (start) > 0)
420                 {
421                   pg->hosts[off].minport = LOW_PORT;
422                   pg->hosts[off++].hostname = start;
423                 }
424               start = pos + 1;
425             }
426           pos++;
427         }
428       if (strlen (start) > 0)
429         {
430           pg->hosts[off].minport = LOW_PORT;
431           pg->hosts[off++].hostname = start;
432         }
433       if (off == 0)
434         {
435           GNUNET_free (start);
436           GNUNET_free (pg->hosts);
437           pg->hosts = NULL;
438         }
439       hostcnt = off;
440       minport = 0;              /* make gcc happy */
441     }
442   else
443     {
444       hostcnt = 0;
445       minport = LOW_PORT;
446     }
447   for (off = 0; off < total; off++)
448     {
449       if (hostcnt > 0)
450         {
451           hostname = pg->hosts[off % hostcnt].hostname;
452           pcfg = make_config (cfg, &pg->hosts[off % hostcnt].minport);
453         }
454       else
455         {
456           hostname = NULL;
457           pcfg = make_config (cfg, &minport);
458         }
459       if (NULL == pcfg)
460         {
461           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
462                       _
463                       ("Could not create configuration for peer number %u on `%s'!\n"),
464                       off, hostname == NULL ? "localhost" : hostname);
465           continue;
466         }
467
468       if (GNUNET_YES ==
469           GNUNET_CONFIGURATION_get_value_string (pcfg, "PATHS", "SERVICEHOME",
470                                                  &baseservicehome))
471         {
472           tempsize = snprintf (NULL, 0, "%s/%d/", baseservicehome, off) + 1;
473           newservicehome = GNUNET_malloc (tempsize);
474           snprintf (newservicehome, tempsize, "%s/%d/", baseservicehome, off);
475         }
476       else
477         {
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);
482         }
483       GNUNET_CONFIGURATION_set_value_string (pcfg,
484                                              "PATHS",
485                                              "SERVICEHOME", newservicehome);
486
487       pg->peers[off].cfg = pcfg;
488       pg->peers[off].daemon = GNUNET_TESTING_daemon_start (sched,
489                                                            pcfg,
490                                                            hostname,
491                                                            cb, cb_cls);
492       if (NULL == pg->peers[off].daemon)
493         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
494                     _("Could not start peer number %u!\n"), off);
495     }
496   return pg;
497 }
498
499
500 /**
501  * Shutdown all peers started in the given group.
502  * 
503  * @param pg handle to the peer group
504  */
505 void
506 GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg)
507 {
508   unsigned int off;
509
510   for (off = 0; off < pg->total; off++)
511     {
512       /* FIXME: should we wait for our
513          continuations to be called here? This
514          would require us to take a continuation
515          as well... */
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);
520     }
521   GNUNET_free (pg->peers);
522   if (NULL != pg->hosts)
523     {
524       GNUNET_free (pg->hosts[0].hostname);
525       GNUNET_free (pg->hosts);
526     }
527   GNUNET_free (pg);
528 }
529
530
531 /* end of testing_group.c */