- distribute peers equally among island nodes on SuperMUC
[oweals/gnunet.git] / src / testbed / testbed_api_testbed.c
index 767d91cb969e8e043cf2854d1fc412d336f1f7ff..c4cfe59b114da6f0d3ab77f5cf616eab82689531 100644 (file)
  */
 #define DEFAULT_SETUP_TIMEOUT 300
 
+/**
+ * Testbed Run Handle
+ */
+struct RunContext;
+
 /**
  * Context information for the operation we start
  */
@@ -82,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
    */
@@ -116,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,10 +229,17 @@ struct RunContext
    */
   struct GNUNET_TESTBED_Host **hosts;
 
+#if ENABLE_LL
+  /**
+   * Array of SuperMUC islands
+   */
+  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
@@ -195,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;
 
@@ -291,8 +350,21 @@ struct RunContext
    */
   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)
 {  
@@ -300,13 +372,31 @@ rcop_key (void *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)
 {
@@ -323,6 +413,15 @@ search_iterator (void *cls, uint32_t key, void *value)
   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)
 {
@@ -342,6 +441,13 @@ search_rcop (struct RunContext *rc, struct GNUNET_TESTBED_Operation *op)
   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)
 {
@@ -351,6 +457,14 @@ insert_rcop (struct RunContext *rc, struct RunContextOperation *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)
 {
@@ -360,6 +474,25 @@ remove_rcop (struct RunContext *rc, struct RunContextOperation *rcop)
                                                          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
  *
@@ -370,12 +503,12 @@ 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 (0 == GNUNET_CONTAINER_multihashmap32_size (rc->rcop_map));
   GNUNET_CONTAINER_multihashmap32_destroy (rc->rcop_map);
@@ -385,8 +518,12 @@ cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
     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);
@@ -395,6 +532,15 @@ 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)
 {
@@ -408,18 +554,29 @@ rcop_cleanup_iterator (void *cls, uint32_t key, void *value)
   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 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)
@@ -509,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)
 {
@@ -652,8 +815,17 @@ static void
 create_peers (struct RunContext *rc)
 {
   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 =
@@ -664,12 +836,31 @@ create_peers (struct RunContext *rc)
   {
     rcop = GNUNET_malloc (sizeof (struct RunContextOperation));
     rcop->rc = rc;
-    rcop->op =
-        GNUNET_TESTBED_peer_create (rc->c,
-                                    (0 ==
-                                     rc->num_hosts) ? rc->h : rc->hosts[peer %
-                                                                        rc->num_hosts],
-                                    rc->cfg, &peer_create_cb, rcop);
+#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);
   }
@@ -706,11 +897,71 @@ event_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event)
       remove_rcop (rc, rcop);
       GNUNET_TESTBED_operation_done (rcop->op);
       GNUNET_free (rcop);
-      if (rc->reg_hosts == rc->num_hosts)
+#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++)
+      {
+        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);
+      shutdown_now (rc);
+      return;
+    }
+  }
+#if ENABLE_LL
+  if (RC_ISLANDS_LINKED == rc->state)
+  {
+    switch (event->type)
+    {
+    case GNUNET_TESTBED_ET_OPERATION_FINISHED:
+      rcop = event->op_cls;
+      if (NULL != event->details.operation_finished.emsg)
       {
-        rc->state = RC_LINKED;
-        create_peers (rc);
+        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);
@@ -718,6 +969,7 @@ event_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event)
       return;
     }
   }
+#endif
   if (GNUNET_TESTBED_ET_OPERATION_FINISHED != event->type)
     goto call_cc;
   if (NULL == (rcop = search_rcop (rc, event->op)))
@@ -858,23 +1110,39 @@ register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct RunContext *rc = cls;
   struct RunContextOperation *rcop;
-  unsigned int slave;
+  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[slave],
-                                          rc->h, rc->cfg, GNUNET_YES);
+          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++)
+    {
+      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;
   }
@@ -941,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.
  *
@@ -993,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)))
@@ -1018,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)
@@ -1036,6 +1346,7 @@ 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");
@@ -1109,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;
@@ -1207,22 +1519,26 @@ 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;
       }
     }