starting point for reading topology from file
[oweals/gnunet.git] / src / testing / testing_group.c
index 3ca2f181a54e27b67a1da587e976303b905afeb5..f3eeb906940b6fa9876c0a47e47d4e76adc77d39 100644 (file)
@@ -229,6 +229,21 @@ struct CreateTopologyContext
   void *cls;
 };
 
+enum States
+{
+  /** Waiting to read number of peers */
+  NUM_PEERS,
+
+  /** Should find next peer index */
+  PEER_INDEX,
+
+  /** Should find colon */
+  COLON,
+
+  /** Should read other peer index, space, or endline */
+  OTHER_PEER_INDEX
+};
+
 #if OLD
 struct PeerConnection
 {
@@ -601,6 +616,19 @@ struct ConnectContext
   struct ConnectTopologyContext *ct_ctx;
 };
 
+struct UnblacklistContext
+{
+  /**
+   * The peergroup
+   */
+  struct GNUNET_TESTING_PeerGroup *pg;
+
+  /**
+   * uid of the first peer
+   */
+  uint32_t first_uid;
+};
+
 /**
  * Convert unique ID to hash code.
  *
@@ -700,6 +728,11 @@ GNUNET_TESTING_topology_get (enum GNUNET_TESTING_Topology *topology,
        */
     "NONE",
 
+      /**
+       * Read the topology from a file.
+       */
+    "FROM_FILE",
+
     NULL
   };
 
@@ -1903,6 +1936,63 @@ create_clique (struct GNUNET_TESTING_PeerGroup *pg,
   return connect_attempts;
 }
 
