ReadOnly: add a readonly flag to the getdbmap control and show the readonly setting...
[sahlberg/ctdb.git] / tools / ctdb.c
index 20b09b9e525bfe43fefd13502f24a59d614abc67..1770fe4e8055c38ed810326a4a8deab9c22e0091 100644 (file)
@@ -44,6 +44,7 @@ static struct {
        int timelimit;
        uint32_t pnn;
        int machinereadable;
+       int verbose;
        int maxruntime;
 } options;
 
@@ -105,7 +106,7 @@ static void verify_node(struct ctdb_context *ctdb)
 /*
  check if a database exists
 */
-static int db_exists(struct ctdb_context *ctdb, const char *db_name)
+static int db_exists(struct ctdb_context *ctdb, const char *db_name, bool *persistent)
 {
        int i, ret;
        struct ctdb_dbid_map *dbmap=NULL;
@@ -121,6 +122,9 @@ static int db_exists(struct ctdb_context *ctdb, const char *db_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;
                }
        }
@@ -156,7 +160,7 @@ static int control_process_exists(struct ctdb_context *ctdb, int argc, const cha
 /*
   display statistics structure
  */
-static void show_statistics(struct ctdb_statistics *s)
+static void show_statistics(struct ctdb_statistics *s, int show_header)
 {
        TALLOC_CTX *tmp_ctx = talloc_new(NULL);
        int i;
@@ -210,33 +214,100 @@ static void show_statistics(struct ctdb_statistics *s)
        tmp    /= 24;
        days    = tmp;
 
-       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;i<ARRAY_SIZE(fields);i++) {
-               if (strchr(fields[i].name, '.')) {
-                       preflen = strcspn(fields[i].name, ".")+1;
-                       if (!prefix || strncmp(prefix, fields[i].name, preflen) != 0) {
-                               prefix = fields[i].name;
-                               printf(" %*.*s\n", preflen-1, preflen-1, fields[i].name);
+       if (options.machinereadable){
+               if (show_header) {
+                       printf("CTDB version:");
+                       printf("Current time of statistics:");
+                       printf("Statistics collected since:");
+                       for (i=0;i<ARRAY_SIZE(fields);i++) {
+                               printf("%s:", fields[i].name);
                        }
-               } else {
-                       preflen = 0;
+                       printf("num_reclock_ctdbd_latency:");
+                       printf("min_reclock_ctdbd_latency:");
+                       printf("avg_reclock_ctdbd_latency:");
+                       printf("max_reclock_ctdbd_latency:");
+
+                       printf("num_reclock_recd_latency:");
+                       printf("min_reclock_recd_latency:");
+                       printf("avg_reclock_recd_latency:");
+                       printf("max_reclock_recd_latency:");
+
+                       printf("num_call_latency:");
+                       printf("min_call_latency:");
+                       printf("avg_call_latency:");
+                       printf("max_call_latency:");
+
+                       printf("num_lockwait_latency:");
+                       printf("min_lockwait_latency:");
+                       printf("avg_lockwait_latency:");
+                       printf("max_lockwait_latency:");
+
+                       printf("num_childwrite_latency:");
+                       printf("min_childwrite_latency:");
+                       printf("avg_childwrite_latency:");
+                       printf("max_childwrite_latency:");
+                       printf("\n");
                }
-               printf(" %*s%-22s%*s%10u\n", 
-                      preflen?4:0, "",
-                      fields[i].name+preflen, 
-                      preflen?0:4, "",
-                      *(uint32_t *)(fields[i].offset+(uint8_t *)s));
-       }
-       printf(" %-30s     %.6f sec\n", "max_reclock_ctdbd", s->reclock.ctdbd);
-       printf(" %-30s     %.6f sec\n", "max_reclock_recd", s->reclock.recd);
+               printf("%d:", CTDB_VERSION);
+               printf("%d:", (int)s->statistics_current_time.tv_sec);
+               printf("%d:", (int)s->statistics_start_time.tv_sec);
+               for (i=0;i<ARRAY_SIZE(fields);i++) {
+                       printf("%d:", *(uint32_t *)(fields[i].offset+(uint8_t *)s));
+               }
+               printf("%d:", s->reclock.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;i<ARRAY_SIZE(fields);i++) {
+                       if (strchr(fields[i].name, '.')) {
+                               preflen = strcspn(fields[i].name, ".")+1;
+                               if (!prefix || strncmp(prefix, fields[i].name, preflen) != 0) {
+                                       prefix = fields[i].name;
+                                       printf(" %*.*s\n", preflen-1, preflen-1, fields[i].name);
+                               }
+                       } else {
+                               preflen = 0;
+                       }
+                       printf(" %*s%-22s%*s%10u\n", 
+                              preflen?4:0, "",
+                              fields[i].name+preflen, 
+                              preflen?0:4, "",
+                              *(uint32_t *)(fields[i].offset+(uint8_t *)s));
+               }
+               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 sec\n", "max_call_latency", s->max_call_latency);
-       printf(" %-30s     %.6f sec\n", "max_lockwait_latency", s->max_lockwait_latency);
-       printf(" %-30s     %.6f sec\n", "max_childwrite_latency", s->max_childwrite_latency);
-       printf(" %-30s     %.6f sec\n", "max_childwrite_latency", s->max_childwrite_latency);
+               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);
 }
@@ -273,14 +344,14 @@ static int control_statistics_all(struct ctdb_context *ctdb)
                }
                statistics.max_hop_count = 
                        MAX(statistics.max_hop_count, s1.max_hop_count);
-               statistics.max_call_latency = 
-                       MAX(statistics.max_call_latency, s1.max_call_latency);
-               statistics.max_lockwait_latency = 
-                       MAX(statistics.max_lockwait_latency, s1.max_lockwait_latency);
+               statistics.call_latency.max = 
+                       MAX(statistics.call_latency.max, s1.call_latency.max);
+               statistics.lockwait_latency.max = 
+                       MAX(statistics.lockwait_latency.max, s1.lockwait_latency.max);
        }
        talloc_free(nodes);
        printf("Gathered statistics for %u nodes\n", num_nodes);
