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