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