/* 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 . */ #include "ctdbd_test.c" /* This is lazy... but it is test code! */ #define CTDB_TEST_MAX_NODES 256 #define CTDB_TEST_MAX_IPS 1024 /* 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; } static 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. */ static 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); } /* Format of each line is "IP CURRENT_PNN ALLOWED_PNN,...". */ static bool read_ctdb_public_ip_info(TALLOC_CTX *ctx, int numnodes, struct ctdb_public_ip_list ** all_ips, struct ctdb_all_public_ips *** avail) { char line[1024]; ctdb_sock_addr addr; char *t, *tok; struct ctdb_public_ip_list * ta; int pnn, numips, curr, n, i; struct ctdb_all_public_ips * a; struct ctdb_public_ip_list *last = NULL; *avail = talloc_array_size(ctx, sizeof(struct ctdb_all_public_ips *), CTDB_TEST_MAX_NODES); memset(*avail, 0, sizeof(struct ctdb_all_public_ips *) * CTDB_TEST_MAX_NODES); numips = 0; *all_ips = NULL; while (fgets(line, sizeof(line), stdin) != NULL) { /* Get rid of pesky newline */ if ((t = strchr(line, '\n')) != NULL) { *t = '\0'; } /* Exit on an empty line */ if (line[0] == '\0') { break; } /* Get the IP address */ tok = strtok(line, " \t"); if (tok == NULL) { DEBUG(DEBUG_ERR, (__location__ " WARNING, bad line ignored :%s\n", line)); continue; } if (!parse_ip(tok, NULL, 0, &addr)) { DEBUG(DEBUG_ERR, (__location__ " ERROR, bad address :%s\n", tok)); continue; } numips++; if (numips > CTDB_TEST_MAX_IPS) { DEBUG(DEBUG_ERR, ("ERROR: Exceeding CTDB_TEST_MAX_IPS: %d\n", CTDB_TEST_MAX_IPS)); exit(1); } /* Get the PNN */ pnn = -1; tok = strtok(NULL, " \t"); if (tok != NULL) { pnn = (int) strtol(tok, (char **) NULL, 10); } /* Add address + pnn to all_ips */ 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 (*all_ips == NULL) { *all_ips = last; } tok = strtok(NULL, " \t#"); if (tok == NULL) { continue; } /* Handle allowed nodes for addr */ t = strtok(tok, ","); while (t != NULL) { n = (int) strtol(t, (char **) NULL, 10); if ((*avail)[n] == NULL) { (*avail)[n] = talloc_array(ctx, struct ctdb_all_public_ips, CTDB_TEST_MAX_IPS); (*avail)[n]->num = 0; } curr = (*avail)[n]->num; (*avail)[n]->ips[curr].pnn = pnn; memcpy(&((*avail)[n]->ips[curr].addr), &addr, sizeof(addr)); (*avail)[n]->num++; t = strtok(NULL, ","); } } /* Build list of all allowed IPs */ a = talloc_array(ctx, struct ctdb_all_public_ips, CTDB_TEST_MAX_IPS); a->num = numips; for (ta = *all_ips, i=0; ta != NULL && i < numips ; ta = ta->next, i++) { a->ips[i].pnn = ta->pnn; memcpy(&(a->ips[i].addr), &(ta->addr), sizeof(ta->addr)); } /* Assign it to any nodes that don't have a list assigned */ for (n = 0; n < numnodes; n++) { if ((*avail)[n] == NULL) { (*avail)[n] = a; } } return true; } static void print_ctdb_available_ips(int numnodes, struct ctdb_all_public_ips **avail) { int n, i; for (n = 0; n < numnodes; n++) { if ((avail[n] != NULL) && (avail[n]->num > 0)) { printf("%d:", n); for (i = 0; i < avail[n]->num; i++) { printf("%s%s", (i == 0) ? " " : ", ", ctdb_addr_to_str(&(avail[n]->ips[i].addr))); } printf("\n"); } } } static void ctdb_test_read_ctdb_public_ip_info(const char nodestates[]) { int numnodes; struct ctdb_public_ip_list *l; struct ctdb_all_public_ips **avail; char *tok, *ns; TALLOC_CTX *tmp_ctx = talloc_new(NULL); /* Avoid that const */ ns = talloc_strdup(tmp_ctx, nodestates); numnodes = 0; tok = strtok(ns, ","); while (tok != NULL) { numnodes++; if (numnodes > CTDB_TEST_MAX_NODES) { DEBUG(DEBUG_ERR, ("ERROR: Exceeding CTDB_TEST_MAX_NODES: %d\n", CTDB_TEST_MAX_NODES)); exit(1); } tok = strtok(NULL, ","); } read_ctdb_public_ip_info(tmp_ctx, numnodes, &l, &avail); print_ctdb_public_ip_list(l); print_ctdb_available_ips(numnodes, avail); talloc_free(tmp_ctx); } /* Read 2 IPs from stdin, calculate the IP distance and print it. */ static 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. */ static 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. */ static 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); } static uint32_t *get_tunable_values(TALLOC_CTX *tmp_ctx, int numnodes, const char *tunable) { int i; char *tok; uint32_t *tvals = talloc_zero_array(tmp_ctx, uint32_t, numnodes); char *t = getenv(tunable); if (t) { if (strcmp(t, "1") == 0) { for (i=0; i CTDB_TEST_MAX_NODES) { DEBUG(DEBUG_ERR, ("ERROR: Exceeding CTDB_TEST_MAX_NODES: %d\n", CTDB_TEST_MAX_NODES)); exit(1); } tok = strtok(NULL, ","); } /* Fake things up... */ (*ctdb)->num_nodes = numnodes; /* Default to LCP2 */ (*ctdb)->tunable.lcp2_public_ip_assignment = 1; (*ctdb)->tunable.deterministic_public_ips = 0; (*ctdb)->tunable.disable_ip_failover = 0; (*ctdb)->tunable.no_ip_failback = 0; if ((t = getenv("CTDB_IP_ALGORITHM"))) { if (strcmp(t, "lcp2") == 0) { (*ctdb)->tunable.lcp2_public_ip_assignment = 1; } else if (strcmp(t, "nondet") == 0) { (*ctdb)->tunable.lcp2_public_ip_assignment = 0; } else if (strcmp(t, "det") == 0) { (*ctdb)->tunable.lcp2_public_ip_assignment = 0; (*ctdb)->tunable.deterministic_public_ips = 1; } else { fprintf(stderr, "ERROR: unknown IP algorithm %s\n", t); exit(1); } } tval_noiptakeover = get_tunable_values(*ctdb, numnodes, "CTDB_SET_NoIPTakeover"); tval_noiptakeoverondisabled = get_tunable_values(*ctdb, numnodes, "CTDB_SET_NoIPHostOnAllDisabled"); runstate = get_runstate(*ctdb, numnodes); nodemap = talloc_array(*ctdb, struct ctdb_node_map, numnodes); nodemap->num = numnodes; if (!read_ips_for_multiple_nodes) { read_ctdb_public_ip_info(*ctdb, numnodes, all_ips, &avail); } (*ctdb)->nodes = talloc_array(*ctdb, struct ctdb_node *, numnodes); // FIXME: bogus size, overkill for (i=0; i < numnodes; i++) { nodemap->nodes[i].pnn = i; nodemap->nodes[i].flags = nodeflags[i]; /* nodemap->nodes[i].sockaddr is uninitialised */ if (read_ips_for_multiple_nodes) { read_ctdb_public_ip_info(*ctdb, numnodes, all_ips, &avail); } (*ctdb)->nodes[i] = talloc(*ctdb, struct ctdb_node); (*ctdb)->nodes[i]->pnn = i; (*ctdb)->nodes[i]->flags = nodeflags[i]; (*ctdb)->nodes[i]->available_public_ips = avail[i]; (*ctdb)->nodes[i]->known_public_ips = avail[i]; } *ipflags = set_ipflags_internal(*ctdb, *ctdb, nodemap, tval_noiptakeover, tval_noiptakeoverondisabled, runstate); } /* IP layout is read from stdin. */ static void ctdb_test_lcp2_allocate_unassigned(const char nodestates[]) { struct ctdb_context *ctdb; struct ctdb_public_ip_list *all_ips; struct ctdb_ipflags *ipflags; uint32_t *lcp2_imbalances; bool *newly_healthy; ctdb_test_init(nodestates, &ctdb, &all_ips, &ipflags, false); lcp2_init(ctdb, ipflags, all_ips, NULL, &lcp2_imbalances, &newly_healthy); lcp2_allocate_unassigned(ctdb, ipflags, all_ips, lcp2_imbalances); print_ctdb_public_ip_list(all_ips); talloc_free(ctdb); } /* IP layout is read from stdin. */ static void ctdb_test_lcp2_failback(const char nodestates[]) { struct ctdb_context *ctdb; struct ctdb_public_ip_list *all_ips; struct ctdb_ipflags *ipflags; uint32_t *lcp2_imbalances; bool *newly_healthy; ctdb_test_init(nodestates, &ctdb, &all_ips, &ipflags, false); lcp2_init(ctdb, ipflags, all_ips, NULL, &lcp2_imbalances, &newly_healthy); lcp2_failback(ctdb, ipflags, all_ips, lcp2_imbalances, newly_healthy); print_ctdb_public_ip_list(all_ips); talloc_free(ctdb); } /* IP layout is read from stdin. */ static void ctdb_test_lcp2_failback_loop(const char nodestates[]) { struct ctdb_context *ctdb; struct ctdb_public_ip_list *all_ips; struct ctdb_ipflags *ipflags; uint32_t *lcp2_imbalances; bool *newly_healthy; ctdb_test_init(nodestates, &ctdb, &all_ips, &ipflags, false); lcp2_init(ctdb, ipflags, all_ips, NULL, &lcp2_imbalances, &newly_healthy); lcp2_failback(ctdb, ipflags, all_ips, lcp2_imbalances, newly_healthy); print_ctdb_public_ip_list(all_ips); talloc_free(ctdb); } /* IP layout is read from stdin. See comment for ctdb_test_init() for * explanation of read_ips_for_multiple_nodes. */ static void ctdb_test_ctdb_takeover_run_core(const char nodestates[], bool read_ips_for_multiple_nodes) { struct ctdb_context *ctdb; struct ctdb_public_ip_list *all_ips; struct ctdb_ipflags *ipflags; ctdb_test_init(nodestates, &ctdb, &all_ips, &ipflags, read_ips_for_multiple_nodes); ctdb_takeover_run_core(ctdb, ipflags, &all_ips, NULL); print_ctdb_public_ip_list(all_ips); talloc_free(ctdb); } static void usage(void) { fprintf(stderr, "usage: ctdb_takeover_tests \n"); exit(1); } int main(int argc, const char *argv[]) { DEBUGLEVEL = DEBUG_DEBUG; if (getenv("CTDB_TEST_LOGLEVEL")) { DEBUGLEVEL = 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 (argc == 3 && strcmp(argv[1], "ip_info") == 0) { ctdb_test_read_ctdb_public_ip_info(argv[2]); } 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], false); } else if (argc == 4 && strcmp(argv[1], "ctdb_takeover_run_core") == 0 && strcmp(argv[3], "multi") == 0) { ctdb_test_ctdb_takeover_run_core(argv[2], true); } else { usage(); } return 0; }