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
)
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 */
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 ctdb_public_ip_list
);
60 last
->next
= talloc(ctx
, struct ctdb_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 ctdb_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 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
);
100 /* Format of each line is "IP CURRENT_PNN ALLOWED_PNN,...".
103 read_ctdb_public_ip_info(TALLOC_CTX
*ctx
,
105 struct ctdb_public_ip_list
** all_ips
,
106 struct ctdb_all_public_ips
*** avail
)
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
);
119 sizeof(struct ctdb_all_public_ips
*) * CTDB_TEST_MAX_NODES
);
123 while (fgets(line
, sizeof(line
), stdin
) != NULL
) {
125 /* Get rid of pesky newline */
126 if ((t
= strchr(line
, '\n')) != NULL
) {
130 /* Exit on an empty line */
131 if (line
[0] == '\0') {
135 /* Get the IP address */
136 tok
= strtok(line
, " \t");
138 DEBUG(DEBUG_ERR
, (__location__
" WARNING, bad line ignored :%s\n", line
));
142 if (!parse_ip(tok
, NULL
, 0, &addr
)) {
143 DEBUG(DEBUG_ERR
, (__location__
" ERROR, bad address :%s\n", tok
));
148 if (numips
> CTDB_TEST_MAX_IPS
) {
149 DEBUG(DEBUG_ERR
, ("ERROR: Exceeding CTDB_TEST_MAX_IPS: %d\n", CTDB_TEST_MAX_IPS
));
155 tok
= strtok(NULL
, " \t");
157 pnn
= (int) strtol(tok
, (char **) NULL
, 10);
160 /* Add address + pnn to all_ips */
162 last
= talloc(ctx
, struct ctdb_public_ip_list
);
164 last
->next
= talloc(ctx
, struct ctdb_public_ip_list
);
169 memcpy(&(last
->addr
), &addr
, sizeof(addr
));
170 if (*all_ips
== NULL
) {
174 tok
= strtok(NULL
, " \t#");
179 /* Handle allowed nodes for addr */
180 t
= strtok(tok
, ",");
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
));
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
);
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
) {
215 static void print_ctdb_available_ips(int numnodes
,
216 struct ctdb_all_public_ips
**avail
)
220 for (n
= 0; n
< numnodes
; n
++) {
221 if ((avail
[n
] != NULL
) && (avail
[n
]->num
> 0)) {
223 for (i
= 0; i
< avail
[n
]->num
; i
++) {
225 (i
== 0) ? " " : ", ",
226 ctdb_addr_to_str(&(avail
[n
]->ips
[i
].addr
)));
233 static void ctdb_test_read_ctdb_public_ip_info(const char nodestates
[])
236 struct ctdb_public_ip_list
*l
;
237 struct ctdb_all_public_ips
**avail
;
240 TALLOC_CTX
*tmp_ctx
= talloc_new(NULL
);
242 /* Avoid that const */
243 ns
= talloc_strdup(tmp_ctx
, nodestates
);
246 tok
= strtok(ns
, ",");
247 while (tok
!= NULL
) {
249 if (numnodes
> CTDB_TEST_MAX_NODES
) {
250 DEBUG(DEBUG_ERR
, ("ERROR: Exceeding CTDB_TEST_MAX_NODES: %d\n", CTDB_TEST_MAX_NODES
));
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
;
270 TALLOC_CTX
*tmp_ctx
= talloc_new(NULL
);
272 l
= read_ctdb_public_ip_list(tmp_ctx
);
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
;
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
)) {
306 fprintf(stderr
, "IP NOT PRESENT IN LIST");
310 distance
= ip_distance_2_sum(&(t
->addr
), l
, pnn
);
311 printf ("%lu\n", (unsigned long) distance
);
313 fprintf(stderr
, "BAD INPUT");
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
;
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
,
343 uint32_t *tvals
= talloc_zero_array(tmp_ctx
, uint32_t, numnodes
);
344 char *t
= getenv(tunable
);
347 if (strcmp(t
, "1") == 0) {
348 for (i
=0; i
<numnodes
; i
++) {
352 tok
= strtok(t
, ",");
354 while (tok
!= NULL
) {
356 (uint32_t) strtol(tok
, NULL
, 0);
358 tok
= strtok(NULL
, ",");
361 fprintf(stderr
, "ERROR: Wrong number of values in %s\n", tunable
);
370 static enum ctdb_runstate
*get_runstate(TALLOC_CTX
*tmp_ctx
,
375 enum ctdb_runstate
*runstate
=
376 talloc_zero_array(tmp_ctx
, enum ctdb_runstate
, numnodes
);
377 char *t
= getenv("CTDB_TEST_RUNSTATE");
380 for (i
=0; i
<numnodes
; i
++) {
381 runstate
[i
] = CTDB_RUNSTATE_RUNNING
;
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
];
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
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
;
417 uint32_t nodeflags
[CTDB_TEST_MAX_NODES
];
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
);
430 tok
= strtok(ns
, ",");
431 while (tok
!= NULL
) {
432 nodeflags
[numnodes
] = (uint32_t) strtol(tok
, NULL
, 0);
434 if (numnodes
> CTDB_TEST_MAX_NODES
) {
435 DEBUG(DEBUG_ERR
, ("ERROR: Exceeding CTDB_TEST_MAX_NODES: %d\n", CTDB_TEST_MAX_NODES
));
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;
459 fprintf(stderr
, "ERROR: unknown IP algorithm %s\n", t
);
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
,
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
,
500 tval_noiptakeoverondisabled
,
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
;
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
);
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
;
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
);
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
;
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
);
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
);
593 static void usage(void)
595 fprintf(stderr
, "usage: ctdb_takeover_tests <op>\n");
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"));
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);