Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F3687378
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
51 KB
Referenced Files
None
Subscribers
None
View Options
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, ¤t_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
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Apr 21, 7:18 PM (3 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1665485
Default Alt Text
(51 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment