Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F3687243
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
22 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment