- distribute peers equally among island nodes on SuperMUC
[oweals/gnunet.git] / src / testbed / testbed_api_testbed.c
index 948cf4d5568e0b3522b30a60d1cc9f941c11cdac..c4cfe59b114da6f0d3ab77f5cf616eab82689531 100644 (file)
@@ -1,6 +1,6 @@
 /*
   This file is part of GNUnet
-  (C) 2008--2012 Christian Grothoff (and other contributing authors)
+  (C) 2008--2013 Christian Grothoff (and other contributing authors)
 
   GNUnet is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published
 #define DEFAULT_SETUP_TIMEOUT 300
 
 /**
- * DLL of operations
+ * Testbed Run Handle
+ */
+struct RunContext;
+
+/**
+ * Context information for the operation we start
  */
-struct DLLOperation
+struct RunContextOperation
 {
   /**
    * The testbed operation handle
@@ -69,15 +74,6 @@ struct DLLOperation
    */
   void *cls;
 
-  /**
-   * The next pointer for DLL
-   */
-  struct DLLOperation *next;
-
-  /**
-   * The prev pointer for DLL
-   */
-  struct DLLOperation *prev;
 };
 
 
@@ -91,6 +87,13 @@ enum State
    */
   RC_INIT = 0,
 
+#if ENABLE_LL
+  /**
+   * Island level controllers are started and linked
+   */
+  RC_ISLANDS_LINKED,
+#endif
+
   /**
    * Controllers on given hosts started and linked
    */
@@ -125,6 +128,45 @@ enum State
 };
 
 
+/**
+ * Context for host compability checks
+ */
+struct CompatibilityCheckContext
+{
+  /**
+   * The run context
+   */
+  struct RunContext *rc;
+
+  /**
+   * Handle for the compability check
+   */
+  struct GNUNET_TESTBED_HostHabitableCheckHandle *h;
+
+  /**
+   * Index of the host in the run context's hosts array
+   */
+  unsigned int index;
+};
+
+#if ENABLE_LL
+/**
+ * Structure to represent an island of SuperMUC's nodes
+ */
+struct Island
+{
+  /**
+   * Array of nodes in this island
+   */
+  struct GNUNET_TESTBED_Host **hosts;
+
+  /**
+   * Number of nodes in the above array
+   */
+  unsigned int nhosts;
+};
+#endif
+
 /**
  * Testbed Run Handle
  */
@@ -178,24 +220,26 @@ struct RunContext
   void *test_master_cls;
 
   /**
-   * The head element of DLL operations
+   * A hashmap for operations started by us
    */
-  struct DLLOperation *dll_op_head;
+  struct GNUNET_CONTAINER_MultiHashMap32 *rcop_map;
 
   /**
-   * The tail element of DLL operations
+   * An array of hosts loaded from the hostkeys file
    */
-  struct DLLOperation *dll_op_tail;
+  struct GNUNET_TESTBED_Host **hosts;
 
+#if ENABLE_LL
   /**
-   * An array of hosts loaded from the hostkeys file
+   * Array of SuperMUC islands
    */
-  struct GNUNET_TESTBED_Host **hosts;
+  struct Island **islands;
+#endif
 
   /**
-   * The handle for whether a host is habitable or not
+   * Array of compatibility check contexts
    */
-  struct GNUNET_TESTBED_HostHabitableCheckHandle **hc_handles;
+  struct CompatibilityCheckContext *hclist;
 
   /**
    * Array of peers which we create
@@ -209,7 +253,8 @@ struct RunContext
   struct GNUNET_TESTBED_Operation *topology_operation;
 
   /**
-   * The file containing topology data. Only used if the topology is set to 'FROM_FILE'
+   * The file containing topology data. Only used if the topology is set to
+   * 'FROM_FILE'
    */
   char *topo_file;
 
@@ -295,9 +340,159 @@ struct RunContext
    */
   unsigned int random_links;
 
+  /**
+   * the number of overlay link connection attempts that succeeded
+   */
+  unsigned int links_succeeded;
+
+  /**
+   * the number of overlay link connection attempts that failed
+   */
+  unsigned int links_failed;
+
+#if ENABLE_LL
+  /**
+   * Number of SuperMUC islands we are running on
+   */
+  unsigned int nislands;
+#endif
+};
+
+
+/**
+ * Return a 32-bit key from a pointer
+ *
+ * @param rcop the pointer
+ * @return 32-bit key
+ */
+static uint32_t
+rcop_key (void *rcop)
+{  
+  return * ((uint32_t *) &rcop);
+}
+
+
+/**
+ * Context information used for finding a pointer in the rcop_map
+ */
+struct SearchContext
+{
+  /**
+   * The operation pointer to look for
+   */
+  struct GNUNET_TESTBED_Operation *query;
+
+  /**
+   * The Run context operation which has the operation being queried
+   */
+  struct RunContextOperation *result;
 };
 
 
+/**
+ * Iterator for searching over the elements matching a given query
+ *
+ * @param cls the SearchContext
+ * @param key the 32-bit key
+ * @param value the RunContextOperation element
+ * @return GNUNET_YES to continue iteration; GNUNET_NO to cancel it
+ */
+static int
+search_iterator (void *cls, uint32_t key, void *value)
+{
+  struct RunContextOperation *rcop = value;
+  struct SearchContext *sc = cls;
+
+  GNUNET_assert (NULL != rcop);
+  if (sc->query == rcop->op)
+  {
+    GNUNET_assert (NULL == sc->result);
+    sc->result = rcop;
+    return GNUNET_NO;
+  }
+  return GNUNET_YES;
+}
+
+
+/**
+ * Initiate a search for the given operation in the rcop_map
+ *
+ * @param rc the RunContext whose rcop_map will be searched for the given
+ *          operation
+ * @param op the given operation to search for
+ * @return the matching RunContextOperation if found; NULL if not
+ */
+static struct RunContextOperation *
+search_rcop (struct RunContext *rc, struct GNUNET_TESTBED_Operation *op)
+{
+  struct SearchContext sc;
+  
+  sc.query = op;
+  sc.result = NULL;
+  if (GNUNET_SYSERR == 
+      GNUNET_CONTAINER_multihashmap32_get_multiple (rc->rcop_map,
+                                                    rcop_key (op),
+                                                    &search_iterator,
+                                                    &sc))
+  {
+    GNUNET_assert (NULL != sc.result);
+    return sc.result;
+  }
+  return NULL;
+}
+
+
+/**
+ * Insert an RunContextOperation into the rcop_map of the given RunContext
+ *
+ * @param rc the RunContext into whose map is to be used for insertion
+ * @param rcop the RunContextOperation to insert
+ */
+static void
+insert_rcop (struct RunContext *rc, struct RunContextOperation *rcop)
+{
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CONTAINER_multihashmap32_put (rc->rcop_map,
+                                                      rcop_key (rcop->op), rcop,
+                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
+}
+
+
+/**
+ * Remove a RunContextOperation from the rcop_map of the given RunContext
+ *
+ * @param rc the RunContext from whose map the given RunContextOperaton has to
+ *          be removed
+ * @param rcop the RunContextOperation
+ */
+static void
+remove_rcop (struct RunContext *rc, struct RunContextOperation *rcop)
+{
+  GNUNET_assert (GNUNET_YES ==
+                 GNUNET_CONTAINER_multihashmap32_remove (rc->rcop_map,
+                                                         rcop_key (rcop->op),
+                                                         rcop));
+}
+
+#if ENABLE_LL
+static void
+cleanup_islands (struct RunContext *rc)
+{
+  struct Island *island;
+  unsigned int cnt;
+
+  GNUNET_assert (NULL != rc->islands);
+  for (cnt = 0; cnt < rc->nislands; cnt++)
+  {
+    island = rc->islands[cnt];
+    GNUNET_free (island->hosts);
+    GNUNET_free (island);
+  }
+  GNUNET_free (rc->islands);
+  rc->islands = NULL;
+}
+#endif
+
 /**
  * Assuming all peers have been destroyed cleanup run handle
  *
@@ -308,22 +503,27 @@ static void
 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct RunContext *rc = cls;
-  unsigned int hid;
+  unsigned int cnt;
 
   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == rc->register_hosts_task);
   GNUNET_assert (NULL == rc->reg_handle);
   GNUNET_assert (NULL == rc->peers);
-  GNUNET_assert (NULL == rc->hc_handles);
+  GNUNET_assert (NULL == rc->hclist);
   GNUNET_assert (RC_PEERS_SHUTDOWN == rc->state);
-  GNUNET_assert (NULL == rc->dll_op_head);
+  GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap32_size (rc->rcop_map));
+  GNUNET_CONTAINER_multihashmap32_destroy (rc->rcop_map);
   if (NULL != rc->c)
     GNUNET_TESTBED_controller_disconnect (rc->c);
   if (NULL != rc->cproc)
     GNUNET_TESTBED_controller_stop (rc->cproc);
   if (NULL != rc->h)
     GNUNET_TESTBED_host_destroy (rc->h);
-  for (hid = 0; hid < rc->num_hosts; hid++)
-    GNUNET_TESTBED_host_destroy (rc->hosts[hid]);
+#if ENABLE_LL
+  if (NULL != rc->islands)
+    cleanup_islands (rc);
+#endif
+  for (cnt = 0; cnt < rc->num_hosts; cnt++)
+    GNUNET_TESTBED_host_destroy (rc->hosts[cnt]);
   GNUNET_free_non_null (rc->hosts);
   if (NULL != rc->cfg)
     GNUNET_CONFIGURATION_destroy (rc->cfg);
@@ -332,19 +532,51 @@ cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   GNUNET_free (rc);
 }
 
+
+/**
+ * Iterator for cleaning up elements from rcop_map 
+ *
+ * @param cls the RunContext
+ * @param key the 32-bit key
+ * @param value the RunContextOperation element
+ * @return always GNUNET_YES
+ */
+static int
+rcop_cleanup_iterator (void *cls, uint32_t key, void *value)
+{
+  struct RunContext *rc = cls;
+  struct RunContextOperation *rcop = value;
+
+  GNUNET_assert (rc == rcop->rc);
+  remove_rcop (rc, rcop);
+  GNUNET_TESTBED_operation_done (rcop->op);
+  GNUNET_free (rcop);
+  return GNUNET_YES;
+}
+
+
+/**
+ * Frees memory, closes pending operations, cancels actives tasks of the given
+ * RunContext 
+ *
+ * @param rc the RunContext
+ */
 static void
 cleanup (struct RunContext *rc)
 {
-  struct DLLOperation *dll_op;
+  struct CompatibilityCheckContext *hc;
   unsigned int nhost;
 
-  if (NULL != rc->hc_handles)
+  if (NULL != rc->hclist)
   {
     for (nhost = 0; nhost < rc->num_hosts; nhost++)
-      if (NULL != rc->hc_handles[nhost])
-        GNUNET_TESTBED_is_host_habitable_cancel (rc->hc_handles[nhost]);
-    GNUNET_free (rc->hc_handles);
-    rc->hc_handles = NULL;
+    {
+      hc = &rc->hclist[nhost];
+      if (NULL != hc->h)
+        GNUNET_TESTBED_is_host_habitable_cancel (hc->h);
+    }
+    GNUNET_free (rc->hclist);
+    rc->hclist = NULL;
   }
   /* Stop register hosts task if it is running */
   if (GNUNET_SCHEDULER_NO_TASK != rc->register_hosts_task)
@@ -373,12 +605,10 @@ cleanup (struct RunContext *rc)
     rc->topology_operation = NULL;
   }
   /* cancel any exiting operations */
-  while (NULL != (dll_op = rc->dll_op_head))
-  {
-    GNUNET_TESTBED_operation_done (dll_op->op);
-    GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op);
-    GNUNET_free (dll_op);
-  }
+  GNUNET_assert (GNUNET_SYSERR != 
+                 GNUNET_CONTAINER_multihashmap32_iterate (rc->rcop_map,
+                                                          &rcop_cleanup_iterator,
+                                                          rc));
 }
 
 
@@ -392,7 +622,7 @@ static void
 shutdown_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct RunContext *rc = cls;
-  struct DLLOperation *dll_op;
+  struct RunContextOperation *rcop;
 
   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != rc->shutdown_run_task);
   rc->shutdown_run_task = GNUNET_SCHEDULER_NO_TASK;
@@ -403,13 +633,13 @@ shutdown_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   {
     if (NULL != rc->peers)
     {
-      dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
-      dll_op->op = GNUNET_TESTBED_shutdown_peers (rc->c, dll_op, NULL, NULL);
-      GNUNET_assert (NULL != dll_op->op);
+      rcop = GNUNET_malloc (sizeof (struct RunContextOperation));
+      rcop->rc = rc;
+      rcop->op = GNUNET_TESTBED_shutdown_peers (rc->c, rcop, NULL, NULL);
+      GNUNET_assert (NULL != rcop->op);
       DEBUG ("Shutting down peers\n");
       rc->pstart_time = GNUNET_TIME_absolute_get ();
-      GNUNET_CONTAINER_DLL_insert_tail (rc->dll_op_head, rc->dll_op_tail,
-                                        dll_op);
+      insert_rcop (rc, rcop);
       return;
     }
   }
@@ -436,6 +666,12 @@ shutdown_now (struct RunContext *rc)
 }
 
 
+/**
+ * Task run upon any interrupt.  Common ones are SIGINT & SIGTERM.
+ *
+ * @param cls the RunContext which has to be acted upon
+ * @param tc the scheduler task context
+ */
 static void
 interrupt (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
@@ -473,18 +709,19 @@ static void
 start_peers_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct RunContext *rc = cls;
-  struct DLLOperation *dll_op;
+  struct RunContextOperation *rcop;
   unsigned int peer;
 
   DEBUG ("Starting Peers\n");
   rc->pstart_time = GNUNET_TIME_absolute_get ();
   for (peer = 0; peer < rc->num_peers; peer++)
   {
-    dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
-    dll_op->op = GNUNET_TESTBED_peer_start (NULL, rc->peers[peer], NULL, NULL);
-    GNUNET_assert (NULL != dll_op->op);
-    dll_op->cls = rc->peers[peer];
-    GNUNET_CONTAINER_DLL_insert_tail (rc->dll_op_head, rc->dll_op_tail, dll_op);
+    rcop = GNUNET_malloc (sizeof (struct RunContextOperation));
+    rcop->rc = rc;
+    rcop->op  = GNUNET_TESTBED_peer_start (NULL, rc->peers[peer], NULL, NULL);
+    GNUNET_assert (NULL != rcop->op);
+    rcop->cls = rc->peers[peer];
+    insert_rcop (rc, rcop);
   }
   rc->peer_count = 0;
 }
@@ -502,15 +739,14 @@ start_peers_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 static void
 peer_create_cb (void *cls, struct GNUNET_TESTBED_Peer *peer, const char *emsg)
 {
-  struct DLLOperation *dll_op = cls;
+  struct RunContextOperation *rcop = cls;
   struct RunContext *rc;
 
-  GNUNET_assert (NULL != dll_op);
-  rc = dll_op->rc;
-  GNUNET_assert (NULL != rc);
-  GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op);
-  GNUNET_TESTBED_operation_done (dll_op->op);
-  GNUNET_free (dll_op);
+  GNUNET_assert (NULL != rcop);
+  GNUNET_assert (NULL != (rc = rcop->rc));
+  remove_rcop (rc, rcop);
+  GNUNET_TESTBED_operation_done (rcop->op);
+  GNUNET_free (rcop);
   if (NULL == peer)
   {
     if (NULL != emsg)
@@ -540,7 +776,8 @@ call_master (struct RunContext *rc)
   GNUNET_SCHEDULER_cancel (rc->timeout_task);
   rc->timeout_task = GNUNET_SCHEDULER_NO_TASK;
   if (NULL != rc->test_master)
-    rc->test_master (rc->test_master_cls, rc->num_peers, rc->peers);
+    rc->test_master (rc->test_master_cls, rc->num_peers, rc->peers,
+                     rc->links_succeeded, rc->links_failed);
 }
 
 
@@ -562,6 +799,8 @@ topology_completion_callback (void *cls, unsigned int nsuccess,
   DEBUG ("Overlay topology generated in %s\n", prof_time (rc));
   GNUNET_TESTBED_operation_done (rc->topology_operation);
   rc->topology_operation = NULL;
+  rc->links_succeeded = nsuccess;
+  rc->links_failed = nfailures;
   rc->state = RC_READY;
   call_master (rc);
 }
