Tests: Initial test code for LCP2 IP allocation algorithm.
authorMartin Schwenke <martin@meltin.net>
Thu, 28 Jul 2011 05:22:42 +0000 (15:22 +1000)
committerRonnie Sahlberg <ronniesahlberg@gmail.com>
Thu, 28 Jul 2011 23:01:36 +0000 (09:01 +1000)
Move struct ctdb_public_ip_list to ctdb_private.h and put some
definitions for some functions from ctdb_takeover.c there.  This
allows those functions to be called from unit tests.

Add ctdb_takeover_tests.c and the Makefile support to build it.

Signed-off-by: Martin Schwenke <martin@meltin.net>
(This used to be ctdb commit 9d34be0233edf3bc022345c0494c4b2a4d7f8480)

ctdb/Makefile.in
ctdb/include/ctdb_private.h
ctdb/server/ctdb_takeover.c
ctdb/tests/src/ctdb_takeover_tests.c [new file with mode: 0644]

index aec64e1d9f2864e9d3a0f774ea2bfdb8127b7b54..5fa9e98c10df0471fc56f5d74a597f2720a94da0 100755 (executable)
@@ -70,6 +70,7 @@ TEST_BINS=tests/bin/ctdb_bench tests/bin/ctdb_fetch tests/bin/ctdb_fetch_one \
        tests/bin/ctdb_fetch_lock_once tests/bin/ctdb_store \
        tests/bin/ctdb_randrec tests/bin/ctdb_persistent \
        tests/bin/ctdb_traverse tests/bin/rb_test tests/bin/ctdb_transaction \
+       tests/bin/ctdb_takeover_tests
        @INFINIBAND_BINS@
 
 BINS = bin/ctdb @CTDB_SCSI_IO@ bin/smnotify bin/ping_pong bin/ltdbtool
@@ -190,6 +191,12 @@ tests/bin/ctdb_transaction: $(CTDB_CLIENT_OBJ) tests/src/ctdb_transaction.o
        @echo Linking $@
        @$(CC) $(CFLAGS) -o $@ tests/src/ctdb_transaction.o $(CTDB_CLIENT_OBJ) $(LIB_FLAGS)
 
+CTDB_TAKEOVER_OBJ = $(CTDB_SERVER_OBJ:server/ctdbd.o=)
+
+tests/bin/ctdb_takeover_tests: $(CTDB_TAKEOVER_OBJ) tests/src/ctdb_takeover_tests.o
+       @echo Linking $@
+       @$(CC) $(CFLAGS) -o $@ tests/src/ctdb_takeover_tests.o $(CTDB_TAKEOVER_OBJ) $(LIB_FLAGS)
+
 tests/bin/ibwrapper_test: $(CTDB_CLIENT_OBJ) ib/ibwrapper_test.o
        @echo Linking $@
        @$(CC) $(CFLAGS) -o $@ ib/ibwrapper_test.o $(CTDB_CLIENT_OBJ) $(LIB_FLAGS)
