adhering to indentation standard
[oweals/gnunet.git] / src / testbed / testbed_api_testbed.c
1 /*
2       This file is part of GNUnet
3       (C) 2008--2012 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 testbed/testbed_api_testbed.c
23  * @brief high-level testbed management
24  * @author Christian Grothoff
25  * @author Sree Harsha Totakura
26  */
27
28 #include "platform.h"
29 #include "gnunet_testbed_service.h"
30
31 /**
32  * Generic loggins shorthand
33  */
34 #define LOG(kind,...)                                   \
35   GNUNET_log_from (kind, "testbed-api-testbed", __VA_ARGS__)
36
37 /**
38  * Opaque handle to an abstract operation to be executed by the testing framework.
39  */
40 struct GNUNET_TESTBED_Testbed
41 {
42   // FIXME!
43 };
44
45
46 /**
47  * DLL of operations
48  */
49 struct DLLOperation
50 {
51   /**
52    * The testbed operation handle
53    */
54   struct GNUNET_TESTBED_Operation *op;
55
56   /**
57    * Context information for GNUNET_TESTBED_run()
58    */
59   struct RunContext *rc;
60
61   /**
62    * Closure
63    */
64   void *cls;
65
66   /**
67    * The next pointer for DLL
68    */
69   struct DLLOperation *next;
70
71   /**
72    * The prev pointer for DLL
73    */
74   struct DLLOperation *prev;
75 };
76
77
78 /**
79  * States of RunContext
80  */
81 enum State
82 {
83   /**
84    * Initial state
85    */
86   RC_INIT = 0,
87
88   /**
89    * Peers have been started
90    */
91   RC_PEERS_STARTED,
92
93   /**
94    * Peers are stopped
95    */
96   RC_PEERS_STOPPED,
97
98   /**
99    * Peers are destroyed
100    */
101   RC_PEERS_DESTROYED
102 };
103
104
105 /**
106  * Testbed Run Handle
107  */
108 struct RunContext
109 {
110   /**
111    * The controller handle
112    */
113   struct GNUNET_TESTBED_Controller *c;
114
115   /**
116    * Handle to the host on which the controller runs
117    */
118   struct GNUNET_TESTBED_Host *h;
119
120   /**
121    * The handle to the controller process
122    */
123   struct GNUNET_TESTBED_ControllerProc *cproc;
124
125   /**
126    * The callback to use as controller callback
127    */
128   GNUNET_TESTBED_ControllerCallback cc;
129
130   /**
131    * The pointer to the controller callback
132    */
133   void *cc_cls;
134
135   /**
136    * Master task to call when testbed initialization is done
137    */
138   GNUNET_SCHEDULER_Task master;
139
140   /**
141    * The closure for the master task
142    */
143   void *master_cls;
144
145   /**
146    * The head element of DLL operations
147    */
148   struct DLLOperation *dll_op_head;
149
150   /**
151    * The tail element of DLL operations
152    */
153   struct DLLOperation *dll_op_tail;
154
155   /**
156    * Array of peers which we create
157    */
158   struct GNUNET_TESTBED_Peer **peers;
159
160   /**
161    * The event mask for the controller
162    */
163   uint64_t event_mask;
164
165   /**
166    * State of this context
167    */
168   enum State state;
169
170   /**
171    * Current peer count for an operation; Set this to 0 and increment for each
172    * successful operation on a peer
173    */
174   unsigned int peer_count;
175
176   /**
177    * number of peers to start
178    */
179   unsigned int num_peers;
180
181 };
182
183
184
185
186 /**
187  * Configure and run a testbed using the given
188  * master controller on 'num_hosts' starting
189  * 'num_peers' using the given peer configuration.
190  *
191  * @param controller master controller for the testbed
192  *                   (must not be destroyed until after the
193  *                    testbed is destroyed).
194  * @param num_hosts number of hosts in 'hosts', 0 to only
195  *        use 'localhost'
196  * @param hosts list of hosts to use for the testbed
197  * @param num_peers number of peers to start
198  * @param peer_cfg peer configuration template to use
199  * @param underlay_topology underlay topology to create
200  * @param va topology-specific options
201  * @return handle to the testbed
202  */
203 struct GNUNET_TESTBED_Testbed *
204 GNUNET_TESTBED_create_va (struct GNUNET_TESTBED_Controller *controller,
205                           unsigned int num_hosts,
206                           struct GNUNET_TESTBED_Host **hosts,
207                           unsigned int num_peers,
208                           const struct GNUNET_CONFIGURATION_Handle *peer_cfg,
209                           enum GNUNET_TESTBED_TopologyOption underlay_topology,
210                           va_list va)
211 {
212   GNUNET_break (0);
213   return NULL;
214 }
215
216
217 /**
218  * Configure and run a testbed using the given
219  * master controller on 'num_hosts' starting
220  * 'num_peers' using the given peer configuration.
221  *
222  * @param controller master controller for the testbed
223  *                   (must not be destroyed until after the
224  *                    testbed is destroyed).
225  * @param num_hosts number of hosts in 'hosts', 0 to only
226  *        use 'localhost'
227  * @param hosts list of hosts to use for the testbed
228  * @param num_peers number of peers to start
229  * @param peer_cfg peer configuration template to use
230  * @param underlay_topology underlay topology to create
231  * @param ... topology-specific options
232  */
233 struct GNUNET_TESTBED_Testbed *
234 GNUNET_TESTBED_create (struct GNUNET_TESTBED_Controller *controller,
235                        unsigned int num_hosts,
236                        struct GNUNET_TESTBED_Host **hosts,
237                        unsigned int num_peers,
238                        const struct GNUNET_CONFIGURATION_Handle *peer_cfg,
239                        enum GNUNET_TESTBED_TopologyOption underlay_topology,
240                        ...)
241 {
242   GNUNET_break (0);
243   return NULL;
244 }
245
246
247 /**
248  * Destroy a testbed.  Stops all running peers and then
249  * destroys all peers.  Does NOT destroy the master controller.
250  *
251  * @param testbed testbed to destroy
252  */
253 void
254 GNUNET_TESTBED_destroy (struct GNUNET_TESTBED_Testbed *testbed)
255 {
256   GNUNET_break (0);
257 }
258
259
260 /**
261  * Task for starting peers
262  *
263  * @param cls the RunHandle
264  * @param tc the task context from scheduler
265  */
266 static void
267 start_peers_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
268 {
269   struct RunContext *rc = cls;
270   struct DLLOperation *dll_op;
271   unsigned int peer;
272
273   LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting Peers\n");
274   for (peer = 0; peer < rc->num_peers; peer++)
275   {
276     dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
277     dll_op->op = GNUNET_TESTBED_peer_start (rc->peers[peer]);
278     dll_op->cls = rc->peers[peer];
279     GNUNET_CONTAINER_DLL_insert_tail (rc->dll_op_head, rc->dll_op_tail, dll_op);
280   }
281   rc->peer_count = 0;
282 }
283
284
285 /**
286  * Functions of this signature are called when a peer has been successfully
287  * created
288  *
289  * @param cls the closure from GNUNET_TESTBED_peer_create()
290  * @param peer the handle for the created peer; NULL on any error during
291  *          creation
292  * @param emsg NULL if peer is not NULL; else MAY contain the error description
293  */
294 static void
295 peer_create_cb (void *cls, struct GNUNET_TESTBED_Peer *peer, const char *emsg)
296 {
297   struct DLLOperation *dll_op = cls;
298   struct RunContext *rc;
299
300   GNUNET_assert (NULL != dll_op);
301   rc = dll_op->rc;
302   GNUNET_assert (NULL != rc);
303   GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op);
304   GNUNET_TESTBED_operation_done (dll_op->op);
305   GNUNET_free (dll_op);
306   if (NULL == peer)
307   {
308     if (NULL != emsg)
309       LOG (GNUNET_ERROR_TYPE_WARNING, "Error while creating a peer: %s\n",
310            emsg);
311     /* FIXME: GNUNET_TESTBED_shutdown_run()? */
312     return;
313   }
314   rc->peers[rc->peer_count] = peer;
315   rc->peer_count++;
316   if (rc->peer_count < rc->num_peers)
317     return;
318   LOG (GNUNET_ERROR_TYPE_DEBUG, "Required peers created successfully\n");
319   GNUNET_SCHEDULER_add_now (&start_peers_task, rc);
320 }
321
322
323 /**
324  * Assuming all peers have been destroyed cleanup run handle
325  *
326  * @param cls the run handle
327  * @param tc the task context from scheduler
328  */
329 static void
330 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
331 {
332   struct RunContext *rc = cls;
333   struct DLLOperation *dll_op;
334
335   GNUNET_assert (NULL == rc->peers);
336   GNUNET_assert (RC_PEERS_DESTROYED == rc->state);
337   if (NULL != rc->c)
338     GNUNET_TESTBED_controller_disconnect (rc->c);
339   if (NULL != rc->cproc)
340     GNUNET_TESTBED_controller_stop (rc->cproc);
341   if (NULL != rc->h)
342     GNUNET_TESTBED_host_destroy (rc->h);
343   if (NULL != rc->dll_op_head)
344   {
345     LOG (GNUNET_ERROR_TYPE_WARNING,
346          _("Some operations are still pending. Cancelling them\n"));
347     while (NULL != (dll_op = rc->dll_op_head))
348     {
349       GNUNET_TESTBED_operation_cancel (dll_op->op);
350       GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op);
351       GNUNET_free (dll_op);
352     }
353   }
354   GNUNET_free (rc);
355 }
356
357
358 /**
359  * Signature of the event handler function called by the
360  * respective event controller.
361  *
362  * @param cls closure
363  * @param event information about the event
364  */
365 static void
366 event_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event)
367 {
368   struct RunContext *rc = cls;
369   struct DLLOperation *dll_op;
370   unsigned int peer_id;
371
372
373   if ((RC_INIT != rc->state) &&
374       ((GNUNET_TESTBED_ET_OPERATION_FINISHED == event->type) ||
375        (GNUNET_TESTBED_ET_PEER_STOP == event->type)))
376   {
377     for (dll_op = rc->dll_op_head; NULL != dll_op; dll_op = dll_op->next)
378     {
379       if ((GNUNET_TESTBED_ET_OPERATION_FINISHED == event->type) &&
380           (event->details.operation_finished.operation == dll_op->op))
381         break;
382       if ((GNUNET_TESTBED_ET_PEER_STOP == event->type) &&
383           (event->details.peer_stop.peer == dll_op->cls))
384         break;
385     }
386     if (NULL == dll_op)
387       goto call_cc;
388     GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op);
389     GNUNET_TESTBED_operation_done (dll_op->op);
390     GNUNET_free (dll_op);
391     rc->peer_count++;
392     if (rc->peer_count < rc->num_peers)
393       return;
394     switch (rc->state)
395     {
396     case RC_PEERS_STARTED:
397       rc->state = RC_PEERS_STOPPED;
398       rc->peer_count = 0;
399       for (peer_id = 0; peer_id < rc->num_peers; peer_id++)
400       {
401         dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
402         dll_op->op = GNUNET_TESTBED_peer_destroy (rc->peers[peer_id]);
403         GNUNET_CONTAINER_DLL_insert_tail (rc->dll_op_head, rc->dll_op_tail,
404                                           dll_op);
405       }
406       break;
407     case RC_PEERS_STOPPED:
408       rc->state = RC_PEERS_DESTROYED;
409       GNUNET_free (rc->peers);
410       rc->peers = NULL;
411       LOG (GNUNET_ERROR_TYPE_DEBUG, "All peers successfully destroyed\n");
412       GNUNET_SCHEDULER_add_now (&cleanup_task, rc);
413       break;
414     default:
415       GNUNET_assert (0);
416     }
417     return;
418   }
419
420 call_cc:
421   rc->cc (rc->cc_cls, event);
422   if (GNUNET_TESTBED_ET_PEER_START != event->type)
423     return;
424   for (dll_op = rc->dll_op_head; NULL != dll_op; dll_op = dll_op->next)
425     if ((NULL != dll_op->cls) &&
426         (event->details.peer_start.peer == dll_op->cls))
427       break;
428   GNUNET_assert (NULL != dll_op);
429   GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op);
430   GNUNET_TESTBED_operation_done (dll_op->op);
431   GNUNET_free (dll_op);
432   rc->peer_count++;
433   if (rc->peer_count < rc->num_peers)
434     return;
435   LOG (GNUNET_ERROR_TYPE_DEBUG, "Peers started successfully\n");
436   rc->state = RC_PEERS_STARTED;
437   GNUNET_SCHEDULER_add_continuation (rc->master, rc->master_cls,
438                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
439 }
440
441
442
443 /**
444  * Callback to signal successfull startup of the controller process
445  *
446  * @param cls the closure from GNUNET_TESTBED_controller_start()
447  * @param cfg the configuration with which the controller has been started;
448  *          NULL if status is not GNUNET_OK
449  * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not,
450  *          GNUNET_TESTBED_controller_stop() shouldn't be called in this case
451  */
452 static void
453 controller_status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg,
454                       int status)
455 {
456   struct RunContext *rc = cls;
457   struct DLLOperation *dll_op;
458   unsigned int peer;
459
460   if (status != GNUNET_OK)
461   {
462     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Testbed startup failed\n");
463     return;
464   }
465   rc->c =
466       GNUNET_TESTBED_controller_connect (cfg, rc->h, rc->event_mask, &event_cb,
467                                          rc);
468   rc->peers =
469       GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Peer *) * rc->num_peers);
470   GNUNET_assert (NULL != rc->c);
471   rc->peer_count = 0;
472   for (peer = 0; peer < rc->num_peers; peer++)
473   {
474     dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
475     dll_op->rc = rc;
476     dll_op->op =
477         GNUNET_TESTBED_peer_create (rc->c, rc->h, cfg, peer_create_cb, dll_op);
478     GNUNET_CONTAINER_DLL_insert_tail (rc->dll_op_head, rc->dll_op_tail, dll_op);
479   }
480 }
481
482
483 /**
484  * Stops the testbed run and releases any used resources
485  *
486  * @param rc the tesbed run handle
487  * @param tc the task context from scheduler
488  */
489 void
490 shutdown_run_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
491 {
492   struct RunContext *rc = cls;
493   struct DLLOperation *dll_op;
494   unsigned int peer;
495
496   if (NULL != rc->c)
497   {
498     if (NULL != rc->peers)
499     {
500       rc->peer_count = 0;
501       for (peer = 0; peer < rc->num_peers; peer++)
502       {
503         dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
504         dll_op->op = GNUNET_TESTBED_peer_stop (rc->peers[peer]);
505         dll_op->cls = rc->peers[peer];
506         GNUNET_CONTAINER_DLL_insert_tail (rc->dll_op_head, rc->dll_op_tail,
507                                           dll_op);
508       }
509       return;
510     }
511   }
512   rc->state = RC_PEERS_DESTROYED;       /* No peers are present so we consider the
513                                          * state where all peers are destroyed  */
514   GNUNET_SCHEDULER_add_now (&cleanup_task, rc);
515 }
516
517
518 /**
519  * Convenience method for running a testbed with
520  * a single call.  Underlay and overlay topology
521  * are configured using the "UNDERLAY" and "OVERLAY"
522  * options in the "[testbed]" section of the configuration\
523  * (with possible options given in "UNDERLAY_XXX" and/or
524  * "OVERLAY_XXX").
525  *
526  * The testbed is to be terminated using a call to
527  * "GNUNET_SCHEDULER_shutdown".
528  *
529  * @param host_filename name of the file with the 'hosts', NULL
530  *        to run everything on 'localhost'
531  * @param cfg configuration to use (for testbed, controller and peers)
532  * @param num_peers number of peers to start; FIXME: maybe put that ALSO into cfg?
533  * @param event_mask bit mask with set of events to call 'cc' for;
534  *                   or-ed values of "1LL" shifted by the
535  *                   respective 'enum GNUNET_TESTBED_EventType'
536  *                   (i.e.  "(1LL << GNUNET_TESTBED_ET_CONNECT) || ...")
537  * @param cc controller callback to invoke on events
538  * @param cc_cls closure for cc
539  * @param master task to run once the testbed is ready
540  */
541 void
542 GNUNET_TESTBED_run (const char *host_filename,
543                     const struct GNUNET_CONFIGURATION_Handle *cfg,
544                     unsigned int num_peers, uint64_t event_mask,
545                     GNUNET_TESTBED_ControllerCallback cc, void *cc_cls,
546                     GNUNET_SCHEDULER_Task master, void *master_cls)
547 {
548   struct RunContext *rc;
549
550   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START);
551   rc = GNUNET_malloc (sizeof (struct RunContext));
552   GNUNET_break (NULL == host_filename); /* Currently we do not support host
553                                          * files */
554   host_filename = NULL;
555   rc->h = GNUNET_TESTBED_host_create (NULL, NULL, 0);
556   GNUNET_assert (NULL != rc->h);
557   rc->cproc =
558       GNUNET_TESTBED_controller_start ("127.0.0.1", rc->h, cfg,
559                                        &controller_status_cb, rc);
560   GNUNET_assert (NULL != rc->cproc);
561   rc->num_peers = num_peers;
562   rc->event_mask = event_mask;
563   rc->cc = cc;
564   rc->cc_cls = cc_cls;
565   rc->master = master;
566   rc->master_cls = master_cls;
567   rc->state = RC_INIT;
568   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
569                                 &shutdown_run_task, rc);
570 }
571
572 /* end of testbed_api_testbed.c */