/* ctdb control tool Copyright (C) Andrew Tridgell 2007 This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "includes.h" #include "lib/events/events.h" #include "system/filesys.h" #include "popt.h" #include "cmdline.h" #include "../include/ctdb.h" #include "../include/ctdb_private.h" /* show usage message */ static void usage(void) { printf( "Usage: ctdb_control [options] \n" "\nControls:\n" " ping\n" " process-exists see if a process exists\n" " status show ctdb status on a node\n" " statusreset reset status on a node\n" " debug set ctdb debug level on a node\n" " debuglevel display ctdb debug levels\n" " getvnnmap display ctdb vnnmap\n" " setvnnmap *\n" " getdbmap lists databases on a node\n" " getnodemap lists nodes known to a ctdb daemon\n" " createdb create a database\n" " catdb lists all keys/data in a db\n" " cpdb lists all keys in a remote tdb\n" " setdmaster sets new dmaster for all records in the database\n" " cleardb deletes all records in a db\n" " getrecmode get recovery mode\n" " setrecmode set recovery mode\n" " writerecord \n" " recover recover the cluster\n" " attach attach a database\n"); exit(1); } /* see if a process exists */ static int control_process_exists(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t vnn, pid; int ret; if (argc < 1) { usage(); } if (sscanf(argv[0], "%u:%u", &vnn, &pid) != 2) { printf("Badly formed vnn:pid\n"); return -1; } ret = ctdb_ctrl_process_exists(ctdb, vnn, pid); if (ret == 0) { printf("%u:%u exists\n", vnn, pid); } else { printf("%u:%u does not exist\n", vnn, pid); } return ret; } /* display status structure */ static void show_status(struct ctdb_status *s) { printf("CTDB version %u\n", CTDB_VERSION); printf(" client_packets_sent %u\n", s->client_packets_sent); printf(" client_packets_recv %u\n", s->client_packets_recv); printf(" req_call %u\n", s->client.req_call); printf(" req_message %u\n", s->client.req_message); printf(" req_finished %u\n", s->client.req_finished); printf(" req_connect_wait %u\n", s->client.req_connect_wait); printf(" req_shutdown %u\n", s->client.req_shutdown); printf(" req_control %u\n", s->client.req_control); printf(" node_packets_sent %u\n", s->node_packets_sent); printf(" node_packets_recv %u\n", s->node_packets_recv); printf(" req_call %u\n", s->count.req_call); printf(" reply_call %u\n", s->count.reply_call); printf(" req_dmaster %u\n", s->count.req_dmaster); printf(" reply_dmaster %u\n", s->count.reply_dmaster); printf(" reply_error %u\n", s->count.reply_error); printf(" req_message %u\n", s->count.req_message); printf(" req_finished %u\n", s->count.req_finished); printf(" total_calls %u\n", s->total_calls); printf(" pending_calls %u\n", s->pending_calls); printf(" lockwait_calls %u\n", s->lockwait_calls); printf(" traverse_calls %u\n", s->traverse_calls); printf(" pending_lockwait_calls %u\n", s->pending_lockwait_calls); printf(" max_hop_count %u\n", s->max_hop_count); printf(" max_call_latency %.6f sec\n", s->max_call_latency); printf(" max_lockwait_latency %.6f sec\n", s->max_lockwait_latency); } /* display remote ctdb status combined from all nodes */ static int control_status_all(struct ctdb_context *ctdb) { int ret, i; struct ctdb_status status; uint32_t *nodes; uint32_t num_nodes; nodes = ctdb_get_connected_nodes(ctdb, timeval_current_ofs(1, 0), ctdb, &num_nodes); CTDB_NO_MEMORY(ctdb, nodes); ZERO_STRUCT(status); for (i=0;inum; i++) { if (nodemap->nodes[i].flags&NODE_FLAGS_CONNECTED) { num_nodes++; } } printf("number of active nodes:%d\n",num_nodes); /* 3: go to all active nodes and activate recovery mode */ printf("\n3: set recovery mode for all active nodes\n"); for (j=0; jnum; j++) { /* dont change it for nodes that are unavailable */ if (!(nodemap->nodes[j].flags&NODE_FLAGS_CONNECTED)) { continue; } printf("setting node %d to recovery mode\n",nodemap->nodes[j].vnn); ret = ctdb_ctrl_setrecmode(ctdb, nodemap->nodes[j].vnn, CTDB_RECOVERY_ACTIVE); if (ret != 0) { printf("Unable to set recmode on node %u\n", nodemap->nodes[j].vnn); return ret; } } /* 4: get a list of all databases */ printf("\n4: getting list of databases to recover\n"); ret = ctdb_ctrl_getdbmap(ctdb, vnn, ctdb, &dbmap); if (ret != 0) { printf("Unable to get dbids from node %u\n", vnn); return ret; } for (i=0;inum;i++) { const char *path; ctdb_ctrl_getdbpath(ctdb, dbmap->dbids[i], ctdb, &path); printf("dbid:0x%08x path:%s\n", dbmap->dbids[i], path); } /* 5: pull all records from all other nodes across to this node (this merges based on rsn internally) */ printf("\n5: merge all records from remote nodes\n"); for (i=0;inum;i++) { printf("recovering database 0x%08x\n",dbmap->dbids[i]); for (j=0; jnum; j++) { /* we dont need to merge with ourselves */ if (nodemap->nodes[j].vnn == vnn) { continue; } /* dont merge from nodes that are unavailable */ if (!(nodemap->nodes[j].flags&NODE_FLAGS_CONNECTED)) { continue; } printf("merging all records from node %d for database 0x%08x\n", nodemap->nodes[j].vnn, dbmap->dbids[i]); ret = ctdb_ctrl_copydb(ctdb, nodemap->nodes[j].vnn, vnn, dbmap->dbids[i], CTDB_LMASTER_ANY, ctdb); if (ret != 0) { printf("Unable to copy db from node %u to node %u\n", nodemap->nodes[j].vnn, vnn); return ret; } } } /* 6: update dmaster to point to this node for all databases/nodes */ printf("\n6: repoint dmaster to the recovery node\n"); dmaster = vnn; printf("new dmaster is %d\n", dmaster); for (i=0;inum;i++) { for (j=0; jnum; j++) { /* dont repoint nodes that are unavailable */ if (!(nodemap->nodes[j].flags&NODE_FLAGS_CONNECTED)) { continue; } printf("setting dmaster to %d for node %d db 0x%08x\n",dmaster,nodemap->nodes[j].vnn,dbmap->dbids[i]); ret = ctdb_ctrl_setdmaster(ctdb, nodemap->nodes[j].vnn, ctdb, dbmap->dbids[i], dmaster); if (ret != 0) { printf("Unable to set dmaster for node %u db:0x%08x\n", nodemap->nodes[j].vnn, dbmap->dbids[i]); return ret; } } } /* 7: push all records out to the nodes again */ printf("\n7: push all records to remote nodes\n"); for (i=0;inum;i++) { printf("distributing new database 0x%08x\n",dbmap->dbids[i]); for (j=0; jnum; j++) { /* we dont need to push to ourselves */ if (nodemap->nodes[j].vnn == vnn) { continue; } /* dont push to nodes that are unavailable */ if (!(nodemap->nodes[j].flags&NODE_FLAGS_CONNECTED)) { continue; } printf("pushing all records to node %d for database 0x%08x\n", nodemap->nodes[j].vnn, dbmap->dbids[i]); ret = ctdb_ctrl_copydb(ctdb, vnn, nodemap->nodes[j].vnn, dbmap->dbids[i], CTDB_LMASTER_ANY, ctdb); if (ret != 0) { printf("Unable to copy db from node %u to node %u\n", vnn, nodemap->nodes[j].vnn); return ret; } } } /* 8: build a new vnn map */ printf("\n8: build a new vnn map with a new generation id\n"); vnnmap = talloc_zero_size(ctdb, offsetof(struct ctdb_vnn_map, map) + 4*num_nodes); if (vnnmap == NULL) { DEBUG(0,(__location__ " Unable to allocate vnn_map structure\n")); exit(1); } generation = random(); vnnmap->generation = generation; vnnmap->size = num_nodes; for (i=j=0;inum;i++) { if (nodemap->nodes[i].flags&NODE_FLAGS_CONNECTED) { vnnmap->map[j++]=nodemap->nodes[i].vnn; } } printf("Generation:%d\n",vnnmap->generation); printf("Size:%d\n",vnnmap->size); for(i=0;isize;i++){ printf("hash:%d lmaster:%d\n",i,vnnmap->map[i]); } /* 9: push the new vnn map out to all the nodes */ printf("\n9: distribute the new vnn map\n"); for (j=0; jnum; j++) { /* dont push to nodes that are unavailable */ if (!(nodemap->nodes[j].flags&NODE_FLAGS_CONNECTED)) { continue; } printf("setting new vnn map on node %d\n",nodemap->nodes[j].vnn); ret = ctdb_ctrl_setvnnmap(ctdb, nodemap->nodes[j].vnn, ctdb, vnnmap); if (ret != 0) { printf("Unable to set vnnmap for node %u\n", vnn); return ret; } } /* 10: disable recovery mode */ printf("\n10: restore recovery mode back to normal\n"); for (j=0; jnum; j++) { /* dont push to nodes that are unavailable */ if (!(nodemap->nodes[j].flags&NODE_FLAGS_CONNECTED)) { continue; } printf("changing recovery mode back to normal for node %d\n",nodemap->nodes[j].vnn); ret = ctdb_ctrl_setrecmode(ctdb, nodemap->nodes[j].vnn, CTDB_RECOVERY_NORMAL); if (ret != 0) { printf("Unable to set recmode on node %u\n", nodemap->nodes[j].vnn); return ret; } } return 0; } /* display remote ctdb vnn map */ static int control_getvnnmap(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t vnn; int i, ret; struct ctdb_vnn_map *vnnmap=NULL; if (argc < 1) { usage(); } vnn = strtoul(argv[0], NULL, 0); ret = ctdb_ctrl_getvnnmap(ctdb, timeval_current_ofs(1, 0), vnn, ctdb, &vnnmap); if (ret != 0) { printf("Unable to get vnnmap from node %u\n", vnn); return ret; } printf("Generation:%d\n",vnnmap->generation); printf("Size:%d\n",vnnmap->size); for(i=0;isize;i++){ printf("hash:%d lmaster:%d\n",i,vnnmap->map[i]); } return 0; } /* display recovery mode of a remote node */ static int control_getrecmode(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t vnn, recmode; int ret; if (argc < 1) { usage(); } vnn = strtoul(argv[0], NULL, 0); ret = ctdb_ctrl_getrecmode(ctdb, vnn, &recmode); if (ret != 0) { printf("Unable to get recmode from node %u\n", vnn); return ret; } printf("Recovery mode:%s (%d)\n",recmode==CTDB_RECOVERY_NORMAL?"NORMAL":"RECOVERY",recmode); return 0; } /* set recovery mode of a remote node */ static int control_setrecmode(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t vnn, recmode; int ret; if (argc < 2) { usage(); } vnn = strtoul(argv[0], NULL, 0); recmode = strtoul(argv[0], NULL, 0); ret = ctdb_ctrl_setrecmode(ctdb, vnn, recmode); if (ret != 0) { printf("Unable to set recmode on node %u\n", vnn); return ret; } return 0; } /* display remote list of keys/data for a db */ static int control_catdb(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t dbid; struct ctdb_db_context *ctdb_db; int ret; if (argc < 1) { usage(); } dbid = strtoul(argv[0], NULL, 0); ctdb_db = find_ctdb_db(ctdb, dbid); if (ctdb_db == NULL) { printf("Unable to find database 0x%x\n", dbid); return -1; } ret = ctdb_dump_db(ctdb_db, stdout); if (ret == -1) { printf("Unable to dump database\n"); return -1; } printf("Dumped %d records\n", ret); return 0; } /* copy a db from one node to another */ static int control_cpdb(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t fromvnn, tovnn, dbid; int ret; TALLOC_CTX *mem_ctx; if (argc < 3) { usage(); } fromvnn = strtoul(argv[0], NULL, 0); tovnn = strtoul(argv[1], NULL, 0); dbid = strtoul(argv[2], NULL, 0); mem_ctx = talloc_new(ctdb); ret = ctdb_ctrl_copydb(ctdb, fromvnn, tovnn, dbid, CTDB_LMASTER_ANY, mem_ctx); if (ret != 0) { printf("Unable to copy db from node %u to node %u\n", fromvnn, tovnn); return ret; } talloc_free(mem_ctx); return 0; } /* display a list of the databases on a remote ctdb */ static int control_getdbmap(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t vnn; int i, ret; struct ctdb_dbid_map *dbmap=NULL; if (argc < 1) { usage(); } vnn = strtoul(argv[0], NULL, 0); ret = ctdb_ctrl_getdbmap(ctdb, vnn, ctdb, &dbmap); if (ret != 0) { printf("Unable to get dbids from node %u\n", vnn); return ret; } printf("Number of databases:%d\n", dbmap->num); for(i=0;inum;i++){ const char *path; ctdb_ctrl_getdbpath(ctdb, dbmap->dbids[i], ctdb, &path); printf("dbid:0x%08x path:%s\n", dbmap->dbids[i], path); } return 0; } /* display a list nodes known to a remote ctdb */ static int control_getnodemap(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t vnn; int i, ret; struct ctdb_node_map *nodemap=NULL; if (argc < 1) { usage(); } vnn = strtoul(argv[0], NULL, 0); ret = ctdb_ctrl_getnodemap(ctdb, timeval_current_ofs(1, 0), vnn, ctdb, &nodemap); if (ret != 0) { printf("Unable to get nodemap from node %u\n", vnn); return ret; } printf("Number of nodes:%d\n", nodemap->num); for(i=0;inum;i++){ printf("vnn:%d %s%s\n", nodemap->nodes[i].vnn, nodemap->nodes[i].flags&NODE_FLAGS_CONNECTED? "CONNECTED":"UNAVAILABLE", nodemap->nodes[i].vnn==vnn?" (THIS NODE)":""); } return 0; } /* set remote ctdb vnn map */ static int control_setvnnmap(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t vnn, num_nodes, generation; struct ctdb_vnn_map *vnnmap; int i, ret; if (argc < 3) { usage(); } vnn = strtoul(argv[0], NULL, 0); generation = strtoul(argv[1], NULL, 0); num_nodes = strtoul(argv[2], NULL, 0); vnnmap = talloc_zero_size(ctdb, offsetof(struct ctdb_vnn_map, map) + 4*num_nodes); if (vnnmap == NULL) { DEBUG(0,(__location__ " Unable to allocate vnn_map structure\n")); exit(1); } vnnmap->generation = generation; vnnmap->size = num_nodes; for (i=0;isize;i++) { vnnmap->map[i] = strtoul(argv[3+i], NULL, 0); } ret = ctdb_ctrl_setvnnmap(ctdb, vnn, ctdb, vnnmap); if (ret != 0) { printf("Unable to set vnnmap for node %u\n", vnn); return ret; } return 0; } /* write a record to a remote tdb */ static int control_writerecord(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t vnn, dbid; TDB_DATA key, data; int ret; if (argc < 4) { usage(); } vnn = strtoul(argv[0], NULL, 0); dbid = strtoul(argv[1], NULL, 0); key.dptr = discard_const(argv[2]); key.dsize = strlen((const char *)(key.dptr)); data.dptr = discard_const(argv[3]); data.dsize = strlen((const char *)(data.dptr)); ret = ctdb_ctrl_write_record(ctdb, vnn, ctdb, dbid, key, data); if (ret != 0) { printf("Unable to set vnnmap for node %u\n", vnn); return ret; } return 0; } /* set the dmaster for all records in a database */ static int control_setdmaster(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t vnn, dbid, dmaster; int ret; if (argc < 3) { usage(); } vnn = strtoul(argv[0], NULL, 0); dbid = strtoul(argv[1], NULL, 0); dmaster = strtoul(argv[2], NULL, 0); ret = ctdb_ctrl_setdmaster(ctdb, vnn, ctdb, dbid, dmaster); if (ret != 0) { printf("Unable to set dmaster for node %u db:0x%08x\n", vnn, dbid); return ret; } return 0; } /* clears a database */ static int control_cleardb(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t vnn, dbid; int ret; if (argc < 2) { usage(); } vnn = strtoul(argv[0], NULL, 0); dbid = strtoul(argv[1], NULL, 0); ret = ctdb_ctrl_cleardb(ctdb, vnn, ctdb, dbid); if (ret != 0) { printf("Unable to clear db for node %u db:0x%08x\n", vnn, dbid); return ret; } return 0; } /* create a database */ static int control_createdb(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t vnn; const char *dbname; int ret; int32_t res; TDB_DATA data; struct timeval timeout; if (argc < 2) { usage(); } vnn = strtoul(argv[0], NULL, 0); dbname = argv[1]; /* tell ctdb daemon to attach */ data.dptr = discard_const(dbname); data.dsize = strlen(dbname)+1; timeout = timeval_current_ofs(1, 0); ret = ctdb_control(ctdb, vnn, 0, CTDB_CONTROL_DB_ATTACH, 0, data, ctdb, &data, &res, &timeout); if (ret != 0 || res != 0 || data.dsize != sizeof(uint32_t)) { DEBUG(0,("Failed to attach to database '%s'\n", dbname)); return -1; } return 0; } /* ping all node */ static int control_ping(struct ctdb_context *ctdb, int argc, const char **argv) { int ret, i; uint32_t *nodes; uint32_t num_nodes; nodes = ctdb_get_connected_nodes(ctdb, timeval_current_ofs(1, 0), ctdb, &num_nodes); CTDB_NO_MEMORY(ctdb, nodes); for (i=0;i