index 8a24d3b676fa4f33196a1c464edfce5c8e4f2755..37f8a7344ad1ede654aeecf58f82964f556b6794 100644 (file)
@@ -1411,4 +1411,37 @@ int32_t ctdb_local_schedule_for_deletion(struct ctdb_db_context *ctdb_db,
 
 struct ctdb_ltdb_header *ctdb_header_from_record_handle(struct ctdb_record_handle *h);
 
+/* For unit testing ctdb_transaction.c. */
+struct ctdb_public_ip_list {
+       struct ctdb_public_ip_list *next;
+       uint32_t pnn;
+       ctdb_sock_addr addr;
+};
+uint32_t ip_distance(ctdb_sock_addr *ip1, ctdb_sock_addr *ip2);
+uint32_t ip_distance_2_sum(ctdb_sock_addr *ip,
+                          struct ctdb_public_ip_list *ips,
+                          int pnn);
+uint32_t lcp2_imbalance(struct ctdb_public_ip_list * all_ips, int pnn);
+void lcp2_init(struct ctdb_context * tmp_ctx,
+              struct ctdb_node_map * nodemap,
+              uint32_t mask,
+              struct ctdb_public_ip_list *all_ips,
+              uint32_t **lcp2_imbalances,
+              bool **newly_healthy);
+void lcp2_allocate_unassigned(struct ctdb_context *ctdb,
+                             struct ctdb_node_map *nodemap,
+                             uint32_t mask,
+                             struct ctdb_public_ip_list *all_ips,
+                             uint32_t *lcp2_imbalances);
+bool lcp2_failback(struct ctdb_context *ctdb,
+                  struct ctdb_node_map *nodemap,
+                  uint32_t mask,
+                  struct ctdb_public_ip_list *all_ips,
+                  uint32_t *lcp2_imbalances,
+                  bool *newly_healthy);
+void ctdb_takeover_run_core(struct ctdb_context *ctdb,
+                           struct ctdb_node_map *nodemap,
+                           struct ctdb_public_ip_list **all_ips_p);
+
+
 #endif
index 74edd819b6837b0f4a85524df6d83e812093afa7..5512acc379b07086813bab77e2262a8c09f0a1e2 100644 (file)
@@ -1059,13 +1059,6 @@ int ctdb_set_single_public_ip(struct ctdb_context *ctdb,
        return 0;
 }
 
-struct ctdb_public_ip_list {
-       struct ctdb_public_ip_list *next;
-       uint32_t pnn;
-       ctdb_sock_addr addr;
-};
-
-
 /* Given a physical node, return the number of
    public addresses that is currently assigned to this node.
 */
diff --git a/ctdb/tests/src/ctdb_takeover_tests.c b/ctdb/tests/src/ctdb_takeover_tests.c
new file mode 100644 (file)
index 0000000..5fd2332
--- /dev/null
@@ -0,0 +1,378 @@
+/* 
+   Tests for ctdb_takeover.c
+
+   Copyright (C) Martin Schwenke 2011
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../include/ctdb_private.h"
+
+/*
+ * Need these, since they're defined in ctdbd.c but we can't link
+ * that.
+ */
+int script_log_level;
+bool fast_start;
+void ctdb_load_nodes_file(struct ctdb_context *ctdb) {}
+
+/* Format of each line is "IP pnn" - the separator has to be at least
+ * 1 space (not a tab or whatever - a space!).
+ */
+static struct ctdb_public_ip_list *
+read_ctdb_public_ip_list(TALLOC_CTX *ctx)
+{
+       char line[1024];
+       ctdb_sock_addr addr;
+       char *t;
+       int pnn;
+       struct ctdb_public_ip_list *last = NULL;
+
+       struct ctdb_public_ip_list *ret = NULL;
+
+       while (fgets(line, sizeof(line), stdin) != NULL) {
+               
+               if ((t = strchr(line, ' ')) != NULL) {
+                       /* Make line contain just the address */
+                       *t = '\0';
+                       /* Point to PNN or leading whitespace...  */
+                       t++;
+                       pnn = (int) strtol(t, (char **) NULL, 10);
+               } else {
+                       /* Assume just an IP address, default to PNN -1 */
+                       if ((t = strchr(line, '\n')) != NULL) {
+                               *t = '\0';
+                       }
+                       pnn = -1;
+               }
+              
+               if (parse_ip(line, NULL, 0, &addr)) {
+                       if (last == NULL) {
+                               last = talloc(ctx, struct ctdb_public_ip_list);
+                       } else {
+                               last->next = talloc(ctx, struct ctdb_public_ip_list);
+                               last = last->next;
+                       }
+                       last->next = NULL;
+                       last->pnn = pnn;
+                       memcpy(&(last->addr), &addr, sizeof(addr));
+                       if (ret == NULL) {
+                               ret = last;
+                       }
+               } else {
+                       DEBUG(DEBUG_ERR, (__location__ " ERROR, bad address :%s\n", line));
+               }
+       }
+                       
+       return ret;
+}
+
+void print_ctdb_public_ip_list(struct ctdb_public_ip_list * ips)
+{
+       while (ips) {
+               printf("%s %d\n", ctdb_addr_to_str(&(ips->addr)), ips->pnn);
+               ips = ips->next;
+       }
+}
+
+/* Read some IPs from stdin, 1 per line, parse them and then print
+ * them back out. */
+void ctdb_test_read_ctdb_public_ip_list(void)
+{
+       struct ctdb_public_ip_list *l;
+
+       TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+
+       l = read_ctdb_public_ip_list(tmp_ctx);
+
+       print_ctdb_public_ip_list(l);
+
+       talloc_free(tmp_ctx);
+}
+
+/* Read 2 IPs from stdin, calculate the IP distance and print it. */
+void ctdb_test_ip_distance(void)
+{
+       struct ctdb_public_ip_list *l;
+       uint32_t distance;
+
+       TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+
+       l = read_ctdb_public_ip_list(tmp_ctx);
+
+       if (l && l->next) {
+               distance = ip_distance(&(l->addr), &(l->next->addr));
+               printf ("%lu\n", (unsigned long) distance);
+       }
+
+       talloc_free(tmp_ctx);
+}
+
+/* Read some IPs from stdin, calculate the sum of the squares of the
+ * IP distances between the 1st argument and those read that are on
+ * the given node. The given IP must one of the ones in the list.  */
+void ctdb_test_ip_distance_2_sum(const char ip[], int pnn)
+{
+       struct ctdb_public_ip_list *l;
+       struct ctdb_public_ip_list *t;
+       ctdb_sock_addr addr;
+       uint32_t distance;
+
+       TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+
+       
+       l = read_ctdb_public_ip_list(tmp_ctx);
+
+       if (l && parse_ip(ip, NULL, 0, &addr)) {
+               /* find the entry for the specified IP */
+               for (t=l; t!=NULL; t=t->next) {
+                       if (ctdb_same_ip(&(t->addr), &addr)) {
+                               break;
+                       }
+               }
+
+               if (t == NULL) {
+                       fprintf(stderr, "IP NOT PRESENT IN LIST");
+                       exit(1);
+               }
+
+               distance = ip_distance_2_sum(&(t->addr), l, pnn);
+               printf ("%lu\n", (unsigned long) distance);
+       } else {
+               fprintf(stderr, "BAD INPUT");
+               exit(1);
+       }
+
+       talloc_free(tmp_ctx);
+}
+
+/* Read some IPs from stdin, calculate the sume of the squares of the
+ * IP distances between the first and the rest, and print it. */
+void ctdb_test_lcp2_imbalance(int pnn)
+{
+       struct ctdb_public_ip_list *l;
+       uint32_t imbalance;
+
+       TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+
+       l = read_ctdb_public_ip_list(tmp_ctx);
+
+       imbalance = lcp2_imbalance(l, pnn);
+       printf ("%lu\n", (unsigned long) imbalance);
+
+       talloc_free(tmp_ctx);
+}
+
+void ctdb_test_init(const char nodestates[],
+                   struct ctdb_context **ctdb,
+                   struct ctdb_public_ip_list **all_ips,
+                   struct ctdb_node_map **nodemap)
+{
+       struct ctdb_public_ip_list *t;
+       struct ctdb_all_public_ips *available_public_ips;
+       int i, numips, numnodes;
+
+       numnodes = strlen(nodestates);
+
+       *ctdb = talloc_zero(NULL, struct ctdb_context);
+
+       /* Fake things up... */
+       (*ctdb)->num_nodes = numnodes;
+
+       (*ctdb)->tunable.deterministic_public_ips = 0;
+       (*ctdb)->tunable.disable_ip_failover = 0;
+       (*ctdb)->tunable.no_ip_failback = 0;
+
+       if (getenv("CTDB_LCP2")) {
+               if (strcmp(getenv("CTDB_LCP2"), "yes") == 0) {
+                       (*ctdb)->tunable.lcp2_public_ip_assignment = 1;
+               } else {
+                       (*ctdb)->tunable.lcp2_public_ip_assignment = 0;
+               }
+       }
+
+       *nodemap =  talloc_array(*ctdb, struct ctdb_node_map, numnodes);
+       (*nodemap)->num = numnodes;
+
+       for (i=0; i < numnodes; i++) {
+               (*nodemap)->nodes[i].pnn = i;
+               (*nodemap)->nodes[i].flags = nodestates[i] - '0';
+               /* *nodemap->nodes[i].sockaddr is uninitialised */
+       }
+
+       *all_ips = read_ctdb_public_ip_list(*ctdb);
+       numips = 0;
+       for (t = *all_ips; t != NULL; t = t->next) {
+               numips++;
+       }
+
+       available_public_ips = talloc_array(*ctdb, struct ctdb_all_public_ips, numips); // FIXME: bogus size, overkill
+       available_public_ips->num = numips;
+       for (t = *all_ips, i=0; t != NULL && i < numips ; t = t->next, i++) {
+               available_public_ips->ips[i].pnn = t->pnn;
+               memcpy(&(available_public_ips->ips[i].addr), &(t->addr), sizeof(t->addr));
+       }
+
+       (*ctdb)->nodes = talloc_array(*ctdb, struct ctdb_node *, numnodes); // FIXME: bogus size, overkill
+
+       /* Setup both nodemap and ctdb->nodes.  Mark all nodes as
+        * healthy - change this later. */
+       for (i=0; i < numnodes; i++) {
+               uint32_t flags = nodestates[i] - '0' ? NODE_FLAGS_UNHEALTHY : 0;
+               (*nodemap)->nodes[i].pnn = i;
+               (*nodemap)->nodes[i].flags = flags;
+               /* nodemap->nodes[i].sockaddr is uninitialised */
+
+               (*ctdb)->nodes[i] = talloc(*ctdb, struct ctdb_node);
+               (*ctdb)->nodes[i]->pnn = i;
+               (*ctdb)->nodes[i]->flags = flags;
+               (*ctdb)->nodes[i]->available_public_ips = available_public_ips;
+               (*ctdb)->nodes[i]->known_public_ips = available_public_ips;
+       }
+}
+
+/* IP layout is read from stdin. */
+void ctdb_test_lcp2_allocate_unassigned(const char nodestates[])
+{
+       struct ctdb_context *ctdb;
+       struct ctdb_public_ip_list *all_ips;
+       struct ctdb_node_map *nodemap;
+
+       uint32_t *lcp2_imbalances;
+       bool *newly_healthy;
+
+       ctdb_test_init(nodestates, &ctdb, &all_ips, &nodemap);
+
+       lcp2_init(ctdb, nodemap,
+                 NODE_FLAGS_INACTIVE|NODE_FLAGS_DISABLED,
+                 all_ips, &lcp2_imbalances, &newly_healthy);
+
+       lcp2_allocate_unassigned(ctdb, nodemap,
+                                NODE_FLAGS_INACTIVE|NODE_FLAGS_DISABLED,
+                                all_ips, lcp2_imbalances);
+
+       print_ctdb_public_ip_list(all_ips);
+
+       talloc_free(ctdb);
+}
+
+/* IP layout is read from stdin. */
+void ctdb_test_lcp2_failback(const char nodestates[])
+{
+       struct ctdb_context *ctdb;
+       struct ctdb_public_ip_list *all_ips;
+       struct ctdb_node_map *nodemap;
+
+       uint32_t *lcp2_imbalances;
+       bool *newly_healthy;
+
+       ctdb_test_init(nodestates, &ctdb, &all_ips, &nodemap);
+
+       lcp2_init(ctdb, nodemap,
+                 NODE_FLAGS_INACTIVE|NODE_FLAGS_DISABLED,
+                 all_ips, &lcp2_imbalances, &newly_healthy);
+
+       lcp2_failback(ctdb, nodemap,
+                                NODE_FLAGS_INACTIVE|NODE_FLAGS_DISABLED,
+                     all_ips, lcp2_imbalances, newly_healthy);
+
+       print_ctdb_public_ip_list(all_ips);
+
+       talloc_free(ctdb);
+}
+
+/* IP layout is read from stdin. */
+void ctdb_test_lcp2_failback_loop(const char nodestates[])
+{
+       struct ctdb_context *ctdb;
+       struct ctdb_public_ip_list *all_ips;
+       struct ctdb_node_map *nodemap;
+
+       uint32_t *lcp2_imbalances;
+       bool *newly_healthy;
+
+       ctdb_test_init(nodestates, &ctdb, &all_ips, &nodemap);
+
+       lcp2_init(ctdb, nodemap,
+                 NODE_FLAGS_INACTIVE|NODE_FLAGS_DISABLED,
+                 all_ips, &lcp2_imbalances, &newly_healthy);
+
+try_again:
+       if (lcp2_failback(ctdb, nodemap,
+                         NODE_FLAGS_INACTIVE|NODE_FLAGS_DISABLED,
+                         all_ips, lcp2_imbalances, newly_healthy)) {
+               goto try_again;
+       }
+
+       print_ctdb_public_ip_list(all_ips);
+
+       talloc_free(ctdb);
+}
+
+/* IP layout is read from stdin. */
+void ctdb_test_ctdb_takeover_run_core(const char nodestates[])
+{
+       struct ctdb_context *ctdb;
+       struct ctdb_public_ip_list *all_ips;
+       struct ctdb_node_map *nodemap;
+
+       ctdb_test_init(nodestates, &ctdb, &all_ips, &nodemap);
+
+       ctdb_takeover_run_core(ctdb, nodemap, &all_ips);
+
+       print_ctdb_public_ip_list(all_ips);
+
+       talloc_free(ctdb);
+}
+
+void usage(void)
+{
+       fprintf(stderr, "usage: ctdb_takeover_tests <op>\n");
+       exit(1);
+}
+
+int main(int argc, const char *argv[])
+{
+       LogLevel = DEBUG_DEBUG;
+       if (getenv("CTDB_TEST_LOGLEVEL")) {
+               LogLevel = atoi(getenv("CTDB_TEST_LOGLEVEL"));
+       }
+
+       if (argc < 2) {
+               usage();
+       }
+
+       if (strcmp(argv[1], "ip_list") == 0) {
+               ctdb_test_read_ctdb_public_ip_list();
+       } else if (strcmp(argv[1], "ip_distance") == 0) {
+               ctdb_test_ip_distance();
+       } else if (argc == 4 && strcmp(argv[1], "ip_distance_2_sum") == 0) {
+               ctdb_test_ip_distance_2_sum(argv[2], atoi(argv[3]));
+       } else if (argc >= 3 && strcmp(argv[1], "lcp2_imbalance") == 0) {
+               ctdb_test_lcp2_imbalance(atoi(argv[2]));
+       } else if (argc == 3 && strcmp(argv[1], "lcp2_allocate_unassigned") == 0) {
+               ctdb_test_lcp2_allocate_unassigned(argv[2]);
+       } else if (argc == 3 && strcmp(argv[1], "lcp2_failback") == 0) {
+               ctdb_test_lcp2_failback(argv[2]);
+       } else if (argc == 3 && strcmp(argv[1], "lcp2_failback_loop") == 0) {
+               ctdb_test_lcp2_failback_loop(argv[2]);
+       } else if (argc == 3 && strcmp(argv[1], "ctdb_takeover_run_core") == 0) {
+               ctdb_test_ctdb_takeover_run_core(argv[2]);
+       } else {
+               usage();
+       }
+
+       return 0;
+}