Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/lib/fencing/st_output.c b/lib/fencing/st_output.c
index f23f59cca7..3ce9d01314 100644
--- a/lib/fencing/st_output.c
+++ b/lib/fencing/st_output.c
@@ -1,606 +1,625 @@
/*
* Copyright 2019-2024 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/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>
/*!
* \internal
* \brief Convert seconds and nanoseconds to a date/time/time-zone string
*
* \param[in] sec Seconds
* \param[in] nsec Nanoseconds
* \param[in] show_usec Whether to show time in microseconds resolution (if
* false, use seconds resolution)
*
* \return A string representation of \p sec and \nsec
*
* \note The caller is responsible for freeing the return value using \p free().
*/
static char *
timespec_string(time_t sec, long nsec, bool show_usec) {
const struct timespec ts = {
.tv_sec = sec,
.tv_nsec = nsec,
};
return pcmk__timespec2str(&ts,
crm_time_log_date
|crm_time_log_timeofday
|crm_time_log_with_timezone
|(show_usec? crm_time_usecs : 0));
}
+/*!
+ * \internal
+ * \brief Return a readable string equivalent of a fencing history item's action
+ *
+ * \param[in] history Fencing history entry
+ *
+ * \return Readable string equivalent of action belonging to \p history
+ */
+static const char *
+history_action_text(const stonith_history_t *history)
+{
+ if (pcmk__str_eq(history->action, PCMK_ACTION_ON, pcmk__str_none)) {
+ return "unfencing";
+ }
+ if (pcmk__str_eq(history->action, PCMK_ACTION_OFF, pcmk__str_none)) {
+ return "turning off";
+ }
+ return pcmk__s(history->action, "fencing");
+}
+
/*!
* \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_text() except user-oriented (i.e.,
* for cluster status) instead of developer-oriented (for debug logs).
*/
static const char *
state_str(const 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
* \param[in] show_opts Flag group of pcmk_show_opt_e
*
* \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(const stonith_history_t *history,
bool full_history, const char *later_succeeded,
uint32_t show_opts)
{
GString *str = g_string_sized_new(256); // Generous starting size
char *completed_time_s = NULL;
if ((history->state == st_failed) || (history->state == st_done)) {
completed_time_s = timespec_string(history->completed,
history->completed_nsec, true);
}
- pcmk__g_strcat(str,
- stonith_action_str(history->action), " of ", history->target,
+ pcmk__g_strcat(str, history_action_text(history), " of ", history->target,
NULL);
if (!pcmk_is_set(show_opts, pcmk_show_failed_detail)) {
// More human-friendly
if (((history->state == st_failed) || (history->state == st_done))
&& (history->delegate != NULL)) {
pcmk__g_strcat(str, " by ", history->delegate, NULL);
}
pcmk__g_strcat(str, " for ", history->client, "@", history->origin,
NULL);
if (!full_history) {
g_string_append(str, " last"); // For example, "last failed at ..."
}
}
pcmk__add_word(&str, 0, state_str(history));
// For failed actions, add exit reason if available
if ((history->state == st_failed) && (history->exit_reason != NULL)) {
pcmk__g_strcat(str, " (", history->exit_reason, ")", NULL);
}
if (pcmk_is_set(show_opts, pcmk_show_failed_detail)) {
// More technical
g_string_append(str, ": ");
// For completed actions, add delegate if available
if (((history->state == st_failed) || (history->state == st_done))
&& (history->delegate != NULL)) {
pcmk__g_strcat(str, PCMK_XA_DELEGATE "=", history->delegate, ", ",
NULL);
}
// Add information about originator
pcmk__g_strcat(str,
PCMK_XA_CLIENT "=", history->client, ", "
PCMK_XA_ORIGIN "=", history->origin, NULL);
// For completed actions, add completion time
if (completed_time_s != 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");
}
pcmk__g_strcat(str, "='", completed_time_s, "'", NULL);
}
} else if (completed_time_s != NULL) {
// More human-friendly
pcmk__g_strcat(str, " at ", completed_time_s, NULL);
}
if ((history->state == st_failed) && (later_succeeded != NULL)) {
pcmk__g_strcat(str,
" (a later attempt from ", later_succeeded,
" succeeded)", NULL);
}
free(completed_time_s);
return g_string_free(str, FALSE);
}
PCMK__OUTPUT_ARGS("failed-fencing-list", "stonith_history_t *", "GList *",
"uint32_t", "uint32_t", "bool")
static int
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);
bool print_spacer = va_arg(args, int);
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),
false, 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",
"uint32_t", "bool")
static int
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);
bool print_spacer = va_arg(args, int);
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),
false, 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", "uint32_t", "bool")
static int
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);
bool print_spacer = va_arg(args, int);
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),
false, 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", "uint32_t", "bool")
static int
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);
bool print_spacer G_GNUC_UNUSED = va_arg(args, int);
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),
false, 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, PCMK_XE_FENCE_HISTORY,
PCMK_XA_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, PCMK__XE_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 = timespec_string(when, 0, false);
pcmk__output_create_xml_node(out, PCMK_XE_LAST_FENCED,
PCMK_XA_TARGET, target,
PCMK_XA_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", "uint32_t", "bool")
static int
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);
bool print_spacer = va_arg(args, int);
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),
false, 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 *", "bool", "bool",
"const char *", "uint32_t")
static int
stonith_event_html(pcmk__output_t *out, va_list args)
{
stonith_history_t *event = va_arg(args, stonith_history_t *);
bool full_history = va_arg(args, int);
bool completed_only G_GNUC_UNUSED = va_arg(args, int);
const char *succeeded = va_arg(args, const char *);
uint32_t show_opts = va_arg(args, uint32_t);
gchar *desc = stonith__history_description(event, full_history, succeeded,
show_opts);
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 *", "bool", "bool",
"const char *", "uint32_t")
static int
stonith_event_text(pcmk__output_t *out, va_list args)
{
stonith_history_t *event = va_arg(args, stonith_history_t *);
bool full_history = va_arg(args, int);
bool completed_only = va_arg(args, int);
const char *succeeded = va_arg(args, const char *);
uint32_t show_opts = va_arg(args, uint32_t);
if (completed_only) {
pcmk__formatted_printf(out, "%lld\n", (long long) event->completed);
} else {
gchar *desc = stonith__history_description(event, full_history, succeeded,
show_opts);
pcmk__indented_printf(out, "%s\n", desc);
g_free(desc);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "bool", "bool",
"const char *", "uint32_t")
static int
stonith_event_xml(pcmk__output_t *out, va_list args)
{
stonith_history_t *event = va_arg(args, stonith_history_t *);
bool full_history G_GNUC_UNUSED = va_arg(args, int);
bool completed_only 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);
xmlNodePtr node = NULL;
node = pcmk__output_create_xml_node(out, PCMK_XE_FENCE_EVENT,
PCMK_XA_ACTION, event->action,
PCMK_XA_TARGET, event->target,
PCMK_XA_CLIENT, event->client,
PCMK_XA_ORIGIN, event->origin,
NULL);
switch (event->state) {
case st_failed:
pcmk__xe_set_props(node,
PCMK_XA_STATUS, PCMK_VALUE_FAILED,
PCMK_XA_EXIT_REASON, event->exit_reason,
NULL);
break;
case st_done:
crm_xml_add(node, PCMK_XA_STATUS, PCMK_VALUE_SUCCESS);
break;
default: {
char *state = pcmk__itoa(event->state);
pcmk__xe_set_props(node,
PCMK_XA_STATUS, PCMK_VALUE_PENDING,
PCMK_XA_EXTENDED_STATUS, state,
NULL);
free(state);
break;
}
}
if (event->delegate != NULL) {
crm_xml_add(node, PCMK_XA_DELEGATE, event->delegate);
}
if ((event->state == st_failed) || (event->state == st_done)) {
char *time_s = timespec_string(event->completed, event->completed_nsec,
true);
crm_xml_add(node, PCMK_XA_COMPLETED, time_s);
free(time_s);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("validate", "const char *", "const char *", "const char *",
"const 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 *);
const char *output = va_arg(args, const char *);
const char *error_output = va_arg(args, const 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, PCMK__XE_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, PCMK__XE_DIV, NULL, NULL, buf);
free(buf);
}
out->subprocess_output(out, rc, output, error_output);
return rc;
}
PCMK__OUTPUT_ARGS("validate", "const char *", "const char *", "const char *",
"const 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 *);
const char *output = va_arg(args, const char *);
const char *error_output = va_arg(args, const 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 *", "const char *",
"const 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 *);
const char *output = va_arg(args, const char *);
const char *error_output = va_arg(args, const char *);
int rc = va_arg(args, int);
const char *valid = pcmk__btoa(rc == pcmk_ok);
xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_VALIDATE,
PCMK_XA_AGENT, agent,
PCMK_XA_VALID, valid,
NULL);
if (device != NULL) {
crm_xml_add(node, PCMK_XA_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", failed_history },
{ "fencing-list", "default", stonith_history },
{ "full-fencing-list", "default", 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", 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);
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Apr 21, 7:07 PM (16 h, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1665379
Default Alt Text
(22 KB)

Event Timeline