diff options
Diffstat (limited to '')
-rw-r--r-- | tools/crmadmin.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/tools/crmadmin.c b/tools/crmadmin.c new file mode 100644 index 0000000..0b400ae --- /dev/null +++ b/tools/crmadmin.c @@ -0,0 +1,275 @@ +/* + * Copyright 2004-2023 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> // atoi() + +#include <glib.h> // gboolean, GMainLoop, etc. +#include <libxml/tree.h> // xmlNode + +#include <pacemaker-internal.h> + +#include <crm/common/cmdline_internal.h> +#include <crm/common/output_internal.h> + +#define SUMMARY "query and manage the Pacemaker controller" + +static enum { + cmd_none, + cmd_health, + cmd_whois_dc, + cmd_list_nodes, + cmd_pacemakerd_health, +} command = cmd_none; + +struct { + gboolean health; + gint timeout; + char *optarg; + char *ipc_name; + gboolean bash_export; +} options = { + .optarg = NULL, + .ipc_name = NULL, + .bash_export = FALSE +}; + +gboolean command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); + +static GOptionEntry command_options[] = { + { "status", 'S', 0, G_OPTION_ARG_CALLBACK, command_cb, + "Display the status of the specified node." + "\n Result is state of node's internal finite state" + "\n machine, which can be useful for debugging", + "NODE" + }, + { "pacemakerd", 'P', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Display the status of local pacemakerd." + "\n Result is the state of the sub-daemons watched" + "\n by pacemakerd.", + NULL + }, + { "dc_lookup", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Display the uname of the node co-ordinating the cluster." + "\n This is an internal detail rarely useful to" + "\n administrators except when deciding on which" + "\n node to examine the logs.", + NULL + }, + { "nodes", 'N', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Display the uname of all member nodes [optionally filtered by type (comma-separated)]" + "\n Types: all (default), cluster, guest, remote", + "TYPE" + }, + { "health", 'H', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.health, + NULL, + NULL + }, + + { NULL } +}; + +static GOptionEntry additional_options[] = { + { "timeout", 't', 0, G_OPTION_ARG_CALLBACK, command_cb, + "Time to wait before declaring the operation" + "\n failed", + "TIMESPEC" + }, + { "bash-export", 'B', 0, G_OPTION_ARG_NONE, &options.bash_export, + "Display nodes as shell commands of the form 'export uname=uuid'" + "\n (valid with -N/--nodes)", + }, + { "ipc-name", 'i', 0, G_OPTION_ARG_STRING, &options.ipc_name, + "Name to use for ipc instead of 'crmadmin' (with -P/--pacemakerd).", + "NAME" + }, + + { NULL } +}; + +gboolean +command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) +{ + if (!strcmp(option_name, "--status") || !strcmp(option_name, "-S")) { + command = cmd_health; + crm_trace("Option %c => %s", 'S', optarg); + } + + if (!strcmp(option_name, "--pacemakerd") || !strcmp(option_name, "-P")) { + command = cmd_pacemakerd_health; + } + + if (!strcmp(option_name, "--dc_lookup") || !strcmp(option_name, "-D")) { + command = cmd_whois_dc; + } + + if (!strcmp(option_name, "--nodes") || !strcmp(option_name, "-N")) { + command = cmd_list_nodes; + } + + if (!strcmp(option_name, "--timeout") || !strcmp(option_name, "-t")) { + options.timeout = crm_parse_interval_spec(optarg); + if (errno == EINVAL) { + return FALSE; + } + return TRUE; + } + + pcmk__str_update(&options.optarg, optarg); + return TRUE; +} + +static pcmk__supported_format_t formats[] = { + PCMK__SUPPORTED_FORMAT_NONE, + PCMK__SUPPORTED_FORMAT_TEXT, + PCMK__SUPPORTED_FORMAT_XML, + { NULL, NULL, NULL } +}; + +static GOptionContext * +build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) { + GOptionContext *context = NULL; + + const char *description = "Notes:\n\n" + "Time Specification:\n\n" + "The TIMESPEC in any command line option can be specified in many different\n" + "formats. It can be just an integer number of seconds, a number plus units\n" + "(ms/msec/us/usec/s/sec/m/min/h/hr), or an ISO 8601 period specification.\n\n" + "Report bugs to " PCMK__BUG_URL; + + GOptionEntry extra_prog_entries[] = { + { "quiet", 'q', 0, G_OPTION_ARG_NONE, &(args->quiet), + "Display only the essential query information", + NULL }, + + { NULL } + }; + + context = pcmk__build_arg_context(args, "text (default), xml", group, NULL); + g_option_context_set_description(context, description); + + /* Add the -q option, which cannot be part of the globally supported options + * because some tools use that flag for something else. + */ + pcmk__add_main_args(context, extra_prog_entries); + + pcmk__add_arg_group(context, "command", "Commands:", + "Show command options", command_options); + pcmk__add_arg_group(context, "additional", "Additional Options:", + "Show additional options", additional_options); + return context; +} + +int +main(int argc, char **argv) +{ + crm_exit_t exit_code = CRM_EX_OK; + int rc; + int argerr = 0; + + GError *error = NULL; + + pcmk__output_t *out = NULL; + + GOptionGroup *output_group = NULL; + pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY); + gchar **processed_args = pcmk__cmdline_preproc(argv, "itKNS"); + GOptionContext *context = build_arg_context(args, &output_group); + + pcmk__register_formats(output_group, formats); + if (!g_option_context_parse_strv(context, &processed_args, &error)) { + exit_code = CRM_EX_USAGE; + goto done; + } + + pcmk__cli_init_logging("crmadmin", args->verbosity); + + rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv); + if (rc != pcmk_rc_ok) { + exit_code = CRM_EX_ERROR; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s", + args->output_ty, pcmk_rc_str(rc)); + goto done; + } + + pcmk__register_lib_messages(out); + + out->quiet = args->quiet; + + if (!pcmk__force_args(context, &error, "%s --xml-simple-list --xml-substitute", g_get_prgname())) { + goto done; + } + + if (args->version) { + out->version(out, false); + goto done; + } + + if (options.health) { + out->err(out, "Cluster-wide health option not supported"); + ++argerr; + } + + if (command == cmd_none) { + out->err(out, "error: Must specify a command option"); + ++argerr; + } + + if (argerr) { + char *help = g_option_context_get_help(context, TRUE, NULL); + + out->err(out, "%s", help); + g_free(help); + exit_code = CRM_EX_USAGE; + goto done; + } + + switch (command) { + case cmd_health: + rc = pcmk__controller_status(out, options.optarg, + (unsigned int) options.timeout); + break; + case cmd_pacemakerd_health: + rc = pcmk__pacemakerd_status(out, options.ipc_name, + (unsigned int) options.timeout, true, + NULL); + break; + case cmd_list_nodes: + rc = pcmk__list_nodes(out, options.optarg, options.bash_export); + break; + case cmd_whois_dc: + rc = pcmk__designated_controller(out, + (unsigned int) options.timeout); + break; + case cmd_none: + rc = pcmk_rc_error; + break; + } + + if (rc != pcmk_rc_ok) { + out->err(out, "error: Command failed: %s", pcmk_rc_str(rc)); + exit_code = pcmk_rc2exitc(rc); + } + +done: + g_strfreev(processed_args); + pcmk__free_arg_context(context); + + pcmk__output_and_clear_error(&error, out); + + if (out != NULL) { + out->finish(out, exit_code, true, NULL); + pcmk__output_free(out); + } + pcmk__unregister_formats(); + return crm_exit(exit_code); +} |