+/**
+ * Iterator over hash map entries.
+ *
+ * @param cls closure the peer group
+ * @param key the key stored in the hashmap is the
+ *            index of the peer to connect to
+ * @param value value in the hash map, handle to the peer daemon
+ * @return GNUNET_YES if we should continue to
+ *         iterate,
+ *         GNUNET_NO if not.
+ */
+static int
+unblacklist_iterator (void *cls,
+                      const GNUNET_HashCode * key,
+                      void *value)
+{
+  struct UnblacklistContext *un_ctx = cls;
+  uint32_t second_pos;
+
+  uid_from_hash (key, &second_pos);
+
+  unblacklist_connections(un_ctx->pg, un_ctx->first_uid, second_pos);
+
+  return GNUNET_YES;
+}
+
+/**
+ * Create a blacklist topology based on the allowed topology
+ * which disallows any connections not in the allowed topology
+ * at the transport level.
+ *
+ * @param pg the peergroup to create the topology on
+ * @param proc the connection processor to call to allow
+ *        up connections between two peers
+ *
+ * @return the number of connections that were set up
+ *
+ */
+static unsigned int
+copy_allowed (struct GNUNET_TESTING_PeerGroup *pg,
+             GNUNET_TESTING_ConnectionProcessor proc)
+{
+  struct UnblacklistContext un_ctx;
+  unsigned int count;
+  unsigned int total;
+
+  un_ctx.pg = pg;
+  total = 0;
+  for (count = 0; count < pg->total - 1; count++)
+    {
+      un_ctx.first_uid = count;
+      total += GNUNET_CONTAINER_multihashmap_iterate(pg->peers[count].allowed_peers, &unblacklist_iterator, &un_ctx);
+    }
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Unblacklisted %u peers\n", total);
+  return total;
+}
+
 /**
  * Create a topology given a peer group (set of running peers)
  * and a connection processor.
@@ -1936,6 +2026,145 @@ create_line (struct GNUNET_TESTING_PeerGroup *pg,
   return connect_attempts;
 }
 
+/**
+ * Create a topology given a peer group (set of running peers)
+ * and a connection processor.
+ *
+ * @param pg the peergroup to create the topology on
+ * @param filename the file to read topology information from
+ * @param proc the connection processor to call to actually set
+ *        up connections between two peers
+ *
+ * @return the number of connections that were set up
+ *
+ */
+static unsigned int
+create_from_file (struct GNUNET_TESTING_PeerGroup *pg,
+                  char *filename,
+                  GNUNET_TESTING_ConnectionProcessor proc)
+{
+  int connect_attempts;
+  unsigned int first_peer_index;
+  unsigned int second_peer_index;
+  connect_attempts = 0;
+  struct stat frstat;
+  int count;
+  char *data;
+  char *buf;
+  unsigned int total_peers;
+
+  enum States curr_state;
+
+  if (GNUNET_OK != GNUNET_DISK_file_test (filename))
+      GNUNET_DISK_fn_write (filename, NULL, 0, GNUNET_DISK_PERM_USER_READ
+        | GNUNET_DISK_PERM_USER_WRITE);
+
+  if ((0 != STAT (filename, &frstat)) || (frstat.st_size == 0))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Could not open file specified for topology!");
+      return connect_attempts;
+    }
+
+  data = GNUNET_malloc_large (frstat.st_size);
+  GNUNET_assert(data != NULL);
+  if (frstat.st_size !=
+      GNUNET_DISK_fn_read (filename, data, frstat.st_size))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Could not read file %s specified for host list, ending test!", filename);
+      GNUNET_free (data);
+      return connect_attempts;
+    }
+
+  buf = data;
+  count = 0;
+  /* First line should contain a single integer, specifying the number of peers */
+  /* Each subsequent line should contain this format PEER_INDEX:OTHER_PEER_INDEX[,...] */
+  curr_state = NUM_PEERS;
+  while (count < frstat.st_size - 1)
+    {
+      if ((buf[count] == '\n') || (buf[count] == ' '))
+      {
+        count++;
+        continue;
+      }
+
+      switch (curr_state)
+      {
+        case NUM_PEERS:
+          if (1 != sscanf(&buf[count], "%u", &total_peers))
+            {
+              GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to read number of peers from topology file!\n");
+              GNUNET_free_non_null(data);
+              return connect_attempts;
+            }
+          GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Read %u total peers in topology\n", total_peers);
+          curr_state = PEER_INDEX;
+          while((buf[count] != '\n') && (count < frstat.st_size - 1))
+            count++;
+          count++;
+          break;
+        case PEER_INDEX:
+          if (1 != sscanf(&buf[count], "%u", &first_peer_index))
+            {
+              GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to read peer index from topology file!\n");
+              GNUNET_free_non_null(data);
+              return connect_attempts;
+            }
+          GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Read next peer index %u\n", first_peer_index);
+          while((buf[count] != ':') && (count < frstat.st_size - 1))
+            count++;
+          count++;
+          curr_state = OTHER_PEER_INDEX;
+          break;
+        case COLON:
+          if (1 == sscanf(&buf[count], ":"))
+            curr_state = OTHER_PEER_INDEX;
+          count++;
+          break;
+        case OTHER_PEER_INDEX:
+          if (1 != sscanf(&buf[count], "%u", &second_peer_index))
+            {
+              GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to peer index from topology file!\n");
+              GNUNET_free_non_null(data);
+              return connect_attempts;
+            }
+          GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Read second peer index %u\n", second_peer_index);
+          while((buf[count] != '\n') && (buf[count] != ' ') && (count < frstat.st_size - 1))
+            count++;
+          if (buf[count] == '\n')
+          {
+            curr_state = PEER_INDEX;
+          }
+          else if (buf[count] != ' ')
+          {
+            curr_state = OTHER_PEER_INDEX;
+          }
+          count++;
+          curr_state = OTHER_PEER_INDEX;
+          break;
+        default:
+          GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Found bad data in topology file while in state %d!\n", curr_state);
+          GNUNET_break(0);
+          return connect_attempts;
+      }
+
+    }
+#if 0
+  /* Connect each peer to the next highest numbered peer */
+  for (count = 0; count < pg->total - 1; count++)
+    {
+#if VERBOSE_TESTING
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Connecting peer %d to peer %d\n", first_peer_index, second_peer_index);
+#endif
+      connect_attempts += proc (pg, first_peer_index, second_peer_index);
+    }
+#endif
+  return connect_attempts;
+}
+
 /**
  * Create a topology given a peer group (set of running peers)
  * and a connection processor.
@@ -2037,6 +2266,7 @@ blacklist_file_iterator (void *cls, const GNUNET_HashCode * key, void *value)
 
   temppeer = &peer->id;
   GNUNET_CRYPTO_hash_to_enc (&temppeer->hashPubKey, &peer_enc);
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Writing entry %s:%s to file\n", blacklist_ctx->transport, (char *) &peer_enc);
   fprintf (blacklist_ctx->temp_file_handle, "%s:%s\n",
            blacklist_ctx->transport, (char *) &peer_enc);
 
@@ -2212,6 +2442,7 @@ create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg,
   unsigned int i;
   char *pos;
   char *temp_transports;
+  int entry_count;
 
   procarr = GNUNET_malloc (sizeof (struct GNUNET_OS_Process *) * pg->total);
   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
@@ -2233,7 +2464,7 @@ create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg,
             {
               temp_transports[i] = '\0';
               blacklist_ctx.transport = pos;
-              GNUNET_CONTAINER_multihashmap_iterate (pg->
+              entry_count = GNUNET_CONTAINER_multihashmap_iterate (pg->
                                                      peers
                                                      [pg_iter].blacklisted_peers,
                                                      &blacklist_file_iterator,
@@ -2618,6 +2849,7 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
   int ret;
   unsigned int num_connections;
   int unblacklisted_connections;
+  char *filename;
 
   switch (topology)
     {
@@ -2681,6 +2913,21 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
 #endif
       num_connections = create_line (pg, &add_allowed_connections);
       break;
+    case GNUNET_TESTING_TOPOLOGY_FROM_FILE:
+#if VERBOSE_TESTING
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  _("Creating topology from file!\n"));
+#endif
+      if (GNUNET_OK ==
+          GNUNET_CONFIGURATION_get_value_string (pg->cfg, "testing", "topology_file",
+                                                 &filename))
+        num_connections = create_from_file (pg, filename, &add_allowed_connections);
+      else
+      {
+        GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Missing configuration option TESTING:TOPOLOGY_FILE for creating topology from file!");
+        num_connections = 0;
+      }
+      break;
     case GNUNET_TESTING_TOPOLOGY_NONE:
 #if VERBOSE_TESTING
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -2716,8 +2963,7 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
     }
 
   /* Use the create clique method to initially set all connections as blacklisted. */
