ctdb-daemon: Rename struct ctdb_all_public_ips to ctdb_public_ip_list_old
[obnox/samba/samba-obnox.git] / ctdb / tests / src / ctdb_takeover_tests.c
1 /* 
2    Tests for ctdb_takeover.c
3
4    Copyright (C) Martin Schwenke 2011
5
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.
10    
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.
15    
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/>.
18 */
19
20 #include "ctdbd_test.c"
21
22 /* This is lazy... but it is test code! */
23 #define CTDB_TEST_MAX_NODES 256
24 #define CTDB_TEST_MAX_IPS 1024
25
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!).
28  */
29 static struct public_ip_list *
30 read_ctdb_public_ip_list(TALLOC_CTX *ctx)
31 {
32         char line[1024];
33         ctdb_sock_addr addr;
34         char *t;
35         int pnn;
36         struct public_ip_list *last = NULL;
37
38         struct public_ip_list *ret = NULL;
39
40         while (fgets(line, sizeof(line), stdin) != NULL) {
41                 
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';
52                         }
53                         pnn = -1;
54                 }
55                
56                 if (parse_ip(line, NULL, 0, &addr)) {
57                         if (last == NULL) {
58                                 last = talloc(ctx, struct public_ip_list);
59                         } else {
60                                 last->next = talloc(ctx, struct public_ip_list);
61                                 last = last->next;
62                         }
63                         last->next = NULL;
64                         last->pnn = pnn;
65                         memcpy(&(last->addr), &addr, sizeof(addr));
66                         if (ret == NULL) {
67                                 ret = last;
68                         }
69                 } else {
70                         DEBUG(DEBUG_ERR, (__location__ " ERROR, bad address :%s\n", line));
71                 }
72         }
73                         
74         return ret;
75 }
76
77 static void print_ctdb_public_ip_list(struct public_ip_list * ips)
78 {
79         while (ips) {
80                 printf("%s %d\n", ctdb_addr_to_str(&(ips->addr)), ips->pnn);
81                 ips = ips->next;
82         }
83 }
84
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)
88 {
89         struct public_ip_list *l;
90
91         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
92
93         l = read_ctdb_public_ip_list(tmp_ctx);
94
95         print_ctdb_public_ip_list(l);
96
97         talloc_free(tmp_ctx);
98 }
99
100 /* Format of each line is "IP CURRENT_PNN ALLOWED_PNN,...".
101  */
102 static bool
103 read_ctdb_public_ip_info(TALLOC_CTX *ctx,
104                          int numnodes,
105                          struct public_ip_list ** all_ips,
106                          struct ctdb_public_ip_list_old *** avail)
107 {
108         char line[1024];
109         ctdb_sock_addr addr;
110         char *t, *tok;
111         struct public_ip_list * ta;
112         int pnn, numips, curr, n, i;
113         struct ctdb_public_ip_list_old * a;
114
115         struct public_ip_list *last = NULL;
116
117         *avail = talloc_array_size(ctx, sizeof(struct ctdb_public_ip_list_old *),
118                                    CTDB_TEST_MAX_NODES);
119         memset(*avail, 0,
120                sizeof(struct ctdb_public_ip_list_old *) * CTDB_TEST_MAX_NODES);
121
122         numips = 0;
123         *all_ips = NULL;
124         while (fgets(line, sizeof(line), stdin) != NULL) {
125
126                 /* Get rid of pesky newline */
127                 if ((t = strchr(line, '\n')) != NULL) {
128                         *t = '\0';
129                 }
130
131                 /* Exit on an empty line */
132                 if (line[0] == '\0') {
133                         break;
134                 }
135
136                 /* Get the IP address */
137                 tok = strtok(line, " \t");
138                 if (tok == NULL) {
139                         DEBUG(DEBUG_ERR, (__location__ " WARNING, bad line ignored :%s\n", line));
140                         continue;
141                 }
142
143                 if (!parse_ip(tok, NULL, 0, &addr)) {
144                         DEBUG(DEBUG_ERR, (__location__ " ERROR, bad address :%s\n", tok));
145                         continue;
146                 }
147
148                 numips++;
149                 if (numips > CTDB_TEST_MAX_IPS) {
150                         DEBUG(DEBUG_ERR, ("ERROR: Exceeding CTDB_TEST_MAX_IPS: %d\n", CTDB_TEST_MAX_IPS));
151                         exit(1);
152                 }
153
154                 /* Get the PNN */
155                 pnn = -1;
156                 tok = strtok(NULL, " \t");
157                 if (tok != NULL) {
158                         pnn = (int) strtol(tok, (char **) NULL, 10);
159                 }
160
161                 /* Add address + pnn to all_ips */
162                 if (last == NULL) {
163                         last = talloc(ctx, struct public_ip_list);
164                 } else {
165                         last->next = talloc(ctx, struct public_ip_list);
166                         last = last->next;
167                 }
168                 last->next = NULL;
169                 last->pnn = pnn;
170                 memcpy(&(last->addr), &addr, sizeof(addr));
171                 if (*all_ips == NULL) {
172                         *all_ips = last;
173                 }
174
175                 tok = strtok(NULL, " \t#");
176                 if (tok == NULL) {
177                         continue;
178                 }
179
180                 /* Handle allowed nodes for addr */
181                 t = strtok(tok, ",");
182                 while (t != NULL) {
183                         n = (int) strtol(t, (char **) NULL, 10);
184                         if ((*avail)[n] == NULL) {
185                                 (*avail)[n] = talloc_array(ctx, struct ctdb_public_ip_list_old, CTDB_TEST_MAX_IPS);
186                                 (*avail)[n]->num = 0;
187                         }
188                         curr = (*avail)[n]->num;
189                         (*avail)[n]->ips[curr].pnn = pnn;
190                         memcpy(&((*avail)[n]->ips[curr].addr),
191                                &addr, sizeof(addr));
192                         (*avail)[n]->num++;
193                         t = strtok(NULL, ",");
194                 }
195
196         }
197
198         /* Build list of all allowed IPs */
199         a = talloc_array(ctx, struct ctdb_public_ip_list_old, CTDB_TEST_MAX_IPS);
200         a->num = numips;
201         for (ta = *all_ips, i=0; ta != NULL && i < numips ; ta = ta->next, i++) {
202                 a->ips[i].pnn = ta->pnn;
203                 memcpy(&(a->ips[i].addr), &(ta->addr), sizeof(ta->addr));
204         }
205
206         /* Assign it to any nodes that don't have a list assigned */
207         for (n = 0; n < numnodes; n++) {
208                 if ((*avail)[n] == NULL) {
209                         (*avail)[n] = a;
210                 }
211         }
212
213         return true;
214 }
215
216 static void print_ctdb_available_ips(int numnodes,
217                                      struct ctdb_public_ip_list_old **avail)
218 {
219         int n, i;
220
221         for (n = 0; n < numnodes; n++) {
222                 if ((avail[n] != NULL) && (avail[n]->num > 0)) {
223                         printf("%d:", n);
224                         for (i = 0; i < avail[n]->num; i++) {
225                                 printf("%s%s",
226                                        (i == 0) ? " " : ", ",
227                                        ctdb_addr_to_str(&(avail[n]->ips[i].addr)));
228                         }
229                         printf("\n");
230                 }
231         }
232 }
233
234 static void ctdb_test_read_ctdb_public_ip_info(const char nodestates[])
235 {
236         int numnodes;
237         struct public_ip_list *l;
238         struct ctdb_public_ip_list_old **avail;
239         char *tok, *ns;
240
241         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
242
243         /* Avoid that const */
244         ns = talloc_strdup(tmp_ctx, nodestates);
245
246         numnodes = 0;
247         tok = strtok(ns, ",");
248         while (tok != NULL) {
249                 numnodes++;
250                 if (numnodes > CTDB_TEST_MAX_NODES) {
251                         DEBUG(DEBUG_ERR, ("ERROR: Exceeding CTDB_TEST_MAX_NODES: %d\n", CTDB_TEST_MAX_NODES));
252                         exit(1);
253                 }
254                 tok = strtok(NULL, ",");
255         }
256         
257         read_ctdb_public_ip_info(tmp_ctx, numnodes, &l, &avail);
258
259         print_ctdb_public_ip_list(l);
260         print_ctdb_available_ips(numnodes, avail);
261
262         talloc_free(tmp_ctx);
263 }
264
265 /* Read 2 IPs from stdin, calculate the IP distance and print it. */
266 static void ctdb_test_ip_distance(void)
267 {
268         struct public_ip_list *l;
269         uint32_t distance;
270
271         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
272
273         l = read_ctdb_public_ip_list(tmp_ctx);
274
275         if (l && l->next) {
276                 distance = ip_distance(&(l->addr), &(l->next->addr));
277                 printf ("%lu\n", (unsigned long) distance);
278         }
279
280         talloc_free(tmp_ctx);
281 }
282
283 /* Read some IPs from stdin, calculate the sum of the squares of the
284  * IP distances between the 1st argument and those read that are on
285  * the given node. The given IP must one of the ones in the list.  */
286 static void ctdb_test_ip_distance_2_sum(const char ip[], int pnn)
287 {
288         struct public_ip_list *l;
289         struct public_ip_list *t;
290         ctdb_sock_addr addr;
291         uint32_t distance;
292
293         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
294
295         
296         l = read_ctdb_public_ip_list(tmp_ctx);
297
298         if (l && parse_ip(ip, NULL, 0, &addr)) {
299                 /* find the entry for the specified IP */
300                 for (t=l; t!=NULL; t=t->next) {
301                         if (ctdb_same_ip(&(t->addr), &addr)) {
302                                 break;
303                         }
304                 }
305
306                 if (t == NULL) {
307                         fprintf(stderr, "IP NOT PRESENT IN LIST");
308                         exit(1);
309                 }
310
311                 distance = ip_distance_2_sum(&(t->addr), l, pnn);
312                 printf ("%lu\n", (unsigned long) distance);
313         } else {
314                 fprintf(stderr, "BAD INPUT");
315                 exit(1);
316         }
317
318         talloc_free(tmp_ctx);
319 }
320
321 /* Read some IPs from stdin, calculate the sume of the squares of the
322  * IP distances between the first and the rest, and print it. */
323 static void ctdb_test_lcp2_imbalance(int pnn)
324 {
325         struct public_ip_list *l;
326         uint32_t imbalance;
327
328         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
329
330         l = read_ctdb_public_ip_list(tmp_ctx);
331
332         imbalance = lcp2_imbalance(l, pnn);
333         printf ("%lu\n", (unsigned long) imbalance);
334
335         talloc_free(tmp_ctx);
336 }
337
338 static uint32_t *get_tunable_values(TALLOC_CTX *tmp_ctx,
339                                     int numnodes,
340                                     const char *tunable)
341 {
342         int i;
343         char *tok;
344         uint32_t *tvals = talloc_zero_array(tmp_ctx, uint32_t, numnodes);
345         char *t = getenv(tunable);
346
347         if (t) {
348                 if (strcmp(t, "1") == 0) {
349                         for (i=0; i<numnodes; i++) {
350                                 tvals[i] = 1;
351                         }
352                 } else {
353                         tok = strtok(t, ",");
354                         i = 0;
355                         while (tok != NULL) {
356                                 tvals[i] =
357                                         (uint32_t) strtol(tok, NULL, 0);
358                                 i++;
359                                 tok = strtok(NULL, ",");
360                         }
361                         if (i != numnodes) {
362                                 fprintf(stderr, "ERROR: Wrong number of values in %s\n", tunable);
363                                 exit(1);
364                         }
365                 }
366         }
367
368         return tvals;
369 }
370
371 static enum ctdb_runstate *get_runstate(TALLOC_CTX *tmp_ctx,
372                                         int numnodes)
373 {
374         int i;
375         uint32_t *tvals;
376         enum ctdb_runstate *runstate =
377                 talloc_zero_array(tmp_ctx, enum ctdb_runstate, numnodes);
378         char *t = getenv("CTDB_TEST_RUNSTATE");
379
380         if (t == NULL) {
381                 for (i=0; i<numnodes; i++) {
382                         runstate[i] = CTDB_RUNSTATE_RUNNING;
383                 }
384         } else {
385                 tvals = get_tunable_values(tmp_ctx, numnodes, "CTDB_TEST_RUNSTATE");
386                 for (i=0; i<numnodes; i++) {
387                         runstate[i] = (enum ctdb_runstate) tvals[i];
388                 }
389                 talloc_free(tvals);
390         }
391
392         return runstate;
393 }
394
395 /* Fake up enough CTDB state to be able to run the IP allocation
396  * algorithm.  Usually this sets up some standard state, sets the node
397  * states from the command-line and reads the current IP layout from
398  * stdin.
399  *
400  * However, if read_ips_for_multiple_nodes is true then each node's
401  * idea of the IP layout is read separately from stdin.  In this mode
402  * is doesn't make much sense to use read_ctdb_public_ip_info's
403  * optional ALLOWED_PNN,... list in the input, since each node is
404  * being handled separately anyway.  IPs for each node are separated
405  * by a blank line.  This mode is for testing weird behaviours where
406  * the IP layouts differs across nodes and we want to improve
407  * create_merged_ip_list(), so should only be used in tests of
408  * ctdb_takeover_run_core().  Yes, it is a hack...  :-)
409  */
410 static void ctdb_test_init(const char nodestates[],
411                            struct ctdb_context **ctdb,
412                            struct public_ip_list **all_ips,
413                            struct ctdb_ipflags **ipflags,
414                            bool read_ips_for_multiple_nodes)
415 {
416         struct ctdb_public_ip_list_old **avail;
417         int i, numnodes;
418         uint32_t nodeflags[CTDB_TEST_MAX_NODES];
419         char *tok, *ns, *t;
420         struct ctdb_node_map_old *nodemap;
421         uint32_t *tval_noiptakeover;
422         uint32_t *tval_noiptakeoverondisabled;
423         enum ctdb_runstate *runstate;
424
425         *ctdb = talloc_zero(NULL, struct ctdb_context);
426
427         /* Avoid that const */
428         ns = talloc_strdup(*ctdb, nodestates);
429
430         numnodes = 0;
431         tok = strtok(ns, ",");
432         while (tok != NULL) {
433                 nodeflags[numnodes] = (uint32_t) strtol(tok, NULL, 0);
434                 numnodes++;
435                 if (numnodes >= CTDB_TEST_MAX_NODES) {
436                         DEBUG(DEBUG_ERR, ("ERROR: Exceeding CTDB_TEST_MAX_NODES: %d\n", CTDB_TEST_MAX_NODES));
437                         exit(1);
438                 }
439                 tok = strtok(NULL, ",");
440         }
441         
442         /* Fake things up... */
443         (*ctdb)->num_nodes = numnodes;
444
445         /* Default to LCP2 */
446         (*ctdb)->tunable.lcp2_public_ip_assignment = 1;
447         (*ctdb)->tunable.deterministic_public_ips = 0;
448         (*ctdb)->tunable.disable_ip_failover = 0;
449         (*ctdb)->tunable.no_ip_failback = 0;
450
451         if ((t = getenv("CTDB_IP_ALGORITHM"))) {
452                 if (strcmp(t, "lcp2") == 0) {
453                         (*ctdb)->tunable.lcp2_public_ip_assignment = 1;
454                 } else if (strcmp(t, "nondet") == 0) {
455                         (*ctdb)->tunable.lcp2_public_ip_assignment = 0;
456                 } else if (strcmp(t, "det") == 0) {
457                         (*ctdb)->tunable.lcp2_public_ip_assignment = 0;
458                         (*ctdb)->tunable.deterministic_public_ips = 1;
459                 } else {
460                         fprintf(stderr, "ERROR: unknown IP algorithm %s\n", t);
461                         exit(1);
462                 }
463         }
464
465         tval_noiptakeover = get_tunable_values(*ctdb, numnodes,
466                                                "CTDB_SET_NoIPTakeover");
467         tval_noiptakeoverondisabled =
468                 get_tunable_values(*ctdb, numnodes,
469                                    "CTDB_SET_NoIPHostOnAllDisabled");
470
471         runstate = get_runstate(*ctdb, numnodes);
472
473         nodemap =  talloc_array(*ctdb, struct ctdb_node_map_old, numnodes);
474         nodemap->num = numnodes;
475
476         if (!read_ips_for_multiple_nodes) {
477                 read_ctdb_public_ip_info(*ctdb, numnodes, all_ips, &avail);
478         }
479
480         (*ctdb)->nodes = talloc_array(*ctdb, struct ctdb_node *, numnodes); // FIXME: bogus size, overkill
481
482         for (i=0; i < numnodes; i++) {
483                 nodemap->nodes[i].pnn = i;
484                 nodemap->nodes[i].flags = nodeflags[i];
485                 /* nodemap->nodes[i].sockaddr is uninitialised */
486
487                 if (read_ips_for_multiple_nodes) {
488                         read_ctdb_public_ip_info(*ctdb, numnodes,
489                                                  all_ips, &avail);
490                 }
491
492                 (*ctdb)->nodes[i] = talloc(*ctdb, struct ctdb_node);
493                 (*ctdb)->nodes[i]->pnn = i;
494                 (*ctdb)->nodes[i]->flags = nodeflags[i];
495                 (*ctdb)->nodes[i]->available_public_ips = avail[i];
496                 (*ctdb)->nodes[i]->known_public_ips = avail[i];
497         }
498
499         *ipflags = set_ipflags_internal(*ctdb, *ctdb, nodemap,
500                                         tval_noiptakeover,
501                                         tval_noiptakeoverondisabled,
502                                         runstate);
503 }
504
505 /* IP layout is read from stdin. */
506 static void ctdb_test_lcp2_allocate_unassigned(const char nodestates[])
507 {
508         struct ctdb_context *ctdb;
509         struct public_ip_list *all_ips;
510         struct ctdb_ipflags *ipflags;
511
512         uint32_t *lcp2_imbalances;
513         bool *newly_healthy;
514
515         ctdb_test_init(nodestates, &ctdb, &all_ips, &ipflags, false);
516
517         lcp2_init(ctdb, ipflags, all_ips, NULL,
518                   &lcp2_imbalances, &newly_healthy);
519
520         lcp2_allocate_unassigned(ctdb, ipflags,
521                                  all_ips, lcp2_imbalances);
522
523         print_ctdb_public_ip_list(all_ips);
524
525         talloc_free(ctdb);
526 }
527
528 /* IP layout is read from stdin. */
529 static void ctdb_test_lcp2_failback(const char nodestates[])
530 {
531         struct ctdb_context *ctdb;
532         struct public_ip_list *all_ips;
533         struct ctdb_ipflags *ipflags;
534
535         uint32_t *lcp2_imbalances;
536         bool *newly_healthy;
537
538         ctdb_test_init(nodestates, &ctdb, &all_ips, &ipflags, false);
539
540         lcp2_init(ctdb, ipflags, all_ips, NULL,
541                   &lcp2_imbalances, &newly_healthy);
542
543         lcp2_failback(ctdb, ipflags,
544                       all_ips, lcp2_imbalances, newly_healthy);
545
546         print_ctdb_public_ip_list(all_ips);
547
548         talloc_free(ctdb);
549 }
550
551 /* IP layout is read from stdin. */
552 static void ctdb_test_lcp2_failback_loop(const char nodestates[])
553 {
554         struct ctdb_context *ctdb;
555         struct public_ip_list *all_ips;
556         struct ctdb_ipflags *ipflags;
557
558         uint32_t *lcp2_imbalances;
559         bool *newly_healthy;
560
561         ctdb_test_init(nodestates, &ctdb, &all_ips, &ipflags, false);
562
563         lcp2_init(ctdb, ipflags, all_ips, NULL,
564                   &lcp2_imbalances, &newly_healthy);
565
566         lcp2_failback(ctdb, ipflags,
567                       all_ips, lcp2_imbalances, newly_healthy);
568
569         print_ctdb_public_ip_list(all_ips);
570
571         talloc_free(ctdb);
572 }
573
574 /* IP layout is read from stdin.  See comment for ctdb_test_init() for
575  * explanation of read_ips_for_multiple_nodes.
576  */
577 static void ctdb_test_ctdb_takeover_run_core(const char nodestates[],
578                                              bool read_ips_for_multiple_nodes)
579 {
580         struct ctdb_context *ctdb;
581         struct public_ip_list *all_ips;
582         struct ctdb_ipflags *ipflags;
583
584         ctdb_test_init(nodestates, &ctdb, &all_ips, &ipflags,
585                        read_ips_for_multiple_nodes);
586
587         ctdb_takeover_run_core(ctdb, ipflags, &all_ips, NULL);
588
589         print_ctdb_public_ip_list(all_ips);
590
591         talloc_free(ctdb);
592 }
593
594 static void usage(void)
595 {
596         fprintf(stderr, "usage: ctdb_takeover_tests <op>\n");
597         exit(1);
598 }
599
600 int main(int argc, const char *argv[])
601 {
602         DEBUGLEVEL = DEBUG_DEBUG;
603         if (getenv("CTDB_TEST_LOGLEVEL")) {
604                 DEBUGLEVEL = atoi(getenv("CTDB_TEST_LOGLEVEL"));
605         }
606
607         if (argc < 2) {
608                 usage();
609         }
610
611         if (strcmp(argv[1], "ip_list") == 0) {
612                 ctdb_test_read_ctdb_public_ip_list();
613         } else if (argc == 3 && strcmp(argv[1], "ip_info") == 0) {
614                 ctdb_test_read_ctdb_public_ip_info(argv[2]);
615         } else if (strcmp(argv[1], "ip_distance") == 0) {
616                 ctdb_test_ip_distance();
617         } else if (argc == 4 && strcmp(argv[1], "ip_distance_2_sum") == 0) {
618                 ctdb_test_ip_distance_2_sum(argv[2], atoi(argv[3]));
619         } else if (argc >= 3 && strcmp(argv[1], "lcp2_imbalance") == 0) {
620                 ctdb_test_lcp2_imbalance(atoi(argv[2]));
621         } else if (argc == 3 && strcmp(argv[1], "lcp2_allocate_unassigned") == 0) {
622                 ctdb_test_lcp2_allocate_unassigned(argv[2]);
623         } else if (argc == 3 && strcmp(argv[1], "lcp2_failback") == 0) {
624                 ctdb_test_lcp2_failback(argv[2]);
625         } else if (argc == 3 && strcmp(argv[1], "lcp2_failback_loop") == 0) {
626                 ctdb_test_lcp2_failback_loop(argv[2]);
627         } else if (argc == 3 &&
628                    strcmp(argv[1], "ctdb_takeover_run_core") == 0) {
629                 ctdb_test_ctdb_takeover_run_core(argv[2], false);
630         } else if (argc == 4 &&
631                    strcmp(argv[1], "ctdb_takeover_run_core") == 0 &&
632                    strcmp(argv[3], "multi") == 0) {
633                 ctdb_test_ctdb_takeover_run_core(argv[2], true);
634         } else {
635                 usage();
636         }
637
638         return 0;
639 }