Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/include/pcmki/pcmki_cluster_queries.h b/include/pcmki/pcmki_cluster_queries.h
index 30a4bd2c6a..d08675634f 100644
--- a/include/pcmki/pcmki_cluster_queries.h
+++ b/include/pcmki/pcmki_cluster_queries.h
@@ -1,28 +1,29 @@
/*
* Copyright 2020-2022 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef PCMK__PCMKI_PCMKI_CLUSTER_QUERIES__H
# define PCMK__PCMKI_PCMKI_CLUSTER_QUERIES__H
#include <glib.h> // gboolean, GMainLoop, etc.
#include <crm/crm.h>
#include <crm/common/output_internal.h>
#include <crm/common/ipc_controld.h>
#include <crm/common/ipc_pacemakerd.h>
int pcmk__controller_status(pcmk__output_t *out, const char *node_name,
- guint message_timeout_ms);
-int pcmk__designated_controller(pcmk__output_t *out, guint message_timeout_ms);
+ unsigned int message_timeout_ms);
+int pcmk__designated_controller(pcmk__output_t *out,
+ unsigned int message_timeout_ms);
int pcmk__pacemakerd_status(pcmk__output_t *out, const char *ipc_name,
- guint message_timeout_ms,
+ unsigned int message_timeout_ms,
enum pcmk_pacemakerd_state *state);
int pcmk__list_nodes(pcmk__output_t *out, char *node_types, gboolean bash_export);
#endif
diff --git a/include/pcmki/pcmki_status.h b/include/pcmki/pcmki_status.h
index 0dde21c6cb..9f442e7aaa 100644
--- a/include/pcmki/pcmki_status.h
+++ b/include/pcmki/pcmki_status.h
@@ -1,59 +1,59 @@
/*
* Copyright 2022 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef PCMK__PCMKI_PCMKI_STATUS__H
#define PCMK__PCMKI_PCMKI_STATUS__H
#include <stdbool.h>
#include <stdint.h>
#include <crm/cib/cib_types.h>
#include <crm/pengine/pe_types.h>
#include <crm/common/output_internal.h>
#include <pcmki/pcmki_fence.h>
#ifdef __cplusplus
extern "C" {
#endif
/*!
* \internal
* \brief Print one-line status suitable for use with monitoring software
*
* \param[in] data_set Working set of CIB state
*
* \return Standard Pacemaker return code
*
* \note This function's output (and the return code when the program exits)
* should conform to https://www.monitoring-plugins.org/doc/guidelines.html
*
* \note This function is planned to be deprecated and then removed in the
* future. It should only be called from crm_mon, and no additional
* callers should be added.
*/
int pcmk__output_simple_status(pcmk__output_t *out, pe_working_set_t *data_set);
int pcmk__output_cluster_status(pcmk__output_t *out, stonith_t *stonith,
cib_t *cib, xmlNode *current_cib,
enum pcmk__fence_history fence_history,
uint32_t show, uint32_t show_opts,
const char *only_node, const char *only_rsc,
const char *neg_location_prefix,
bool simple_output);
int pcmk__status(pcmk__output_t *out, cib_t *cib,
enum pcmk__fence_history fence_history, uint32_t show,
uint32_t show_opts, const char *only_node,
const char *only_rsc, const char *neg_location_prefix,
- bool simple_output, guint timeout_ms);
+ bool simple_output, unsigned int timeout_ms);
#ifdef __cplusplus
}
#endif
#endif
diff --git a/lib/pacemaker/pcmk_cluster_queries.c b/lib/pacemaker/pcmk_cluster_queries.c
index d920100f82..73cd4f804c 100644
--- a/lib/pacemaker/pcmk_cluster_queries.c
+++ b/lib/pacemaker/pcmk_cluster_queries.c
@@ -1,666 +1,641 @@
/*
* Copyright 2020-2022 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <glib.h> // gboolean, GMainLoop, etc.
#include <libxml/tree.h> // xmlNode
#include <pacemaker.h>
#include <pacemaker-internal.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/cib/internal.h>
#include <crm/msg_xml.h>
#include <crm/common/output_internal.h>
#include <crm/common/xml.h>
#include <crm/common/xml_internal.h>
#include <crm/common/iso8601.h>
#include <crm/common/ipc_controld.h>
#include <crm/common/ipc_pacemakerd.h>
-#include <crm/common/mainloop.h>
-
-#define DEFAULT_MESSAGE_TIMEOUT_MS 30000
-
+//! Object to store API results, a timeout, and an output object
typedef struct {
pcmk__output_t *out;
- GMainLoop *mainloop;
int rc;
- guint message_timer_id;
- guint message_timeout_ms;
+ bool reply_received;
+ unsigned int message_timeout_ms;
enum pcmk_pacemakerd_state pcmkd_state;
} data_t;
-static void
-quit_main_loop(data_t *data)
-{
- if (data->mainloop != NULL) {
- GMainLoop *mloop = data->mainloop;
-
- data->mainloop = NULL; // Don't re-enter this block
- pcmk_quit_main_loop(mloop, 10);
- g_main_loop_unref(mloop);
- }
-}
-
-static gboolean
-admin_message_timeout(gpointer user_data)
-{
- data_t *data = user_data;
- pcmk__output_t *out = data->out;
-
- out->err(out, "error: No reply received from controller before timeout (%dms)",
- data->message_timeout_ms);
- data->message_timer_id = 0;
- data->rc = ETIMEDOUT;
- quit_main_loop(data);
- return FALSE; // Tells glib to remove source
-}
-
-static void
-start_main_loop(data_t *data)
-{
- if (data->message_timeout_ms < 1) {
- data->message_timeout_ms = DEFAULT_MESSAGE_TIMEOUT_MS;
- }
-
- data->rc = ECONNRESET; // For unexpected disconnects
- data->mainloop = g_main_loop_new(NULL, FALSE);
- data->message_timer_id = g_timeout_add(data->message_timeout_ms,
- admin_message_timeout,
- data);
- g_main_loop_run(data->mainloop);
-}
-
-static void
-event_done(data_t *data, pcmk_ipc_api_t *api)
-{
- pcmk_disconnect_ipc(api);
- quit_main_loop(data);
-}
-
/*!
* \internal
* \brief Process a reply from the controller IPC API
*
* \param[in,out] data API results and options
* \param[in,out] controld_api Controller connection
* \param[in] event_type Type of event that occurred
* \param[in] status Event status
* \param[in] event_data \p pcmk_controld_api_reply_t object containing
* event-specific data
*/
static pcmk_controld_api_reply_t *
controld_event_reply(data_t *data, pcmk_ipc_api_t *controld_api,
enum pcmk_ipc_event event_type, crm_exit_t status,
const void *event_data)
{
pcmk__output_t *out = data->out;
pcmk_controld_api_reply_t *reply = event_data;
switch (event_type) {
case pcmk_ipc_event_disconnect:
if (data->rc == ECONNRESET) { // Unexpected
out->err(out, "error: Lost connection to controller");
}
- event_done(data, controld_api);
return NULL;
case pcmk_ipc_event_reply:
break;
default:
return NULL;
}
- if (data->message_timer_id != 0) {
- g_source_remove(data->message_timer_id);
- data->message_timer_id = 0;
- }
-
if (status != CRM_EX_OK) {
out->err(out, "error: Bad reply from controller: %s",
crm_exit_str(status));
data->rc = EBADMSG;
- event_done(data, controld_api);
return NULL;
}
if (reply->reply_type != pcmk_controld_reply_ping) {
out->err(out, "error: Unknown reply type %d from controller",
reply->reply_type);
data->rc = EBADMSG;
- event_done(data, controld_api);
return NULL;
}
return reply;
}
/*!
* \internal
* \brief Process a controller status IPC event
*
* \param[in,out] controld_api Controller connection
* \param[in] event_type Type of event that occurred
* \param[in] status Event status
* \param[in,out] event_data \p pcmk_controld_api_reply_t object containing
* event-specific data
* \param[in,out] user_data \p data_t object for API results and options
*/
static void
controller_status_event_cb(pcmk_ipc_api_t *controld_api,
enum pcmk_ipc_event event_type, crm_exit_t status,
void *event_data, void *user_data)
{
data_t *data = (data_t *) user_data;
pcmk__output_t *out = data->out;
pcmk_controld_api_reply_t *reply = controld_event_reply(data, controld_api,
event_type, status,
event_data);
if (reply != NULL) {
out->message(out, "health",
reply->data.ping.sys_from, reply->host_from,
reply->data.ping.fsa_state, reply->data.ping.result);
data->rc = pcmk_rc_ok;
+ data->reply_received = true;
}
-
- event_done(data, controld_api);
}
/*!
* \internal
* \brief Process a designated controller IPC event
*
* \param[in,out] controld_api Controller connection
* \param[in] event_type Type of event that occurred
* \param[in] status Event status
* \param[in,out] event_data \p pcmk_controld_api_reply_t object containing
* event-specific data
* \param[in,out] user_data \p data_t object for API results and options
*/
static void
designated_controller_event_cb(pcmk_ipc_api_t *controld_api,
enum pcmk_ipc_event event_type,
crm_exit_t status, void *event_data,
void *user_data)
{
data_t *data = (data_t *) user_data;
pcmk__output_t *out = data->out;
pcmk_controld_api_reply_t *reply = controld_event_reply(data, controld_api,
event_type, status,
event_data);
if (reply != NULL) {
out->message(out, "dc", reply->host_from);
data->rc = pcmk_rc_ok;
+ data->reply_received = true;
}
-
- event_done(data, controld_api);
}
/*!
* \internal
* \brief Process a \p pacemakerd status IPC event
*
* \param[in,out] pacemakerd_api \p pacemakerd connection
* \param[in] event_type Type of event that occurred
* \param[in] status Event status
* \param[in,out] event_data \p pcmk_pacemakerd_api_reply_t object
* containing event-specific data
* \param[in,out] user_data \p data_t object for API results and options
*/
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)
{
data_t *data = user_data;
pcmk__output_t *out = data->out;
pcmk_pacemakerd_api_reply_t *reply = event_data;
switch (event_type) {
case pcmk_ipc_event_disconnect:
if (data->rc == ECONNRESET) { // Unexpected
out->err(out, "error: Lost connection to pacemakerd");
}
- event_done(data, pacemakerd_api);
return;
case pcmk_ipc_event_reply:
break;
default:
return;
}
- if (data->message_timer_id != 0) {
- g_source_remove(data->message_timer_id);
- data->message_timer_id = 0;
- }
-
if (status != CRM_EX_OK) {
out->err(out, "error: Bad reply from pacemakerd: %s",
crm_exit_str(status));
- event_done(data, pacemakerd_api);
data->rc = EBADMSG;
return;
}
if (reply->reply_type != pcmk_pacemakerd_reply_ping) {
out->err(out, "error: Unknown reply type %d from pacemakerd",
reply->reply_type);
- event_done(data, pacemakerd_api);
data->rc = EBADMSG;
return;
}
+ data->reply_received = true;
+
// Parse desired information from reply
data->pcmkd_state = reply->data.ping.state;
if (reply->data.ping.status == pcmk_rc_ok) {
crm_time_t *when = crm_time_new(NULL);
char *when_s = NULL;
crm_time_set_timet(when, &reply->data.ping.last_good);
when_s = crm_time_as_string(when,
crm_time_log_date
|crm_time_log_timeofday
|crm_time_log_with_timezone);
out->message(out, "pacemakerd-health",
reply->data.ping.sys_from, reply->data.ping.state, NULL,
when_s);
crm_time_free(when);
free(when_s);
} else {
out->message(out, "pacemakerd-health",
reply->data.ping.sys_from, reply->data.ping.state,
"query failed", NULL);
}
data->rc = pcmk_rc_ok;
- event_done(data, pacemakerd_api);
}
static pcmk_ipc_api_t *
ipc_connect(data_t *data, enum pcmk_ipc_server server, pcmk_ipc_callback_t cb,
enum pcmk_ipc_dispatch dispatch_type, bool eremoteio_ok)
{
int rc;
pcmk__output_t *out = data->out;
pcmk_ipc_api_t *api = NULL;
rc = pcmk_new_ipc_api(&api, server);
if (api == NULL) {
out->err(out, "error: Could not connect to %s: %s",
pcmk_ipc_name(api, true),
pcmk_rc_str(rc));
data->rc = rc;
return NULL;
}
if (cb != NULL) {
pcmk_register_ipc_callback(api, cb, data);
}
rc = pcmk_connect_ipc(api, dispatch_type);
if (rc != pcmk_rc_ok) {
if ((rc == EREMOTEIO) && eremoteio_ok) {
/* EREMOTEIO may be expected and acceptable for some callers.
* Preserve the return code in case callers need to handle it
* specially.
*/
} else {
out->err(out, "error: Could not connect to %s: %s",
pcmk_ipc_name(api, true), pcmk_rc_str(rc));
}
data->rc = rc;
pcmk_free_ipc_api(api);
return NULL;
}
return api;
}
+/*!
+ * \internal
+ * \brief Poll an IPC API connection until timeout or a reply is received
+ *
+ * \param[in,out] data API results and options
+ * \param[in,out] api IPC API connection
+ * \param[in] on_node If not \p NULL, name of the node to poll (used only
+ * for logging)
+ *
+ * \note Sets the \p rc member of \p data on error
+ */
+static void
+poll_until_reply(data_t *data, pcmk_ipc_api_t *api, const char *on_node)
+{
+ pcmk__output_t *out = data->out;
+
+ uint64_t start_nsec = qb_util_nano_current_get();
+ uint64_t end_nsec = start_nsec;
+ uint64_t elapsed_ms = 0;
+ uint64_t remaining_ms = data->message_timeout_ms;
+
+ while (remaining_ms > 0) {
+ int rc = pcmk_poll_ipc(api, remaining_ms);
+
+ if (rc == EAGAIN) {
+ // Poll timed out
+ break;
+ }
+
+ if (rc != pcmk_rc_ok) {
+ out->err(out, "error: Failed to poll %s API%s%s: %s",
+ pcmk_ipc_name(api, true), (on_node != NULL)? " on " : "",
+ pcmk__s(on_node, ""), pcmk_rc_str(rc));
+ data->rc = rc;
+ return;
+ }
+
+ pcmk_dispatch_ipc(api);
+
+ if (data->reply_received) {
+ return;
+ }
+ end_nsec = qb_util_nano_current_get();
+ elapsed_ms = (end_nsec - start_nsec) / QB_TIME_NS_IN_MSEC;
+ remaining_ms = data->message_timeout_ms - elapsed_ms;
+ }
+
+ out->err(out,
+ "error: Timed out after %ums waiting for reply from %s API%s%s",
+ data->message_timeout_ms, pcmk_ipc_name(api, true),
+ (on_node != NULL)? " on " : "", pcmk__s(on_node, ""));
+ data->rc = EAGAIN;
+}
+
/*!
* \internal
* \brief Get and output controller status
*
* \param[in,out] out Output object
* \param[in] node_name Name of node whose status is desired
* (\p NULL for DC)
* \param[in] message_timeout_ms How long to wait for a reply from the
* \p pacemaker-controld API. If 0,
* \p pcmk_ipc_dispatch_sync will be used.
- * Otherwise, \p pcmk_ipc_dispatch_main will
- * be used, and a new mainloop will be
- * created for this purpose (freed before
- * return).
+ * Otherwise, \p pcmk_ipc_dispatch_poll will
+ * be used.
*
* \return Standard Pacemaker return code
*/
int
pcmk__controller_status(pcmk__output_t *out, const char *node_name,
- guint message_timeout_ms)
+ unsigned int message_timeout_ms)
{
data_t data = {
.out = out,
- .mainloop = NULL,
.rc = pcmk_rc_ok,
- .message_timer_id = 0,
+ .reply_received = false,
.message_timeout_ms = message_timeout_ms,
.pcmkd_state = pcmk_pacemakerd_state_invalid,
};
- enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_main;
+ enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
pcmk_ipc_api_t *controld_api = NULL;
if (message_timeout_ms == 0) {
dispatch_type = pcmk_ipc_dispatch_sync;
}
controld_api = ipc_connect(&data, pcmk_ipc_controld,
controller_status_event_cb, dispatch_type,
false);
if (controld_api != NULL) {
int rc = pcmk_controld_api_ping(controld_api, node_name);
if (rc != pcmk_rc_ok) {
out->err(out, "error: Could not ping controller API on %s: %s",
pcmk__s(node_name, "DC"), pcmk_rc_str(rc));
data.rc = rc;
}
- if (dispatch_type == pcmk_ipc_dispatch_main) {
- start_main_loop(&data);
+ if (dispatch_type == pcmk_ipc_dispatch_poll) {
+ poll_until_reply(&data, controld_api, pcmk__s(node_name, "DC"));
}
-
pcmk_free_ipc_api(controld_api);
}
return data.rc;
}
+
// Documented in header
int
pcmk_controller_status(xmlNodePtr *xml, const char *node_name,
unsigned int message_timeout_ms)
{
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__xml_output_new(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
pcmk__register_lib_messages(out);
- rc = pcmk__controller_status(out, node_name, (guint) message_timeout_ms);
+ rc = pcmk__controller_status(out, node_name, message_timeout_ms);
pcmk__xml_output_finish(out, xml);
return rc;
}
/*!
* \internal
* \brief Get and output designated controller node name
*
* \param[in,out] out Output object
* \param[in] message_timeout_ms How long to wait for a reply from the
* \p pacemaker-controld API. If 0,
* \p pcmk_ipc_dispatch_sync will be used.
- * Otherwise, \p pcmk_ipc_dispatch_main will
- * be used, and a new mainloop will be
- * created for this purpose (freed before
- * return).
+ * Otherwise, \p pcmk_ipc_dispatch_poll will
+ * be used.
*
* \return Standard Pacemaker return code
*/
int
-pcmk__designated_controller(pcmk__output_t *out, guint message_timeout_ms)
+pcmk__designated_controller(pcmk__output_t *out,
+ unsigned int message_timeout_ms)
{
data_t data = {
.out = out,
- .mainloop = NULL,
.rc = pcmk_rc_ok,
- .message_timer_id = 0,
+ .reply_received = false,
.message_timeout_ms = message_timeout_ms,
.pcmkd_state = pcmk_pacemakerd_state_invalid,
};
- enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_main;
+ enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
pcmk_ipc_api_t *controld_api = NULL;
if (message_timeout_ms == 0) {
dispatch_type = pcmk_ipc_dispatch_sync;
}
controld_api = ipc_connect(&data, pcmk_ipc_controld,
designated_controller_event_cb, dispatch_type,
false);
if (controld_api != NULL) {
int rc = pcmk_controld_api_ping(controld_api, NULL);
if (rc != pcmk_rc_ok) {
out->err(out, "error: Could not ping controller API on DC: %s",
pcmk_rc_str(rc));
data.rc = rc;
}
- if (dispatch_type == pcmk_ipc_dispatch_main) {
- start_main_loop(&data);
+ if (dispatch_type == pcmk_ipc_dispatch_poll) {
+ poll_until_reply(&data, controld_api, "DC");
}
-
pcmk_free_ipc_api(controld_api);
}
return data.rc;
}
// Documented in header
int
pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms)
{
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__xml_output_new(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
pcmk__register_lib_messages(out);
- rc = pcmk__designated_controller(out, (guint) message_timeout_ms);
+ rc = pcmk__designated_controller(out, message_timeout_ms);
pcmk__xml_output_finish(out, xml);
return rc;
}
/*!
* \internal
* \brief Get and output \p pacemakerd status
*
* \param[in,out] out Output object
* \param[in] ipc_name IPC name for request
* \param[in] message_timeout_ms How long to wait for a reply from the
* \p pacemakerd API. If 0,
* \p pcmk_ipc_dispatch_sync will be used.
- * Otherwise, \p pcmk_ipc_dispatch_main will
- * be used, and a new mainloop will be
- * created for this purpose (freed before
- * return).
+ * Otherwise, \p pcmk_ipc_dispatch_poll will
+ * be used.
* \param[out] state Where to store the \p pacemakerd state, if
* not \p NULL
*
* \return Standard Pacemaker return code
*
* \note This function returns \p EREMOTEIO if run on a Pacemaker Remote node
* with \p pacemaker-remoted running, since \p pacemakerd is not proxied
* to remote nodes. The fencer and CIB may still be accessible, but
* \p state will be \p pcmk_pacemakerd_state_invalid.
*/
int
pcmk__pacemakerd_status(pcmk__output_t *out, const char *ipc_name,
- guint message_timeout_ms,
+ unsigned int message_timeout_ms,
enum pcmk_pacemakerd_state *state)
{
data_t data = {
.out = out,
- .mainloop = NULL,
- .rc = pcmk_rc_ipc_unresponsive,
- .message_timer_id = 0,
+ .rc = pcmk_rc_ok,
+ .reply_received = false,
.message_timeout_ms = message_timeout_ms,
.pcmkd_state = pcmk_pacemakerd_state_invalid,
};
- enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_main;
+ enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
pcmk_ipc_api_t *pacemakerd_api = NULL;
if (message_timeout_ms == 0) {
dispatch_type = pcmk_ipc_dispatch_sync;
}
pacemakerd_api = ipc_connect(&data, pcmk_ipc_pacemakerd,
pacemakerd_event_cb, dispatch_type, true);
if (pacemakerd_api != NULL) {
int rc = pcmk_pacemakerd_api_ping(pacemakerd_api, ipc_name);
if (rc != pcmk_rc_ok) {
out->err(out, "error: Could not ping launcher API: %s",
pcmk_rc_str(rc));
data.rc = rc;
}
- if (dispatch_type == pcmk_ipc_dispatch_main) {
- start_main_loop(&data);
+ if (dispatch_type == pcmk_ipc_dispatch_poll) {
+ poll_until_reply(&data, pacemakerd_api, NULL);
}
pcmk_free_ipc_api(pacemakerd_api);
}
if (state != NULL) {
*state = data.pcmkd_state;
}
return data.rc;
}
// Documented in header
int
pcmk_pacemakerd_status(xmlNodePtr *xml, const char *ipc_name,
unsigned int message_timeout_ms)
{
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__xml_output_new(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
pcmk__register_lib_messages(out);
- rc = pcmk__pacemakerd_status(out, ipc_name, (guint) message_timeout_ms,
- NULL);
+ rc = pcmk__pacemakerd_status(out, ipc_name, message_timeout_ms, NULL);
pcmk__xml_output_finish(out, xml);
return rc;
}
/* user data for looping through remote node xpath searches */
struct node_data {
pcmk__output_t *out;
int found;
const char *field; /* XML attribute to check for node name */
const char *type;
gboolean bash_export;
};
static void
remote_node_print_helper(xmlNode *result, void *user_data)
{
struct node_data *data = user_data;
pcmk__output_t *out = data->out;
const char *name = crm_element_value(result, XML_ATTR_UNAME);
const char *id = crm_element_value(result, data->field);
// node name and node id are the same for remote/guest nodes
out->message(out, "crmadmin-node", data->type,
name ? name : id,
id,
data->bash_export);
data->found++;
}
// \return Standard Pacemaker return code
int
pcmk__list_nodes(pcmk__output_t *out, char *node_types, gboolean bash_export)
{
xmlNode *xml_node = NULL;
int rc;
rc = cib__signon_query(NULL, &xml_node);
if (rc == pcmk_rc_ok) {
struct node_data data = {
.out = out,
.found = 0,
.bash_export = bash_export
};
out->begin_list(out, NULL, NULL, "nodes");
if (!pcmk__str_empty(node_types) && strstr(node_types, "all")) {
node_types = NULL;
}
if (pcmk__str_empty(node_types) || strstr(node_types, "cluster")) {
data.field = "id";
data.type = "cluster";
crm_foreach_xpath_result(xml_node, PCMK__XP_MEMBER_NODE_CONFIG,
remote_node_print_helper, &data);
}
if (pcmk__str_empty(node_types) || strstr(node_types, "guest")) {
data.field = "value";
data.type = "guest";
crm_foreach_xpath_result(xml_node, PCMK__XP_GUEST_NODE_CONFIG,
remote_node_print_helper, &data);
}
if (pcmk__str_empty(node_types) || !pcmk__strcmp(node_types, ",|^remote", pcmk__str_regex)) {
data.field = "id";
data.type = "remote";
crm_foreach_xpath_result(xml_node, PCMK__XP_REMOTE_NODE_CONFIG,
remote_node_print_helper, &data);
}
out->end_list(out);
if (data.found == 0) {
out->info(out, "No nodes configured");
}
free_xml(xml_node);
}
return rc;
}
int
pcmk_list_nodes(xmlNodePtr *xml, char *node_types)
{
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__xml_output_new(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
pcmk__register_lib_messages(out);
rc = pcmk__list_nodes(out, node_types, FALSE);
pcmk__xml_output_finish(out, xml);
return rc;
}
diff --git a/lib/pacemaker/pcmk_status.c b/lib/pacemaker/pcmk_status.c
index 605068e645..4963f09d9d 100644
--- a/lib/pacemaker/pcmk_status.c
+++ b/lib/pacemaker/pcmk_status.c
@@ -1,392 +1,392 @@
/*
* Copyright 2004-2022 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 <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <crm/cib/internal.h>
#include <crm/common/output.h>
#include <crm/common/results.h>
#include <crm/fencing/internal.h>
#include <crm/stonith-ng.h>
#include <pacemaker.h>
#include <pacemaker-internal.h>
static int
cib_connect(pcmk__output_t *out, cib_t *cib, xmlNode **current_cib)
{
int rc = pcmk_rc_ok;
CRM_CHECK(cib != NULL, return EINVAL);
if (cib->state == cib_connected_query ||
cib->state == cib_connected_command) {
return rc;
}
crm_trace("Connecting to the CIB");
rc = cib->cmds->signon(cib, crm_system_name, cib_query);
rc = pcmk_legacy2rc(rc);
if (rc != pcmk_rc_ok) {
out->err(out, "Could not connect to the CIB: %s",
pcmk_rc_str(rc));
return rc;
}
rc = cib->cmds->query(cib, NULL, current_cib,
cib_scope_local | cib_sync_call);
rc = pcmk_legacy2rc(rc);
return rc;
}
static stonith_t *
fencing_connect(void)
{
stonith_t *st = stonith_api_new();
int rc = pcmk_rc_ok;
if (st == NULL) {
return NULL;
}
rc = st->cmds->connect(st, crm_system_name, NULL);
if (rc == pcmk_rc_ok) {
return st;
} else {
stonith_api_delete(st);
return NULL;
}
}
/*!
* \internal
* \brief Output the cluster status given a fencer and CIB connection
*
* \param[in,out] out Output object
* \param[in,out] stonith Fencer connection
* \param[in,out] cib CIB connection
* \param[in] current_cib Current CIB XML
* \param[in] fence_history How much of the fencing history to output
* \param[in] show Group of \p pcmk_section_e flags
* \param[in] show_opts Group of \p pcmk_show_opt_e flags
* \param[in] only_node If a node name or tag, include only the
* matching node(s) (if any) in the output.
* If \p "*" or \p NULL, include all nodes
* in the output.
* \param[in] only_rsc If a resource ID or tag, include only the
* matching resource(s) (if any) in the
* output. If \p "*" or \p NULL, include all
* resources in the output.
* \param[in] neg_location_prefix Prefix denoting a ban in a constraint ID
* \param[in] simple_output Whether to use a simple output format.
* Note: This is for use by \p crm_mon only
* and is planned to be deprecated.
*
* \return Standard Pacemaker return code
*/
int
pcmk__output_cluster_status(pcmk__output_t *out, stonith_t *stonith, cib_t *cib,
xmlNode *current_cib, enum pcmk__fence_history fence_history,
uint32_t show, uint32_t show_opts,
const char *only_node, const char *only_rsc,
const char *neg_location_prefix, bool simple_output)
{
xmlNode *cib_copy = copy_xml(current_cib);
stonith_history_t *stonith_history = NULL;
int history_rc = 0;
pe_working_set_t *data_set = NULL;
GList *unames = NULL;
GList *resources = NULL;
int rc = pcmk_rc_ok;
if (cli_config_update(&cib_copy, NULL, FALSE) == FALSE) {
cib__clean_up_connection(&cib);
free_xml(cib_copy);
rc = pcmk_rc_schema_validation;
out->err(out, "Upgrade failed: %s", pcmk_rc_str(rc));
return rc;
}
/* get the stonith-history if there is evidence we need it */
if (fence_history != pcmk__fence_history_none) {
history_rc = pcmk__get_fencing_history(stonith, &stonith_history,
fence_history);
}
data_set = pe_new_working_set();
CRM_ASSERT(data_set != NULL);
pe__set_working_set_flags(data_set, pe_flag_no_compat);
data_set->input = cib_copy;
data_set->priv = out;
cluster_status(data_set);
/* Unpack constraints if any section will need them
* (tickets may be referenced in constraints but not granted yet,
* and bans need negative location constraints) */
if (pcmk_is_set(show, pcmk_section_bans) || pcmk_is_set(show, pcmk_section_tickets)) {
pcmk__unpack_constraints(data_set);
}
unames = pe__build_node_name_list(data_set, only_node);
resources = pe__build_rsc_list(data_set, only_rsc);
/* Always print DC if NULL. */
if (data_set->dc_node == NULL) {
show |= pcmk_section_dc;
}
if (simple_output) {
rc = pcmk__output_simple_status(out, data_set);
} else {
out->message(out, "cluster-status", data_set, pcmk_rc2exitc(history_rc),
stonith_history, fence_history, show, show_opts,
neg_location_prefix, unames, resources);
}
g_list_free_full(unames, free);
g_list_free_full(resources, free);
stonith_history_free(stonith_history);
stonith_history = NULL;
pe_free_working_set(data_set);
return rc;
}
int
pcmk_status(xmlNodePtr *xml)
{
cib_t *cib = NULL;
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
uint32_t show_opts = pcmk_show_pending | pcmk_show_inactive_rscs | pcmk_show_timing;
cib = cib_new();
if (cib == NULL) {
return pcmk_rc_cib_corrupt;
}
rc = pcmk__xml_output_new(&out, xml);
if (rc != pcmk_rc_ok) {
cib_delete(cib);
return rc;
}
pcmk__register_lib_messages(out);
pe__register_messages(out);
stonith__register_messages(out);
rc = pcmk__status(out, cib, pcmk__fence_history_full, pcmk_section_all,
show_opts, NULL, NULL, NULL, false, 0);
pcmk__xml_output_finish(out, xml);
cib_delete(cib);
return rc;
}
/*!
* \internal
* \brief Query and output the cluster status
*
* The operation is considered a success if we're able to get the \p pacemakerd
* state. If possible, we'll also try to connect to the fencer and CIB and
* output their respective status information.
*
* \param[in,out] out Output object
* \param[in,out] cib CIB connection
* \param[in] fence_history How much of the fencing history to output
* \param[in] show Group of \p pcmk_section_e flags
* \param[in] show_opts Group of \p pcmk_show_opt_e flags
* \param[in] only_node If a node name or tag, include only the
* matching node(s) (if any) in the output.
* If \p "*" or \p NULL, include all nodes
* in the output.
* \param[in] only_rsc If a resource ID or tag, include only the
* matching resource(s) (if any) in the
* output. If \p "*" or \p NULL, include all
* resources in the output.
* \param[in] neg_location_prefix Prefix denoting a ban in a constraint ID
* \param[in] simple_output Whether to use a simple output format.
* Note: This is for use by \p crm_mon only
* and is planned to be deprecated.
* \param[in] timeout_ms How long to wait for a reply from the
* \p pacemakerd API. If 0,
* \p pcmk_ipc_dispatch_sync will be used.
* If positive, \p pcmk_ipc_dispatch_main
* will be used, and a new mainloop will be
* created for this purpose (freed before
* return).
*
* \return Standard Pacemaker return code
*/
int
pcmk__status(pcmk__output_t *out, cib_t *cib,
enum pcmk__fence_history fence_history, uint32_t show,
uint32_t show_opts, const char *only_node, const char *only_rsc,
const char *neg_location_prefix, bool simple_output,
- guint timeout_ms)
+ unsigned int timeout_ms)
{
xmlNode *current_cib = NULL;
int rc = pcmk_rc_ok;
stonith_t *stonith = NULL;
enum pcmk_pacemakerd_state state = pcmk_pacemakerd_state_invalid;
if (cib == NULL) {
return ENOTCONN;
}
if ((cib->variant == cib_native)
&& (cib->state != cib_connected_query)
&& (cib->state != cib_connected_command)) {
rc = pcmk__pacemakerd_status(out, crm_system_name, timeout_ms, &state);
switch (rc) {
case pcmk_rc_ok:
switch (state) {
case pcmk_pacemakerd_state_running:
case pcmk_pacemakerd_state_shutting_down:
// CIB may still be available while shutting down
break;
default:
return rc;
}
break;
case EREMOTEIO:
/* We'll always get EREMOTEIO if we run this on a Pacemaker
* Remote node. The fencer and CIB might be available.
*/
rc = pcmk_rc_ok;
break;
default:
return rc;
}
}
if (fence_history != pcmk__fence_history_none && cib->variant == cib_native) {
stonith = fencing_connect();
}
rc = cib_connect(out, cib, &current_cib);
if (rc != pcmk_rc_ok) {
goto done;
}
rc = pcmk__output_cluster_status(out, stonith, cib, current_cib,
fence_history, show, show_opts, only_node,
only_rsc, neg_location_prefix,
simple_output);
if (rc != pcmk_rc_ok) {
out->err(out, "Error outputting status info from the fencer or CIB");
}
done:
if (stonith != NULL) {
if (stonith->state != stonith_disconnected) {
stonith->cmds->remove_notification(stonith, NULL);
stonith->cmds->disconnect(stonith);
}
stonith_api_delete(stonith);
}
if (current_cib != NULL) {
free_xml(current_cib);
}
return pcmk_rc_ok;
}
/* This is an internal-only function that is planned to be deprecated and removed.
* It should only ever be called from crm_mon.
*/
int
pcmk__output_simple_status(pcmk__output_t *out, pe_working_set_t *data_set)
{
int nodes_online = 0;
int nodes_standby = 0;
int nodes_maintenance = 0;
GString *offline_nodes = NULL;
bool no_dc = false;
bool offline = false;
bool has_warnings = false;
if (data_set->dc_node == NULL) {
has_warnings = true;
no_dc = true;
}
for (GList *iter = data_set->nodes; iter != NULL; iter = iter->next) {
pe_node_t *node = (pe_node_t *) iter->data;
if (node->details->standby && node->details->online) {
nodes_standby++;
} else if (node->details->maintenance && node->details->online) {
nodes_maintenance++;
} else if (node->details->online) {
nodes_online++;
} else {
pcmk__add_word(&offline_nodes, 1024, "offline node:");
pcmk__add_word(&offline_nodes, 0, pe__node_name(node));
has_warnings = true;
offline = true;
}
}
if (has_warnings) {
out->info(out, "CLUSTER WARN: %s%s%s",
no_dc ? "No DC" : "",
no_dc && offline ? ", " : "",
(offline? (const char *) offline_nodes->str : ""));
if (offline_nodes != NULL) {
g_string_free(offline_nodes, TRUE);
}
} else {
char *nodes_standby_s = NULL;
char *nodes_maint_s = NULL;
if (nodes_standby > 0) {
nodes_standby_s = crm_strdup_printf(", %d standby node%s", nodes_standby,
pcmk__plural_s(nodes_standby));
}
if (nodes_maintenance > 0) {
nodes_maint_s = crm_strdup_printf(", %d maintenance node%s",
nodes_maintenance,
pcmk__plural_s(nodes_maintenance));
}
out->info(out, "CLUSTER OK: %d node%s online%s%s, "
"%d resource instance%s configured",
nodes_online, pcmk__plural_s(nodes_online),
nodes_standby_s != NULL ? nodes_standby_s : "",
nodes_maint_s != NULL ? nodes_maint_s : "",
data_set->ninstances, pcmk__plural_s(data_set->ninstances));
free(nodes_standby_s);
free(nodes_maint_s);
}
if (has_warnings) {
return pcmk_rc_error;
} else {
return pcmk_rc_ok;
}
/* coverity[leaked_storage] False positive */
}
diff --git a/tools/crmadmin.c b/tools/crmadmin.c
index 269f80521e..b467a2014f 100644
--- a/tools/crmadmin.c
+++ b/tools/crmadmin.c
@@ -1,273 +1,275 @@
/*
* Copyright 2004-2022 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 users@clusterlabs.org";
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, options.timeout);
+ rc = pcmk__controller_status(out, options.optarg,
+ (unsigned int) options.timeout);
break;
case cmd_pacemakerd_health:
- rc = pcmk__pacemakerd_status(out, options.ipc_name, options.timeout,
- NULL);
+ rc = pcmk__pacemakerd_status(out, options.ipc_name,
+ (unsigned int) options.timeout, 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, options.timeout);
+ 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);
}
return crm_exit(exit_code);
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Apr 21, 7:18 PM (12 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1665485
Default Alt Text
(51 KB)

Event Timeline