ctdb-util: Rename db_wrap to tdb_wrap and make it a build subsystem
[Samba.git] / ctdb / tests / src / ctdb_takeover_tests.c
blob04b49e879b2da7d2e2978efcfa84a0a97468adbf
1 /*
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 ctdb_public_ip_list *
30 read_ctdb_public_ip_list(TALLOC_CTX *ctx)
32 char line[1024];
33 ctdb_sock_addr addr;
34 char *t;
35 int pnn;
36 struct ctdb_public_ip_list *last = NULL;
38 struct ctdb_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 */
44 *t = '\0';
45 /* Point to PNN or leading whitespace... */
46 t++;
47 pnn = (int) strtol(t, (char **) NULL, 10);
48 } else {
49 /* Assume just an IP address, default to PNN -1 */
50 if ((t = strchr(line, '\n')) != NULL) {
51 *t = '\0';
53 pnn = -1;
56 if (parse_ip(line, NULL, 0, &addr)) {
57 if (last == NULL) {
58 last = talloc(ctx, struct ctdb_public_ip_list);
59 } else {
60 last->next = talloc(ctx, struct ctdb_public_ip_list);
61 last = last->next;
63 last->next = NULL;
64 last->pnn = pnn;
65 memcpy(&(last->addr), &addr, sizeof(addr));
66 if (ret == NULL) {
67 ret = last;
69 } else {
70 DEBUG(DEBUG_ERR, (__location__ " ERROR, bad address :%s\n", line));
74 return ret;
77 static void print_ctdb_public_ip_list(struct ctdb_public_ip_list * ips)
79 while (ips) {
80 printf("%s %d\n", ctdb_addr_to_str(&(ips->addr)), ips->pnn);
81 ips = ips->next;
85 /* Read some IPs from stdin, 1 per line, parse them and then print
86 * them back out. */
87 static void ctdb_test_read_ctdb_public_ip_list(void)
89 struct ctdb_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);
97 talloc_free(tmp_ctx);
100 /* Format of each line is "IP CURRENT_PNN ALLOWED_PNN,...".
102 static bool
103 read_ctdb_public_ip_info(TALLOC_CTX *ctx,
104 int numnodes,
105 struct ctdb_public_ip_list ** all_ips,
106 struct ctdb_all_public_ips *** avail)
108 char line[1024];
109 ctdb_sock_addr addr;
110 char *t, *tok;
111 struct ctdb_public_ip_list * ta;
112 int pnn, numips, curr, n, i;
113 struct ctdb_all_public_ips * a;
115 struct ctdb_public_ip_list *last = NULL;
117 *avail = talloc_array_size(ctx, sizeof(struct ctdb_all_public_ips *), CTDB_TEST_MAX_NODES);
118 memset(*avail, 0,
119 sizeof(struct ctdb_all_public_ips *) * CTDB_TEST_MAX_NODES);
121 numips = 0;
122 *all_ips = NULL;
123 while (fgets(line, sizeof(line), stdin) != NULL) {
125 /* Get rid of pesky newline */
126 if ((t = strchr(line, '\n')) != NULL) {
127 *t = '\0';
130 /* Exit on an empty line */
131 if (line[0] == '\0') {
132 break;
135 /* Get the IP address */
136 tok = strtok(line, " \t");
137 if (tok == NULL) {
138 DEBUG(DEBUG_ERR, (__location__ " WARNING, bad line ignored :%s\n", line));
139 continue;
142 if (!parse_ip(tok, NULL, 0, &addr)) {
143 DEBUG(DEBUG_ERR, (__location__ " ERROR, bad address :%s\n", tok));
144 continue;
147 numips++;
148 if (numips > CTDB_TEST_MAX_IPS) {
149 DEBUG(DEBUG_ERR, ("ERROR: Exceeding CTDB_TEST_MAX_IPS: %d\n", CTDB_TEST_MAX_IPS));
150 exit(1);
153 /* Get the PNN */
154 pnn = -1;
155 tok = strtok(NULL, " \t");
156 if (tok != NULL) {
157 pnn = (int) strtol(tok, (char **) NULL, 10);
160 /* Add address + pnn to all_ips */
161 if (last == NULL) {
162 last = talloc(ctx, struct ctdb_public_ip_list);
163 } else {
164 last->next = talloc(ctx, struct ctdb_public_ip_list);
165 last = last->next;
167 last->next = NULL;
168 last->pnn = pnn;
169 memcpy(&(last->addr), &addr, sizeof(addr));
170 if (*all_ips == NULL) {
171 *all_ips = last;
174 tok = strtok(NULL, " \t#");
175 if (tok == NULL) {
176 continue;
179 /* Handle allowed nodes for addr */
180 t = strtok(tok, ",");
181 while (t != NULL) {
182 n = (int) strtol(t, (char **) NULL, 10);
183 if ((*avail)[n] == NULL) {
184 (*avail)[n] = talloc_array(ctx, struct ctdb_all_public_ips, CTDB_TEST_MAX_IPS);
185 (*avail)[n]->num = 0;
187 curr = (*avail)[n]->num;
188 (*avail)[n]->ips[curr].pnn = pnn;
189 memcpy(&((*avail)[n]->ips[curr].addr),
190 &addr, sizeof(addr));
191 (*avail)[n]->num++;
192 t = strtok(NULL, ",");
197 /* Build list of all allowed IPs */
198 a = talloc_array(ctx, struct ctdb_all_public_ips, CTDB_TEST_MAX_IPS);
199 a->num = numips;
200 for (ta = *all_ips, i=0; ta != NULL && i < numips ; ta = ta->next, i++) {
201 a->ips[i].pnn = ta->pnn;
202 memcpy(&(a->ips[i].addr), &(ta->addr), sizeof(ta->addr));
205 /* Assign it to any nodes that don't have a list assigned */
206 for (n = 0; n < numnodes; n++) {
207 if ((*avail)[n] == NULL) {
208 (*avail)[n] = a;
212 return true;
215 static void print_ctdb_available_ips(int numnodes,
216 struct ctdb_all_public_ips **avail)
218 int n, i;
220 for (n = 0; n < numnodes; n++) {
221 if ((avail[n] != NULL) && (avail[n]->num > 0)) {
222 printf("%d:", n);
223 for (i = 0; i < avail[n]->num; i++) {
224 printf("%s%s",
225 (i == 0) ? " " : ", ",
226 ctdb_addr_to_str(&(avail[n]->ips[i].addr)));
228 printf("\n");
233 static void ctdb_test_read_ctdb_public_ip_info(const char nodestates[])
235 int numnodes;
236 struct ctdb_public_ip_list *l;
237 struct ctdb_all_public_ips **avail;
238 char *tok, *ns;
240 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
242 /* Avoid that const */
243 ns = talloc_strdup(tmp_ctx, nodestates);
245 numnodes = 0;
246 tok = strtok(ns, ",");
247 while (tok != NULL) {
248 numnodes++;
249 if (numnodes > CTDB_TEST_MAX_NODES) {
250 DEBUG(DEBUG_ERR, ("ERROR: Exceeding CTDB_TEST_MAX_NODES: %d\n", CTDB_TEST_MAX_NODES));
251 exit(1);
253 tok = strtok(NULL, ",");
256 read_ctdb_public_ip_info(tmp_ctx, numnodes, &l, &avail);
258 print_ctdb_public_ip_list(l);
259 print_ctdb_available_ips(numnodes, avail);
261 talloc_free(tmp_ctx);
264 /* Read 2 IPs from stdin, calculate the IP distance and print it. */
265 static void ctdb_test_ip_distance(void)
267 struct ctdb_public_ip_list *l;
268 uint32_t distance;
270 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
272 l = read_ctdb_public_ip_list(tmp_ctx);
274 if (l && l->next) {
275 distance = ip_distance(&(l->addr), &(l->next->addr));
276 printf ("%lu\n", (unsigned long) distance);
279 talloc_free(tmp_ctx);
282 /* Read some IPs from stdin, calculate the sum of the squares of the
283 * IP distances between the 1st argument and those read that are on
284 * the given node. The given IP must one of the ones in the list. */
285 static void ctdb_test_ip_distance_2_sum(const char ip[], int pnn)
287 struct ctdb_public_ip_list *l;
288 struct ctdb_public_ip_list *t;
289 ctdb_sock_addr addr;
290 uint32_t distance;
292 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
295 l = read_ctdb_public_ip_list(tmp_ctx);
297 if (l && parse_ip(ip, NULL, 0, &addr)) {
298 /* find the entry for the specified IP */
299 for (t=l; t!=NULL; t=t->next) {
300 if (ctdb_same_ip(&(t->addr), &addr)) {
301 break;
305 if (t == NULL) {
306 fprintf(stderr, "IP NOT PRESENT IN LIST");
307 exit(1);
310 distance = ip_distance_2_sum(&(t->addr), l, pnn);
311 printf ("%lu\n", (unsigned long) distance);
312 } else {
313 fprintf(stderr, "BAD INPUT");
314 exit(1);
317 talloc_free(tmp_ctx);
320 /* Read some IPs from stdin, calculate the sume of the squares of the
321 * IP distances between the first and the rest, and print it. */
322 static void ctdb_test_lcp2_imbalance(int pnn)
324 struct ctdb_public_ip_list *l;
325 uint32_t imbalance;
327 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
329 l = read_ctdb_public_ip_list(tmp_ctx);
331 imbalance = lcp2_imbalance(l, pnn);
332 printf ("%lu\n", (unsigned long) imbalance);
334 talloc_free(tmp_ctx);
337 static uint32_t *get_tunable_values(TALLOC_CTX *tmp_ctx,
338 int numnodes,
339 const char *tunable)
341 int i;
342 char *tok;
343 uint32_t *tvals = talloc_zero_array(tmp_ctx, uint32_t, numnodes);
344 char *t = getenv(tunable);
346 if (t) {
347 if (strcmp(t, "1") == 0) {
348 for (i=0; i<numnodes; i++) {
349 tvals[i] = 1;
351 } else {
352 tok = strtok(t, ",");
353 i = 0;
354 while (tok != NULL) {
355 tvals[i] =
356 (uint32_t) strtol(tok, NULL, 0);
357 i++;
358 tok = strtok(NULL, ",");
360 if (i != numnodes) {
361 fprintf(stderr, "ERROR: Wrong number of values in %s\n", tunable);
362 exit(1);
367 return tvals;
370 static enum ctdb_runstate *get_runstate(TALLOC_CTX *tmp_ctx,
371 int numnodes)
373 int i;
374 uint32_t *tvals;
375 enum ctdb_runstate *runstate =
376 talloc_zero_array(tmp_ctx, enum ctdb_runstate, numnodes);
377 char *t = getenv("CTDB_TEST_RUNSTATE");
379 if (t == NULL) {
380 for (i=0; i<numnodes; i++) {
381 runstate[i] = CTDB_RUNSTATE_RUNNING;
383 } else {
384 tvals = get_tunable_values(tmp_ctx, numnodes, "CTDB_TEST_RUNSTATE");
385 for (i=0; i<numnodes; i++) {
386 runstate[i] = (enum ctdb_runstate) tvals[i];
388 talloc_free(tvals);
391 return runstate;
394 /* Fake up enough CTDB state to be able to run the IP allocation
395 * algorithm. Usually this sets up some standard state, sets the node
396 * states from the command-line and reads the current IP layout from
397 * stdin.
399 * However, if read_ips_for_multiple_nodes is true then each node's
400 * idea of the IP layout is read separately from stdin. In this mode
401 * is doesn't make much sense to use read_ctdb_public_ip_info's
402 * optional ALLOWED_PNN,... list in the input, since each node is
403 * being handled separately anyway. IPs for each node are separated
404 * by a blank line. This mode is for testing weird behaviours where
405 * the IP layouts differs across nodes and we want to improve
406 * create_merged_ip_list(), so should only be used in tests of
407 * ctdb_takeover_run_core(). Yes, it is a hack... :-)
409 static void ctdb_test_init(const char nodestates[],
410 struct ctdb_context **ctdb,
411 struct ctdb_public_ip_list **all_ips,
412 struct ctdb_ipflags **ipflags,
413 bool read_ips_for_multiple_nodes)
415 struct ctdb_all_public_ips **avail;
416 int i, numnodes;
417 uint32_t nodeflags[CTDB_TEST_MAX_NODES];
418 char *tok, *ns, *t;
419 struct ctdb_node_map *nodemap;
420 uint32_t *tval_noiptakeover;
421 uint32_t *tval_noiptakeoverondisabled;
422 enum ctdb_runstate *runstate;
424 *ctdb = talloc_zero(NULL, struct ctdb_context);
426 /* Avoid that const */
427 ns = talloc_strdup(*ctdb, nodestates);
429 numnodes = 0;
430 tok = strtok(ns, ",");
431 while (tok != NULL) {
432 nodeflags[numnodes] = (uint32_t) strtol(tok, NULL, 0);
433 numnodes++;
434 if (numnodes > CTDB_TEST_MAX_NODES) {
435 DEBUG(DEBUG_ERR, ("ERROR: Exceeding CTDB_TEST_MAX_NODES: %d\n", CTDB_TEST_MAX_NODES));
436 exit(1);
438 tok = strtok(NULL, ",");
441 /* Fake things up... */
442 (*ctdb)->num_nodes = numnodes;
444 /* Default to LCP2 */
445 (*ctdb)->tunable.lcp2_public_ip_assignment = 1;
446 (*ctdb)->tunable.deterministic_public_ips = 0;
447 (*ctdb)->tunable.disable_ip_failover = 0;
448 (*ctdb)->tunable.no_ip_failback = 0;
450 if ((t = getenv("CTDB_IP_ALGORITHM"))) {
451 if (strcmp(t, "lcp2") == 0) {
452 (*ctdb)->tunable.lcp2_public_ip_assignment = 1;
453 } else if (strcmp(t, "nondet") == 0) {
454 (*ctdb)->tunable.lcp2_public_ip_assignment = 0;
455 } else if (strcmp(t, "det") == 0) {
456 (*ctdb)->tunable.lcp2_public_ip_assignment = 0;
457 (*ctdb)->tunable.deterministic_public_ips = 1;
458 } else {
459 fprintf(stderr, "ERROR: unknown IP algorithm %s\n", t);
460 exit(1);
464 tval_noiptakeover = get_tunable_values(*ctdb, numnodes,
465 "CTDB_SET_NoIPTakeover");
466 tval_noiptakeoverondisabled =
467 get_tunable_values(*ctdb, numnodes,
468 "CTDB_SET_NoIPHostOnAllDisabled");
470 runstate = get_runstate(*ctdb, numnodes);
472 nodemap = talloc_array(*ctdb, struct ctdb_node_map, numnodes);
473 nodemap->num = numnodes;
475 if (!read_ips_for_multiple_nodes) {
476 read_ctdb_public_ip_info(*ctdb, numnodes, all_ips, &avail);
479 (*ctdb)->nodes = talloc_array(*ctdb, struct ctdb_node *, numnodes); // FIXME: bogus size, overkill
481 for (i=0; i < numnodes; i++) {
482 nodemap->nodes[i].pnn = i;
483 nodemap->nodes[i].flags = nodeflags[i];
484 /* nodemap->nodes[i].sockaddr is uninitialised */
486 if (read_ips_for_multiple_nodes) {
487 read_ctdb_public_ip_info(*ctdb, numnodes,
488 all_ips, &avail);
491 (*ctdb)->nodes[i] = talloc(*ctdb, struct ctdb_node);
492 (*ctdb)->nodes[i]->pnn = i;
493 (*ctdb)->nodes[i]->flags = nodeflags[i];
494 (*ctdb)->nodes[i]->available_public_ips = avail[i];
495 (*ctdb)->nodes[i]->known_public_ips = avail[i];
498 *ipflags = set_ipflags_internal(*ctdb, *ctdb, nodemap,
499 tval_noiptakeover,
500 tval_noiptakeoverondisabled,
501 runstate);
504 /* IP layout is read from stdin. */
505 static void ctdb_test_lcp2_allocate_unassigned(const char nodestates[])
507 struct ctdb_context *ctdb;
508 struct ctdb_public_ip_list *all_ips;
509 struct ctdb_ipflags *ipflags;
511 uint32_t *lcp2_imbalances;
512 bool *newly_healthy;
514 ctdb_test_init(nodestates, &ctdb, &all_ips, &ipflags, false);
516 lcp2_init(ctdb, ipflags, all_ips, NULL,
517 &lcp2_imbalances, &newly_healthy);
519 lcp2_allocate_unassigned(ctdb, ipflags,
520 all_ips, lcp2_imbalances);
522 print_ctdb_public_ip_list(all_ips);
524 talloc_free(ctdb);
527 /* IP layout is read from stdin. */
528 static void ctdb_test_lcp2_failback(const char nodestates[])
530 struct ctdb_context *ctdb;
531 struct ctdb_public_ip_list *all_ips;
532 struct ctdb_ipflags *ipflags;
534 uint32_t *lcp2_imbalances;
535 bool *newly_healthy;
537 ctdb_test_init(nodestates, &ctdb, &all_ips, &ipflags, false);
539 lcp2_init(ctdb, ipflags, all_ips, NULL,
540 &lcp2_imbalances, &newly_healthy);
542 lcp2_failback(ctdb, ipflags,
543 all_ips, lcp2_imbalances, newly_healthy);
545 print_ctdb_public_ip_list(all_ips);
547 talloc_free(ctdb);
550 /* IP layout is read from stdin. */
551 static void ctdb_test_lcp2_failback_loop(const char nodestates[])
553 struct ctdb_context *ctdb;
554 struct ctdb_public_ip_list *all_ips;
555 struct ctdb_ipflags *ipflags;
557 uint32_t *lcp2_imbalances;
558 bool *newly_healthy;
560 ctdb_test_init(nodestates, &ctdb, &all_ips, &ipflags, false);
562 lcp2_init(ctdb, ipflags, all_ips, NULL,
563 &lcp2_imbalances, &newly_healthy);
565 lcp2_failback(ctdb, ipflags,
566 all_ips, lcp2_imbalances, newly_healthy);
568 print_ctdb_public_ip_list(all_ips);
570 talloc_free(ctdb);
573 /* IP layout is read from stdin. See comment for ctdb_test_init() for
574 * explanation of read_ips_for_multiple_nodes.
576 static void ctdb_test_ctdb_takeover_run_core(const char nodestates[],
577 bool read_ips_for_multiple_nodes)
579 struct ctdb_context *ctdb;
580 struct ctdb_public_ip_list *all_ips;
581 struct ctdb_ipflags *ipflags;
583 ctdb_test_init(nodestates, &ctdb, &all_ips, &ipflags,
584 read_ips_for_multiple_nodes);
586 ctdb_takeover_run_core(ctdb, ipflags, &all_ips, NULL);
588 print_ctdb_public_ip_list(all_ips);
590 talloc_free(ctdb);
593 static void usage(void)
595 fprintf(stderr, "usage: ctdb_takeover_tests <op>\n");
596 exit(1);
599 int main(int argc, const char *argv[])
601 LogLevel = DEBUG_DEBUG;
602 if (getenv("CTDB_TEST_LOGLEVEL")) {
603 LogLevel = atoi(getenv("CTDB_TEST_LOGLEVEL"));
606 if (argc < 2) {
607 usage();
610 if (strcmp(argv[1], "ip_list") == 0) {
611 ctdb_test_read_ctdb_public_ip_list();
612 } else if (argc == 3 && strcmp(argv[1], "ip_info") == 0) {
613 ctdb_test_read_ctdb_public_ip_info(argv[2]);
614 } else if (strcmp(argv[1], "ip_distance") == 0) {
615 ctdb_test_ip_distance();
616 } else if (argc == 4 && strcmp(argv[1], "ip_distance_2_sum") == 0) {
617 ctdb_test_ip_distance_2_sum(argv[2], atoi(argv[3]));
618 } else if (argc >= 3 && strcmp(argv[1], "lcp2_imbalance") == 0) {
619 ctdb_test_lcp2_imbalance(atoi(argv[2]));
620 } else if (argc == 3 && strcmp(argv[1], "lcp2_allocate_unassigned") == 0) {
621 ctdb_test_lcp2_allocate_unassigned(argv[2]);
622 } else if (argc == 3 && strcmp(argv[1], "lcp2_failback") == 0) {
623 ctdb_test_lcp2_failback(argv[2]);
624 } else if (argc == 3 && strcmp(argv[1], "lcp2_failback_loop") == 0) {
625 ctdb_test_lcp2_failback_loop(argv[2]);
626 } else if (argc == 3 &&
627 strcmp(argv[1], "ctdb_takeover_run_core") == 0) {
628 ctdb_test_ctdb_takeover_run_core(argv[2], false);
629 } else if (argc == 4 &&
630 strcmp(argv[1], "ctdb_takeover_run_core") == 0 &&
631 strcmp(argv[3], "multi") == 0) {
632 ctdb_test_ctdb_takeover_run_core(argv[2], true);
633 } else {
634 usage();
637 return 0;