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