diff options
Diffstat (limited to '')
-rw-r--r-- | tools/crm_node.c | 751 |
1 files changed, 516 insertions, 235 deletions
diff --git a/tools/crm_node.c b/tools/crm_node.c index ac2a190..1e7ce6c 100644 --- a/tools/crm_node.c +++ b/tools/crm_node.c @@ -9,6 +9,7 @@ #include <crm_internal.h> +#include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> @@ -24,6 +25,8 @@ #include <crm/common/ipc_controld.h> #include <crm/common/attrd_internal.h> +#include <pacemaker-internal.h> + #define SUMMARY "crm_node - Tool for displaying low-level node information" struct { @@ -42,8 +45,10 @@ gboolean command_cb(const gchar *option_name, const gchar *optarg, gpointer data gboolean name_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); gboolean remove_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error); +static GError *error = NULL; static GMainLoop *mainloop = NULL; static crm_exit_t exit_code = CRM_EX_OK; +static pcmk__output_t *out = NULL; #define INDENT " " @@ -91,6 +96,13 @@ static GOptionEntry addl_entries[] = { { NULL } }; +static pcmk__supported_format_t formats[] = { + PCMK__SUPPORTED_FORMAT_NONE, + PCMK__SUPPORTED_FORMAT_TEXT, + PCMK__SUPPORTED_FORMAT_XML, + { NULL, NULL, NULL } +}; + gboolean command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { if (pcmk__str_eq("-i", option_name, pcmk__str_casei) || pcmk__str_eq("--cluster-id", option_name, pcmk__str_casei)) { @@ -104,7 +116,7 @@ command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError } else if (pcmk__str_eq("-q", option_name, pcmk__str_casei) || pcmk__str_eq("--quorum", option_name, pcmk__str_casei)) { options.command = 'q'; } else { - g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_INVALID_PARAM, "Unknown param passed to command_cb: %s\n", option_name); + g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_INVALID_PARAM, "Unknown param passed to command_cb: %s", option_name); return FALSE; } @@ -121,7 +133,6 @@ name_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **e gboolean remove_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { if (optarg == NULL) { - crm_err("-R option requires an argument"); g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_INVALID_PARAM, "-R option requires an argument"); return FALSE; } @@ -132,6 +143,184 @@ remove_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError * return TRUE; } +PCMK__OUTPUT_ARGS("node-id", "uint32_t") +static int +node_id_default(pcmk__output_t *out, va_list args) { + uint32_t node_id = va_arg(args, uint32_t); + + out->info(out, "%" PRIu32, node_id); + return pcmk_rc_ok; +} + +PCMK__OUTPUT_ARGS("node-id", "uint32_t") +static int +node_id_xml(pcmk__output_t *out, va_list args) { + uint32_t node_id = va_arg(args, uint32_t); + + char *id_s = crm_strdup_printf("%" PRIu32, node_id); + + pcmk__output_create_xml_node(out, "node-info", + "nodeid", id_s, + NULL); + + free(id_s); + return pcmk_rc_ok; +} + +PCMK__OUTPUT_ARGS("node-list", "GList *") +static int +node_list_default(pcmk__output_t *out, va_list args) +{ + GList *nodes = va_arg(args, GList *); + + for (GList *node_iter = nodes; node_iter != NULL; node_iter = node_iter->next) { + pcmk_controld_api_node_t *node = node_iter->data; + out->info(out, "%" PRIu32 " %s %s", node->id, pcmk__s(node->uname, ""), + pcmk__s(node->state, "")); + } + + return pcmk_rc_ok; +} + +PCMK__OUTPUT_ARGS("node-list", "GList *") +static int +node_list_xml(pcmk__output_t *out, va_list args) +{ + GList *nodes = va_arg(args, GList *); + + out->begin_list(out, NULL, NULL, "nodes"); + + for (GList *node_iter = nodes; node_iter != NULL; node_iter = node_iter->next) { + pcmk_controld_api_node_t *node = node_iter->data; + char *id_s = crm_strdup_printf("%" PRIu32, node->id); + + pcmk__output_create_xml_node(out, "node", + "id", id_s, + "name", node->uname, + "state", node->state, + NULL); + + free(id_s); + } + + out->end_list(out); + + return pcmk_rc_ok; +} + +PCMK__OUTPUT_ARGS("node-name", "uint32_t", "const char *") +static int +node_name_default(pcmk__output_t *out, va_list args) { + uint32_t node_id G_GNUC_UNUSED = va_arg(args, uint32_t); + const char *node_name = va_arg(args, const char *); + + out->info(out, "%s", node_name); + return pcmk_rc_ok; +} + +PCMK__OUTPUT_ARGS("node-name", "uint32_t", "const char *") +static int +node_name_xml(pcmk__output_t *out, va_list args) { + uint32_t node_id = va_arg(args, uint32_t); + const char *node_name = va_arg(args, const char *); + + char *id_s = crm_strdup_printf("%" PRIu32, node_id); + + pcmk__output_create_xml_node(out, "node-info", + "nodeid", id_s, + XML_ATTR_UNAME, node_name, + NULL); + + free(id_s); + return pcmk_rc_ok; +} + +PCMK__OUTPUT_ARGS("partition-list", "GList *") +static int +partition_list_default(pcmk__output_t *out, va_list args) +{ + GList *nodes = va_arg(args, GList *); + + GString *buffer = NULL; + + for (GList *node_iter = nodes; node_iter != NULL; node_iter = node_iter->next) { + pcmk_controld_api_node_t *node = node_iter->data; + if (pcmk__str_eq(node->state, "member", pcmk__str_none)) { + pcmk__add_separated_word(&buffer, 128, pcmk__s(node->uname, ""), " "); + } + } + + if (buffer != NULL) { + out->info(out, "%s", buffer->str); + g_string_free(buffer, TRUE); + return pcmk_rc_ok; + } + + return pcmk_rc_no_output; +} + +PCMK__OUTPUT_ARGS("partition-list", "GList *") +static int +partition_list_xml(pcmk__output_t *out, va_list args) +{ + GList *nodes = va_arg(args, GList *); + + out->begin_list(out, NULL, NULL, "nodes"); + + for (GList *node_iter = nodes; node_iter != NULL; node_iter = node_iter->next) { + pcmk_controld_api_node_t *node = node_iter->data; + + if (pcmk__str_eq(node->state, "member", pcmk__str_none)) { + char *id_s = crm_strdup_printf("%" PRIu32, node->id); + + pcmk__output_create_xml_node(out, "node", + "id", id_s, + "name", node->uname, + "state", node->state, + NULL); + free(id_s); + } + } + + out->end_list(out); + return pcmk_rc_ok; +} + +PCMK__OUTPUT_ARGS("quorum", "bool") +static int +quorum_default(pcmk__output_t *out, va_list args) { + bool have_quorum = va_arg(args, int); + + out->info(out, "%d", have_quorum); + return pcmk_rc_ok; +} + +PCMK__OUTPUT_ARGS("quorum", "bool") +static int +quorum_xml(pcmk__output_t *out, va_list args) { + bool have_quorum = va_arg(args, int); + + pcmk__output_create_xml_node(out, "cluster-info", + "quorum", have_quorum ? "true" : "false", + NULL); + return pcmk_rc_ok; +} + +static pcmk__message_entry_t fmt_functions[] = { + { "node-id", "default", node_id_default }, + { "node-id", "xml", node_id_xml }, + { "node-list", "default", node_list_default }, + { "node-list", "xml", node_list_xml }, + { "node-name", "default", node_name_default }, + { "node-name", "xml", node_name_xml }, + { "quorum", "default", quorum_default }, + { "quorum", "xml", quorum_xml }, + { "partition-list", "default", partition_list_default }, + { "partition-list", "xml", partition_list_xml }, + + { NULL, NULL, NULL } +}; + static gint sort_node(gconstpointer a, gconstpointer b) { @@ -152,7 +341,8 @@ controller_event_cb(pcmk_ipc_api_t *controld_api, switch (event_type) { case pcmk_ipc_event_disconnect: if (exit_code == CRM_EX_DISCONNECT) { // Unexpected - fprintf(stderr, "error: Lost connection to controller\n"); + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Lost connection to controller"); } goto done; break; @@ -165,93 +355,26 @@ controller_event_cb(pcmk_ipc_api_t *controld_api, } if (status != CRM_EX_OK) { - fprintf(stderr, "error: Bad reply from controller: %s\n", - crm_exit_str(status)); + exit_code = status; + g_set_error(&error, PCMK__EXITC_ERROR, status, + "Bad reply from controller: %s", + crm_exit_str(status)); goto done; } - // Parse desired info from reply and display to user - switch (options.command) { - case 'i': - if (reply->reply_type != pcmk_controld_reply_info) { - fprintf(stderr, - "error: Unknown reply type %d from controller\n", - reply->reply_type); - goto done; - } - if (reply->data.node_info.id == 0) { - fprintf(stderr, - "error: Controller reply did not contain node ID\n"); - exit_code = CRM_EX_PROTOCOL; - goto done; - } - printf("%d\n", reply->data.node_info.id); - break; - - case 'n': - case 'N': - if (reply->reply_type != pcmk_controld_reply_info) { - fprintf(stderr, - "error: Unknown reply type %d from controller\n", - reply->reply_type); - goto done; - } - if (reply->data.node_info.uname == NULL) { - fprintf(stderr, "Node is not known to cluster\n"); - exit_code = CRM_EX_NOHOST; - goto done; - } - printf("%s\n", reply->data.node_info.uname); - break; - - case 'q': - if (reply->reply_type != pcmk_controld_reply_info) { - fprintf(stderr, - "error: Unknown reply type %d from controller\n", - reply->reply_type); - goto done; - } - printf("%d\n", reply->data.node_info.have_quorum); - if (!(reply->data.node_info.have_quorum)) { - exit_code = CRM_EX_QUORUM; - goto done; - } - break; + if (reply->reply_type != pcmk_controld_reply_nodes) { + g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_INDETERMINATE, + "Unknown reply type %d from controller", + reply->reply_type); + goto done; + } - case 'l': - case 'p': - if (reply->reply_type != pcmk_controld_reply_nodes) { - fprintf(stderr, - "error: Unknown reply type %d from controller\n", - reply->reply_type); - goto done; - } - reply->data.nodes = g_list_sort(reply->data.nodes, sort_node); - for (GList *node_iter = reply->data.nodes; - node_iter != NULL; node_iter = node_iter->next) { - - pcmk_controld_api_node_t *node = node_iter->data; - const char *uname = (node->uname? node->uname : ""); - const char *state = (node->state? node->state : ""); - - if (options.command == 'l') { - printf("%lu %s %s\n", - (unsigned long) node->id, uname, state); - - // i.e. CRM_NODE_MEMBER, but we don't want to include cluster.h - } else if (!strcmp(state, "member")) { - printf("%s ", uname); - } - } - if (options.command == 'p') { - printf("\n"); - } - break; + reply->data.nodes = g_list_sort(reply->data.nodes, sort_node); - default: - fprintf(stderr, "internal error: Controller reply not expected\n"); - exit_code = CRM_EX_SOFTWARE; - goto done; + if (options.command == 'p') { + out->message(out, "partition-list", reply->data.nodes); + } else if (options.command == 'l') { + out->message(out, "node-list", reply->data.nodes); } // Success @@ -262,7 +385,7 @@ done: } static void -run_controller_mainloop(uint32_t nodeid, bool list_nodes) +run_controller_mainloop(void) { pcmk_ipc_api_t *controld_api = NULL; int rc; @@ -273,31 +396,30 @@ run_controller_mainloop(uint32_t nodeid, bool list_nodes) // Create controller IPC object rc = pcmk_new_ipc_api(&controld_api, pcmk_ipc_controld); if (rc != pcmk_rc_ok) { - fprintf(stderr, "error: Could not connect to controller: %s\n", - pcmk_rc_str(rc)); + g_set_error(&error, PCMK__RC_ERROR, rc, + "Could not connect to controller: %s", + pcmk_rc_str(rc)); return; } pcmk_register_ipc_callback(controld_api, controller_event_cb, NULL); // Connect to controller - rc = pcmk_connect_ipc(controld_api, pcmk_ipc_dispatch_main); + rc = pcmk__connect_ipc(controld_api, pcmk_ipc_dispatch_main, 5); if (rc != pcmk_rc_ok) { - fprintf(stderr, "error: Could not connect to controller: %s\n", - pcmk_rc_str(rc)); exit_code = pcmk_rc2exitc(rc); + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Could not connect to %s: %s", + pcmk_ipc_name(controld_api, true), pcmk_rc_str(rc)); return; } - if (list_nodes) { - rc = pcmk_controld_api_list_nodes(controld_api); - } else { - rc = pcmk_controld_api_node_info(controld_api, nodeid); - } + rc = pcmk_controld_api_list_nodes(controld_api); + if (rc != pcmk_rc_ok) { - fprintf(stderr, "error: Could not ping controller: %s\n", - pcmk_rc_str(rc)); pcmk_disconnect_ipc(controld_api); exit_code = pcmk_rc2exitc(rc); + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Could not ping controller: %s", pcmk_rc_str(rc)); return; } @@ -310,169 +432,295 @@ run_controller_mainloop(uint32_t nodeid, bool list_nodes) } static void -print_node_name(void) +print_node_id(void) { - // Check environment first (i.e. when called by resource agent) - const char *name = getenv("OCF_RESKEY_" CRM_META "_" XML_LRM_ATTR_TARGET); + uint32_t nodeid; + int rc = pcmk__query_node_info(out, &nodeid, NULL, NULL, NULL, NULL, NULL, + false, 0); - if (name != NULL) { - printf("%s\n", name); - exit_code = CRM_EX_OK; + if (rc != pcmk_rc_ok) { + /* pcmk__query_node_info already sets an error message on the output object, + * so there's no need to call g_set_error here. That would just create a + * duplicate error message in the output. + */ + exit_code = pcmk_rc2exitc(rc); return; + } - } else { - /* Otherwise ask the controller. - * FIXME: Use pcmk__query_node_name() after conversion to formatted - * output. - */ - run_controller_mainloop(0, false); + rc = out->message(out, "node-id", nodeid); + + if (rc != pcmk_rc_ok) { + g_set_error(&error, PCMK__RC_ERROR, rc, "Could not print node ID: %s", + pcmk_rc_str(rc)); } + + exit_code = pcmk_rc2exitc(rc); } -static int -cib_remove_node(long id, const char *name) +static void +print_node_name(uint32_t nodeid) { - int rc; - cib_t *cib = NULL; - xmlNode *node = NULL; - xmlNode *node_state = NULL; + int rc = pcmk_rc_ok; + char *node_name = NULL; + + if (nodeid == 0) { + // Check environment first (i.e. when called by resource agent) + const char *name = getenv("OCF_RESKEY_" CRM_META "_" XML_LRM_ATTR_TARGET); + + if (name != NULL) { + rc = out->message(out, "node-name", 0, name); + goto done; + } + } - crm_trace("Removing %s from the CIB", name); + // Otherwise ask the controller - if(name == NULL && id == 0) { - return -ENOTUNIQ; + /* pcmk__query_node_name already sets an error message on the output object, + * so there's no need to call g_set_error here. That would just create a + * duplicate error message in the output. + */ + rc = pcmk__query_node_name(out, nodeid, &node_name, 0); + if (rc != pcmk_rc_ok) { + exit_code = pcmk_rc2exitc(rc); + return; } - node = create_xml_node(NULL, XML_CIB_TAG_NODE); - node_state = create_xml_node(NULL, XML_CIB_TAG_STATE); + rc = out->message(out, "node-name", 0, node_name); - crm_xml_add(node, XML_ATTR_UNAME, name); - crm_xml_add(node_state, XML_ATTR_UNAME, name); - if (id > 0) { - crm_xml_set_id(node, "%ld", id); - crm_xml_add(node_state, XML_ATTR_ID, ID(node)); +done: + if (node_name != NULL) { + free(node_name); } - cib = cib_new(); - cib->cmds->signon(cib, crm_system_name, cib_command); + if (rc != pcmk_rc_ok) { + g_set_error(&error, PCMK__RC_ERROR, rc, "Could not print node name: %s", + pcmk_rc_str(rc)); + } - rc = cib->cmds->remove(cib, XML_CIB_TAG_NODES, node, cib_sync_call); - if (rc != pcmk_ok) { - printf("Could not remove %s[%ld] from " XML_CIB_TAG_NODES ": %s", - name, id, pcmk_strerror(rc)); + exit_code = pcmk_rc2exitc(rc); +} + +static void +print_quorum(void) +{ + bool quorum; + int rc = pcmk__query_node_info(out, NULL, NULL, NULL, NULL, &quorum, NULL, + false, 0); + + if (rc != pcmk_rc_ok) { + /* pcmk__query_node_info already sets an error message on the output object, + * so there's no need to call g_set_error here. That would just create a + * duplicate error message in the output. + */ + exit_code = pcmk_rc2exitc(rc); + return; + } + + rc = out->message(out, "quorum", quorum); + + if (rc != pcmk_rc_ok) { + g_set_error(&error, PCMK__RC_ERROR, rc, "Could not print quorum status: %s", + pcmk_rc_str(rc)); + } + + exit_code = pcmk_rc2exitc(rc); +} + +/*! + * \internal + * \brief Extend a transaction by removing a node from a CIB section + * + * \param[in,out] cib Active CIB connection + * \param[in] element CIB element containing node name and/or ID + * \param[in] section CIB section that \p element is in + * \param[in] node_name Name of node to purge (NULL to leave unspecified) + * \param[in] node_id Node ID of node to purge (0 to leave unspecified) + * + * \note At least one of node_name and node_id must be specified. + * \return Standard Pacemaker return code + */ +static int +remove_from_section(cib_t *cib, const char *element, const char *section, + const char *node_name, long node_id) +{ + xmlNode *xml = NULL; + int rc = pcmk_rc_ok; + + xml = create_xml_node(NULL, element); + if (xml == NULL) { + return pcmk_rc_error; + } + crm_xml_add(xml, XML_ATTR_UNAME, node_name); + if (node_id > 0) { + crm_xml_set_id(xml, "%ld", node_id); + } + rc = cib->cmds->remove(cib, section, xml, cib_transaction); + free_xml(xml); + return (rc >= 0)? pcmk_rc_ok : pcmk_legacy2rc(rc); +} + +/*! + * \internal + * \brief Purge a node from CIB + * + * \param[in] node_name Name of node to purge (or NULL to leave unspecified) + * \param[in] node_id Node ID of node to purge (or 0 to leave unspecified) + * + * \note At least one of node_name and node_id must be specified. + * \return Standard Pacemaker return code + */ +static int +purge_node_from_cib(const char *node_name, long node_id) +{ + int rc = pcmk_rc_ok; + int commit_rc = pcmk_rc_ok; + cib_t *cib = NULL; + + // Connect to CIB and start a transaction + cib = cib_new(); + if (cib == NULL) { + return ENOTCONN; + } + rc = cib->cmds->signon(cib, crm_system_name, cib_command); + if (rc == pcmk_ok) { + rc = cib->cmds->init_transaction(cib); } - rc = cib->cmds->remove(cib, XML_CIB_TAG_STATUS, node_state, cib_sync_call); if (rc != pcmk_ok) { - printf("Could not remove %s[%ld] from " XML_CIB_TAG_STATUS ": %s", - name, id, pcmk_strerror(rc)); + rc = pcmk_legacy2rc(rc); + cib__clean_up_connection(&cib); + return rc; + } + + // Remove from configuration and status + rc = remove_from_section(cib, XML_CIB_TAG_NODE, XML_CIB_TAG_NODES, + node_name, node_id); + if (rc == pcmk_rc_ok) { + rc = remove_from_section(cib, XML_CIB_TAG_STATE, XML_CIB_TAG_STATUS, + node_name, node_id); } + // Commit the transaction + commit_rc = cib->cmds->end_transaction(cib, (rc == pcmk_rc_ok), + cib_sync_call); cib__clean_up_connection(&cib); + + if ((rc == pcmk_rc_ok) && (commit_rc == pcmk_ok)) { + crm_debug("Purged node %s (%ld) from CIB", + pcmk__s(node_name, "by ID"), node_id); + } return rc; } +/*! + * \internal + * \brief Purge a node from a single server's peer cache + * + * \param[in] server IPC server to send request to + * \param[in] node_name Name of node to purge (or NULL to leave unspecified) + * \param[in] node_id Node ID of node to purge (or 0 to leave unspecified) + * + * \note At least one of node_name and node_id must be specified. + * \return Standard Pacemaker return code + */ static int -controller_remove_node(const char *node_name, long nodeid) +purge_node_from(enum pcmk_ipc_server server, const char *node_name, + long node_id) { - pcmk_ipc_api_t *controld_api = NULL; + pcmk_ipc_api_t *api = NULL; int rc; - // Create controller IPC object - rc = pcmk_new_ipc_api(&controld_api, pcmk_ipc_controld); + rc = pcmk_new_ipc_api(&api, server); if (rc != pcmk_rc_ok) { - fprintf(stderr, "error: Could not connect to controller: %s\n", - pcmk_rc_str(rc)); - return ENOTCONN; + goto done; } - // Connect to controller (without main loop) - rc = pcmk_connect_ipc(controld_api, pcmk_ipc_dispatch_sync); + rc = pcmk__connect_ipc(api, pcmk_ipc_dispatch_sync, 5); if (rc != pcmk_rc_ok) { - fprintf(stderr, "error: Could not connect to controller: %s\n", - pcmk_rc_str(rc)); - pcmk_free_ipc_api(controld_api); - return rc; + goto done; } - rc = pcmk_ipc_purge_node(controld_api, node_name, nodeid); - if (rc != pcmk_rc_ok) { - fprintf(stderr, - "error: Could not clear node from controller's cache: %s\n", - pcmk_rc_str(rc)); + rc = pcmk_ipc_purge_node(api, node_name, node_id); +done: + if (rc != pcmk_rc_ok) { // Debug message already logged on success + g_set_error(&error, PCMK__RC_ERROR, rc, + "Could not purge node %s from %s: %s", + pcmk__s(node_name, "by ID"), pcmk_ipc_name(api, true), + pcmk_rc_str(rc)); } - - pcmk_free_ipc_api(controld_api); - return pcmk_rc_ok; + pcmk_free_ipc_api(api); + return rc; } +/*! + * \internal + * \brief Purge a node from the fencer's peer cache + * + * \param[in] node_name Name of node to purge (or NULL to leave unspecified) + * \param[in] node_id Node ID of node to purge (or 0 to leave unspecified) + * + * \note At least one of node_name and node_id must be specified. + * \return Standard Pacemaker return code + */ static int -tools_remove_node_cache(const char *node_name, long nodeid, const char *target) +purge_node_from_fencer(const char *node_name, long node_id) { - int rc = -1; + int rc = pcmk_rc_ok; crm_ipc_t *conn = NULL; xmlNode *cmd = NULL; - conn = crm_ipc_new(target, 0); - if (!conn) { - return -ENOTCONN; + conn = crm_ipc_new("stonith-ng", 0); + if (conn == NULL) { + rc = ENOTCONN; + exit_code = pcmk_rc2exitc(rc); + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Could not connect to fencer to purge node %s", + pcmk__s(node_name, "by ID")); + return rc; } - if (!crm_ipc_connect(conn)) { - crm_perror(LOG_ERR, "Connection to %s failed", target); + + rc = pcmk__connect_generic_ipc(conn); + if (rc != pcmk_rc_ok) { + exit_code = pcmk_rc2exitc(rc); + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Could not connect to fencer to purge node %s: %s", + pcmk__s(node_name, "by ID"), pcmk_rc_str(rc)); crm_ipc_destroy(conn); - return -ENOTCONN; + return rc; } - crm_trace("Removing %s[%ld] from the %s membership cache", - node_name, nodeid, target); - - if(pcmk__str_eq(target, T_ATTRD, pcmk__str_casei)) { - cmd = create_xml_node(NULL, __func__); - - crm_xml_add(cmd, F_TYPE, T_ATTRD); - crm_xml_add(cmd, F_ORIG, crm_system_name); - - crm_xml_add(cmd, PCMK__XA_TASK, PCMK__ATTRD_CMD_PEER_REMOVE); - - pcmk__xe_add_node(cmd, node_name, nodeid); - - } else { // Fencer or pacemakerd - cmd = create_request(CRM_OP_RM_NODE_CACHE, NULL, NULL, target, - crm_system_name, NULL); - if (nodeid > 0) { - crm_xml_set_id(cmd, "%ld", nodeid); - } - crm_xml_add(cmd, XML_ATTR_UNAME, node_name); + cmd = create_request(CRM_OP_RM_NODE_CACHE, NULL, NULL, "stonith-ng", + crm_system_name, NULL); + if (node_id > 0) { + crm_xml_set_id(cmd, "%ld", node_id); } + crm_xml_add(cmd, XML_ATTR_UNAME, node_name); rc = crm_ipc_send(conn, cmd, 0, 0, NULL); - crm_debug("%s peer cache cleanup for %s (%ld): %d", - target, node_name, nodeid, rc); - - if (rc > 0) { - // @TODO Should this be done just once after all the rest? - rc = cib_remove_node(nodeid, node_name); - } - - if (conn) { - crm_ipc_close(conn); - crm_ipc_destroy(conn); + if (rc >= 0) { + rc = pcmk_rc_ok; + crm_debug("Purged node %s (%ld) from fencer", + pcmk__s(node_name, "by ID"), node_id); + } else { + rc = pcmk_legacy2rc(rc); + fprintf(stderr, "Could not purge node %s from fencer: %s\n", + pcmk__s(node_name, "by ID"), pcmk_rc_str(rc)); } free_xml(cmd); - return rc > 0 ? 0 : rc; + crm_ipc_close(conn); + crm_ipc_destroy(conn); + return rc; } static void remove_node(const char *target_uname) { - int rc; - int d = 0; + int rc = pcmk_rc_ok; long nodeid = 0; const char *node_name = NULL; char *endptr = NULL; - const char *daemons[] = { - "stonith-ng", - T_ATTRD, - CRM_SYSTEM_MCP, + const enum pcmk_ipc_server servers[] = { + pcmk_ipc_controld, + pcmk_ipc_attrd, }; // Check whether node was specified by name or numeric ID @@ -485,25 +733,28 @@ remove_node(const char *target_uname) node_name = target_uname; } - rc = controller_remove_node(node_name, nodeid); + for (int i = 0; i < PCMK__NELEM(servers); ++i) { + rc = purge_node_from(servers[i], node_name, nodeid); + if (rc != pcmk_rc_ok) { + exit_code = pcmk_rc2exitc(rc); + return; + } + } + + // The fencer hasn't been converted to pcmk_ipc_api_t yet + rc = purge_node_from_fencer(node_name, nodeid); if (rc != pcmk_rc_ok) { exit_code = pcmk_rc2exitc(rc); return; } - for (d = 0; d < PCMK__NELEM(daemons); d++) { - if (tools_remove_node_cache(node_name, nodeid, daemons[d])) { - crm_err("Failed to connect to %s to remove node '%s'", - daemons[d], target_uname); - exit_code = CRM_EX_ERROR; - return; - } - } - exit_code = CRM_EX_OK; + // Lastly, purge the node from the CIB itself + rc = purge_node_from_cib(node_name, nodeid); + exit_code = pcmk_rc2exitc(rc); } static GOptionContext * -build_arg_context(pcmk__common_args_t *args, GOptionGroup *group) { +build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) { GOptionContext *context = NULL; GOptionEntry extra_prog_entries[] = { @@ -514,7 +765,7 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup *group) { { NULL } }; - context = pcmk__build_arg_context(args, NULL, &group, NULL); + context = pcmk__build_arg_context(args, "text (default), xml", group, NULL); /* Add the -q option, which cannot be part of the globally supported options * because some tools use that flag for something else. @@ -531,13 +782,14 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup *group) { int main(int argc, char **argv) { - GError *error = NULL; + int rc = pcmk_rc_ok; GOptionGroup *output_group = NULL; pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY); gchar **processed_args = pcmk__cmdline_preproc(argv, "NR"); - GOptionContext *context = build_arg_context(args, output_group); + 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; @@ -545,49 +797,72 @@ main(int argc, char **argv) pcmk__cli_init_logging("crm_node", args->verbosity); + rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv); + if (rc != pcmk_rc_ok) { + exit_code = pcmk_rc2exitc(rc); + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Error creating output format %s: %s", args->output_ty, + pcmk_rc_str(rc)); + goto done; + } + + if (!pcmk__force_args(context, &error, "%s --xml-simple-list", g_get_prgname())) { + exit_code = CRM_EX_SOFTWARE; + goto done; + } + if (args->version) { - g_strfreev(processed_args); - pcmk__free_arg_context(context); - /* FIXME: When crm_node is converted to use formatted output, this can go. */ - pcmk__cli_help('v'); + out->version(out, false); + goto done; } if (options.command == 0) { char *help = g_option_context_get_help(context, TRUE, NULL); - fprintf(stderr, "%s", help); + out->err(out, "%s", help); g_free(help); exit_code = CRM_EX_USAGE; goto done; } if (options.dangerous_cmd && options.force_flag == FALSE) { - fprintf(stderr, "The supplied command is considered dangerous." - " To prevent accidental destruction of the cluster," - " the --force flag is required in order to proceed.\n"); exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "The supplied command is considered dangerous." + " To prevent accidental destruction of the cluster," + " the --force flag is required in order to proceed."); goto done; } + pcmk__register_lib_messages(out); + pcmk__register_messages(out, fmt_functions); + switch (options.command) { - case 'n': - print_node_name(); + case 'i': + print_node_id(); break; - case 'R': - remove_node(options.target_uname); + + case 'n': + print_node_name(0); break; - case 'i': + case 'q': + print_quorum(); + break; + case 'N': - /* FIXME: Use pcmk__query_node_name() after conversion to formatted - * output - */ - run_controller_mainloop(options.nodeid, false); + print_node_name(options.nodeid); break; + + case 'R': + remove_node(options.target_uname); + break; + case 'l': case 'p': - run_controller_mainloop(0, true); + run_controller_mainloop(); break; + default: break; } @@ -596,6 +871,12 @@ done: g_strfreev(processed_args); pcmk__free_arg_context(context); - pcmk__output_and_clear_error(&error, NULL); + 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); } |