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