summaryrefslogtreecommitdiffstats
path: root/tools/crm_node.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--tools/crm_node.c751
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);
}