/* Test stubs and support functions for some CTDB client functions Copyright (C) Martin Schwenke 2011 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . */ /* Useful for functions that don't get struct ctdb_context passed */ static struct ctdb_context *ctdb_global; /* Read a nodemap from stdin. Each line looks like: * [RECMASTER] [CURRENT] [CAPABILITIES] * EOF or a blank line terminates input. * * By default, capablities for each node are * CTDB_CAP_RECMASTER|CTDB_CAP_LMASTER|CTDB_CAP_NATGW. These 3 * capabilities can be faked off by adding, for example, * -CTDB_CAP_RECMASTER. LVS can be faked on by adding * CTDB_CAP_LVS. */ /* A fake flag that is only supported by some functions */ #define NODE_FLAGS_FAKE_TIMEOUT 0x80000000 static void ctdb_test_stubs_read_nodemap(struct ctdb_context *ctdb) { char line[1024]; TALLOC_FREE(ctdb->nodes); ctdb->pnn = -1; ctdb->num_nodes = 0; ctdb->nodes = NULL; while ((fgets(line, sizeof(line), stdin) != NULL) && (line[0] != '\n')) { uint32_t pnn, flags, capabilities; char *tok, *t; const char *ip; ctdb_sock_addr saddr; /* Get rid of pesky newline */ if ((t = strchr(line, '\n')) != NULL) { *t = '\0'; } /* Get PNN */ tok = strtok(line, " \t"); if (tok == NULL) { DEBUG(DEBUG_ERR, (__location__ " WARNING, bad line (PNN) ignored \"%s\"\n", line)); continue; } pnn = (uint32_t)strtoul(tok, NULL, 0); /* Get IP */ tok = strtok(NULL, " \t"); if (tok == NULL) { DEBUG(DEBUG_ERR, (__location__ " WARNING, bad line (no IP) ignored \"%s\"\n", line)); continue; } if (!parse_ip(tok, NULL, 0, &saddr)) { DEBUG(DEBUG_ERR, (__location__ " WARNING, bad line (IP) ignored \"%s\"\n", line)); continue; } ip = talloc_strdup(ctdb, tok); /* Get flags */ tok = strtok(NULL, " \t"); if (tok == NULL) { DEBUG(DEBUG_ERR, (__location__ " WARNING, bad line (flags) ignored \"%s\"\n", line)); continue; } flags = (uint32_t)strtoul(tok, NULL, 0); capabilities = CTDB_CAP_RECMASTER|CTDB_CAP_LMASTER|CTDB_CAP_NATGW; tok = strtok(NULL, " \t"); while (tok != NULL) { if (strcmp(tok, "CURRENT") == 0) { ctdb->pnn = pnn; } else if (strcmp(tok, "RECMASTER") == 0) { ctdb->recovery_master = pnn; } else if (strcmp(tok, "-CTDB_CAP_RECMASTER") == 0) { capabilities &= ~CTDB_CAP_RECMASTER; } else if (strcmp(tok, "-CTDB_CAP_LMASTER") == 0) { capabilities &= ~CTDB_CAP_LMASTER; } else if (strcmp(tok, "-CTDB_CAP_NATGW") == 0) { capabilities &= ~CTDB_CAP_NATGW; } else if (strcmp(tok, "CTDB_CAP_LVS") == 0) { capabilities |= CTDB_CAP_LVS; } else if (strcmp(tok, "TIMEOUT") == 0) { /* This can be done with just a flag * value but it is probably clearer * and less error-prone to fake this * with an explicit token */ flags |= NODE_FLAGS_FAKE_TIMEOUT; } tok = strtok(NULL, " \t"); } ctdb->nodes = talloc_realloc(ctdb, ctdb->nodes, struct ctdb_node *, ctdb->num_nodes + 1); if (ctdb->nodes == NULL) { DEBUG(DEBUG_ERR, ("OOM allocating nodes array\n")); exit (1); } ctdb->nodes[ctdb->num_nodes] = talloc_zero(ctdb, struct ctdb_node); if (ctdb->nodes[ctdb->num_nodes] == NULL) { DEBUG(DEBUG_ERR, ("OOM allocating node structure\n")); exit (1); } ctdb->nodes[ctdb->num_nodes]->ctdb = ctdb; ctdb->nodes[ctdb->num_nodes]->name = "fakectdb"; ctdb->nodes[ctdb->num_nodes]->pnn = pnn; ctdb->nodes[ctdb->num_nodes]->address.address = ip; ctdb->nodes[ctdb->num_nodes]->address.port = 0; ctdb->nodes[ctdb->num_nodes]->flags = flags; ctdb->nodes[ctdb->num_nodes]->capabilities = capabilities; ctdb->num_nodes++; } } #ifdef CTDB_TEST_OVERRIDE_MAIN static void ctdb_test_stubs_print_nodemap(struct ctdb_context *ctdb) { int i; for (i = 0; i < ctdb->num_nodes; i++) { printf("%ld\t0x%lx%s%s\n", (unsigned long) ctdb->nodes[i]->pnn, (unsigned long) ctdb->nodes[i]->flags, ctdb->nodes[i]->pnn == ctdb->pnn ? "\tCURRENT" : "", ctdb->nodes[i]->pnn == ctdb->recovery_master ? "\tRECMASTER" : ""); } } #endif /* Read interfaces information. Same format as "ctdb ifaces -Y" * output: * :Name:LinkStatus:References: * :eth2:1:4294967294 * :eth1:1:4294967292 */ struct ctdb_iface { struct ctdb_iface *prev, *next; const char *name; bool link_up; uint32_t references; }; static void ctdb_test_stubs_read_ifaces(struct ctdb_context *ctdb) { char line[1024]; struct ctdb_iface *iface; while ((fgets(line, sizeof(line), stdin) != NULL) && (line[0] != '\n')) { uint16_t link_state; uint32_t references; char *tok, *t, *name; /* Get rid of pesky newline */ if ((t = strchr(line, '\n')) != NULL) { *t = '\0'; } if (strcmp(line, ":Name:LinkStatus:References:") == 0) { continue; } /* name */ //tok = strtok(line, ":"); /* Leading colon... */ tok = strtok(line, ":"); if (tok == NULL) { DEBUG(DEBUG_ERR, (__location__ " WARNING, bad line ignored \"%s\"\n", line)); continue; } name = tok; /* link_state */ tok = strtok(NULL, ":"); if (tok == NULL) { DEBUG(DEBUG_ERR, (__location__ " WARNING, bad line ignored \"%s\"\n", line)); continue; } link_state = (uint16_t)strtoul(tok, NULL, 0); /* references... */ tok = strtok(NULL, ":"); if (tok == NULL) { DEBUG(DEBUG_ERR, (__location__ " WARNING, bad line ignored \"%s\"\n", line)); continue; } references = (uint32_t)strtoul(tok, NULL, 0); iface = talloc_zero(ctdb, struct ctdb_iface); if (iface == NULL) { DEBUG(DEBUG_ERR, ("OOM allocating iface\n")); exit (1); } iface->name = talloc_strdup(iface, name); iface->link_up = link_state; iface->references = references; DLIST_ADD(ctdb->ifaces, iface); } } #ifdef CTDB_TEST_OVERRIDE_MAIN static void ctdb_test_stubs_print_ifaces(struct ctdb_context *ctdb) { struct ctdb_iface *iface; printf(":Name:LinkStatus:References:\n"); for (iface = ctdb->ifaces; iface != NULL; iface = iface->next) { printf(":%s:%u:%u:\n", iface->name, iface->link_up, iface->references); } } #endif /* Read vnn map. * output: * * * * ... */ /* struct ctdb_vnn_map { uint32_t generation; uint32_t size; uint32_t *map; }; */ static void ctdb_test_stubs_read_vnnmap(struct ctdb_context *ctdb) { char line[1024]; TALLOC_FREE(ctdb->vnn_map); ctdb->vnn_map = talloc_zero(ctdb, struct ctdb_vnn_map); if (ctdb->vnn_map == NULL) { DEBUG(DEBUG_ERR, ("OOM allocating vnnmap\n")); exit (1); } ctdb->vnn_map->generation = INVALID_GENERATION; ctdb->vnn_map->size = 0; ctdb->vnn_map->map = NULL; while ((fgets(line, sizeof(line), stdin) != NULL) && (line[0] != '\n')) { uint32_t n; char *t; /* Get rid of pesky newline */ if ((t = strchr(line, '\n')) != NULL) { *t = '\0'; } n = (uint32_t) strtol(line, NULL, 0); /* generation */ if (ctdb->vnn_map->generation == INVALID_GENERATION) { ctdb->vnn_map->generation = n; continue; } ctdb->vnn_map->map = talloc_realloc(ctdb, ctdb->vnn_map->map, uint32_t, ctdb->vnn_map->size + 1); if (ctdb->vnn_map->map == NULL) { DEBUG(DEBUG_ERR, ("OOM allocating vnn_map->map\n")); exit (1); } ctdb->vnn_map->map[ctdb->vnn_map->size] = n; ctdb->vnn_map->size++; } } #ifdef CTDB_TEST_OVERRIDE_MAIN static void ctdb_test_stubs_print_vnnmap(struct ctdb_context *ctdb) { int i; printf("%d\n", ctdb->vnn_map->generation); for (i = 0; i < ctdb->vnn_map->size; i++) { printf("%d\n", ctdb->vnn_map->map[i]); } } #endif static void ctdb_test_stubs_fake_setup(struct ctdb_context *ctdb) { char line[1024]; while (fgets(line, sizeof(line), stdin) != NULL) { char *t; /* Get rid of pesky newline */ if ((t = strchr(line, '\n')) != NULL) { *t = '\0'; } if (strcmp(line, "NODEMAP") == 0) { ctdb_test_stubs_read_nodemap(ctdb); } else if (strcmp(line, "IFACES") == 0) { ctdb_test_stubs_read_ifaces(ctdb); } else if (strcmp(line, "VNNMAP") == 0) { ctdb_test_stubs_read_vnnmap(ctdb); } else { printf("Unknown line %s\n", line); exit(1); } } } /* Support... */ static bool current_node_is_connected (struct ctdb_context *ctdb) { int i; for (i = 0; i < ctdb->num_nodes; i++) { if (ctdb->nodes[i]->pnn == ctdb->pnn) { if (ctdb->nodes[i]->flags & (NODE_FLAGS_DISCONNECTED | NODE_FLAGS_DELETED)) { return false; } else { return true; } } } /* Shouldn't really happen, so fag an error */ return false; } /* Stubs... */ struct ctdb_context *ctdb_cmdline_client_stub(struct tevent_context *ev, struct timeval req_timeout) { return ctdb_global; } struct tevent_context *tevent_context_init_stub(TALLOC_CTX *mem_ctx) { struct ctdb_context *ctdb; ctdb = talloc_zero(NULL, struct ctdb_context); ctdb_set_socketname(ctdb, "fake"); ctdb_test_stubs_fake_setup(ctdb); ctdb_global = ctdb; return tevent_context_init_byname(mem_ctx, NULL); } /* Copied from ctdb_recover.c */ int ctdb_control_getnodemap(struct ctdb_context *ctdb, uint32_t opcode, TDB_DATA indata, TDB_DATA *outdata) { uint32_t i, num_nodes; struct ctdb_node_map *node_map; CHECK_CONTROL_DATA_SIZE(0); num_nodes = ctdb->num_nodes; outdata->dsize = offsetof(struct ctdb_node_map, nodes) + num_nodes*sizeof(struct ctdb_node_and_flags); outdata->dptr = (unsigned char *)talloc_zero_size(outdata, outdata->dsize); if (!outdata->dptr) { DEBUG(DEBUG_ALERT, (__location__ " Failed to allocate nodemap array\n")); exit(1); } node_map = (struct ctdb_node_map *)outdata->dptr; node_map->num = num_nodes; for (i=0; inodes[i]->address.address, NULL, /* TODO: pass in the correct interface here*/ 0, &node_map->nodes[i].addr) == 0) { DEBUG(DEBUG_ERR, (__location__ " Failed to parse %s into a sockaddr\n", ctdb->nodes[i]->address.address)); } node_map->nodes[i].pnn = ctdb->nodes[i]->pnn; node_map->nodes[i].flags = ctdb->nodes[i]->flags; } return 0; } int ctdb_ctrl_getnodemap_stub(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode, TALLOC_CTX *mem_ctx, struct ctdb_node_map **nodemap) { int ret; TDB_DATA indata; TDB_DATA *outdata; if (!current_node_is_connected(ctdb)) { return -1; } indata.dsize = 0; indata.dptr = NULL; outdata = talloc_zero(ctdb, TDB_DATA); ret = ctdb_control_getnodemap(ctdb, CTDB_CONTROL_GET_NODEMAP, indata, outdata); if (ret == 0) { *nodemap = (struct ctdb_node_map *) outdata->dptr; } return ret; } int ctdb_ctrl_getvnnmap_stub(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode, TALLOC_CTX *mem_ctx, struct ctdb_vnn_map **vnnmap) { *vnnmap = talloc(ctdb, struct ctdb_vnn_map); if (*vnnmap == NULL) { DEBUG(DEBUG_ERR, (__location__ "OOM\n")); exit (1); } (*vnnmap)->map = talloc_array(*vnnmap, uint32_t, ctdb->vnn_map->size); (*vnnmap)->generation = ctdb->vnn_map->generation; (*vnnmap)->size = ctdb->vnn_map->size; memcpy((*vnnmap)->map, ctdb->vnn_map->map, sizeof(uint32_t) * (*vnnmap)->size); return 0; } int ctdb_ctrl_getrecmode_stub(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx, struct timeval timeout, uint32_t destnode, uint32_t *recmode) { *recmode = ctdb->recovery_mode; return 0; } int ctdb_ctrl_getrecmaster_stub(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx, struct timeval timeout, uint32_t destnode, uint32_t *recmaster) { *recmaster = ctdb->recovery_master; return 0; } int ctdb_ctrl_getpnn_stub(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode) { if (!current_node_is_connected(ctdb)) { return -1; } if (destnode == CTDB_CURRENT_NODE) { return ctdb->pnn; } else { return destnode; } } /* From ctdb_takeover.c */ int32_t ctdb_control_get_ifaces(struct ctdb_context *ctdb, struct ctdb_req_control *c, TDB_DATA *outdata) { int i, num, len; struct ctdb_control_get_ifaces *ifaces; struct ctdb_iface *cur; /* count how many public ip structures we have */ num = 0; for (cur=ctdb->ifaces;cur;cur=cur->next) { num++; } len = offsetof(struct ctdb_control_get_ifaces, ifaces) + num*sizeof(struct ctdb_control_iface_info); ifaces = talloc_zero_size(outdata, len); CTDB_NO_MEMORY(ctdb, ifaces); i = 0; for (cur=ctdb->ifaces;cur;cur=cur->next) { strcpy(ifaces->ifaces[i].name, cur->name); ifaces->ifaces[i].link_state = cur->link_up; ifaces->ifaces[i].references = cur->references; i++; } ifaces->num = i; len = offsetof(struct ctdb_control_get_ifaces, ifaces) + i*sizeof(struct ctdb_control_iface_info); outdata->dsize = len; outdata->dptr = (uint8_t *)ifaces; return 0; } int ctdb_ctrl_get_ifaces_stub(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode, TALLOC_CTX *mem_ctx, struct ctdb_control_get_ifaces **ifaces) { TDB_DATA *outdata; int ret; if (!current_node_is_connected(ctdb)) { return -1; } outdata = talloc(mem_ctx, TDB_DATA); ret = ctdb_control_get_ifaces(ctdb, NULL, outdata); if (ret == 0) { *ifaces = (struct ctdb_control_get_ifaces *)outdata->dptr; } return ret; } int ctdb_client_check_message_handlers_stub(struct ctdb_context *ctdb, uint64_t *ids, uint32_t num, uint8_t *result) { DEBUG(DEBUG_ERR, (__location__ " NOT IMPLEMENTED\n")); return -1; } int ctdb_ctrl_getcapabilities_stub(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode, uint32_t *capabilities) { if (ctdb->nodes[destnode]->flags & NODE_FLAGS_FAKE_TIMEOUT) { /* Placeholder for line#, instead of __location__ */ DEBUG(DEBUG_ERR, ("__LOCATION__ control timed out." " reqid:1234567890 opcode:80 dstnode:%d\n", destnode)); DEBUG(DEBUG_ERR, ("__LOCATION__ ctdb_control_recv failed\n")); DEBUG(DEBUG_ERR, ("__LOCATION__ ctdb_ctrl_getcapabilities_recv failed\n")); return -1; } if (ctdb->nodes[destnode]->flags & NODE_FLAGS_DISCONNECTED) { DEBUG(DEBUG_ERR, ("ctdb_control error: 'ctdb_control to disconnected node\n")); /* Placeholder for line#, instead of __location__ */ DEBUG(DEBUG_ERR, ("__LOCATION__ ctdb_ctrl_getcapabilities_recv failed\n")); return -1; } *capabilities = ctdb->nodes[destnode]->capabilities; return 0; } /* This is to support testing ctdb xpnn */ bool ctdb_sys_have_ip_stub(ctdb_sock_addr *addr) { int i; struct ctdb_context *ctdb = ctdb_global; for (i = 0; i < ctdb->num_nodes; i++) { ctdb_sock_addr node_addr; if (ctdb->pnn == ctdb->nodes[i]->pnn) { if (!parse_ip(ctdb->nodes[i]->address.address, NULL, 0, &node_addr)) { continue; } if (ctdb_same_ip(addr, &node_addr)) { return true; } } } return false; }