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