7e85f3ca252e3fa8c6d47686049b16529c951769
[oweals/gnunet.git] / src / testbed / gnunet-testbed-profiler.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011, 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/gnunet-testbed-profiler.c
23  * @brief Profiling driver for the testbed.
24  * @author Sree Harsha Totakura <sreeharsha@totakura.in> 
25  */
26
27 #include "platform.h"
28 #include "gnunet_common.h"
29 #include "gnunet_testbed_service.h"
30 #include "testbed_api_hosts.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  * DLL of operations
40  */
41 struct DLLOperation
42 {
43   /**
44    * The testbed operation handle
45    */
46   struct GNUNET_TESTBED_Operation *op;
47
48   /**
49    * Closure
50    */
51   void *cls;
52
53   /**
54    * The next pointer for DLL
55    */
56   struct DLLOperation *next;
57
58   /**
59    * The prev pointer for DLL
60    */
61   struct DLLOperation *prev;
62 };
63
64
65 /**
66  * Availanle states during profiling
67  */
68 enum State
69 {
70   /**
71    * Initial state
72    */
73   STATE_INIT = 0,
74
75   /**
76    * Starting slaves
77    */
78   STATE_SLAVES_STARTING
79 };
80
81
82 /**
83  * An array of hosts loaded from the hostkeys file
84  */
85 static struct GNUNET_TESTBED_Host **hosts;
86
87 /**
88  * The array of peers; we fill this as the peers are given to us by the testbed
89  */
90 static struct GNUNET_TESTBED_Peer **peers;
91
92 /* /\** */
93 /*  * Operation handle */
94 /*  *\/ */
95 /* static struct GNUNET_TESTBED_Operation *op; */
96
97 /**
98  * Host registration handle
99  */
100 static struct GNUNET_TESTBED_HostRegistrationHandle *reg_handle;
101
102 /**
103  * Handle to the master controller process
104  */
105 struct GNUNET_TESTBED_ControllerProc *mc_proc;
106
107 /**
108  * Handle to the master controller
109  */
110 struct GNUNET_TESTBED_Controller *mc;
111
112 /**
113  * Handle to global configuration
114  */
115 struct GNUNET_CONFIGURATION_Handle *cfg;
116
117 /**
118  * Head of the operations list
119  */
120 struct DLLOperation *dll_op_head;
121
122 /**
123  * Tail of the operations list
124  */
125 struct DLLOperation *dll_op_tail;
126
127 /**
128  * Abort task identifier
129  */
130 static GNUNET_SCHEDULER_TaskIdentifier abort_task;
131
132 /**
133  * Host registration task identifier
134  */
135 static GNUNET_SCHEDULER_TaskIdentifier register_hosts_task;
136
137 /**
138  * Global event mask for all testbed events
139  */
140 uint64_t event_mask;
141
142 /**
143  * Current peer id
144  */
145 unsigned int peer_id;
146
147 /**
148  * Number of peers to be started by the profiler
149  */
150 static unsigned int num_peers;
151
152 /**
153  * Number of hosts in the hosts array
154  */
155 static unsigned int num_hosts;
156
157 /**
158  * Global testing status
159  */
160 static int result;
161
162 /**
163  * current state of profiling
164  */
165 enum State state;
166
167
168 /**
169  * Shutdown nicely
170  *
171  * @param cls NULL
172  * @param tc the task context
173  */
174 static void
175 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
176 {
177   struct DLLOperation *dll_op;
178   unsigned int nhost;
179
180   if (GNUNET_SCHEDULER_NO_TASK != abort_task)
181     GNUNET_SCHEDULER_cancel (abort_task);
182   if (GNUNET_SCHEDULER_NO_TASK != register_hosts_task)
183     GNUNET_SCHEDULER_cancel (register_hosts_task);
184   GNUNET_free_non_null (peers);
185   if (NULL != reg_handle)
186     GNUNET_TESTBED_cancel_registration (reg_handle);
187   for (nhost = 0; nhost < num_hosts; nhost++)
188     if (NULL != hosts[nhost])
189       GNUNET_TESTBED_host_destroy (hosts[nhost]);
190   GNUNET_free_non_null (hosts);
191   dll_op = dll_op_head;
192   while (NULL != dll_op)
193   {
194     GNUNET_TESTBED_operation_cancel (dll_op->op);
195     GNUNET_free_non_null (dll_op->cls);
196     GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
197     GNUNET_free (dll_op);
198   }
199   if (NULL != mc)
200     GNUNET_TESTBED_controller_disconnect (mc);
201   if (NULL != mc_proc)
202     GNUNET_TESTBED_controller_stop (mc_proc);
203   if (NULL != cfg)
204     GNUNET_CONFIGURATION_destroy (cfg);
205   GNUNET_SCHEDULER_shutdown (); /* Stop scheduler to shutdown testbed run */  
206 }
207
208
209 /**
210  * abort task to run on test timed out
211  *
212  * @param cls NULL
213  * @param tc the task context
214  */
215 static void
216 do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
217 {
218   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Aborting\n");
219   abort_task = GNUNET_SCHEDULER_NO_TASK;
220   result = GNUNET_SYSERR;
221   GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
222 }
223
224
225 /**
226  * Controller event callback
227  *
228  * @param cls NULL
229  * @param event the controller event
230  */
231 static void
232 controller_event_cb (void *cls,
233                      const struct GNUNET_TESTBED_EventInformation *event)
234 {
235   struct DLLOperation *dll_op;
236   switch (state)
237   {
238   case STATE_SLAVES_STARTING:
239     switch (event->type)
240     {
241     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
242       {
243         static unsigned int slaves_started;
244       
245         dll_op = event->details.operation_finished.op_cls;
246         GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
247         if (NULL != event->details.operation_finished.emsg)
248         {
249           LOG (GNUNET_ERROR_TYPE_WARNING,
250                _("An operation has failed while starting slaves\n"));
251           GNUNET_SCHEDULER_cancel (abort_task);
252           abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
253           return;
254         }
255         GNUNET_TESTBED_operation_done (dll_op->op);
256         GNUNET_free (dll_op);
257         /* Proceed to start peers */
258         if (++slaves_started == num_hosts - 1)
259           printf ("All slaves started successfully"); 
260       }
261       break;
262     default:
263       GNUNET_assert (0);
264     }
265     break;
266   default:
267     GNUNET_assert (0);
268   }
269 }
270
271
272 /**
273  * Task to register all hosts available in the global host list
274  *
275  * @param cls NULL
276  * @param tc the scheduler task context
277  */
278 static void
279 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
280
281
282 /**
283  * Callback which will be called to after a host registration succeeded or failed
284  *
285  * @param cls the closure
286  * @param emsg the error message; NULL if host registration is successful
287  */
288 static void
289 host_registration_completion (void *cls, const char *emsg)
290 {
291   reg_handle = NULL;
292   if (NULL != emsg)
293   {
294     LOG (GNUNET_ERROR_TYPE_WARNING,
295          _("Host registration failed for a host. Error: %s\n"), emsg);
296     GNUNET_SCHEDULER_cancel (abort_task);
297     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
298     return;
299   }
300   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
301 }
302
303
304 /**
305  * Task to register all hosts available in the global host list
306  *
307  * @param cls NULL
308  * @param tc the scheduler task context
309  */
310 static void
311 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
312 {
313   struct DLLOperation *dll_op;
314   static unsigned int reg_host;
315   unsigned int slave;
316
317   register_hosts_task = GNUNET_SCHEDULER_NO_TASK;  
318   if (reg_host == num_hosts - 1)
319   {
320     LOG (GNUNET_ERROR_TYPE_DEBUG,
321          "All hosts successfully registered\n");
322     /* Start slaves */
323     for (slave = 1; slave < num_hosts; slave++)
324     {
325       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
326       dll_op->op = GNUNET_TESTBED_controller_link (dll_op,
327                                                    mc,
328                                                    hosts[slave],
329                                                    NULL,
330                                                    cfg,
331                                                    GNUNET_YES);
332       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
333     }
334     return;
335   }
336   reg_handle = GNUNET_TESTBED_register_host (mc, hosts[++reg_host],
337                                              host_registration_completion,
338                                              NULL);
339 }
340
341
342 /**
343  * Callback to signal successfull startup of the controller process
344  *
345  * @param cls the closure from GNUNET_TESTBED_controller_start()
346  * @param cfg the configuration with which the controller has been started;
347  *          NULL if status is not GNUNET_OK
348  * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not,
349  *          GNUNET_TESTBED_controller_stop() shouldn't be called in this case
350  */
351 static void
352 status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *config, int status)
353 {
354   GNUNET_SCHEDULER_cancel (abort_task);
355   if (GNUNET_OK != status)
356   {
357     mc_proc = NULL;
358     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
359     return;
360   }
361   event_mask = 0;
362   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START);
363   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_STOP);
364   event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
365   event_mask |= (1LL << GNUNET_TESTBED_ET_DISCONNECT);
366   mc = GNUNET_TESTBED_controller_connect (config, hosts[0], event_mask,
367                                           &controller_event_cb, NULL);
368   if (NULL == mc)
369   {
370     LOG (GNUNET_ERROR_TYPE_WARNING,
371          _("Unable to connect to master controller -- Check config\n"));
372     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
373     return;
374   }
375   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
376   abort_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
377                                              &do_abort, NULL);
378 }
379
380
381 /**
382  * Main function that will be run by the scheduler.
383  *
384  * @param cls closure
385  * @param args remaining command-line arguments
386  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
387  * @param cfg configuration
388  */
389 static void
390 run (void *cls, char *const *args, const char *cfgfile,
391      const struct GNUNET_CONFIGURATION_Handle *config)
392 {
393   unsigned int nhost;
394
395   if (NULL == args[0])
396   {
397     fprintf (stderr, _("No hosts-file specified on command line\n"));
398     return;
399   }
400   if (0 == num_peers)
401   {
402     result = GNUNET_OK;
403     return;
404   }
405   num_hosts = GNUNET_TESTBED_hosts_load_from_file (args[0], &hosts);
406   if (0 == num_hosts)
407   {
408     fprintf (stderr, _("No hosts loaded. Need atleast one host\n"));
409     return;
410   }
411   for (nhost = 0; nhost < num_hosts; nhost++)
412   {
413     if (GNUNET_YES != GNUNET_TESTBED_is_host_habitable (hosts[nhost]))
414     {
415       fprintf (stderr, _("Host %s cannot start testbed\n"),
416                          GNUNET_TESTBED_host_get_hostname_ (hosts[nhost]));
417       break;
418     }
419   }
420   if (num_hosts != nhost)
421   {
422     fprintf (stderr, _("Exiting\n"));
423     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
424     return;
425   }
426   cfg = GNUNET_CONFIGURATION_dup (config);
427   mc_proc = 
428       GNUNET_TESTBED_controller_start (GNUNET_TESTBED_host_get_hostname_ 
429                                        (hosts[0]),
430                                        hosts[0],
431                                        cfg,
432                                        status_cb,
433                                        NULL);
434   abort_task =
435       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
436                                     (GNUNET_TIME_UNIT_SECONDS, 5), &do_abort,
437                                     NULL);
438 }
439
440
441 /**
442  * Main function.
443  *
444  * @return 0 on success
445  */
446 int
447 main (int argc, char *const *argv)
448 {
449   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
450     { 'n', "num-peers", "COUNT",
451       gettext_noop ("create COUNT number of peers"),
452       GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_peers },
453     { 'n', "num-peers", "COUNT",
454       gettext_noop ("create COUNT number of peers"),
455       GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_peers },
456     GNUNET_GETOPT_OPTION_END
457   };
458   int ret;
459
460   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
461     return 2;
462   
463   result = GNUNET_SYSERR;
464   ret =
465       GNUNET_PROGRAM_run (argc, argv, "gnunet-testbed-profiler [OPTIONS] hosts-file",
466                           _("Profiler for testbed"),
467                           options, &run, NULL);
468   if (GNUNET_OK != ret)
469     return ret;
470   if (GNUNET_OK != result)
471     return 1;
472   return 0;
473 }