-       show_statistics(&statistics);
+       show_statistics(&statistics, 1);
        return 0;
 }
 
@@ -301,7 +372,7 @@ static int control_statistics(struct ctdb_context *ctdb, int argc, const char **
                DEBUG(DEBUG_ERR, ("Unable to get statistics from node %u\n", options.pnn));
                return ret;
        }
-       show_statistics(&statistics);
+       show_statistics(&statistics, 1);
        return 0;
 }
 
@@ -322,6 +393,37 @@ static int control_statistics_reset(struct ctdb_context *ctdb, int argc, const c
 }
 
 
+/*
+  display remote ctdb rolling statistics
+ */
+static int control_stats(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       int ret;
+       struct ctdb_statistics_wire *stats;
+       int i, num_records = -1;
+
+       if (argc ==1) {
+               num_records = atoi(argv[0]) - 1;
+       }
+
+       ret = ctdb_ctrl_getstathistory(ctdb, TIMELIMIT(), options.pnn, ctdb, &stats);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to get rolling statistics from node %u\n", options.pnn));
+               return ret;
+       }
+       for (i=0;i<stats->num;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 uptime of remote node
  */
@@ -664,6 +766,7 @@ struct natgw_node {
 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;
@@ -742,6 +845,14 @@ static int control_natgwlist(struct ctdb_context *ctdb, int argc, const char **a
         */
        for(i=0;i<nodemap->num;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;
                }
@@ -750,6 +861,14 @@ static int control_natgwlist(struct ctdb_context *ctdb, int argc, const char **a
        if (i == nodemap->num) {
                for(i=0;i<nodemap->num;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;
                        }
@@ -759,6 +878,14 @@ static int control_natgwlist(struct ctdb_context *ctdb, int argc, const char **a
        if (i == nodemap->num) {
                for(i=0;i<nodemap->num;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;
                        }
@@ -766,6 +893,7 @@ static int control_natgwlist(struct ctdb_context *ctdb, int argc, const char **a
                /* or if we still can not find any */
                if (i == nodemap->num) {
                        printf("-1 0.0.0.0\n");
+                       ret = 2; /* matches ENOENT */
                }
        }
 
@@ -783,7 +911,7 @@ static int control_natgwlist(struct ctdb_context *ctdb, int argc, const char **a
                       !!(nodemap->nodes[i].flags&NODE_FLAGS_STOPPED));
        }
 
-       return 0;
+       return ret;
 }
 
 /*
@@ -1062,11 +1190,16 @@ static int control_get_tickles(struct ctdb_context *ctdb, int argc, const char *
        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;
@@ -1081,6 +1214,9 @@ static int control_get_tickles(struct ctdb_context *ctdb, int argc, const char *
        if (options.machinereadable){
                printf(":source ip:port:destination ip:port:\n");
                for (i=0;i<list->tickles.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));
                }
@@ -1088,6 +1224,9 @@ static int control_get_tickles(struct ctdb_context *ctdb, int argc, const char *
                printf("Tickles for ip:%s\n", ctdb_addr_to_str(&list->addr));
                printf("Num tickles:%u\n", list->tickles.num);
                for (i=0;i<list->tickles.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));
                }
@@ -1193,6 +1332,7 @@ static int move_ip(struct ctdb_context *ctdb, ctdb_sock_addr *addr, uint32_t pnn
 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) {
@@ -1211,8 +1351,16 @@ static int control_moveip(struct ctdb_context *ctdb, int argc, const char **argv
                return -1;
        }
 
-       if (move_ip(ctdb, &addr, pnn) != 0) {
-               DEBUG(DEBUG_ERR,("Failed to move ip to node %d\n", pnn));
+       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;
        }
 
@@ -1376,14 +1524,150 @@ find_other_host_for_public_ip(struct ctdb_context *ctdb, ctdb_sock_addr *addr)
        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; i<nodemap->num;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;
+       }
+
+
+       ret = ctdb_ctrl_getrecmaster(ctdb, ctdb, TIMELIMIT(), options.pnn, &recmaster);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to get recmaster from node %u\n", options.pnn));
+               return ret;
+       }
+
+       /* 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; i<nodemap->num; 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;
-       uint32_t pnn;
+       int len, retries = 0;
        unsigned mask;
        ctdb_sock_addr addr;
        struct ctdb_control_ip_iface *pub;
@@ -1402,23 +1686,28 @@ static int control_addip(struct ctdb_context *ctdb, int argc, const char **argv)
                return -1;
        }
 
-       ret = control_get_all_public_ips(ctdb, tmp_ctx, &ips);
+       /* 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 cluster\n"));
+               DEBUG(DEBUG_ERR, ("Unable to get public ip list from node %u\n", options.pnn));
                talloc_free(tmp_ctx);
-               return ret;
+               return -1;
        }
-
-
-       /* check if some other node is already serving this ip, if not,
-        * we will claim it
-        */
        for (i=0;i<ips->num;i++) {
                if (ctdb_same_ip(&addr, &ips->ips[i].addr)) {
-                       break;
+                       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);
@@ -1428,23 +1717,32 @@ static int control_addip(struct ctdb_context *ctdb, int argc, const char **argv)
        pub->len   = strlen(argv[1])+1;
        memcpy(&pub->iface[0], argv[1], strlen(argv[1])+1);
 
-       ret = ctdb_ctrl_add_public_ip(ctdb, TIMELIMIT(), options.pnn, pub);
+       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\n", options.pnn));
+               DEBUG(DEBUG_ERR, ("Unable to add public ip to node %u. Giving up.\n", options.pnn));
                talloc_free(tmp_ctx);
                return ret;
        }
 
-       if (i == ips->num) {
-               /* no one has this ip so we claim it */
-               pnn  = options.pnn;
-       } else {
-               pnn  = ips->ips[i].pnn;
-       }
-
-       if (move_ip(ctdb, &addr, pnn) != 0) {
-               DEBUG(DEBUG_ERR,("Failed to move ip to node %d\n", pnn));
-               return -1;
+       do {
+               ret = control_ipreallocate(ctdb, argc, argv);
+               if (ret != 0) {
+                       DEBUG(DEBUG_ERR, ("IP Reallocate failed on 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, ("IP Reallocate failed on node %u. Giving up.\n", options.pnn));
+               talloc_free(tmp_ctx);
+               return ret;
        }
 
        talloc_free(tmp_ctx);
@@ -1527,6 +1825,7 @@ static int control_delip_all(struct ctdb_context *ctdb, int argc, const char **a
 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);
@@ -1573,8 +1872,16 @@ static int control_delip(struct ctdb_context *ctdb, int argc, const char **argv)
        if (ips->ips[i].pnn == options.pnn) {
                ret = find_other_host_for_public_ip(ctdb, &addr);
                if (ret != -1) {
-                       if (move_ip(ctdb, &addr, ret) != 0) {
-                               DEBUG(DEBUG_ERR,("Failed to move ip to node %d\n", ret));
+                       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;
                        }
                }
@@ -1809,7 +2116,11 @@ static int control_ip(struct ctdb_context *ctdb, int argc, const char **argv)
        }
 
        if (options.machinereadable){
-               printf(":Public IP:Node:ActiveInterface:AvailableInterfaces:ConfiguredInterfaces:\n");
+               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");
@@ -1869,19 +2180,29 @@ static int control_ip(struct ctdb_context *ctdb, int argc, const char **argv)
                }
 
                if (options.machinereadable){
-                       printf(":%s:%d:%s:%s:%s:\n",
-                              ctdb_addr_to_str(&ips->ips[ips->num-i].addr),
-                              ips->ips[ips->num-i].pnn,
-                              aciface?aciface:"",
-                              avifaces?avifaces:"",
-                              cifaces?cifaces:"");
+                       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 {
-                       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:"");
+                       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);
        }
@@ -2052,151 +2373,12 @@ static int control_getpid(struct ctdb_context *ctdb, int argc, const char **argv
        return 0;
 }
 
-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
+  disable a remote node
  */
-static int control_ipreallocate(struct ctdb_context *ctdb, int argc, const char **argv)
+static int control_disable(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; i<nodemap->num;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;
-       }
-
-
-       ret = ctdb_ctrl_getrecmaster(ctdb, ctdb, TIMELIMIT(), options.pnn, &recmaster);
-       if (ret != 0) {
-               DEBUG(DEBUG_ERR, ("Unable to get recmaster from node %u\n", options.pnn));
-               return ret;
-       }
-
-       /* 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; i<nodemap->num; 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) < 3.0) {
-               event_loop_once(ctdb->ev);
-       }
-       if (ipreallocate_finished == 1) {
-               return 0;
-       }
-
-       DEBUG(DEBUG_ERR,("Timed out waiting for recmaster ipreallocate. Trying again\n"));
-       retries++;
-       sleep(1);
-       goto again;
-
-       return 0;
-}
-
-
-/*
-  disable a remote node
- */
-static int control_disable(struct ctdb_context *ctdb, int argc, const char **argv)
-{
-       int ret;
+       int ret;
        struct ctdb_node_map *nodemap=NULL;
 
        /* check if the node is already disabled */
@@ -2794,6 +2976,7 @@ 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;
 
        if (argc < 1) {
                usage();
@@ -2802,12 +2985,12 @@ static int control_catdb(struct ctdb_context *ctdb, int argc, const char **argv)
        db_name = argv[0];
 
 
-       if (db_exists(ctdb, db_name)) {
+       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, db_name, false, 0);
+       ctdb_db = ctdb_attach(ctdb, db_name, persistent, 0);
 
        if (ctdb_db == NULL) {
                DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name));
@@ -2829,6 +3012,438 @@ static int control_catdb(struct ctdb_context *ctdb, int argc, const char **argv)
        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;
+
+       d->count++;
+       
+       return ctdb_dumpdb_record(d->ctdb, key, data, stdout);
+}
+
+/*
+  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, 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, 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, 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, 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;
+       int fd;
+
+       if (argc < 2) {
+               usage();
+       }
+
+       tdb_file = argv[0];
+
+       tdb = tdb_open(tdb_file, 0, 0, O_RDONLY, 0);
+       if (tdb == NULL) {
+               DEBUG(DEBUG_ERR,("Failed to open TDB file %s\n", tdb_file));
+               return -1;
+       }
+
+       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)) {
+               DEBUG(DEBUG_ERR,("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) {
+                       DEBUG(DEBUG_ERR,("Failed to open output file %s\n", argv[2]));
+                       return -1;
+               }
+               write(fd, data.dptr+sizeof(struct ctdb_ltdb_header), data.dsize-sizeof(struct ctdb_ltdb_header));
+               close(fd);
+       } else {
+               write(1, data.dptr+sizeof(struct ctdb_ltdb_header), data.dsize-sizeof(struct ctdb_ltdb_header));
+       }
+
+       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, 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 <port>\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)
@@ -2939,12 +3554,13 @@ static int control_getdbmap(struct ctdb_context *ctdb, int argc, const char **ar
        }
 
        if(options.machinereadable){
-               printf(":ID:Name:Path:Persistent:Unhealthy:\n");
+               printf(":ID:Name:Path:Persistent:Unhealthy:ReadOnly:\n");
                for(i=0;i<dbmap->num;i++){
                        const char *path;
                        const char *name;
                        const char *health;
                        bool persistent;
+                       bool readonly;
 
                        ctdb_ctrl_getdbpath(ctdb, TIMELIMIT(), options.pnn,
                                            dbmap->dbs[i].dbid, ctdb, &path);
@@ -2952,10 +3568,11 @@ static int control_getdbmap(struct ctdb_context *ctdb, int argc, const char **ar
                                            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",
+                       persistent = dbmap->dbs[i].flags & CTDB_DB_FLAGS_PERSISTENT;
+                       readonly   = dbmap->dbs[i].flags & CTDB_DB_FLAGS_READONLY;
+                       printf(":0x%08X:%s:%s:%d:%d:%d:\n",
                               dbmap->dbs[i].dbid, name, path,
-                              !!(persistent), !!(health));
+                              !!(persistent), !!(health), !!(readonly));
                }
                return 0;
        }
@@ -2966,14 +3583,17 @@ static int control_getdbmap(struct ctdb_context *ctdb, int argc, const char **ar
                const char *name;
                const char *health;
                bool persistent;
+               bool readonly;
 
                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%s\n",
+               persistent = dbmap->dbs[i].flags & CTDB_DB_FLAGS_PERSISTENT;
+               readonly   = dbmap->dbs[i].flags & CTDB_DB_FLAGS_READONLY;
+               printf("dbid:0x%08x name:%s path:%s%s%s%s\n",
                       dbmap->dbs[i].dbid, name, path,
                       persistent?" PERSISTENT":"",
+                      readonly?" READONLY":"",
                       health?" UNHEALTHY":"");
        }
 
@@ -3006,6 +3626,7 @@ static int control_getdbstatus(struct ctdb_context *ctdb, int argc, const char *
                const char *name;
                const char *health;
                bool persistent;
+               bool readonly;
 
                ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &name);
                if (strcmp(name, db_name) != 0) {
@@ -3014,10 +3635,12 @@ static int control_getdbstatus(struct ctdb_context *ctdb, int argc, const char *
 
                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",
+               persistent = dbmap->dbs[i].flags & CTDB_DB_FLAGS_PERSISTENT;
+               readonly   = dbmap->dbs[i].flags & CTDB_DB_FLAGS_READONLY;
+               printf("dbid: 0x%08x\nname: %s\npath: %s\nPERSISTENT: %s\nREADONLY: %s\nHEALTH: %s\n",
                       dbmap->dbs[i].dbid, name, path,
                       persistent?"yes":"no",
+                      readonly?"yes":"no",
                       health?health:"OK");
                return 0;
        }
@@ -3384,13 +4007,23 @@ static int control_attach(struct ctdb_context *ctdb, int argc, const char **argv
 {
        const char *db_name;
        struct ctdb_db_context *ctdb_db;
+       bool persistent = false;
 
        if (argc < 1) {
                usage();
        }
        db_name = argv[0];
+       if (argc > 2) {
+               usage();
+       }
+       if (argc == 2) {
+               if (strcmp(argv[1], "persistent") != 0) {
+                       usage();
+               }
+               persistent = true;
+       }
 
-       ctdb_db = ctdb_attach(ctdb, db_name, false, 0);
+       ctdb_db = ctdb_attach(ctdb, db_name, persistent, 0);
        if (ctdb_db == NULL) {
                DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name));
                return -1;
@@ -3598,7 +4231,7 @@ static int control_backupdb(struct ctdb_context *ctdb, int argc, const char **ar
                                     allow_unhealthy));
        }
 
-       ctdb_db = ctdb_attach(ctdb, argv[0], dbmap->dbs[i].persistent, 0);
+       ctdb_db = ctdb_attach(ctdb, 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);
@@ -3650,7 +4283,7 @@ static int control_backupdb(struct ctdb_context *ctdb, int argc, const char **ar
 
        dbhdr.version = DB_VERSION;
        dbhdr.timestamp = time(NULL);
-       dbhdr.persistent = dbmap->dbs[i].persistent;
+       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"));
@@ -3676,6 +4309,9 @@ done:
                        DEBUG(DEBUG_ERR,("close failed: %s\n", strerror(errno)));
                }
        }
+
+       DEBUG(DEBUG_ERR,("Database acked up to %s\n", argv[1]));
+
        talloc_free(tmp_ctx);
        return status;
 }
@@ -3720,9 +4356,9 @@ static int control_restoredb(struct ctdb_context *ctdb, int argc, const char **a
                return -1;
        }
 
-       dbname = dbhdr.name;
+       dbname = discard_const(dbhdr.name);
        if (argc == 2) {
-               dbname = argv[1];
+               dbname = discard_const(argv[1]);
        }
 
        outdata.dsize = dbhdr.size;
@@ -3994,7 +4630,7 @@ static int control_wipedb(struct ctdb_context *ctdb, int argc,
                return -1;
        }
 
-       ctdb_db = ctdb_attach(ctdb, argv[0], dbmap->dbs[i].persistent, 0);
+       ctdb_db = ctdb_attach(ctdb, 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]));
@@ -4119,51 +4755,9 @@ static int control_wipedb(struct ctdb_context *ctdb, int argc,
                return -1;
        }
 
-       talloc_free(tmp_ctx);
-       return 0;
-}
-
-/*
- * set flags of a node in the nodemap
- */
-static int control_setflags(struct ctdb_context *ctdb, int argc, const char **argv)
-{
-       int ret;
-       int32_t status;
-       int node;
-       int flags;
-       TDB_DATA data;
-       struct ctdb_node_flag_change c;
-
-       if (argc != 2) {
-               usage();
-               return -1;
-       }
-
-       if (sscanf(argv[0], "%d", &node) != 1) {
-               DEBUG(DEBUG_ERR, ("Badly formed node\n"));
-               usage();
-               return -1;
-       }
-       if (sscanf(argv[1], "0x%x", &flags) != 1) {
-               DEBUG(DEBUG_ERR, ("Badly formed flags\n"));
-               usage();
-               return -1;
-       }
-
-       c.pnn       = node;
-       c.old_flags = 0;
-       c.new_flags = flags;
-
-       data.dsize = sizeof(c);
-       data.dptr = (unsigned char *)&c;
+       DEBUG(DEBUG_ERR, ("Database wiped.\n"));
 
-       ret = ctdb_control(ctdb, options.pnn, 0, CTDB_CONTROL_MODIFY_FLAGS, 0, 
-                          data, NULL, NULL, &status, NULL, NULL);
-       if (ret != 0 || status != 0) {
-               DEBUG(DEBUG_ERR,("Failed to modify flags\n"));
-               return -1;
-       }
+       talloc_free(tmp_ctx);
        return 0;
 }
 
@@ -4303,60 +4897,35 @@ static int control_msglisten(struct ctdb_context *ctdb, int argc, const char **a
 
 /*
   list all nodes in the cluster
-  if the daemon is running, we read the data from the daemon.
-  if the daemon is not running we parse the nodes file directly
+  we parse the nodes file directly
  */
 static int control_listnodes(struct ctdb_context *ctdb, int argc, const char **argv)
 {
-       int i, ret;
-       struct ctdb_node_map *nodemap=NULL;
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+       struct pnn_node *pnn_nodes;
+       struct pnn_node *pnn_node;
 
-       if (ctdb != NULL) {
-               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;
-               }
+       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(i=0;i<nodemap->num;i++){
-                       if (nodemap->nodes[i].flags & NODE_FLAGS_DELETED) {
-                               continue;
-                       }
-                       if (options.machinereadable){
-                               printf(":%d:%s:\n", nodemap->nodes[i].pnn, ctdb_addr_to_str(&nodemap->nodes[i].addr));
-                       } else {
-                               printf("%s\n", ctdb_addr_to_str(&nodemap->nodes[i].addr));
-                       }
-               }
-       } else {
-               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"));
+       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;
                }
-
-               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);
-                       }
+               if (options.machinereadable){
+                       printf(":%d:%s:\n", pnn_node->pnn, pnn_node->addr);
+               } else {
+                       printf("%s\n", pnn_node->addr);
                }
-               talloc_free(mem_ctx);
        }
+       talloc_free(mem_ctx);
 
        return 0;
 }
@@ -4428,6 +4997,7 @@ static const struct {
        { "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", "<ip>" },
        { "ifaces",          control_ifaces,            true,   false,  "show which interfaces that ctdb manages" },
@@ -4436,6 +5006,7 @@ static const struct {
        { "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>"},
+       { "cattdb",          control_cattdb,            true,   false,  "dump a database" ,                     "<dbname>"},
        { "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" },
@@ -4447,7 +5018,7 @@ static const struct {
        { "getdebug",        control_getdebug,          true,   false,  "get debug level" },
        { "getlog",          control_getlog,            true,   false,  "get the log data from the in memory ringbuffer", "<level>" },
        { "clearlog",          control_clearlog,        true,   false,  "clear the log data from the in memory ringbuffer" },
-       { "attach",          control_attach,            true,   false,  "attach to a database",                 "<dbname>" },
+       { "attach",          control_attach,            true,   false,  "attach to a database",                 "<dbname> [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" },
@@ -4467,7 +5038,7 @@ static const struct {
        { "killtcp",         kill_tcp,                  false,  false, "kill a tcp connection.", "<srcip:port> <dstip:port>" },
        { "gratiousarp",     control_gratious_arp,      false,  false, "send a gratious arp", "<ip> <interface>" },
        { "tickle",          tickle_tcp,                false,  false, "send a tcp tickle ack", "<srcip:port> <dstip:port>" },
-       { "gettickles",      control_get_tickles,       false,  false, "get the list of tickles registered for this ip", "<ip>" },
+       { "gettickles",      control_get_tickles,       false,  false, "get the list of tickles registered for this ip", "<ip> [<port>]" },
        { "addtickle",       control_add_tickle,        false,  false, "add a tickle for this ip", "<ip>:<port> <ip>:<port>" },
 
        { "deltickle",       control_del_tickle,        false,  false, "delete a tickle from this ip", "<ip>:<port> <ip>:<port>" },
@@ -4489,7 +5060,6 @@ static const struct {
        { "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 (or all scripts)", "[all]"},
        { "enablescript",     control_enablescript,  false,     false, "enable an eventscript", "<script>"},
        { "disablescript",    control_disablescript,  false,    false, "disable an eventscript", "<script>"},
@@ -4504,7 +5074,13 @@ static const struct {
        { "getdbprio",        control_getdbprio,        false,  false, "Get DB priority", "<dbid>"},
        { "msglisten",        control_msglisten,        false,  false, "Listen on a srvid port for messages", "<msg srvid>"},
        { "msgsend",          control_msgsend,  false,  false, "Send a message to srvid", "<srvid> <message>"},
-       { "sync",            control_ipreallocate,      true,   false,  "wait until ctdbd has synced all state changes" },
+       { "sync",            control_ipreallocate,      false,  false,  "wait until ctdbd has synced all state changes" },
+       { "pfetch",          control_pfetch,            false,  false,  "fetch a record from a persistent database", "<db> <key> [<file>]" },
+       { "pstore",          control_pstore,            false,  false,  "write a record to a persistent database", "<db> <key> <file containing record>" },
+       { "tfetch",          control_tfetch,            false,  true,  "fetch a record from a [c]tdb-file", "<tdb-file> <key> [<file>]" },
+       { "readkey",         control_readkey,           true,   false,  "read the content off a database key", "<tdb-file> <key>" },
+       { "writekey",        control_writekey,          true,   false,  "write to a database key", "<tdb-file> <key> <value>" },
+       { "checktcpport",    control_chktcpport,        false,  true,  "check if a service is bound to a specific tcp port or not", "<port>" },
 };
 
 /*
@@ -4518,6 +5094,7 @@ static void usage(void)
 "Options:\n" \
 "   -n <node>          choose node number, or 'all' (defaults to local node)\n"
 "   -Y                 generate machinereadable output\n"
+"   -v                 generate verbose output\n"
 "   -t <timelimit>     set timelimit for control in seconds (default %u)\n", options.timelimit);
        printf("Controls:\n");
        for (i=0;i<ARRAY_SIZE(ctdb_commands);i++) {
@@ -4549,6 +5126,7 @@ int main(int argc, const char *argv[])
                { "timelimit", 't', POPT_ARG_INT, &options.timelimit, 0, "timelimit", "integer" },
                { "node",      'n', POPT_ARG_STRING, &nodestring, 0, "node", "integer|all" },
                { "machinereadable", 'Y', POPT_ARG_NONE, &options.machinereadable, 0, "enable machinereadable output", NULL },
+               { "verbose",    'v', POPT_ARG_NONE, &options.verbose, 0, "enable verbose output", NULL },
                { "maxruntime", 'T', POPT_ARG_INT, &options.maxruntime, 0, "die if runtime exceeds this limit (in seconds)", "integer" },
                POPT_TABLEEND
        };