From 9cbdb406129343dac30883f43bfe1e8e046d6041 Mon Sep 17 00:00:00 2001 From: Amitay Isaacs Date: Mon, 21 Nov 2016 17:39:02 +1100 Subject: [PATCH] ctdb-tool: Add helper for talking to event daemon Signed-off-by: Amitay Isaacs Reviewed-by: Martin Schwenke --- ctdb/packaging/RPM/ctdb.spec.in | 1 + ctdb/tools/ctdb_event.c | 532 ++++++++++++++++++++++++++++++++ ctdb/wscript | 6 + 3 files changed, 539 insertions(+) create mode 100644 ctdb/tools/ctdb_event.c diff --git a/ctdb/packaging/RPM/ctdb.spec.in b/ctdb/packaging/RPM/ctdb.spec.in index 2a0fa0b4606..986b7b0b2bb 100644 --- a/ctdb/packaging/RPM/ctdb.spec.in +++ b/ctdb/packaging/RPM/ctdb.spec.in @@ -218,6 +218,7 @@ rm -rf $RPM_BUILD_ROOT %{_libexecdir}/ctdb/ctdb_event_helper %{_libexecdir}/ctdb/ctdb_recovery_helper %{_libexecdir}/ctdb/ctdb_mutex_fcntl_helper +%{_libexecdir}/ctdb/ctdb_event %{_libexecdir}/ctdb/ctdb_natgw %{_libexecdir}/ctdb/ctdb_lvs %{_libexecdir}/ctdb/ctdb_killtcp diff --git a/ctdb/tools/ctdb_event.c b/ctdb/tools/ctdb_event.c new file mode 100644 index 00000000000..62b4b9121a7 --- /dev/null +++ b/ctdb/tools/ctdb_event.c @@ -0,0 +1,532 @@ +/* + CTDB event daemon control tool + + Copyright (C) Amitay Isaacs 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include "replace.h" +#include "system/network.h" +#include "system/time.h" + +#include +#include + +#include "lib/util/debug.h" + +#include "protocol/protocol_api.h" +#include "client/client.h" +#include "common/logging.h" + +struct tool_context { + struct tevent_context *ev; + struct ctdb_event_context *eclient; +}; + +static void usage(void); + +static char *compact_args(const char **argv, int argc, int from) +{ + char *arg_str = NULL; + int i; + + for (i = from; i < argc; i++) { + arg_str = talloc_asprintf_append(arg_str, "%s ", argv[i]); + if (arg_str == NULL) { + fprintf(stderr, "talloc_asprintf_append() failed\n"); + exit(1); + } + } + + return arg_str; +} + +static int command_run(TALLOC_CTX *mem_ctx, struct tool_context *tctx, + int argc, const char **argv) +{ + struct tevent_req *req; + enum ctdb_event event; + const char *arg_str; + int timeout; + int eargs; + int ret, result; + bool status; + + if (argc < 2) { + usage(); + } + + event = ctdb_event_from_string(argv[0]); + if (event == CTDB_EVENT_MAX) { + fprintf(stderr, "Invalid event '%s'\n", argv[0]); + return 1; + } + + timeout = atoi(argv[1]); + if (timeout < 0) { + timeout = 0; + } + + switch (event) { + case CTDB_EVENT_INIT: + case CTDB_EVENT_SETUP: + case CTDB_EVENT_STARTUP: + case CTDB_EVENT_MONITOR: + case CTDB_EVENT_IPREALLOCATED: + eargs = 0; + break; + + case CTDB_EVENT_TAKE_IP: + case CTDB_EVENT_RELEASE_IP: + eargs = 3; + break; + + case CTDB_EVENT_UPDATE_IP: + eargs = 4; + break; + + default: + eargs = -1; + break; + } + + if (eargs < 0) { + fprintf(stderr, "Cannot run event %s\n", argv[0]); + return 1; + } + + if (argc != 2 + eargs) { + fprintf(stderr, "Insufficient arguments for event %s\n", + argv[0]); + return 1; + } + + arg_str = compact_args(argv, argc, 2); + + req = ctdb_event_run_send(mem_ctx, tctx->ev, tctx->eclient, + event, timeout, arg_str); + if (req == NULL) { + return ENOMEM; + } + + tevent_req_poll(req, tctx->ev); + + status = ctdb_event_run_recv(req, &ret, &result); + talloc_free(req); + if (! status) { + fprintf(stderr, "Failed to run event %s, ret=%d\n", + argv[0], ret); + return ret; + } + + if (result == -ETIME) { + fprintf(stderr, "Event %s timed out\n", argv[0]); + } else if (result == -ECANCELED) { + fprintf(stderr, "Event %s got cancelled\n", argv[0]); + } else if (result != 0) { + fprintf(stderr, "Failed to run event %s, result=%d\n", + argv[0], result); + } + + ret = (result < 0) ? -result : result; + return ret; +} + +static double timeval_delta(struct timeval *tv2, struct timeval *tv) +{ + return (tv2->tv_sec - tv->tv_sec) + + (tv2->tv_usec - tv->tv_usec) * 1.0e-6; +} + +static void print_status_one(struct ctdb_script *script) +{ + if (script->status == -ETIME) { + printf("%-20s %-10s %s", script->name, "TIMEDOUT", + ctime(&script->start.tv_sec)); + } else if (script->status == -ENOEXEC) { + printf("%-20s %-10s\n", script->name, "DISABLED"); + } else if (script->status < 0) { + printf("%-20s %-10s (%s)\n", script->name, "CANNOT RUN", + strerror(-script->status)); + } else if (script->status == 0) { + printf("%-20s %-10s %.3lf %s", script->name, "OK", + timeval_delta(&script->finished, &script->start), + ctime(&script->start.tv_sec)); + } else { + printf("%-20s %-10s %.3lf %s", script->name, "ERROR", + timeval_delta(&script->finished, &script->start), + ctime(&script->start.tv_sec)); + } + + if (script->status != 0 && script->status != -ENOEXEC) { + printf(" OUTPUT: %s\n", script->output); + } +} + +static int command_status(TALLOC_CTX *mem_ctx, struct tool_context *tctx, + int argc, const char **argv) +{ + struct tevent_req *req; + struct ctdb_script_list *script_list; + enum ctdb_event event; + uint32_t state; + int ret, result, event_status, i; + bool status; + + if (argc < 1) { + event = CTDB_EVENT_MONITOR; + } else { + event = ctdb_event_from_string(argv[0]); + if (event == CTDB_EVENT_MAX) { + fprintf(stderr, "Invalid event '%s'\n", argv[0]); + return EINVAL; + } + } + + if (argc < 2) { + state = CTDB_EVENT_LAST_RUN; + } else { + if (strcmp(argv[1], "lastrun") == 0) { + state = CTDB_EVENT_LAST_RUN; + } else if (strcmp(argv[1], "lastpass") == 0) { + state = CTDB_EVENT_LAST_PASS; + } else if (strcmp(argv[1], "lastfail") == 0) { + state = CTDB_EVENT_LAST_FAIL; + } else { + fprintf(stderr, "Invalid state %s\n", argv[1]); + return EINVAL; + } + } + + req = ctdb_event_status_send(mem_ctx, tctx->ev, tctx->eclient, + event, state); + if (req == NULL) { + return ENOMEM; + } + + tevent_req_poll(req, tctx->ev); + + status = ctdb_event_status_recv(req, &ret, &result, &event_status, + mem_ctx, &script_list); + talloc_free(req); + if (! status) { + fprintf(stderr, "Failed to get event %s status, ret=%d\n", + argv[0], ret); + return ret; + } + + if (result != 0) { + fprintf(stderr, "Failed to get event %s status, result=%d\n", + argv[0], result); + return result; + } + + if (script_list == NULL) { + if (state == CTDB_EVENT_LAST_RUN) { + printf("Event %s has never run\n", argv[0]); + } else if (state == CTDB_EVENT_LAST_PASS) { + printf("Event %s has never passed\n", argv[0]); + } else if (state == CTDB_EVENT_LAST_FAIL) { + printf("Event %s has never failed\n", argv[0]); + } + } else { + for (i=0; inum_scripts; i++) { + print_status_one(&script_list->script[i]); + } + talloc_free(script_list); + } + + return event_status; +} + +static int command_script_list(TALLOC_CTX *mem_ctx, struct tool_context *tctx, + int argc, const char **argv) +{ + struct tevent_req *req; + struct ctdb_script_list *script_list = NULL; + int ret, result, i; + bool status; + + if (argc != 0) { + usage(); + } + + req = ctdb_event_script_list_send(mem_ctx, tctx->ev, tctx->eclient); + if (req == NULL) { + return ENOMEM; + } + + tevent_req_poll(req, tctx->ev); + + status = ctdb_event_script_list_recv(req, &ret, &result, + mem_ctx, &script_list); + talloc_free(req); + if (! status) { + fprintf(stderr, "Failed to get script list, ret=%d\n", ret); + return ret; + } + + if (result != 0) { + fprintf(stderr, "Failed to get script list, result=%d\n", + result); + return result; + } + + if (script_list == NULL || script_list->num_scripts == 0) { + printf("No event scripts found\n"); + } else { + for (i=0; inum_scripts; i++) { + struct ctdb_script *s; + + s = &script_list->script[i]; + + if (s->status == -ENOEXEC) { + printf("%-20s DISABLED\n", s->name); + } else { + printf("%-20s\n", s->name); + } + } + talloc_free(script_list); + } + + return 0; +} + +static int command_script_enable(TALLOC_CTX *mem_ctx, + struct tool_context *tctx, + int argc, const char **argv) +{ + struct tevent_req *req; + int ret, result; + bool status; + + if (argc != 1) { + usage(); + } + + req = ctdb_event_script_enable_send(mem_ctx, tctx->ev, tctx->eclient, + argv[0]); + if (req == NULL) { + return ENOMEM; + } + + tevent_req_poll(req, tctx->ev); + + status = ctdb_event_script_enable_recv(req, &ret, &result); + talloc_free(req); + if (! status) { + fprintf(stderr, "Failed to enable script %s, ret=%d\n", + argv[0], ret); + return ret; + } + + if (result == -ENOENT) { + fprintf(stderr, "Script %s does not exist\n", argv[0]); + } else if (result == -EINVAL) { + fprintf(stderr, "Script name %s is invalid\n", argv[0]); + } else if (result != 0) { + fprintf(stderr, "Failed to enable script %s, result=%d\n", + argv[0], result); + } + + ret = (result < 0) ? -result : result; + return ret; +} + +static int command_script_disable(TALLOC_CTX *mem_ctx, + struct tool_context *tctx, + int argc, const char **argv) +{ + struct tevent_req *req; + int ret, result; + bool status; + + if (argc != 1) { + usage(); + } + + req = ctdb_event_script_disable_send(mem_ctx, tctx->ev, tctx->eclient, + argv[0]); + if (req == NULL) { + return ENOMEM; + } + + tevent_req_poll(req, tctx->ev); + + status = ctdb_event_script_disable_recv(req, &ret, &result); + talloc_free(req); + if (! status) { + fprintf(stderr, "Failed to disable script %s, ret=%d\n", + argv[0], ret); + return ret; + } + + if (result == -ENOENT) { + fprintf(stderr, "Script %s does not exist\n", argv[0]); + } else if (result == -EINVAL) { + fprintf(stderr, "Script name %s is invalid\n", argv[0]); + } else if (result != 0) { + fprintf(stderr, "Failed to disable script %s, result=%d\n", + argv[0], result); + } + + ret = (result < 0) ? -result : result; + return ret; +} + +static const struct ctdb_event_cmd { + const char *name; + const char *str1; + const char *str2; + int (*fn)(TALLOC_CTX *, struct tool_context *, int, const char **); + const char *msg; + const char *args; +} ctdb_event_commands[] = { + { "run", "run", NULL, command_run, + "Run an event", " " }, + { "status", "status", NULL, command_status, + "Get last status of an event", + "[] [lastrun|lastpass|lastfail]" }, + { "script list", "script", "list", command_script_list, + "Get list of event scripts", NULL }, + { "script enable", "script", "enable", command_script_enable, + "Enable an event script", "