@@ -575,9 +814,18 @@ topology_completion_callback (void *cls, unsigned int nsuccess,
 static void
 create_peers (struct RunContext *rc)
 {
-  struct DLLOperation *dll_op;
+  struct RunContextOperation *rcop;
+  struct GNUNET_TESTBED_Host *host;
   unsigned int peer;
+#if ENABLE_LL
+  struct Island *island;
+  unsigned int icnt;
+  unsigned int hcnt;
 
+  island = NULL;
+  icnt = 0;
+  hcnt = 0;
+#endif
   DEBUG ("Creating peers\n");
   rc->pstart_time = GNUNET_TIME_absolute_get ();
   rc->peers =
@@ -586,16 +834,35 @@ create_peers (struct RunContext *rc)
   rc->peer_count = 0;
   for (peer = 0; peer < rc->num_peers; peer++)
   {
-    dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
-    dll_op->rc = rc;
-    dll_op->op =
-        GNUNET_TESTBED_peer_create (rc->c,
-                                    (0 ==
-                                     rc->num_hosts) ? rc->h : rc->hosts[peer %
-                                                                        rc->num_hosts],
-                                    rc->cfg, peer_create_cb, dll_op);    
-    GNUNET_assert (NULL != dll_op->op);
-    GNUNET_CONTAINER_DLL_insert_tail (rc->dll_op_head, rc->dll_op_tail, dll_op);
+    rcop = GNUNET_malloc (sizeof (struct RunContextOperation));
+    rcop->rc = rc;
+#if ENABLE_LL
+    if (0 != rc->nislands)
+    {
+      island = rc->islands[icnt];
+      if (hcnt == island->nhosts)
+      {
+        icnt++;
+        if (icnt == rc->nislands)
+          icnt = 0;
+        island = rc->islands[icnt];
+        hcnt = 0;
+      }
+      GNUNET_assert (icnt < rc->nislands);
+      GNUNET_assert (hcnt < island->nhosts);
+      GNUNET_assert (NULL != island->hosts[hcnt]);
+      host = island->hosts[hcnt];
+      hcnt++;
+    }
+    else
+      host = rc->h;
+#else
+    host = (0 == rc->num_hosts) ? rc->h : rc->hosts[peer % rc->num_hosts];
+#endif
+    rcop->op = GNUNET_TESTBED_peer_create (rc->c, host, rc->cfg,
+                                           &peer_create_cb, rcop);
+    GNUNET_assert (NULL != rcop->op);
+    insert_rcop (rc, rcop);
   }
 }
 
@@ -611,14 +878,14 @@ static void
 event_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event)
 {
   struct RunContext *rc = cls;
-  struct DLLOperation *dll_op;
+  struct RunContextOperation *rcop;
 
   if (RC_INIT == rc->state)
   {
     switch (event->type)
     {
     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
-      dll_op = event->op_cls;
+      rcop = event->op_cls;
       if (NULL != event->details.operation_finished.emsg)
       {
         LOG (GNUNET_ERROR_TYPE_ERROR, _("Linking controllers failed. Exiting"));
@@ -626,15 +893,45 @@ event_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event)
       }
       else
         rc->reg_hosts++;
-      GNUNET_assert (event->op == dll_op->op);
-      GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op);
-      GNUNET_TESTBED_operation_done (dll_op->op);
-      GNUNET_free (dll_op);
-      if (rc->reg_hosts == rc->num_hosts)
+      GNUNET_assert (event->op == rcop->op);
+      remove_rcop (rc, rcop);
+      GNUNET_TESTBED_operation_done (rcop->op);
+      GNUNET_free (rcop);
+#if !ENABLE_LL
+      if (rc->reg_hosts != rc->num_hosts)
+        return;
+      rc->state = RC_LINKED;
+      create_peers (rc);
+#else
+      if (rc->reg_hosts != rc->nislands)
+        return;
+      struct Island *island;
+      struct GNUNET_TESTBED_Host *host;
+      unsigned int cnt;
+      unsigned int cnt2;
+      rc->state = RC_ISLANDS_LINKED;
+      rc->reg_hosts = 0;
+      for (cnt = 0; cnt < rc->nislands; cnt++)
       {
-        rc->state = RC_LINKED;
-        create_peers (rc);
+        island = rc->islands[cnt];
+        for (cnt2 = 1; cnt2 < island->nhosts; cnt2++)
+        {
+          host = island->hosts[cnt2];
+          rcop = GNUNET_malloc (sizeof (struct RunContextOperation));
+          rcop->rc = rc;
+          rcop->op =
+                GNUNET_TESTBED_controller_link (rcop, rc->c, host,
+                                                island->hosts[0], GNUNET_YES);
+          GNUNET_assert (NULL != rcop->op);
+          insert_rcop (rc, rcop);
+          rc->reg_hosts++;
+        }
       }
+      if (0 != rc->reg_hosts)
+        return;      
+      rc->state = RC_LINKED;
+      create_peers (rc);
+#endif
       return;
     default:
       GNUNET_break (0);
@@ -642,17 +939,44 @@ event_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event)
       return;
     }
   }
