tools/ctdb: add "ctdb ipinfo <ip>"
[ctdb.git] / tools / ctdb.c
index 2cc7193f6fb5f7a300114a65df2787ba4796d154..5c49dbaa77790eea9f6861c7f28fdef805aad392 100644 (file)
@@ -502,18 +502,19 @@ static int control_status(struct ctdb_context *ctdb, int argc, const char **argv
        }
 
        if(options.machinereadable){
-               printf(":Node:IP:Disconnected:Banned:Disabled:Unhealthy:Stopped:\n");
+               printf(":Node:IP:Disconnected:Banned:Disabled:Unhealthy:Stopped:Inactive:\n");
                for(i=0;i<nodemap->num;i++){
                        if (nodemap->nodes[i].flags & NODE_FLAGS_DELETED) {
                                continue;
                        }
-                       printf(":%d:%s:%d:%d:%d:%d:%d:\n", nodemap->nodes[i].pnn,
+                       printf(":%d:%s:%d:%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));
+                              !!(nodemap->nodes[i].flags&NODE_FLAGS_STOPPED),
+                              !!(nodemap->nodes[i].flags&NODE_FLAGS_INACTIVE));
                }
                return 0;
        }
@@ -530,6 +531,7 @@ static int control_status(struct ctdb_context *ctdb, int argc, const char **argv
                        { NODE_FLAGS_UNHEALTHY,             "UNHEALTHY" },
                        { NODE_FLAGS_DELETED,               "DELETED" },
                        { NODE_FLAGS_STOPPED,               "STOPPED" },
+                       { NODE_FLAGS_INACTIVE,              "INACTIVE" },
                };
                char *flags_str = NULL;
                int j;
@@ -725,48 +727,141 @@ static int control_natgwlist(struct ctdb_context *ctdb, int argc, const char **a
        return 0;
 }
 
-
 /*
-  display the status of the monitoring scripts
+  display the status of the scripts for monitoring (or other events)
  */
-static int control_scriptstatus(struct ctdb_context *ctdb, int argc, const char **argv)
+static int control_one_scriptstatus(struct ctdb_context *ctdb,
+                                   enum ctdb_eventscript_call type)
 {
-       int i, ret;
-       struct ctdb_monitoring_wire *script_status;
+       struct ctdb_scripts_wire *script_status;
+       int ret, i;
 
-       ret = ctdb_ctrl_getscriptstatus(ctdb, TIMELIMIT(), options.pnn, ctdb, &script_status);
+       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;
        }
 
-       printf("%d scripts were executed last monitoring cycle\n", script_status->num_scripts);
+       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; i<script_status->num_scripts; i++) {
-               if (script_status->scripts[i].disabled) {
-                       printf("%-20s Status:DISABLED\n",
-                               script_status->scripts[i].name);
+               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;
-               } 
-               printf("%-20s Status:%s    ",
-                       script_status->scripts[i].name,
-                       script_status->scripts[i].timedout?"TIMEDOUT":script_status->scripts[i].status==0?"OK":"ERROR");
-               if (script_status->scripts[i].timedout == 0) {
+               }
+               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));
                }
-               printf("%s",
-                       ctime(&script_status->scripts[i].start.tv_sec));
-               if ((script_status->scripts[i].timedout != 0)
-               ||  (script_status->scripts[i].status != 0) ) {
-                       printf("   OUTPUT:%s\n",
-                               script_status->scripts[i].output);
+               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
@@ -1584,6 +1679,150 @@ static int control_ip(struct ctdb_context *ctdb, int argc, const char **argv)
        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; i<info->num; 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, ret;
+       TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+       struct ctdb_control_get_ifaces *ifaces;
+
+       /* read the public ip list from this node */
+       ret = ctdb_ctrl_get_ifaces(ctdb, TIMELIMIT(), options.pnn,
+                                  tmp_ctx, &ifaces);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to get interfaces from node %u\n",
+                                 options.pnn));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       if (options.machinereadable){
+               printf(":Name:LinkStatus:References:\n");
+       } else {
+               printf("Interfaces on node %u\n", options.pnn);
+       }
+
+       for (i=0; i<ifaces->num; 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);
+               }
+       }
+
+       talloc_free(tmp_ctx);
+       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
  */
@@ -2345,6 +2584,9 @@ static int control_catdb(struct ctdb_context *ctdb, int argc, const char **argv)
        ret = ctdb_dump_db(ctdb_db, stdout);
        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);
@@ -2462,22 +2704,94 @@ static int control_getdbmap(struct ctdb_context *ctdb, int argc, const char **ar
                return ret;
        }
 
