2 Tests for ctdb_takeover.c
4 Copyright (C) Martin Schwenke 2011
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>.
20 #include "ctdbd_test.c"
22 /* This is lazy... but it is test code! */
23 #define CTDB_TEST_MAX_NODES 256
24 #define CTDB_TEST_MAX_IPS 1024
26 /* Format of each line is "IP pnn" - the separator has to be at least
27 * 1 space (not a tab or whatever - a space!).
29 static struct public_ip_list *
30 read_ctdb_public_ip_list(TALLOC_CTX *ctx)
36 struct public_ip_list *last = NULL;
38 struct public_ip_list *ret = NULL;
40 while (fgets(line, sizeof(line), stdin) != NULL) {
42 if ((t = strchr(line, ' ')) != NULL) {
43 /* Make line contain just the address */
45 /* Point to PNN or leading whitespace... */
47 pnn = (int) strtol(t, (char **) NULL, 10);
49 /* Assume just an IP address, default to PNN -1 */
50 if ((t = strchr(line, '\n')) != NULL) {
56 if (parse_ip(line, NULL, 0, &addr)) {
58 last = talloc(ctx, struct public_ip_list);
60 last->next = talloc(ctx, struct public_ip_list);
65 memcpy(&(last->addr), &addr, sizeof(addr));
70 DEBUG(DEBUG_ERR, (__location__ " ERROR, bad address :%s\n", line));
77 static void print_ctdb_public_ip_list(struct public_ip_list * ips)
80 printf("%s %d\n", ctdb_addr_to_str(&(ips->addr)), ips->pnn);
85 /* Read some IPs from stdin, 1 per line, parse them and then print
87 static void ctdb_test_read_ctdb_public_ip_list(void)
89 struct public_ip_list *l;
91 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
93 l = read_ctdb_public_ip_list(tmp_ctx);
95 print_ctdb_public_ip_list(l);
100 static uint32_t *get_tunable_values(TALLOC_CTX *tmp_ctx,
102 const char *tunable);
103 static enum ctdb_runstate *get_runstate(TALLOC_CTX *tmp_ctx,
106 /* Format of each line is "IP CURRENT_PNN ALLOWED_PNN,...".
109 read_ctdb_public_ip_info(TALLOC_CTX *ctx ,
111 struct public_ip_list ** all_ips,
112 struct ctdb_public_ip_list_old *** known,
113 struct ctdb_public_ip_list_old *** avail)
118 struct public_ip_list * ta;
119 int pnn, numips, curr, n, i;
120 struct ctdb_public_ip_list_old * a;
122 struct public_ip_list *last = NULL;
123 enum ctdb_runstate *runstate;
125 runstate = get_runstate(ctx, numnodes);
127 *known = talloc_array_size(ctx, sizeof(struct ctdb_public_ip_list_old *), CTDB_TEST_MAX_NODES);
129 sizeof(struct ctdb_public_ip_list_old *) * CTDB_TEST_MAX_NODES);
131 *avail = talloc_array_size(ctx, sizeof(struct ctdb_public_ip_list_old *),
132 CTDB_TEST_MAX_NODES);
134 sizeof(struct ctdb_public_ip_list_old *) * CTDB_TEST_MAX_NODES);
138 while (fgets(line, sizeof(line), stdin) != NULL) {
140 /* Get rid of pesky newline */
141 if ((t = strchr(line, '\n')) != NULL) {
145 /* Exit on an empty line */
146 if (line[0] == '\0') {
150 /* Get the IP address */
151 tok = strtok(line, " \t");
153 DEBUG(DEBUG_ERR, (__location__ " WARNING, bad line ignored :%s\n", line));
157 if (!parse_ip(tok, NULL, 0, &addr)) {
158 DEBUG(DEBUG_ERR, (__location__ " ERROR, bad address :%s\n", tok));
163 if (numips > CTDB_TEST_MAX_IPS) {
164 DEBUG(DEBUG_ERR, ("ERROR: Exceeding CTDB_TEST_MAX_IPS: %d\n", CTDB_TEST_MAX_IPS));
170 tok = strtok(NULL, " \t");
172 pnn = (int) strtol(tok, (char **) NULL, 10);
175 /* Add address + pnn to all_ips */
177 last = talloc(ctx, struct public_ip_list);
179 last->next = talloc(ctx, struct public_ip_list);
184 memcpy(&(last->addr), &addr, sizeof(addr));
185 if (*all_ips == NULL) {
189 tok = strtok(NULL, " \t#");
194 /* Handle allowed nodes for addr */
195 t = strtok(tok, ",");
197 n = (int) strtol(t, (char **) NULL, 10);
198 if ((*known)[n] == NULL) {
199 (*known)[n] = talloc_array(ctx, struct ctdb_public_ip_list_old, CTDB_TEST_MAX_IPS);
200 (*known)[n]->num = 0;
202 curr = (*known)[n]->num;
203 (*known)[n]->ips[curr].pnn = pnn;
204 memcpy(&((*known)[n]->ips[curr].addr),
205 &addr, sizeof(addr));
207 t = strtok(NULL, ",");
212 /* Build list of all allowed IPs */
213 a = talloc_array(ctx, struct ctdb_public_ip_list_old, CTDB_TEST_MAX_IPS);
215 for (ta = *all_ips, i=0; ta != NULL && i < numips ; ta = ta->next, i++) {
216 a->ips[i].pnn = ta->pnn;
217 memcpy(&(a->ips[i].addr), &(ta->addr), sizeof(ta->addr));
220 /* Assign it to any nodes that don't have a list assigned */
221 for (n = 0; n < numnodes; n++) {
222 if ((*known)[n] == NULL) {
225 if (runstate[n] == CTDB_RUNSTATE_RUNNING) {
226 (*avail)[n] = (*known)[n];
233 static void print_ctdb_available_ips(int numnodes,
234 struct ctdb_public_ip_list_old **avail)
238 for (n = 0; n < numnodes; n++) {
239 if ((avail[n] != NULL) && (avail[n]->num > 0)) {
241 for (i = 0; i < avail[n]->num; i++) {
243 (i == 0) ? " " : ", ",
244 ctdb_addr_to_str(&(avail[n]->ips[i].addr)));
251 static void ctdb_test_read_ctdb_public_ip_info(const char nodestates[])
254 struct public_ip_list *l;
255 struct ctdb_public_ip_list_old **known;
256 struct ctdb_public_ip_list_old **avail;
259 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
261 /* Avoid that const */
262 ns = talloc_strdup(tmp_ctx, nodestates);
265 tok = strtok(ns, ",");
266 while (tok != NULL) {
268 if (numnodes > CTDB_TEST_MAX_NODES) {
269 DEBUG(DEBUG_ERR, ("ERROR: Exceeding CTDB_TEST_MAX_NODES: %d\n", CTDB_TEST_MAX_NODES));
272 tok = strtok(NULL, ",");
275 read_ctdb_public_ip_info(tmp_ctx, numnodes, &l, &known, &avail);
277 print_ctdb_public_ip_list(l);
278 print_ctdb_available_ips(numnodes, avail);
280 talloc_free(tmp_ctx);
283 /* Read 2 IPs from stdin, calculate the IP distance and print it. */
284 static void ctdb_test_ip_distance(void)
286 struct public_ip_list *l;
289 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
291 l = read_ctdb_public_ip_list(tmp_ctx);
294 distance = ip_distance(&(l->addr), &(l->next->addr));
295 printf ("%lu\n", (unsigned long) distance);
298 talloc_free(tmp_ctx);
301 /* Read some IPs from stdin, calculate the sum of the squares of the
302 * IP distances between the 1st argument and those read that are on
303 * the given node. The given IP must one of the ones in the list. */
304 static void ctdb_test_ip_distance_2_sum(const char ip[], int pnn)
306 struct public_ip_list *l;
307 struct public_ip_list *t;
311 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
314 l = read_ctdb_public_ip_list(tmp_ctx);
316 if (l && parse_ip(ip, NULL, 0, &addr)) {
317 /* find the entry for the specified IP */
318 for (t=l; t!=NULL; t=t->next) {
319 if (ctdb_same_ip(&(t->addr), &addr)) {
325 fprintf(stderr, "IP NOT PRESENT IN LIST");
329 distance = ip_distance_2_sum(&(t->addr), l, pnn);
330 printf ("%lu\n", (unsigned long) distance);
332 fprintf(stderr, "BAD INPUT");
336 talloc_free(tmp_ctx);
339 /* Read some IPs from stdin, calculate the sume of the squares of the
340 * IP distances between the first and the rest, and print it. */
341 static void ctdb_test_lcp2_imbalance(int pnn)
343 struct public_ip_list *l;
346 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
348 l = read_ctdb_public_ip_list(tmp_ctx);
350 imbalance = lcp2_imbalance(l, pnn);
351 printf ("%lu\n", (unsigned long) imbalance);
353 talloc_free(tmp_ctx);
356 static uint32_t *get_tunable_values(TALLOC_CTX *tmp_ctx,
362 uint32_t *tvals = talloc_zero_array(tmp_ctx, uint32_t, numnodes);
363 char *t = getenv(tunable);
366 if (strcmp(t, "1") == 0) {
367 for (i=0; i<numnodes; i++) {
371 tok = strtok(t, ",");
373 while (tok != NULL) {
375 (uint32_t) strtol(tok, NULL, 0);
377 tok = strtok(NULL, ",");
380 fprintf(stderr, "ERROR: Wrong number of values in %s\n", tunable);
389 static enum ctdb_runstate *get_runstate(TALLOC_CTX *tmp_ctx,
394 enum ctdb_runstate *runstate =
395 talloc_zero_array(tmp_ctx, enum ctdb_runstate, numnodes);
396 char *t = getenv("CTDB_TEST_RUNSTATE");
399 for (i=0; i<numnodes; i++) {
400 runstate[i] = CTDB_RUNSTATE_RUNNING;
403 tvals = get_tunable_values(tmp_ctx, numnodes, "CTDB_TEST_RUNSTATE");
404 for (i=0; i<numnodes; i++) {
405 runstate[i] = (enum ctdb_runstate) tvals[i];
413 /* Fake up enough CTDB state to be able to run the IP allocation
414 * algorithm. Usually this sets up some standard state, sets the node
415 * states from the command-line and reads the current IP layout from
418 * However, if read_ips_for_multiple_nodes is true then each node's
419 * idea of the IP layout is read separately from stdin. In this mode
420 * is doesn't make much sense to use read_ctdb_public_ip_info's
421 * optional ALLOWED_PNN,... list in the input, since each node is
422 * being handled separately anyway. IPs for each node are separated
423 * by a blank line. This mode is for testing weird behaviours where
424 * the IP layouts differs across nodes and we want to improve
425 * create_merged_ip_list(), so should only be used in tests of
426 * ctdb_takeover_run_core(). Yes, it is a hack... :-)
428 static void ctdb_test_init(const char nodestates[],
429 struct ctdb_context **ctdb,
430 struct public_ip_list **all_ips,
431 struct ipalloc_state **ipalloc_state,
432 bool read_ips_for_multiple_nodes)
434 struct ctdb_public_ip_list_old **known;
435 struct ctdb_public_ip_list_old **avail;
437 uint32_t nodeflags[CTDB_TEST_MAX_NODES];
439 struct ctdb_node_map_old *nodemap;
440 uint32_t *tval_noiptakeover;
441 uint32_t *tval_noiptakeoverondisabled;
443 *ctdb = talloc_zero(NULL, struct ctdb_context);
445 /* Avoid that const */
446 ns = talloc_strdup(*ctdb, nodestates);
449 tok = strtok(ns, ",");
450 while (tok != NULL) {
451 nodeflags[numnodes] = (uint32_t) strtol(tok, NULL, 0);
453 if (numnodes >= CTDB_TEST_MAX_NODES) {
454 DEBUG(DEBUG_ERR, ("ERROR: Exceeding CTDB_TEST_MAX_NODES: %d\n", CTDB_TEST_MAX_NODES));
457 tok = strtok(NULL, ",");
460 /* Fake things up... */
461 (*ctdb)->num_nodes = numnodes;
463 /* Default to LCP2 */
464 (*ctdb)->tunable.lcp2_public_ip_assignment = 1;
465 (*ctdb)->tunable.deterministic_public_ips = 0;
466 (*ctdb)->tunable.disable_ip_failover = 0;
467 (*ctdb)->tunable.no_ip_failback = 0;
469 if ((t = getenv("CTDB_IP_ALGORITHM"))) {
470 if (strcmp(t, "lcp2") == 0) {
471 (*ctdb)->tunable.lcp2_public_ip_assignment = 1;
472 } else if (strcmp(t, "nondet") == 0) {
473 (*ctdb)->tunable.lcp2_public_ip_assignment = 0;
474 } else if (strcmp(t, "det") == 0) {
475 (*ctdb)->tunable.lcp2_public_ip_assignment = 0;
476 (*ctdb)->tunable.deterministic_public_ips = 1;
478 fprintf(stderr, "ERROR: unknown IP algorithm %s\n", t);
483 tval_noiptakeover = get_tunable_values(*ctdb, numnodes,
484 "CTDB_SET_NoIPTakeover");
485 tval_noiptakeoverondisabled =
486 get_tunable_values(*ctdb, numnodes,
487 "CTDB_SET_NoIPHostOnAllDisabled");
489 nodemap = talloc_array(*ctdb, struct ctdb_node_map_old, numnodes);
490 nodemap->num = numnodes;
492 if (!read_ips_for_multiple_nodes) {
493 read_ctdb_public_ip_info(*ctdb, numnodes, all_ips,
497 (*ctdb)->nodes = talloc_array(*ctdb, struct ctdb_node *, numnodes); // FIXME: bogus size, overkill
499 *ipalloc_state = ipalloc_state_init(*ctdb, *ctdb);
501 for (i=0; i < numnodes; i++) {
502 nodemap->nodes[i].pnn = i;
503 nodemap->nodes[i].flags = nodeflags[i];
504 /* nodemap->nodes[i].sockaddr is uninitialised */
506 if (read_ips_for_multiple_nodes) {
507 read_ctdb_public_ip_info(*ctdb, numnodes,
508 all_ips, &known, &avail);
511 (*ctdb)->nodes[i] = talloc(*ctdb, struct ctdb_node);
512 (*ctdb)->nodes[i]->pnn = i;
513 (*ctdb)->nodes[i]->flags = nodeflags[i];
515 (*ipalloc_state)->available_public_ips[i] = avail[i];
516 (*ipalloc_state)->known_public_ips[i] = known[i];
519 (*ipalloc_state)->ipflags =
520 set_ipflags_internal(*ipalloc_state, nodemap,
522 tval_noiptakeoverondisabled);
525 /* IP layout is read from stdin. */
526 static void ctdb_test_lcp2_allocate_unassigned(const char nodestates[])
528 struct ctdb_context *ctdb;
529 struct public_ip_list *all_ips;
530 struct ipalloc_state *ipalloc_state;
532 uint32_t *lcp2_imbalances;
535 ctdb_test_init(nodestates, &ctdb, &all_ips, &ipalloc_state,
538 lcp2_init(ipalloc_state, all_ips, NULL,
539 &lcp2_imbalances, &newly_healthy);
541 lcp2_allocate_unassigned(ipalloc_state,
542 all_ips, lcp2_imbalances);
544 print_ctdb_public_ip_list(all_ips);
549 /* IP layout is read from stdin. */
550 static void ctdb_test_lcp2_failback(const char nodestates[])
552 struct ctdb_context *ctdb;
553 struct public_ip_list *all_ips;
554 struct ipalloc_state *ipalloc_state;
556 uint32_t *lcp2_imbalances;
559 ctdb_test_init(nodestates, &ctdb, &all_ips, &ipalloc_state,
562 lcp2_init(ipalloc_state, all_ips, NULL,
563 &lcp2_imbalances, &newly_healthy);
565 lcp2_failback(ipalloc_state,
566 all_ips, lcp2_imbalances, newly_healthy);
568 print_ctdb_public_ip_list(all_ips);
573 /* IP layout is read from stdin. */
574 static void ctdb_test_lcp2_failback_loop(const char nodestates[])
576 struct ctdb_context *ctdb;
577 struct public_ip_list *all_ips;
578 struct ipalloc_state *ipalloc_state;
580 uint32_t *lcp2_imbalances;
583 ctdb_test_init(nodestates, &ctdb, &all_ips, &ipalloc_state,
586 lcp2_init(ipalloc_state, all_ips, NULL,
587 &lcp2_imbalances, &newly_healthy);
589 lcp2_failback(ipalloc_state,
590 all_ips, lcp2_imbalances, newly_healthy);
592 print_ctdb_public_ip_list(all_ips);
597 /* IP layout is read from stdin. See comment for ctdb_test_init() for
598 * explanation of read_ips_for_multiple_nodes.
600 static void ctdb_test_ctdb_takeover_run_core(const char nodestates[],
601 bool read_ips_for_multiple_nodes)
603 struct ctdb_context *ctdb;
604 struct public_ip_list *all_ips;
605 struct ipalloc_state *ipalloc_state;
607 ctdb_test_init(nodestates, &ctdb, &all_ips, &ipalloc_state,
608 read_ips_for_multiple_nodes);
610 all_ips = create_merged_ip_list(ctdb, ipalloc_state);
612 ctdb_takeover_run_core(ipalloc_state, all_ips, NULL);
614 print_ctdb_public_ip_list(all_ips);
619 static void usage(void)
621 fprintf(stderr, "usage: ctdb_takeover_tests <op>\n");
625 int main(int argc, const char *argv[])
627 DEBUGLEVEL = DEBUG_DEBUG;
628 if (getenv("CTDB_TEST_LOGLEVEL")) {
629 DEBUGLEVEL = atoi(getenv("CTDB_TEST_LOGLEVEL"));
636 if (strcmp(argv[1], "ip_list") == 0) {
637 ctdb_test_read_ctdb_public_ip_list();
638 } else if (argc == 3 && strcmp(argv[1], "ip_info") == 0) {
639 ctdb_test_read_ctdb_public_ip_info(argv[2]);
640 } else if (strcmp(argv[1], "ip_distance") == 0) {
641 ctdb_test_ip_distance();
642 } else if (argc == 4 && strcmp(argv[1], "ip_distance_2_sum") == 0) {
643 ctdb_test_ip_distance_2_sum(argv[2], atoi(argv[3]));
644 } else if (argc >= 3 && strcmp(argv[1], "lcp2_imbalance") == 0) {
645 ctdb_test_lcp2_imbalance(atoi(argv[2]));
646 } else if (argc == 3 && strcmp(argv[1], "lcp2_allocate_unassigned") == 0) {
647 ctdb_test_lcp2_allocate_unassigned(argv[2]);
648 } else if (argc == 3 && strcmp(argv[1], "lcp2_failback") == 0) {
649 ctdb_test_lcp2_failback(argv[2]);
650 } else if (argc == 3 && strcmp(argv[1], "lcp2_failback_loop") == 0) {
651 ctdb_test_lcp2_failback_loop(argv[2]);
652 } else if (argc == 3 &&
653 strcmp(argv[1], "ctdb_takeover_run_core") == 0) {
654 ctdb_test_ctdb_takeover_run_core(argv[2], false);
655 } else if (argc == 4 &&
656 strcmp(argv[1], "ctdb_takeover_run_core") == 0 &&
657 strcmp(argv[3], "multi") == 0) {
658 ctdb_test_ctdb_takeover_run_core(argv[2], true);