/* ctdb control tool Copyright (C) Andrew Tridgell 2007 Copyright (C) Ronnie Sahlberg 2007 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 . */ #include "includes.h" #include "lib/tevent/tevent.h" #include "system/time.h" #include "system/filesys.h" #include "system/network.h" #include "system/locale.h" #include "popt.h" #include "cmdline.h" #include "../include/ctdb.h" #include "../include/ctdb_client.h" #include "../include/ctdb_private.h" #include "../common/rb_tree.h" #include "db_wrap.h" #define ERR_TIMEOUT 20 /* timed out trying to reach node */ #define ERR_NONODE 21 /* node does not exist */ #define ERR_DISNODE 22 /* node is disconnected */ struct ctdb_connection *ctdb_connection; static void usage(void); static struct { int timelimit; uint32_t pnn; uint32_t *nodes; int machinereadable; int verbose; int maxruntime; int printemptyrecords; int printdatasize; int printlmaster; int printhash; int printrecordflags; } options; #define TIMELIMIT() timeval_current_ofs(options.timelimit, 0) #define LONGTIMELIMIT() timeval_current_ofs(options.timelimit*10, 0) #ifdef CTDB_VERS static int control_version(struct ctdb_context *ctdb, int argc, const char **argv) { #define STR(x) #x #define XSTR(x) STR(x) printf("CTDB version: %s\n", XSTR(CTDB_VERS)); return 0; } #endif #define CTDB_NOMEM_ABORT(p) do { if (!(p)) { \ DEBUG(DEBUG_ALERT,("ctdb fatal error: %s\n", \ "Out of memory in " __location__ )); \ abort(); \ }} while (0) /* Pretty print the flags to a static buffer in human-readable format. * This never returns NULL! */ static const char *pretty_print_flags(uint32_t flags) { int j; static const struct { uint32_t flag; const char *name; } flag_names[] = { { NODE_FLAGS_DISCONNECTED, "DISCONNECTED" }, { NODE_FLAGS_PERMANENTLY_DISABLED, "DISABLED" }, { NODE_FLAGS_BANNED, "BANNED" }, { NODE_FLAGS_UNHEALTHY, "UNHEALTHY" }, { NODE_FLAGS_DELETED, "DELETED" }, { NODE_FLAGS_STOPPED, "STOPPED" }, { NODE_FLAGS_INACTIVE, "INACTIVE" }, }; static char flags_str[512]; /* Big enough to contain all flag names */ flags_str[0] = '\0'; for (j=0;j= 'a' && h <= 'f') return h - 'a' + 10; if (h >= 'A' && h <= 'F') return h - 'f' + 10; return h - '0'; } static TDB_DATA hextodata(TALLOC_CTX *mem_ctx, const char *str) { int i, len; TDB_DATA key = {NULL, 0}; len = strlen(str); if (len & 0x01) { DEBUG(DEBUG_ERR,("Key specified with odd number of hexadecimal digits\n")); return key; } key.dsize = len>>1; key.dptr = talloc_size(mem_ctx, key.dsize); for (i=0; i < len/2; i++) { key.dptr[i] = h2i(str[i*2]) << 4 | h2i(str[i*2+1]); } return key; } /* Parse a nodestring. Parameter dd_ok controls what happens to nodes * that are disconnected or deleted. If dd_ok is true those nodes are * included in the output list of nodes. If dd_ok is false, those * nodes are filtered from the "all" case and cause an error if * explicitly specified. */ static bool parse_nodestring(struct ctdb_context *ctdb, const char * nodestring, uint32_t current_pnn, bool dd_ok, uint32_t **nodes, uint32_t *pnn_mode) { int n; uint32_t i; struct ctdb_node_map *nodemap; *nodes = NULL; if (!ctdb_getnodemap(ctdb_connection, CTDB_CURRENT_NODE, &nodemap)) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n")); exit(10); } if (nodestring != NULL) { *nodes = talloc_array(ctdb, uint32_t, 0); CTDB_NOMEM_ABORT(*nodes); n = 0; if (strcmp(nodestring, "all") == 0) { *pnn_mode = CTDB_BROADCAST_ALL; /* all */ for (i = 0; i < nodemap->num; i++) { if ((nodemap->nodes[i].flags & (NODE_FLAGS_DISCONNECTED | NODE_FLAGS_DELETED)) && !dd_ok) { continue; } *nodes = talloc_realloc(ctdb, *nodes, uint32_t, n+1); CTDB_NOMEM_ABORT(*nodes); (*nodes)[n] = i; n++; } } else { /* x{,y...} */ char *ns, *tok; ns = talloc_strdup(ctdb, nodestring); tok = strtok(ns, ","); while (tok != NULL) { uint32_t pnn; i = (uint32_t)strtoul(tok, NULL, 0); if (i >= nodemap->num) { DEBUG(DEBUG_ERR, ("Node %u does not exist\n", i)); exit(ERR_NONODE); } if ((nodemap->nodes[i].flags & (NODE_FLAGS_DISCONNECTED | NODE_FLAGS_DELETED)) && !dd_ok) { DEBUG(DEBUG_ERR, ("Node %u has status %s\n", i, pretty_print_flags(nodemap->nodes[i].flags))); exit(ERR_DISNODE); } if (!ctdb_getpnn(ctdb_connection, i, &pnn)) { DEBUG(DEBUG_ERR, ("Can not access node %u. Node is not operational.\n", i)); exit(10); } *nodes = talloc_realloc(ctdb, *nodes, uint32_t, n+1); CTDB_NOMEM_ABORT(*nodes); (*nodes)[n] = i; n++; tok = strtok(NULL, ","); } talloc_free(ns); if (n == 1) { *pnn_mode = (*nodes)[0]; } else { *pnn_mode = CTDB_MULTICAST; } } } else { /* default - no nodes specified */ *nodes = talloc_array(ctdb, uint32_t, 1); CTDB_NOMEM_ABORT(*nodes); *pnn_mode = CTDB_CURRENT_NODE; if (!ctdb_getpnn(ctdb_connection, current_pnn, &((*nodes)[0]))) { return false; } } ctdb_free_nodemap(nodemap); return true; } /* check if a database exists */ static int db_exists(struct ctdb_context *ctdb, const char *db_name, bool *persistent) { int i, ret; struct ctdb_dbid_map *dbmap=NULL; ret = ctdb_ctrl_getdbmap(ctdb, TIMELIMIT(), options.pnn, ctdb, &dbmap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get dbids from node %u\n", options.pnn)); return -1; } for(i=0;inum;i++){ const char *name; ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &name); if (!strcmp(name, db_name)) { if (persistent) { *persistent = dbmap->dbs[i].flags & CTDB_DB_FLAGS_PERSISTENT; } return 0; } } return -1; } /* see if a process exists */ static int control_process_exists(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t pnn, pid; int ret; if (argc < 1) { usage(); } if (sscanf(argv[0], "%u:%u", &pnn, &pid) != 2) { DEBUG(DEBUG_ERR, ("Badly formed pnn:pid\n")); return -1; } ret = ctdb_ctrl_process_exists(ctdb, pnn, pid); if (ret == 0) { printf("%u:%u exists\n", pnn, pid); } else { printf("%u:%u does not exist\n", pnn, pid); } return ret; } /* display statistics structure */ static void show_statistics(struct ctdb_statistics *s, int show_header) { TALLOC_CTX *tmp_ctx = talloc_new(NULL); int i; const char *prefix=NULL; int preflen=0; int tmp, days, hours, minutes, seconds; const struct { const char *name; uint32_t offset; } fields[] = { #define STATISTICS_FIELD(n) { #n, offsetof(struct ctdb_statistics, n) } STATISTICS_FIELD(num_clients), STATISTICS_FIELD(frozen), STATISTICS_FIELD(recovering), STATISTICS_FIELD(num_recoveries), STATISTICS_FIELD(client_packets_sent), STATISTICS_FIELD(client_packets_recv), STATISTICS_FIELD(node_packets_sent), STATISTICS_FIELD(node_packets_recv), STATISTICS_FIELD(keepalive_packets_sent), STATISTICS_FIELD(keepalive_packets_recv), STATISTICS_FIELD(node.req_call), STATISTICS_FIELD(node.reply_call), STATISTICS_FIELD(node.req_dmaster), STATISTICS_FIELD(node.reply_dmaster), STATISTICS_FIELD(node.reply_error), STATISTICS_FIELD(node.req_message), STATISTICS_FIELD(node.req_control), STATISTICS_FIELD(node.reply_control), STATISTICS_FIELD(client.req_call), STATISTICS_FIELD(client.req_message), STATISTICS_FIELD(client.req_control), STATISTICS_FIELD(timeouts.call), STATISTICS_FIELD(timeouts.control), STATISTICS_FIELD(timeouts.traverse), STATISTICS_FIELD(total_calls), STATISTICS_FIELD(pending_calls), STATISTICS_FIELD(lockwait_calls), STATISTICS_FIELD(pending_lockwait_calls), STATISTICS_FIELD(childwrite_calls), STATISTICS_FIELD(pending_childwrite_calls), STATISTICS_FIELD(memory_used), STATISTICS_FIELD(max_hop_count), STATISTICS_FIELD(total_ro_delegations), STATISTICS_FIELD(total_ro_revokes), }; tmp = s->statistics_current_time.tv_sec - s->statistics_start_time.tv_sec; seconds = tmp%60; tmp /= 60; minutes = tmp%60; tmp /= 60; hours = tmp%24; tmp /= 24; days = tmp; if (options.machinereadable){ if (show_header) { printf("CTDB version:"); printf("Current time of statistics:"); printf("Statistics collected since:"); for (i=0;istatistics_current_time.tv_sec); printf("%d:", (int)s->statistics_start_time.tv_sec); for (i=0;ireclock.ctdbd.num); printf("%.6f:", s->reclock.ctdbd.min); printf("%.6f:", s->reclock.ctdbd.num?s->reclock.ctdbd.total/s->reclock.ctdbd.num:0.0); printf("%.6f:", s->reclock.ctdbd.max); printf("%d:", s->reclock.recd.num); printf("%.6f:", s->reclock.recd.min); printf("%.6f:", s->reclock.recd.num?s->reclock.recd.total/s->reclock.recd.num:0.0); printf("%.6f:", s->reclock.recd.max); printf("%d:", s->call_latency.num); printf("%.6f:", s->call_latency.min); printf("%.6f:", s->call_latency.num?s->call_latency.total/s->call_latency.num:0.0); printf("%.6f:", s->call_latency.max); printf("%d:", s->lockwait_latency.num); printf("%.6f:", s->lockwait_latency.min); printf("%.6f:", s->lockwait_latency.num?s->lockwait_latency.total/s->lockwait_latency.num:0.0); printf("%.6f:", s->lockwait_latency.max); printf("%d:", s->childwrite_latency.num); printf("%.6f:", s->childwrite_latency.min); printf("%.6f:", s->childwrite_latency.num?s->childwrite_latency.total/s->childwrite_latency.num:0.0); printf("%.6f:", s->childwrite_latency.max); printf("\n"); } else { printf("CTDB version %u\n", CTDB_VERSION); printf("Current time of statistics : %s", ctime(&s->statistics_current_time.tv_sec)); printf("Statistics collected since : (%03d %02d:%02d:%02d) %s", days, hours, minutes, seconds, ctime(&s->statistics_start_time.tv_sec)); for (i=0;ihop_count_bucket[i]); } printf("\n"); printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n", "reclock_ctdbd MIN/AVG/MAX", s->reclock.ctdbd.min, s->reclock.ctdbd.num?s->reclock.ctdbd.total/s->reclock.ctdbd.num:0.0, s->reclock.ctdbd.max, s->reclock.ctdbd.num); printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n", "reclock_recd MIN/AVG/MAX", s->reclock.recd.min, s->reclock.recd.num?s->reclock.recd.total/s->reclock.recd.num:0.0, s->reclock.recd.max, s->reclock.recd.num); printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n", "call_latency MIN/AVG/MAX", s->call_latency.min, s->call_latency.num?s->call_latency.total/s->call_latency.num:0.0, s->call_latency.max, s->call_latency.num); printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n", "lockwait_latency MIN/AVG/MAX", s->lockwait_latency.min, s->lockwait_latency.num?s->lockwait_latency.total/s->lockwait_latency.num:0.0, s->lockwait_latency.max, s->lockwait_latency.num); printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n", "childwrite_latency MIN/AVG/MAX", s->childwrite_latency.min, s->childwrite_latency.num?s->childwrite_latency.total/s->childwrite_latency.num:0.0, s->childwrite_latency.max, s->childwrite_latency.num); } talloc_free(tmp_ctx); } /* display remote ctdb statistics combined from all nodes */ static int control_statistics_all(struct ctdb_context *ctdb) { int ret, i; struct ctdb_statistics statistics; uint32_t *nodes; uint32_t num_nodes; nodes = ctdb_get_connected_nodes(ctdb, TIMELIMIT(), ctdb, &num_nodes); CTDB_NO_MEMORY(ctdb, nodes); ZERO_STRUCT(statistics); for (i=0;inum;i++) { if (stats->stats[i].statistics_start_time.tv_sec == 0) { continue; } show_statistics(&stats->stats[i], i==0); if (i == num_records) { break; } } return 0; } /* display remote ctdb db statistics */ static int control_dbstatistics(struct ctdb_context *ctdb, int argc, const char **argv) { TALLOC_CTX *tmp_ctx = talloc_new(ctdb); struct ctdb_db_statistics *dbstatistics; struct ctdb_dbid_map *dbmap=NULL; int i, ret; if (argc < 1) { usage(); } ret = ctdb_ctrl_getdbmap(ctdb, TIMELIMIT(), options.pnn, tmp_ctx, &dbmap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get dbids from node %u\n", options.pnn)); return ret; } for(i=0;inum;i++){ const char *name; ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, tmp_ctx, &name); if(!strcmp(argv[0], name)){ talloc_free(discard_const(name)); break; } talloc_free(discard_const(name)); } if (i == dbmap->num) { DEBUG(DEBUG_ERR,("No database with name '%s' found\n", argv[0])); talloc_free(tmp_ctx); return -1; } if (!ctdb_getdbstat(ctdb_connection, options.pnn, dbmap->dbs[i].dbid, &dbstatistics)) { DEBUG(DEBUG_ERR,("Failed to read db statistics from node\n")); talloc_free(tmp_ctx); return -1; } printf("DB Statistics:\n"); printf("RO Delegations: %d\n", dbstatistics->db_ro_delegations); printf("RO Revokes: %d\n", dbstatistics->db_ro_revokes); printf(" hop_count_buckets:"); for (i=0;ihop_count_bucket[i]); } printf("\n"); ctdb_free_dbstat(dbstatistics); return 0; } /* display uptime of remote node */ static int control_uptime(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; struct ctdb_uptime *uptime = NULL; int tmp, days, hours, minutes, seconds; ret = ctdb_ctrl_uptime(ctdb, ctdb, TIMELIMIT(), options.pnn, &uptime); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get uptime from node %u\n", options.pnn)); return ret; } if (options.machinereadable){ printf(":Current Node Time:Ctdb Start Time:Last Recovery/Failover Time:Last Recovery/IPFailover Duration:\n"); printf(":%u:%u:%u:%lf\n", (unsigned int)uptime->current_time.tv_sec, (unsigned int)uptime->ctdbd_start_time.tv_sec, (unsigned int)uptime->last_recovery_finished.tv_sec, timeval_delta(&uptime->last_recovery_finished, &uptime->last_recovery_started) ); return 0; } printf("Current time of node : %s", ctime(&uptime->current_time.tv_sec)); tmp = uptime->current_time.tv_sec - uptime->ctdbd_start_time.tv_sec; seconds = tmp%60; tmp /= 60; minutes = tmp%60; tmp /= 60; hours = tmp%24; tmp /= 24; days = tmp; printf("Ctdbd start time : (%03d %02d:%02d:%02d) %s", days, hours, minutes, seconds, ctime(&uptime->ctdbd_start_time.tv_sec)); tmp = uptime->current_time.tv_sec - uptime->last_recovery_finished.tv_sec; seconds = tmp%60; tmp /= 60; minutes = tmp%60; tmp /= 60; hours = tmp%24; tmp /= 24; days = tmp; printf("Time of last recovery/failover: (%03d %02d:%02d:%02d) %s", days, hours, minutes, seconds, ctime(&uptime->last_recovery_finished.tv_sec)); printf("Duration of last recovery/failover: %lf seconds\n", timeval_delta(&uptime->last_recovery_finished, &uptime->last_recovery_started)); return 0; } /* show the PNN of the current node */ static int control_pnn(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t mypnn; bool ret; ret = ctdb_getpnn(ctdb_connection, options.pnn, &mypnn); if (!ret) { DEBUG(DEBUG_ERR, ("Unable to get pnn from node.")); return -1; } printf("PNN:%d\n", mypnn); return 0; } struct pnn_node { struct pnn_node *next; const char *addr; int pnn; }; static struct pnn_node *read_nodes_file(TALLOC_CTX *mem_ctx) { const char *nodes_list; int nlines; char **lines; int i, pnn; struct pnn_node *pnn_nodes = NULL; struct pnn_node *pnn_node; struct pnn_node *tmp_node; /* read the nodes file */ nodes_list = getenv("CTDB_NODES"); if (nodes_list == NULL) { nodes_list = "/etc/ctdb/nodes"; } lines = file_lines_load(nodes_list, &nlines, mem_ctx); if (lines == NULL) { return NULL; } while (nlines > 0 && strcmp(lines[nlines-1], "") == 0) { nlines--; } for (i=0, pnn=0; ipnn = pnn++; pnn_node->addr = talloc_strdup(pnn_node, node); pnn_node->next = pnn_nodes; pnn_nodes = pnn_node; } /* swap them around so we return them in incrementing order */ pnn_node = pnn_nodes; pnn_nodes = NULL; while (pnn_node) { tmp_node = pnn_node; pnn_node = pnn_node->next; tmp_node->next = pnn_nodes; pnn_nodes = tmp_node; } return pnn_nodes; } /* show the PNN of the current node discover the pnn by loading the nodes file and try to bind to all addresses one at a time until the ip address is found. */ static int control_xpnn(struct ctdb_context *ctdb, int argc, const char **argv) { TALLOC_CTX *mem_ctx = talloc_new(NULL); struct pnn_node *pnn_nodes; struct pnn_node *pnn_node; pnn_nodes = read_nodes_file(mem_ctx); if (pnn_nodes == NULL) { DEBUG(DEBUG_ERR,("Failed to read nodes file\n")); talloc_free(mem_ctx); return -1; } for(pnn_node=pnn_nodes;pnn_node;pnn_node=pnn_node->next) { ctdb_sock_addr addr; if (parse_ip(pnn_node->addr, NULL, 63999, &addr) == 0) { DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s' in nodes file\n", pnn_node->addr)); talloc_free(mem_ctx); return -1; } if (ctdb_sys_have_ip(&addr)) { printf("PNN:%d\n", pnn_node->pnn); talloc_free(mem_ctx); return 0; } } printf("Failed to detect which PNN this node is\n"); talloc_free(mem_ctx); return -1; } /* Helpers for ctdb status */ static bool is_partially_online(struct ctdb_node_and_flags *node) { int j; bool ret = false; if (node->flags == 0) { struct ctdb_ifaces_list *ifaces; if (ctdb_getifaces(ctdb_connection, node->pnn, &ifaces)) { for (j=0; j < ifaces->num; j++) { if (ifaces->ifaces[j].link_state != 0) { continue; } ret = true; break; } ctdb_free_ifaces(ifaces); } } return ret; } static int control_status_1_machine(int mypnn, struct ctdb_node_and_flags *node) { printf(":%d:%s:%d:%d:%d:%d:%d:%d:%d:%c:\n", node->pnn, ctdb_addr_to_str(&node->addr), !!(node->flags&NODE_FLAGS_DISCONNECTED), !!(node->flags&NODE_FLAGS_BANNED), !!(node->flags&NODE_FLAGS_PERMANENTLY_DISABLED), !!(node->flags&NODE_FLAGS_UNHEALTHY), !!(node->flags&NODE_FLAGS_STOPPED), !!(node->flags&NODE_FLAGS_INACTIVE), is_partially_online(node) ? 1 : 0, (node->pnn == mypnn)?'Y':'N'); return node->flags; } static int control_status_1_human(int mypnn, struct ctdb_node_and_flags *node) { printf("pnn:%d %-16s %s%s\n", node->pnn, ctdb_addr_to_str(&node->addr), is_partially_online(node) ? "PARTIALLYONLINE" : pretty_print_flags(node->flags), node->pnn == mypnn?" (THIS NODE)":""); return node->flags; } /* display remote ctdb status */ static int control_status(struct ctdb_context *ctdb, int argc, const char **argv) { int i; struct ctdb_vnn_map *vnnmap=NULL; struct ctdb_node_map *nodemap=NULL; uint32_t recmode, recmaster, mypnn; if (!ctdb_getpnn(ctdb_connection, options.pnn, &mypnn)) { return -1; } if (!ctdb_getnodemap(ctdb_connection, options.pnn, &nodemap)) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from node %u\n", options.pnn)); return -1; } if (options.machinereadable) { printf(":Node:IP:Disconnected:Banned:Disabled:Unhealthy:Stopped" ":Inactive:PartiallyOnline:ThisNode:\n"); for (i=0;inum;i++) { if (nodemap->nodes[i].flags & NODE_FLAGS_DELETED) { continue; } (void) control_status_1_machine(mypnn, &nodemap->nodes[i]); } return 0; } printf("Number of nodes:%d\n", nodemap->num); for(i=0;inum;i++){ if (nodemap->nodes[i].flags & NODE_FLAGS_DELETED) { continue; } (void) control_status_1_human(mypnn, &nodemap->nodes[i]); } if (!ctdb_getvnnmap(ctdb_connection, options.pnn, &vnnmap)) { DEBUG(DEBUG_ERR, ("Unable to get vnnmap from node %u\n", options.pnn)); return -1; } if (vnnmap->generation == INVALID_GENERATION) { printf("Generation:INVALID\n"); } else { 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]); } ctdb_free_vnnmap(vnnmap); if (!ctdb_getrecmode(ctdb_connection, options.pnn, &recmode)) { DEBUG(DEBUG_ERR, ("Unable to get recmode from node %u\n", options.pnn)); return -1; } printf("Recovery mode:%s (%d)\n",recmode==CTDB_RECOVERY_NORMAL?"NORMAL":"RECOVERY",recmode); if (!ctdb_getrecmaster(ctdb_connection, options.pnn, &recmaster)) { DEBUG(DEBUG_ERR, ("Unable to get recmaster from node %u\n", options.pnn)); return -1; } printf("Recovery master:%d\n",recmaster); return 0; } static int control_nodestatus(struct ctdb_context *ctdb, int argc, const char **argv) { int i, ret; struct ctdb_node_map *nodemap=NULL; uint32_t * nodes; uint32_t pnn_mode, mypnn; if (argc > 1) { usage(); } if (!parse_nodestring(ctdb, argc == 1 ? argv[0] : NULL, options.pnn, true, &nodes, &pnn_mode)) { return -1; } if (options.machinereadable) { printf(":Node:IP:Disconnected:Banned:Disabled:Unhealthy:Stopped" ":Inactive:PartiallyOnline:ThisNode:\n"); } else if (pnn_mode == CTDB_BROADCAST_ALL) { printf("Number of nodes:%d\n", (int) talloc_array_length(nodes)); } if (!ctdb_getpnn(ctdb_connection, options.pnn, &mypnn)) { DEBUG(DEBUG_ERR, ("Unable to get PNN from local node\n")); return -1; } if (!ctdb_getnodemap(ctdb_connection, options.pnn, &nodemap)) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from node %u\n", options.pnn)); return -1; } ret = 0; for (i = 0; i < talloc_array_length(nodes); i++) { if (options.machinereadable) { ret |= control_status_1_machine(mypnn, &nodemap->nodes[nodes[i]]); } else { ret |= control_status_1_human(mypnn, &nodemap->nodes[nodes[i]]); } } return ret; } struct natgw_node { struct natgw_node *next; const char *addr; }; /* display the list of nodes belonging to this natgw configuration */ static int control_natgwlist(struct ctdb_context *ctdb, int argc, const char **argv) { int i, ret; uint32_t capabilities; const char *natgw_list; int nlines; char **lines; struct natgw_node *natgw_nodes = NULL; struct natgw_node *natgw_node; struct ctdb_node_map *nodemap=NULL; /* read the natgw nodes file into a linked list */ natgw_list = getenv("NATGW_NODES"); if (natgw_list == NULL) { natgw_list = "/etc/ctdb/natgw_nodes"; } lines = file_lines_load(natgw_list, &nlines, ctdb); if (lines == NULL) { ctdb_set_error(ctdb, "Failed to load natgw node list '%s'\n", natgw_list); return -1; } while (nlines > 0 && strcmp(lines[nlines-1], "") == 0) { nlines--; } for (i=0;iaddr = talloc_strdup(natgw_node, node); CTDB_NO_MEMORY(ctdb, natgw_node->addr); natgw_node->next = natgw_nodes; natgw_nodes = natgw_node; } ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, ctdb, &nodemap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node.\n")); return ret; } i=0; while(inum) { for(natgw_node=natgw_nodes;natgw_node;natgw_node=natgw_node->next) { if (!strcmp(natgw_node->addr, ctdb_addr_to_str(&nodemap->nodes[i].addr))) { break; } } /* this node was not in the natgw so we just remove it from * the list */ if ((natgw_node == NULL) || (nodemap->nodes[i].flags & NODE_FLAGS_DISCONNECTED) ) { int j; for (j=i+1; jnum; j++) { nodemap->nodes[j-1] = nodemap->nodes[j]; } nodemap->num--; continue; } i++; } /* pick a node to be natgwmaster * we dont allow STOPPED, DELETED, BANNED or UNHEALTHY nodes to become the natgwmaster */ for(i=0;inum;i++){ if (!(nodemap->nodes[i].flags & (NODE_FLAGS_DISCONNECTED|NODE_FLAGS_STOPPED|NODE_FLAGS_DELETED|NODE_FLAGS_BANNED|NODE_FLAGS_UNHEALTHY))) { ret = ctdb_ctrl_getcapabilities(ctdb, TIMELIMIT(), nodemap->nodes[i].pnn, &capabilities); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get capabilities from node %u\n", nodemap->nodes[i].pnn)); return ret; } if (!(capabilities&CTDB_CAP_NATGW)) { continue; } printf("%d %s\n", nodemap->nodes[i].pnn,ctdb_addr_to_str(&nodemap->nodes[i].addr)); break; } } /* we couldnt find any healthy node, try unhealthy ones */ if (i == nodemap->num) { for(i=0;inum;i++){ if (!(nodemap->nodes[i].flags & (NODE_FLAGS_DISCONNECTED|NODE_FLAGS_STOPPED|NODE_FLAGS_DELETED))) { ret = ctdb_ctrl_getcapabilities(ctdb, TIMELIMIT(), nodemap->nodes[i].pnn, &capabilities); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get capabilities from node %u\n", nodemap->nodes[i].pnn)); return ret; } if (!(capabilities&CTDB_CAP_NATGW)) { continue; } printf("%d %s\n", nodemap->nodes[i].pnn,ctdb_addr_to_str(&nodemap->nodes[i].addr)); break; } } } /* unless all nodes are STOPPED, when we pick one anyway */ if (i == nodemap->num) { for(i=0;inum;i++){ if (!(nodemap->nodes[i].flags & (NODE_FLAGS_DISCONNECTED|NODE_FLAGS_DELETED))) { ret = ctdb_ctrl_getcapabilities(ctdb, TIMELIMIT(), nodemap->nodes[i].pnn, &capabilities); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get capabilities from node %u\n", nodemap->nodes[i].pnn)); return ret; } if (!(capabilities&CTDB_CAP_NATGW)) { continue; } printf("%d %s\n", nodemap->nodes[i].pnn, ctdb_addr_to_str(&nodemap->nodes[i].addr)); break; } } /* or if we still can not find any */ if (i == nodemap->num) { printf("-1 0.0.0.0\n"); ret = 2; /* matches ENOENT */ } } /* print the pruned list of nodes belonging to this natgw list */ for(i=0;inum;i++){ if (nodemap->nodes[i].flags & NODE_FLAGS_DELETED) { continue; } printf(":%d:%s:%d:%d:%d:%d:%d\n", nodemap->nodes[i].pnn, ctdb_addr_to_str(&nodemap->nodes[i].addr), !!(nodemap->nodes[i].flags&NODE_FLAGS_DISCONNECTED), !!(nodemap->nodes[i].flags&NODE_FLAGS_BANNED), !!(nodemap->nodes[i].flags&NODE_FLAGS_PERMANENTLY_DISABLED), !!(nodemap->nodes[i].flags&NODE_FLAGS_UNHEALTHY), !!(nodemap->nodes[i].flags&NODE_FLAGS_STOPPED)); } return ret; } /* display the status of the scripts for monitoring (or other events) */ static int control_one_scriptstatus(struct ctdb_context *ctdb, enum ctdb_eventscript_call type) { struct ctdb_scripts_wire *script_status; int ret, i; ret = ctdb_ctrl_getscriptstatus(ctdb, TIMELIMIT(), options.pnn, ctdb, type, &script_status); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get script status from node %u\n", options.pnn)); return ret; } if (script_status == NULL) { if (!options.machinereadable) { printf("%s cycle never run\n", ctdb_eventscript_call_names[type]); } return 0; } if (!options.machinereadable) { printf("%d scripts were executed last %s cycle\n", script_status->num_scripts, ctdb_eventscript_call_names[type]); } for (i=0; inum_scripts; i++) { const char *status = NULL; switch (script_status->scripts[i].status) { case -ETIME: status = "TIMEDOUT"; break; case -ENOEXEC: status = "DISABLED"; break; case 0: status = "OK"; break; default: if (script_status->scripts[i].status > 0) status = "ERROR"; break; } if (options.machinereadable) { printf(":%s:%s:%i:%s:%lu.%06lu:%lu.%06lu:%s:\n", ctdb_eventscript_call_names[type], script_status->scripts[i].name, script_status->scripts[i].status, status, (long)script_status->scripts[i].start.tv_sec, (long)script_status->scripts[i].start.tv_usec, (long)script_status->scripts[i].finished.tv_sec, (long)script_status->scripts[i].finished.tv_usec, script_status->scripts[i].output); continue; } if (status) printf("%-20s Status:%s ", script_status->scripts[i].name, status); else /* Some other error, eg from stat. */ printf("%-20s Status:CANNOT RUN (%s)", script_status->scripts[i].name, strerror(-script_status->scripts[i].status)); if (script_status->scripts[i].status >= 0) { printf("Duration:%.3lf ", timeval_delta(&script_status->scripts[i].finished, &script_status->scripts[i].start)); } if (script_status->scripts[i].status != -ENOEXEC) { printf("%s", ctime(&script_status->scripts[i].start.tv_sec)); if (script_status->scripts[i].status != 0) { printf(" OUTPUT:%s\n", script_status->scripts[i].output); } } else { printf("\n"); } } return 0; } static int control_scriptstatus(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; enum ctdb_eventscript_call type, min, max; const char *arg; if (argc > 1) { DEBUG(DEBUG_ERR, ("Unknown arguments to scriptstatus\n")); return -1; } if (argc == 0) arg = ctdb_eventscript_call_names[CTDB_EVENT_MONITOR]; else arg = argv[0]; for (type = 0; type < CTDB_EVENT_MAX; type++) { if (strcmp(arg, ctdb_eventscript_call_names[type]) == 0) { min = type; max = type+1; break; } } if (type == CTDB_EVENT_MAX) { if (strcmp(arg, "all") == 0) { min = 0; max = CTDB_EVENT_MAX; } else { DEBUG(DEBUG_ERR, ("Unknown event type %s\n", argv[0])); return -1; } } if (options.machinereadable) { printf(":Type:Name:Code:Status:Start:End:Error Output...:\n"); } for (type = min; type < max; type++) { ret = control_one_scriptstatus(ctdb, type); if (ret != 0) { return ret; } } return 0; } /* enable an eventscript */ static int control_enablescript(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; if (argc < 1) { usage(); } ret = ctdb_ctrl_enablescript(ctdb, TIMELIMIT(), options.pnn, argv[0]); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to enable script %s on node %u\n", argv[0], options.pnn)); return ret; } return 0; } /* disable an eventscript */ static int control_disablescript(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; if (argc < 1) { usage(); } ret = ctdb_ctrl_disablescript(ctdb, TIMELIMIT(), options.pnn, argv[0]); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to disable script %s on node %u\n", argv[0], options.pnn)); return ret; } return 0; } /* display the pnn of the recovery master */ static int control_recmaster(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t recmaster; if (!ctdb_getrecmaster(ctdb_connection, options.pnn, &recmaster)) { DEBUG(DEBUG_ERR, ("Unable to get recmaster from node %u\n", options.pnn)); return -1; } printf("%d\n",recmaster); return 0; } /* add a tickle to a public address */ static int control_add_tickle(struct ctdb_context *ctdb, int argc, const char **argv) { struct ctdb_tcp_connection t; TDB_DATA data; int ret; if (argc < 2) { usage(); } if (parse_ip_port(argv[0], &t.src_addr) == 0) { DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0])); return -1; } if (parse_ip_port(argv[1], &t.dst_addr) == 0) { DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[1])); return -1; } data.dptr = (uint8_t *)&t; data.dsize = sizeof(t); /* tell all nodes about this tcp connection */ ret = ctdb_control(ctdb, options.pnn, 0, CTDB_CONTROL_TCP_ADD_DELAYED_UPDATE, 0, data, ctdb, NULL, NULL, NULL, NULL); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to add tickle\n")); return -1; } return 0; } /* delete a tickle from a node */ static int control_del_tickle(struct ctdb_context *ctdb, int argc, const char **argv) { struct ctdb_tcp_connection t; TDB_DATA data; int ret; if (argc < 2) { usage(); } if (parse_ip_port(argv[0], &t.src_addr) == 0) { DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0])); return -1; } if (parse_ip_port(argv[1], &t.dst_addr) == 0) { DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[1])); return -1; } data.dptr = (uint8_t *)&t; data.dsize = sizeof(t); /* tell all nodes about this tcp connection */ ret = ctdb_control(ctdb, options.pnn, 0, CTDB_CONTROL_TCP_REMOVE, 0, data, ctdb, NULL, NULL, NULL, NULL); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to remove tickle\n")); return -1; } return 0; } /* get a list of all tickles for this pnn */ static int control_get_tickles(struct ctdb_context *ctdb, int argc, const char **argv) { struct ctdb_control_tcp_tickle_list *list; ctdb_sock_addr addr; int i, ret; unsigned port = 0; if (argc < 1) { usage(); } if (argc == 2) { port = atoi(argv[1]); } if (parse_ip(argv[0], NULL, 0, &addr) == 0) { DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0])); return -1; } ret = ctdb_ctrl_get_tcp_tickles(ctdb, TIMELIMIT(), options.pnn, ctdb, &addr, &list); if (ret == -1) { DEBUG(DEBUG_ERR, ("Unable to list tickles\n")); return -1; } if (options.machinereadable){ printf(":source ip:port:destination ip:port:\n"); for (i=0;itickles.num;i++) { if (port && port != ntohs(list->tickles.connections[i].dst_addr.ip.sin_port)) { continue; } printf(":%s:%u", ctdb_addr_to_str(&list->tickles.connections[i].src_addr), ntohs(list->tickles.connections[i].src_addr.ip.sin_port)); printf(":%s:%u:\n", ctdb_addr_to_str(&list->tickles.connections[i].dst_addr), ntohs(list->tickles.connections[i].dst_addr.ip.sin_port)); } } else { printf("Tickles for ip:%s\n", ctdb_addr_to_str(&list->addr)); printf("Num tickles:%u\n", list->tickles.num); for (i=0;itickles.num;i++) { if (port && port != ntohs(list->tickles.connections[i].dst_addr.ip.sin_port)) { continue; } printf("SRC: %s:%u ", ctdb_addr_to_str(&list->tickles.connections[i].src_addr), ntohs(list->tickles.connections[i].src_addr.ip.sin_port)); printf("DST: %s:%u\n", ctdb_addr_to_str(&list->tickles.connections[i].dst_addr), ntohs(list->tickles.connections[i].dst_addr.ip.sin_port)); } } talloc_free(list); return 0; } static int move_ip(struct ctdb_context *ctdb, ctdb_sock_addr *addr, uint32_t pnn) { struct ctdb_all_public_ips *ips; struct ctdb_public_ip ip; int i, ret; uint32_t *nodes; uint32_t disable_time; TDB_DATA data; struct ctdb_node_map *nodemap=NULL; TALLOC_CTX *tmp_ctx = talloc_new(ctdb); disable_time = 30; data.dptr = (uint8_t*)&disable_time; data.dsize = sizeof(disable_time); ret = ctdb_client_send_message(ctdb, CTDB_BROADCAST_CONNECTED, CTDB_SRVID_DISABLE_IP_CHECK, data); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to send message to disable ipcheck\n")); return -1; } /* read the public ip list from the node */ ret = ctdb_ctrl_get_public_ips(ctdb, TIMELIMIT(), pnn, ctdb, &ips); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get public ip list from node %u\n", pnn)); talloc_free(tmp_ctx); return -1; } for (i=0;inum;i++) { if (ctdb_same_ip(addr, &ips->ips[i].addr)) { break; } } if (i==ips->num) { DEBUG(DEBUG_ERR, ("Node %u can not host ip address '%s'\n", pnn, ctdb_addr_to_str(addr))); talloc_free(tmp_ctx); return -1; } ip.pnn = pnn; ip.addr = *addr; data.dptr = (uint8_t *)&ip; data.dsize = sizeof(ip); ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), options.pnn, tmp_ctx, &nodemap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from node %u\n", options.pnn)); talloc_free(tmp_ctx); return ret; } nodes = list_of_active_nodes_except_pnn(ctdb, nodemap, tmp_ctx, pnn); ret = ctdb_client_async_control(ctdb, CTDB_CONTROL_RELEASE_IP, nodes, 0, LONGTIMELIMIT(), false, data, NULL, NULL, NULL); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to release IP on nodes\n")); talloc_free(tmp_ctx); return -1; } ret = ctdb_ctrl_takeover_ip(ctdb, LONGTIMELIMIT(), pnn, &ip); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to take over IP on node %d\n", pnn)); talloc_free(tmp_ctx); return -1; } /* update the recovery daemon so it now knows to expect the new node assignment for this ip. */ ret = ctdb_client_send_message(ctdb, CTDB_BROADCAST_CONNECTED, CTDB_SRVID_RECD_UPDATE_IP, data); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to send message to update the ip on the recovery master.\n")); return -1; } talloc_free(tmp_ctx); return 0; } /* move/failover an ip address to a specific node */ static int control_moveip(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t pnn; int ret, retries = 0; ctdb_sock_addr addr; if (argc < 2) { usage(); return -1; } if (parse_ip(argv[0], NULL, 0, &addr) == 0) { DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0])); return -1; } if (sscanf(argv[1], "%u", &pnn) != 1) { DEBUG(DEBUG_ERR, ("Badly formed pnn\n")); return -1; } do { ret = move_ip(ctdb, &addr, pnn); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to move ip to node %d. Wait 3 second and try again.\n", pnn)); sleep(3); retries++; } } while (retries < 5 && ret != 0); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to move ip to node %d. Giving up.\n", pnn)); return -1; } return 0; } static int rebalance_node(struct ctdb_context *ctdb, uint32_t pnn) { uint32_t recmaster; TDB_DATA data; if (ctdb_ctrl_getrecmaster(ctdb, ctdb, TIMELIMIT(), pnn, &recmaster) != 0) { DEBUG(DEBUG_ERR, ("Unable to get recmaster from node %u\n", pnn)); return -1; } data.dptr = (uint8_t *)&pnn; data.dsize = sizeof(uint32_t); if (ctdb_client_send_message(ctdb, recmaster, CTDB_SRVID_REBALANCE_NODE, data) != 0) { DEBUG(DEBUG_ERR,("Failed to send message to force node reallocation\n")); return -1; } return 0; } /* rebalance a node by setting it to allow failback and triggering a takeover run */ static int control_rebalancenode(struct ctdb_context *ctdb, int argc, const char **argv) { switch (options.pnn) { case CTDB_BROADCAST_ALL: case CTDB_CURRENT_NODE: DEBUG(DEBUG_ERR,("You must specify a node number with -n for the node to rebalance\n")); return -1; } return rebalance_node(ctdb, options.pnn); } static int getips_store_callback(void *param, void *data) { struct ctdb_public_ip *node_ip = (struct ctdb_public_ip *)data; struct ctdb_all_public_ips *ips = param; int i; i = ips->num++; ips->ips[i].pnn = node_ip->pnn; ips->ips[i].addr = node_ip->addr; return 0; } static int getips_count_callback(void *param, void *data) { uint32_t *count = param; (*count)++; return 0; } #define IP_KEYLEN 4 static uint32_t *ip_key(ctdb_sock_addr *ip) { static uint32_t key[IP_KEYLEN]; bzero(key, sizeof(key)); switch (ip->sa.sa_family) { case AF_INET: key[0] = ip->ip.sin_addr.s_addr; break; case AF_INET6: { uint32_t *s6_a32 = (uint32_t *)&(ip->ip6.sin6_addr.s6_addr); key[0] = s6_a32[3]; key[1] = s6_a32[2]; key[2] = s6_a32[1]; key[3] = s6_a32[0]; break; } default: DEBUG(DEBUG_ERR, (__location__ " ERROR, unknown family passed :%u\n", ip->sa.sa_family)); return key; } return key; } static void *add_ip_callback(void *parm, void *data) { return parm; } static int control_get_all_public_ips(struct ctdb_context *ctdb, TALLOC_CTX *tmp_ctx, struct ctdb_all_public_ips **ips) { struct ctdb_all_public_ips *tmp_ips; struct ctdb_node_map *nodemap=NULL; trbt_tree_t *ip_tree; int i, j, len, ret; uint32_t count; ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, tmp_ctx, &nodemap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from node %u\n", options.pnn)); return ret; } ip_tree = trbt_create(tmp_ctx, 0); for(i=0;inum;i++){ if (nodemap->nodes[i].flags & NODE_FLAGS_DELETED) { continue; } if (nodemap->nodes[i].flags & NODE_FLAGS_DISCONNECTED) { continue; } /* read the public ip list from this node */ ret = ctdb_ctrl_get_public_ips(ctdb, TIMELIMIT(), nodemap->nodes[i].pnn, tmp_ctx, &tmp_ips); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get public ip list from node %u\n", nodemap->nodes[i].pnn)); return -1; } for (j=0; jnum;j++) { struct ctdb_public_ip *node_ip; node_ip = talloc(tmp_ctx, struct ctdb_public_ip); node_ip->pnn = tmp_ips->ips[j].pnn; node_ip->addr = tmp_ips->ips[j].addr; trbt_insertarray32_callback(ip_tree, IP_KEYLEN, ip_key(&tmp_ips->ips[j].addr), add_ip_callback, node_ip); } talloc_free(tmp_ips); } /* traverse */ count = 0; trbt_traversearray32(ip_tree, IP_KEYLEN, getips_count_callback, &count); len = offsetof(struct ctdb_all_public_ips, ips) + count*sizeof(struct ctdb_public_ip); tmp_ips = talloc_zero_size(tmp_ctx, len); trbt_traversearray32(ip_tree, IP_KEYLEN, getips_store_callback, tmp_ips); *ips = tmp_ips; return 0; } /* * scans all other nodes and returns a pnn for another node that can host this * ip address or -1 */ static int find_other_host_for_public_ip(struct ctdb_context *ctdb, ctdb_sock_addr *addr) { TALLOC_CTX *tmp_ctx = talloc_new(ctdb); struct ctdb_all_public_ips *ips; struct ctdb_node_map *nodemap=NULL; int i, j, ret; ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, tmp_ctx, &nodemap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from node %u\n", options.pnn)); talloc_free(tmp_ctx); return ret; } for(i=0;inum;i++){ if (nodemap->nodes[i].flags & NODE_FLAGS_INACTIVE) { continue; } if (nodemap->nodes[i].pnn == options.pnn) { continue; } /* read the public ip list from this node */ ret = ctdb_ctrl_get_public_ips(ctdb, TIMELIMIT(), nodemap->nodes[i].pnn, tmp_ctx, &ips); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get public ip list from node %u\n", nodemap->nodes[i].pnn)); return -1; } for (j=0;jnum;j++) { if (ctdb_same_ip(addr, &ips->ips[j].addr)) { talloc_free(tmp_ctx); return nodemap->nodes[i].pnn; } } talloc_free(ips); } talloc_free(tmp_ctx); return -1; } static uint32_t ipreallocate_finished; /* handler for receiving the response to ipreallocate */ static void ip_reallocate_handler(struct ctdb_context *ctdb, uint64_t srvid, TDB_DATA data, void *private_data) { ipreallocate_finished = 1; } static void ctdb_every_second(struct event_context *ev, struct timed_event *te, struct timeval t, void *p) { struct ctdb_context *ctdb = talloc_get_type(p, struct ctdb_context); event_add_timed(ctdb->ev, ctdb, timeval_current_ofs(1, 0), ctdb_every_second, ctdb); } /* ask the recovery daemon on the recovery master to perform a ip reallocation */ static int control_ipreallocate(struct ctdb_context *ctdb, int argc, const char **argv) { int i, ret; TDB_DATA data; struct takeover_run_reply rd; uint32_t recmaster; struct ctdb_node_map *nodemap=NULL; int retries=0; struct timeval tv = timeval_current(); /* we need some events to trigger so we can timeout and restart the loop */ event_add_timed(ctdb->ev, ctdb, timeval_current_ofs(1, 0), ctdb_every_second, ctdb); rd.pnn = ctdb_ctrl_getpnn(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE); if (rd.pnn == -1) { DEBUG(DEBUG_ERR, ("Failed to get pnn of local node\n")); return -1; } rd.srvid = getpid(); /* register a message port for receiveing the reply so that we can receive the reply */ ctdb_client_set_message_handler(ctdb, rd.srvid, ip_reallocate_handler, NULL); data.dptr = (uint8_t *)&rd; data.dsize = sizeof(rd); again: /* check that there are valid nodes available */ if (ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), options.pnn, ctdb, &nodemap) != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n")); return -1; } for (i=0; inum;i++) { if ((nodemap->nodes[i].flags & (NODE_FLAGS_DELETED|NODE_FLAGS_BANNED|NODE_FLAGS_STOPPED)) == 0) { break; } } if (i==nodemap->num) { DEBUG(DEBUG_ERR,("No recmaster available, no need to wait for cluster convergence\n")); return 0; } if (!ctdb_getrecmaster(ctdb_connection, options.pnn, &recmaster)) { DEBUG(DEBUG_ERR, ("Unable to get recmaster from node %u\n", options.pnn)); return -1; } /* verify the node exists */ if (ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), recmaster, ctdb, &nodemap) != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n")); return -1; } /* check tha there are nodes available that can act as a recmaster */ for (i=0; inum; i++) { if (nodemap->nodes[i].flags & (NODE_FLAGS_DELETED|NODE_FLAGS_BANNED|NODE_FLAGS_STOPPED)) { continue; } break; } if (i == nodemap->num) { DEBUG(DEBUG_ERR,("No possible nodes to host addresses.\n")); return 0; } /* verify the recovery master is not STOPPED, nor BANNED */ if (nodemap->nodes[recmaster].flags & (NODE_FLAGS_DELETED|NODE_FLAGS_BANNED|NODE_FLAGS_STOPPED)) { DEBUG(DEBUG_ERR,("No suitable recmaster found. Try again\n")); retries++; sleep(1); goto again; } /* verify the recovery master is not STOPPED, nor BANNED */ if (nodemap->nodes[recmaster].flags & (NODE_FLAGS_DELETED|NODE_FLAGS_BANNED|NODE_FLAGS_STOPPED)) { DEBUG(DEBUG_ERR,("No suitable recmaster found. Try again\n")); retries++; sleep(1); goto again; } ipreallocate_finished = 0; ret = ctdb_client_send_message(ctdb, recmaster, CTDB_SRVID_TAKEOVER_RUN, data); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to send ip takeover run request message to %u\n", options.pnn)); return -1; } tv = timeval_current(); /* this loop will terminate when we have received the reply */ while (timeval_elapsed(&tv) < 5.0 && ipreallocate_finished == 0) { event_loop_once(ctdb->ev); } if (ipreallocate_finished == 1) { return 0; } retries++; sleep(1); goto again; return 0; } /* add a public ip address to a node */ static int control_addip(struct ctdb_context *ctdb, int argc, const char **argv) { int i, ret; int len, retries = 0; unsigned mask; ctdb_sock_addr addr; struct ctdb_control_ip_iface *pub; TALLOC_CTX *tmp_ctx = talloc_new(ctdb); struct ctdb_all_public_ips *ips; if (argc != 2) { talloc_free(tmp_ctx); usage(); } if (!parse_ip_mask(argv[0], argv[1], &addr, &mask)) { DEBUG(DEBUG_ERR, ("Badly formed ip/mask : %s\n", argv[0])); talloc_free(tmp_ctx); return -1; } /* read the public ip list from the node */ ret = ctdb_ctrl_get_public_ips(ctdb, TIMELIMIT(), options.pnn, tmp_ctx, &ips); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get public ip list from node %u\n", options.pnn)); talloc_free(tmp_ctx); return -1; } for (i=0;inum;i++) { if (ctdb_same_ip(&addr, &ips->ips[i].addr)) { DEBUG(DEBUG_ERR,("Can not add ip to node. Node already hosts this ip\n")); return 0; } } /* Dont timeout. This command waits for an ip reallocation which sometimes can take wuite a while if there has been a recent recovery */ alarm(0); len = offsetof(struct ctdb_control_ip_iface, iface) + strlen(argv[1]) + 1; pub = talloc_size(tmp_ctx, len); CTDB_NO_MEMORY(ctdb, pub); pub->addr = addr; pub->mask = mask; pub->len = strlen(argv[1])+1; memcpy(&pub->iface[0], argv[1], strlen(argv[1])+1); do { ret = ctdb_ctrl_add_public_ip(ctdb, TIMELIMIT(), options.pnn, pub); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to add public ip to node %u. Wait 3 seconds and try again.\n", options.pnn)); sleep(3); retries++; } } while (retries < 5 && ret != 0); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to add public ip to node %u. Giving up.\n", options.pnn)); talloc_free(tmp_ctx); return ret; } if (rebalance_node(ctdb, options.pnn) != 0) { DEBUG(DEBUG_ERR,("Error when trying to rebalance node\n")); return ret; } talloc_free(tmp_ctx); return 0; } static int control_delip(struct ctdb_context *ctdb, int argc, const char **argv); static int control_delip_all(struct ctdb_context *ctdb, int argc, const char **argv, ctdb_sock_addr *addr) { TALLOC_CTX *tmp_ctx = talloc_new(ctdb); struct ctdb_node_map *nodemap=NULL; struct ctdb_all_public_ips *ips; int ret, i, j; ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, tmp_ctx, &nodemap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from current node\n")); return ret; } /* remove it from the nodes that are not hosting the ip currently */ for(i=0;inum;i++){ if (nodemap->nodes[i].flags & NODE_FLAGS_INACTIVE) { continue; } if (ctdb_ctrl_get_public_ips(ctdb, TIMELIMIT(), nodemap->nodes[i].pnn, tmp_ctx, &ips) != 0) { DEBUG(DEBUG_ERR, ("Unable to get public ip list from node %d\n", nodemap->nodes[i].pnn)); continue; } for (j=0;jnum;j++) { if (ctdb_same_ip(addr, &ips->ips[j].addr)) { break; } } if (j==ips->num) { continue; } if (ips->ips[j].pnn == nodemap->nodes[i].pnn) { continue; } options.pnn = nodemap->nodes[i].pnn; control_delip(ctdb, argc, argv); } /* remove it from every node (also the one hosting it) */ for(i=0;inum;i++){ if (nodemap->nodes[i].flags & NODE_FLAGS_INACTIVE) { continue; } if (ctdb_ctrl_get_public_ips(ctdb, TIMELIMIT(), nodemap->nodes[i].pnn, tmp_ctx, &ips) != 0) { DEBUG(DEBUG_ERR, ("Unable to get public ip list from node %d\n", nodemap->nodes[i].pnn)); continue; } for (j=0;jnum;j++) { if (ctdb_same_ip(addr, &ips->ips[j].addr)) { break; } } if (j==ips->num) { continue; } options.pnn = nodemap->nodes[i].pnn; control_delip(ctdb, argc, argv); } talloc_free(tmp_ctx); return 0; } /* delete a public ip address from a node */ static int control_delip(struct ctdb_context *ctdb, int argc, const char **argv) { int i, ret; int retries = 0; ctdb_sock_addr addr; struct ctdb_control_ip_iface pub; TALLOC_CTX *tmp_ctx = talloc_new(ctdb); struct ctdb_all_public_ips *ips; if (argc != 1) { talloc_free(tmp_ctx); usage(); } if (parse_ip(argv[0], NULL, 0, &addr) == 0) { DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0])); return -1; } if (options.pnn == CTDB_BROADCAST_ALL) { return control_delip_all(ctdb, argc, argv, &addr); } pub.addr = addr; pub.mask = 0; pub.len = 0; ret = ctdb_ctrl_get_public_ips(ctdb, TIMELIMIT(), options.pnn, tmp_ctx, &ips); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get public ip list from cluster\n")); talloc_free(tmp_ctx); return ret; } for (i=0;inum;i++) { if (ctdb_same_ip(&addr, &ips->ips[i].addr)) { break; } } if (i==ips->num) { DEBUG(DEBUG_ERR, ("This node does not support this public address '%s'\n", ctdb_addr_to_str(&addr))); talloc_free(tmp_ctx); return -1; } if (ips->ips[i].pnn == options.pnn) { ret = find_other_host_for_public_ip(ctdb, &addr); if (ret != -1) { do { ret = move_ip(ctdb, &addr, ret); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to move ip to node %d. Wait 3 seconds and try again.\n", options.pnn)); sleep(3); retries++; } } while (retries < 5 && ret != 0); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to move ip to node %d. Giving up.\n", options.pnn)); return -1; } } } ret = ctdb_ctrl_del_public_ip(ctdb, TIMELIMIT(), options.pnn, &pub); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to del public ip from node %u\n", options.pnn)); talloc_free(tmp_ctx); return ret; } talloc_free(tmp_ctx); return 0; } /* kill a tcp connection */ static int kill_tcp(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; struct ctdb_control_killtcp killtcp; if (argc < 2) { usage(); } if (!parse_ip_port(argv[0], &killtcp.src_addr)) { DEBUG(DEBUG_ERR, ("Bad IP:port '%s'\n", argv[0])); return -1; } if (!parse_ip_port(argv[1], &killtcp.dst_addr)) { DEBUG(DEBUG_ERR, ("Bad IP:port '%s'\n", argv[1])); return -1; } ret = ctdb_ctrl_killtcp(ctdb, TIMELIMIT(), options.pnn, &killtcp); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to killtcp from node %u\n", options.pnn)); return ret; } return 0; } /* send a gratious arp */ static int control_gratious_arp(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; ctdb_sock_addr addr; if (argc < 2) { usage(); } if (!parse_ip(argv[0], NULL, 0, &addr)) { DEBUG(DEBUG_ERR, ("Bad IP '%s'\n", argv[0])); return -1; } ret = ctdb_ctrl_gratious_arp(ctdb, TIMELIMIT(), options.pnn, &addr, argv[1]); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to send gratious_arp from node %u\n", options.pnn)); return ret; } return 0; } /* register a server id */ static int regsrvid(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; struct ctdb_server_id server_id; if (argc < 3) { usage(); } server_id.pnn = strtoul(argv[0], NULL, 0); server_id.type = strtoul(argv[1], NULL, 0); server_id.server_id = strtoul(argv[2], NULL, 0); ret = ctdb_ctrl_register_server_id(ctdb, TIMELIMIT(), &server_id); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to register server_id from node %u\n", options.pnn)); return ret; } DEBUG(DEBUG_ERR,("Srvid registered. Sleeping for 999 seconds\n")); sleep(999); return -1; } /* unregister a server id */ static int unregsrvid(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; struct ctdb_server_id server_id; if (argc < 3) { usage(); } server_id.pnn = strtoul(argv[0], NULL, 0); server_id.type = strtoul(argv[1], NULL, 0); server_id.server_id = strtoul(argv[2], NULL, 0); ret = ctdb_ctrl_unregister_server_id(ctdb, TIMELIMIT(), &server_id); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to unregister server_id from node %u\n", options.pnn)); return ret; } return -1; } /* check if a server id exists */ static int chksrvid(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t status; int ret; struct ctdb_server_id server_id; if (argc < 3) { usage(); } server_id.pnn = strtoul(argv[0], NULL, 0); server_id.type = strtoul(argv[1], NULL, 0); server_id.server_id = strtoul(argv[2], NULL, 0); ret = ctdb_ctrl_check_server_id(ctdb, TIMELIMIT(), options.pnn, &server_id, &status); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to check server_id from node %u\n", options.pnn)); return ret; } if (status) { printf("Server id %d:%d:%d EXISTS\n", server_id.pnn, server_id.type, server_id.server_id); } else { printf("Server id %d:%d:%d does NOT exist\n", server_id.pnn, server_id.type, server_id.server_id); } return 0; } /* get a list of all server ids that are registered on a node */ static int getsrvids(struct ctdb_context *ctdb, int argc, const char **argv) { int i, ret; struct ctdb_server_id_list *server_ids; ret = ctdb_ctrl_get_server_id_list(ctdb, ctdb, TIMELIMIT(), options.pnn, &server_ids); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get server_id list from node %u\n", options.pnn)); return ret; } for (i=0; inum; i++) { printf("Server id %d:%d:%d\n", server_ids->server_ids[i].pnn, server_ids->server_ids[i].type, server_ids->server_ids[i].server_id); } return -1; } /* check if a server id exists */ static int check_srvids(struct ctdb_context *ctdb, int argc, const char **argv) { TALLOC_CTX *tmp_ctx = talloc_new(NULL); uint64_t *ids; uint8_t *result; int i; if (argc < 1) { talloc_free(tmp_ctx); usage(); } ids = talloc_array(tmp_ctx, uint64_t, argc); result = talloc_array(tmp_ctx, uint8_t, argc); for (i = 0; i < argc; i++) { ids[i] = strtoull(argv[i], NULL, 0); } if (!ctdb_check_message_handlers(ctdb_connection, options.pnn, argc, ids, result)) { DEBUG(DEBUG_ERR, ("Unable to check server_id from node %u\n", options.pnn)); talloc_free(tmp_ctx); return -1; } for (i=0; i < argc; i++) { printf("Server id %d:%llu %s\n", options.pnn, (long long)ids[i], result[i] ? "exists" : "does not exist"); } talloc_free(tmp_ctx); return 0; } /* send a tcp tickle ack */ static int tickle_tcp(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; ctdb_sock_addr src, dst; if (argc < 2) { usage(); } if (!parse_ip_port(argv[0], &src)) { DEBUG(DEBUG_ERR, ("Bad IP:port '%s'\n", argv[0])); return -1; } if (!parse_ip_port(argv[1], &dst)) { DEBUG(DEBUG_ERR, ("Bad IP:port '%s'\n", argv[1])); return -1; } ret = ctdb_sys_send_tcp(&src, &dst, 0, 0, 0); if (ret==0) { return 0; } DEBUG(DEBUG_ERR, ("Error while sending tickle ack\n")); return -1; } /* display public ip status */ static int control_ip(struct ctdb_context *ctdb, int argc, const char **argv) { int i, ret; TALLOC_CTX *tmp_ctx = talloc_new(ctdb); struct ctdb_all_public_ips *ips; if (options.pnn == CTDB_BROADCAST_ALL) { /* read the list of public ips from all nodes */ ret = control_get_all_public_ips(ctdb, tmp_ctx, &ips); } else { /* read the public ip list from this node */ ret = ctdb_ctrl_get_public_ips(ctdb, TIMELIMIT(), options.pnn, tmp_ctx, &ips); } if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get public ips from node %u\n", options.pnn)); talloc_free(tmp_ctx); return ret; } if (options.machinereadable){ printf(":Public IP:Node:"); if (options.verbose){ printf("ActiveInterface:AvailableInterfaces:ConfiguredInterfaces:"); } printf("\n"); } else { if (options.pnn == CTDB_BROADCAST_ALL) { printf("Public IPs on ALL nodes\n"); } else { printf("Public IPs on node %u\n", options.pnn); } } for (i=1;i<=ips->num;i++) { struct ctdb_control_public_ip_info *info = NULL; int32_t pnn; char *aciface = NULL; char *avifaces = NULL; char *cifaces = NULL; if (options.pnn == CTDB_BROADCAST_ALL) { pnn = ips->ips[ips->num-i].pnn; } else { pnn = options.pnn; } if (pnn != -1) { ret = ctdb_ctrl_get_public_ip_info(ctdb, TIMELIMIT(), pnn, ctdb, &ips->ips[ips->num-i].addr, &info); } else { ret = -1; } if (ret == 0) { int j; for (j=0; j < info->num; j++) { if (cifaces == NULL) { cifaces = talloc_strdup(info, info->ifaces[j].name); } else { cifaces = talloc_asprintf_append(cifaces, ",%s", info->ifaces[j].name); } if (info->active_idx == j) { aciface = info->ifaces[j].name; } if (info->ifaces[j].link_state == 0) { continue; } if (avifaces == NULL) { avifaces = talloc_strdup(info, info->ifaces[j].name); } else { avifaces = talloc_asprintf_append(avifaces, ",%s", info->ifaces[j].name); } } } if (options.machinereadable){ printf(":%s:%d:", ctdb_addr_to_str(&ips->ips[ips->num-i].addr), ips->ips[ips->num-i].pnn); if (options.verbose){ printf("%s:%s:%s:", aciface?aciface:"", avifaces?avifaces:"", cifaces?cifaces:""); } printf("\n"); } else { if (options.verbose) { printf("%s node[%d] active[%s] available[%s] configured[%s]\n", ctdb_addr_to_str(&ips->ips[ips->num-i].addr), ips->ips[ips->num-i].pnn, aciface?aciface:"", avifaces?avifaces:"", cifaces?cifaces:""); } else { printf("%s %d\n", ctdb_addr_to_str(&ips->ips[ips->num-i].addr), ips->ips[ips->num-i].pnn); } } talloc_free(info); } talloc_free(tmp_ctx); return 0; } /* public ip info */ static int control_ipinfo(struct ctdb_context *ctdb, int argc, const char **argv) { int i, ret; ctdb_sock_addr addr; TALLOC_CTX *tmp_ctx = talloc_new(ctdb); struct ctdb_control_public_ip_info *info; if (argc != 1) { talloc_free(tmp_ctx); usage(); } if (parse_ip(argv[0], NULL, 0, &addr) == 0) { DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0])); return -1; } /* read the public ip info from this node */ ret = ctdb_ctrl_get_public_ip_info(ctdb, TIMELIMIT(), options.pnn, tmp_ctx, &addr, &info); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get public ip[%s]info from node %u\n", argv[0], options.pnn)); talloc_free(tmp_ctx); return ret; } printf("Public IP[%s] info on node %u\n", ctdb_addr_to_str(&info->ip.addr), options.pnn); printf("IP:%s\nCurrentNode:%d\nNumInterfaces:%u\n", ctdb_addr_to_str(&info->ip.addr), info->ip.pnn, info->num); for (i=0; inum; i++) { info->ifaces[i].name[CTDB_IFACE_SIZE] = '\0'; printf("Interface[%u]: Name:%s Link:%s References:%u%s\n", i+1, info->ifaces[i].name, info->ifaces[i].link_state?"up":"down", (unsigned int)info->ifaces[i].references, (i==info->active_idx)?" (active)":""); } talloc_free(tmp_ctx); return 0; } /* display interfaces status */ static int control_ifaces(struct ctdb_context *ctdb, int argc, const char **argv) { int i; struct ctdb_ifaces_list *ifaces; /* read the public ip list from this node */ if (!ctdb_getifaces(ctdb_connection, options.pnn, &ifaces)) { DEBUG(DEBUG_ERR, ("Unable to get interfaces from node %u\n", options.pnn)); return -1; } if (options.machinereadable){ printf(":Name:LinkStatus:References:\n"); } else { printf("Interfaces on node %u\n", options.pnn); } for (i=0; inum; i++) { if (options.machinereadable){ printf(":%s:%s:%u\n", ifaces->ifaces[i].name, ifaces->ifaces[i].link_state?"1":"0", (unsigned int)ifaces->ifaces[i].references); } else { printf("name:%s link:%s references:%u\n", ifaces->ifaces[i].name, ifaces->ifaces[i].link_state?"up":"down", (unsigned int)ifaces->ifaces[i].references); } } ctdb_free_ifaces(ifaces); return 0; } /* set link status of an interface */ static int control_setifacelink(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; TALLOC_CTX *tmp_ctx = talloc_new(ctdb); struct ctdb_control_iface_info info; ZERO_STRUCT(info); if (argc != 2) { usage(); } if (strlen(argv[0]) > CTDB_IFACE_SIZE) { DEBUG(DEBUG_ERR, ("interfaces name '%s' too long\n", argv[0])); talloc_free(tmp_ctx); return -1; } strcpy(info.name, argv[0]); if (strcmp(argv[1], "up") == 0) { info.link_state = 1; } else if (strcmp(argv[1], "down") == 0) { info.link_state = 0; } else { DEBUG(DEBUG_ERR, ("link state invalid '%s' should be 'up' or 'down'\n", argv[1])); talloc_free(tmp_ctx); return -1; } /* read the public ip list from this node */ ret = ctdb_ctrl_set_iface_link(ctdb, TIMELIMIT(), options.pnn, tmp_ctx, &info); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to set link state for interfaces %s node %u\n", argv[0], options.pnn)); talloc_free(tmp_ctx); return ret; } talloc_free(tmp_ctx); return 0; } /* display pid of a ctdb daemon */ static int control_getpid(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t pid; int ret; ret = ctdb_ctrl_getpid(ctdb, TIMELIMIT(), options.pnn, &pid); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get daemon pid from node %u\n", options.pnn)); return ret; } printf("Pid:%d\n", pid); return 0; } /* disable a remote node */ static int control_disable(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; struct ctdb_node_map *nodemap=NULL; /* check if the node is already disabled */ if (ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, ctdb, &nodemap) != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n")); exit(10); } if (nodemap->nodes[options.pnn].flags & NODE_FLAGS_PERMANENTLY_DISABLED) { DEBUG(DEBUG_ERR,("Node %d is already disabled.\n", options.pnn)); return 0; } do { ret = ctdb_ctrl_modflags(ctdb, TIMELIMIT(), options.pnn, NODE_FLAGS_PERMANENTLY_DISABLED, 0); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to disable node %u\n", options.pnn)); return ret; } sleep(1); /* read the nodemap and verify the change took effect */ if (ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, ctdb, &nodemap) != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n")); exit(10); } } while (!(nodemap->nodes[options.pnn].flags & NODE_FLAGS_PERMANENTLY_DISABLED)); ret = control_ipreallocate(ctdb, argc, argv); if (ret != 0) { DEBUG(DEBUG_ERR, ("IP Reallocate failed on node %u\n", options.pnn)); return ret; } return 0; } /* enable a disabled remote node */ static int control_enable(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; struct ctdb_node_map *nodemap=NULL; /* check if the node is already enabled */ if (ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, ctdb, &nodemap) != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n")); exit(10); } if (!(nodemap->nodes[options.pnn].flags & NODE_FLAGS_PERMANENTLY_DISABLED)) { DEBUG(DEBUG_ERR,("Node %d is already enabled.\n", options.pnn)); return 0; } do { ret = ctdb_ctrl_modflags(ctdb, TIMELIMIT(), options.pnn, 0, NODE_FLAGS_PERMANENTLY_DISABLED); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to enable node %u\n", options.pnn)); return ret; } sleep(1); /* read the nodemap and verify the change took effect */ if (ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, ctdb, &nodemap) != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n")); exit(10); } } while (nodemap->nodes[options.pnn].flags & NODE_FLAGS_PERMANENTLY_DISABLED); ret = control_ipreallocate(ctdb, argc, argv); if (ret != 0) { DEBUG(DEBUG_ERR, ("IP Reallocate failed on node %u\n", options.pnn)); return ret; } return 0; } /* stop a remote node */ static int control_stop(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; struct ctdb_node_map *nodemap=NULL; do { ret = ctdb_ctrl_stop_node(ctdb, TIMELIMIT(), options.pnn); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to stop node %u try again\n", options.pnn)); } sleep(1); /* read the nodemap and verify the change took effect */ if (ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, ctdb, &nodemap) != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n")); exit(10); } } while (!(nodemap->nodes[options.pnn].flags & NODE_FLAGS_STOPPED)); ret = control_ipreallocate(ctdb, argc, argv); if (ret != 0) { DEBUG(DEBUG_ERR, ("IP Reallocate failed on node %u\n", options.pnn)); return ret; } return 0; } /* restart a stopped remote node */ static int control_continue(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; struct ctdb_node_map *nodemap=NULL; do { ret = ctdb_ctrl_continue_node(ctdb, TIMELIMIT(), options.pnn); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to continue node %u\n", options.pnn)); return ret; } sleep(1); /* read the nodemap and verify the change took effect */ if (ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, ctdb, &nodemap) != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n")); exit(10); } } while (nodemap->nodes[options.pnn].flags & NODE_FLAGS_STOPPED); ret = control_ipreallocate(ctdb, argc, argv); if (ret != 0) { DEBUG(DEBUG_ERR, ("IP Reallocate failed on node %u\n", options.pnn)); return ret; } return 0; } static uint32_t get_generation(struct ctdb_context *ctdb) { struct ctdb_vnn_map *vnnmap=NULL; int ret; /* wait until the recmaster is not in recovery mode */ while (1) { uint32_t recmode, recmaster; if (vnnmap != NULL) { talloc_free(vnnmap); vnnmap = NULL; } /* get the recmaster */ if (!ctdb_getrecmaster(ctdb_connection, CTDB_CURRENT_NODE, &recmaster)) { DEBUG(DEBUG_ERR, ("Unable to get recmaster from node %u\n", options.pnn)); exit(10); } /* get recovery mode */ if (!ctdb_getrecmode(ctdb_connection, recmaster, &recmode)) { DEBUG(DEBUG_ERR, ("Unable to get recmode from node %u\n", options.pnn)); exit(10); } /* get the current generation number */ ret = ctdb_ctrl_getvnnmap(ctdb, TIMELIMIT(), recmaster, ctdb, &vnnmap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get vnnmap from recmaster (%u)\n", recmaster)); exit(10); } if ((recmode == CTDB_RECOVERY_NORMAL) && (vnnmap->generation != 1)){ return vnnmap->generation; } sleep(1); } } /* ban a node from the cluster */ static int control_ban(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; struct ctdb_node_map *nodemap=NULL; struct ctdb_ban_time bantime; if (argc < 1) { usage(); } /* verify the node exists */ ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, ctdb, &nodemap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n")); return ret; } if (nodemap->nodes[options.pnn].flags & NODE_FLAGS_BANNED) { DEBUG(DEBUG_ERR,("Node %u is already banned.\n", options.pnn)); return -1; } bantime.pnn = options.pnn; bantime.time = strtoul(argv[0], NULL, 0); ret = ctdb_ctrl_set_ban(ctdb, TIMELIMIT(), options.pnn, &bantime); if (ret != 0) { DEBUG(DEBUG_ERR,("Banning node %d for %d seconds failed.\n", bantime.pnn, bantime.time)); return -1; } ret = control_ipreallocate(ctdb, argc, argv); if (ret != 0) { DEBUG(DEBUG_ERR, ("IP Reallocate failed on node %u\n", options.pnn)); return ret; } return 0; } /* unban a node from the cluster */ static int control_unban(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; struct ctdb_node_map *nodemap=NULL; struct ctdb_ban_time bantime; /* verify the node exists */ ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, ctdb, &nodemap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n")); return ret; } if (!(nodemap->nodes[options.pnn].flags & NODE_FLAGS_BANNED)) { DEBUG(DEBUG_ERR,("Node %u is not banned.\n", options.pnn)); return -1; } bantime.pnn = options.pnn; bantime.time = 0; ret = ctdb_ctrl_set_ban(ctdb, TIMELIMIT(), options.pnn, &bantime); if (ret != 0) { DEBUG(DEBUG_ERR,("Unbanning node %d failed.\n", bantime.pnn)); return -1; } ret = control_ipreallocate(ctdb, argc, argv); if (ret != 0) { DEBUG(DEBUG_ERR, ("IP Reallocate failed on node %u\n", options.pnn)); return ret; } return 0; } /* show ban information for a node */ static int control_showban(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; struct ctdb_node_map *nodemap=NULL; struct ctdb_ban_time *bantime; /* verify the node exists */ ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, ctdb, &nodemap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n")); return ret; } ret = ctdb_ctrl_get_ban(ctdb, TIMELIMIT(), options.pnn, ctdb, &bantime); if (ret != 0) { DEBUG(DEBUG_ERR,("Showing ban info for node %d failed.\n", options.pnn)); return -1; } if (bantime->time == 0) { printf("Node %u is not banned\n", bantime->pnn); } else { printf("Node %u is banned banned for %d seconds\n", bantime->pnn, bantime->time); } return 0; } /* shutdown a daemon */ static int control_shutdown(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; ret = ctdb_ctrl_shutdown(ctdb, TIMELIMIT(), options.pnn); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to shutdown node %u\n", options.pnn)); return ret; } return 0; } /* trigger a recovery */ static int control_recover(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; uint32_t generation, next_generation; /* record the current generation number */ generation = get_generation(ctdb); ret = ctdb_ctrl_freeze_priority(ctdb, TIMELIMIT(), options.pnn, 1); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to freeze node\n")); return ret; } ret = ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.pnn, CTDB_RECOVERY_ACTIVE); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to set recovery mode\n")); return ret; } /* wait until we are in a new generation */ while (1) { next_generation = get_generation(ctdb); if (next_generation != generation) { return 0; } sleep(1); } return 0; } /* display monitoring mode of a remote node */ static int control_getmonmode(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t monmode; int ret; ret = ctdb_ctrl_getmonmode(ctdb, TIMELIMIT(), options.pnn, &monmode); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get monmode from node %u\n", options.pnn)); return ret; } if (!options.machinereadable){ printf("Monitoring mode:%s (%d)\n",monmode==CTDB_MONITORING_ACTIVE?"ACTIVE":"DISABLED",monmode); } else { printf(":mode:\n"); printf(":%d:\n",monmode); } return 0; } /* display capabilities of a remote node */ static int control_getcapabilities(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t capabilities; int ret; ret = ctdb_ctrl_getcapabilities(ctdb, TIMELIMIT(), options.pnn, &capabilities); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get capabilities from node %u\n", options.pnn)); return ret; } if (!options.machinereadable){ printf("RECMASTER: %s\n", (capabilities&CTDB_CAP_RECMASTER)?"YES":"NO"); printf("LMASTER: %s\n", (capabilities&CTDB_CAP_LMASTER)?"YES":"NO"); printf("LVS: %s\n", (capabilities&CTDB_CAP_LVS)?"YES":"NO"); printf("NATGW: %s\n", (capabilities&CTDB_CAP_NATGW)?"YES":"NO"); } else { printf(":RECMASTER:LMASTER:LVS:NATGW:\n"); printf(":%d:%d:%d:%d:\n", !!(capabilities&CTDB_CAP_RECMASTER), !!(capabilities&CTDB_CAP_LMASTER), !!(capabilities&CTDB_CAP_LVS), !!(capabilities&CTDB_CAP_NATGW)); } return 0; } /* display lvs configuration */ static int control_lvs(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t *capabilities; struct ctdb_node_map *nodemap=NULL; int i, ret; int healthy_count = 0; ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), options.pnn, ctdb, &nodemap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from node %u\n", options.pnn)); return ret; } capabilities = talloc_array(ctdb, uint32_t, nodemap->num); CTDB_NO_MEMORY(ctdb, capabilities); /* collect capabilities for all connected nodes */ for (i=0; inum; i++) { if (nodemap->nodes[i].flags & NODE_FLAGS_INACTIVE) { continue; } if (nodemap->nodes[i].flags & NODE_FLAGS_PERMANENTLY_DISABLED) { continue; } ret = ctdb_ctrl_getcapabilities(ctdb, TIMELIMIT(), i, &capabilities[i]); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get capabilities from node %u\n", i)); return ret; } if (!(capabilities[i] & CTDB_CAP_LVS)) { continue; } if (!(nodemap->nodes[i].flags & NODE_FLAGS_UNHEALTHY)) { healthy_count++; } } /* Print all LVS nodes */ for (i=0; inum; i++) { if (nodemap->nodes[i].flags & NODE_FLAGS_INACTIVE) { continue; } if (nodemap->nodes[i].flags & NODE_FLAGS_PERMANENTLY_DISABLED) { continue; } if (!(capabilities[i] & CTDB_CAP_LVS)) { continue; } if (healthy_count != 0) { if (nodemap->nodes[i].flags & NODE_FLAGS_UNHEALTHY) { continue; } } printf("%d:%s\n", i, ctdb_addr_to_str(&nodemap->nodes[i].addr)); } return 0; } /* display who is the lvs master */ static int control_lvsmaster(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t *capabilities; struct ctdb_node_map *nodemap=NULL; int i, ret; int healthy_count = 0; ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), options.pnn, ctdb, &nodemap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from node %u\n", options.pnn)); return ret; } capabilities = talloc_array(ctdb, uint32_t, nodemap->num); CTDB_NO_MEMORY(ctdb, capabilities); /* collect capabilities for all connected nodes */ for (i=0; inum; i++) { if (nodemap->nodes[i].flags & NODE_FLAGS_INACTIVE) { continue; } if (nodemap->nodes[i].flags & NODE_FLAGS_PERMANENTLY_DISABLED) { continue; } ret = ctdb_ctrl_getcapabilities(ctdb, TIMELIMIT(), i, &capabilities[i]); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get capabilities from node %u\n", i)); return ret; } if (!(capabilities[i] & CTDB_CAP_LVS)) { continue; } if (!(nodemap->nodes[i].flags & NODE_FLAGS_UNHEALTHY)) { healthy_count++; } } /* find and show the lvsmaster */ for (i=0; inum; i++) { if (nodemap->nodes[i].flags & NODE_FLAGS_INACTIVE) { continue; } if (nodemap->nodes[i].flags & NODE_FLAGS_PERMANENTLY_DISABLED) { continue; } if (!(capabilities[i] & CTDB_CAP_LVS)) { continue; } if (healthy_count != 0) { if (nodemap->nodes[i].flags & NODE_FLAGS_UNHEALTHY) { continue; } } if (options.machinereadable){ printf("%d\n", i); } else { printf("Node %d is LVS master\n", i); } return 0; } printf("There is no LVS master\n"); return -1; } /* disable monitoring on a node */ static int control_disable_monmode(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; ret = ctdb_ctrl_disable_monmode(ctdb, TIMELIMIT(), options.pnn); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to disable monmode on node %u\n", options.pnn)); return ret; } printf("Monitoring mode:%s\n","DISABLED"); return 0; } /* enable monitoring on a node */ static int control_enable_monmode(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; ret = ctdb_ctrl_enable_monmode(ctdb, TIMELIMIT(), options.pnn); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to enable monmode on node %u\n", options.pnn)); return ret; } printf("Monitoring mode:%s\n","ACTIVE"); return 0; } /* display remote list of keys/data for a db */ static int control_catdb(struct ctdb_context *ctdb, int argc, const char **argv) { const char *db_name; struct ctdb_db_context *ctdb_db; int ret; bool persistent; struct ctdb_dump_db_context c; if (argc < 1) { usage(); } db_name = argv[0]; if (db_exists(ctdb, db_name, &persistent)) { DEBUG(DEBUG_ERR,("Database '%s' does not exist\n", db_name)); return -1; } ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), db_name, persistent, 0); if (ctdb_db == NULL) { DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name)); return -1; } if (options.printlmaster) { ret = ctdb_ctrl_getvnnmap(ctdb, TIMELIMIT(), options.pnn, ctdb, &ctdb->vnn_map); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get vnnmap from node %u\n", options.pnn)); return ret; } } ZERO_STRUCT(c); c.f = stdout; c.printemptyrecords = (bool)options.printemptyrecords; c.printdatasize = (bool)options.printdatasize; c.printlmaster = (bool)options.printlmaster; c.printhash = (bool)options.printhash; c.printrecordflags = (bool)options.printrecordflags; /* traverse and dump the cluster tdb */ ret = ctdb_dump_db(ctdb_db, &c); if (ret == -1) { DEBUG(DEBUG_ERR, ("Unable to dump database\n")); DEBUG(DEBUG_ERR, ("Maybe try 'ctdb getdbstatus %s'" " and 'ctdb getvar AllowUnhealthyDBRead'\n", db_name)); return -1; } talloc_free(ctdb_db); printf("Dumped %d records\n", ret); return 0; } struct cattdb_data { struct ctdb_context *ctdb; uint32_t count; }; static int cattdb_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *private_data) { struct cattdb_data *d = private_data; struct ctdb_dump_db_context c; d->count++; ZERO_STRUCT(c); c.f = stdout; c.printemptyrecords = (bool)options.printemptyrecords; c.printdatasize = (bool)options.printdatasize; c.printlmaster = false; c.printhash = (bool)options.printhash; c.printrecordflags = true; return ctdb_dumpdb_record(d->ctdb, key, data, &c); } /* cat the local tdb database using same format as catdb */ static int control_cattdb(struct ctdb_context *ctdb, int argc, const char **argv) { const char *db_name; struct ctdb_db_context *ctdb_db; struct cattdb_data d; bool persistent; if (argc < 1) { usage(); } db_name = argv[0]; if (db_exists(ctdb, db_name, &persistent)) { DEBUG(DEBUG_ERR,("Database '%s' does not exist\n", db_name)); return -1; } ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), db_name, false, 0); if (ctdb_db == NULL) { DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name)); return -1; } /* traverse the local tdb */ d.count = 0; d.ctdb = ctdb; if (tdb_traverse_read(ctdb_db->ltdb->tdb, cattdb_traverse, &d) == -1) { printf("Failed to cattdb data\n"); exit(10); } talloc_free(ctdb_db); printf("Dumped %d records\n", d.count); return 0; } /* display the content of a database key */ static int control_readkey(struct ctdb_context *ctdb, int argc, const char **argv) { const char *db_name; struct ctdb_db_context *ctdb_db; struct ctdb_record_handle *h; TALLOC_CTX *tmp_ctx = talloc_new(ctdb); TDB_DATA key, data; bool persistent; if (argc < 2) { usage(); } db_name = argv[0]; if (db_exists(ctdb, db_name, &persistent)) { DEBUG(DEBUG_ERR,("Database '%s' does not exist\n", db_name)); return -1; } ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), db_name, persistent, 0); if (ctdb_db == NULL) { DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name)); return -1; } key.dptr = discard_const(argv[1]); key.dsize = strlen((char *)key.dptr); h = ctdb_fetch_lock(ctdb_db, tmp_ctx, key, &data); if (h == NULL) { printf("Failed to fetch record '%s' on node %d\n", (const char *)key.dptr, ctdb_get_pnn(ctdb)); talloc_free(tmp_ctx); exit(10); } printf("Data: size:%d ptr:[%s]\n", (int)data.dsize, data.dptr); talloc_free(ctdb_db); talloc_free(tmp_ctx); return 0; } /* display the content of a database key */ static int control_writekey(struct ctdb_context *ctdb, int argc, const char **argv) { const char *db_name; struct ctdb_db_context *ctdb_db; struct ctdb_record_handle *h; TALLOC_CTX *tmp_ctx = talloc_new(ctdb); TDB_DATA key, data; bool persistent; if (argc < 3) { usage(); } db_name = argv[0]; if (db_exists(ctdb, db_name, &persistent)) { DEBUG(DEBUG_ERR,("Database '%s' does not exist\n", db_name)); return -1; } ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), db_name, persistent, 0); if (ctdb_db == NULL) { DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name)); return -1; } key.dptr = discard_const(argv[1]); key.dsize = strlen((char *)key.dptr); h = ctdb_fetch_lock(ctdb_db, tmp_ctx, key, &data); if (h == NULL) { printf("Failed to fetch record '%s' on node %d\n", (const char *)key.dptr, ctdb_get_pnn(ctdb)); talloc_free(tmp_ctx); exit(10); } data.dptr = discard_const(argv[2]); data.dsize = strlen((char *)data.dptr); if (ctdb_record_store(h, data) != 0) { printf("Failed to store record\n"); } talloc_free(h); talloc_free(ctdb_db); talloc_free(tmp_ctx); return 0; } /* fetch a record from a persistent database */ static int control_pfetch(struct ctdb_context *ctdb, int argc, const char **argv) { const char *db_name; struct ctdb_db_context *ctdb_db; TALLOC_CTX *tmp_ctx = talloc_new(ctdb); struct ctdb_transaction_handle *h; TDB_DATA key, data; int fd, ret; bool persistent; if (argc < 2) { talloc_free(tmp_ctx); usage(); } db_name = argv[0]; if (db_exists(ctdb, db_name, &persistent)) { DEBUG(DEBUG_ERR,("Database '%s' does not exist\n", db_name)); talloc_free(tmp_ctx); return -1; } if (!persistent) { DEBUG(DEBUG_ERR,("Database '%s' is not persistent\n", db_name)); talloc_free(tmp_ctx); return -1; } ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), db_name, persistent, 0); if (ctdb_db == NULL) { DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name)); talloc_free(tmp_ctx); return -1; } h = ctdb_transaction_start(ctdb_db, tmp_ctx); if (h == NULL) { DEBUG(DEBUG_ERR,("Failed to start transaction on database %s\n", db_name)); talloc_free(tmp_ctx); return -1; } key.dptr = discard_const(argv[1]); key.dsize = strlen(argv[1]); ret = ctdb_transaction_fetch(h, tmp_ctx, key, &data); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to fetch record\n")); talloc_free(tmp_ctx); return -1; } if (data.dsize == 0 || data.dptr == NULL) { DEBUG(DEBUG_ERR,("Record is empty\n")); talloc_free(tmp_ctx); return -1; } if (argc == 3) { fd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0600); if (fd == -1) { DEBUG(DEBUG_ERR,("Failed to open output file %s\n", argv[2])); talloc_free(tmp_ctx); return -1; } write(fd, data.dptr, data.dsize); close(fd); } else { write(1, data.dptr, data.dsize); } /* abort the transaction */ talloc_free(h); talloc_free(tmp_ctx); return 0; } /* fetch a record from a tdb-file */ static int control_tfetch(struct ctdb_context *ctdb, int argc, const char **argv) { const char *tdb_file; TDB_CONTEXT *tdb; TDB_DATA key, data; TALLOC_CTX *tmp_ctx = talloc_new(NULL); int fd; if (argc < 2) { usage(); } tdb_file = argv[0]; tdb = tdb_open(tdb_file, 0, 0, O_RDONLY, 0); if (tdb == NULL) { printf("Failed to open TDB file %s\n", tdb_file); return -1; } if (!strncmp(argv[1], "0x", 2)) { key = hextodata(tmp_ctx, argv[1] + 2); if (key.dsize == 0) { printf("Failed to convert \"%s\" into a TDB_DATA\n", argv[1]); return -1; } } else { key.dptr = discard_const(argv[1]); key.dsize = strlen(argv[1]); } data = tdb_fetch(tdb, key); if (data.dptr == NULL || data.dsize < sizeof(struct ctdb_ltdb_header)) { printf("Failed to read record %s from tdb %s\n", argv[1], tdb_file); tdb_close(tdb); return -1; } tdb_close(tdb); if (argc == 3) { fd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0600); if (fd == -1) { printf("Failed to open output file %s\n", argv[2]); return -1; } if (options.verbose){ write(fd, data.dptr, data.dsize); } else { write(fd, data.dptr+sizeof(struct ctdb_ltdb_header), data.dsize-sizeof(struct ctdb_ltdb_header)); } close(fd); } else { if (options.verbose){ write(1, data.dptr, data.dsize); } else { write(1, data.dptr+sizeof(struct ctdb_ltdb_header), data.dsize-sizeof(struct ctdb_ltdb_header)); } } talloc_free(tmp_ctx); return 0; } /* store a record and header to a tdb-file */ static int control_tstore(struct ctdb_context *ctdb, int argc, const char **argv) { const char *tdb_file; TDB_CONTEXT *tdb; TDB_DATA key, data; TALLOC_CTX *tmp_ctx = talloc_new(NULL); if (argc < 3) { usage(); } tdb_file = argv[0]; tdb = tdb_open(tdb_file, 0, 0, O_RDWR, 0); if (tdb == NULL) { printf("Failed to open TDB file %s\n", tdb_file); return -1; } if (!strncmp(argv[1], "0x", 2)) { key = hextodata(tmp_ctx, argv[1] + 2); if (key.dsize == 0) { printf("Failed to convert \"%s\" into a TDB_DATA\n", argv[1]); return -1; } } else { key.dptr = discard_const(argv[1]); key.dsize = strlen(argv[1]); } if (!strncmp(argv[2], "0x", 2)) { data = hextodata(tmp_ctx, argv[2] + 2); if (data.dsize == 0) { printf("Failed to convert \"%s\" into a TDB_DATA\n", argv[2]); return -1; } } else { data.dptr = discard_const(argv[2]); data.dsize = strlen(argv[2]); } if (data.dsize < sizeof(struct ctdb_ltdb_header)) { printf("Not enough data. You must specify the full ctdb_ltdb_header too when storing\n"); return -1; } if (tdb_store(tdb, key, data, TDB_REPLACE) != 0) { printf("Failed to write record %s to tdb %s\n", argv[1], tdb_file); tdb_close(tdb); return -1; } tdb_close(tdb); talloc_free(tmp_ctx); return 0; } /* write a record to a persistent database */ static int control_pstore(struct ctdb_context *ctdb, int argc, const char **argv) { const char *db_name; struct ctdb_db_context *ctdb_db; TALLOC_CTX *tmp_ctx = talloc_new(ctdb); struct ctdb_transaction_handle *h; struct stat st; TDB_DATA key, data; int fd, ret; if (argc < 3) { talloc_free(tmp_ctx); usage(); } fd = open(argv[2], O_RDONLY); if (fd == -1) { DEBUG(DEBUG_ERR,("Failed to open file containing record data : %s %s\n", argv[2], strerror(errno))); talloc_free(tmp_ctx); return -1; } ret = fstat(fd, &st); if (ret == -1) { DEBUG(DEBUG_ERR,("fstat of file %s failed: %s\n", argv[2], strerror(errno))); close(fd); talloc_free(tmp_ctx); return -1; } if (!S_ISREG(st.st_mode)) { DEBUG(DEBUG_ERR,("Not a regular file %s\n", argv[2])); close(fd); talloc_free(tmp_ctx); return -1; } data.dsize = st.st_size; if (data.dsize == 0) { data.dptr = NULL; } else { data.dptr = talloc_size(tmp_ctx, data.dsize); if (data.dptr == NULL) { DEBUG(DEBUG_ERR,("Failed to talloc %d of memory to store record data\n", (int)data.dsize)); close(fd); talloc_free(tmp_ctx); return -1; } ret = read(fd, data.dptr, data.dsize); if (ret != data.dsize) { DEBUG(DEBUG_ERR,("Failed to read %d bytes of record data\n", (int)data.dsize)); close(fd); talloc_free(tmp_ctx); return -1; } } close(fd); db_name = argv[0]; ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), db_name, true, 0); if (ctdb_db == NULL) { DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name)); talloc_free(tmp_ctx); return -1; } h = ctdb_transaction_start(ctdb_db, tmp_ctx); if (h == NULL) { DEBUG(DEBUG_ERR,("Failed to start transaction on database %s\n", db_name)); talloc_free(tmp_ctx); return -1; } key.dptr = discard_const(argv[1]); key.dsize = strlen(argv[1]); ret = ctdb_transaction_store(h, key, data); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to store record\n")); talloc_free(tmp_ctx); return -1; } ret = ctdb_transaction_commit(h); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to commit transaction\n")); talloc_free(tmp_ctx); return -1; } talloc_free(tmp_ctx); return 0; } /* check if a service is bound to a port or not */ static int control_chktcpport(struct ctdb_context *ctdb, int argc, const char **argv) { int s, ret; unsigned v; int port; struct sockaddr_in sin; if (argc != 1) { printf("Use: ctdb chktcport \n"); return EINVAL; } port = atoi(argv[0]); s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (s == -1) { printf("Failed to open local socket\n"); return errno; } v = fcntl(s, F_GETFL, 0); fcntl(s, F_SETFL, v | O_NONBLOCK); bzero(&sin, sizeof(sin)); sin.sin_family = PF_INET; sin.sin_port = htons(port); ret = bind(s, (struct sockaddr *)&sin, sizeof(sin)); close(s); if (ret == -1) { printf("Failed to bind to local socket: %d %s\n", errno, strerror(errno)); return errno; } return 0; } static void log_handler(struct ctdb_context *ctdb, uint64_t srvid, TDB_DATA data, void *private_data) { DEBUG(DEBUG_ERR,("Log data received\n")); if (data.dsize > 0) { printf("%s", data.dptr); } exit(0); } /* display a list of log messages from the in memory ringbuffer */ static int control_getlog(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; int32_t res; struct ctdb_get_log_addr log_addr; TDB_DATA data; TALLOC_CTX *tmp_ctx = talloc_new(ctdb); char *errmsg; struct timeval tv; if (argc != 1) { DEBUG(DEBUG_ERR,("Invalid arguments\n")); talloc_free(tmp_ctx); return -1; } log_addr.pnn = ctdb_ctrl_getpnn(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE); log_addr.srvid = getpid(); if (isalpha(argv[0][0]) || argv[0][0] == '-') { log_addr.level = get_debug_by_desc(argv[0]); } else { log_addr.level = strtol(argv[0], NULL, 0); } data.dptr = (unsigned char *)&log_addr; data.dsize = sizeof(log_addr); DEBUG(DEBUG_ERR, ("Pulling logs from node %u\n", options.pnn)); ctdb_client_set_message_handler(ctdb, log_addr.srvid, log_handler, NULL); sleep(1); DEBUG(DEBUG_ERR,("Listen for response on %d\n", (int)log_addr.srvid)); ret = ctdb_control(ctdb, options.pnn, 0, CTDB_CONTROL_GET_LOG, 0, data, tmp_ctx, NULL, &res, NULL, &errmsg); if (ret != 0 || res != 0) { DEBUG(DEBUG_ERR,("Failed to get logs - %s\n", errmsg)); talloc_free(tmp_ctx); return -1; } tv = timeval_current(); /* this loop will terminate when we have received the reply */ while (timeval_elapsed(&tv) < 3.0) { event_loop_once(ctdb->ev); } DEBUG(DEBUG_INFO,("Timed out waiting for log data.\n")); talloc_free(tmp_ctx); return 0; } /* clear the in memory log area */ static int control_clearlog(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; int32_t res; char *errmsg; TALLOC_CTX *tmp_ctx = talloc_new(ctdb); ret = ctdb_control(ctdb, options.pnn, 0, CTDB_CONTROL_CLEAR_LOG, 0, tdb_null, tmp_ctx, NULL, &res, NULL, &errmsg); if (ret != 0 || res != 0) { DEBUG(DEBUG_ERR,("Failed to clear logs\n")); talloc_free(tmp_ctx); return -1; } talloc_free(tmp_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) { int i, ret; struct ctdb_dbid_map *dbmap=NULL; ret = ctdb_ctrl_getdbmap(ctdb, TIMELIMIT(), options.pnn, ctdb, &dbmap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get dbids from node %u\n", options.pnn)); return ret; } if(options.machinereadable){ printf(":ID:Name:Path:Persistent:Sticky:Unhealthy:ReadOnly:\n"); for(i=0;inum;i++){ const char *path; const char *name; const char *health; bool persistent; bool readonly; bool sticky; ctdb_ctrl_getdbpath(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &path); ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &name); ctdb_ctrl_getdbhealth(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &health); persistent = dbmap->dbs[i].flags & CTDB_DB_FLAGS_PERSISTENT; readonly = dbmap->dbs[i].flags & CTDB_DB_FLAGS_READONLY; sticky = dbmap->dbs[i].flags & CTDB_DB_FLAGS_STICKY; printf(":0x%08X:%s:%s:%d:%d:%d:%d:\n", dbmap->dbs[i].dbid, name, path, !!(persistent), !!(sticky), !!(health), !!(readonly)); } return 0; } printf("Number of databases:%d\n", dbmap->num); for(i=0;inum;i++){ const char *path; const char *name; const char *health; bool persistent; bool readonly; bool sticky; ctdb_ctrl_getdbpath(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &path); ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &name); ctdb_ctrl_getdbhealth(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &health); persistent = dbmap->dbs[i].flags & CTDB_DB_FLAGS_PERSISTENT; readonly = dbmap->dbs[i].flags & CTDB_DB_FLAGS_READONLY; sticky = dbmap->dbs[i].flags & CTDB_DB_FLAGS_STICKY; printf("dbid:0x%08x name:%s path:%s%s%s%s%s\n", dbmap->dbs[i].dbid, name, path, persistent?" PERSISTENT":"", sticky?" STICKY":"", readonly?" READONLY":"", health?" UNHEALTHY":""); } return 0; } /* display the status of a database on a remote ctdb */ static int control_getdbstatus(struct ctdb_context *ctdb, int argc, const char **argv) { int i, ret; struct ctdb_dbid_map *dbmap=NULL; const char *db_name; if (argc < 1) { usage(); } db_name = argv[0]; ret = ctdb_ctrl_getdbmap(ctdb, TIMELIMIT(), options.pnn, ctdb, &dbmap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get dbids from node %u\n", options.pnn)); return ret; } for(i=0;inum;i++){ const char *path; const char *name; const char *health; bool persistent; bool readonly; bool sticky; ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &name); if (strcmp(name, db_name) != 0) { continue; } ctdb_ctrl_getdbpath(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &path); ctdb_ctrl_getdbhealth(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &health); persistent = dbmap->dbs[i].flags & CTDB_DB_FLAGS_PERSISTENT; readonly = dbmap->dbs[i].flags & CTDB_DB_FLAGS_READONLY; sticky = dbmap->dbs[i].flags & CTDB_DB_FLAGS_STICKY; printf("dbid: 0x%08x\nname: %s\npath: %s\nPERSISTENT: %s\nSTICKY: %s\nREADONLY: %s\nHEALTH: %s\n", dbmap->dbs[i].dbid, name, path, persistent?"yes":"no", sticky?"yes":"no", readonly?"yes":"no", health?health:"OK"); return 0; } DEBUG(DEBUG_ERR, ("db %s doesn't exist on node %u\n", db_name, options.pnn)); return 0; } /* check if the local node is recmaster or not it will return 1 if this node is the recmaster and 0 if it is not or if the local ctdb daemon could not be contacted */ static int control_isnotrecmaster(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t mypnn, recmaster; mypnn = ctdb_ctrl_getpnn(ctdb, TIMELIMIT(), options.pnn); if (mypnn == -1) { printf("Failed to get pnn of node\n"); return 1; } if (!ctdb_getrecmaster(ctdb_connection, options.pnn, &recmaster)) { printf("Failed to get the recmaster\n"); return 1; } if (recmaster != mypnn) { printf("this node is not the recmaster\n"); return 1; } printf("this node is the recmaster\n"); return 0; } /* ping a node */ static int control_ping(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; struct timeval tv = timeval_current(); ret = ctdb_ctrl_ping(ctdb, options.pnn); if (ret == -1) { printf("Unable to get ping response from node %u\n", options.pnn); return -1; } else { printf("response from %u time=%.6f sec (%d clients)\n", options.pnn, timeval_elapsed(&tv), ret); } return 0; } /* get a tunable */ static int control_getvar(struct ctdb_context *ctdb, int argc, const char **argv) { const char *name; uint32_t value; int ret; if (argc < 1) { usage(); } name = argv[0]; ret = ctdb_ctrl_get_tunable(ctdb, TIMELIMIT(), options.pnn, name, &value); if (ret == -1) { DEBUG(DEBUG_ERR, ("Unable to get tunable variable '%s'\n", name)); return -1; } printf("%-23s = %u\n", name, value); return 0; } /* set a tunable */ static int control_setvar(struct ctdb_context *ctdb, int argc, const char **argv) { const char *name; uint32_t value; int ret; if (argc < 2) { usage(); } name = argv[0]; value = strtoul(argv[1], NULL, 0); ret = ctdb_ctrl_set_tunable(ctdb, TIMELIMIT(), options.pnn, name, value); if (ret == -1) { DEBUG(DEBUG_ERR, ("Unable to set tunable variable '%s'\n", name)); return -1; } return 0; } /* list all tunables */ static int control_listvars(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t count; const char **list; int ret, i; ret = ctdb_ctrl_list_tunables(ctdb, TIMELIMIT(), options.pnn, ctdb, &list, &count); if (ret == -1) { DEBUG(DEBUG_ERR, ("Unable to list tunable variables\n")); return -1; } for (i=0;i 2) { usage(); } if (argc == 2) { if (strcmp(argv[1], "persistent") != 0) { usage(); } persistent = true; } ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), db_name, persistent, 0); if (ctdb_db == NULL) { DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name)); return -1; } return 0; } /* set db priority */ static int control_setdbprio(struct ctdb_context *ctdb, int argc, const char **argv) { struct ctdb_db_priority db_prio; int ret; if (argc < 2) { usage(); } db_prio.db_id = strtoul(argv[0], NULL, 0); db_prio.priority = strtoul(argv[1], NULL, 0); ret = ctdb_ctrl_set_db_priority(ctdb, TIMELIMIT(), options.pnn, &db_prio); if (ret != 0) { DEBUG(DEBUG_ERR,("Unable to set db prio\n")); return -1; } return 0; } /* get db priority */ static int control_getdbprio(struct ctdb_context *ctdb, int argc, const char **argv) { uint32_t db_id, priority; int ret; if (argc < 1) { usage(); } db_id = strtoul(argv[0], NULL, 0); ret = ctdb_ctrl_get_db_priority(ctdb, TIMELIMIT(), options.pnn, db_id, &priority); if (ret != 0) { DEBUG(DEBUG_ERR,("Unable to get db prio\n")); return -1; } DEBUG(DEBUG_ERR,("Priority:%u\n", priority)); return 0; } /* set the sticky records capability for a database */ static int control_setdbsticky(struct ctdb_context *ctdb, int argc, const char **argv) { TALLOC_CTX *tmp_ctx = talloc_new(ctdb); uint32_t db_id; struct ctdb_dbid_map *dbmap=NULL; int i, ret; if (argc < 1) { usage(); } if (!strncmp(argv[0], "0x", 2)) { db_id = strtoul(argv[0] + 2, NULL, 0); } else { ret = ctdb_ctrl_getdbmap(ctdb, TIMELIMIT(), options.pnn, tmp_ctx, &dbmap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get dbids from node %u\n", options.pnn)); talloc_free(tmp_ctx); return ret; } for(i=0;inum;i++){ const char *name; ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, tmp_ctx, &name); if(!strcmp(argv[0], name)){ talloc_free(discard_const(name)); break; } talloc_free(discard_const(name)); } if (i == dbmap->num) { DEBUG(DEBUG_ERR,("No database with name '%s' found\n", argv[0])); talloc_free(tmp_ctx); return -1; } db_id = dbmap->dbs[i].dbid; } ret = ctdb_ctrl_set_db_sticky(ctdb, options.pnn, db_id); if (ret != 0) { DEBUG(DEBUG_ERR,("Unable to set db to support sticky records\n")); talloc_free(tmp_ctx); return -1; } talloc_free(tmp_ctx); return 0; } /* set the readonly capability for a database */ static int control_setdbreadonly(struct ctdb_context *ctdb, int argc, const char **argv) { TALLOC_CTX *tmp_ctx = talloc_new(ctdb); uint32_t db_id; struct ctdb_dbid_map *dbmap=NULL; int i, ret; if (argc < 1) { usage(); } if (!strncmp(argv[0], "0x", 2)) { db_id = strtoul(argv[0] + 2, NULL, 0); } else { ret = ctdb_ctrl_getdbmap(ctdb, TIMELIMIT(), options.pnn, tmp_ctx, &dbmap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get dbids from node %u\n", options.pnn)); talloc_free(tmp_ctx); return ret; } for(i=0;inum;i++){ const char *name; ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, tmp_ctx, &name); if(!strcmp(argv[0], name)){ talloc_free(discard_const(name)); break; } talloc_free(discard_const(name)); } if (i == dbmap->num) { DEBUG(DEBUG_ERR,("No database with name '%s' found\n", argv[0])); talloc_free(tmp_ctx); return -1; } db_id = dbmap->dbs[i].dbid; } ret = ctdb_ctrl_set_db_readonly(ctdb, options.pnn, db_id); if (ret != 0) { DEBUG(DEBUG_ERR,("Unable to set db to support readonly\n")); talloc_free(tmp_ctx); return -1; } talloc_free(tmp_ctx); return 0; } /* get db seqnum */ static int control_getdbseqnum(struct ctdb_context *ctdb, int argc, const char **argv) { bool ret; uint32_t db_id; uint64_t seqnum; if (argc < 1) { usage(); } db_id = strtoul(argv[0], NULL, 0); ret = ctdb_getdbseqnum(ctdb_connection, options.pnn, db_id, &seqnum); if (!ret) { DEBUG(DEBUG_ERR, ("Unable to get seqnum from node.")); return -1; } printf("Sequence number:%lld\n", (long long)seqnum); return 0; } /* run an eventscript on a node */ static int control_eventscript(struct ctdb_context *ctdb, int argc, const char **argv) { TDB_DATA data; int ret; int32_t res; char *errmsg; TALLOC_CTX *tmp_ctx = talloc_new(ctdb); if (argc != 1) { DEBUG(DEBUG_ERR,("Invalid arguments\n")); return -1; } data.dptr = (unsigned char *)discard_const(argv[0]); data.dsize = strlen((char *)data.dptr) + 1; DEBUG(DEBUG_ERR, ("Running eventscripts with arguments \"%s\" on node %u\n", data.dptr, options.pnn)); ret = ctdb_control(ctdb, options.pnn, 0, CTDB_CONTROL_RUN_EVENTSCRIPTS, 0, data, tmp_ctx, NULL, &res, NULL, &errmsg); if (ret != 0 || res != 0) { DEBUG(DEBUG_ERR,("Failed to run eventscripts - %s\n", errmsg)); talloc_free(tmp_ctx); return -1; } talloc_free(tmp_ctx); return 0; } #define DB_VERSION 1 #define MAX_DB_NAME 64 struct db_file_header { unsigned long version; time_t timestamp; unsigned long persistent; unsigned long size; const char name[MAX_DB_NAME]; }; struct backup_data { struct ctdb_marshall_buffer *records; uint32_t len; uint32_t total; bool traverse_error; }; static int backup_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *private) { struct backup_data *bd = talloc_get_type(private, struct backup_data); struct ctdb_rec_data *rec; /* add the record */ rec = ctdb_marshall_record(bd->records, 0, key, NULL, data); if (rec == NULL) { bd->traverse_error = true; DEBUG(DEBUG_ERR,("Failed to marshall record\n")); return -1; } bd->records = talloc_realloc_size(NULL, bd->records, rec->length + bd->len); if (bd->records == NULL) { DEBUG(DEBUG_ERR,("Failed to expand marshalling buffer\n")); bd->traverse_error = true; return -1; } bd->records->count++; memcpy(bd->len+(uint8_t *)bd->records, rec, rec->length); bd->len += rec->length; talloc_free(rec); bd->total++; return 0; } /* * backup a database to a file */ static int control_backupdb(struct ctdb_context *ctdb, int argc, const char **argv) { int i, ret; struct ctdb_dbid_map *dbmap=NULL; TALLOC_CTX *tmp_ctx = talloc_new(ctdb); struct db_file_header dbhdr; struct ctdb_db_context *ctdb_db; struct backup_data *bd; int fh = -1; int status = -1; const char *reason = NULL; if (argc != 2) { DEBUG(DEBUG_ERR,("Invalid arguments\n")); return -1; } ret = ctdb_ctrl_getdbmap(ctdb, TIMELIMIT(), options.pnn, tmp_ctx, &dbmap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get dbids from node %u\n", options.pnn)); return ret; } for(i=0;inum;i++){ const char *name; ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, tmp_ctx, &name); if(!strcmp(argv[0], name)){ talloc_free(discard_const(name)); break; } talloc_free(discard_const(name)); } if (i == dbmap->num) { DEBUG(DEBUG_ERR,("No database with name '%s' found\n", argv[0])); talloc_free(tmp_ctx); return -1; } ret = ctdb_ctrl_getdbhealth(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, tmp_ctx, &reason); if (ret != 0) { DEBUG(DEBUG_ERR,("Unable to get dbhealth for database '%s'\n", argv[0])); talloc_free(tmp_ctx); return -1; } if (reason) { uint32_t allow_unhealthy = 0; ctdb_ctrl_get_tunable(ctdb, TIMELIMIT(), options.pnn, "AllowUnhealthyDBRead", &allow_unhealthy); if (allow_unhealthy != 1) { DEBUG(DEBUG_ERR,("database '%s' is unhealthy: %s\n", argv[0], reason)); DEBUG(DEBUG_ERR,("disallow backup : tunable AllowUnhealthyDBRead = %u\n", allow_unhealthy)); talloc_free(tmp_ctx); return -1; } DEBUG(DEBUG_WARNING,("WARNING database '%s' is unhealthy - see 'ctdb getdbstatus %s'\n", argv[0], argv[0])); DEBUG(DEBUG_WARNING,("WARNING! allow backup of unhealthy database: " "tunnable AllowUnhealthyDBRead = %u\n", allow_unhealthy)); } ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), argv[0], dbmap->dbs[i].flags & CTDB_DB_FLAGS_PERSISTENT, 0); if (ctdb_db == NULL) { DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", argv[0])); talloc_free(tmp_ctx); return -1; } ret = tdb_transaction_start(ctdb_db->ltdb->tdb); if (ret == -1) { DEBUG(DEBUG_ERR,("Failed to start transaction\n")); talloc_free(tmp_ctx); return -1; } bd = talloc_zero(tmp_ctx, struct backup_data); if (bd == NULL) { DEBUG(DEBUG_ERR,("Failed to allocate backup_data\n")); talloc_free(tmp_ctx); return -1; } bd->records = talloc_zero(bd, struct ctdb_marshall_buffer); if (bd->records == NULL) { DEBUG(DEBUG_ERR,("Failed to allocate ctdb_marshall_buffer\n")); talloc_free(tmp_ctx); return -1; } bd->len = offsetof(struct ctdb_marshall_buffer, data); bd->records->db_id = ctdb_db->db_id; /* traverse the database collecting all records */ if (tdb_traverse_read(ctdb_db->ltdb->tdb, backup_traverse, bd) == -1 || bd->traverse_error) { DEBUG(DEBUG_ERR,("Traverse error\n")); talloc_free(tmp_ctx); return -1; } tdb_transaction_cancel(ctdb_db->ltdb->tdb); fh = open(argv[1], O_RDWR|O_CREAT, 0600); if (fh == -1) { DEBUG(DEBUG_ERR,("Failed to open file '%s'\n", argv[1])); talloc_free(tmp_ctx); return -1; } dbhdr.version = DB_VERSION; dbhdr.timestamp = time(NULL); dbhdr.persistent = dbmap->dbs[i].flags & CTDB_DB_FLAGS_PERSISTENT; dbhdr.size = bd->len; if (strlen(argv[0]) >= MAX_DB_NAME) { DEBUG(DEBUG_ERR,("Too long dbname\n")); goto done; } strncpy(discard_const(dbhdr.name), argv[0], MAX_DB_NAME); ret = write(fh, &dbhdr, sizeof(dbhdr)); if (ret == -1) { DEBUG(DEBUG_ERR,("write failed: %s\n", strerror(errno))); goto done; } ret = write(fh, bd->records, bd->len); if (ret == -1) { DEBUG(DEBUG_ERR,("write failed: %s\n", strerror(errno))); goto done; } status = 0; done: if (fh != -1) { ret = close(fh); if (ret == -1) { DEBUG(DEBUG_ERR,("close failed: %s\n", strerror(errno))); } } DEBUG(DEBUG_ERR,("Database backed up to %s\n", argv[1])); talloc_free(tmp_ctx); return status; } /* * restore a database from a file */ static int control_restoredb(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; TALLOC_CTX *tmp_ctx = talloc_new(ctdb); TDB_DATA outdata; TDB_DATA data; struct db_file_header dbhdr; struct ctdb_db_context *ctdb_db; struct ctdb_node_map *nodemap=NULL; struct ctdb_vnn_map *vnnmap=NULL; int i, fh; struct ctdb_control_wipe_database w; uint32_t *nodes; uint32_t generation; struct tm *tm; char tbuf[100]; char *dbname; if (argc < 1 || argc > 2) { DEBUG(DEBUG_ERR,("Invalid arguments\n")); return -1; } fh = open(argv[0], O_RDONLY); if (fh == -1) { DEBUG(DEBUG_ERR,("Failed to open file '%s'\n", argv[0])); talloc_free(tmp_ctx); return -1; } read(fh, &dbhdr, sizeof(dbhdr)); if (dbhdr.version != DB_VERSION) { DEBUG(DEBUG_ERR,("Invalid version of database dump. File is version %lu but expected version was %u\n", dbhdr.version, DB_VERSION)); talloc_free(tmp_ctx); return -1; } dbname = discard_const(dbhdr.name); if (argc == 2) { dbname = discard_const(argv[1]); } outdata.dsize = dbhdr.size; outdata.dptr = talloc_size(tmp_ctx, outdata.dsize); if (outdata.dptr == NULL) { DEBUG(DEBUG_ERR,("Failed to allocate data of size '%lu'\n", dbhdr.size)); close(fh); talloc_free(tmp_ctx); return -1; } read(fh, outdata.dptr, outdata.dsize); close(fh); tm = localtime(&dbhdr.timestamp); strftime(tbuf,sizeof(tbuf)-1,"%Y/%m/%d %H:%M:%S", tm); printf("Restoring database '%s' from backup @ %s\n", dbname, tbuf); ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), dbname, dbhdr.persistent, 0); if (ctdb_db == NULL) { DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", dbname)); talloc_free(tmp_ctx); return -1; } ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), options.pnn, ctdb, &nodemap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from node %u\n", options.pnn)); talloc_free(tmp_ctx); return ret; } ret = ctdb_ctrl_getvnnmap(ctdb, TIMELIMIT(), options.pnn, tmp_ctx, &vnnmap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get vnnmap from node %u\n", options.pnn)); talloc_free(tmp_ctx); return ret; } /* freeze all nodes */ nodes = list_of_active_nodes(ctdb, nodemap, tmp_ctx, true); for (i=1; i<=NUM_DB_PRIORITIES; i++) { if (ctdb_client_async_control(ctdb, CTDB_CONTROL_FREEZE, nodes, i, TIMELIMIT(), false, tdb_null, NULL, NULL, NULL) != 0) { DEBUG(DEBUG_ERR, ("Unable to freeze nodes.\n")); ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.pnn, CTDB_RECOVERY_ACTIVE); talloc_free(tmp_ctx); return -1; } } generation = vnnmap->generation; data.dptr = (void *)&generation; data.dsize = sizeof(generation); /* start a cluster wide transaction */ nodes = list_of_active_nodes(ctdb, nodemap, tmp_ctx, true); if (ctdb_client_async_control(ctdb, CTDB_CONTROL_TRANSACTION_START, nodes, 0, TIMELIMIT(), false, data, NULL, NULL, NULL) != 0) { DEBUG(DEBUG_ERR, ("Unable to start cluster wide transactions.\n")); return -1; } w.db_id = ctdb_db->db_id; w.transaction_id = generation; data.dptr = (void *)&w; data.dsize = sizeof(w); /* wipe all the remote databases. */ nodes = list_of_active_nodes(ctdb, nodemap, tmp_ctx, true); if (ctdb_client_async_control(ctdb, CTDB_CONTROL_WIPE_DATABASE, nodes, 0, TIMELIMIT(), false, data, NULL, NULL, NULL) != 0) { DEBUG(DEBUG_ERR, ("Unable to wipe database.\n")); ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.pnn, CTDB_RECOVERY_ACTIVE); talloc_free(tmp_ctx); return -1; } /* push the database */ nodes = list_of_active_nodes(ctdb, nodemap, tmp_ctx, true); if (ctdb_client_async_control(ctdb, CTDB_CONTROL_PUSH_DB, nodes, 0, TIMELIMIT(), false, outdata, NULL, NULL, NULL) != 0) { DEBUG(DEBUG_ERR, ("Failed to push database.\n")); ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.pnn, CTDB_RECOVERY_ACTIVE); talloc_free(tmp_ctx); return -1; } data.dptr = (void *)&ctdb_db->db_id; data.dsize = sizeof(ctdb_db->db_id); /* mark the database as healthy */ nodes = list_of_active_nodes(ctdb, nodemap, tmp_ctx, true); if (ctdb_client_async_control(ctdb, CTDB_CONTROL_DB_SET_HEALTHY, nodes, 0, TIMELIMIT(), false, data, NULL, NULL, NULL) != 0) { DEBUG(DEBUG_ERR, ("Failed to mark database as healthy.\n")); ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.pnn, CTDB_RECOVERY_ACTIVE); talloc_free(tmp_ctx); return -1; } data.dptr = (void *)&generation; data.dsize = sizeof(generation); /* commit all the changes */ if (ctdb_client_async_control(ctdb, CTDB_CONTROL_TRANSACTION_COMMIT, nodes, 0, TIMELIMIT(), false, data, NULL, NULL, NULL) != 0) { DEBUG(DEBUG_ERR, ("Unable to commit databases.\n")); ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.pnn, CTDB_RECOVERY_ACTIVE); talloc_free(tmp_ctx); return -1; } /* thaw all nodes */ nodes = list_of_active_nodes(ctdb, nodemap, tmp_ctx, true); if (ctdb_client_async_control(ctdb, CTDB_CONTROL_THAW, nodes, 0, TIMELIMIT(), false, tdb_null, NULL, NULL, NULL) != 0) { DEBUG(DEBUG_ERR, ("Unable to thaw nodes.\n")); ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.pnn, CTDB_RECOVERY_ACTIVE); talloc_free(tmp_ctx); return -1; } talloc_free(tmp_ctx); return 0; } /* * dump a database backup from a file */ static int control_dumpdbbackup(struct ctdb_context *ctdb, int argc, const char **argv) { TALLOC_CTX *tmp_ctx = talloc_new(ctdb); TDB_DATA outdata; struct db_file_header dbhdr; int i, fh; struct tm *tm; char tbuf[100]; struct ctdb_rec_data *rec = NULL; struct ctdb_marshall_buffer *m; struct ctdb_dump_db_context c; if (argc != 1) { DEBUG(DEBUG_ERR,("Invalid arguments\n")); return -1; } fh = open(argv[0], O_RDONLY); if (fh == -1) { DEBUG(DEBUG_ERR,("Failed to open file '%s'\n", argv[0])); talloc_free(tmp_ctx); return -1; } read(fh, &dbhdr, sizeof(dbhdr)); if (dbhdr.version != DB_VERSION) { DEBUG(DEBUG_ERR,("Invalid version of database dump. File is version %lu but expected version was %u\n", dbhdr.version, DB_VERSION)); talloc_free(tmp_ctx); return -1; } outdata.dsize = dbhdr.size; outdata.dptr = talloc_size(tmp_ctx, outdata.dsize); if (outdata.dptr == NULL) { DEBUG(DEBUG_ERR,("Failed to allocate data of size '%lu'\n", dbhdr.size)); close(fh); talloc_free(tmp_ctx); return -1; } read(fh, outdata.dptr, outdata.dsize); close(fh); m = (struct ctdb_marshall_buffer *)outdata.dptr; tm = localtime(&dbhdr.timestamp); strftime(tbuf,sizeof(tbuf)-1,"%Y/%m/%d %H:%M:%S", tm); printf("Backup of database name:'%s' dbid:0x%x08x from @ %s\n", dbhdr.name, m->db_id, tbuf); ZERO_STRUCT(c); c.f = stdout; c.printemptyrecords = (bool)options.printemptyrecords; c.printdatasize = (bool)options.printdatasize; c.printlmaster = false; c.printhash = (bool)options.printhash; c.printrecordflags = (bool)options.printrecordflags; for (i=0; i < m->count; i++) { uint32_t reqid = 0; TDB_DATA key, data; /* we do not want the header splitted, so we pass NULL*/ rec = ctdb_marshall_loop_next(m, rec, &reqid, NULL, &key, &data); ctdb_dumpdb_record(ctdb, key, data, &c); } printf("Dumped %d records\n", i); talloc_free(tmp_ctx); return 0; } /* * wipe a database from a file */ static int control_wipedb(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; TALLOC_CTX *tmp_ctx = talloc_new(ctdb); TDB_DATA data; struct ctdb_db_context *ctdb_db; struct ctdb_node_map *nodemap = NULL; struct ctdb_vnn_map *vnnmap = NULL; int i; struct ctdb_control_wipe_database w; uint32_t *nodes; uint32_t generation; struct ctdb_dbid_map *dbmap = NULL; if (argc != 1) { DEBUG(DEBUG_ERR,("Invalid arguments\n")); return -1; } ret = ctdb_ctrl_getdbmap(ctdb, TIMELIMIT(), options.pnn, tmp_ctx, &dbmap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get dbids from node %u\n", options.pnn)); return ret; } for(i=0;inum;i++){ const char *name; ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, tmp_ctx, &name); if(!strcmp(argv[0], name)){ talloc_free(discard_const(name)); break; } talloc_free(discard_const(name)); } if (i == dbmap->num) { DEBUG(DEBUG_ERR, ("No database with name '%s' found\n", argv[0])); talloc_free(tmp_ctx); return -1; } ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), argv[0], dbmap->dbs[i].flags & CTDB_DB_FLAGS_PERSISTENT, 0); if (ctdb_db == NULL) { DEBUG(DEBUG_ERR, ("Unable to attach to database '%s'\n", argv[0])); talloc_free(tmp_ctx); return -1; } ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), options.pnn, ctdb, &nodemap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from node %u\n", options.pnn)); talloc_free(tmp_ctx); return ret; } ret = ctdb_ctrl_getvnnmap(ctdb, TIMELIMIT(), options.pnn, tmp_ctx, &vnnmap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get vnnmap from node %u\n", options.pnn)); talloc_free(tmp_ctx); return ret; } /* freeze all nodes */ nodes = list_of_active_nodes(ctdb, nodemap, tmp_ctx, true); for (i=1; i<=NUM_DB_PRIORITIES; i++) { ret = ctdb_client_async_control(ctdb, CTDB_CONTROL_FREEZE, nodes, i, TIMELIMIT(), false, tdb_null, NULL, NULL, NULL); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to freeze nodes.\n")); ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.pnn, CTDB_RECOVERY_ACTIVE); talloc_free(tmp_ctx); return -1; } } generation = vnnmap->generation; data.dptr = (void *)&generation; data.dsize = sizeof(generation); /* start a cluster wide transaction */ nodes = list_of_active_nodes(ctdb, nodemap, tmp_ctx, true); ret = ctdb_client_async_control(ctdb, CTDB_CONTROL_TRANSACTION_START, nodes, 0, TIMELIMIT(), false, data, NULL, NULL, NULL); if (ret!= 0) { DEBUG(DEBUG_ERR, ("Unable to start cluster wide " "transactions.\n")); return -1; } w.db_id = ctdb_db->db_id; w.transaction_id = generation; data.dptr = (void *)&w; data.dsize = sizeof(w); /* wipe all the remote databases. */ nodes = list_of_active_nodes(ctdb, nodemap, tmp_ctx, true); if (ctdb_client_async_control(ctdb, CTDB_CONTROL_WIPE_DATABASE, nodes, 0, TIMELIMIT(), false, data, NULL, NULL, NULL) != 0) { DEBUG(DEBUG_ERR, ("Unable to wipe database.\n")); ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.pnn, CTDB_RECOVERY_ACTIVE); talloc_free(tmp_ctx); return -1; } data.dptr = (void *)&ctdb_db->db_id; data.dsize = sizeof(ctdb_db->db_id); /* mark the database as healthy */ nodes = list_of_active_nodes(ctdb, nodemap, tmp_ctx, true); if (ctdb_client_async_control(ctdb, CTDB_CONTROL_DB_SET_HEALTHY, nodes, 0, TIMELIMIT(), false, data, NULL, NULL, NULL) != 0) { DEBUG(DEBUG_ERR, ("Failed to mark database as healthy.\n")); ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.pnn, CTDB_RECOVERY_ACTIVE); talloc_free(tmp_ctx); return -1; } data.dptr = (void *)&generation; data.dsize = sizeof(generation); /* commit all the changes */ if (ctdb_client_async_control(ctdb, CTDB_CONTROL_TRANSACTION_COMMIT, nodes, 0, TIMELIMIT(), false, data, NULL, NULL, NULL) != 0) { DEBUG(DEBUG_ERR, ("Unable to commit databases.\n")); ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.pnn, CTDB_RECOVERY_ACTIVE); talloc_free(tmp_ctx); return -1; } /* thaw all nodes */ nodes = list_of_active_nodes(ctdb, nodemap, tmp_ctx, true); if (ctdb_client_async_control(ctdb, CTDB_CONTROL_THAW, nodes, 0, TIMELIMIT(), false, tdb_null, NULL, NULL, NULL) != 0) { DEBUG(DEBUG_ERR, ("Unable to thaw nodes.\n")); ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.pnn, CTDB_RECOVERY_ACTIVE); talloc_free(tmp_ctx); return -1; } DEBUG(DEBUG_ERR, ("Database wiped.\n")); talloc_free(tmp_ctx); return 0; } /* dump memory usage */ static int control_dumpmemory(struct ctdb_context *ctdb, int argc, const char **argv) { TDB_DATA data; int ret; int32_t res; char *errmsg; TALLOC_CTX *tmp_ctx = talloc_new(ctdb); ret = ctdb_control(ctdb, options.pnn, 0, CTDB_CONTROL_DUMP_MEMORY, 0, tdb_null, tmp_ctx, &data, &res, NULL, &errmsg); if (ret != 0 || res != 0) { DEBUG(DEBUG_ERR,("Failed to dump memory - %s\n", errmsg)); talloc_free(tmp_ctx); return -1; } write(1, data.dptr, data.dsize); talloc_free(tmp_ctx); return 0; } /* handler for memory dumps */ static void mem_dump_handler(struct ctdb_context *ctdb, uint64_t srvid, TDB_DATA data, void *private_data) { write(1, data.dptr, data.dsize); exit(0); } /* dump memory usage on the recovery daemon */ static int control_rddumpmemory(struct ctdb_context *ctdb, int argc, const char **argv) { int ret; TDB_DATA data; struct rd_memdump_reply rd; rd.pnn = ctdb_ctrl_getpnn(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE); if (rd.pnn == -1) { DEBUG(DEBUG_ERR, ("Failed to get pnn of local node\n")); return -1; } rd.srvid = getpid(); /* register a message port for receiveing the reply so that we can receive the reply */ ctdb_client_set_message_handler(ctdb, rd.srvid, mem_dump_handler, NULL); data.dptr = (uint8_t *)&rd; data.dsize = sizeof(rd); ret = ctdb_client_send_message(ctdb, options.pnn, CTDB_SRVID_MEM_DUMP, data); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to send memdump request message to %u\n", options.pnn)); return -1; } /* this loop will terminate when we have received the reply */ while (1) { event_loop_once(ctdb->ev); } return 0; } /* send a message to a srvid */ static int control_msgsend(struct ctdb_context *ctdb, int argc, const char **argv) { unsigned long srvid; int ret; TDB_DATA data; if (argc < 2) { usage(); } srvid = strtoul(argv[0], NULL, 0); data.dptr = (uint8_t *)discard_const(argv[1]); data.dsize= strlen(argv[1]); ret = ctdb_client_send_message(ctdb, CTDB_BROADCAST_CONNECTED, srvid, data); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to send memdump request message to %u\n", options.pnn)); return -1; } return 0; } /* handler for msglisten */ static void msglisten_handler(struct ctdb_context *ctdb, uint64_t srvid, TDB_DATA data, void *private_data) { int i; printf("Message received: "); for (i=0;iev); } return 0; } /* list all nodes in the cluster we parse the nodes file directly */ static int control_listnodes(struct ctdb_context *ctdb, int argc, const char **argv) { TALLOC_CTX *mem_ctx = talloc_new(NULL); struct pnn_node *pnn_nodes; struct pnn_node *pnn_node; pnn_nodes = read_nodes_file(mem_ctx); if (pnn_nodes == NULL) { DEBUG(DEBUG_ERR,("Failed to read nodes file\n")); talloc_free(mem_ctx); return -1; } for(pnn_node=pnn_nodes;pnn_node;pnn_node=pnn_node->next) { ctdb_sock_addr addr; if (parse_ip(pnn_node->addr, NULL, 63999, &addr) == 0) { DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s' in nodes file\n", pnn_node->addr)); talloc_free(mem_ctx); return -1; } if (options.machinereadable){ printf(":%d:%s:\n", pnn_node->pnn, pnn_node->addr); } else { printf("%s\n", pnn_node->addr); } } talloc_free(mem_ctx); return 0; } /* reload the nodes file on the local node */ static int control_reload_nodes_file(struct ctdb_context *ctdb, int argc, const char **argv) { int i, ret; int mypnn; struct ctdb_node_map *nodemap=NULL; mypnn = ctdb_ctrl_getpnn(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE); if (mypnn == -1) { DEBUG(DEBUG_ERR, ("Failed to read pnn of local node\n")); return -1; } ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, ctdb, &nodemap); if (ret != 0) { DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n")); return ret; } /* reload the nodes file on all remote nodes */ for (i=0;inum;i++) { if (nodemap->nodes[i].pnn == mypnn) { continue; } DEBUG(DEBUG_NOTICE, ("Reloading nodes file on node %u\n", nodemap->nodes[i].pnn)); ret = ctdb_ctrl_reload_nodes_file(ctdb, TIMELIMIT(), nodemap->nodes[i].pnn); if (ret != 0) { DEBUG(DEBUG_ERR, ("ERROR: Failed to reload nodes file on node %u. You MUST fix that node manually!\n", nodemap->nodes[i].pnn)); } } /* reload the nodes file on the local node */ DEBUG(DEBUG_NOTICE, ("Reloading nodes file on node %u\n", mypnn)); ret = ctdb_ctrl_reload_nodes_file(ctdb, TIMELIMIT(), mypnn); if (ret != 0) { DEBUG(DEBUG_ERR, ("ERROR: Failed to reload nodes file on node %u. You MUST fix that node manually!\n", mypnn)); } /* initiate a recovery */ control_recover(ctdb, argc, argv); return 0; } static const struct { const char *name; int (*fn)(struct ctdb_context *, int, const char **); bool auto_all; bool without_daemon; /* can be run without daemon running ? */ const char *msg; const char *args; } ctdb_commands[] = { #ifdef CTDB_VERS { "version", control_version, true, false, "show version of ctdb" }, #endif { "status", control_status, true, false, "show node status" }, { "uptime", control_uptime, true, false, "show node uptime" }, { "ping", control_ping, true, false, "ping all nodes" }, { "getvar", control_getvar, true, false, "get a tunable variable", ""}, { "setvar", control_setvar, true, false, "set a tunable variable", " "}, { "listvars", control_listvars, true, false, "list tunable variables"}, { "statistics", control_statistics, false, false, "show statistics" }, { "statisticsreset", control_statistics_reset, true, false, "reset statistics"}, { "stats", control_stats, false, false, "show rolling statistics", "[number of history records]" }, { "ip", control_ip, false, false, "show which public ip's that ctdb manages" }, { "ipinfo", control_ipinfo, true, false, "show details about a public ip that ctdb manages", "" }, { "ifaces", control_ifaces, true, false, "show which interfaces that ctdb manages" }, { "setifacelink", control_setifacelink, true, false, "set interface link status", " " }, { "process-exists", control_process_exists, true, false, "check if a process exists on a node", ""}, { "getdbmap", control_getdbmap, true, false, "show the database map" }, { "getdbstatus", control_getdbstatus, true, false, "show the status of a database", "" }, { "catdb", control_catdb, true, false, "dump a ctdb database" , ""}, { "cattdb", control_cattdb, true, false, "dump a local tdb database" , ""}, { "getmonmode", control_getmonmode, true, false, "show monitoring mode" }, { "getcapabilities", control_getcapabilities, true, false, "show node capabilities" }, { "pnn", control_pnn, true, false, "show the pnn of the currnet node" }, { "lvs", control_lvs, true, false, "show lvs configuration" }, { "lvsmaster", control_lvsmaster, true, false, "show which node is the lvs master" }, { "disablemonitor", control_disable_monmode,true, false, "set monitoring mode to DISABLE" }, { "enablemonitor", control_enable_monmode, true, false, "set monitoring mode to ACTIVE" }, { "setdebug", control_setdebug, true, false, "set debug level", "" }, { "getdebug", control_getdebug, true, false, "get debug level" }, { "getlog", control_getlog, true, false, "get the log data from the in memory ringbuffer", "" }, { "clearlog", control_clearlog, true, false, "clear the log data from the in memory ringbuffer" }, { "attach", control_attach, true, false, "attach to a database", " [persistent]" }, { "dumpmemory", control_dumpmemory, true, false, "dump memory map to stdout" }, { "rddumpmemory", control_rddumpmemory, true, false, "dump memory map from the recovery daemon to stdout" }, { "getpid", control_getpid, true, false, "get ctdbd process ID" }, { "disable", control_disable, true, false, "disable a nodes public IP" }, { "enable", control_enable, true, false, "enable a nodes public IP" }, { "stop", control_stop, true, false, "stop a node" }, { "continue", control_continue, true, false, "re-start a stopped node" }, { "ban", control_ban, true, false, "ban a node from the cluster", ""}, { "unban", control_unban, true, false, "unban a node" }, { "showban", control_showban, true, false, "show ban information"}, { "shutdown", control_shutdown, true, false, "shutdown ctdbd" }, { "recover", control_recover, true, false, "force recovery" }, { "sync", control_ipreallocate, true, false, "wait until ctdbd has synced all state changes" }, { "ipreallocate", control_ipreallocate, true, false, "force the recovery daemon to perform a ip reallocation procedure" }, { "thaw", control_thaw, true, false, "thaw databases", "[priority:1-3]" }, { "isnotrecmaster", control_isnotrecmaster, false, false, "check if the local node is recmaster or not" }, { "killtcp", kill_tcp, false, false, "kill a tcp connection.", " " }, { "gratiousarp", control_gratious_arp, false, false, "send a gratious arp", " " }, { "tickle", tickle_tcp, false, false, "send a tcp tickle ack", " " }, { "gettickles", control_get_tickles, false, false, "get the list of tickles registered for this ip", " []" }, { "addtickle", control_add_tickle, false, false, "add a tickle for this ip", ": :" }, { "deltickle", control_del_tickle, false, false, "delete a tickle from this ip", ": :" }, { "regsrvid", regsrvid, false, false, "register a server id", " " }, { "unregsrvid", unregsrvid, false, false, "unregister a server id", " " }, { "chksrvid", chksrvid, false, false, "check if a server id exists", " " }, { "getsrvids", getsrvids, false, false, "get a list of all server ids"}, { "check_srvids", check_srvids, false, false, "check if a srvid exists", "+" }, { "vacuum", ctdb_vacuum, false, true, "vacuum the databases of empty records", "[max_records]"}, { "repack", ctdb_repack, false, false, "repack all databases", "[max_freelist]"}, { "listnodes", control_listnodes, false, true, "list all nodes in the cluster"}, { "reloadnodes", control_reload_nodes_file, false, false, "reload the nodes file and restart the transport on all nodes"}, { "moveip", control_moveip, false, false, "move/failover an ip address to another node", " "}, { "addip", control_addip, true, false, "add a ip address to a node", " "}, { "delip", control_delip, false, false, "delete an ip address from a node", ""}, { "eventscript", control_eventscript, true, false, "run the eventscript with the given parameters on a node", ""}, { "backupdb", control_backupdb, false, false, "backup the database into a file.", " "}, { "restoredb", control_restoredb, false, false, "restore the database from a file.", " [dbname]"}, { "dumpdbbackup", control_dumpdbbackup, false, true, "dump database backup from a file.", ""}, { "wipedb", control_wipedb, false, false, "wipe the contents of a database.", ""}, { "recmaster", control_recmaster, false, false, "show the pnn for the recovery master."}, { "scriptstatus", control_scriptstatus, true, false, "show the status of the monitoring scripts (or all scripts)", "[all]"}, { "enablescript", control_enablescript, false, false, "enable an eventscript", "