OBJS = @TDBOBJ@ @TALLOCOBJ@ @LIBREPLACEOBJ@ @INFINIBAND_WRAPPER_OBJ@ $(EXTRA_OBJ) $(EVENTS_OBJ) $(CTDB_OBJ)
-BINS = bin/ctdbd bin/ctdbd_test bin/ctdb_test bin/ctdb_bench bin/ctdb_messaging bin/ctdb_fetch bin/ctdb_fetch1 bin/lockwait @INFINIBAND_BINS@
+BINS = bin/ctdbd bin/ctdbd_test bin/ctdb_test bin/ctdb_bench bin/ctdb_messaging bin/ctdb_fetch bin/ctdb_fetch1 bin/lockwait bin/ctdb_status @INFINIBAND_BINS@
DIRS = lib bin
@echo Linking $@
@$(CC) $(CFLAGS) -o $@ direct/ctdbd.o $(OBJS) $(LIB_FLAGS)
+bin/ctdb_status: $(OBJS) tools/ctdb_status.o
+ @echo Linking $@
+ @$(CC) $(CFLAGS) -o $@ tools/ctdb_status.o $(OBJS) $(LIB_FLAGS)
+
bin/ctdbd_test: $(OBJS) direct/ctdbd_test.o
@echo Linking $@
@$(CC) $(CFLAGS) -o $@ direct/ctdbd_test.o
#include "lib/events/events.h"
#include "system/filesys.h"
#include "popt.h"
+#include "../include/ctdb.h"
+#include "../include/ctdb_private.h"
/* Handle common command line options for ctdb test progs
*/
return ctdb;
}
+
+
+/*
+ startup a client only ctdb context
+ */
+struct ctdb_context *ctdb_cmdline_client(struct event_context *ev, const char *ctdb_socket)
+{
+ struct ctdb_context *ctdb;
+ int ret;
+
+ /* initialise ctdb */
+ ctdb = ctdb_init(ev);
+ if (ctdb == NULL) {
+ printf("Failed to init ctdb\n");
+ exit(1);
+ }
+
+ ctdb->daemon.name = talloc_strdup(ctdb, ctdb_socket);
+
+ ret = ctdb_socket_connect(ctdb);
+ if (ret != 0) {
+ DEBUG(0,(__location__ " Failed to connect to daemon\n"));
+ talloc_free(ctdb);
+ return NULL;
+ }
+
+ return ctdb;
+}
struct ctdb_req_header *hdr = (struct ctdb_req_header *)data;
TALLOC_CTX *tmp_ctx;
+ ctdb->status.node_packets_recv++;
+
/* place the packet as a child of the tmp_ctx. We then use
talloc_free() below to free it. If any of the calls want
to keep it, then they will steal it somewhere else, and the
void ctdb_queue_packet(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
{
struct ctdb_node *node;
+ ctdb->status.node_packets_sent++;
node = ctdb->nodes[hdr->destnode];
if (hdr->destnode == ctdb->vnn && !(ctdb->flags & CTDB_FLAG_SELF_CONNECT)) {
ctdb_defer_packet(ctdb, hdr);
state->state = CTDB_CALL_DONE;
}
+static void ctdb_reply_status(struct ctdb_context *ctdb, struct ctdb_req_header *hdr);
+
/*
this is called in the client, when data comes in from the daemon
*/
ctdb_reply_connect_wait(ctdb, hdr);
break;
+ case CTDB_REPLY_STATUS:
+ ctdb_reply_status(ctdb, hdr);
+ break;
+
default:
DEBUG(0,("bogus operation code:%d\n",hdr->operation));
}
/*
connect to a unix domain socket
*/
-static int ux_socket_connect(struct ctdb_context *ctdb)
+int ctdb_socket_connect(struct ctdb_context *ctdb)
{
struct sockaddr_un addr;
/* if the domain socket is not yet open, open it */
if (ctdb->daemon.sd==-1) {
- ux_socket_connect(ctdb);
+ ctdb_socket_connect(ctdb);
}
ret = ctdb_ltdb_lock(ctdb_db, call->key);
/* if the domain socket is not yet open, open it */
if (ctdb->daemon.sd==-1) {
- ux_socket_connect(ctdb);
+ ctdb_socket_connect(ctdb);
}
ZERO_STRUCT(c);
/* if the domain socket is not yet open, open it */
if (ctdb->daemon.sd==-1) {
- ux_socket_connect(ctdb);
+ ctdb_socket_connect(ctdb);
}
res = ctdb_queue_send(ctdb->daemon.queue, (uint8_t *)&r.hdr, r.hdr.length);
/* if the domain socket is not yet open, open it */
if (ctdb->daemon.sd==-1) {
- ux_socket_connect(ctdb);
+ ctdb_socket_connect(ctdb);
}
len = sizeof(struct ctdb_req_shutdown);
}
}
+enum ctdb_status_states {CTDB_STATUS_WAIT, CTDB_STATUS_DONE};
+
+struct ctdb_status_state {
+ uint32_t reqid;
+ struct ctdb_status *status;
+ enum ctdb_status_states state;
+};
+
+/*
+ handle a ctdb_reply_status reply
+ */
+static void ctdb_reply_status(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
+{
+ struct ctdb_reply_status *r = (struct ctdb_reply_status *)hdr;
+ struct ctdb_status_state *state;
+
+ state = idr_find_type(ctdb->idr, hdr->reqid, struct ctdb_status_state);
+ if (state == NULL) {
+ DEBUG(0, ("reqid %d not found\n", hdr->reqid));
+ return;
+ }
+
+ *state->status = r->status;
+ state->state = CTDB_STATUS_DONE;
+}
+
+/*
+ wait until we're the only node left.
+ this function never returns
+*/
+int ctdb_status(struct ctdb_context *ctdb, struct ctdb_status *status)
+{
+ struct ctdb_req_status r;
+ int ret;
+ struct ctdb_status_state *state;
+
+ /* if the domain socket is not yet open, open it */
+ if (ctdb->daemon.sd==-1) {
+ ctdb_socket_connect(ctdb);
+ }
+
+ state = talloc(ctdb, struct ctdb_status_state);
+ CTDB_NO_MEMORY(ctdb, state);
+
+ state->reqid = idr_get_new(ctdb->idr, state, 0xFFFF);
+ state->status = status;
+ state->state = CTDB_STATUS_WAIT;
+
+ ZERO_STRUCT(r);
+ r.hdr.length = sizeof(r);
+ r.hdr.ctdb_magic = CTDB_MAGIC;
+ r.hdr.ctdb_version = CTDB_VERSION;
+ r.hdr.operation = CTDB_REQ_STATUS;
+ r.hdr.reqid = state->reqid;
+
+ ret = ctdb_client_queue_pkt(ctdb, &(r.hdr));
+ if (ret != 0) {
+ talloc_free(state);
+ return -1;
+ }
+
+ while (state->state == CTDB_STATUS_WAIT) {
+ event_loop_once(ctdb->ev);
+ }
+
+ talloc_free(state);
+
+ return 0;
+}
}
+/*
+ send a packet to a client
+ */
+static int daemon_queue_send(struct ctdb_client *client, struct ctdb_req_header *hdr)
+{
+ client->ctdb->status.client_packets_sent++;
+ return ctdb_queue_send(client->queue, (uint8_t *)hdr, hdr->length);
+}
+
/*
message handler for when we are in daemon mode. This redirects the message
to the right client
r->datalen = data.dsize;
memcpy(&r->data[0], data.dptr, data.dsize);
- ctdb_queue_send(client->queue, (uint8_t *)&r->hdr, len);
+ daemon_queue_send(client, &r->hdr);
talloc_free(r);
}
r.vnn = ctdb_get_vnn(client->ctdb);
r.num_connected = client->ctdb->num_connected;
- res = ctdb_queue_send(client->queue, (uint8_t *)&r.hdr, r.hdr.length);
+ res = daemon_queue_send(client, &r.hdr);
+ if (res != 0) {
+ DEBUG(0,(__location__ " Failed to queue a connect wait response\n"));
+ return;
+ }
+}
+
+
+/*
+ called when the daemon gets a status request from a client
+ */
+static void daemon_request_status(struct ctdb_client *client,
+ struct ctdb_req_status *c)
+{
+ struct ctdb_reply_status r;
+ int res;
+
+ /* now send the reply */
+ ZERO_STRUCT(r);
+
+ r.hdr.length = sizeof(r);
+ r.hdr.ctdb_magic = CTDB_MAGIC;
+ r.hdr.ctdb_version = CTDB_VERSION;
+ r.hdr.operation = CTDB_REPLY_STATUS;
+ r.hdr.reqid = c->hdr.reqid;
+ r.status = client->ctdb->status;
+
+ res = daemon_queue_send(client, &r.hdr);
if (res != 0) {
DEBUG(0,(__location__ " Failed to queue a connect wait response\n"));
return;
struct ctdb_client *client;
uint32_t reqid;
struct ctdb_call *call;
+ struct timeval start_time;
};
/*
res = ctdb_daemon_call_recv(state, dstate->call);
if (res != 0) {
DEBUG(0, (__location__ " ctdbd_call_recv() returned error\n"));
+ client->ctdb->status.pending_calls--;
+ ctdb_latency(&client->ctdb->status.max_call_latency, dstate->start_time);
return;
}
r = ctdbd_allocate_pkt(dstate, length);
if (r == NULL) {
DEBUG(0, (__location__ " Failed to allocate reply_call in ctdb daemon\n"));
+ client->ctdb->status.pending_calls--;
+ ctdb_latency(&client->ctdb->status.max_call_latency, dstate->start_time);
return;
}
memset(r, 0, offsetof(struct ctdb_reply_call, data));
r->datalen = dstate->call->reply_data.dsize;
memcpy(&r->data[0], dstate->call->reply_data.dptr, r->datalen);
- res = ctdb_queue_send(client->queue, (uint8_t *)&r->hdr, r->hdr.length);
+ res = daemon_queue_send(client, &r->hdr);
if (res != 0) {
DEBUG(0, (__location__ "Failed to queue packet from daemon to client\n"));
}
talloc_free(dstate);
+ client->ctdb->status.pending_calls--;
+ ctdb_latency(&client->ctdb->status.max_call_latency, dstate->start_time);
}
int ret;
struct ctdb_context *ctdb = client->ctdb;
+ ctdb->status.total_calls++;
+ ctdb->status.pending_calls++;
+
ctdb_db = find_ctdb_db(client->ctdb, c->db_id);
if (!ctdb_db) {
DEBUG(0, (__location__ " Unknown database in request. db_id==0x%08x",
c->db_id));
+ ctdb->status.pending_calls--;
return;
}
daemon_incoming_packet, client);
if (ret == -2) {
/* will retry later */
+ ctdb->status.pending_calls--;
return;
}
if (ret != 0) {
DEBUG(0,(__location__ " Unable to fetch record\n"));
+ ctdb->status.pending_calls--;
return;
}
if (dstate == NULL) {
ctdb_ltdb_unlock(ctdb_db, key);
DEBUG(0,(__location__ " Unable to allocate dstate\n"));
+ ctdb->status.pending_calls--;
return;
}
+ dstate->start_time = timeval_current();
dstate->client = client;
dstate->reqid = c->hdr.reqid;
talloc_steal(dstate, data.dptr);
if (call == NULL) {
ctdb_ltdb_unlock(ctdb_db, key);
DEBUG(0,(__location__ " Unable to allocate call\n"));
+ ctdb->status.pending_calls--;
+ ctdb_latency(&ctdb->status.max_call_latency, dstate->start_time);
return;
}
if (state == NULL) {
DEBUG(0,(__location__ " Unable to setup call send\n"));
+ ctdb->status.pending_calls--;
+ ctdb_latency(&ctdb->status.max_call_latency, dstate->start_time);
return;
}
talloc_steal(state, dstate);
daemon_request_shutdown(client, (struct ctdb_req_shutdown *)hdr);
break;
+ case CTDB_REQ_STATUS:
+ daemon_request_status(client, (struct ctdb_req_status *)hdr);
+ break;
+
default:
DEBUG(0,(__location__ " daemon: unrecognized operation %d\n",
hdr->operation));
talloc_free(tmp_ctx);
}
-
+/*
+ called when the daemon gets a incoming packet
+ */
static void ctdb_daemon_read_cb(uint8_t *data, size_t cnt, void *args)
{
struct ctdb_client *client = talloc_get_type(args, struct ctdb_client);
return;
}
+ client->ctdb->status.client_packets_recv++;
+
if (cnt < sizeof(*hdr)) {
ctdb_set_error(client->ctdb, "Bad packet length %d in daemon\n", cnt);
return;
return;
}
+ DEBUG(3,(__location__ " client request %d of type %d length %d from "
+ "node %d to %d\n", hdr->reqid, hdr->operation, hdr->length,
+ hdr->srcnode, hdr->destnode));
+
/* it is the responsibility of the incoming packet function to free 'data' */
daemon_incoming_packet(client, data, cnt);
}
struct lockwait_handle {
+ struct ctdb_context *ctdb;
struct fd_event *fde;
int fd[2];
pid_t child;
talloc_set_destructor(h, NULL);
close(h->fd[0]);
DEBUG(3,(__location__ " lockwait took %.6f seconds\n", timeval_elapsed(&h->t)));
+ h->ctdb->status.pending_lockwait_calls--;
talloc_free(h);
callback(p);
waitpid(child, NULL, 0);
static int lockwait_destructor(struct lockwait_handle *h)
{
+ h->ctdb->status.pending_lockwait_calls--;
close(h->fd[0]);
kill(h->child, SIGKILL);
waitpid(h->child, NULL, 0);
struct lockwait_handle *result;
int ret;
+ ctdb_db->ctdb->status.lockwait_calls++;
+ ctdb_db->ctdb->status.pending_lockwait_calls++;
+
if (!(result = talloc_zero(ctdb_db, struct lockwait_handle))) {
+ ctdb_db->ctdb->status.pending_lockwait_calls--;
return NULL;
}
if (ret != 0) {
talloc_free(result);
+ ctdb_db->ctdb->status.pending_lockwait_calls--;
return NULL;
}
close(result->fd[0]);
close(result->fd[1]);
talloc_free(result);
+ ctdb_db->ctdb->status.pending_lockwait_calls--;
return NULL;
}
result->callback = callback;
result->private_data = private_data;
+ result->ctdb = ctdb_db->ctdb;
if (result->child == 0) {
close(result->fd[0]);
(void *)result);
if (result->fde == NULL) {
talloc_free(result);
+ ctdb_db->ctdb->status.pending_lockwait_calls--;
return NULL;
}
return p;
}
+
+/*
+ update a max latency number
+ */
+void ctdb_latency(double *latency, struct timeval t)
+{
+ double l = timeval_elapsed(&t);
+ if (l > *latency) {
+ *latency = l;
+ }
+}
struct ctdb_db_context *find_ctdb_db(struct ctdb_context *ctdb, uint32_t id);
+struct ctdb_context *ctdb_cmdline_client(struct event_context *ev, const char *ctdb_socket);
+
+struct ctdb_status;
+int ctdb_status(struct ctdb_context *ctdb, struct ctdb_status *status);
+
#endif
struct ctdb_queue *queue;
};
+/*
+ ctdb status information
+ */
+struct ctdb_status {
+ uint32_t client_packets_sent;
+ uint32_t client_packets_recv;
+ uint32_t node_packets_sent;
+ uint32_t node_packets_recv;
+ uint32_t total_calls;
+ uint32_t pending_calls;
+ uint32_t lockwait_calls;
+ uint32_t pending_lockwait_calls;
+ double max_call_latency;
+ double max_lockwait_latency;
+};
+
/* main state of the ctdb daemon */
struct ctdb_context {
struct event_context *ev;
struct ctdb_db_context *db_list;
struct ctdb_message_list *message_list;
struct ctdb_daemon_data daemon;
+ struct ctdb_status status;
};
struct ctdb_db_context {
CTDB_REQ_REGISTER = 1000,
CTDB_REQ_CONNECT_WAIT = 1001,
CTDB_REPLY_CONNECT_WAIT = 1002,
- CTDB_REQ_SHUTDOWN = 1003
+ CTDB_REQ_SHUTDOWN = 1003,
+ CTDB_REQ_STATUS = 1004,
+ CTDB_REPLY_STATUS = 1005
};
#define CTDB_MAGIC 0x43544442 /* CTDB */
uint32_t num_connected;
};
+struct ctdb_req_status {
+ struct ctdb_req_header hdr;
+};
+
+struct ctdb_reply_status {
+ struct ctdb_req_header hdr;
+ struct ctdb_status status;
+};
+
/* internal prototypes */
void ctdb_set_error(struct ctdb_context *ctdb, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
void ctdb_fatal(struct ctdb_context *ctdb, const char *msg);
void ctdb_recv_raw_pkt(void *p, uint8_t *data, uint32_t length);
+int ctdb_socket_connect(struct ctdb_context *ctdb);
+
+void ctdb_latency(double *latency, struct timeval t);
+
#endif
--- /dev/null
+/*
+ ctdb status tool
+
+ Copyright (C) Andrew Tridgell 2007
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "system/filesys.h"
+#include "popt.h"
+#include "cmdline.h"
+#include "../include/ctdb_private.h"
+
+
+/*
+ display status structure
+ */
+static void show_status(struct ctdb_status *s)
+{
+ printf(" client_packets_sent %u\n", s->client_packets_sent);
+ printf(" client_packets_recv %u\n", s->client_packets_recv);
+ printf(" node_packets_sent %u\n", s->node_packets_sent);
+ printf(" node_packets_recv %u\n", s->node_packets_recv);
+ printf(" total_calls %u\n", s->total_calls);
+ printf(" pending_calls %u\n", s->pending_calls);
+ printf(" lockwait_calls %u\n", s->lockwait_calls);
+ printf(" pending_lockwait_calls %u\n", s->pending_lockwait_calls);
+ printf(" max_call_latency %.6f seconds\n", s->max_call_latency);
+ printf(" max_lockwait_latency %.6f seconds\n", s->max_lockwait_latency);
+}
+
+/*
+ show usage message
+ */
+static void usage(void)
+{
+ printf("Usage: ctdb_status <socketpath>\n");
+ exit(1);
+}
+
+/*
+ main program
+*/
+int main(int argc, const char *argv[])
+{
+ struct ctdb_context *ctdb;
+ struct poptOption popt_options[] = {
+ POPT_AUTOHELP
+ POPT_CTDB_CMDLINE
+ POPT_TABLEEND
+ };
+ int opt;
+ const char **extra_argv;
+ int extra_argc = 0;
+ int ret;
+ poptContext pc;
+ struct event_context *ev;
+ const char *ctdb_socket;
+ struct ctdb_status status;
+
+ pc = poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_KEEP_FIRST);
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ default:
+ fprintf(stderr, "Invalid option %s: %s\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ exit(1);
+ }
+ }
+
+ /* setup the remaining options for the main program to use */
+ extra_argv = poptGetArgs(pc);
+ if (extra_argv) {
+ extra_argv++;
+ while (extra_argv[extra_argc]) extra_argc++;
+ }
+
+ if (extra_argc < 1) {
+ usage();
+ }
+
+ ctdb_socket = extra_argv[0];
+
+ ev = event_context_init(NULL);
+
+ /* initialise ctdb */
+ ctdb = ctdb_cmdline_client(ev, ctdb_socket);
+ if (ctdb == NULL) {
+ printf("Failed to init ctdb\n");
+ exit(1);
+ }
+
+ ret = ctdb_status(ctdb, &status);
+ if (ret != 0) {
+ printf("Failed to get ctdb status\n");
+ exit(1);
+ }
+
+ show_status(&status);
+
+ return 0;
+}