+       if(options.machinereadable){
+               printf(":ID:Name:Path:Persistent:Unhealthy:\n");
+               for(i=0;i<dbmap->num;i++){
+                       const char *path;
+                       const char *name;
+                       const char *health;
+                       bool persistent;
+
+                       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].persistent;
+                       printf(":0x%08X:%s:%s:%d:%d:\n",
+                              dbmap->dbs[i].dbid, name, path,
+                              !!(persistent), !!(health));
+               }
+               return 0;
+       }
+
        printf("Number of databases:%d\n", dbmap->num);
        for(i=0;i<dbmap->num;i++){
                const char *path;
                const char *name;
+               const char *health;
                bool persistent;
 
                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].persistent;
-               printf("dbid:0x%08x name:%s path:%s %s\n", dbmap->dbs[i].dbid, name, 
-                      path, persistent?"PERSISTENT":"");
+               printf("dbid:0x%08x name:%s path:%s%s%s\n",
+                      dbmap->dbs[i].dbid, name, path,
+                      persistent?" PERSISTENT":"",
+                      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;i<dbmap->num;i++){
+               const char *path;
+               const char *name;
+               const char *health;
+               bool persistent;
+
+               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].persistent;
+               printf("dbid: 0x%08x\nname: %s\npath: %s\nPERSISTENT: %s\nHEALTH: %s\n",
+                      dbmap->dbs[i].dbid, name, path,
+                      persistent?"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
@@ -3011,6 +3325,7 @@ static int control_backupdb(struct ctdb_context *ctdb, int argc, const char **ar
        struct backup_data *bd;
        int fh = -1;
        int status = -1;
+       const char *reason = NULL;
 
        if (argc != 2) {
                DEBUG(DEBUG_ERR,("Invalid arguments\n"));
@@ -3039,6 +3354,37 @@ static int control_backupdb(struct ctdb_context *ctdb, int argc, const char **ar
                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 : tunnable 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, argv[0], dbmap->dbs[i].persistent, 0);
        if (ctdb_db == NULL) {
@@ -3264,6 +3610,22 @@ static int control_restoredb(struct ctdb_context *ctdb, int argc, const char **a
                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);
 
@@ -3299,6 +3661,250 @@ static int control_restoredb(struct ctdb_context *ctdb, int argc, const char **a
        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;
+
+       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);
+
+       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, stdout);
+       }
+
+       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;i<dbmap->num;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, argv[0], dbmap->dbs[i].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;
+       }
+
+       talloc_free(tmp_ctx);
+       return 0;
+}
+
 /*
  * set flags of a node in the nodemap
  */
@@ -3542,8 +4148,12 @@ static const struct {
        { "statistics",      control_statistics,        false,  false, "show statistics" },
        { "statisticsreset", control_statistics_reset,  true,   false,  "reset statistics"},
        { "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", "<ip>" },
+       { "ifaces",          control_ifaces,            true,   false,  "show which interfaces that ctdb manages" },
+       { "setifacelink",    control_setifacelink,      true,   false,  "set interface link status", "<iface> <status>" },
        { "process-exists",  control_process_exists,    true,   false,  "check if a process exists on a node",  "<pid>"},
        { "getdbmap",        control_getdbmap,          true,   false,  "show the database map" },
+       { "getdbstatus",     control_getdbstatus,       true,   false,  "show the status of a database", "<dbname>" },
        { "catdb",           control_catdb,             true,   false,  "dump a database" ,                     "<dbname>"},
        { "getmonmode",      control_getmonmode,        true,   false,  "show monitoring mode" },
        { "getcapabilities", control_getcapabilities,   true,   false,  "show node capabilities" },
@@ -3592,9 +4202,11 @@ static const struct {
        { "eventscript",     control_eventscript,       true,   false, "run the eventscript with the given parameters on a node", "<arguments>"},
        { "backupdb",        control_backupdb,          false,  false, "backup the database into a file.", "<database> <file>"},
        { "restoredb",        control_restoredb,        false,  false, "restore the database from a file.", "<file>"},
+       { "dumpdbbackup",    control_dumpdbbackup,      false,  true,  "dump database backup from a file.", "<file>"},
+       { "wipedb",           control_wipedb,        false,     false, "wipe the contents of a database.", "<dbname>"},
        { "recmaster",        control_recmaster,        false,  false, "show the pnn for the recovery master."},
        { "setflags",        control_setflags,          false,  false, "set flags for a node in the nodemap.", "<node> <flags>"},
-       { "scriptstatus",    control_scriptstatus,  false,      false, "show the status of the monitoring scripts"},
+       { "scriptstatus",    control_scriptstatus,  false,      false, "show the status of the monitoring scripts (or all scripts)", "[all]"},
        { "enablescript",     control_enablescript,  false,     false, "enable an eventscript", "<script>"},
        { "disablescript",    control_disablescript,  false,    false, "disable an eventscript", "<script>"},
        { "natgwlist",        control_natgwlist,        false,  false, "show the nodes belonging to this natgw configuration"},