pacemaker 2.1.8-2.1.8
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
st_output.c
Go to the documentation of this file.
1/*
2 * Copyright 2019-2024 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 */
9
10#include <crm_internal.h>
11#include <stdarg.h>
12#include <stdint.h>
13
14#include <crm/stonith-ng.h>
15#include <crm/common/iso8601.h>
16#include <crm/common/util.h>
17#include <crm/common/xml.h>
18#include <crm/common/output.h>
23
37static char *
38timespec_string(time_t sec, long nsec, bool show_usec) {
39 const struct timespec ts = {
40 .tv_sec = sec,
41 .tv_nsec = nsec,
42 };
43
44 return pcmk__timespec2str(&ts,
48 |(show_usec? crm_time_usecs : 0));
49}
50
61static const char *
62state_str(const stonith_history_t *history)
63{
64 switch (history->state) {
65 case st_failed: return "failed";
66 case st_done: return "successful";
67 default: return "pending";
68 }
69}
70
88gchar *
90 bool full_history, const char *later_succeeded,
91 uint32_t show_opts)
92{
93 GString *str = g_string_sized_new(256); // Generous starting size
94 char *completed_time_s = NULL;
95
96 if ((history->state == st_failed) || (history->state == st_done)) {
97 completed_time_s = timespec_string(history->completed,
98 history->completed_nsec, true);
99 }
100
101 pcmk__g_strcat(str,
102 stonith_action_str(history->action), " of ", history->target,
103 NULL);
104
105 if (!pcmk_is_set(show_opts, pcmk_show_failed_detail)) {
106 // More human-friendly
107 if (((history->state == st_failed) || (history->state == st_done))
108 && (history->delegate != NULL)) {
109
110 pcmk__g_strcat(str, " by ", history->delegate, NULL);
111 }
112 pcmk__g_strcat(str, " for ", history->client, "@", history->origin,
113 NULL);
114 if (!full_history) {
115 g_string_append(str, " last"); // For example, "last failed at ..."
116 }
117 }
118
119 pcmk__add_word(&str, 0, state_str(history));
120
121 // For failed actions, add exit reason if available
122 if ((history->state == st_failed) && (history->exit_reason != NULL)) {
123 pcmk__g_strcat(str, " (", history->exit_reason, ")", NULL);
124 }
125
126 if (pcmk_is_set(show_opts, pcmk_show_failed_detail)) {
127 // More technical
128 g_string_append(str, ": ");
129
130 // For completed actions, add delegate if available
131 if (((history->state == st_failed) || (history->state == st_done))
132 && (history->delegate != NULL)) {
133
134 pcmk__g_strcat(str, PCMK_XA_DELEGATE "=", history->delegate, ", ",
135 NULL);
136 }
137
138 // Add information about originator
139 pcmk__g_strcat(str,
140 PCMK_XA_CLIENT "=", history->client, ", "
141 PCMK_XA_ORIGIN "=", history->origin, NULL);
142
143 // For completed actions, add completion time
144 if (completed_time_s != NULL) {
145 if (full_history) {
146 g_string_append(str, ", completed");
147 } else if (history->state == st_failed) {
148 g_string_append(str, ", last-failed");
149 } else {
150 g_string_append(str, ", last-successful");
151 }
152 pcmk__g_strcat(str, "='", completed_time_s, "'", NULL);
153 }
154 } else if (completed_time_s != NULL) {
155 // More human-friendly
156 pcmk__g_strcat(str, " at ", completed_time_s, NULL);
157 }
158
159 if ((history->state == st_failed) && (later_succeeded != NULL)) {
160 pcmk__g_strcat(str,
161 " (a later attempt from ", later_succeeded,
162 " succeeded)", NULL);
163 }
164
165 free(completed_time_s);
166 return g_string_free(str, FALSE);
167}
168
169PCMK__OUTPUT_ARGS("failed-fencing-list", "stonith_history_t *", "GList *",
170 "uint32_t", "uint32_t", "bool")
171static int
172failed_history(pcmk__output_t *out, va_list args)
173{
174 stonith_history_t *history = va_arg(args, stonith_history_t *);
175 GList *only_node = va_arg(args, GList *);
176 uint32_t section_opts = va_arg(args, uint32_t);
177 uint32_t show_opts = va_arg(args, uint32_t);
178 bool print_spacer = va_arg(args, int);
179
180 int rc = pcmk_rc_no_output;
181
182 for (stonith_history_t *hp = history; hp; hp = hp->next) {
183 if (hp->state != st_failed) {
184 continue;
185 }
186
187 if (!pcmk__str_in_list(hp->target, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
188 continue;
189 }
190
191 PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Failed Fencing Actions");
192 out->message(out, "stonith-event", hp,
193 pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
194 false, stonith__later_succeeded(hp, history), show_opts);
195 out->increment_list(out);
196 }
197
199 return rc;
200}
201
202PCMK__OUTPUT_ARGS("fencing-list", "stonith_history_t *", "GList *", "uint32_t",
203 "uint32_t", "bool")
204static int
205stonith_history(pcmk__output_t *out, va_list args)
206{
207 stonith_history_t *history = va_arg(args, stonith_history_t *);
208 GList *only_node = va_arg(args, GList *);
209 uint32_t section_opts = va_arg(args, uint32_t);
210 uint32_t show_opts = va_arg(args, uint32_t);
211 bool print_spacer = va_arg(args, int);
212
213 int rc = pcmk_rc_no_output;
214
215 for (stonith_history_t *hp = history; hp; hp = hp->next) {
216 if (!pcmk__str_in_list(hp->target, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
217 continue;
218 }
219
220 if (hp->state != st_failed) {
221 PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Fencing History");
222 out->message(out, "stonith-event", hp,
223 pcmk_all_flags_set(section_opts,
225 false, stonith__later_succeeded(hp, history), show_opts);
226 out->increment_list(out);
227 }
228 }
229
231 return rc;
232}
233
234PCMK__OUTPUT_ARGS("full-fencing-list", "crm_exit_t", "stonith_history_t *",
235 "GList *", "uint32_t", "uint32_t", "bool")
236static int
237full_history(pcmk__output_t *out, va_list args)
238{
239 crm_exit_t history_rc G_GNUC_UNUSED = va_arg(args, crm_exit_t);
240 stonith_history_t *history = va_arg(args, stonith_history_t *);
241 GList *only_node = va_arg(args, GList *);
242 uint32_t section_opts = va_arg(args, uint32_t);
243 uint32_t show_opts = va_arg(args, uint32_t);
244 bool print_spacer = va_arg(args, int);
245
246 int rc = pcmk_rc_no_output;
247
248 for (stonith_history_t *hp = history; hp; hp = hp->next) {
249 if (!pcmk__str_in_list(hp->target, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
250 continue;
251 }
252
253 PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Fencing History");
254 out->message(out, "stonith-event", hp,
255 pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
256 false, stonith__later_succeeded(hp, history), show_opts);
257 out->increment_list(out);
258 }
259
261 return rc;
262}
263
264PCMK__OUTPUT_ARGS("full-fencing-list", "crm_exit_t", "stonith_history_t *",
265 "GList *", "uint32_t", "uint32_t", "bool")
266static int
267full_history_xml(pcmk__output_t *out, va_list args)
268{
269 crm_exit_t history_rc = va_arg(args, crm_exit_t);
270 stonith_history_t *history = va_arg(args, stonith_history_t *);
271 GList *only_node = va_arg(args, GList *);
272 uint32_t section_opts = va_arg(args, uint32_t);
273 uint32_t show_opts = va_arg(args, uint32_t);
274 bool print_spacer G_GNUC_UNUSED = va_arg(args, int);
275
276 int rc = pcmk_rc_no_output;
277
278 if (history_rc == 0) {
279 for (stonith_history_t *hp = history; hp; hp = hp->next) {
280 if (!pcmk__str_in_list(hp->target, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
281 continue;
282 }
283
284 PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Fencing History");
285 out->message(out, "stonith-event", hp,
286 pcmk_all_flags_set(section_opts,
288 false, stonith__later_succeeded(hp, history), show_opts);
289 out->increment_list(out);
290 }
291
293 } else {
294 char *rc_s = pcmk__itoa(history_rc);
295
297 PCMK_XA_STATUS, rc_s,
298 NULL);
299 free(rc_s);
300
301 rc = pcmk_rc_ok;
302 }
303
304 return rc;
305}
306
307PCMK__OUTPUT_ARGS("last-fenced", "const char *", "time_t")
308static int
309last_fenced_html(pcmk__output_t *out, va_list args) {
310 const char *target = va_arg(args, const char *);
311 time_t when = va_arg(args, time_t);
312
313 if (when) {
314 char *buf = crm_strdup_printf("Node %s last fenced at: %s", target, ctime(&when));
315 pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL, buf);
316 free(buf);
317 return pcmk_rc_ok;
318 } else {
319 return pcmk_rc_no_output;
320 }
321}
322
323PCMK__OUTPUT_ARGS("last-fenced", "const char *", "time_t")
324static int
325last_fenced_text(pcmk__output_t *out, va_list args) {
326 const char *target = va_arg(args, const char *);
327 time_t when = va_arg(args, time_t);
328
329 if (when) {
330 pcmk__indented_printf(out, "Node %s last fenced at: %s", target, ctime(&when));
331 } else {
332 pcmk__indented_printf(out, "Node %s has never been fenced\n", target);
333 }
334
335 return pcmk_rc_ok;
336}
337
338PCMK__OUTPUT_ARGS("last-fenced", "const char *", "time_t")
339static int
340last_fenced_xml(pcmk__output_t *out, va_list args) {
341 const char *target = va_arg(args, const char *);
342 time_t when = va_arg(args, time_t);
343
344 if (when) {
345 char *buf = timespec_string(when, 0, false);
346
349 PCMK_XA_WHEN, buf,
350 NULL);
351
352 free(buf);
353 return pcmk_rc_ok;
354 } else {
355 return pcmk_rc_no_output;
356 }
357}
358
359PCMK__OUTPUT_ARGS("pending-fencing-list", "stonith_history_t *", "GList *",
360 "uint32_t", "uint32_t", "bool")
361static int
362pending_actions(pcmk__output_t *out, va_list args)
363{
364 stonith_history_t *history = va_arg(args, stonith_history_t *);
365 GList *only_node = va_arg(args, GList *);
366 uint32_t section_opts = va_arg(args, uint32_t);
367 uint32_t show_opts = va_arg(args, uint32_t);
368 bool print_spacer = va_arg(args, int);
369
370 int rc = pcmk_rc_no_output;
371
372 for (stonith_history_t *hp = history; hp; hp = hp->next) {
373 if (!pcmk__str_in_list(hp->target, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
374 continue;
375 }
376
377 /* Skip the rest of the history after we see a failed/done action */
378 if ((hp->state == st_failed) || (hp->state == st_done)) {
379 break;
380 }
381
382 PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Pending Fencing Actions");
383 out->message(out, "stonith-event", hp,
384 pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
385 false, stonith__later_succeeded(hp, history), show_opts);
386 out->increment_list(out);
387 }
388
390 return rc;
391}
392
393PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "bool", "bool",
394 "const char *", "uint32_t")
395static int
396stonith_event_html(pcmk__output_t *out, va_list args)
397{
398 stonith_history_t *event = va_arg(args, stonith_history_t *);
399 bool full_history = va_arg(args, int);
400 bool completed_only G_GNUC_UNUSED = va_arg(args, int);
401 const char *succeeded = va_arg(args, const char *);
402 uint32_t show_opts = va_arg(args, uint32_t);
403
404 gchar *desc = stonith__history_description(event, full_history, succeeded,
405 show_opts);
406
407 switch(event->state) {
408 case st_done:
409 out->list_item(out, "successful-stonith-event", "%s", desc);
410 break;
411
412 case st_failed:
413 out->list_item(out, "failed-stonith-event", "%s", desc);
414 break;
415
416 default:
417 out->list_item(out, "pending-stonith-event", "%s", desc);
418 break;
419 }
420 g_free(desc);
421 return pcmk_rc_ok;
422}
423
424PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "bool", "bool",
425 "const char *", "uint32_t")
426static int
427stonith_event_text(pcmk__output_t *out, va_list args)
428{
429 stonith_history_t *event = va_arg(args, stonith_history_t *);
430 bool full_history = va_arg(args, int);
431 bool completed_only = va_arg(args, int);
432 const char *succeeded = va_arg(args, const char *);
433 uint32_t show_opts = va_arg(args, uint32_t);
434
435 if (completed_only) {
436 pcmk__formatted_printf(out, "%lld\n", (long long) event->completed);
437 } else {
438 gchar *desc = stonith__history_description(event, full_history, succeeded,
439 show_opts);
440
441 pcmk__indented_printf(out, "%s\n", desc);
442 g_free(desc);
443 }
444
445 return pcmk_rc_ok;
446}
447
448PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "bool", "bool",
449 "const char *", "uint32_t")
450static int
451stonith_event_xml(pcmk__output_t *out, va_list args)
452{
453 stonith_history_t *event = va_arg(args, stonith_history_t *);
454 bool full_history G_GNUC_UNUSED = va_arg(args, int);
455 bool completed_only G_GNUC_UNUSED = va_arg(args, int);
456 const char *succeeded G_GNUC_UNUSED = va_arg(args, const char *);
457 uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t);
458
459 xmlNodePtr node = NULL;
460
462 PCMK_XA_ACTION, event->action,
463 PCMK_XA_TARGET, event->target,
464 PCMK_XA_CLIENT, event->client,
465 PCMK_XA_ORIGIN, event->origin,
466 NULL);
467
468 switch (event->state) {
469 case st_failed:
472 PCMK_XA_EXIT_REASON, event->exit_reason,
473 NULL);
474 break;
475
476 case st_done:
478 break;
479
480 default: {
481 char *state = pcmk__itoa(event->state);
485 NULL);
486 free(state);
487 break;
488 }
489 }
490
491 if (event->delegate != NULL) {
492 crm_xml_add(node, PCMK_XA_DELEGATE, event->delegate);
493 }
494
495 if ((event->state == st_failed) || (event->state == st_done)) {
496 char *time_s = timespec_string(event->completed, event->completed_nsec,
497 true);
498
499 crm_xml_add(node, PCMK_XA_COMPLETED, time_s);
500 free(time_s);
501 }
502
503 return pcmk_rc_ok;
504}
505
506PCMK__OUTPUT_ARGS("validate", "const char *", "const char *", "const char *",
507 "const char *", "int")
508static int
509validate_agent_html(pcmk__output_t *out, va_list args) {
510 const char *agent = va_arg(args, const char *);
511 const char *device = va_arg(args, const char *);
512 const char *output = va_arg(args, const char *);
513 const char *error_output = va_arg(args, const char *);
514 int rc = va_arg(args, int);
515
516 if (device) {
517 char *buf = crm_strdup_printf("Validation of %s on %s %s", agent, device,
518 rc ? "failed" : "succeeded");
519 pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL, buf);
520 free(buf);
521 } else {
522 char *buf = crm_strdup_printf("Validation of %s %s", agent,
523 rc ? "failed" : "succeeded");
524 pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL, buf);
525 free(buf);
526 }
527
528 out->subprocess_output(out, rc, output, error_output);
529 return rc;
530}
531
532PCMK__OUTPUT_ARGS("validate", "const char *", "const char *", "const char *",
533 "const char *", "int")
534static int
535validate_agent_text(pcmk__output_t *out, va_list args) {
536 const char *agent = va_arg(args, const char *);
537 const char *device = va_arg(args, const char *);
538 const char *output = va_arg(args, const char *);
539 const char *error_output = va_arg(args, const char *);
540 int rc = va_arg(args, int);
541
542 if (device) {
543 pcmk__indented_printf(out, "Validation of %s on %s %s\n", agent, device,
544 rc ? "failed" : "succeeded");
545 } else {
546 pcmk__indented_printf(out, "Validation of %s %s\n", agent,
547 rc ? "failed" : "succeeded");
548 }
549
550 out->subprocess_output(out, rc, output, error_output);
551 return rc;
552}
553
554PCMK__OUTPUT_ARGS("validate", "const char *", "const char *", "const char *",
555 "const char *", "int")
556static int
557validate_agent_xml(pcmk__output_t *out, va_list args) {
558 const char *agent = va_arg(args, const char *);
559 const char *device = va_arg(args, const char *);
560 const char *output = va_arg(args, const char *);
561 const char *error_output = va_arg(args, const char *);
562 int rc = va_arg(args, int);
563
564 const char *valid = pcmk__btoa(rc == pcmk_ok);
565 xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_VALIDATE,
566 PCMK_XA_AGENT, agent,
567 PCMK_XA_VALID, valid,
568 NULL);
569
570 if (device != NULL) {
571 crm_xml_add(node, PCMK_XA_DEVICE, device);
572 }
573
575 out->subprocess_output(out, rc, output, error_output);
577
578 return rc;
579}
580
581static pcmk__message_entry_t fmt_functions[] = {
582 { "failed-fencing-list", "default", failed_history },
583 { "fencing-list", "default", stonith_history },
584 { "full-fencing-list", "default", full_history },
585 { "full-fencing-list", "xml", full_history_xml },
586 { "last-fenced", "html", last_fenced_html },
587 { "last-fenced", "log", last_fenced_text },
588 { "last-fenced", "text", last_fenced_text },
589 { "last-fenced", "xml", last_fenced_xml },
590 { "pending-fencing-list", "default", pending_actions },
591 { "stonith-event", "html", stonith_event_html },
592 { "stonith-event", "log", stonith_event_text },
593 { "stonith-event", "text", stonith_event_text },
594 { "stonith-event", "xml", stonith_event_xml },
595 { "validate", "html", validate_agent_html },
596 { "validate", "log", validate_agent_text },
597 { "validate", "text", validate_agent_text },
598 { "validate", "xml", validate_agent_xml },
599
600 { NULL, NULL, NULL }
601};
602
603void
Utility functions.
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:98
const char * stonith__later_succeeded(const stonith_history_t *event, const stonith_history_t *top_history)
Definition st_client.c:2265
ISO_8601 Date handling.
#define crm_time_log_timeofday
Definition iso8601.h:68
#define crm_time_usecs
Definition iso8601.h:76
#define crm_time_log_with_timezone
Definition iso8601.h:69
#define crm_time_log_date
Definition iso8601.h:67
char * pcmk__timespec2str(const struct timespec *ts, uint32_t flags)
Definition iso8601.c:2107
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition nvpair.c:301
#define PCMK_VALUE_SUCCESS
Definition options.h:213
#define PCMK_VALUE_PENDING
Definition options.h:190
#define PCMK_VALUE_FAILED
Definition options.h:151
Control output from tools.
@ pcmk_show_failed_detail
Definition output.h:67
#define pcmk_section_fencing_all
Definition output.h:46
Formatted output for pacemaker tools.
void pcmk__output_xml_pop_parent(pcmk__output_t *out)
Definition output_xml.c:564
void pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr parent)
Definition output_xml.c:549
void pcmk__register_messages(pcmk__output_t *out, const pcmk__message_entry_t *table)
Definition output.c:204
#define PCMK__OUTPUT_LIST_HEADER(out_obj, cond, retcode, title...)
xmlNodePtr pcmk__output_create_html_node(pcmk__output_t *out, const char *element_name, const char *id, const char *class_name, const char *text)
void pcmk__indented_printf(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
xmlNodePtr pcmk__output_create_xml_node(pcmk__output_t *out, const char *name,...) G_GNUC_NULL_TERMINATED
Definition output_xml.c:516
void void void pcmk__formatted_printf(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
#define PCMK__OUTPUT_ARGS(ARGS...)
#define PCMK__OUTPUT_LIST_FOOTER(out_obj, retcode)
const char * target
Definition pcmk_fence.c:29
@ pcmk_rc_no_output
Definition results.h:131
@ pcmk_rc_ok
Definition results.h:162
#define pcmk_ok
Definition results.h:69
enum crm_exit_e crm_exit_t
gchar * stonith__history_description(const stonith_history_t *history, bool full_history, const char *later_succeeded, uint32_t show_opts)
Definition st_output.c:89
void stonith__register_messages(pcmk__output_t *out)
Definition st_output.c:604
Fencing aka. STONITH.
const char * stonith_action_str(const char *action)
Turn fence action into a more readable string.
Definition st_client.c:2113
@ st_failed
Definition stonith-ng.h:72
@ st_done
Definition stonith-ng.h:70
gboolean pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags)
Definition strings.c:981
@ pcmk__str_star_matches
@ pcmk__str_casei
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1296
This structure contains everything that makes up a single output formatter.
struct stonith_history_s * next
Definition stonith-ng.h:107
Wrappers for and extensions to libxml2.
void pcmk__xe_set_props(xmlNodePtr node,...) G_GNUC_NULL_TERMINATED
Definition xml.c:2279
#define PCMK_XA_VALID
Definition xml_names.h:435
#define PCMK_XA_AGENT
Definition xml_names.h:229
#define PCMK_XA_TARGET
Definition xml_names.h:413
#define PCMK_XA_CLIENT
Definition xml_names.h:242
#define PCMK_XA_WHEN
Definition xml_names.h:445
#define PCMK_XA_STATUS
Definition xml_names.h:405
#define PCMK_XA_ORIGIN
Definition xml_names.h:346
#define PCMK_XA_EXTENDED_STATUS
Definition xml_names.h:276
#define PCMK_XE_FENCE_EVENT
Definition xml_names.h:111
#define PCMK_XA_DELEGATE
Definition xml_names.h:255
#define PCMK_XA_EXIT_REASON
Definition xml_names.h:269
#define PCMK_XE_LAST_FENCED
Definition xml_names.h:123
#define PCMK_XE_FENCE_HISTORY
Definition xml_names.h:112
#define PCMK_XA_ACTION
Definition xml_names.h:224
#define PCMK_XA_COMPLETED
Definition xml_names.h:245
#define PCMK_XE_VALIDATE
Definition xml_names.h:214
#define PCMK_XA_DEVICE
Definition xml_names.h:258
#define PCMK__XE_DIV