diff --git a/include/crm/crm.h b/include/crm/crm.h index dc2adc1..ce2074b 100644 --- a/include/crm/crm.h +++ b/include/crm/crm.h @@ -51,7 +51,7 @@ extern "C" { * >=3.0.13: Fail counts include operation name and interval * >=3.2.0: DC supports PCMK_LRM_OP_INVALID and PCMK_LRM_OP_NOT_CONNECTED */ -# define CRM_FEATURE_SET "3.4.0" +# define CRM_FEATURE_SET "3.4.1" # define EOS '\0' # define DIMOF(a) ((int) (sizeof(a)/sizeof(a[0])) ) diff --git a/tools/crmadmin.c b/tools/crmadmin.c index 4688458..2ebdd14 100644 --- a/tools/crmadmin.c +++ b/tools/crmadmin.c @@ -20,7 +20,9 @@ #include #include #include +#include #include +#include #include #define DEFAULT_MESSAGE_TIMEOUT_MS 30000 @@ -31,6 +33,8 @@ static GMainLoop *mainloop = NULL; bool do_work(pcmk_ipc_api_t *api); void do_find_node_list(xmlNode *xml_node); +static char *ipc_name = NULL; + gboolean admin_message_timeout(gpointer data); static enum { @@ -40,6 +44,7 @@ static enum { cmd_elect_dc, cmd_whois_dc, cmd_list_nodes, + cmd_pacemakerd_health, } command = cmd_none; static gboolean BE_VERBOSE = FALSE; @@ -82,6 +87,15 @@ static pcmk__cli_option_t long_options[] = { pcmk__option_default }, { + "pacemakerd", no_argument, NULL, 'P', + "Display the status of local pacemakerd.", pcmk__option_default + }, + { + "-spacer-", no_argument, NULL, '-', + "\n\tResult is the state of the sub-daemons watched by pacemakerd.\n", + pcmk__option_default + }, + { "dc_lookup", no_argument, NULL, 'D', "Display the uname of the node co-ordinating the cluster.", pcmk__option_default @@ -122,16 +136,21 @@ static pcmk__cli_option_t long_options[] = { { "bash-export", no_argument, NULL, 'B', "Display nodes as shell commands of the form 'export uname=uuid' " - "(valid with -N/--nodes)'\n", + "(valid with -N/--nodes)", + pcmk__option_default + }, + { + "ipc-name", required_argument, NULL, 'i', + "Name to use for ipc instead of 'crmadmin' (with -P/--pacemakerd).", pcmk__option_default }, { "-spacer-", no_argument, NULL, '-', - "Notes:", pcmk__option_default + "\nNotes:", pcmk__option_default }, { "-spacer-", no_argument, NULL, '-', - "The -K and -E commands do not work and may be removed in a future " + "\nThe -K and -E commands do not work and may be removed in a future " "version.", pcmk__option_default }, @@ -223,6 +242,88 @@ done: quit_main_loop(exit_code); } +static void +pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api, + enum pcmk_ipc_event event_type, crm_exit_t status, + void *event_data, void *user_data) +{ + pcmk_pacemakerd_api_reply_t *reply = event_data; + + switch (event_type) { + case pcmk_ipc_event_disconnect: + if (exit_code == CRM_EX_DISCONNECT) { // Unexpected + fprintf(stderr, "error: Lost connection to pacemakerd\n"); + } + goto done; + break; + + case pcmk_ipc_event_reply: + break; + + default: + return; + } + + if (message_timer_id != 0) { + g_source_remove(message_timer_id); + message_timer_id = 0; + } + + if (status != CRM_EX_OK) { + fprintf(stderr, "error: Bad reply from pacemakerd: %s", + crm_exit_str(status)); + exit_code = status; + goto done; + } + + if (reply->reply_type != pcmk_pacemakerd_reply_ping) { + fprintf(stderr, "error: Unknown reply type %d from pacemakerd\n", + reply->reply_type); + goto done; + } + + // Parse desired information from reply + switch (command) { + case cmd_pacemakerd_health: + { + crm_time_t *crm_when = crm_time_new(NULL); + char *pinged_buf = NULL; + + crm_time_set_timet(crm_when, &reply->data.ping.last_good); + pinged_buf = crm_time_as_string(crm_when, + crm_time_log_date | crm_time_log_timeofday | + crm_time_log_with_timezone); + + printf("Status of %s: '%s' %s %s\n", + reply->data.ping.sys_from, + (reply->data.ping.status == pcmk_rc_ok)? + pcmk_pacemakerd_api_daemon_state_enum2text( + reply->data.ping.state):"query failed", + (reply->data.ping.status == pcmk_rc_ok)?"last updated":"", + (reply->data.ping.status == pcmk_rc_ok)?pinged_buf:""); + if (BE_SILENT && + (reply->data.ping.state != pcmk_pacemakerd_state_invalid)) { + fprintf(stderr, "%s\n", + (reply->data.ping.status == pcmk_rc_ok)? + pcmk_pacemakerd_api_daemon_state_enum2text( + reply->data.ping.state): + "query failed"); + } + exit_code = CRM_EX_OK; + free(pinged_buf); + } + break; + + default: // Not really possible here + exit_code = CRM_EX_SOFTWARE; + break; + } + +done: + pcmk_disconnect_ipc(pacemakerd_api); + quit_main_loop(exit_code); +} + // \return Standard Pacemaker return code static int list_nodes() @@ -257,7 +358,9 @@ main(int argc, char **argv) int flag; int rc; pcmk_ipc_api_t *controld_api = NULL; + pcmk_ipc_api_t *pacemakerd_api = NULL; bool need_controld_api = true; + bool need_pacemakerd_api = false; crm_log_cli_init("crmadmin"); pcmk__set_cli_options(NULL, " [options]", long_options, @@ -282,7 +385,9 @@ main(int argc, char **argv) message_timeout_ms = DEFAULT_MESSAGE_TIMEOUT_MS; } break; - + case 'i': + ipc_name = strdup(optarg); + break; case '$': case '?': pcmk__cli_help(flag, CRM_EX_OK); @@ -304,6 +409,11 @@ main(int argc, char **argv) case 'q': BE_SILENT = TRUE; break; + case 'P': + command = cmd_pacemakerd_health; + need_pacemakerd_api = true; + need_controld_api = false; + break; case 'S': command = cmd_health; crm_trace("Option %c => %s", flag, optarg); @@ -369,7 +479,26 @@ main(int argc, char **argv) } } - if (do_work(controld_api)) { + // Connect to pacemakerd if needed + if (need_pacemakerd_api) { + rc = pcmk_new_ipc_api(&pacemakerd_api, pcmk_ipc_pacemakerd); + if (pacemakerd_api == NULL) { + fprintf(stderr, "error: Could not connect to pacemakerd: %s\n", + pcmk_rc_str(rc)); + exit_code = pcmk_rc2exitc(rc); + goto done; + } + pcmk_register_ipc_callback(pacemakerd_api, pacemakerd_event_cb, NULL); + rc = pcmk_connect_ipc(pacemakerd_api, pcmk_ipc_dispatch_main); + if (rc != pcmk_rc_ok) { + fprintf(stderr, "error: Could not connect to pacemakerd: %s\n", + pcmk_rc_str(rc)); + exit_code = pcmk_rc2exitc(rc); + goto done; + } + } + + if (do_work(controld_api?controld_api:pacemakerd_api)) { // A reply is needed from controller, so run main loop to get it exit_code = CRM_EX_DISCONNECT; // For unexpected disconnects mainloop = g_main_loop_new(NULL, FALSE); @@ -379,12 +508,19 @@ main(int argc, char **argv) } done: + if (controld_api != NULL) { pcmk_ipc_api_t *capi = controld_api; - controld_api = NULL; // Ensure we can't free this twice pcmk_free_ipc_api(capi); } + + if (pacemakerd_api != NULL) { + pcmk_ipc_api_t *capi = pacemakerd_api; + pacemakerd_api = NULL; // Ensure we can't free this twice + pcmk_free_ipc_api(capi); + } + if (mainloop != NULL) { g_main_loop_unref(mainloop); mainloop = NULL; @@ -394,30 +530,35 @@ done: // \return True if reply from controller is needed bool -do_work(pcmk_ipc_api_t *controld_api) +do_work(pcmk_ipc_api_t *api) { bool need_reply = false; int rc = pcmk_rc_ok; switch (command) { case cmd_shutdown: - rc = pcmk_controld_api_shutdown(controld_api, dest_node); + rc = pcmk_controld_api_shutdown(api, dest_node); break; case cmd_health: // dest_node != NULL case cmd_whois_dc: // dest_node == NULL - rc = pcmk_controld_api_ping(controld_api, dest_node); + rc = pcmk_controld_api_ping(api, dest_node); need_reply = true; break; case cmd_elect_dc: - rc = pcmk_controld_api_start_election(controld_api); + rc = pcmk_controld_api_start_election(api); break; case cmd_list_nodes: rc = list_nodes(); break; + case cmd_pacemakerd_health: + rc = pcmk_pacemakerd_api_ping(api, ipc_name); + need_reply = true; + break; + case cmd_none: // not actually possible here break; }