Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F7988880
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
120 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/fencing/st_output.c b/lib/fencing/st_output.c
index d72636e29f..30b2ffd47a 100644
--- a/lib/fencing/st_output.c
+++ b/lib/fencing/st_output.c
@@ -1,520 +1,547 @@
/*
* Copyright 2019-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 <stdarg.h>
#include <stdint.h>
#include <crm/stonith-ng.h>
#include <crm/msg_xml.h>
#include <crm/common/iso8601.h>
#include <crm/common/util.h>
#include <crm/common/xml.h>
#include <crm/common/output.h>
#include <crm/common/output_internal.h>
#include <crm/common/xml_internal.h>
#include <crm/fencing/internal.h>
#include <crm/pengine/internal.h>
static char *
time_t_string(time_t when) {
crm_time_t *crm_when = crm_time_new(NULL);
char *buf = NULL;
crm_time_set_timet(crm_when, &when);
buf = crm_time_as_string(crm_when, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
crm_time_free(crm_when);
return buf;
}
/*!
* \internal
* \brief Return a status-friendly description of fence history entry state
*
* \param[in] history Fence history entry to describe
*
* \return One-word description of history entry state
* \note This is similar to stonith_op_state_str() except user-oriented (i.e.
* for cluster status) instead of developer-oriented (for debug logs).
*/
static const char *
state_str(stonith_history_t *history)
{
switch (history->state) {
case st_failed: return "failed";
case st_done: return "successful";
default: return "pending";
}
}
/*!
* \internal
* \brief Create a description of a fencing history entry for status displays
*
* \param[in] history Fencing history entry to describe
* \param[in] full_history Whether this is for full or condensed history
* \param[in] later_succeeded Node that a later equivalent attempt succeeded
* from, or NULL if none
*
* \return Newly created string with fencing history entry description
*
* \note The caller is responsible for freeing the return value with g_free().
* \note This is similar to stonith__event_description(), except this is used
* for history entries (stonith_history_t) in status displays rather than
* event notifications (stonith_event_t) in log messages.
*/
gchar *
stonith__history_description(stonith_history_t *history, bool full_history,
const char *later_succeeded)
{
GString *str = g_string_sized_new(256); // Generous starting size
char *retval = NULL;
char *completed_time = NULL;
if ((history->state == st_failed) || (history->state == st_done)) {
completed_time = time_t_string(history->completed);
}
g_string_printf(str, "%s of %s %s",
stonith_action_str(history->action), history->target,
state_str(history));
// For failed actions, add exit reason if available
if ((history->state == st_failed) && (history->exit_reason != NULL)) {
g_string_append_printf(str, " (%s)", history->exit_reason);
}
g_string_append(str, ": ");
// For completed actions, add delegate if available
if (((history->state == st_failed) || (history->state == st_done))
&& (history->delegate != NULL)) {
g_string_append_printf(str, "delegate=%s, ", history->delegate);
}
// Add information about originator
g_string_append_printf(str, "client=%s, origin=%s",
history->client, history->origin);
// For completed actions, add completion time
if (completed_time != NULL) {
if (full_history) {
g_string_append(str, ", completed");
} else if (history->state == st_failed) {
g_string_append(str, ", last-failed");
} else {
g_string_append(str, ", last-successful");
}
g_string_append_printf(str, "='%s'", completed_time);
}
if ((history->state == st_failed) && (later_succeeded != NULL)) {
g_string_append_printf(str, " (a later attempt from %s succeeded)",
later_succeeded);
}
retval = str->str;
g_string_free(str, FALSE);
free(completed_time);
return retval;
}
-PCMK__OUTPUT_ARGS("failed-fencing-list", "stonith_history_t *", "GList *", "uint32_t",
- "gboolean")
+PCMK__OUTPUT_ARGS("failed-fencing-list", "stonith_history_t *", "GList *",
+ "uint32_t", "uint32_t", "gboolean")
int
-stonith__failed_history(pcmk__output_t *out, va_list args) {
+stonith__failed_history(pcmk__output_t *out, va_list args)
+{
stonith_history_t *history = va_arg(args, stonith_history_t *);
GList *only_node = va_arg(args, GList *);
uint32_t section_opts = va_arg(args, uint32_t);
+ uint32_t show_opts = va_arg(args, uint32_t);
gboolean print_spacer = va_arg(args, gboolean);
int rc = pcmk_rc_no_output;
for (stonith_history_t *hp = history; hp; hp = hp->next) {
if (hp->state != st_failed) {
continue;
}
if (!pcmk__str_in_list(hp->target, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
continue;
}
PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Failed Fencing Actions");
- out->message(out, "stonith-event", hp, pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
- stonith__later_succeeded(hp, history));
+ out->message(out, "stonith-event", hp,
+ pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
+ stonith__later_succeeded(hp, history), show_opts);
out->increment_list(out);
}
PCMK__OUTPUT_LIST_FOOTER(out, rc);
return rc;
}
-PCMK__OUTPUT_ARGS("fencing-list", "stonith_history_t *", "GList *", "uint32_t", "gboolean")
+PCMK__OUTPUT_ARGS("fencing-list", "stonith_history_t *", "GList *", "uint32_t",
+ "uint32_t", "gboolean")
int
-stonith__history(pcmk__output_t *out, va_list args) {
+stonith__history(pcmk__output_t *out, va_list args)
+{
stonith_history_t *history = va_arg(args, stonith_history_t *);
GList *only_node = va_arg(args, GList *);
uint32_t section_opts = va_arg(args, uint32_t);
+ uint32_t show_opts = va_arg(args, uint32_t);
gboolean print_spacer = va_arg(args, gboolean);
int rc = pcmk_rc_no_output;
for (stonith_history_t *hp = history; hp; hp = hp->next) {
if (!pcmk__str_in_list(hp->target, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
continue;
}
if (hp->state != st_failed) {
PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Fencing History");
- out->message(out, "stonith-event", hp, pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
- stonith__later_succeeded(hp, history));
+ out->message(out, "stonith-event", hp,
+ pcmk_all_flags_set(section_opts,
+ pcmk_section_fencing_all),
+ stonith__later_succeeded(hp, history), show_opts);
out->increment_list(out);
}
}
PCMK__OUTPUT_LIST_FOOTER(out, rc);
return rc;
}
-PCMK__OUTPUT_ARGS("full-fencing-list", "crm_exit_t", "stonith_history_t *", "GList *",
- "uint32_t", "gboolean")
+PCMK__OUTPUT_ARGS("full-fencing-list", "crm_exit_t", "stonith_history_t *",
+ "GList *", "uint32_t", "uint32_t", "gboolean")
int
-stonith__full_history(pcmk__output_t *out, va_list args) {
+stonith__full_history(pcmk__output_t *out, va_list args)
+{
crm_exit_t history_rc G_GNUC_UNUSED = va_arg(args, crm_exit_t);
stonith_history_t *history = va_arg(args, stonith_history_t *);
GList *only_node = va_arg(args, GList *);
uint32_t section_opts = va_arg(args, uint32_t);
+ uint32_t show_opts = va_arg(args, uint32_t);
gboolean print_spacer = va_arg(args, gboolean);
int rc = pcmk_rc_no_output;
for (stonith_history_t *hp = history; hp; hp = hp->next) {
if (!pcmk__str_in_list(hp->target, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
continue;
}
PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Fencing History");
- out->message(out, "stonith-event", hp, pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
- stonith__later_succeeded(hp, history));
+ out->message(out, "stonith-event", hp,
+ pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
+ stonith__later_succeeded(hp, history), show_opts);
out->increment_list(out);
}
PCMK__OUTPUT_LIST_FOOTER(out, rc);
return rc;
}
-PCMK__OUTPUT_ARGS("full-fencing-list", "crm_exit_t", "stonith_history_t *", "GList *",
- "uint32_t", "gboolean")
+PCMK__OUTPUT_ARGS("full-fencing-list", "crm_exit_t", "stonith_history_t *",
+ "GList *", "uint32_t", "uint32_t", "gboolean")
static int
-full_history_xml(pcmk__output_t *out, va_list args) {
+full_history_xml(pcmk__output_t *out, va_list args)
+{
crm_exit_t history_rc = va_arg(args, crm_exit_t);
stonith_history_t *history = va_arg(args, stonith_history_t *);
GList *only_node = va_arg(args, GList *);
uint32_t section_opts = va_arg(args, uint32_t);
+ uint32_t show_opts = va_arg(args, uint32_t);
gboolean print_spacer G_GNUC_UNUSED = va_arg(args, gboolean);
int rc = pcmk_rc_no_output;
if (history_rc == 0) {
for (stonith_history_t *hp = history; hp; hp = hp->next) {
if (!pcmk__str_in_list(hp->target, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
continue;
}
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Fencing History");
- out->message(out, "stonith-event", hp, pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
- stonith__later_succeeded(hp, history));
+ out->message(out, "stonith-event", hp,
+ pcmk_all_flags_set(section_opts,
+ pcmk_section_fencing_all),
+ stonith__later_succeeded(hp, history), show_opts);
out->increment_list(out);
}
PCMK__OUTPUT_LIST_FOOTER(out, rc);
} else {
char *rc_s = pcmk__itoa(history_rc);
pcmk__output_create_xml_node(out, "fence_history",
"status", rc_s,
NULL);
free(rc_s);
rc = pcmk_rc_ok;
}
return rc;
}
PCMK__OUTPUT_ARGS("last-fenced", "const char *", "time_t")
static int
last_fenced_html(pcmk__output_t *out, va_list args) {
const char *target = va_arg(args, const char *);
time_t when = va_arg(args, time_t);
if (when) {
char *buf = crm_strdup_printf("Node %s last fenced at: %s", target, ctime(&when));
pcmk__output_create_html_node(out, "div", NULL, NULL, buf);
free(buf);
return pcmk_rc_ok;
} else {
return pcmk_rc_no_output;
}
}
PCMK__OUTPUT_ARGS("last-fenced", "const char *", "time_t")
static int
last_fenced_text(pcmk__output_t *out, va_list args) {
const char *target = va_arg(args, const char *);
time_t when = va_arg(args, time_t);
if (when) {
pcmk__indented_printf(out, "Node %s last fenced at: %s", target, ctime(&when));
} else {
pcmk__indented_printf(out, "Node %s has never been fenced\n", target);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("last-fenced", "const char *", "time_t")
static int
last_fenced_xml(pcmk__output_t *out, va_list args) {
const char *target = va_arg(args, const char *);
time_t when = va_arg(args, time_t);
if (when) {
char *buf = time_t_string(when);
pcmk__output_create_xml_node(out, "last-fenced",
"target", target,
"when", buf,
NULL);
free(buf);
return pcmk_rc_ok;
} else {
return pcmk_rc_no_output;
}
}
-PCMK__OUTPUT_ARGS("pending-fencing-list", "stonith_history_t *", "GList *", "uint32_t",
- "gboolean")
+PCMK__OUTPUT_ARGS("pending-fencing-list", "stonith_history_t *", "GList *",
+ "uint32_t", "uint32_t", "gboolean")
int
-stonith__pending_actions(pcmk__output_t *out, va_list args) {
+stonith__pending_actions(pcmk__output_t *out, va_list args)
+{
stonith_history_t *history = va_arg(args, stonith_history_t *);
GList *only_node = va_arg(args, GList *);
uint32_t section_opts = va_arg(args, uint32_t);
+ uint32_t show_opts = va_arg(args, uint32_t);
gboolean print_spacer = va_arg(args, gboolean);
int rc = pcmk_rc_no_output;
for (stonith_history_t *hp = history; hp; hp = hp->next) {
if (!pcmk__str_in_list(hp->target, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
continue;
}
/* Skip the rest of the history after we see a failed/done action */
if ((hp->state == st_failed) || (hp->state == st_done)) {
break;
}
PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Pending Fencing Actions");
- out->message(out, "stonith-event", hp, pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
- stonith__later_succeeded(hp, history));
+ out->message(out, "stonith-event", hp,
+ pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
+ stonith__later_succeeded(hp, history), show_opts);
out->increment_list(out);
}
PCMK__OUTPUT_LIST_FOOTER(out, rc);
return rc;
}
-PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "int", "const char *")
+PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "int", "const char *",
+ "uint32_t")
static int
-stonith_event_html(pcmk__output_t *out, va_list args) {
+stonith_event_html(pcmk__output_t *out, va_list args)
+{
stonith_history_t *event = va_arg(args, stonith_history_t *);
int full_history = va_arg(args, int);
const char *succeeded = va_arg(args, const char *);
+ uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t);
gchar *desc = stonith__history_description(event, full_history, succeeded);
switch(event->state) {
case st_done:
out->list_item(out, "successful-stonith-event", "%s", desc);
break;
case st_failed:
out->list_item(out, "failed-stonith-event", "%s", desc);
break;
default:
out->list_item(out, "pending-stonith-event", "%s", desc);
break;
}
g_free(desc);
return pcmk_rc_ok;
}
-PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "int", "const char *")
+PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "int", "const char *",
+ "uint32_t")
static int
-stonith_event_text(pcmk__output_t *out, va_list args) {
+stonith_event_text(pcmk__output_t *out, va_list args)
+{
stonith_history_t *event = va_arg(args, stonith_history_t *);
int full_history = va_arg(args, int);
const char *succeeded = va_arg(args, const char *);
+ uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t);
gchar *desc = stonith__history_description(event, full_history, succeeded);
pcmk__indented_printf(out, "%s\n", desc);
g_free(desc);
return pcmk_rc_ok;
}
-PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "int", "const char *")
+PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "int", "const char *",
+ "uint32_t")
static int
-stonith_event_xml(pcmk__output_t *out, va_list args) {
+stonith_event_xml(pcmk__output_t *out, va_list args)
+{
stonith_history_t *event = va_arg(args, stonith_history_t *);
int full_history G_GNUC_UNUSED = va_arg(args, int);
const char *succeeded G_GNUC_UNUSED = va_arg(args, const char *);
+ uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t);
char *buf = NULL;
xmlNodePtr node = pcmk__output_create_xml_node(out, "fence_event",
"action", event->action,
"target", event->target,
"client", event->client,
"origin", event->origin,
NULL);
switch (event->state) {
case st_failed:
pcmk__xe_set_props(node, "status", "failed",
XML_LRM_ATTR_EXIT_REASON, event->exit_reason,
NULL);
break;
case st_done:
crm_xml_add(node, "status", "success");
break;
default: {
char *state = pcmk__itoa(event->state);
pcmk__xe_set_props(node, "status", "pending",
"extended-status", state,
NULL);
free(state);
break;
}
}
if (event->delegate != NULL) {
crm_xml_add(node, "delegate", event->delegate);
}
if (event->state == st_failed || event->state == st_done) {
buf = time_t_string(event->completed);
crm_xml_add(node, "completed", buf);
free(buf);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("validate", "const char *", "const char *", "char *", "char *", "int")
static int
validate_agent_html(pcmk__output_t *out, va_list args) {
const char *agent = va_arg(args, const char *);
const char *device = va_arg(args, const char *);
char *output = va_arg(args, char *);
char *error_output = va_arg(args, char *);
int rc = va_arg(args, int);
if (device) {
char *buf = crm_strdup_printf("Validation of %s on %s %s", agent, device,
rc ? "failed" : "succeeded");
pcmk__output_create_html_node(out, "div", NULL, NULL, buf);
free(buf);
} else {
char *buf = crm_strdup_printf("Validation of %s %s", agent,
rc ? "failed" : "succeeded");
pcmk__output_create_html_node(out, "div", NULL, NULL, buf);
free(buf);
}
out->subprocess_output(out, rc, output, error_output);
return rc;
}
PCMK__OUTPUT_ARGS("validate", "const char *", "const char *", "char *", "char *", "int")
static int
validate_agent_text(pcmk__output_t *out, va_list args) {
const char *agent = va_arg(args, const char *);
const char *device = va_arg(args, const char *);
char *output = va_arg(args, char *);
char *error_output = va_arg(args, char *);
int rc = va_arg(args, int);
if (device) {
pcmk__indented_printf(out, "Validation of %s on %s %s\n", agent, device,
rc ? "failed" : "succeeded");
} else {
pcmk__indented_printf(out, "Validation of %s %s\n", agent,
rc ? "failed" : "succeeded");
}
out->subprocess_output(out, rc, output, error_output);
return rc;
}
PCMK__OUTPUT_ARGS("validate", "const char *", "const char *", "char *", "char *", "int")
static int
validate_agent_xml(pcmk__output_t *out, va_list args) {
const char *agent = va_arg(args, const char *);
const char *device = va_arg(args, const char *);
char *output = va_arg(args, char *);
char *error_output = va_arg(args, char *);
int rc = va_arg(args, int);
xmlNodePtr node = pcmk__output_create_xml_node(out, "validate",
"agent", agent,
"valid", pcmk__btoa(rc),
NULL);
if (device != NULL) {
crm_xml_add(node, "device", device);
}
pcmk__output_xml_push_parent(out, node);
out->subprocess_output(out, rc, output, error_output);
pcmk__output_xml_pop_parent(out);
return rc;
}
static pcmk__message_entry_t fmt_functions[] = {
{ "failed-fencing-list", "default", stonith__failed_history },
{ "fencing-list", "default", stonith__history },
{ "full-fencing-list", "default", stonith__full_history },
{ "full-fencing-list", "xml", full_history_xml },
{ "last-fenced", "html", last_fenced_html },
{ "last-fenced", "log", last_fenced_text },
{ "last-fenced", "text", last_fenced_text },
{ "last-fenced", "xml", last_fenced_xml },
{ "pending-fencing-list", "default", stonith__pending_actions },
{ "stonith-event", "html", stonith_event_html },
{ "stonith-event", "log", stonith_event_text },
{ "stonith-event", "text", stonith_event_text },
{ "stonith-event", "xml", stonith_event_xml },
{ "validate", "html", validate_agent_html },
{ "validate", "log", validate_agent_text },
{ "validate", "text", validate_agent_text },
{ "validate", "xml", validate_agent_xml },
{ NULL, NULL, NULL }
};
void
stonith__register_messages(pcmk__output_t *out) {
pcmk__register_messages(out, fmt_functions);
}
diff --git a/lib/pacemaker/pcmk_fence.c b/lib/pacemaker/pcmk_fence.c
index 9d7d3c5af9..35f2e45aec 100644
--- a/lib/pacemaker/pcmk_fence.c
+++ b/lib/pacemaker/pcmk_fence.c
@@ -1,600 +1,604 @@
/*
* Copyright 2009-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 <crm/common/mainloop.h>
#include <crm/common/results.h>
+#include <crm/common/output.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 <pacemaker-internal.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;
pcmk__action_result_t result;
} async_fence_data = { NULL, };
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 stonith_history_t *
reduce_fence_history(stonith_history_t *history)
{
stonith_history_t *new, *hp, *np;
if (!history) {
return history;
}
new = history;
hp = new->next;
new->next = NULL;
while (hp) {
stonith_history_t *hp_next = hp->next;
hp->next = NULL;
for (np = new; ; np = np->next) {
if ((hp->state == st_done) || (hp->state == st_failed)) {
/* action not in progress */
if (pcmk__str_eq(hp->target, np->target, pcmk__str_casei) &&
pcmk__str_eq(hp->action, np->action, pcmk__str_none) &&
(hp->state == np->state) &&
((hp->state == st_done) ||
pcmk__str_eq(hp->delegate, np->delegate, pcmk__str_casei))) {
/* purge older hp */
stonith_history_free(hp);
break;
}
}
if (!np->next) {
np->next = hp;
break;
}
}
hp = hp_next;
}
return new;
}
static void
notify_callback(stonith_t * st, stonith_event_t * e)
{
if (pcmk__str_eq(async_fence_data.target, e->target, pcmk__str_casei)
&& pcmk__str_eq(async_fence_data.action, e->action, pcmk__str_none)) {
pcmk__set_result(&async_fence_data.result,
stonith__event_exit_status(e),
stonith__event_execution_status(e),
stonith__event_exit_reason(e));
g_main_loop_quit(mainloop);
}
}
static void
fence_callback(stonith_t * stonith, stonith_callback_data_t * data)
{
pcmk__set_result(&async_fence_data.result, stonith__exit_status(data),
stonith__execution_status(data),
stonith__exit_reason(data));
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) {
g_main_loop_quit(mainloop);
pcmk__set_result(&async_fence_data.result, CRM_EX_ERROR,
PCMK_EXEC_NOT_CONNECTED, pcmk_strerror(rc));
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);
pcmk__set_result(&async_fence_data.result, CRM_EX_ERROR,
PCMK_EXEC_ERROR, pcmk_strerror(call_id));
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__request_fencing(stonith_t *st, const char *target, const char *action,
const char *name, unsigned int timeout,
unsigned int tolerance, int delay, char **reason)
{
crm_trigger_t *trig;
int rc = pcmk_rc_ok;
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;
pcmk__set_result(&async_fence_data.result, CRM_EX_ERROR, PCMK_EXEC_UNKNOWN,
NULL);
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);
if (reason != NULL) {
// Give the caller ownership of the exit reason
*reason = async_fence_data.result.exit_reason;
async_fence_data.result.exit_reason = NULL;
}
rc = stonith__result2rc(&async_fence_data.result);
pcmk__reset_result(&async_fence_data.result);
return rc;
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_request_fencing(stonith_t *st, const char *target, const char *action,
const char *name, unsigned int timeout,
unsigned int tolerance, int delay, char **reason)
{
return pcmk__request_fencing(st, target, action, name, timeout, tolerance,
delay, reason);
}
#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 (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->message(out, "stonith-event", hp, true,
+ stonith__later_succeeded(hp, history),
+ (uint32_t) pcmk_show_failed_detail);
out->increment_list(out);
}
if (latest) {
if (out->is_quiet(out)) {
pcmk__formatted_printf(out, "%lld\n", (long long) latest->completed);
} else if (!verbose) { // already printed if verbose
- out->message(out, "stonith-event", latest, 0, NULL);
+ out->message(out, "stonith-event", latest, false, NULL,
+ (uint32_t) pcmk_show_failed_detail);
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
int
pcmk__get_fencing_history(stonith_t *st, stonith_history_t **stonith_history,
enum pcmk__fence_history fence_history)
{
int rc = pcmk_rc_ok;
if (st == NULL) {
rc = ENOTCONN;
} else if (fence_history != pcmk__fence_history_none) {
rc = st->cmds->history(st, st_opt_sync_call, NULL, stonith_history, 120);
rc = pcmk_legacy2rc(rc);
if (rc != pcmk_rc_ok) {
return rc;
}
*stonith_history = stonith__sort_history(*stonith_history);
if (fence_history == pcmk__fence_history_reduced) {
*stonith_history = reduce_fence_history(*stonith_history);
}
}
return rc;
}
diff --git a/lib/pacemaker/pcmk_output.c b/lib/pacemaker/pcmk_output.c
index 9a522a3e51..bbaf5cde04 100644
--- a/lib/pacemaker/pcmk_output.c
+++ b/lib/pacemaker/pcmk_output.c
@@ -1,1922 +1,1926 @@
/*
* Copyright 2019-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 <crm/common/output.h>
#include <crm/common/results.h>
#include <crm/msg_xml.h>
#include <crm/stonith-ng.h>
#include <crm/fencing/internal.h>
#include <crm/pengine/internal.h>
#include <libxml/tree.h>
#include <pacemaker-internal.h>
#include <stdint.h>
static char *
colocations_header(pe_resource_t *rsc, pcmk__colocation_t *cons,
gboolean dependents) {
char *score = NULL;
char *retval = NULL;
score = score2char(cons->score);
if (cons->primary_role > RSC_ROLE_STARTED) {
retval = crm_strdup_printf("%s (score=%s, %s role=%s, id=%s)",
rsc->id, score, dependents ? "needs" : "with",
role2text(cons->primary_role), cons->id);
} else {
retval = crm_strdup_printf("%s (score=%s, id=%s)",
rsc->id, score, cons->id);
}
free(score);
return retval;
}
static void
colocations_xml_node(pcmk__output_t *out, pe_resource_t *rsc,
pcmk__colocation_t *cons) {
char *score = NULL;
xmlNodePtr node = NULL;
score = score2char(cons->score);
node = pcmk__output_create_xml_node(out, XML_CONS_TAG_RSC_DEPEND,
"id", cons->id,
"rsc", cons->dependent->id,
"with-rsc", cons->primary->id,
"score", score,
NULL);
if (cons->node_attribute) {
xmlSetProp(node, (pcmkXmlStr) "node-attribute", (pcmkXmlStr) cons->node_attribute);
}
if (cons->dependent_role != RSC_ROLE_UNKNOWN) {
xmlSetProp(node, (pcmkXmlStr) "rsc-role",
(pcmkXmlStr) role2text(cons->dependent_role));
}
if (cons->primary_role != RSC_ROLE_UNKNOWN) {
xmlSetProp(node, (pcmkXmlStr) "with-rsc-role",
(pcmkXmlStr) role2text(cons->primary_role));
}
free(score);
}
static int
do_locations_list_xml(pcmk__output_t *out, pe_resource_t *rsc, bool add_header)
{
GList *lpc = NULL;
GList *list = rsc->rsc_location;
int rc = pcmk_rc_no_output;
for (lpc = list; lpc != NULL; lpc = lpc->next) {
pe__location_t *cons = lpc->data;
GList *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);
if (add_header) {
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "locations");
}
pcmk__output_create_xml_node(out, XML_CONS_TAG_RSC_LOCATION,
"node", node->details->uname,
"rsc", rsc->id,
"id", cons->id,
"score", score,
NULL);
free(score);
}
}
if (add_header) {
PCMK__OUTPUT_LIST_FOOTER(out, rc);
}
return rc;
}
PCMK__OUTPUT_ARGS("rsc-action-item", "const char *", "pe_resource_t *",
"pe_node_t *", "pe_node_t *", "pe_action_t *",
"pe_action_t *")
static int
rsc_action_item(pcmk__output_t *out, va_list args)
{
const char *change = va_arg(args, const char *);
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
pe_node_t *origin = va_arg(args, pe_node_t *);
pe_node_t *destination = va_arg(args, pe_node_t *);
pe_action_t *action = va_arg(args, pe_action_t *);
pe_action_t *source = va_arg(args, pe_action_t *);
int len = 0;
char *reason = NULL;
char *details = NULL;
bool same_host = FALSE;
bool same_role = FALSE;
bool need_role = FALSE;
static int rsc_width = 5;
static int detail_width = 5;
CRM_ASSERT(action);
CRM_ASSERT(destination != NULL || origin != NULL);
if(source == NULL) {
source = action;
}
len = strlen(rsc->id);
if(len > rsc_width) {
rsc_width = len + 2;
}
if ((rsc->role > RSC_ROLE_STARTED)
|| (rsc->next_role > RSC_ROLE_UNPROMOTED)) {
need_role = TRUE;
}
if(origin != NULL && destination != NULL && origin->details == destination->details) {
same_host = TRUE;
}
if(rsc->role == rsc->next_role) {
same_role = TRUE;
}
if (need_role && (origin == NULL)) {
/* Starting and promoting a promotable clone instance */
details = crm_strdup_printf("%s -> %s %s", role2text(rsc->role), role2text(rsc->next_role), destination->details->uname);
} else if (origin == NULL) {
/* Starting a resource */
details = crm_strdup_printf("%s", destination->details->uname);
} else if (need_role && (destination == NULL)) {
/* Stopping a promotable clone instance */
details = crm_strdup_printf("%s %s", role2text(rsc->role), origin->details->uname);
} else if (destination == NULL) {
/* Stopping a resource */
details = crm_strdup_printf("%s", origin->details->uname);
} else if (need_role && same_role && same_host) {
/* Recovering, restarting or re-promoting a promotable clone instance */
details = crm_strdup_printf("%s %s", role2text(rsc->role), origin->details->uname);
} else if (same_role && same_host) {
/* Recovering or Restarting a normal resource */
details = crm_strdup_printf("%s", origin->details->uname);
} else if (need_role && same_role) {
/* Moving a promotable clone instance */
details = crm_strdup_printf("%s -> %s %s", origin->details->uname, destination->details->uname, role2text(rsc->role));
} else if (same_role) {
/* Moving a normal resource */
details = crm_strdup_printf("%s -> %s", origin->details->uname, destination->details->uname);
} else if (same_host) {
/* Promoting or demoting a promotable clone instance */
details = crm_strdup_printf("%s -> %s %s", role2text(rsc->role), role2text(rsc->next_role), origin->details->uname);
} else {
/* Moving and promoting/demoting */
details = crm_strdup_printf("%s %s -> %s %s", role2text(rsc->role), origin->details->uname, role2text(rsc->next_role), destination->details->uname);
}
len = strlen(details);
if(len > detail_width) {
detail_width = len;
}
if(source->reason && !pcmk_is_set(action->flags, pe_action_runnable)) {
reason = crm_strdup_printf("due to %s (blocked)", source->reason);
} else if(source->reason) {
reason = crm_strdup_printf("due to %s", source->reason);
} else if (!pcmk_is_set(action->flags, pe_action_runnable)) {
reason = strdup("blocked");
}
out->list_item(out, NULL, "%-8s %-*s ( %*s )%s%s", change, rsc_width,
rsc->id, detail_width, details, reason ? " " : "", reason ? reason : "");
free(details);
free(reason);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("rsc-action-item", "const char *", "pe_resource_t *",
"pe_node_t *", "pe_node_t *", "pe_action_t *",
"pe_action_t *")
static int
rsc_action_item_xml(pcmk__output_t *out, va_list args)
{
const char *change = va_arg(args, const char *);
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
pe_node_t *origin = va_arg(args, pe_node_t *);
pe_node_t *destination = va_arg(args, pe_node_t *);
pe_action_t *action = va_arg(args, pe_action_t *);
pe_action_t *source = va_arg(args, pe_action_t *);
char *change_str = NULL;
bool same_host = FALSE;
bool same_role = FALSE;
bool need_role = FALSE;
xmlNode *xml = NULL;
CRM_ASSERT(action);
CRM_ASSERT(destination != NULL || origin != NULL);
if (source == NULL) {
source = action;
}
if ((rsc->role > RSC_ROLE_STARTED)
|| (rsc->next_role > RSC_ROLE_UNPROMOTED)) {
need_role = TRUE;
}
if(origin != NULL && destination != NULL && origin->details == destination->details) {
same_host = TRUE;
}
if(rsc->role == rsc->next_role) {
same_role = TRUE;
}
change_str = g_ascii_strdown(change, -1);
xml = pcmk__output_create_xml_node(out, "rsc_action",
"action", change_str,
"resource", rsc->id,
NULL);
g_free(change_str);
if (need_role && (origin == NULL)) {
/* Starting and promoting a promotable clone instance */
pcmk__xe_set_props(xml,
"role", role2text(rsc->role),
"next-role", role2text(rsc->next_role),
"dest", destination->details->uname,
NULL);
} else if (origin == NULL) {
/* Starting a resource */
crm_xml_add(xml, "node", destination->details->uname);
} else if (need_role && (destination == NULL)) {
/* Stopping a promotable clone instance */
pcmk__xe_set_props(xml,
"role", role2text(rsc->role),
"node", origin->details->uname,
NULL);
} else if (destination == NULL) {
/* Stopping a resource */
crm_xml_add(xml, "node", origin->details->uname);
} else if (need_role && same_role && same_host) {
/* Recovering, restarting or re-promoting a promotable clone instance */
pcmk__xe_set_props(xml,
"role", role2text(rsc->role),
"source", origin->details->uname,
NULL);
} else if (same_role && same_host) {
/* Recovering or Restarting a normal resource */
crm_xml_add(xml, "source", origin->details->uname);
} else if (need_role && same_role) {
/* Moving a promotable clone instance */
pcmk__xe_set_props(xml,
"source", origin->details->uname,
"dest", destination->details->uname,
"role", role2text(rsc->role),
NULL);
} else if (same_role) {
/* Moving a normal resource */
pcmk__xe_set_props(xml,
"source", origin->details->uname,
"dest", destination->details->uname,
NULL);
} else if (same_host) {
/* Promoting or demoting a promotable clone instance */
pcmk__xe_set_props(xml,
"role", role2text(rsc->role),
"next-role", role2text(rsc->next_role),
"source", origin->details->uname,
NULL);
} else {
/* Moving and promoting/demoting */
pcmk__xe_set_props(xml,
"role", role2text(rsc->role),
"source", origin->details->uname,
"next-role", role2text(rsc->next_role),
"dest", destination->details->uname,
NULL);
}
if (source->reason && !pcmk_is_set(action->flags, pe_action_runnable)) {
pcmk__xe_set_props(xml,
"reason", source->reason,
"blocked", "true",
NULL);
} else if(source->reason) {
crm_xml_add(xml, "reason", source->reason);
} else if (!pcmk_is_set(action->flags, pe_action_runnable)) {
pcmk__xe_set_bool_attr(xml, "blocked", true);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("rsc-is-colocated-with-list", "pe_resource_t *", "gboolean")
static int
rsc_is_colocated_with_list(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
gboolean recursive = va_arg(args, gboolean);
int rc = pcmk_rc_no_output;
if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) {
return rc;
}
pe__set_resource_flags(rsc, pe_rsc_allocating);
for (GList *lpc = rsc->rsc_cons; lpc != NULL; lpc = lpc->next) {
pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
char *hdr = NULL;
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Resources %s is colocated with", rsc->id);
if (pcmk_is_set(cons->primary->flags, pe_rsc_allocating)) {
out->list_item(out, NULL, "%s (id=%s - loop)",
cons->primary->id, cons->id);
continue;
}
hdr = colocations_header(cons->primary, cons, FALSE);
out->list_item(out, NULL, "%s", hdr);
free(hdr);
/* Empty list header just for indentation of information about this resource. */
out->begin_list(out, NULL, NULL, NULL);
out->message(out, "locations-list", cons->primary);
if (recursive) {
out->message(out, "rsc-is-colocated-with-list",
cons->primary, recursive);
}
out->end_list(out);
}
PCMK__OUTPUT_LIST_FOOTER(out, rc);
return rc;
}
PCMK__OUTPUT_ARGS("rsc-is-colocated-with-list", "pe_resource_t *", "gboolean")
static int
rsc_is_colocated_with_list_xml(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
gboolean recursive = va_arg(args, gboolean);
int rc = pcmk_rc_no_output;
if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) {
return rc;
}
pe__set_resource_flags(rsc, pe_rsc_allocating);
for (GList *lpc = rsc->rsc_cons; lpc != NULL; lpc = lpc->next) {
pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
if (pcmk_is_set(cons->primary->flags, pe_rsc_allocating)) {
colocations_xml_node(out, cons->primary, cons);
continue;
}
colocations_xml_node(out, cons->primary, cons);
do_locations_list_xml(out, cons->primary, false);
if (recursive) {
out->message(out, "rsc-is-colocated-with-list",
cons->primary, recursive);
}
}
return rc;
}
PCMK__OUTPUT_ARGS("rscs-colocated-with-list", "pe_resource_t *", "gboolean")
static int
rscs_colocated_with_list(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
gboolean recursive = va_arg(args, gboolean);
int rc = pcmk_rc_no_output;
if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) {
return rc;
}
pe__set_resource_flags(rsc, pe_rsc_allocating);
for (GList *lpc = rsc->rsc_cons_lhs; lpc != NULL; lpc = lpc->next) {
pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
char *hdr = NULL;
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Resources colocated with %s", rsc->id);
if (pcmk_is_set(cons->dependent->flags, pe_rsc_allocating)) {
out->list_item(out, NULL, "%s (id=%s - loop)",
cons->dependent->id, cons->id);
continue;
}
hdr = colocations_header(cons->dependent, cons, TRUE);
out->list_item(out, NULL, "%s", hdr);
free(hdr);
/* Empty list header just for indentation of information about this resource. */
out->begin_list(out, NULL, NULL, NULL);
out->message(out, "locations-list", cons->dependent);
if (recursive) {
out->message(out, "rscs-colocated-with-list",
cons->dependent, recursive);
}
out->end_list(out);
}
PCMK__OUTPUT_LIST_FOOTER(out, rc);
return rc;
}
PCMK__OUTPUT_ARGS("rscs-colocated-with-list", "pe_resource_t *", "gboolean")
static int
rscs_colocated_with_list_xml(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
gboolean recursive = va_arg(args, gboolean);
int rc = pcmk_rc_no_output;
if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) {
return rc;
}
pe__set_resource_flags(rsc, pe_rsc_allocating);
for (GList *lpc = rsc->rsc_cons_lhs; lpc != NULL; lpc = lpc->next) {
pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
if (pcmk_is_set(cons->dependent->flags, pe_rsc_allocating)) {
colocations_xml_node(out, cons->dependent, cons);
continue;
}
colocations_xml_node(out, cons->dependent, cons);
do_locations_list_xml(out, cons->dependent, false);
if (recursive) {
out->message(out, "rscs-colocated-with-list",
cons->dependent, recursive);
}
}
return rc;
}
PCMK__OUTPUT_ARGS("locations-list", "pe_resource_t *")
static int
locations_list(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
GList *lpc = NULL;
GList *list = rsc->rsc_location;
int rc = pcmk_rc_no_output;
for (lpc = list; lpc != NULL; lpc = lpc->next) {
pe__location_t *cons = lpc->data;
GList *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_LIST_HEADER(out, FALSE, rc, "Locations");
out->list_item(out, NULL, "Node %s (score=%s, id=%s, rsc=%s)",
node->details->uname, score, cons->id, rsc->id);
free(score);
}
}
PCMK__OUTPUT_LIST_FOOTER(out, rc);
return rc;
}
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 *);
return do_locations_list_xml(out, rsc, true);
}
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 = 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);
pcmk__unpack_constraints(data_set);
// Constraints apply to group/clone, not member/instance
rsc = uber_parent(rsc);
out->message(out, "locations-list", rsc);
pe__clear_resource_flags_on_all(data_set, pe_rsc_allocating);
out->message(out, "rscs-colocated-with-list", rsc, recursive);
pe__clear_resource_flags_on_all(data_set, pe_rsc_allocating);
out->message(out, "rsc-is-colocated-with-list", rsc, 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);
pcmk__unpack_constraints(data_set);
// Constraints apply to group/clone, not member/instance
rsc = uber_parent(rsc);
pcmk__output_xml_create_parent(out, "constraints", NULL);
do_locations_list_xml(out, rsc, false);
pe__clear_resource_flags_on_all(data_set, pe_rsc_allocating);
out->message(out, "rscs-colocated-with-list", rsc, recursive);
pe__clear_resource_flags_on_all(data_set, pe_rsc_allocating);
out->message(out, "rsc-is-colocated-with-list", rsc, recursive);
pcmk__output_xml_pop_parent(out);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("health", "const char *", "const char *", "const char *", "const char *")
static int
health_text(pcmk__output_t *out, va_list args)
{
const char *sys_from G_GNUC_UNUSED = va_arg(args, const char *);
const char *host_from = va_arg(args, const char *);
const char *fsa_state = va_arg(args, const char *);
const char *result = va_arg(args, const char *);
if (!out->is_quiet(out)) {
return out->info(out, "Controller on %s in state %s: %s", crm_str(host_from),
crm_str(fsa_state), crm_str(result));
} else if (fsa_state != NULL) {
pcmk__formatted_printf(out, "%s\n", fsa_state);
return pcmk_rc_ok;
}
return pcmk_rc_no_output;
}
PCMK__OUTPUT_ARGS("health", "const char *", "const char *", "const char *", "const char *")
static int
health_xml(pcmk__output_t *out, va_list args)
{
const char *sys_from = va_arg(args, const char *);
const char *host_from = va_arg(args, const char *);
const char *fsa_state = va_arg(args, const char *);
const char *result = va_arg(args, const 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", "const char *", "const char *", "const char *")
static int
pacemakerd_health_text(pcmk__output_t *out, va_list args)
{
const char *sys_from = va_arg(args, const char *);
const char *state = va_arg(args, const char *);
const char *last_updated = va_arg(args, const char *);
if (!out->is_quiet(out)) {
return 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 {
pcmk__formatted_printf(out, "%s\n", crm_str(state));
return pcmk_rc_ok;
}
return pcmk_rc_no_output;
}
PCMK__OUTPUT_ARGS("pacemakerd-health", "const char *", "const char *", "const char *")
static int
pacemakerd_health_xml(pcmk__output_t *out, va_list args)
{
const char *sys_from = va_arg(args, const char *);
const char *state = va_arg(args, const char *);
const char *last_updated = va_arg(args, const 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("profile", "const char *", "clock_t", "clock_t")
static int
profile_default(pcmk__output_t *out, va_list args) {
const char *xml_file = va_arg(args, const char *);
clock_t start = va_arg(args, clock_t);
clock_t end = va_arg(args, clock_t);
out->list_item(out, NULL, "Testing %s ... %.2f secs", xml_file,
(end - start) / (float) CLOCKS_PER_SEC);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("profile", "const char *", "clock_t", "clock_t")
static int
profile_xml(pcmk__output_t *out, va_list args) {
const char *xml_file = va_arg(args, const char *);
clock_t start = va_arg(args, clock_t);
clock_t end = va_arg(args, clock_t);
char *duration = pcmk__ftoa((end - start) / (float) CLOCKS_PER_SEC);
pcmk__output_create_xml_node(out, "timing",
"file", xml_file,
"duration", duration,
NULL);
free(duration);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("dc", "const char *")
static int
dc_text(pcmk__output_t *out, va_list args)
{
const char *dc = va_arg(args, const char *);
if (!out->is_quiet(out)) {
return out->info(out, "Designated Controller is: %s", crm_str(dc));
} else if (dc != NULL) {
pcmk__formatted_printf(out, "%s\n", dc);
return pcmk_rc_ok;
}
return pcmk_rc_no_output;
}
PCMK__OUTPUT_ARGS("dc", "const char *")
static int
dc_xml(pcmk__output_t *out, va_list args)
{
const char *dc = va_arg(args, const char *);
pcmk__output_create_xml_node(out, "dc",
"node_name", crm_str(dc),
NULL);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("crmadmin-node", "const char *", "const char *", "const char *", "gboolean")
static int
crmadmin_node_text(pcmk__output_t *out, va_list args)
{
const char *type = va_arg(args, const char *);
const char *name = va_arg(args, const char *);
const char *id = va_arg(args, const char *);
gboolean BASH_EXPORT = va_arg(args, gboolean);
if (out->is_quiet(out)) {
pcmk__formatted_printf(out, "%s\n", crm_str(name));
return pcmk_rc_ok;
} else if (BASH_EXPORT) {
return out->info(out, "export %s=%s", crm_str(name), crm_str(id));
} else {
return out->info(out, "%s node: %s (%s)", type ? type : "cluster",
crm_str(name), crm_str(id));
}
}
PCMK__OUTPUT_ARGS("crmadmin-node", "const char *", "const char *", "const char *", "gboolean")
static int
crmadmin_node_xml(pcmk__output_t *out, va_list args)
{
const char *type = va_arg(args, const char *);
const char *name = va_arg(args, const char *);
const char *id = va_arg(args, const char *);
pcmk__output_create_xml_node(out, "node",
"type", type ? type : "cluster",
"name", crm_str(name),
"id", crm_str(id),
NULL);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("digests", "pe_resource_t *", "pe_node_t *", "const char *",
"guint", "op_digest_cache_t *")
static int
digests_text(pcmk__output_t *out, va_list args)
{
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
pe_node_t *node = va_arg(args, pe_node_t *);
const char *task = va_arg(args, const char *);
guint interval_ms = va_arg(args, guint);
op_digest_cache_t *digests = va_arg(args, op_digest_cache_t *);
char *action_desc = NULL;
const char *rsc_desc = "unknown resource";
const char *node_desc = "unknown node";
if (interval_ms != 0) {
action_desc = crm_strdup_printf("%ums-interval %s action", interval_ms,
((task == NULL)? "unknown" : task));
} else if (pcmk__str_eq(task, "monitor", pcmk__str_none)) {
action_desc = strdup("probe action");
} else {
action_desc = crm_strdup_printf("%s action",
((task == NULL)? "unknown" : task));
}
if ((rsc != NULL) && (rsc->id != NULL)) {
rsc_desc = rsc->id;
}
if ((node != NULL) && (node->details->uname != NULL)) {
node_desc = node->details->uname;
}
out->begin_list(out, NULL, NULL, "Digests for %s %s on %s",
rsc_desc, action_desc, node_desc);
free(action_desc);
if (digests == NULL) {
out->list_item(out, NULL, "none");
out->end_list(out);
return pcmk_rc_ok;
}
if (digests->digest_all_calc != NULL) {
out->list_item(out, NULL, "%s (all parameters)",
digests->digest_all_calc);
}
if (digests->digest_secure_calc != NULL) {
out->list_item(out, NULL, "%s (non-private parameters)",
digests->digest_secure_calc);
}
if (digests->digest_restart_calc != NULL) {
out->list_item(out, NULL, "%s (non-reloadable parameters)",
digests->digest_restart_calc);
}
out->end_list(out);
return pcmk_rc_ok;
}
static void
add_digest_xml(xmlNode *parent, const char *type, const char *digest,
xmlNode *digest_source)
{
if (digest != NULL) {
xmlNodePtr digest_xml = create_xml_node(parent, "digest");
crm_xml_add(digest_xml, "type", ((type == NULL)? "unspecified" : type));
crm_xml_add(digest_xml, "hash", digest);
if (digest_source != NULL) {
add_node_copy(digest_xml, digest_source);
}
}
}
PCMK__OUTPUT_ARGS("digests", "pe_resource_t *", "pe_node_t *", "const char *",
"guint", "op_digest_cache_t *")
static int
digests_xml(pcmk__output_t *out, va_list args)
{
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
pe_node_t *node = va_arg(args, pe_node_t *);
const char *task = va_arg(args, const char *);
guint interval_ms = va_arg(args, guint);
op_digest_cache_t *digests = va_arg(args, op_digest_cache_t *);
char *interval_s = crm_strdup_printf("%ums", interval_ms);
xmlNode *xml = NULL;
xml = pcmk__output_create_xml_node(out, "digests",
"resource", crm_str(rsc->id),
"node", crm_str(node->details->uname),
"task", crm_str(task),
"interval", interval_s,
NULL);
free(interval_s);
if (digests != NULL) {
add_digest_xml(xml, "all", digests->digest_all_calc,
digests->params_all);
add_digest_xml(xml, "nonprivate", digests->digest_secure_calc,
digests->params_secure);
add_digest_xml(xml, "nonreloadable", digests->digest_restart_calc,
digests->params_restart);
}
return pcmk_rc_ok;
}
#define STOP_SANITY_ASSERT(lineno) do { \
if(current && current->details->unclean) { \
/* It will be a pseudo op */ \
} else if(stop == NULL) { \
crm_err("%s:%d: No stop action exists for %s", \
__func__, lineno, rsc->id); \
CRM_ASSERT(stop != NULL); \
} else if (pcmk_is_set(stop->flags, pe_action_optional)) { \
crm_err("%s:%d: Action %s is still optional", \
__func__, lineno, stop->uuid); \
CRM_ASSERT(!pcmk_is_set(stop->flags, pe_action_optional)); \
} \
} while(0)
PCMK__OUTPUT_ARGS("rsc-action", "pe_resource_t *", "pe_node_t *", "pe_node_t *")
static int
rsc_action_default(pcmk__output_t *out, va_list args)
{
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
pe_node_t *current = va_arg(args, pe_node_t *);
pe_node_t *next = va_arg(args, pe_node_t *);
GList *possible_matches = NULL;
char *key = NULL;
int rc = pcmk_rc_no_output;
bool moving = false;
pe_node_t *start_node = NULL;
pe_action_t *start = NULL;
pe_action_t *stop = NULL;
pe_action_t *promote = NULL;
pe_action_t *demote = NULL;
if (!pcmk_is_set(rsc->flags, pe_rsc_managed)
|| (current == NULL && next == NULL)) {
pe_rsc_info(rsc, "Leave %s\t(%s%s)",
rsc->id, role2text(rsc->role),
!pcmk_is_set(rsc->flags, pe_rsc_managed)? " unmanaged" : "");
return rc;
}
moving = (current != NULL) && (next != NULL)
&& (current->details != next->details);
possible_matches = pe__resource_actions(rsc, next, RSC_START, FALSE);
if (possible_matches) {
start = possible_matches->data;
g_list_free(possible_matches);
}
if ((start == NULL) || !pcmk_is_set(start->flags, pe_action_runnable)) {
start_node = NULL;
} else {
start_node = current;
}
possible_matches = pe__resource_actions(rsc, start_node, RSC_STOP, FALSE);
if (possible_matches) {
stop = possible_matches->data;
g_list_free(possible_matches);
} else if (pcmk_is_set(rsc->flags, pe_rsc_stop_unexpected)) {
/* The resource is multiply active with multiple-active set to
* stop_unexpected, and not stopping on its current node, but it should
* be stopping elsewhere.
*/
possible_matches = pe__resource_actions(rsc, NULL, RSC_STOP, FALSE);
if (possible_matches != NULL) {
stop = possible_matches->data;
g_list_free(possible_matches);
}
}
possible_matches = pe__resource_actions(rsc, next, RSC_PROMOTE, FALSE);
if (possible_matches) {
promote = possible_matches->data;
g_list_free(possible_matches);
}
possible_matches = pe__resource_actions(rsc, next, RSC_DEMOTE, FALSE);
if (possible_matches) {
demote = possible_matches->data;
g_list_free(possible_matches);
}
if (rsc->role == rsc->next_role) {
pe_action_t *migrate_op = NULL;
CRM_CHECK(next != NULL, return rc);
possible_matches = pe__resource_actions(rsc, next, RSC_MIGRATED, FALSE);
if (possible_matches) {
migrate_op = possible_matches->data;
}
if ((migrate_op != NULL) && (current != NULL)
&& pcmk_is_set(migrate_op->flags, pe_action_runnable)) {
rc = out->message(out, "rsc-action-item", "Migrate", rsc, current,
next, start, NULL);
} else if (pcmk_is_set(rsc->flags, pe_rsc_reload)) {
rc = out->message(out, "rsc-action-item", "Reload", rsc, current,
next, start, NULL);
} else if (start == NULL || pcmk_is_set(start->flags, pe_action_optional)) {
if ((demote != NULL) && (promote != NULL)
&& !pcmk_is_set(demote->flags, pe_action_optional)
&& !pcmk_is_set(promote->flags, pe_action_optional)) {
rc = out->message(out, "rsc-action-item", "Re-promote", rsc,
current, next, promote, demote);
} else {
pe_rsc_info(rsc, "Leave %s\t(%s %s)", rsc->id,
role2text(rsc->role), next->details->uname);
}
} else if (!pcmk_is_set(start->flags, pe_action_runnable)) {
rc = out->message(out, "rsc-action-item", "Stop", rsc, current,
NULL, stop, (stop && stop->reason)? stop : start);
STOP_SANITY_ASSERT(__LINE__);
} else if (moving && current) {
rc = out->message(out, "rsc-action-item", pcmk_is_set(rsc->flags, pe_rsc_failed)? "Recover" : "Move",
rsc, current, next, stop, NULL);
} else if (pcmk_is_set(rsc->flags, pe_rsc_failed)) {
rc = out->message(out, "rsc-action-item", "Recover", rsc, current,
NULL, stop, NULL);
STOP_SANITY_ASSERT(__LINE__);
} else {
rc = out->message(out, "rsc-action-item", "Restart", rsc, current,
next, start, NULL);
/* STOP_SANITY_ASSERT(__LINE__); False positive for migrate-fail-7 */
}
g_list_free(possible_matches);
return rc;
}
if(stop
&& (rsc->next_role == RSC_ROLE_STOPPED
|| (start && !pcmk_is_set(start->flags, pe_action_runnable)))) {
GList *gIter = NULL;
key = stop_key(rsc);
for (gIter = rsc->running_on; gIter != NULL; gIter = gIter->next) {
pe_node_t *node = (pe_node_t *) gIter->data;
pe_action_t *stop_op = NULL;
possible_matches = find_actions(rsc->actions, key, node);
if (possible_matches) {
stop_op = possible_matches->data;
g_list_free(possible_matches);
}
if (stop_op && (stop_op->flags & pe_action_runnable)) {
STOP_SANITY_ASSERT(__LINE__);
}
if (out->message(out, "rsc-action-item", "Stop", rsc, node, NULL,
stop_op, (stop_op && stop_op->reason)? stop_op : start) == pcmk_rc_ok) {
rc = pcmk_rc_ok;
}
}
free(key);
} else if ((stop != NULL)
&& pcmk_all_flags_set(rsc->flags, pe_rsc_failed|pe_rsc_stop)) {
/* 'stop' may be NULL if the failure was ignored */
rc = out->message(out, "rsc-action-item", "Recover", rsc, current,
next, stop, start);
STOP_SANITY_ASSERT(__LINE__);
} else if (moving) {
rc = out->message(out, "rsc-action-item", "Move", rsc, current, next,
stop, NULL);
STOP_SANITY_ASSERT(__LINE__);
} else if (pcmk_is_set(rsc->flags, pe_rsc_reload)) {
rc = out->message(out, "rsc-action-item", "Reload", rsc, current, next,
start, NULL);
} else if (stop != NULL && !pcmk_is_set(stop->flags, pe_action_optional)) {
rc = out->message(out, "rsc-action-item", "Restart", rsc, current,
next, start, NULL);
STOP_SANITY_ASSERT(__LINE__);
} else if (rsc->role == RSC_ROLE_PROMOTED) {
CRM_LOG_ASSERT(current != NULL);
rc = out->message(out, "rsc-action-item", "Demote", rsc, current,
next, demote, NULL);
} else if (rsc->next_role == RSC_ROLE_PROMOTED) {
CRM_LOG_ASSERT(next);
rc = out->message(out, "rsc-action-item", "Promote", rsc, current,
next, promote, NULL);
} else if (rsc->role == RSC_ROLE_STOPPED && rsc->next_role > RSC_ROLE_STOPPED) {
rc = out->message(out, "rsc-action-item", "Start", rsc, current, next,
start, NULL);
}
return rc;
}
PCMK__OUTPUT_ARGS("node-action", "char *", "char *", "char *")
static int
node_action(pcmk__output_t *out, va_list args)
{
char *task = va_arg(args, char *);
char *node_name = va_arg(args, char *);
char *reason = va_arg(args, char *);
if (task == NULL) {
return pcmk_rc_no_output;
} else if (reason) {
out->list_item(out, NULL, "%s %s '%s'", task, node_name, reason);
} else {
crm_notice(" * %s %s", task, node_name);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("node-action", "char *", "char *", "char *")
static int
node_action_xml(pcmk__output_t *out, va_list args)
{
char *task = va_arg(args, char *);
char *node_name = va_arg(args, char *);
char *reason = va_arg(args, char *);
if (task == NULL) {
return pcmk_rc_no_output;
} else if (reason) {
pcmk__output_create_xml_node(out, "node_action",
"task", task,
"node", node_name,
"reason", reason,
NULL);
} else {
crm_notice(" * %s %s", task, node_name);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("inject-cluster-action", "const char *", "const char *", "xmlNodePtr")
static int
inject_cluster_action(pcmk__output_t *out, va_list args)
{
const char *node = va_arg(args, const char *);
const char *task = va_arg(args, const char *);
xmlNodePtr rsc = va_arg(args, xmlNodePtr);
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
if(rsc) {
out->list_item(out, NULL, "Cluster action: %s for %s on %s", task, ID(rsc), node);
} else {
out->list_item(out, NULL, "Cluster action: %s on %s", task, node);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("inject-cluster-action", "const char *", "const char *", "xmlNodePtr")
static int
inject_cluster_action_xml(pcmk__output_t *out, va_list args)
{
const char *node = va_arg(args, const char *);
const char *task = va_arg(args, const char *);
xmlNodePtr rsc = va_arg(args, xmlNodePtr);
xmlNodePtr xml_node = NULL;
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
xml_node = pcmk__output_create_xml_node(out, "cluster_action",
"task", task,
"node", node,
NULL);
if (rsc) {
crm_xml_add(xml_node, "id", ID(rsc));
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("inject-fencing-action", "char *", "const char *")
static int
inject_fencing_action(pcmk__output_t *out, va_list args)
{
char *target = va_arg(args, char *);
const char *op = va_arg(args, const char *);
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
out->list_item(out, NULL, "Fencing %s (%s)", target, op);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("inject-fencing-action", "char *", "const char *")
static int
inject_fencing_action_xml(pcmk__output_t *out, va_list args)
{
char *target = va_arg(args, char *);
const char *op = va_arg(args, const char *);
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
pcmk__output_create_xml_node(out, "fencing_action",
"target", target,
"op", op,
NULL);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("inject-attr", "const char *", "const char *", "xmlNodePtr")
static int
inject_attr(pcmk__output_t *out, va_list args)
{
const char *name = va_arg(args, const char *);
const char *value = va_arg(args, const char *);
xmlNodePtr cib_node = va_arg(args, xmlNodePtr);
xmlChar *node_path = NULL;
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
node_path = xmlGetNodePath(cib_node);
out->list_item(out, NULL, "Injecting attribute %s=%s into %s '%s'",
name, value, node_path, ID(cib_node));
free(node_path);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("inject-attr", "const char *", "const char *", "xmlNodePtr")
static int
inject_attr_xml(pcmk__output_t *out, va_list args)
{
const char *name = va_arg(args, const char *);
const char *value = va_arg(args, const char *);
xmlNodePtr cib_node = va_arg(args, xmlNodePtr);
xmlChar *node_path = NULL;
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
node_path = xmlGetNodePath(cib_node);
pcmk__output_create_xml_node(out, "inject_attr",
"name", name,
"value", value,
"node_path", node_path,
"cib_node", ID(cib_node),
NULL);
free(node_path);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("inject-spec", "const char *")
static int
inject_spec(pcmk__output_t *out, va_list args)
{
const char *spec = va_arg(args, const char *);
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
out->list_item(out, NULL, "Injecting %s into the configuration", spec);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("inject-spec", "const char *")
static int
inject_spec_xml(pcmk__output_t *out, va_list args)
{
const char *spec = va_arg(args, const char *);
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
pcmk__output_create_xml_node(out, "inject_spec",
"spec", spec,
NULL);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("inject-modify-config", "char *", "char *")
static int
inject_modify_config(pcmk__output_t *out, va_list args)
{
char *quorum = va_arg(args, char *);
char *watchdog = va_arg(args, char *);
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
out->begin_list(out, NULL, NULL, "Performing Requested Modifications");
if (quorum) {
out->list_item(out, NULL, "Setting quorum: %s", quorum);
}
if (watchdog) {
out->list_item(out, NULL, "Setting watchdog: %s", watchdog);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("inject-modify-config", "char *", "char *")
static int
inject_modify_config_xml(pcmk__output_t *out, va_list args)
{
char *quorum = va_arg(args, char *);
char *watchdog = va_arg(args, char *);
xmlNodePtr node = NULL;
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
node = pcmk__output_xml_create_parent(out, "modifications", NULL);
if (quorum) {
crm_xml_add(node, "quorum", quorum);
}
if (watchdog) {
crm_xml_add(node, "watchdog", watchdog);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("inject-modify-node", "const char *", "char *")
static int
inject_modify_node(pcmk__output_t *out, va_list args)
{
const char *action = va_arg(args, const char *);
char *node = va_arg(args, char *);
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
if (pcmk__str_eq(action, "Online", pcmk__str_none)) {
out->list_item(out, NULL, "Bringing node %s online", node);
return pcmk_rc_ok;
} else if (pcmk__str_eq(action, "Offline", pcmk__str_none)) {
out->list_item(out, NULL, "Taking node %s offline", node);
return pcmk_rc_ok;
} else if (pcmk__str_eq(action, "Failing", pcmk__str_none)) {
out->list_item(out, NULL, "Failing node %s", node);
return pcmk_rc_ok;
}
return pcmk_rc_no_output;
}
PCMK__OUTPUT_ARGS("inject-modify-node", "const char *", "char *")
static int
inject_modify_node_xml(pcmk__output_t *out, va_list args)
{
const char *action = va_arg(args, const char *);
char *node = va_arg(args, char *);
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
pcmk__output_create_xml_node(out, "modify_node",
"action", action,
"node", node,
NULL);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("inject-modify-ticket", "const char *", "char *")
static int
inject_modify_ticket(pcmk__output_t *out, va_list args)
{
const char *action = va_arg(args, const char *);
char *ticket = va_arg(args, char *);
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
if (pcmk__str_eq(action, "Standby", pcmk__str_none)) {
out->list_item(out, NULL, "Making ticket %s standby", ticket);
} else {
out->list_item(out, NULL, "%s ticket %s", action, ticket);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("inject-modify-ticket", "const char *", "char *")
static int
inject_modify_ticket_xml(pcmk__output_t *out, va_list args)
{
const char *action = va_arg(args, const char *);
char *ticket = va_arg(args, char *);
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
pcmk__output_create_xml_node(out, "modify_ticket",
"action", action,
"ticket", ticket,
NULL);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("inject-pseudo-action", "const char *", "const char *")
static int
inject_pseudo_action(pcmk__output_t *out, va_list args)
{
const char *node = va_arg(args, const char *);
const char *task = va_arg(args, const char *);
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
out->list_item(out, NULL, "Pseudo action: %s%s%s", task, node ? " on " : "",
node ? node : "");
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("inject-pseudo-action", "const char *", "const char *")
static int
inject_pseudo_action_xml(pcmk__output_t *out, va_list args)
{
const char *node = va_arg(args, const char *);
const char *task = va_arg(args, const char *);
xmlNodePtr xml_node = NULL;
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
xml_node = pcmk__output_create_xml_node(out, "pseudo_action",
"task", task,
NULL);
if (node) {
crm_xml_add(xml_node, "node", node);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("inject-rsc-action", "const char *", "const char *", "char *", "guint")
static int
inject_rsc_action(pcmk__output_t *out, va_list args)
{
const char *rsc = va_arg(args, const char *);
const char *operation = va_arg(args, const char *);
char *node = va_arg(args, char *);
guint interval_ms = va_arg(args, guint);
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
if (interval_ms) {
out->list_item(out, NULL, "Resource action: %-15s %s=%u on %s",
rsc, operation, interval_ms, node);
} else {
out->list_item(out, NULL, "Resource action: %-15s %s on %s",
rsc, operation, node);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("inject-rsc-action", "const char *", "const char *", "char *", "guint")
static int
inject_rsc_action_xml(pcmk__output_t *out, va_list args)
{
const char *rsc = va_arg(args, const char *);
const char *operation = va_arg(args, const char *);
char *node = va_arg(args, char *);
guint interval_ms = va_arg(args, guint);
xmlNodePtr xml_node = NULL;
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
xml_node = pcmk__output_create_xml_node(out, "rsc_action",
"resource", rsc,
"op", operation,
"node", node,
NULL);
if (interval_ms) {
char *interval_s = pcmk__itoa(interval_ms);
crm_xml_add(xml_node, "interval", interval_s);
free(interval_s);
}
return pcmk_rc_ok;
}
#define CHECK_RC(retcode, retval) \
if (retval == pcmk_rc_ok) { \
retcode = pcmk_rc_ok; \
}
PCMK__OUTPUT_ARGS("cluster-status", "pe_working_set_t *", "crm_exit_t", "stonith_history_t *",
"gboolean", "uint32_t", "uint32_t", "const char *", "GList *", "GList *")
int
pcmk__cluster_status_text(pcmk__output_t *out, va_list args)
{
pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
crm_exit_t history_rc = va_arg(args, crm_exit_t);
stonith_history_t *stonith_history = va_arg(args, stonith_history_t *);
gboolean fence_history = va_arg(args, gboolean);
uint32_t section_opts = va_arg(args, uint32_t);
uint32_t show_opts = va_arg(args, uint32_t);
const char *prefix = va_arg(args, const char *);
GList *unames = va_arg(args, GList *);
GList *resources = va_arg(args, GList *);
int rc = pcmk_rc_no_output;
bool already_printed_failure = false;
CHECK_RC(rc, out->message(out, "cluster-summary", data_set,
section_opts, show_opts));
if (pcmk_is_set(section_opts, pcmk_section_nodes) && unames) {
CHECK_RC(rc, out->message(out, "node-list", data_set->nodes, unames,
resources, show_opts, rc == pcmk_rc_ok));
}
/* Print resources section, if needed */
if (pcmk_is_set(section_opts, pcmk_section_resources)) {
CHECK_RC(rc, out->message(out, "resource-list", data_set, show_opts,
TRUE, unames, resources, rc == pcmk_rc_ok));
}
/* print Node Attributes section if requested */
if (pcmk_is_set(section_opts, pcmk_section_attributes)) {
CHECK_RC(rc, out->message(out, "node-attribute-list", data_set,
show_opts, rc == pcmk_rc_ok, unames, resources));
}
/* If requested, print resource operations (which includes failcounts)
* or just failcounts
*/
if (pcmk_any_flags_set(section_opts, pcmk_section_operations | pcmk_section_failcounts)) {
CHECK_RC(rc, out->message(out, "node-summary", data_set, unames,
resources, section_opts, show_opts, rc == pcmk_rc_ok));
}
/* If there were any failed actions, print them */
if (pcmk_is_set(section_opts, pcmk_section_failures)
&& xml_has_children(data_set->failed)) {
CHECK_RC(rc, out->message(out, "failed-action-list", data_set, unames,
resources, show_opts, rc == pcmk_rc_ok));
}
/* Print failed stonith actions */
if (pcmk_is_set(section_opts, pcmk_section_fence_failed) && fence_history) {
if (history_rc == 0) {
stonith_history_t *hp = stonith__first_matching_event(stonith_history, stonith__event_state_eq,
GINT_TO_POINTER(st_failed));
if (hp) {
- CHECK_RC(rc, out->message(out, "failed-fencing-list", stonith_history, unames,
- section_opts, rc == pcmk_rc_ok));
+ CHECK_RC(rc, out->message(out, "failed-fencing-list",
+ stonith_history, unames, section_opts,
+ show_opts, rc == pcmk_rc_ok));
}
} else {
PCMK__OUTPUT_SPACER_IF(out, rc == pcmk_rc_ok);
out->begin_list(out, NULL, NULL, "Failed Fencing Actions");
out->list_item(out, NULL, "Failed to get fencing history: %s",
crm_exit_str(history_rc));
out->end_list(out);
already_printed_failure = true;
}
}
/* Print tickets if requested */
if (pcmk_is_set(section_opts, pcmk_section_tickets)) {
CHECK_RC(rc, out->message(out, "ticket-list", data_set, rc == pcmk_rc_ok));
}
/* Print negative location constraints if requested */
if (pcmk_is_set(section_opts, pcmk_section_bans)) {
CHECK_RC(rc, out->message(out, "ban-list", data_set, prefix, resources,
show_opts, rc == pcmk_rc_ok));
}
/* Print stonith history */
if (fence_history && pcmk_any_flags_set(section_opts, pcmk_section_fencing_all)) {
if (history_rc != 0) {
if (!already_printed_failure) {
PCMK__OUTPUT_SPACER_IF(out, rc == pcmk_rc_ok);
out->begin_list(out, NULL, NULL, "Failed Fencing Actions");
out->list_item(out, NULL, "Failed to get fencing history: %s",
crm_exit_str(history_rc));
out->end_list(out);
}
} else if (pcmk_is_set(section_opts, pcmk_section_fence_worked)) {
stonith_history_t *hp = stonith__first_matching_event(stonith_history, stonith__event_state_neq,
GINT_TO_POINTER(st_failed));
if (hp) {
CHECK_RC(rc, out->message(out, "fencing-list", hp, unames,
- section_opts, rc == pcmk_rc_ok));
+ section_opts, show_opts,
+ rc == pcmk_rc_ok));
}
} else if (pcmk_is_set(section_opts, pcmk_section_fence_pending)) {
stonith_history_t *hp = stonith__first_matching_event(stonith_history, stonith__event_state_pending, NULL);
if (hp) {
- CHECK_RC(rc, out->message(out, "pending-fencing-list", hp, unames,
- section_opts, rc == pcmk_rc_ok));
+ CHECK_RC(rc, out->message(out, "pending-fencing-list", hp,
+ unames, section_opts, show_opts,
+ rc == pcmk_rc_ok));
}
}
}
return rc;
}
PCMK__OUTPUT_ARGS("cluster-status", "pe_working_set_t *", "crm_exit_t", "stonith_history_t *",
"gboolean", "uint32_t", "uint32_t", "const char *", "GList *", "GList *")
static int
cluster_status_xml(pcmk__output_t *out, va_list args)
{
pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
crm_exit_t history_rc = va_arg(args, crm_exit_t);
stonith_history_t *stonith_history = va_arg(args, stonith_history_t *);
gboolean fence_history = va_arg(args, gboolean);
uint32_t section_opts = va_arg(args, uint32_t);
uint32_t show_opts = va_arg(args, uint32_t);
const char *prefix = va_arg(args, const char *);
GList *unames = va_arg(args, GList *);
GList *resources = va_arg(args, GList *);
out->message(out, "cluster-summary", data_set, section_opts, show_opts);
/*** NODES ***/
if (pcmk_is_set(section_opts, pcmk_section_nodes)) {
out->message(out, "node-list", data_set->nodes, unames, resources,
show_opts, FALSE);
}
/* Print resources section, if needed */
if (pcmk_is_set(section_opts, pcmk_section_resources)) {
/* XML output always displays full details. */
uint32_t full_show_opts = show_opts & ~pcmk_show_brief;
out->message(out, "resource-list", data_set, full_show_opts,
FALSE, unames, resources, FALSE);
}
/* print Node Attributes section if requested */
if (pcmk_is_set(section_opts, pcmk_section_attributes)) {
out->message(out, "node-attribute-list", data_set, show_opts, FALSE,
unames, resources);
}
/* If requested, print resource operations (which includes failcounts)
* or just failcounts
*/
if (pcmk_any_flags_set(section_opts, pcmk_section_operations | pcmk_section_failcounts)) {
out->message(out, "node-summary", data_set, unames,
resources, section_opts, show_opts, FALSE);
}
/* If there were any failed actions, print them */
if (pcmk_is_set(section_opts, pcmk_section_failures)
&& xml_has_children(data_set->failed)) {
out->message(out, "failed-action-list", data_set, unames, resources,
show_opts, FALSE);
}
/* Print stonith history */
if (pcmk_is_set(section_opts, pcmk_section_fencing_all) && fence_history) {
out->message(out, "full-fencing-list", history_rc, stonith_history,
- unames, section_opts, FALSE);
+ unames, section_opts, show_opts, FALSE);
}
/* Print tickets if requested */
if (pcmk_is_set(section_opts, pcmk_section_tickets)) {
out->message(out, "ticket-list", data_set, FALSE);
}
/* Print negative location constraints if requested */
if (pcmk_is_set(section_opts, pcmk_section_bans)) {
out->message(out, "ban-list", data_set, prefix, resources, show_opts,
FALSE);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("cluster-status", "pe_working_set_t *", "crm_exit_t", "stonith_history_t *",
"gboolean", "uint32_t", "uint32_t", "const char *", "GList *",
"GList *")
static int
cluster_status_html(pcmk__output_t *out, va_list args)
{
pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
crm_exit_t history_rc = va_arg(args, crm_exit_t);
stonith_history_t *stonith_history = va_arg(args, stonith_history_t *);
gboolean fence_history = va_arg(args, gboolean);
uint32_t section_opts = va_arg(args, uint32_t);
uint32_t show_opts = va_arg(args, uint32_t);
const char *prefix = va_arg(args, const char *);
GList *unames = va_arg(args, GList *);
GList *resources = va_arg(args, GList *);
bool already_printed_failure = false;
out->message(out, "cluster-summary", data_set, section_opts, show_opts);
/*** NODE LIST ***/
if (pcmk_is_set(section_opts, pcmk_section_nodes) && unames) {
out->message(out, "node-list", data_set->nodes, unames, resources,
show_opts, FALSE);
}
/* Print resources section, if needed */
if (pcmk_is_set(section_opts, pcmk_section_resources)) {
out->message(out, "resource-list", data_set, show_opts, TRUE, unames,
resources, FALSE);
}
/* print Node Attributes section if requested */
if (pcmk_is_set(section_opts, pcmk_section_attributes)) {
out->message(out, "node-attribute-list", data_set, show_opts, FALSE,
unames, resources);
}
/* If requested, print resource operations (which includes failcounts)
* or just failcounts
*/
if (pcmk_any_flags_set(section_opts, pcmk_section_operations | pcmk_section_failcounts)) {
out->message(out, "node-summary", data_set, unames,
resources, section_opts, show_opts, FALSE);
}
/* If there were any failed actions, print them */
if (pcmk_is_set(section_opts, pcmk_section_failures)
&& xml_has_children(data_set->failed)) {
out->message(out, "failed-action-list", data_set, unames, resources,
show_opts, FALSE);
}
/* Print failed stonith actions */
if (pcmk_is_set(section_opts, pcmk_section_fence_failed) && fence_history) {
if (history_rc == 0) {
stonith_history_t *hp = stonith__first_matching_event(stonith_history, stonith__event_state_eq,
GINT_TO_POINTER(st_failed));
if (hp) {
out->message(out, "failed-fencing-list", stonith_history, unames,
- section_opts, FALSE);
+ section_opts, show_opts, FALSE);
}
} else {
out->begin_list(out, NULL, NULL, "Failed Fencing Actions");
out->list_item(out, NULL, "Failed to get fencing history: %s",
crm_exit_str(history_rc));
out->end_list(out);
}
}
/* Print stonith history */
if (fence_history && pcmk_any_flags_set(section_opts, pcmk_section_fencing_all)) {
if (history_rc != 0) {
if (!already_printed_failure) {
out->begin_list(out, NULL, NULL, "Failed Fencing Actions");
out->list_item(out, NULL, "Failed to get fencing history: %s",
crm_exit_str(history_rc));
out->end_list(out);
}
} else if (pcmk_is_set(section_opts, pcmk_section_fence_worked)) {
stonith_history_t *hp = stonith__first_matching_event(stonith_history, stonith__event_state_neq,
GINT_TO_POINTER(st_failed));
if (hp) {
- out->message(out, "fencing-list", hp, unames, section_opts, FALSE);
+ out->message(out, "fencing-list", hp, unames, section_opts,
+ show_opts, FALSE);
}
} else if (pcmk_is_set(section_opts, pcmk_section_fence_pending)) {
stonith_history_t *hp = stonith__first_matching_event(stonith_history, stonith__event_state_pending, NULL);
if (hp) {
out->message(out, "pending-fencing-list", hp, unames,
- section_opts, FALSE);
+ section_opts, show_opts, FALSE);
}
}
}
/* Print tickets if requested */
if (pcmk_is_set(section_opts, pcmk_section_tickets)) {
out->message(out, "ticket-list", data_set, FALSE);
}
/* Print negative location constraints if requested */
if (pcmk_is_set(section_opts, pcmk_section_bans)) {
out->message(out, "ban-list", data_set, prefix, resources, show_opts,
FALSE);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("attribute", "char *", "char *", "char *", "char *")
static int
attribute_default(pcmk__output_t *out, va_list args)
{
char *scope = va_arg(args, char *);
char *instance = va_arg(args, char *);
char *name = va_arg(args, char *);
char *value = va_arg(args, char *);
char *host = va_arg(args, char *);
GString *s = g_string_sized_new(50);
if (!pcmk__str_empty(scope)) {
g_string_append_printf(s, "scope=\"%s\" ", scope);
}
if (!pcmk__str_empty(instance)) {
g_string_append_printf(s, "id=\"%s\" ", instance);
}
g_string_append_printf(s, "name=\"%s\" ", name);
if (!pcmk__str_empty(host)) {
g_string_append_printf(s, "host=\"%s\" ", host);
}
g_string_append_printf(s, "value=\"%s\"", value ? value : "");
out->info(out, "%s", s->str);
g_string_free(s, TRUE);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("attribute", "char *", "char *", "char *", "char *")
static int
attribute_xml(pcmk__output_t *out, va_list args)
{
char *scope = va_arg(args, char *);
char *instance = va_arg(args, char *);
char *name = va_arg(args, char *);
char *value = va_arg(args, char *);
char *host = va_arg(args, char *);
xmlNodePtr node = NULL;
node = pcmk__output_create_xml_node(out, "attribute",
"name", name,
"value", value ? value : "",
NULL);
if (!pcmk__str_empty(scope)) {
crm_xml_add(node, "scope", scope);
}
if (!pcmk__str_empty(instance)) {
crm_xml_add(node, "id", instance);
}
if (!pcmk__str_empty(host)) {
crm_xml_add(node, "host", host);
}
return pcmk_rc_ok;
}
static pcmk__message_entry_t fmt_functions[] = {
{ "attribute", "default", attribute_default },
{ "attribute", "xml", attribute_xml },
{ "cluster-status", "default", pcmk__cluster_status_text },
{ "cluster-status", "html", cluster_status_html },
{ "cluster-status", "xml", cluster_status_xml },
{ "crmadmin-node", "default", crmadmin_node_text },
{ "crmadmin-node", "xml", crmadmin_node_xml },
{ "dc", "default", dc_text },
{ "dc", "xml", dc_xml },
{ "digests", "default", digests_text },
{ "digests", "xml", digests_xml },
{ "health", "default", health_text },
{ "health", "xml", health_xml },
{ "inject-attr", "default", inject_attr },
{ "inject-attr", "xml", inject_attr_xml },
{ "inject-cluster-action", "default", inject_cluster_action },
{ "inject-cluster-action", "xml", inject_cluster_action_xml },
{ "inject-fencing-action", "default", inject_fencing_action },
{ "inject-fencing-action", "xml", inject_fencing_action_xml },
{ "inject-modify-config", "default", inject_modify_config },
{ "inject-modify-config", "xml", inject_modify_config_xml },
{ "inject-modify-node", "default", inject_modify_node },
{ "inject-modify-node", "xml", inject_modify_node_xml },
{ "inject-modify-ticket", "default", inject_modify_ticket },
{ "inject-modify-ticket", "xml", inject_modify_ticket_xml },
{ "inject-pseudo-action", "default", inject_pseudo_action },
{ "inject-pseudo-action", "xml", inject_pseudo_action_xml },
{ "inject-rsc-action", "default", inject_rsc_action },
{ "inject-rsc-action", "xml", inject_rsc_action_xml },
{ "inject-spec", "default", inject_spec },
{ "inject-spec", "xml", inject_spec_xml },
{ "locations-list", "default", locations_list },
{ "locations-list", "xml", locations_list_xml },
{ "node-action", "default", node_action },
{ "node-action", "xml", node_action_xml },
{ "pacemakerd-health", "default", pacemakerd_health_text },
{ "pacemakerd-health", "xml", pacemakerd_health_xml },
{ "profile", "default", profile_default, },
{ "profile", "xml", profile_xml },
{ "rsc-action", "default", rsc_action_default },
{ "rsc-action-item", "default", rsc_action_item },
{ "rsc-action-item", "xml", rsc_action_item_xml },
{ "rsc-is-colocated-with-list", "default", rsc_is_colocated_with_list },
{ "rsc-is-colocated-with-list", "xml", rsc_is_colocated_with_list_xml },
{ "rscs-colocated-with-list", "default", rscs_colocated_with_list },
{ "rscs-colocated-with-list", "xml", rscs_colocated_with_list_xml },
{ "stacks-constraints", "default", stacks_and_constraints },
{ "stacks-constraints", "xml", stacks_and_constraints_xml },
{ NULL, NULL, NULL }
};
void
pcmk__register_lib_messages(pcmk__output_t *out) {
pcmk__register_messages(out, fmt_functions);
}
diff --git a/tools/crm_mon_curses.c b/tools/crm_mon_curses.c
index 2b2cc5e187..959b44ad0b 100644
--- a/tools/crm_mon_curses.c
+++ b/tools/crm_mon_curses.c
@@ -1,498 +1,501 @@
/*
* Copyright 2019-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 <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <crm/crm.h>
#include <crm/common/cmdline_internal.h>
#include <crm/stonith-ng.h>
#include <crm/fencing/internal.h>
#include <crm/pengine/internal.h>
#include <glib.h>
#include <pacemaker-internal.h>
#include "crm_mon.h"
#if CURSES_ENABLED
GOptionEntry crm_mon_curses_output_entries[] = {
{ NULL }
};
typedef struct curses_list_data_s {
unsigned int len;
char *singular_noun;
char *plural_noun;
} curses_list_data_t;
typedef struct private_data_s {
GQueue *parent_q;
} private_data_t;
static void
curses_free_priv(pcmk__output_t *out) {
private_data_t *priv = out->priv;
if (priv == NULL) {
return;
}
g_queue_free(priv->parent_q);
free(priv);
out->priv = NULL;
}
static bool
curses_init(pcmk__output_t *out) {
private_data_t *priv = NULL;
/* If curses_init was previously called on this output struct, just return. */
if (out->priv != NULL) {
return true;
} else {
out->priv = calloc(1, sizeof(private_data_t));
if (out->priv == NULL) {
return false;
}
priv = out->priv;
}
priv->parent_q = g_queue_new();
initscr();
cbreak();
noecho();
return true;
}
static void
curses_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
CRM_ASSERT(out != NULL);
echo();
nocbreak();
endwin();
}
static void
curses_reset(pcmk__output_t *out) {
CRM_ASSERT(out != NULL);
curses_free_priv(out);
curses_init(out);
}
static void
curses_subprocess_output(pcmk__output_t *out, int exit_status,
const char *proc_stdout, const char *proc_stderr) {
CRM_ASSERT(out != NULL);
if (proc_stdout != NULL) {
printw("%s\n", proc_stdout);
}
if (proc_stderr != NULL) {
printw("%s\n", proc_stderr);
}
clrtoeol();
refresh();
}
/* curses_version is defined in curses.h, so we can't use that name here.
* Note that this function prints out via text, not with curses.
*/
static void
curses_ver(pcmk__output_t *out, bool extended) {
CRM_ASSERT(out != NULL);
if (extended) {
printf("Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
} else {
printf("Pacemaker %s\n", PACEMAKER_VERSION);
printf("Written by Andrew Beekhof and the "
"Pacemaker project contributors\n");
}
}
G_GNUC_PRINTF(2, 3)
static void
curses_error(pcmk__output_t *out, const char *format, ...) {
va_list ap;
CRM_ASSERT(out != NULL);
/* Informational output does not get indented, to separate it from other
* potentially indented list output.
*/
va_start(ap, format);
vw_printw(stdscr, format, ap);
va_end(ap);
/* Add a newline. */
addch('\n');
clrtoeol();
refresh();
sleep(2);
}
G_GNUC_PRINTF(2, 3)
static int
curses_info(pcmk__output_t *out, const char *format, ...) {
va_list ap;
CRM_ASSERT(out != NULL);
if (out->is_quiet(out)) {
return pcmk_rc_no_output;
}
/* Informational output does not get indented, to separate it from other
* potentially indented list output.
*/
va_start(ap, format);
vw_printw(stdscr, format, ap);
va_end(ap);
/* Add a newline. */
addch('\n');
clrtoeol();
refresh();
return pcmk_rc_ok;
}
static void
curses_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
CRM_ASSERT(out != NULL);
curses_indented_printf(out, "%s", buf);
}
G_GNUC_PRINTF(4, 5)
static void
curses_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
const char *format, ...) {
private_data_t *priv = NULL;
curses_list_data_t *new_list = NULL;
va_list ap;
CRM_ASSERT(out != NULL && out->priv != NULL);
priv = out->priv;
/* Empty formats can be used to create a new level of indentation, but without
* displaying some sort of list header. In that case we need to not do any of
* this stuff. vw_printw will act weird if told to print a NULL.
*/
if (format != NULL) {
va_start(ap, format);
curses_indented_vprintf(out, format, ap);
printw(":\n");
va_end(ap);
}
new_list = calloc(1, sizeof(curses_list_data_t));
new_list->len = 0;
pcmk__str_update(&new_list->singular_noun, singular_noun);
pcmk__str_update(&new_list->plural_noun, plural_noun);
g_queue_push_tail(priv->parent_q, new_list);
}
G_GNUC_PRINTF(3, 4)
static void
curses_list_item(pcmk__output_t *out, const char *id, const char *format, ...) {
va_list ap;
CRM_ASSERT(out != NULL);
va_start(ap, format);
if (id != NULL) {
curses_indented_printf(out, "%s: ", id);
vw_printw(stdscr, format, ap);
} else {
curses_indented_vprintf(out, format, ap);
}
addch('\n');
va_end(ap);
out->increment_list(out);
}
static void
curses_increment_list(pcmk__output_t *out) {
private_data_t *priv = NULL;
gpointer tail;
CRM_ASSERT(out != NULL && out->priv != NULL);
priv = out->priv;
tail = g_queue_peek_tail(priv->parent_q);
CRM_ASSERT(tail != NULL);
((curses_list_data_t *) tail)->len++;
}
static void
curses_end_list(pcmk__output_t *out) {
private_data_t *priv = NULL;
curses_list_data_t *node = NULL;
CRM_ASSERT(out != NULL && out->priv != NULL);
priv = out->priv;
node = g_queue_pop_tail(priv->parent_q);
if (node->singular_noun != NULL && node->plural_noun != NULL) {
if (node->len == 1) {
curses_indented_printf(out, "%d %s found\n", node->len, node->singular_noun);
} else {
curses_indented_printf(out, "%d %s found\n", node->len, node->plural_noun);
}
}
free(node);
}
static bool
curses_is_quiet(pcmk__output_t *out) {
CRM_ASSERT(out != NULL);
return out->quiet;
}
static void
curses_spacer(pcmk__output_t *out) {
CRM_ASSERT(out != NULL);
addch('\n');
}
static void
curses_progress(pcmk__output_t *out, bool end) {
CRM_ASSERT(out != NULL);
if (end) {
printw(".\n");
} else {
addch('.');
}
}
static void
curses_prompt(const char *prompt, bool do_echo, char **dest)
{
int rc = OK;
CRM_ASSERT(prompt != NULL);
CRM_ASSERT(dest != NULL);
/* This is backwards from the text version of this function on purpose. We
* disable echo by default in curses_init, so we need to enable it here if
* asked for.
*/
if (do_echo) {
rc = echo();
}
if (rc == OK) {
printw("%s: ", prompt);
if (*dest != NULL) {
free(*dest);
}
*dest = calloc(1, 1024);
/* On older systems, scanw is defined as taking a char * for its first argument,
* while newer systems rightly want a const char *. Accomodate both here due
* to building with -Werror.
*/
rc = scanw((NCURSES_CONST char *) "%1023s", *dest);
addch('\n');
}
if (rc < 1) {
free(*dest);
*dest = NULL;
}
if (do_echo) {
noecho();
}
}
pcmk__output_t *
crm_mon_mk_curses_output(char **argv) {
pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
if (retval == NULL) {
return NULL;
}
retval->fmt_name = "console";
retval->request = pcmk__quote_cmdline(argv);
retval->init = curses_init;
retval->free_priv = curses_free_priv;
retval->finish = curses_finish;
retval->reset = curses_reset;
retval->register_message = pcmk__register_message;
retval->message = pcmk__call_message;
retval->subprocess_output = curses_subprocess_output;
retval->version = curses_ver;
retval->err = curses_error;
retval->info = curses_info;
retval->output_xml = curses_output_xml;
retval->begin_list = curses_begin_list;
retval->list_item = curses_list_item;
retval->increment_list = curses_increment_list;
retval->end_list = curses_end_list;
retval->is_quiet = curses_is_quiet;
retval->spacer = curses_spacer;
retval->progress = curses_progress;
retval->prompt = curses_prompt;
return retval;
}
G_GNUC_PRINTF(2, 0)
void
curses_formatted_vprintf(pcmk__output_t *out, const char *format, va_list args) {
vw_printw(stdscr, format, args);
clrtoeol();
refresh();
}
G_GNUC_PRINTF(2, 3)
void
curses_formatted_printf(pcmk__output_t *out, const char *format, ...) {
va_list ap;
va_start(ap, format);
curses_formatted_vprintf(out, format, ap);
va_end(ap);
}
G_GNUC_PRINTF(2, 0)
void
curses_indented_vprintf(pcmk__output_t *out, const char *format, va_list args) {
int level = 0;
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
level = g_queue_get_length(priv->parent_q);
for (int i = 0; i < level; i++) {
printw(" ");
}
if (level > 0) {
printw("* ");
}
curses_formatted_vprintf(out, format, args);
}
G_GNUC_PRINTF(2, 3)
void
curses_indented_printf(pcmk__output_t *out, const char *format, ...) {
va_list ap;
va_start(ap, format);
curses_indented_vprintf(out, format, ap);
va_end(ap);
}
PCMK__OUTPUT_ARGS("maint-mode", "unsigned long long int")
static int
cluster_maint_mode_console(pcmk__output_t *out, va_list args) {
unsigned long long flags = va_arg(args, unsigned long long);
if (pcmk_is_set(flags, pe_flag_maintenance_mode)) {
curses_formatted_printf(out, "\n *** Resource management is DISABLED ***\n");
curses_formatted_printf(out, " The cluster will not attempt to start, stop or recover services\n");
return pcmk_rc_ok;
} else if (pcmk_is_set(flags, pe_flag_stop_everything)) {
curses_formatted_printf(out, "\n *** Resource management is DISABLED ***\n");
curses_formatted_printf(out, " The cluster will keep all resources stopped\n");
return pcmk_rc_ok;
} else {
return pcmk_rc_no_output;
}
}
PCMK__OUTPUT_ARGS("cluster-status", "pe_working_set_t *", "crm_exit_t",
"stonith_history_t *", "gboolean", "uint32_t", "uint32_t",
"const char *", "GList *", "GList *")
static int
cluster_status_console(pcmk__output_t *out, va_list args) {
int rc = pcmk_rc_no_output;
blank_screen();
rc = pcmk__cluster_status_text(out, args);
refresh();
return rc;
}
-PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "int", "const char *")
+PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "int", "const char *",
+ "uint32_t")
static int
-stonith_event_console(pcmk__output_t *out, va_list args) {
+stonith_event_console(pcmk__output_t *out, va_list args)
+{
stonith_history_t *event = va_arg(args, stonith_history_t *);
int full_history = va_arg(args, int);
const char *succeeded = va_arg(args, const char *);
+ uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t);
gchar *desc = stonith__history_description(event, full_history, succeeded);
curses_indented_printf(out, "%s\n", desc);
g_free(desc);
return pcmk_rc_ok;
}
static pcmk__message_entry_t fmt_functions[] = {
{ "cluster-status", "console", cluster_status_console },
{ "maint-mode", "console", cluster_maint_mode_console },
{ "stonith-event", "console", stonith_event_console },
{ NULL, NULL, NULL }
};
#endif
void
crm_mon_register_messages(pcmk__output_t *out) {
#if CURSES_ENABLED
pcmk__register_messages(out, fmt_functions);
#endif
}
void
blank_screen(void)
{
#if CURSES_ENABLED
int lpc = 0;
for (lpc = 0; lpc < LINES; lpc++) {
move(lpc, 0);
clrtoeol();
}
move(0, 0);
refresh();
#endif
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Oct 25, 1:59 AM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2618250
Default Alt Text
(120 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment