Tests - Allow some tests in ctdb_takover_tests to specify allowed nodes
[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 "includes.h"
21 #include "../include/ctdb_private.h"
22
23 /* This is lazy... but it is test code! */
24 #define CTDB_TEST_MAX_NODES 256
25 #define CTDB_TEST_MAX_IPS 256
26
27 /*
28  * Need these, since they're defined in ctdbd.c but we can't link
29  * that.
30  */
31 int script_log_level;
32 bool fast_start;
33 void ctdb_load_nodes_file(struct ctdb_context *ctdb) {}
34
35 /* Format of each line is "IP pnn" - the separator has to be at least
36  * 1 space (not a tab or whatever - a space!).
37  */
38 static struct ctdb_public_ip_list *
39 read_ctdb_public_ip_list(TALLOC_CTX *ctx)
40 {
41         char line[1024];
42         ctdb_sock_addr addr;
43         char *t;
44         int pnn;
45         struct ctdb_public_ip_list *last = NULL;
46
47         struct ctdb_public_ip_list *ret = NULL;
48
49         while (fgets(line, sizeof(line), stdin) != NULL) {
50                 
51                 if ((t = strchr(line, ' ')) != NULL) {
52                         /* Make line contain just the address */
53                         *t = '\0';
54                         /* Point to PNN or leading whitespace...  */
55                         t++;
56                         pnn = (int) strtol(t, (char **) NULL, 10);
57                 } else {
58                         /* Assume just an IP address, default to PNN -1 */
59                         if ((t = strchr(line, '\n')) != NULL) {
60                                 *t = '\0';
61                         }
62                         pnn = -1;
63                 }
64                
65                 if (parse_ip(line, NULL, 0, &addr)) {
66                         if (last == NULL) {
67                                 last = talloc(ctx, struct ctdb_public_ip_list);
68                         } else {
69                                 last->next = talloc(ctx, struct ctdb_public_ip_list);
70                                 last = last->next;
71                         }
72                         last->next = NULL;
73                         last->pnn = pnn;
74                         memcpy(&(last->addr), &addr, sizeof(addr));
75                         if (ret == NULL) {
76                                 ret = last;
77                         }
78                 } else {
79                         DEBUG(DEBUG_ERR, (__location__ " ERROR, bad address :%s\n", line));
80                 }
81         }
82                         
83         return ret;
84 }
85
86 void print_ctdb_public_ip_list(struct ctdb_public_ip_list * ips)
87 {
88         while (ips) {
89                 printf("%s %d\n", ctdb_addr_to_str(&(ips->addr)), ips->pnn);
90                 ips = ips->next;
91         }
92 }
93
94 /* Read some IPs from stdin, 1 per line, parse them and then print
95  * them back out. */
96 void ctdb_test_read_ctdb_public_ip_list(void)
97 {
98         struct ctdb_public_ip_list *l;
99
100         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
101
102         l = read_ctdb_public_ip_list(tmp_ctx);
103
104         print_ctdb_public_ip_list(l);
105
106         talloc_free(tmp_ctx);
107 }
108
109 /* Format of each line is "IP pnn" - the separator has to be at least
110  * 1 space (not a tab or whatever - a space!).
111  */
112 static bool
113 read_ctdb_public_ip_info(TALLOC_CTX *ctx,
114                          int numnodes,
115                          struct ctdb_public_ip_list ** all_ips,
116                          struct ctdb_all_public_ips *** avail)
117 {
118         char line[1024];
119         ctdb_sock_addr addr;
120         char *t, *tok;
121         struct ctdb_public_ip_list * ta;
122         int pnn, numips, curr, n, i;
123         struct ctdb_all_public_ips * a;
124
125         struct ctdb_public_ip_list *last = NULL;
126
127         *avail = talloc_array_size(ctx, sizeof(struct ctdb_all_public_ips *), CTDB_TEST_MAX_NODES);
128         memset(*avail, 0,
129                sizeof(struct ctdb_all_public_ips *) * CTDB_TEST_MAX_NODES);
130
131         numips = 0;
132         *all_ips = NULL;
133         while (fgets(line, sizeof(line), stdin) != NULL) {
134
135                 /* Get rid of pesky newline */
136                 if ((t = strchr(line, '\n')) != NULL) {
137                         *t = '\0';
138                 }
139
140                 /* Get the IP address */
141                 tok = strtok(line, " \t");
142                 if (tok == NULL) {
143                         DEBUG(DEBUG_ERR, (__location__ " WARNING, bad line ignored :%s\n", line));
144                         continue;
145                 }
146
147                 if (!parse_ip(tok, NULL, 0, &addr)) {
148                         DEBUG(DEBUG_ERR, (__location__ " ERROR, bad address :%s\n", tok));
149                         continue;
150                 }
151
152                 numips++;
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 ctdb_public_ip_list);
164                 } else {
165                         last->next = talloc(ctx, struct ctdb_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_all_public_ips, 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_all_public_ips, 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 void print_ctdb_available_ips(int numnodes, struct ctdb_all_public_ips **avail)
217 {
218         int n, i;
219
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)));
227                         }
228                         printf("\n");
229                 }
230         }
231 }
232
233 void ctdb_test_read_ctdb_public_ip_info(const char nodestates[])
234 {
235         int numnodes;
236         struct ctdb_public_ip_list *l;
237         struct ctdb_all_public_ips **avail;
238         char *tok, *ns;
239
240         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
241
242         /* Avoid that const */
243         ns = talloc_strdup(tmp_ctx, nodestates);
244
245         numnodes = 0;
246         tok = strtok(ns, ",");
247         while (tok != NULL) {
248                 numnodes++;
249                 tok = strtok(NULL, ",");
250         }
251         
252         read_ctdb_public_ip_info(tmp_ctx, numnodes, &l, &avail);
253
254         print_ctdb_public_ip_list(l);
255         print_ctdb_available_ips(numnodes, avail);
256
257         talloc_free(tmp_ctx);
258 }
259
260 /* Read 2 IPs from stdin, calculate the IP distance and print it. */
261 void ctdb_test_ip_distance(void)
262 {
263         struct ctdb_public_ip_list *l;
264         uint32_t distance;
265
266         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
267
268         l = read_ctdb_public_ip_list(tmp_ctx);
269
270         if (l && l->next) {
271                 distance = ip_distance(&(l->addr), &(l->next->addr));
272                 printf ("%lu\n", (unsigned long) distance);
273         }
274
275         talloc_free(tmp_ctx);
276 }
277
278 /* Read some IPs from stdin, calculate the sum of the squares of the
279  * IP distances between the 1st argument and those read that are on
280  * the given node. The given IP must one of the ones in the list.  */
281 void ctdb_test_ip_distance_2_sum(const char ip[], int pnn)
282 {
283         struct ctdb_public_ip_list *l;
284         struct ctdb_public_ip_list *t;
285         ctdb_sock_addr addr;
286         uint32_t distance;
287
288         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
289
290         
291         l = read_ctdb_public_ip_list(tmp_ctx);
292
293         if (l && parse_ip(ip, NULL, 0, &addr)) {
294                 /* find the entry for the specified IP */
295                 for (t=l; t!=NULL; t=t->next) {
296                         if (ctdb_same_ip(&(t->addr), &addr)) {
297                                 break;
298                         }
299                 }
300
301                 if (t == NULL) {
302                         fprintf(stderr, "IP NOT PRESENT IN LIST");
303                         exit(1);
304                 }
305
306                 distance = ip_distance_2_sum(&(t->addr), l, pnn);
307                 printf ("%lu\n", (unsigned long) distance);
308         } else {
309                 fprintf(stderr, "BAD INPUT");
310                 exit(1);
311         }
312
313         talloc_free(tmp_ctx);
314 }
315
316 /* Read some IPs from stdin, calculate the sume of the squares of the
317  * IP distances between the first and the rest, and print it. */
318 void ctdb_test_lcp2_imbalance(int pnn)
319 {
320         struct ctdb_public_ip_list *l;
321         uint32_t imbalance;
322
323         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
324
325         l = read_ctdb_public_ip_list(tmp_ctx);
326
327         imbalance = lcp2_imbalance(l, pnn);
328         printf ("%lu\n", (unsigned long) imbalance);
329
330         talloc_free(tmp_ctx);
331 }
332
333 void ctdb_test_init(const char nodestates[],
334                     struct ctdb_context **ctdb,
335                     struct ctdb_public_ip_list **all_ips,
336                     struct ctdb_node_map **nodemap)
337 {
338         struct ctdb_all_public_ips **avail;
339         int i, numnodes;
340         uint32_t nodeflags[CTDB_TEST_MAX_NODES];
341         char *tok, *ns;
342
343         *ctdb = talloc_zero(NULL, struct ctdb_context);
344
345         /* Avoid that const */
346         ns = talloc_strdup(*ctdb, nodestates);
347
348         numnodes = 0;
349         tok = strtok(ns, ",");
350         while (tok != NULL) {
351                 nodeflags[numnodes] = (uint32_t) strtol(tok, NULL, 16);
352                 numnodes++;
353                 tok = strtok(NULL, ",");
354         }
355         
356         /* Fake things up... */
357         (*ctdb)->num_nodes = numnodes;
358
359         (*ctdb)->tunable.deterministic_public_ips = 0;
360         (*ctdb)->tunable.disable_ip_failover = 0;
361         (*ctdb)->tunable.no_ip_failback = 0;
362
363         if (getenv("CTDB_LCP2")) {
364                 if (strcmp(getenv("CTDB_LCP2"), "yes") == 0) {
365                         (*ctdb)->tunable.lcp2_public_ip_assignment = 1;
366                 } else {
367                         (*ctdb)->tunable.lcp2_public_ip_assignment = 0;
368                 }
369         }
370
371         *nodemap =  talloc_array(*ctdb, struct ctdb_node_map, numnodes);
372         (*nodemap)->num = numnodes;
373
374         read_ctdb_public_ip_info(*ctdb, numnodes, all_ips, &avail);
375
376         (*ctdb)->nodes = talloc_array(*ctdb, struct ctdb_node *, numnodes); // FIXME: bogus size, overkill
377
378         for (i=0; i < numnodes; i++) {
379                 (*nodemap)->nodes[i].pnn = i;
380                 (*nodemap)->nodes[i].flags = nodeflags[i];
381                 /* nodemap->nodes[i].sockaddr is uninitialised */
382
383                 (*ctdb)->nodes[i] = talloc(*ctdb, struct ctdb_node);
384                 (*ctdb)->nodes[i]->pnn = i;
385                 (*ctdb)->nodes[i]->flags = nodeflags[i];
386                 (*ctdb)->nodes[i]->available_public_ips = avail[i];
387                 (*ctdb)->nodes[i]->known_public_ips = avail[i];
388         }
389 }
390
391 /* IP layout is read from stdin. */
392 void ctdb_test_lcp2_allocate_unassigned(const char nodestates[])
393 {
394         struct ctdb_context *ctdb;
395         struct ctdb_public_ip_list *all_ips;
396         struct ctdb_node_map *nodemap;
397
398         uint32_t *lcp2_imbalances;
399         bool *newly_healthy;
400
401         ctdb_test_init(nodestates, &ctdb, &all_ips, &nodemap);
402
403         lcp2_init(ctdb, nodemap,
404                   NODE_FLAGS_INACTIVE|NODE_FLAGS_DISABLED,
405                   all_ips, &lcp2_imbalances, &newly_healthy);
406
407         lcp2_allocate_unassigned(ctdb, nodemap,
408                                  NODE_FLAGS_INACTIVE|NODE_FLAGS_DISABLED,
409                                  all_ips, lcp2_imbalances);
410
411         print_ctdb_public_ip_list(all_ips);
412
413         talloc_free(ctdb);
414 }
415
416 /* IP layout is read from stdin. */
417 void ctdb_test_lcp2_failback(const char nodestates[])
418 {
419         struct ctdb_context *ctdb;
420         struct ctdb_public_ip_list *all_ips;
421         struct ctdb_node_map *nodemap;
422
423         uint32_t *lcp2_imbalances;
424         bool *newly_healthy;
425
426         ctdb_test_init(nodestates, &ctdb, &all_ips, &nodemap);
427
428         lcp2_init(ctdb, nodemap,
429                   NODE_FLAGS_INACTIVE|NODE_FLAGS_DISABLED,
430                   all_ips, &lcp2_imbalances, &newly_healthy);
431
432         lcp2_failback(ctdb, nodemap,
433                                  NODE_FLAGS_INACTIVE|NODE_FLAGS_DISABLED,
434                       all_ips, lcp2_imbalances, newly_healthy);
435
436         print_ctdb_public_ip_list(all_ips);
437
438         talloc_free(ctdb);
439 }
440
441 /* IP layout is read from stdin. */
442 void ctdb_test_lcp2_failback_loop(const char nodestates[])
443 {
444         struct ctdb_context *ctdb;
445         struct ctdb_public_ip_list *all_ips;
446         struct ctdb_node_map *nodemap;
447
448         uint32_t *lcp2_imbalances;
449         bool *newly_healthy;
450
451         ctdb_test_init(nodestates, &ctdb, &all_ips, &nodemap);
452
453         lcp2_init(ctdb, nodemap,
454                   NODE_FLAGS_INACTIVE|NODE_FLAGS_DISABLED,
455                   all_ips, &lcp2_imbalances, &newly_healthy);
456
457 try_again:
458         if (lcp2_failback(ctdb, nodemap,
459                           NODE_FLAGS_INACTIVE|NODE_FLAGS_DISABLED,
460                           all_ips, lcp2_imbalances, newly_healthy)) {
461                 goto try_again;
462         }
463
464         print_ctdb_public_ip_list(all_ips);
465
466         talloc_free(ctdb);
467 }
468
469 /* IP layout is read from stdin. */
470 void ctdb_test_ctdb_takeover_run_core(const char nodestates[])
471 {
472         struct ctdb_context *ctdb;
473         struct ctdb_public_ip_list *all_ips;
474         struct ctdb_node_map *nodemap;
475
476         ctdb_test_init(nodestates, &ctdb, &all_ips, &nodemap);
477
478         ctdb_takeover_run_core(ctdb, nodemap, &all_ips);
479
480         print_ctdb_public_ip_list(all_ips);
481
482         talloc_free(ctdb);
483 }
484
485 void usage(void)
486 {
487         fprintf(stderr, "usage: ctdb_takeover_tests <op>\n");
488         exit(1);
489 }
490
491 int main(int argc, const char *argv[])
492 {
493         LogLevel = DEBUG_DEBUG;
494         if (getenv("CTDB_TEST_LOGLEVEL")) {
495                 LogLevel = atoi(getenv("CTDB_TEST_LOGLEVEL"));
496         }
497
498         if (argc < 2) {
499                 usage();
500         }
501
502         if (strcmp(argv[1], "ip_list") == 0) {
503                 ctdb_test_read_ctdb_public_ip_list();
504         } else if (argc == 3 && strcmp(argv[1], "ip_info") == 0) {
505                 ctdb_test_read_ctdb_public_ip_info(argv[2]);
506         } else if (strcmp(argv[1], "ip_distance") == 0) {
507                 ctdb_test_ip_distance();
508         } else if (argc == 4 && strcmp(argv[1], "ip_distance_2_sum") == 0) {
509                 ctdb_test_ip_distance_2_sum(argv[2], atoi(argv[3]));
510         } else if (argc >= 3 && strcmp(argv[1], "lcp2_imbalance") == 0) {
511                 ctdb_test_lcp2_imbalance(atoi(argv[2]));
512         } else if (argc == 3 && strcmp(argv[1], "lcp2_allocate_unassigned") == 0) {
513                 ctdb_test_lcp2_allocate_unassigned(argv[2]);
514         } else if (argc == 3 && strcmp(argv[1], "lcp2_failback") == 0) {
515                 ctdb_test_lcp2_failback(argv[2]);
516         } else if (argc == 3 && strcmp(argv[1], "lcp2_failback_loop") == 0) {
517                 ctdb_test_lcp2_failback_loop(argv[2]);
518         } else if (argc == 3 && strcmp(argv[1], "ctdb_takeover_run_core") == 0) {
519                 ctdb_test_ctdb_takeover_run_core(argv[2]);
520         } else {
521                 usage();
522         }
523
524         return 0;
525 }