Added comments, fixed debug code, added doxygen
[oweals/gnunet.git] / src / testing / testing_peergroup.c
1 /*
2  This file is part of GNUnet
3  (C) 2008-2011 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 3, 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_peergroup.c
23  * @brief API implementation for easy peer group creation
24  * @author Nathan Evans
25  * @author Christian Grothoff
26  *
27  */
28 #include "platform.h"
29 #include "gnunet_constants.h"
30 #include "gnunet_arm_service.h"
31 #include "gnunet_testing_lib.h"
32 #include "gnunet_core_service.h"
33 #include "gnunet_disk_lib.h"
34
35 /** Globals **/
36 #define DEFAULT_CONNECT_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30)
37
38 #define DEFAULT_CONNECT_ATTEMPTS 2
39
40 /** Struct definitions **/
41
42 struct PeerGroupStartupContext
43 {
44   struct GNUNET_TESTING_PeerGroup *pg;
45   const struct GNUNET_CONFIGURATION_Handle *cfg;
46   unsigned int total;
47   unsigned int peers_left;
48   unsigned long long max_concurrent_connections;
49   unsigned long long connect_attempts;
50   unsigned long long max_concurrent_ssh;
51   struct GNUNET_TIME_Absolute timeout;
52   GNUNET_TESTING_NotifyConnection connect_cb;
53   GNUNET_TESTING_NotifyCompletion peergroup_cb;
54
55   /**
56    * Closure for all peergroup callbacks.
57    */
58   void *cls;
59
60   const struct GNUNET_TESTING_Host *hostnames;
61   enum GNUNET_TESTING_Topology topology;
62
63   float topology_percentage;
64
65   float topology_probability;
66
67   enum GNUNET_TESTING_Topology restrict_topology;
68   char *restrict_transports;
69   enum GNUNET_TESTING_Topology connect_topology;
70   enum GNUNET_TESTING_TopologyOption connect_topology_option;
71   double connect_topology_option_modifier;
72   int verbose;
73
74   struct ProgressMeter *hostkey_meter;
75   struct ProgressMeter *peer_start_meter;
76   struct ProgressMeter *connect_meter;
77
78   /**
79    * Task used to kill the peergroup.
80    */
81   GNUNET_SCHEDULER_TaskIdentifier die_task;
82
83   char *fail_reason;
84
85   /**
86    * Variable used to store the number of connections we should wait for.
87    */
88   unsigned int expected_connections;
89
90   /**
91    * Time when the connecting peers was started.
92    */
93   struct GNUNET_TIME_Absolute connect_start_time;
94
95   /**
96    * The total number of connections that have been created so far.
97    */
98   unsigned int total_connections;
99
100   /**
101    * The total number of connections that have failed so far.
102    */
103   unsigned int failed_connections;
104
105   /**
106    * File handle to write out topology in dot format.
107    */
108   struct GNUNET_DISK_FileHandle *topology_output_file;
109 };
110
111 struct TopologyOutputContext
112 {
113   struct GNUNET_DISK_FileHandle *file;
114   GNUNET_TESTING_NotifyCompletion notify_cb;
115   void *notify_cb_cls;
116 };
117
118 /**
119  * Simple struct to keep track of progress, and print a
120  * percentage meter for long running tasks.
121  */
122 struct ProgressMeter
123 {
124   /**
125    * Total number of tasks to complete.
126    */
127   unsigned int total;
128
129   /**
130    * Print percentage done after modnum tasks.
131    */
132   unsigned int modnum;
133
134   /**
135    * Print a . each dotnum tasks.
136    */
137   unsigned int dotnum;
138
139   /**
140    * Total number completed thus far.
141    */
142   unsigned int completed;
143
144   /**
145    * Whether or not to print.
146    */
147   int print;
148
149   /**
150    * Startup string for progress meter.
151    */
152   char *startup_string;
153 };
154
155
156 /** Utility functions **/
157
158 /**
159  * Create a meter to keep track of the progress of some task.
160  *
161  * @param total the total number of items to complete
162  * @param start_string a string to prefix the meter with (if printing)
163  * @param print GNUNET_YES to print the meter, GNUNET_NO to count
164  *              internally only
165  *
166  * @return the progress meter
167  */
168 static struct ProgressMeter *
169 create_meter (unsigned int total, char *start_string, int print)
170 {
171   struct ProgressMeter *ret;
172
173   ret = GNUNET_malloc (sizeof (struct ProgressMeter));
174   ret->print = print;
175   ret->total = total;
176   ret->modnum = total / 4;
177   ret->dotnum = (total / 50) + 1;
178   if (start_string != NULL)
179     ret->startup_string = GNUNET_strdup (start_string);
180   else
181     ret->startup_string = GNUNET_strdup ("");
182
183   return ret;
184 }
185
186 /**
187  * Update progress meter (increment by one).
188  *
189  * @param meter the meter to update and print info for
190  *
191  * @return GNUNET_YES if called the total requested,
192  *         GNUNET_NO if more items expected
193  */
194 static int
195 update_meter (struct ProgressMeter *meter)
196 {
197   if (meter->print == GNUNET_YES)
198   {
199     if (meter->completed % meter->modnum == 0)
200     {
201       if (meter->completed == 0)
202       {
203         fprintf (stdout, "%sProgress: [0%%", meter->startup_string);
204       }
205       else
206         fprintf (stdout, "%d%%",
207                  (int) (((float) meter->completed / meter->total) * 100));
208     }
209     else if (meter->completed % meter->dotnum == 0)
210       fprintf (stdout, ".");
211
212     if (meter->completed + 1 == meter->total)
213       fprintf (stdout, "%d%%]\n", 100);
214     fflush (stdout);
215   }
216   meter->completed++;
217
218   if (meter->completed == meter->total)
219     return GNUNET_YES;
220   return GNUNET_NO;
221 }
222
223 /**
224  * Reset progress meter.
225  *
226  * @param meter the meter to reset
227  *
228  * @return GNUNET_YES if meter reset,
229  *         GNUNET_SYSERR on error
230  */
231 static int
232 reset_meter (struct ProgressMeter *meter)
233 {
234   if (meter == NULL)
235     return GNUNET_SYSERR;
236
237   meter->completed = 0;
238   return GNUNET_YES;
239 }
240
241 /**
242  * Release resources for meter
243  *
244  * @param meter the meter to free
245  */
246 static void
247 free_meter (struct ProgressMeter *meter)
248 {
249   GNUNET_free_non_null (meter->startup_string);
250   GNUNET_free (meter);
251 }
252
253
254 /** Functions for creating, starting and connecting the peergroup **/
255
256 /**
257  * Check whether peers successfully shut down.
258  */
259 static void
260 internal_shutdown_callback (void *cls, const char *emsg)
261 {
262   struct PeerGroupStartupContext *pg_start_ctx = cls;
263
264   if (emsg != NULL)
265     pg_start_ctx->peergroup_cb (pg_start_ctx->cls, emsg);
266   else
267     pg_start_ctx->peergroup_cb (pg_start_ctx->cls, pg_start_ctx->fail_reason);
268 }
269
270 /**
271  * Check if the get_handle is being used, if so stop the request.  Either
272  * way, schedule the end_badly_cont function which actually shuts down the
273  * test.
274  */
275 static void
276 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
277 {
278   struct PeerGroupStartupContext *pg_start_ctx = cls;
279
280   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
281               "Failing peer group startup with error: `%s'!\n",
282               pg_start_ctx->fail_reason);
283
284   GNUNET_TESTING_daemons_stop (pg_start_ctx->pg,
285                                GNUNET_TIME_absolute_get_remaining
286                                (pg_start_ctx->timeout),
287                                &internal_shutdown_callback, pg_start_ctx);
288
289   if (pg_start_ctx->hostkey_meter != NULL)
290     free_meter (pg_start_ctx->hostkey_meter);
291   if (pg_start_ctx->peer_start_meter != NULL)
292     free_meter (pg_start_ctx->peer_start_meter);
293   if (pg_start_ctx->connect_meter != NULL)
294     free_meter (pg_start_ctx->connect_meter);
295 }
296
297 /**
298  * This function is called whenever a connection attempt is finished between two of
299  * the started peers (started with GNUNET_TESTING_daemons_start).  The total
300  * number of times this function is called should equal the number returned
301  * from the GNUNET_TESTING_connect_topology call.
302  *
303  * The emsg variable is NULL on success (peers connected), and non-NULL on
304  * failure (peers failed to connect).
305  */
306 static void
307 internal_topology_callback (void *cls, const struct GNUNET_PeerIdentity *first,
308                             const struct GNUNET_PeerIdentity *second,
309                             uint32_t distance,
310                             const struct GNUNET_CONFIGURATION_Handle *first_cfg,
311                             const struct GNUNET_CONFIGURATION_Handle
312                             *second_cfg,
313                             struct GNUNET_TESTING_Daemon *first_daemon,
314                             struct GNUNET_TESTING_Daemon *second_daemon,
315                             const char *emsg)
316 {
317   struct PeerGroupStartupContext *pg_start_ctx = cls;
318   char *temp_str;
319   char *second_str;
320   int temp;
321
322 #if TIMING
323   unsigned long long duration;
324   unsigned long long total_duration;
325   unsigned int new_connections;
326   unsigned int new_failed_connections;
327   double conns_per_sec_recent;
328   double conns_per_sec_total;
329   double failed_conns_per_sec_recent;
330   double failed_conns_per_sec_total;
331 #endif
332
333 #if TIMING
334   if (GNUNET_TIME_absolute_get_difference
335       (connect_last_time,
336        GNUNET_TIME_absolute_get ()).rel_value >
337       GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
338                                      CONN_UPDATE_DURATION).rel_value)
339   {
340     /* Get number of new connections */
341     new_connections = total_connections - previous_connections;
342
343     /* Get number of new FAILED connections */
344     new_failed_connections = failed_connections - previous_failed_connections;
345
346     /* Get duration in seconds */
347     duration =
348         GNUNET_TIME_absolute_get_difference (connect_last_time,
349                                              GNUNET_TIME_absolute_get
350                                              ()).rel_value / 1000;
351     total_duration =
352         GNUNET_TIME_absolute_get_difference (connect_start_time,
353                                              GNUNET_TIME_absolute_get
354                                              ()).rel_value / 1000;
355
356     failed_conns_per_sec_recent = (double) new_failed_connections / duration;
357     failed_conns_per_sec_total = (double) failed_connections / total_duration;
358     conns_per_sec_recent = (double) new_connections / duration;
359     conns_per_sec_total = (double) total_connections / total_duration;
360     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
361                 "Recent: %.2f/s, Total: %.2f/s, Recent failed: %.2f/s, total failed %.2f/s\n",
362                 conns_per_sec_recent, CONN_UPDATE_DURATION, conns_per_sec_total,
363                 failed_conns_per_sec_recent, failed_conns_per_sec_total);
364     connect_last_time = GNUNET_TIME_absolute_get ();
365     previous_connections = total_connections;
366     previous_failed_connections = failed_connections;
367     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
368                 "have %u total_connections, %u failed\n", total_connections,
369                 failed_connections);
370   }
371 #endif
372
373
374   if (emsg == NULL)
375   {
376     pg_start_ctx->total_connections++;
377 #if VERBOSE > 1
378     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
379                 "connected peer %s to peer %s, distance %u\n",
380                 first_daemon->shortname, second_daemon->shortname, distance);
381 #endif
382     if (pg_start_ctx->topology_output_file != NULL)
383     {
384       second_str = GNUNET_strdup (GNUNET_i2s (second));
385       temp =
386           GNUNET_asprintf (&temp_str, "\t\"%s\" -- \"%s\"\n",
387                            GNUNET_i2s (first), second_str);
388       GNUNET_free (second_str);
389       if (temp > 0)
390         GNUNET_DISK_file_write (pg_start_ctx->topology_output_file, temp_str,
391                                 temp);
392       GNUNET_free (temp_str);
393     }
394   }
395   else
396   {
397     pg_start_ctx->failed_connections++;
398 #if VERBOSE
399     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
400                 "Failed to connect peer %s to peer %s with error :\n%s\n",
401                 first_daemon->shortname, second_daemon->shortname, emsg);
402
403     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
404                 "Failed to connect peer %s to peer %s with error :\n%s\n",
405                 first_daemon->shortname, second_daemon->shortname, emsg);
406 #endif
407   }
408
409   GNUNET_assert (pg_start_ctx->connect_meter != NULL);
410   if (pg_start_ctx->connect_cb != NULL)
411     pg_start_ctx->connect_cb (pg_start_ctx->cls, first, second, distance,
412                               first_cfg, second_cfg, first_daemon,
413                               second_daemon, emsg);
414   if (GNUNET_YES == update_meter (pg_start_ctx->connect_meter))
415   {
416 #if VERBOSE
417     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
418                 "Created %d total connections, which is our target number!  Starting next phase of testing.\n",
419                 pg_start_ctx->total_connections);
420 #endif
421
422 #if TIMING
423     total_duration =
424         GNUNET_TIME_absolute_get_difference (connect_start_time,
425                                              GNUNET_TIME_absolute_get
426                                              ()).rel_value / 1000;
427     failed_conns_per_sec_total = (double) failed_connections / total_duration;
428     conns_per_sec_total = (double) total_connections / total_duration;
429     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
430                 "Overall connection info --- Total: %u, Total Failed %u/s\n",
431                 total_connections, failed_connections);
432     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
433                 "Overall connection info --- Total: %.2f/s, Total Failed %.2f/s\n",
434                 conns_per_sec_total, failed_conns_per_sec_total);
435 #endif
436
437     GNUNET_assert (pg_start_ctx->die_task != GNUNET_SCHEDULER_NO_TASK);
438     GNUNET_SCHEDULER_cancel (pg_start_ctx->die_task);
439
440     /* Call final callback, signifying that the peer group has been started and connected */
441     if (pg_start_ctx->peergroup_cb != NULL)
442       pg_start_ctx->peergroup_cb (pg_start_ctx->cls, NULL);
443
444     if (pg_start_ctx->topology_output_file != NULL)
445     {
446       temp = GNUNET_asprintf (&temp_str, "}\n");
447       if (temp > 0)
448         GNUNET_DISK_file_write (pg_start_ctx->topology_output_file, temp_str,
449                                 temp);
450       GNUNET_free (temp_str);
451       GNUNET_DISK_file_close (pg_start_ctx->topology_output_file);
452     }
453   }
454 }
455
456 static void
457 internal_peers_started_callback (void *cls,
458                                  const struct GNUNET_PeerIdentity *id,
459                                  const struct GNUNET_CONFIGURATION_Handle *cfg,
460                                  struct GNUNET_TESTING_Daemon *d,
461                                  const char *emsg)
462 {
463   struct PeerGroupStartupContext *pg_start_ctx = cls;
464
465   if (emsg != NULL)
466   {
467     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
468                 "Failed to start daemon with error: `%s'\n", emsg);
469     return;
470   }
471   GNUNET_assert (id != NULL);
472
473 #if VERBOSE > 1
474   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Started daemon %llu out of %llu\n",
475               (pg_start_ctx->total - pg_start_ctx->peers_left) + 1,
476               pg_start_ctx->total);
477 #endif
478
479   pg_start_ctx->peers_left--;
480
481   if (GNUNET_YES == update_meter (pg_start_ctx->peer_start_meter))
482   {
483 #if VERBOSE
484     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
485                 "All %d daemons started, now connecting peers!\n",
486                 pg_start_ctx->total);
487 #endif
488     GNUNET_assert (pg_start_ctx->die_task != GNUNET_SCHEDULER_NO_TASK);
489     GNUNET_SCHEDULER_cancel (pg_start_ctx->die_task);
490
491     pg_start_ctx->expected_connections = UINT_MAX;
492     // FIXME: why whould peers_left be != 0?? Or pg NULL?
493     if ((pg_start_ctx->pg != NULL) && (pg_start_ctx->peers_left == 0))
494     {
495       pg_start_ctx->connect_start_time = GNUNET_TIME_absolute_get ();
496       pg_start_ctx->expected_connections =
497           GNUNET_TESTING_connect_topology (pg_start_ctx->pg,
498                                            pg_start_ctx->connect_topology,
499                                            pg_start_ctx->connect_topology_option,
500                                            pg_start_ctx->connect_topology_option_modifier,
501                                            DEFAULT_CONNECT_TIMEOUT,
502                                            pg_start_ctx->connect_attempts, NULL,
503                                            NULL);
504
505       pg_start_ctx->connect_meter =
506           create_meter (pg_start_ctx->expected_connections, "Peer connection ",
507                         pg_start_ctx->verbose);
508       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Have %d expected connections\n",
509                   pg_start_ctx->expected_connections);
510     }
511
512     if (pg_start_ctx->expected_connections == 0)
513     {
514       GNUNET_free_non_null (pg_start_ctx->fail_reason);
515       pg_start_ctx->fail_reason =
516           GNUNET_strdup ("from connect topology (bad return)");
517       pg_start_ctx->die_task =
518           GNUNET_SCHEDULER_add_now (&end_badly, pg_start_ctx);
519       return;
520     }
521
522     GNUNET_free_non_null (pg_start_ctx->fail_reason);
523     pg_start_ctx->fail_reason =
524         GNUNET_strdup ("from connect topology (timeout)");
525     pg_start_ctx->die_task =
526         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
527                                       (pg_start_ctx->timeout), &end_badly,
528                                       pg_start_ctx);
529   }
530 }
531
532 /**
533  * Callback indicating that the hostkey was created for a peer.
534  *
535  * @param cls NULL
536  * @param id the peer identity
537  * @param d the daemon handle (pretty useless at this point, remove?)
538  * @param emsg non-null on failure
539  */
540 static void
541 internal_hostkey_callback (void *cls, const struct GNUNET_PeerIdentity *id,
542                            struct GNUNET_TESTING_Daemon *d, const char *emsg)
543 {
544   struct PeerGroupStartupContext *pg_start_ctx = cls;
545   unsigned int create_expected_connections;
546
547   if (emsg != NULL)
548   {
549     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
550                 "Hostkey callback received error: %s\n", emsg);
551   }
552
553 #if VERBOSE > 1
554   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
555               "Hostkey (%d/%d) created for peer `%s'\n",
556               pg_start_ctx->total - pg_start_ctx->peers_left,
557               pg_start_ctx->total, GNUNET_i2s (id));
558 #endif
559
560   pg_start_ctx->peers_left--;
561   if (GNUNET_YES == update_meter (pg_start_ctx->hostkey_meter))
562   {
563     GNUNET_SCHEDULER_cancel (pg_start_ctx->die_task);
564     GNUNET_free_non_null (pg_start_ctx->fail_reason);
565     /* Set up task in case topology creation doesn't finish
566      * within a reasonable amount of time */
567     pg_start_ctx->fail_reason = GNUNET_strdup ("from create_topology");
568     pg_start_ctx->die_task =
569         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
570                                       (pg_start_ctx->timeout), &end_badly,
571                                       pg_start_ctx);
572     pg_start_ctx->peers_left = pg_start_ctx->total;     /* Reset counter */
573     create_expected_connections =
574         GNUNET_TESTING_create_topology (pg_start_ctx->pg,
575                                         pg_start_ctx->topology,
576                                         pg_start_ctx->restrict_topology,
577                                         pg_start_ctx->restrict_transports);
578     if (create_expected_connections > 0)
579     {
580       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
581                   "Topology set up, have %u expected connections, now starting peers!\n",
582                   create_expected_connections);
583       GNUNET_TESTING_daemons_continue_startup (pg_start_ctx->pg);
584     }
585     else
586     {
587       GNUNET_SCHEDULER_cancel (pg_start_ctx->die_task);
588       GNUNET_free_non_null (pg_start_ctx->fail_reason);
589       pg_start_ctx->fail_reason =
590           GNUNET_strdup ("from create topology (bad return)");
591       pg_start_ctx->die_task =
592           GNUNET_SCHEDULER_add_now (&end_badly, pg_start_ctx);
593       return;
594     }
595
596     GNUNET_SCHEDULER_cancel (pg_start_ctx->die_task);
597     GNUNET_free_non_null (pg_start_ctx->fail_reason);
598     pg_start_ctx->fail_reason =
599         GNUNET_strdup ("from continue startup (timeout)");
600     pg_start_ctx->die_task =
601         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
602                                       (pg_start_ctx->timeout), &end_badly,
603                                       pg_start_ctx);
604   }
605 }
606
607
608 /**
609  * Prototype of a callback function indicating that two peers
610  * are currently connected.
611  *
612  * @param cls closure
613  * @param first peer id for first daemon
614  * @param second peer id for the second daemon
615  * @param emsg error message (NULL on success)
616  */
617 void
618 write_topology_cb (void *cls, const struct GNUNET_PeerIdentity *first,
619                    const struct GNUNET_PeerIdentity *second, const char *emsg)
620 {
621   struct TopologyOutputContext *topo_ctx;
622   int temp;
623   char *temp_str;
624   char *temp_pid2;
625
626   topo_ctx = (struct TopologyOutputContext *) cls;
627   GNUNET_assert (topo_ctx->file != NULL);
628   if ((emsg == NULL) && (first != NULL) && (second != NULL))
629   {
630     GNUNET_assert (first != NULL);
631     GNUNET_assert (second != NULL);
632     temp_pid2 = GNUNET_strdup (GNUNET_i2s (second));
633     temp =
634         GNUNET_asprintf (&temp_str, "\t\"%s\" -- \"%s\"\n", GNUNET_i2s (first),
635                          temp_pid2);
636     GNUNET_free (temp_pid2);
637     GNUNET_DISK_file_write (topo_ctx->file, temp_str, temp);
638   }
639   else if ((emsg == NULL) && (first == NULL) && (second == NULL))
640   {
641     temp = GNUNET_asprintf (&temp_str, "}\n");
642     GNUNET_DISK_file_write (topo_ctx->file, temp_str, temp);
643     GNUNET_DISK_file_close (topo_ctx->file);
644     topo_ctx->notify_cb (topo_ctx->notify_cb_cls, NULL);
645     GNUNET_free (topo_ctx);
646   }
647   else
648   {
649     temp = GNUNET_asprintf (&temp_str, "}\n");
650     GNUNET_DISK_file_write (topo_ctx->file, temp_str, temp);
651     GNUNET_DISK_file_close (topo_ctx->file);
652     topo_ctx->notify_cb (topo_ctx->notify_cb_cls, emsg);
653     GNUNET_free (topo_ctx);
654   }
655 }
656
657 /**
658  * Print current topology to a graphviz readable file.
659  *
660  * @param pg a currently running peergroup to print to file
661  * @param output_filename the file to write the topology to
662  * @param notify_cb callback to call upon completion or failure
663  * @param notify_cb_cls closure for notify_cb
664  *
665  */
666 void
667 GNUNET_TESTING_peergroup_topology_to_file (struct GNUNET_TESTING_PeerGroup *pg,
668                                            const char *output_filename,
669                                            GNUNET_TESTING_NotifyCompletion
670                                            notify_cb, void *notify_cb_cls)
671 {
672   struct TopologyOutputContext *topo_ctx;
673   int temp;
674   char *temp_str;
675
676   topo_ctx = GNUNET_malloc (sizeof (struct TopologyOutputContext));
677
678   topo_ctx->notify_cb = notify_cb;
679   topo_ctx->notify_cb_cls = notify_cb_cls;
680   topo_ctx->file =
681       GNUNET_DISK_file_open (output_filename,
682                              GNUNET_DISK_OPEN_READWRITE |
683                              GNUNET_DISK_OPEN_CREATE,
684                              GNUNET_DISK_PERM_USER_READ |
685                              GNUNET_DISK_PERM_USER_WRITE);
686   if (topo_ctx->file == NULL)
687   {
688     notify_cb (notify_cb_cls, "Failed to open output file!");
689     GNUNET_free (topo_ctx);
690     return;
691   }
692
693   temp = GNUNET_asprintf (&temp_str, "strict graph G {\n");
694   if (temp > 0)
695     GNUNET_DISK_file_write (topo_ctx->file, temp_str, temp);
696   GNUNET_free_non_null (temp_str);
697   GNUNET_TESTING_get_topology (pg, &write_topology_cb, topo_ctx);
698 }
699
700 /**
701  * Start a peer group with a given number of peers.  Notify
702  * on completion of peer startup and connection based on given
703  * topological constraints.  Optionally notify on each
704  * established connection.
705  *
706  * @param cfg configuration template to use
707  * @param total number of daemons to start
708  * @param timeout total time allowed for peers to start
709  * @param connect_cb function to call each time two daemons are connected
710  * @param peergroup_cb function to call once all peers are up and connected
711  * @param peergroup_cls closure for peergroup callbacks
712  * @param hostnames linked list of host structs to use to start peers on
713  *                  (NULL to run on localhost only)
714  *
715  * @return NULL on error, otherwise handle to control peer group
716  */
717 struct GNUNET_TESTING_PeerGroup *
718 GNUNET_TESTING_peergroup_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
719                                 unsigned int total,
720                                 struct GNUNET_TIME_Relative timeout,
721                                 GNUNET_TESTING_NotifyConnection connect_cb,
722                                 GNUNET_TESTING_NotifyCompletion peergroup_cb,
723                                 void *peergroup_cls,
724                                 const struct GNUNET_TESTING_Host *hostnames)
725 {
726   struct PeerGroupStartupContext *pg_start_ctx;
727   unsigned long long temp_config_number;
728   char *temp_str;
729   int temp;
730
731   GNUNET_assert (total > 0);
732   GNUNET_assert (cfg != NULL);
733
734   pg_start_ctx = GNUNET_malloc (sizeof (struct PeerGroupStartupContext));
735
736   if (GNUNET_OK !=
737       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "connect_attempts",
738                                              &pg_start_ctx->connect_attempts))
739   {
740     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n",
741                 "testing", "connect_attempts");
742     GNUNET_free (pg_start_ctx);
743     return NULL;
744   }
745
746   if (GNUNET_OK !=
747       GNUNET_CONFIGURATION_get_value_number (cfg, "testing",
748                                              "max_outstanding_connections",
749                                              &pg_start_ctx->max_concurrent_connections))
750   {
751     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n",
752                 "testing", "max_outstanding_connections");
753     GNUNET_free (pg_start_ctx);
754     return NULL;
755   }
756
757   if (GNUNET_OK !=
758       GNUNET_CONFIGURATION_get_value_number (cfg, "testing",
759                                              "max_concurrent_ssh",
760                                              &pg_start_ctx->max_concurrent_ssh))
761   {
762     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n",
763                 "testing", "max_concurrent_ssh");
764     GNUNET_free (pg_start_ctx);
765     return NULL;
766   }
767
768   if (GNUNET_SYSERR ==
769       (pg_start_ctx->verbose =
770        GNUNET_CONFIGURATION_get_value_yesno (cfg, "testing",
771                                              "use_progressbars")))
772   {
773     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n",
774                 "testing", "use_progressbars");
775     GNUNET_free (pg_start_ctx);
776     return NULL;
777   }
778
779   if (GNUNET_OK ==
780       GNUNET_CONFIGURATION_get_value_number (cfg, "testing",
781                                              "peergroup_timeout",
782                                              &temp_config_number))
783     pg_start_ctx->timeout =
784         GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply
785                                           (GNUNET_TIME_UNIT_SECONDS,
786                                            temp_config_number));
787   else
788   {
789     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n",
790                 "testing", "peergroup_timeout");
791     GNUNET_free (pg_start_ctx);
792     return NULL;
793   }
794
795
796   /* Read topology related options from the configuration file */
797   temp_str = NULL;
798   if ((GNUNET_YES ==
799        GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "topology",
800                                               &temp_str)) &&
801       (GNUNET_NO ==
802        GNUNET_TESTING_topology_get (&pg_start_ctx->topology, temp_str)))
803   {
804     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
805                 "Invalid topology `%s' given for section %s option %s\n",
806                 temp_str, "TESTING", "TOPOLOGY");
807     pg_start_ctx->topology = GNUNET_TESTING_TOPOLOGY_CLIQUE;    /* Defaults to NONE, so set better default here */
808   }
809   GNUNET_free_non_null (temp_str);
810
811   if (GNUNET_YES ==
812       GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
813                                              "topology_output_file", &temp_str))
814   {
815     pg_start_ctx->topology_output_file =
816         GNUNET_DISK_file_open (temp_str,
817                                GNUNET_DISK_OPEN_READWRITE |
818                                GNUNET_DISK_OPEN_CREATE,
819                                GNUNET_DISK_PERM_USER_READ |
820                                GNUNET_DISK_PERM_USER_WRITE);
821     if (pg_start_ctx->topology_output_file != NULL)
822     {
823       GNUNET_free (temp_str);
824       temp = GNUNET_asprintf (&temp_str, "strict graph G {\n");
825       if (temp > 0)
826         GNUNET_DISK_file_write (pg_start_ctx->topology_output_file, temp_str,
827                                 temp);
828     }
829     GNUNET_free (temp_str);
830   }
831
832   if (GNUNET_OK !=
833       GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "percentage",
834                                              &temp_str))
835     pg_start_ctx->topology_percentage = 0.5;
836   else
837   {
838     pg_start_ctx->topology_percentage = atof (temp_str);
839     GNUNET_free (temp_str);
840   }
841
842   if (GNUNET_OK !=
843       GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "probability",
844                                              &temp_str))
845     pg_start_ctx->topology_probability = 0.5;
846   else
847   {
848     pg_start_ctx->topology_probability = atof (temp_str);
849     GNUNET_free (temp_str);
850   }
851
852   if ((GNUNET_YES ==
853        GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
854                                               "connect_topology", &temp_str)) &&
855       (GNUNET_NO ==
856        GNUNET_TESTING_topology_get (&pg_start_ctx->connect_topology, temp_str)))
857   {
858     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
859                 "Invalid connect topology `%s' given for section %s option %s\n",
860                 temp_str, "TESTING", "CONNECT_TOPOLOGY");
861   }
862   GNUNET_free_non_null (temp_str);
863
864   if ((GNUNET_YES ==
865        GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
866                                               "connect_topology_option",
867                                               &temp_str)) &&
868       (GNUNET_NO ==
869        GNUNET_TESTING_topology_option_get
870        (&pg_start_ctx->connect_topology_option, temp_str)))
871   {
872     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
873                 "Invalid connect topology option `%s' given for section %s option %s\n",
874                 temp_str, "TESTING", "CONNECT_TOPOLOGY_OPTION");
875     pg_start_ctx->connect_topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_ALL; /* Defaults to NONE, set to ALL */
876   }
877   GNUNET_free_non_null (temp_str);
878
879   if (GNUNET_YES ==
880       GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
881                                              "connect_topology_option_modifier",
882                                              &temp_str))
883   {
884     if (sscanf
885         (temp_str, "%lf", &pg_start_ctx->connect_topology_option_modifier) != 1)
886     {
887       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
888                   _
889                   ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
890                   temp_str, "connect_topology_option_modifier", "TESTING");
891       GNUNET_free (temp_str);
892       GNUNET_free (pg_start_ctx);
893       return NULL;
894     }
895     GNUNET_free (temp_str);
896   }
897
898   if (GNUNET_YES !=
899       GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
900                                              "blacklist_transports",
901                                              &pg_start_ctx->restrict_transports))
902     pg_start_ctx->restrict_transports = NULL;
903
904   pg_start_ctx->restrict_topology = GNUNET_TESTING_TOPOLOGY_NONE;
905   if ((GNUNET_YES ==
906        GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
907                                               "blacklist_topology", &temp_str))
908       && (GNUNET_NO ==
909           GNUNET_TESTING_topology_get (&pg_start_ctx->restrict_topology,
910                                        temp_str)))
911   {
912     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
913                 "Invalid topology `%s' given for section %s option %s\n",
914                 temp_str, "TESTING", "BLACKLIST_TOPOLOGY");
915   }
916
917   GNUNET_free_non_null (temp_str);
918
919   pg_start_ctx->cfg = cfg;
920   pg_start_ctx->total = total;
921   pg_start_ctx->peers_left = total;
922   pg_start_ctx->connect_cb = connect_cb;
923   pg_start_ctx->peergroup_cb = peergroup_cb;
924   pg_start_ctx->cls = peergroup_cls;
925   pg_start_ctx->hostnames = hostnames;
926   pg_start_ctx->hostkey_meter =
927       create_meter (pg_start_ctx->peers_left, "Hostkeys created ",
928                     pg_start_ctx->verbose);
929   pg_start_ctx->peer_start_meter =
930       create_meter (pg_start_ctx->peers_left, "Peers started ",
931                     pg_start_ctx->verbose);
932   /* Make compilers happy */
933   reset_meter (pg_start_ctx->peer_start_meter);
934   pg_start_ctx->fail_reason =
935       GNUNET_strdup
936       ("didn't generate all hostkeys within allowed startup time!");
937   pg_start_ctx->die_task =
938       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
939                                     (pg_start_ctx->timeout), &end_badly,
940                                     pg_start_ctx);
941
942   pg_start_ctx->pg =
943       GNUNET_TESTING_daemons_start (pg_start_ctx->cfg, pg_start_ctx->peers_left,
944                                     pg_start_ctx->max_concurrent_connections,
945                                     pg_start_ctx->max_concurrent_ssh,
946                                     GNUNET_TIME_absolute_get_remaining
947                                     (pg_start_ctx->timeout),
948                                     &internal_hostkey_callback, pg_start_ctx,
949                                     &internal_peers_started_callback,
950                                     pg_start_ctx, &internal_topology_callback,
951                                     pg_start_ctx, pg_start_ctx->hostnames);
952
953   return pg_start_ctx->pg;
954 }
955
956 /* end of testing_peergroup.c */