Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/include/pacemaker.h b/include/pacemaker.h
index a1e76d0ff1..b2a73cd7c5 100644
--- a/include/pacemaker.h
+++ b/include/pacemaker.h
@@ -1,205 +1,246 @@
/*
* Copyright 2019 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 PACEMAKER__H
# define PACEMAKER__H
#ifdef __cplusplus
extern "C" {
#endif
-#ifdef BUILD_PUBLIC_LIBPACEMAKER
-
/**
* \file
* \brief High Level API
* \ingroup pacemaker
*/
# include <crm/stonith-ng.h>
# include <libxml/tree.h>
+/*!
+ * \brief Get controller status
+ *
+ * \param[in,out] xml The destination for the result, as an XML tree.
+ * \param[in] dest_node Destination node for request
+ * \param[in] message_timeout_ms Message timeout
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk_controller_status(xmlNodePtr *xml, char *dest_node, unsigned int message_timeout_ms);
+
+/*!
+ * \brief Get designated controller
+ *
+ * \param[in,out] xml The destination for the result, as an XML tree.
+ * \param[in] message_timeout_ms Message timeout
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms);
+
+/*!
+ * \brief Get pacemakerd status
+ *
+ * \param[in,out] xml The destination for the result, as an XML tree.
+ * \param[in] ipc_name IPC name for request
+ * \param[in] message_timeout_ms Message timeout
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk_pacemakerd_status(xmlNodePtr *xml, char *ipc_name, unsigned int message_timeout_ms);
+
+#ifdef BUILD_PUBLIC_LIBPACEMAKER
+
+/*!
+ * \brief Get nodes list
+ *
+ * \param[in,out] xml The destination for the result, as an XML tree.
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk_list_nodes(xmlNodePtr *xml);
+
/*!
* \brief Perform a STONITH action.
*
* \param[in] st A connection to the STONITH API.
* \param[in] target The node receiving the action.
* \param[in] action The action to perform.
* \param[in] name Who requested the fence action?
* \param[in] timeout How long to wait for the operation to complete (in ms).
* \param[in] tolerance If a successful action for \p target happened within
* this many ms, return 0 without performing the action
* again.
* \param[in] delay Apply a fencing delay. Value -1 means disable also any
* static/random fencing delays from pcmk_delay_base/max.
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_action(stonith_t *st, const char *target, const char *action,
const char *name, unsigned int timeout, unsigned int tolerance,
int delay);
/*!
* \brief List the fencing operations that have occurred for a specific node.
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] st A connection to the STONITH API.
* \param[in] target The node to get history for.
* \param[in] timeout How long to wait for the operation to complete (in ms).
* \param[in] quiet Suppress most output.
* \param[in] verbose Include additional output.
* \param[in] broadcast Gather fencing history from all nodes.
* \param[in] cleanup Clean up fencing history after listing.
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_history(xmlNodePtr *xml, stonith_t *st, char *target,
unsigned int timeout, bool quiet, int verbose,
bool broadcast, bool cleanup);
/*!
* \brief List all installed STONITH agents.
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] st A connection to the STONITH API.
* \param[in] timeout How long to wait for the operation to complete (in ms).
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_installed(xmlNodePtr *xml, stonith_t *st, unsigned int timeout);
/*!
* \brief When was a device last fenced?
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] target The node that was fenced.
* \param[in] as_nodeid
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_last(xmlNodePtr *xml, const char *target, bool as_nodeid);
/*!
* \brief List nodes that can be fenced.
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] st A connection to the STONITH API
* \param[in] device_id Resource ID of fence device to check
* \param[in] timeout How long to wait for the operation to complete (in ms)
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_list_targets(xmlNodePtr *xml, stonith_t *st,
const char *device_id, unsigned int timeout);
/*!
* \brief Get metadata for a resource.
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] st A connection to the STONITH API.
* \param[in] agent The fence agent to get metadata for.
* \param[in] timeout How long to wait for the operation to complete (in ms).
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_metadata(xmlNodePtr *xml, stonith_t *st, char *agent,
unsigned int timeout);
/*!
* \brief List registered fence devices.
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] st A connection to the STONITH API.
* \param[in] target If not NULL, only return devices that can fence
* this node.
* \param[in] timeout How long to wait for the operation to complete (in ms).
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_registered(xmlNodePtr *xml, stonith_t *st, char *target,
unsigned int timeout);
/*!
* \brief Register a fencing level for a specific node, node regex, or attribute.
*
* \p target can take three different forms:
* - name=value, in which case \p target is an attribute.
* - @pattern, in which case \p target is a node regex.
* - Otherwise, \p target is a node name.
*
* \param[in] st A connection to the STONITH API.
* \param[in] target The object to register a fencing level for.
* \param[in] fence_level Index number of level to add.
* \param[in] devices Devices to use in level.
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_register_level(stonith_t *st, char *target, int fence_level,
stonith_key_value_t *devices);
/*!
* \brief Unregister a fencing level for a specific node, node regex, or attribute.
*
* \p target can take three different forms:
* - name=value, in which case \p target is an attribute.
* - @pattern, in which case \p target is a node regex.
* - Otherwise, \p target is a node name.
*
* \param[in] st A connection to the STONITH API.
* \param[in] target The object to unregister a fencing level for.
* \param[in] fence_level Index number of level to remove.
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_unregister_level(stonith_t *st, char *target, int fence_level);
/*!
* \brief Validate a STONITH device configuration.
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] st A connection to the STONITH API.
* \param[in] agent The agent to validate (for example, "fence_xvm").
* \param[in] id STONITH device ID (may be NULL).
* \param[in] params STONITH device configuration parameters.
* \param[in] timeout How long to wait for the operation to complete (in ms).
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_validate(xmlNodePtr *xml, stonith_t *st, const char *agent,
const char *id, stonith_key_value_t *params,
unsigned int timeout);
#endif
#ifdef __cplusplus
}
#endif
#endif
diff --git a/lib/pacemaker/pcmk_cluster_queries.c b/lib/pacemaker/pcmk_cluster_queries.c
index 8d729ebe29..c705b7ff98 100644
--- a/lib/pacemaker/pcmk_cluster_queries.c
+++ b/lib/pacemaker/pcmk_cluster_queries.c
@@ -1,408 +1,483 @@
#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/msg_xml.h>
#include <crm/common/output_internal.h>
#include <crm/common/xml.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
typedef struct {
pcmk__output_t *out;
GMainLoop *mainloop;
int rc;
guint message_timer_id;
guint message_timeout_ms;
} 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);
}
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, 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;
}
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 = 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;
}
event_done(data, controld_api);
}
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 = 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;
}
event_done(data, controld_api);
}
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;
crm_time_t *crm_when;
char *pinged_buf = NULL;
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);
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);
return;
}
// Parse desired information from reply
crm_when = crm_time_new(NULL);
crm_time_set_timet(crm_when, &reply->data.ping.last_good);
pinged_buf = crm_time_as_string(crm_when,
crm_time_log_date | crm_time_log_timeofday |
crm_time_log_with_timezone);
out->message(out, "pacemakerd-health",
reply->data.ping.sys_from,
(reply->data.ping.status == pcmk_rc_ok)?
pcmk_pacemakerd_api_daemon_state_enum2text(
reply->data.ping.state):"query failed",
(reply->data.ping.status == pcmk_rc_ok)?pinged_buf:"");
data->rc = pcmk_rc_ok;
crm_time_free(crm_when);
free(pinged_buf);
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)
{
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, pcmk_ipc_dispatch_main);
if (rc != pcmk_rc_ok) {
out->err(out, "error: Could not connect to %s: %s",
pcmk_ipc_name(api, true),
pcmk_rc_str(rc));
data->rc = rc;
return NULL;
}
return api;
}
int
pcmk__controller_status(pcmk__output_t *out, char *dest_node, guint message_timeout_ms)
{
data_t data = {
.out = out,
.mainloop = NULL,
.rc = pcmk_rc_ok,
.message_timer_id = 0,
.message_timeout_ms = message_timeout_ms
};
pcmk_ipc_api_t *controld_api = ipc_connect(&data, pcmk_ipc_controld, controller_status_event_cb);
if (controld_api != NULL) {
int rc = pcmk_controld_api_ping(controld_api, dest_node);
if (rc != pcmk_rc_ok) {
out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
data.rc = rc;
}
start_main_loop(&data);
pcmk_free_ipc_api(controld_api);
}
return data.rc;
}
+int
+pcmk_controller_status(xmlNodePtr *xml, char *dest_node, unsigned int message_timeout_ms)
+{
+ pcmk__output_t *out = NULL;
+ int rc = pcmk_rc_ok;
+
+ rc = pcmk__out_prologue(&out, xml);
+ if (rc != pcmk_rc_ok) {
+ return rc;
+ }
+
+ pcmk__register_lib_messages(out);
+
+ rc = pcmk__controller_status(out, dest_node, (guint) message_timeout_ms);
+ pcmk__out_epilogue(out, xml, rc);
+ return rc;
+}
+
int
pcmk__designated_controller(pcmk__output_t *out, guint message_timeout_ms)
{
data_t data = {
.out = out,
.mainloop = NULL,
.rc = pcmk_rc_ok,
.message_timer_id = 0,
.message_timeout_ms = message_timeout_ms
};
pcmk_ipc_api_t *controld_api = ipc_connect(&data, pcmk_ipc_controld, designated_controller_event_cb);
if (controld_api != NULL) {
int rc = pcmk_controld_api_ping(controld_api, NULL);
if (rc != pcmk_rc_ok) {
out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
data.rc = rc;
}
start_main_loop(&data);
pcmk_free_ipc_api(controld_api);
}
return data.rc;
}
+int
+pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms)
+{
+ pcmk__output_t *out = NULL;
+ int rc = pcmk_rc_ok;
+
+ rc = pcmk__out_prologue(&out, xml);
+ if (rc != pcmk_rc_ok) {
+ return rc;
+ }
+
+ pcmk__register_lib_messages(out);
+
+ rc = pcmk__designated_controller(out, (guint) message_timeout_ms);
+ pcmk__out_epilogue(out, xml, rc);
+ return rc;
+}
+
int
pcmk__pacemakerd_status(pcmk__output_t *out, char *ipc_name, guint message_timeout_ms)
{
data_t data = {
.out = out,
.mainloop = NULL,
.rc = pcmk_rc_ok,
.message_timer_id = 0,
.message_timeout_ms = message_timeout_ms
};
pcmk_ipc_api_t *pacemakerd_api = ipc_connect(&data, pcmk_ipc_pacemakerd, pacemakerd_event_cb);
if (pacemakerd_api != NULL) {
int rc = pcmk_pacemakerd_api_ping(pacemakerd_api, ipc_name);
if (rc != pcmk_rc_ok) {
out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
data.rc = rc;
}
start_main_loop(&data);
pcmk_free_ipc_api(pacemakerd_api);
}
return data.rc;
}
+int
+pcmk_pacemakerd_status(xmlNodePtr *xml, char *ipc_name, unsigned int message_timeout_ms)
+{
+ pcmk__output_t *out = NULL;
+ int rc = pcmk_rc_ok;
+
+ rc = pcmk__out_prologue(&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);
+ pcmk__out_epilogue(out, xml, rc);
+ return rc;
+}
+
// \return Standard Pacemaker return code
int
pcmk__list_nodes(pcmk__output_t *out, gboolean BASH_EXPORT)
{
cib_t *the_cib = cib_new();
xmlNode *output = NULL;
int rc;
if (the_cib == NULL) {
return ENOMEM;
}
rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
if (rc != pcmk_ok) {
return pcmk_legacy2rc(rc);
}
rc = the_cib->cmds->query(the_cib, NULL, &output,
cib_scope_local | cib_sync_call);
if (rc == pcmk_ok) {
out->message(out, "crmadmin-node-list", output, BASH_EXPORT);
free_xml(output);
}
the_cib->cmds->signoff(the_cib);
return pcmk_legacy2rc(rc);
}
+#ifdef BUILD_PUBLIC_LIBPACEMAKER
+int
+pcmk_list_nodes(xmlNodePtr *xml)
+{
+ pcmk__output_t *out = NULL;
+ int rc = pcmk_rc_ok;
+
+ rc = pcmk__out_prologue(&out, xml);
+ if (rc != pcmk_rc_ok) {
+ return rc;
+ }
+
+ pcmk__register_lib_messages(out);
+
+ rc = pcmk__list_nodes(out, FALSE);
+ pcmk__out_epilogue(out, xml, rc);
+ return rc;
+}
+#endif
+
// remove when parameters removed from tools/crmadmin.c
int
pcmk__shutdown_controller(pcmk__output_t *out, char *dest_node)
{
data_t data = {
.out = out,
.mainloop = NULL,
.rc = pcmk_rc_ok,
};
pcmk_ipc_api_t *controld_api = ipc_connect(&data, pcmk_ipc_controld, NULL);
if (controld_api != NULL) {
int rc = pcmk_controld_api_shutdown(controld_api, dest_node);
if (rc != pcmk_rc_ok) {
out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
data.rc = rc;
}
pcmk_free_ipc_api(controld_api);
}
return data.rc;
}
int
pcmk__start_election(pcmk__output_t *out)
{
data_t data = {
.out = out,
.mainloop = NULL,
.rc = pcmk_rc_ok,
};
pcmk_ipc_api_t *controld_api = ipc_connect(&data, pcmk_ipc_controld, NULL);
if (controld_api != NULL) {
int rc = pcmk_controld_api_start_election(controld_api);
if (rc != pcmk_rc_ok) {
out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
data.rc = rc;
}
pcmk_free_ipc_api(controld_api);
}
return data.rc;
}
diff --git a/lib/pacemaker/pcmk_fence.c b/lib/pacemaker/pcmk_fence.c
index 7beedff3f1..d591379922 100644
--- a/lib/pacemaker/pcmk_fence.c
+++ b/lib/pacemaker/pcmk_fence.c
@@ -1,508 +1,522 @@
/*
* Copyright 2009-2020 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 <crm/common/mainloop.h>
#include <crm/common/results.h>
#include <crm/common/output_internal.h>
#include <crm/stonith-ng.h>
#include <crm/fencing/internal.h>
#include <glib.h>
#include <libxml/tree.h>
#include <pacemaker.h>
#include <pcmki/pcmki_output.h>
#include <pcmki/pcmki_fence.h>
static const int st_opts = st_opt_sync_call | st_opt_allow_suicide;
static GMainLoop *mainloop = NULL;
static struct {
stonith_t *st;
const char *target;
const char *action;
char *name;
unsigned int timeout;
unsigned int tolerance;
int delay;
int rc;
} async_fence_data;
static int
handle_level(stonith_t *st, char *target, int fence_level,
stonith_key_value_t *devices, bool added) {
char *node = NULL;
char *pattern = NULL;
char *name = NULL;
char *value = NULL;
int rc = pcmk_rc_ok;
if (target == NULL) {
// Not really possible, but makes static analysis happy
return EINVAL;
}
/* Determine if targeting by attribute, node name pattern or node name */
value = strchr(target, '=');
if (value != NULL) {
name = target;
*value++ = '\0';
} else if (*target == '@') {
pattern = target + 1;
} else {
node = target;
}
/* Register or unregister level as appropriate */
if (added) {
rc = st->cmds->register_level_full(st, st_opts, node, pattern,
name, value, fence_level,
devices);
} else {
rc = st->cmds->remove_level_full(st, st_opts, node, pattern,
name, value, fence_level);
}
return pcmk_legacy2rc(rc);
}
static void
notify_callback(stonith_t * st, stonith_event_t * e)
{
if (e->result != pcmk_ok) {
return;
}
if (pcmk__str_eq(async_fence_data.target, e->target, pcmk__str_casei) &&
pcmk__str_eq(async_fence_data.action, e->action, pcmk__str_casei)) {
async_fence_data.rc = e->result;
g_main_loop_quit(mainloop);
}
}
static void
fence_callback(stonith_t * stonith, stonith_callback_data_t * data)
{
async_fence_data.rc = data->rc;
g_main_loop_quit(mainloop);
}
static gboolean
async_fence_helper(gpointer user_data)
{
stonith_t *st = async_fence_data.st;
int call_id = 0;
int rc = stonith_api_connect_retry(st, async_fence_data.name, 10);
if (rc != pcmk_ok) {
fprintf(stderr, "Could not connect to fencer: %s\n", pcmk_strerror(rc));
g_main_loop_quit(mainloop);
return TRUE;
}
st->cmds->register_notification(st, T_STONITH_NOTIFY_FENCE, notify_callback);
call_id = st->cmds->fence_with_delay(st,
st_opt_allow_suicide,
async_fence_data.target,
async_fence_data.action,
async_fence_data.timeout/1000,
async_fence_data.tolerance/1000,
async_fence_data.delay);
if (call_id < 0) {
g_main_loop_quit(mainloop);
return TRUE;
}
st->cmds->register_callback(st,
call_id,
async_fence_data.timeout/1000,
st_opt_timeout_updates, NULL, "callback", fence_callback);
return TRUE;
}
int
pcmk__fence_action(stonith_t *st, const char *target, const char *action,
const char *name, unsigned int timeout, unsigned int tolerance,
int delay)
{
crm_trigger_t *trig;
async_fence_data.st = st;
async_fence_data.name = strdup(name);
async_fence_data.target = target;
async_fence_data.action = action;
async_fence_data.timeout = timeout;
async_fence_data.tolerance = tolerance;
async_fence_data.delay = delay;
async_fence_data.rc = pcmk_err_generic;
trig = mainloop_add_trigger(G_PRIORITY_HIGH, async_fence_helper, NULL);
mainloop_set_trigger(trig);
mainloop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(mainloop);
free(async_fence_data.name);
return pcmk_legacy2rc(async_fence_data.rc);
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_action(stonith_t *st, const char *target, const char *action,
const char *name, unsigned int timeout, unsigned int tolerance,
int delay)
{
return pcmk__fence_action(st, target, action, name, timeout, tolerance, delay);
}
#endif
int
pcmk__fence_history(pcmk__output_t *out, stonith_t *st, char *target,
unsigned int timeout, int verbose, bool broadcast,
bool cleanup) {
stonith_history_t *history = NULL, *hp, *latest = NULL;
int rc = pcmk_rc_ok;
int opts = 0;
if (!out->is_quiet(out)) {
if (cleanup) {
out->info(out, "cleaning up fencing-history%s%s",
target ? " for node " : "", target ? target : "");
}
if (broadcast) {
out->info(out, "gather fencing-history from all nodes");
}
}
stonith__set_call_options(opts, target, st_opts);
if (cleanup) {
stonith__set_call_options(opts, target, st_opt_cleanup);
}
if (broadcast) {
stonith__set_call_options(opts, target, st_opt_broadcast);
}
rc = st->cmds->history(st, opts,
pcmk__str_eq(target, "*", pcmk__str_none)? NULL : target,
&history, timeout/1000);
if (cleanup) {
// Cleanup doesn't return a history list
stonith_history_free(history);
return pcmk_legacy2rc(rc);
}
out->begin_list(out, "event", "events", "Fencing history");
history = stonith__sort_history(history);
for (hp = history; hp; hp = hp->next) {
if (hp->state == st_done) {
latest = hp;
}
if (out->is_quiet(out) || !verbose) {
continue;
}
out->message(out, "stonith-event", hp, 1, stonith__later_succeeded(hp, history));
out->increment_list(out);
}
if (latest) {
if (out->is_quiet(out)) {
out->info(out, "%lld", (long long) latest->completed);
} else if (!verbose) { // already printed if verbose
out->message(out, "stonith-event", latest, 0, FALSE);
out->increment_list(out);
}
}
out->end_list(out);
stonith_history_free(history);
return pcmk_legacy2rc(rc);
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_history(xmlNodePtr *xml, stonith_t *st, char *target, unsigned int timeout,
bool quiet, int verbose, bool broadcast, bool cleanup) {
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__out_prologue(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
+ stonith__register_messages(out);
+
out->quiet = quiet;
rc = pcmk__fence_history(out, st, target, timeout, verbose, broadcast, cleanup);
pcmk__out_epilogue(out, xml, rc);
return rc;
}
#endif
int
pcmk__fence_installed(pcmk__output_t *out, stonith_t *st, unsigned int timeout) {
stonith_key_value_t *devices = NULL;
int rc = pcmk_rc_ok;
rc = st->cmds->list_agents(st, st_opt_sync_call, NULL, &devices, timeout/1000);
/* list_agents returns a negative error code or a positive number of agents. */
if (rc < 0) {
return pcmk_legacy2rc(rc);
}
out->begin_list(out, "fence device", "fence devices", "Installed fence devices");
for (stonith_key_value_t *dIter = devices; dIter; dIter = dIter->next) {
out->list_item(out, "device", "%s", dIter->value);
}
out->end_list(out);
stonith_key_value_freeall(devices, 1, 1);
return pcmk_rc_ok;
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_installed(xmlNodePtr *xml, stonith_t *st, unsigned int timeout) {
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__out_prologue(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
+ stonith__register_messages(out);
+
rc = pcmk__fence_installed(out, st, timeout);
pcmk__out_epilogue(out, xml, rc);
return rc;
}
#endif
int
pcmk__fence_last(pcmk__output_t *out, const char *target, bool as_nodeid) {
time_t when = 0;
if (target == NULL) {
return pcmk_rc_ok;
}
if (as_nodeid) {
when = stonith_api_time(atol(target), NULL, FALSE);
} else {
when = stonith_api_time(0, target, FALSE);
}
return out->message(out, "last-fenced", target, when);
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_last(xmlNodePtr *xml, const char *target, bool as_nodeid) {
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__out_prologue(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
+ stonith__register_messages(out);
+
rc = pcmk__fence_last(out, target, as_nodeid);
pcmk__out_epilogue(out, xml, rc);
return rc;
}
#endif
int
pcmk__fence_list_targets(pcmk__output_t *out, stonith_t *st,
const char *device_id, unsigned int timeout) {
GList *targets = NULL;
char *lists = NULL;
int rc = pcmk_rc_ok;
rc = st->cmds->list(st, st_opts, device_id, &lists, timeout/1000);
if (rc != pcmk_rc_ok) {
return pcmk_legacy2rc(rc);
}
targets = stonith__parse_targets(lists);
out->begin_list(out, "fence target", "fence targets", "Fence Targets");
while (targets != NULL) {
out->list_item(out, NULL, "%s", (const char *) targets->data);
targets = targets->next;
}
out->end_list(out);
free(lists);
return rc;
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_list_targets(xmlNodePtr *xml, stonith_t *st, const char *device_id,
unsigned int timeout) {
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__out_prologue(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
+ stonith__register_messages(out);
+
rc = pcmk__fence_list_targets(out, st, device_id, timeout);
pcmk__out_epilogue(out, xml, rc);
return rc;
}
#endif
int
pcmk__fence_metadata(pcmk__output_t *out, stonith_t *st, char *agent,
unsigned int timeout) {
char *buffer = NULL;
int rc = st->cmds->metadata(st, st_opt_sync_call, agent, NULL, &buffer,
timeout/1000);
if (rc != pcmk_rc_ok) {
return pcmk_legacy2rc(rc);
}
out->output_xml(out, "metadata", buffer);
free(buffer);
return rc;
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_metadata(xmlNodePtr *xml, stonith_t *st, char *agent,
unsigned int timeout) {
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__out_prologue(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
+ stonith__register_messages(out);
+
rc = pcmk__fence_metadata(out, st, agent, timeout);
pcmk__out_epilogue(out, xml, rc);
return rc;
}
#endif
int
pcmk__fence_registered(pcmk__output_t *out, stonith_t *st, char *target,
unsigned int timeout) {
stonith_key_value_t *devices = NULL;
int rc = pcmk_rc_ok;
rc = st->cmds->query(st, st_opts, target, &devices, timeout/1000);
/* query returns a negative error code or a positive number of results. */
if (rc < 0) {
return pcmk_legacy2rc(rc);
}
out->begin_list(out, "fence device", "fence devices", "Registered fence devices");
for (stonith_key_value_t *dIter = devices; dIter; dIter = dIter->next) {
out->list_item(out, "device", "%s", dIter->value);
}
out->end_list(out);
stonith_key_value_freeall(devices, 1, 1);
/* Return pcmk_rc_ok here, not the number of results. Callers probably
* don't care.
*/
return pcmk_rc_ok;
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_registered(xmlNodePtr *xml, stonith_t *st, char *target,
unsigned int timeout) {
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__out_prologue(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
+ stonith__register_messages(out);
+
rc = pcmk__fence_registered(out, st, target, timeout);
pcmk__out_epilogue(out, xml, rc);
return rc;
}
#endif
int
pcmk__fence_register_level(stonith_t *st, char *target, int fence_level,
stonith_key_value_t *devices) {
return handle_level(st, target, fence_level, devices, true);
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_register_level(stonith_t *st, char *target, int fence_level,
stonith_key_value_t *devices) {
return pcmk__fence_register_level(st, target, fence_level, devices);
}
#endif
int
pcmk__fence_unregister_level(stonith_t *st, char *target, int fence_level) {
return handle_level(st, target, fence_level, NULL, false);
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_unregister_level(stonith_t *st, char *target, int fence_level) {
return pcmk__fence_unregister_level(st, target, fence_level);
}
#endif
int
pcmk__fence_validate(pcmk__output_t *out, stonith_t *st, const char *agent,
const char *id, stonith_key_value_t *params,
unsigned int timeout) {
char *output = NULL;
char *error_output = NULL;
int rc;
rc = st->cmds->validate(st, st_opt_sync_call, id, NULL, agent, params,
timeout/1000, &output, &error_output);
out->message(out, "validate", agent, id, output, error_output, rc);
return pcmk_legacy2rc(rc);
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_validate(xmlNodePtr *xml, stonith_t *st, const char *agent,
const char *id, stonith_key_value_t *params,
unsigned int timeout) {
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__out_prologue(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
+ stonith__register_messages(out);
+
rc = pcmk__fence_validate(out, st, agent, id, params, timeout);
pcmk__out_epilogue(out, xml, rc);
return rc;
}
#endif
diff --git a/lib/pacemaker/pcmk_output.c b/lib/pacemaker/pcmk_output.c
index 74a7c59326..a637031916 100644
--- a/lib/pacemaker/pcmk_output.c
+++ b/lib/pacemaker/pcmk_output.c
@@ -1,550 +1,549 @@
/*
* Copyright 2019-2020 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 <crm/common/results.h>
#include <crm/common/output_internal.h>
#include <crm/stonith-ng.h>
#include <crm/fencing/internal.h>
#include <libxml/tree.h>
#include <pacemaker-internal.h>
pcmk__supported_format_t pcmk__out_formats[] = {
PCMK__SUPPORTED_FORMAT_XML,
{ NULL, NULL, NULL }
};
int
pcmk__out_prologue(pcmk__output_t **out, xmlNodePtr *xml) {
int rc = pcmk_rc_ok;
if (*xml != NULL) {
xmlFreeNode(*xml);
}
pcmk__register_formats(NULL, pcmk__out_formats);
rc = pcmk__output_new(out, "xml", NULL, NULL);
if (rc != pcmk_rc_ok) {
return rc;
}
- stonith__register_messages(*out);
return rc;
}
void
pcmk__out_epilogue(pcmk__output_t *out, xmlNodePtr *xml, int retval) {
if (retval == pcmk_rc_ok) {
out->finish(out, 0, FALSE, (void **) xml);
}
pcmk__output_free(out);
}
PCMK__OUTPUT_ARGS("colocations-list", "pe_resource_t *", "gboolean", "gboolean")
static int colocations_list(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
gboolean dependents = va_arg(args, gboolean);
gboolean recursive = va_arg(args, gboolean);
GListPtr lpc = NULL;
GListPtr list = rsc->rsc_cons;
bool printed_header = false;
if (dependents) {
list = rsc->rsc_cons_lhs;
}
if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) {
return pcmk_rc_no_output;
}
pe__set_resource_flags(rsc, pe_rsc_allocating);
for (lpc = list; lpc != NULL; lpc = lpc->next) {
rsc_colocation_t *cons = (rsc_colocation_t *) lpc->data;
char *score = NULL;
pe_resource_t *peer = cons->rsc_rh;
if (dependents) {
peer = cons->rsc_lh;
}
if (pcmk_is_set(peer->flags, pe_rsc_allocating)) {
if (dependents == FALSE) {
if (!printed_header) {
out->begin_list(out, NULL, NULL, "Colocations");
printed_header = true;
}
out->list_item(out, NULL, "%s (id=%s - loop)", peer->id, cons->id);
}
continue;
}
if (dependents && recursive) {
if (!printed_header) {
out->begin_list(out, NULL, NULL, "Colocations");
printed_header = true;
}
out->message(out, "colocations-list", rsc, dependents, recursive);
}
if (!printed_header) {
out->begin_list(out, NULL, NULL, "Colocations");
printed_header = true;
}
score = score2char(cons->score);
if (cons->role_rh > RSC_ROLE_STARTED) {
out->list_item(out, NULL, "%s (score=%s, %s role=%s, id=%s",
peer->id, score, dependents ? "needs" : "with",
role2text(cons->role_rh), cons->id);
} else {
out->list_item(out, NULL, "%s (score=%s, id=%s",
peer->id, score, cons->id);
}
free(score);
out->message(out, "locations-list", peer);
if (!dependents && recursive) {
out->message(out, "colocations-list", rsc, dependents, recursive);
}
}
if (printed_header) {
out->end_list(out);
}
return pcmk_rc_no_output;
}
PCMK__OUTPUT_ARGS("colocations-list", "pe_resource_t *", "gboolean", "gboolean")
static int colocations_list_xml(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
gboolean dependents = va_arg(args, gboolean);
gboolean recursive = va_arg(args, gboolean);
GListPtr lpc = NULL;
GListPtr list = rsc->rsc_cons;
bool printed_header = false;
if (dependents) {
list = rsc->rsc_cons_lhs;
}
if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) {
return pcmk_rc_ok;
}
pe__set_resource_flags(rsc, pe_rsc_allocating);
for (lpc = list; lpc != NULL; lpc = lpc->next) {
rsc_colocation_t *cons = (rsc_colocation_t *) lpc->data;
pe_resource_t *peer = cons->rsc_rh;
char *score = NULL;
if (dependents) {
peer = cons->rsc_lh;
}
if (pcmk_is_set(peer->flags, pe_rsc_allocating)) {
if (dependents == FALSE) {
if (!printed_header) {
pcmk__output_xml_create_parent(out, "colocations", NULL);
printed_header = true;
}
pcmk__output_create_xml_node(out, "colocation",
"peer", peer->id,
"id", cons->id,
NULL);
}
continue;
}
if (dependents && recursive) {
if (!printed_header) {
pcmk__output_xml_create_parent(out, "colocations", NULL);
printed_header = true;
}
out->message(out, "colocations-list", rsc, dependents, recursive);
}
if (!printed_header) {
pcmk__output_xml_create_parent(out, "colocations", NULL);
printed_header = true;
}
score = score2char(cons->score);
if (cons->role_rh > RSC_ROLE_STARTED) {
pcmk__output_create_xml_node(out, "colocation",
"peer", peer->id,
"id", cons->id,
"score", score,
"dependents", dependents ? "needs" : "with",
"role", role2text(cons->role_rh),
NULL);
} else {
pcmk__output_create_xml_node(out, "colocation",
"peer", peer->id,
"id", cons->id,
"score", score,
NULL);
}
free(score);
out->message(out, "locations-list", peer);
if (!dependents && recursive) {
out->message(out, "colocations-list", rsc, dependents, recursive);
}
}
if (printed_header) {
pcmk__output_xml_pop_parent(out);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("locations-list", "pe_resource_t *")
static int locations_list(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc G_GNUC_UNUSED = va_arg(args, pe_resource_t *);
GListPtr lpc = NULL;
GListPtr list = rsc->rsc_location;
out->begin_list(out, NULL, NULL, "Locations");
for (lpc = list; lpc != NULL; lpc = lpc->next) {
pe__location_t *cons = lpc->data;
GListPtr lpc2 = NULL;
for (lpc2 = cons->node_list_rh; lpc2 != NULL; lpc2 = lpc2->next) {
pe_node_t *node = (pe_node_t *) lpc2->data;
char *score = score2char(node->weight);
out->list_item(out, NULL, "Node %s (score=%s, id=%s)",
node->details->uname, score, cons->id);
free(score);
}
}
out->end_list(out);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("locations-list", "pe_resource_t *")
static int locations_list_xml(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
GListPtr lpc = NULL;
GListPtr list = rsc->rsc_location;
pcmk__output_xml_create_parent(out, "locations", NULL);
for (lpc = list; lpc != NULL; lpc = lpc->next) {
pe__location_t *cons = lpc->data;
GListPtr lpc2 = NULL;
for (lpc2 = cons->node_list_rh; lpc2 != NULL; lpc2 = lpc2->next) {
pe_node_t *node = (pe_node_t *) lpc2->data;
char *score = score2char(node->weight);
pcmk__output_create_xml_node(out, "location",
"host", node->details->uname,
"id", cons->id,
"score", score,
NULL);
free(score);
}
}
pcmk__output_xml_pop_parent(out);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("stacks-constraints", "pe_resource_t *", "pe_working_set_t *", "gboolean")
static int
stacks_and_constraints(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc G_GNUC_UNUSED = va_arg(args, pe_resource_t *);
pe_working_set_t *data_set G_GNUC_UNUSED = va_arg(args, pe_working_set_t *);
gboolean recursive G_GNUC_UNUSED = va_arg(args, gboolean);
GListPtr lpc = NULL;
xmlNode *cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS,
data_set->input);
unpack_constraints(cib_constraints, data_set);
// Constraints apply to group/clone, not member/instance
rsc = uber_parent(rsc);
for (lpc = data_set->resources; lpc != NULL; lpc = lpc->next) {
pe_resource_t *r = (pe_resource_t *) lpc->data;
pe__clear_resource_flags(r, pe_rsc_allocating);
}
out->message(out, "colocations-list", rsc, TRUE, recursive);
out->begin_list(out, NULL, NULL, "%s", rsc->id);
out->message(out, "locations-list", rsc);
out->end_list(out);
for (lpc = data_set->resources; lpc != NULL; lpc = lpc->next) {
pe_resource_t *r = (pe_resource_t *) lpc->data;
pe__clear_resource_flags(r, pe_rsc_allocating);
}
out->message(out, "colocations-list", rsc, FALSE, recursive);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("stacks-constraints", "pe_resource_t *", "pe_working_set_t *", "gboolean")
static int
stacks_and_constraints_xml(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
gboolean recursive = va_arg(args, gboolean);
GListPtr lpc = NULL;
xmlNode *cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS,
data_set->input);
unpack_constraints(cib_constraints, data_set);
// Constraints apply to group/clone, not member/instance
rsc = uber_parent(rsc);
for (lpc = data_set->resources; lpc != NULL; lpc = lpc->next) {
pe_resource_t *r = (pe_resource_t *) lpc->data;
pe__clear_resource_flags(r, pe_rsc_allocating);
}
pcmk__output_xml_create_parent(out, "constraints", NULL);
out->message(out, "colocations-list", rsc, TRUE, recursive);
pcmk__output_xml_create_parent(out, "resource",
"id", rsc->id,
NULL);
out->message(out, "locations-list", rsc);
pcmk__output_xml_pop_parent(out);
for (lpc = data_set->resources; lpc != NULL; lpc = lpc->next) {
pe_resource_t *r = (pe_resource_t *) lpc->data;
pe__clear_resource_flags(r, pe_rsc_allocating);
}
out->message(out, "colocations-list", rsc, FALSE, recursive);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("health", "char *", "char *", "char *", "char *")
static int
health_text(pcmk__output_t *out, va_list args)
{
char *sys_from = va_arg(args, char *);
char *host_from = va_arg(args, char *);
char *fsa_state = va_arg(args, char *);
char *result = va_arg(args, char *);
if (!out->is_quiet(out)) {
out->info(out, "Status of %s@%s: %s (%s)", crm_str(sys_from),
crm_str(host_from), crm_str(fsa_state), crm_str(result));
} else if (fsa_state != NULL) {
out->info(out, "%s", fsa_state);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("health", "char *", "char *", "char *", "char *")
static int
health_xml(pcmk__output_t *out, va_list args)
{
char *sys_from = va_arg(args, char *);
char *host_from = va_arg(args, char *);
char *fsa_state = va_arg(args, char *);
char *result = va_arg(args, char *);
pcmk__output_create_xml_node(out, crm_str(sys_from),
"node_name", crm_str(host_from),
"state", crm_str(fsa_state),
"result", crm_str(result),
NULL);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("pacemakerd-health", "char *", "char *", "char *")
static int
pacemakerd_health_text(pcmk__output_t *out, va_list args)
{
char *sys_from = va_arg(args, char *);
char *state = va_arg(args, char *);
char *last_updated = va_arg(args, char *);
if (!out->is_quiet(out)) {
out->info(out, "Status of %s: '%s' %s %s", crm_str(sys_from),
crm_str(state), (!pcmk__str_empty(last_updated))?
"last updated":"", crm_str(last_updated));
} else {
out->info(out, "%s", crm_str(state));
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("pacemakerd-health", "char *", "char *", "char *")
static int
pacemakerd_health_xml(pcmk__output_t *out, va_list args)
{
char *sys_from = va_arg(args, char *);
char *state = va_arg(args, char *);
char *last_updated = va_arg(args, char *);
pcmk__output_create_xml_node(out, crm_str(sys_from),
"state", crm_str(state),
"last_updated", crm_str(last_updated),
NULL);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("dc", "char *")
static int
dc_text(pcmk__output_t *out, va_list args)
{
char *dc = va_arg(args, char *);
if (!out->is_quiet(out)) {
out->info(out, "Designated Controller is: %s", crm_str(dc));
} else if (dc != NULL) {
out->info(out, "%s", dc);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("dc", "char *")
static int
dc_xml(pcmk__output_t *out, va_list args)
{
char *dc = va_arg(args, char *);
pcmk__output_create_xml_node(out, "dc",
"node_name", crm_str(dc),
NULL);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("crmadmin-node-list", "pcmk__output_t *", "xmlNode *")
static int
crmadmin_node_list(pcmk__output_t *out, va_list args)
{
xmlNode *xml_node = va_arg(args, xmlNode *);
int found = 0;
xmlNode *node = NULL;
xmlNode *nodes = get_object_root(XML_CIB_TAG_NODES, xml_node);
gboolean BASH_EXPORT = va_arg(args, gboolean);
out->begin_list(out, NULL, NULL, "nodes");
for (node = first_named_child(nodes, XML_CIB_TAG_NODE); node != NULL;
node = crm_next_same_xml(node)) {
const char *node_type = BASH_EXPORT ? NULL :
crm_element_value(node, XML_ATTR_TYPE);
out->message(out, "crmadmin-node", node_type,
crm_str(crm_element_value(node, XML_ATTR_UNAME)),
crm_str(crm_element_value(node, XML_ATTR_ID)),
BASH_EXPORT);
found++;
}
// @TODO List Pacemaker Remote nodes that don't have a <node> entry
out->end_list(out);
if (found == 0) {
out->info(out, "No nodes configured");
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("crmadmin-node", "char *", "char *", "char *", "gboolean")
static int
crmadmin_node_text(pcmk__output_t *out, va_list args)
{
char *type = va_arg(args, char *);
char *name = va_arg(args, char *);
char *id = va_arg(args, char *);
gboolean BASH_EXPORT = va_arg(args, gboolean);
if (BASH_EXPORT) {
out->info(out, "export %s=%s", crm_str(name), crm_str(id));
} else {
out->info(out, "%s node: %s (%s)", type ? type : "member",
crm_str(name), crm_str(id));
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("crmadmin-node", "char *", "char *", "char *", "gboolean")
static int
crmadmin_node_xml(pcmk__output_t *out, va_list args)
{
char *type = va_arg(args, char *);
char *name = va_arg(args, char *);
char *id = va_arg(args, char *);
pcmk__output_create_xml_node(out, "node",
"type", type ? type : "member",
"name", crm_str(name),
"id", crm_str(id),
NULL);
return pcmk_rc_ok;
}
static pcmk__message_entry_t fmt_functions[] = {
{ "colocations-list", "default", colocations_list },
{ "colocations-list", "xml", colocations_list_xml },
{ "locations-list", "default", locations_list },
{ "locations-list", "xml", locations_list_xml },
{ "stacks-constraints", "default", stacks_and_constraints },
{ "stacks-constraints", "xml", stacks_and_constraints_xml },
{ "health", "default", health_text },
{ "health", "xml", health_xml },
{ "pacemakerd-health", "default", pacemakerd_health_text },
{ "pacemakerd-health", "xml", pacemakerd_health_xml },
{ "dc", "default", dc_text },
{ "dc", "xml", dc_xml },
{ "crmadmin-node-list", "default", crmadmin_node_list },
{ "crmadmin-node", "default", crmadmin_node_text },
{ "crmadmin-node", "xml", crmadmin_node_xml },
{ NULL, NULL, NULL }
};
void
pcmk__register_lib_messages(pcmk__output_t *out) {
pcmk__register_messages(out, fmt_functions);
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jul 8, 6:20 PM (17 h, 22 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2002589
Default Alt Text
(54 KB)

Event Timeline