-  if (restrict_topology != GNUNET_TESTING_TOPOLOGY_NONE)
-    create_clique (pg, &blacklist_connections);
+  create_clique (pg, &blacklist_connections);
 
   unblacklisted_connections = 0;
   /* Un-blacklist connections as per the topology specified */
@@ -2795,17 +3041,23 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
       unblacklisted_connections = create_line (pg, &unblacklist_connections);
       break;
     case GNUNET_TESTING_TOPOLOGY_NONE:
+    case GNUNET_TESTING_TOPOLOGY_FROM_FILE:
 #if VERBOSE_TESTING
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   _
                   ("Creating no blacklist topology (all peers can connect at transport level)\n"));
 #endif
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                _
+                ("Creating blacklist topology from allowed\n"));
+    unblacklisted_connections = copy_allowed (pg, &unblacklist_connections);
     default:
       break;
     }
 
   if ((unblacklisted_connections > 0) && (restrict_transports != NULL))
     {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Creating blacklist with `%s'", restrict_transports);
       ret = create_and_copy_blacklist_files (pg, restrict_transports);
       if (ret != GNUNET_OK)
         {
@@ -4124,7 +4376,8 @@ GNUNET_TESTING_daemons_continue_startup (struct GNUNET_TESTING_PeerGroup *pg)
  * @param cb_cls closure for cb
  * @param connect_callback function to call each time two hosts are connected
  * @param connect_callback_cls closure for connect_callback
- * @param hostnames linked list of hosts to use to start peers on (NULL to run on localhost only)
+ * @param hostnames linked list of host structs to use to start peers on
+ *                  (NULL to run on localhost only)
  *
  * @return NULL on error, otherwise handle to control peer group
  */