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