-  for (dll_op = rc->dll_op_head; NULL != dll_op; dll_op = dll_op->next)
+#if ENABLE_LL
+  if (RC_ISLANDS_LINKED == rc->state)
   {
-    if ((GNUNET_TESTBED_ET_OPERATION_FINISHED == event->type) &&
-        (event->op == dll_op->op))
-      break;
+    switch (event->type)
+    {
+    case GNUNET_TESTBED_ET_OPERATION_FINISHED:
+      rcop = event->op_cls;
+      if (NULL != event->details.operation_finished.emsg)
+      {
+        LOG (GNUNET_ERROR_TYPE_ERROR,
+             "Linking 2nd level controllers failed. Exiting");
+        shutdown_now (rc);
+      }
+      else
+        rc->reg_hosts--;
+      GNUNET_assert (event->op == rcop->op);
+      remove_rcop (rc, rcop);
+      GNUNET_TESTBED_operation_done (rcop->op);
+      GNUNET_free (rcop);
+      if (0 != rc->reg_hosts)
+        return;
+      rc->state = RC_LINKED;
+      create_peers (rc);
+      return;
+    default:
+      GNUNET_break (0);
+      shutdown_now (rc);
+      return;
+    }
   }
-  if (NULL == dll_op)
+#endif
+  if (GNUNET_TESTBED_ET_OPERATION_FINISHED != event->type)
+    goto call_cc;
+  if (NULL == (rcop = search_rcop (rc, event->op)))
     goto call_cc;
-  GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op);
-  GNUNET_TESTBED_operation_done (dll_op->op);
-  GNUNET_free (dll_op);
+  remove_rcop (rc, rcop);
+  GNUNET_TESTBED_operation_done (rcop->op);
+  GNUNET_free (rcop);
   if ( (GNUNET_NO == rc->shutdown)
        && (NULL != event->details.operation_finished.emsg) )
   {
@@ -683,15 +1007,11 @@ call_cc:
     rc->cc (rc->cc_cls, event);
   if (GNUNET_TESTBED_ET_PEER_START != event->type)
     return;
-  for (dll_op = rc->dll_op_head; NULL != dll_op; dll_op = dll_op->next)
-    if ((NULL != dll_op->cls) &&
-        (event->details.peer_start.peer == dll_op->cls))
-      break;
-  if (NULL == dll_op)           /* Not our operation */
+  if (NULL == (rcop = search_rcop (rc, event->op))) /* Not our operation */
     return;
-  GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op);
-  GNUNET_TESTBED_operation_done (dll_op->op);
-  GNUNET_free (dll_op);
+  remove_rcop (rc, rcop);
+  GNUNET_TESTBED_operation_done (rcop->op);
+  GNUNET_free (rcop);
   rc->peer_count++;
   if (rc->peer_count < rc->num_peers)
     return;
@@ -789,25 +1109,40 @@ static void
 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct RunContext *rc = cls;
-  struct DLLOperation *dll_op;
-  unsigned int slave;
+  struct RunContextOperation *rcop;
+  unsigned int cnt;
 
   rc->register_hosts_task = GNUNET_SCHEDULER_NO_TASK;
   if (rc->reg_hosts == rc->num_hosts)
   {
     DEBUG ("All hosts successfully registered\n");
-    /* Start slaves */
-    for (slave = 0; slave < rc->num_hosts; slave++)
+    /* Start cnts */
+#if !ENABLE_LL
+    for (cnt = 0; cnt < rc->num_hosts; cnt++)
+    {
+      rcop = GNUNET_malloc (sizeof (struct RunContextOperation));
+      rcop->rc = rc;
+      rcop->op =
+          GNUNET_TESTBED_controller_link (rcop, rc->c, rc->hosts[cnt],
+                                          rc->h, GNUNET_YES);
+      GNUNET_assert (NULL != rcop->op);
+      insert_rcop (rc, rcop);
+    }
+#else
+    struct Island *island;
+    for (cnt = 0; cnt < rc->nislands; cnt++)
     {
-      dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
-      dll_op->rc = rc;
-      dll_op->op =
-          GNUNET_TESTBED_controller_link (dll_op, rc->c, rc->hosts[slave],
-                                          rc->h, rc->cfg, GNUNET_YES);
-      GNUNET_assert (NULL != dll_op->op);
-      GNUNET_CONTAINER_DLL_insert_tail (rc->dll_op_head, rc->dll_op_tail,
-                                        dll_op);
+      island = rc->islands[cnt];
+      GNUNET_assert (0 < island->nhosts);
+      rcop = GNUNET_malloc (sizeof (struct RunContextOperation));
+      rcop->rc = rc;
+      rcop->op =
+          GNUNET_TESTBED_controller_link (rcop, rc->c, island->hosts[0],
+                                          rc->h, GNUNET_YES);
+      GNUNET_assert (NULL != rcop->op);
+      insert_rcop (rc, rcop);
     }
+#endif
     rc->reg_hosts = 0;
     return;
   }
