e15b49dac18a20b8dfd2d67761dc951df9bfb41b
[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 <assert.h>
21
22 #include "ctdbd_test.c"
23
24 /* This is lazy... but it is test code! */
25 #define CTDB_TEST_MAX_NODES 256
26
27 static void print_ctdb_public_ip_list(struct public_ip_list * ips)
28 {
29         while (ips) {
30                 printf("%s %d\n", ctdb_addr_to_str(&(ips->addr)), ips->pnn);
31                 ips = ips->next;
32         }
33 }
34
35 static uint32_t *get_tunable_values(TALLOC_CTX *tmp_ctx,
36                                     int numnodes,
37                                     const char *tunable);
38 static enum ctdb_runstate *get_runstate(TALLOC_CTX *tmp_ctx,
39                                         int numnodes);
40
41 static void add_ip(TALLOC_CTX *mem_ctx,
42                    struct ctdb_public_ip_list *l,
43                    ctdb_sock_addr *addr,
44                    uint32_t pnn)
45 {
46
47         l->ip = talloc_realloc(mem_ctx, l->ip,
48                                struct ctdb_public_ip, l->num + 1);
49         assert(l->ip != NULL);
50
51         l->ip[l->num].addr = *addr;
52         l->ip[l->num].pnn  = pnn;
53         l->num++;
54 }
55
56 /* Format of each line is "IP CURRENT_PNN ALLOWED_PNN,...".
57  */
58 static bool
59 read_ctdb_public_ip_info(TALLOC_CTX *ctx  ,
60                          int numnodes,
61                          struct ctdb_public_ip_list ** known,
62                          struct ctdb_public_ip_list ** avail)
63 {
64         char line[1024];
65         ctdb_sock_addr addr;
66         char *t, *tok;
67         int pnn, n;
68         struct ctdb_public_ip_list * k;
69
70         enum ctdb_runstate *runstate;
71
72         runstate = get_runstate(ctx, numnodes);
73
74         *known = talloc_zero_array(ctx, struct ctdb_public_ip_list,
75                                    CTDB_TEST_MAX_NODES);
76         *avail = talloc_zero_array(ctx, struct ctdb_public_ip_list,
77                                    CTDB_TEST_MAX_NODES);
78
79         /* Known public IPs */
80         k = talloc_zero(ctx, struct ctdb_public_ip_list);
81         assert(k != NULL);
82
83         while (fgets(line, sizeof(line), stdin) != NULL) {
84
85                 /* Get rid of pesky newline */
86                 if ((t = strchr(line, '\n')) != NULL) {
87                         *t = '\0';
88                 }
89
90                 /* Exit on an empty line */
91                 if (line[0] == '\0') {
92                         break;
93                 }
94
95                 /* Get the IP address */
96                 tok = strtok(line, " \t");
97                 if (tok == NULL) {
98                         DEBUG(DEBUG_ERR, (__location__ " WARNING, bad line ignored :%s\n", line));
99                         continue;
100                 }
101
102                 if (!parse_ip(tok, NULL, 0, &addr)) {
103                         DEBUG(DEBUG_ERR, (__location__ " ERROR, bad address :%s\n", tok));
104                         continue;
105                 }
106
107                 /* Get the PNN */
108                 pnn = -1;
109                 tok = strtok(NULL, " \t");
110                 if (tok != NULL) {
111                         pnn = (int) strtol(tok, (char **) NULL, 10);
112                 }
113
114                 add_ip(k, k, &addr, pnn);
115
116                 tok = strtok(NULL, " \t#");
117                 if (tok == NULL) {
118                         continue;
119                 }
120
121                 /* Handle allowed nodes for addr */
122                 t = strtok(tok, ",");
123                 while (t != NULL) {
124                         n = (int) strtol(t, (char **) NULL, 10);
125                         add_ip(*known, &(*known)[n], &addr, pnn);
126                         t = strtok(NULL, ",");
127                 }
128
129         }
130
131         /* Assign it to any nodes that don't have a list assigned */
132         for (n = 0; n < numnodes; n++) {
133                 if ((*known)[n].num == 0) {
134                         (*known)[n] = *k;
135                 }
136                 if (runstate[n] == CTDB_RUNSTATE_RUNNING) {
137                         (*avail)[n] = (*known)[n];
138                 }
139         }
140
141         return true;
142 }
143
144 static uint32_t *get_tunable_values(TALLOC_CTX *tmp_ctx,
145                                     int numnodes,
146                                     const char *tunable)
147 {
148         int i;
149         char *tok;
150         uint32_t *tvals = talloc_zero_array(tmp_ctx, uint32_t, numnodes);
151         char *t = getenv(tunable);
152
153         if (t) {
154                 if (strcmp(t, "1") == 0) {
155                         for (i=0; i<numnodes; i++) {
156                                 tvals[i] = 1;
157                         }
158                 } else {
159                         tok = strtok(t, ",");
160                         i = 0;
161                         while (tok != NULL) {
162                                 tvals[i] =
163                                         (uint32_t) strtol(tok, NULL, 0);
164                                 i++;
165                                 tok = strtok(NULL, ",");
166                         }
167                         if (i != numnodes) {
168                                 fprintf(stderr, "ERROR: Wrong number of values in %s\n", tunable);
169                                 exit(1);
170                         }
171                 }
172         }
173
174         return tvals;
175 }
176
177 static enum ctdb_runstate *get_runstate(TALLOC_CTX *tmp_ctx,
178                                         int numnodes)
179 {
180         int i;
181         uint32_t *tvals;
182         enum ctdb_runstate *runstate =
183                 talloc_zero_array(tmp_ctx, enum ctdb_runstate, numnodes);
184         char *t = getenv("CTDB_TEST_RUNSTATE");
185
186         if (t == NULL) {
187                 for (i=0; i<numnodes; i++) {
188                         runstate[i] = CTDB_RUNSTATE_RUNNING;
189                 }
190         } else {
191                 tvals = get_tunable_values(tmp_ctx, numnodes, "CTDB_TEST_RUNSTATE");
192                 for (i=0; i<numnodes; i++) {
193                         runstate[i] = (enum ctdb_runstate) tvals[i];
194                 }
195                 talloc_free(tvals);
196         }
197
198         return runstate;
199 }
200
201 /* Fake up enough CTDB state to be able to run the IP allocation
202  * algorithm.  Usually this sets up some standard state, sets the node
203  * states from the command-line and reads the current IP layout from
204  * stdin.
205  *
206  * However, if read_ips_for_multiple_nodes is true then each node's
207  * idea of the IP layout is read separately from stdin.  In this mode
208  * is doesn't make much sense to use read_ctdb_public_ip_info's
209  * optional ALLOWED_PNN,... list in the input, since each node is
210  * being handled separately anyway.  IPs for each node are separated
211  * by a blank line.  This mode is for testing weird behaviours where
212  * the IP layouts differs across nodes and we want to improve
213  * create_merged_ip_list(), so should only be used in tests of
214  * ipalloc().  Yes, it is a hack...  :-)
215  */
216 static void ctdb_test_init(const char nodestates[],
217                            struct ctdb_context **ctdb,
218                            struct ipalloc_state **ipalloc_state,
219                            bool read_ips_for_multiple_nodes)
220 {
221         struct ctdb_public_ip_list *known;
222         struct ctdb_public_ip_list *avail;
223         int i, numnodes;
224         uint32_t nodeflags[CTDB_TEST_MAX_NODES];
225         char *tok, *ns, *t;
226         struct ctdb_node_map_old *nodemap;
227         uint32_t *tval_noiptakeover;
228         uint32_t *tval_noiptakeoverondisabled;
229
230         *ctdb = talloc_zero(NULL, struct ctdb_context);
231
232         /* Avoid that const */
233         ns = talloc_strdup(*ctdb, nodestates);
234
235         numnodes = 0;
236         tok = strtok(ns, ",");
237         while (tok != NULL) {
238                 nodeflags[numnodes] = (uint32_t) strtol(tok, NULL, 0);
239                 numnodes++;
240                 if (numnodes >= CTDB_TEST_MAX_NODES) {
241                         DEBUG(DEBUG_ERR, ("ERROR: Exceeding CTDB_TEST_MAX_NODES: %d\n", CTDB_TEST_MAX_NODES));
242                         exit(1);
243                 }
244                 tok = strtok(NULL, ",");
245         }
246         
247         /* Fake things up... */
248         (*ctdb)->num_nodes = numnodes;
249
250         /* Default to LCP2 */
251         (*ctdb)->tunable.lcp2_public_ip_assignment = 1;
252         (*ctdb)->tunable.deterministic_public_ips = 0;
253         (*ctdb)->tunable.disable_ip_failover = 0;
254         (*ctdb)->tunable.no_ip_failback = 0;
255
256         if ((t = getenv("CTDB_IP_ALGORITHM"))) {
257                 if (strcmp(t, "lcp2") == 0) {
258                         (*ctdb)->tunable.lcp2_public_ip_assignment = 1;
259                 } else if (strcmp(t, "nondet") == 0) {
260                         (*ctdb)->tunable.lcp2_public_ip_assignment = 0;
261                 } else if (strcmp(t, "det") == 0) {
262                         (*ctdb)->tunable.lcp2_public_ip_assignment = 0;
263                         (*ctdb)->tunable.deterministic_public_ips = 1;
264                 } else {
265                         fprintf(stderr, "ERROR: unknown IP algorithm %s\n", t);
266                         exit(1);
267                 }
268         }
269
270         tval_noiptakeover = get_tunable_values(*ctdb, numnodes,
271                                                "CTDB_SET_NoIPTakeover");
272         tval_noiptakeoverondisabled =
273                 get_tunable_values(*ctdb, numnodes,
274                                    "CTDB_SET_NoIPHostOnAllDisabled");
275
276         nodemap =  talloc_array(*ctdb, struct ctdb_node_map_old, numnodes);
277         nodemap->num = numnodes;
278
279         if (!read_ips_for_multiple_nodes) {
280                 read_ctdb_public_ip_info(*ctdb, numnodes,
281                                          &known, &avail);
282         }
283
284         (*ctdb)->nodes = talloc_array(*ctdb, struct ctdb_node *, numnodes); // FIXME: bogus size, overkill
285
286         *ipalloc_state = ipalloc_state_init(*ctdb, *ctdb);
287
288         for (i=0; i < numnodes; i++) {
289                 nodemap->nodes[i].pnn = i;
290                 nodemap->nodes[i].flags = nodeflags[i];
291                 /* nodemap->nodes[i].sockaddr is uninitialised */
292
293                 if (read_ips_for_multiple_nodes) {
294                         read_ctdb_public_ip_info(*ctdb, numnodes,
295                                                  &known, &avail);
296                 }
297
298                 (*ctdb)->nodes[i] = talloc(*ctdb, struct ctdb_node);
299                 (*ctdb)->nodes[i]->pnn = i;
300                 (*ctdb)->nodes[i]->flags = nodeflags[i];
301
302                 (*ipalloc_state)->available_public_ips[i] = avail[i];
303                 (*ipalloc_state)->known_public_ips[i] = known[i];
304         }
305
306         set_ipflags_internal(*ipalloc_state, nodemap,
307                              tval_noiptakeover,
308                              tval_noiptakeoverondisabled);
309
310         (*ipalloc_state)->all_ips = create_merged_ip_list(*ctdb,
311                                                           *ipalloc_state);
312
313         (*ipalloc_state)->force_rebalance_nodes = NULL;
314 }
315
316 /* IP layout is read from stdin.  See comment for ctdb_test_init() for
317  * explanation of read_ips_for_multiple_nodes.
318  */
319 static void ctdb_test_ipalloc(const char nodestates[],
320                               bool read_ips_for_multiple_nodes)
321 {
322         struct ctdb_context *ctdb;
323         struct ipalloc_state *ipalloc_state;
324
325         ctdb_test_init(nodestates, &ctdb, &ipalloc_state,
326                        read_ips_for_multiple_nodes);
327
328         ipalloc(ipalloc_state);
329
330         print_ctdb_public_ip_list(ipalloc_state->all_ips);
331
332         talloc_free(ctdb);
333 }
334
335 static void usage(void)
336 {
337         fprintf(stderr, "usage: ctdb_takeover_tests <op>\n");
338         exit(1);
339 }
340
341 int main(int argc, const char *argv[])
342 {
343         DEBUGLEVEL = DEBUG_DEBUG;
344         if (getenv("CTDB_TEST_LOGLEVEL")) {
345                 DEBUGLEVEL = atoi(getenv("CTDB_TEST_LOGLEVEL"));
346         }
347
348         if (argc < 2) {
349                 usage();
350         }
351
352         if (argc == 3 &&
353                    strcmp(argv[1], "ipalloc") == 0) {
354                 ctdb_test_ipalloc(argv[2], false);
355         } else if (argc == 4 &&
356                    strcmp(argv[1], "ipalloc") == 0 &&
357                    strcmp(argv[3], "multi") == 0) {
358                 ctdb_test_ipalloc(argv[2], true);
359         } else {
360                 usage();
361         }
362
363         return 0;
364 }