@@ -862,8 +1197,7 @@ controller_status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg,
   if (rc->topology < GNUNET_TESTBED_TOPOLOGY_NONE)
     event_mask |= GNUNET_TESTBED_ET_CONNECT;
   rc->c =
-      GNUNET_TESTBED_controller_connect (rc->cfg, rc->h, event_mask, &event_cb,
-                                         rc);
+      GNUNET_TESTBED_controller_connect (rc->h, event_mask, &event_cb, rc);
   if (0 < rc->num_hosts)
   {
     rc->reg_hosts = 0;
@@ -875,6 +1209,49 @@ controller_status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg,
 }
 
 
+#if ENABLE_LL
+#define ISLANDNAME_SIZE 4
+static void
+parse_islands (struct RunContext *rc)
+{
+  char island_id[ISLANDNAME_SIZE];
+  struct GNUNET_TESTBED_Host *host;
+  struct Island *island;
+  const char *hostname;
+  unsigned int nhost;
+
+  DEBUG ("Parsing for islands\n");
+  memset (island_id, 0, ISLANDNAME_SIZE);
+  island = NULL;
+  for (nhost = 0; nhost < rc->num_hosts; nhost++)
+  {
+    host = rc->hosts[nhost];
+    hostname = GNUNET_TESTBED_host_get_hostname (host);
+    GNUNET_assert (NULL != hostname);
+    if (NULL == island)
+    {
+      strncpy (island_id, hostname, ISLANDNAME_SIZE - 1);
+      island = GNUNET_malloc (sizeof (struct Island));
+    }
+    if (0 == strncmp (island_id, hostname, ISLANDNAME_SIZE - 1))
+    {      
+      GNUNET_array_append (island->hosts, island->nhosts, host);
+      continue;
+    }
+    DEBUG ("Adding island `%s' with %u hosts\n", island_id, island->nhosts);
+    GNUNET_array_append (rc->islands, rc->nislands, island);
+    island = NULL;
+  }
+  if (NULL != island)
+  {
+    DEBUG ("Adding island `%s' with %u hosts\n", island_id, island->nhosts);
+    GNUNET_array_append (rc->islands, rc->nislands, island);
+  }
+  DEBUG ("Total islands parsed: %u\n", rc->nislands);
+}
+#endif
+
+
 /**
  * Callback function invoked for each interface found.
  *
@@ -927,17 +1304,16 @@ static void
 host_habitable_cb (void *cls, const struct GNUNET_TESTBED_Host *host,
                    int status)
 {
-  struct RunContext *rc = cls;
+  struct CompatibilityCheckContext *hc = cls;
+  struct RunContext *rc;
   struct GNUNET_TESTBED_Host **old_hosts;
   unsigned int nhost;
 
-  for (nhost = 0; nhost < rc->num_hosts; nhost++)
-  {
-    if (host == rc->hosts[nhost])
-      break;
-  }
-  GNUNET_assert (nhost != rc->num_hosts);
-  rc->hc_handles[nhost] = NULL;
+  GNUNET_assert (NULL != (rc = hc->rc));
+  nhost = hc->index;
+  GNUNET_assert (nhost <= rc->num_hosts);
+  GNUNET_assert (host == rc->hosts[nhost]);
+  hc->h = NULL;
   if (GNUNET_NO == status)
   {
     if ((NULL != host) && (NULL != GNUNET_TESTBED_host_get_hostname (host)))
@@ -952,8 +1328,8 @@ host_habitable_cb (void *cls, const struct GNUNET_TESTBED_Host *host,
   rc->reg_hosts++;
   if (rc->reg_hosts < rc->num_hosts)
     return;
-  GNUNET_free (rc->hc_handles);
-  rc->hc_handles = NULL;
+  GNUNET_free (rc->hclist);
+  rc->hclist = NULL;
   rc->h = rc->hosts[0];
   rc->num_hosts--;
   if (0 < rc->num_hosts)
@@ -970,11 +1346,12 @@ host_habitable_cb (void *cls, const struct GNUNET_TESTBED_Host *host,
     GNUNET_free (rc->hosts);
     rc->hosts = NULL;
   }
+  parse_islands (rc);
   GNUNET_OS_network_interfaces_list (netint_proc, rc);
   if (NULL == rc->trusted_ip)
     rc->trusted_ip = GNUNET_strdup ("127.0.0.1");
   rc->cproc =
-      GNUNET_TESTBED_controller_start (rc->trusted_ip, rc->h, rc->cfg,
+      GNUNET_TESTBED_controller_start (rc->trusted_ip, rc->h,
                                        &controller_status_cb, rc);
   GNUNET_free (rc->trusted_ip);
   rc->trusted_ip = NULL;
@@ -1001,7 +1378,7 @@ timeout_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   LOG (GNUNET_ERROR_TYPE_ERROR, _("Shutting down testbed due to timeout while setup.\n"));
   shutdown_now (rc);
    if (NULL != rc->test_master)
-    rc->test_master (rc->test_master_cls, 0, NULL);
+     rc->test_master (rc->test_master_cls, 0, NULL, 0, 0);
    rc->test_master = NULL;
 }
 
@@ -1043,6 +1420,7 @@ GNUNET_TESTBED_run (const char *host_filename,
 {
   struct RunContext *rc;
   char *topology;
+  struct CompatibilityCheckContext *hc;      
   struct GNUNET_TIME_Relative timeout;
   unsigned long long random_links;
   unsigned int hid;
@@ -1141,29 +1519,33 @@ GNUNET_TESTBED_run (const char *host_filename,
   }
   if (0 != rc->num_hosts)
   {
-    rc->hc_handles =
-        GNUNET_malloc (sizeof (struct GNUNET_TESTBED_HostHabitableCheckHandle *)
-                       * rc->num_hosts);
+    rc->hclist = GNUNET_malloc (sizeof (struct CompatibilityCheckContext)
+                                * rc->num_hosts);
     for (nhost = 0; nhost < rc->num_hosts; nhost++)
     {
-      if (NULL ==
-          (rc->hc_handles[nhost] =
-           GNUNET_TESTBED_is_host_habitable (rc->hosts[nhost], rc->cfg,
-                                             &host_habitable_cb, rc)))
+      hc = &rc->hclist[nhost];
+      hc->index = nhost;
+      hc->rc = rc;
+      hc->h = GNUNET_TESTBED_is_host_habitable (rc->hosts[nhost], rc->cfg,
+                                                &host_habitable_cb, hc);
+      if (NULL == hc->h)
       {
         GNUNET_break (0);
         for (nhost = 0; nhost < rc->num_hosts; nhost++)
-          if (NULL != rc->hc_handles[nhost])
-            GNUNET_TESTBED_is_host_habitable_cancel (rc->hc_handles[nhost]);
-        GNUNET_free (rc->hc_handles);
-        rc->hc_handles = NULL;
+        {
+          hc = &rc->hclist[nhost];
+          if (NULL != hc->h)
+            GNUNET_TESTBED_is_host_habitable_cancel (hc->h);
+        }
+        GNUNET_free (rc->hclist);
+        rc->hclist = NULL;
         goto error_cleanup;
       }
     }
   }
   else
     rc->cproc =
-        GNUNET_TESTBED_controller_start ("127.0.0.1", rc->h, rc->cfg,
+        GNUNET_TESTBED_controller_start ("127.0.0.1", rc->h,
                                          &controller_status_cb, rc);
   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, "TESTBED",
                                                         "SETUP_TIMEOUT",
@@ -1172,6 +1554,7 @@ GNUNET_TESTBED_run (const char *host_filename,
     timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
                                              DEFAULT_SETUP_TIMEOUT);
   }
+  rc->rcop_map = GNUNET_CONTAINER_multihashmap32_create (256);
   rc->timeout_task =
       GNUNET_SCHEDULER_add_delayed (timeout, &timeout_task, rc);
   rc->interrupt_task =