pacemaker 2.1.5-a3f44794f94
Scalable High-Availability cluster resource manager
st_output.c
Go to the documentation of this file.
1/*
2 * Copyright 2019-2022 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/msg_xml.h>
16#include <crm/common/iso8601.h>
17#include <crm/common/util.h>
18#include <crm/common/xml.h>
19#include <crm/common/output.h>
24
25static char *
26time_t_string(time_t when) {
27 crm_time_t *crm_when = crm_time_new(NULL);
28 char *buf = NULL;
29
30 crm_time_set_timet(crm_when, &when);
32 crm_time_free(crm_when);
33 return buf;
34}
35
46static const char *
47state_str(stonith_history_t *history)
48{
49 switch (history->state) {
50 case st_failed: return "failed";
51 case st_done: return "successful";
52 default: return "pending";
53 }
54}
55
73gchar *
75 const char *later_succeeded, uint32_t show_opts)
76{
77 GString *str = g_string_sized_new(256); // Generous starting size
78 char *completed_time = NULL;
79
80 if ((history->state == st_failed) || (history->state == st_done)) {
81 completed_time = time_t_string(history->completed);
82 }
83
85 stonith_action_str(history->action), " of ", history->target,
86 NULL);
87
88 if (!pcmk_is_set(show_opts, pcmk_show_failed_detail)) {
89 // More human-friendly
90 if (((history->state == st_failed) || (history->state == st_done))
91 && (history->delegate != NULL)) {
92
93 pcmk__g_strcat(str, " by ", history->delegate, NULL);
94 }
95 pcmk__g_strcat(str, " for ", history->client, "@", history->origin,
96 NULL);
97 if (!full_history) {
98 g_string_append(str, " last"); // For example, "last failed at ..."
99 }
100 }
101
102 pcmk__add_word(&str, 0, state_str(history));
103
104 // For failed actions, add exit reason if available
105 if ((history->state == st_failed) && (history->exit_reason != NULL)) {
106 pcmk__g_strcat(str, " (", history->exit_reason, ")", NULL);
107 }
108
109 if (pcmk_is_set(show_opts, pcmk_show_failed_detail)) {
110 // More technical
111 g_string_append(str, ": ");
112
113 // For completed actions, add delegate if available
114 if (((history->state == st_failed) || (history->state == st_done))
115 && (history->delegate != NULL)) {
116
117 pcmk__g_strcat(str, "delegate=", history->delegate, ", ", NULL);
118 }
119
120 // Add information about originator
121 pcmk__g_strcat(str,
122 "client=", history->client, ", origin=", history->origin,
123 NULL);
124
125 // For completed actions, add completion time
126 if (completed_time != NULL) {
127 if (full_history) {
128 g_string_append(str, ", completed");
129 } else if (history->state == st_failed) {
130 g_string_append(str, ", last-failed");
131 } else {
132 g_string_append(str, ", last-successful");
133 }
134 pcmk__g_strcat(str, "='", completed_time, "'", NULL);
135 }
136 } else { // More human-friendly
137 if (completed_time != NULL) {
138 pcmk__g_strcat(str, " at ", completed_time, NULL);
139 }
140 }
141
142 if ((history->state == st_failed) && (later_succeeded != NULL)) {
143 pcmk__g_strcat(str,
144 " (a later attempt from ", later_succeeded,
145 " succeeded)", NULL);
146 }
147
148 free(completed_time);
149 return g_string_free(str, FALSE);
150}
151
152PCMK__OUTPUT_ARGS("failed-fencing-list", "stonith_history_t *", "GList *",
153 "uint32_t", "uint32_t", "bool")
154static int
155failed_history(pcmk__output_t *out, va_list args)
156{
157 stonith_history_t *history = va_arg(args, stonith_history_t *);
158 GList *only_node = va_arg(args, GList *);
159 uint32_t section_opts = va_arg(args, uint32_t);
160 uint32_t show_opts = va_arg(args, uint32_t);
161 bool print_spacer = va_arg(args, int);
162
163 int rc = pcmk_rc_no_output;
164
165 for (stonith_history_t *hp = history; hp; hp = hp->next) {
166 if (hp->state != st_failed) {
167 continue;
168 }
169
170 if (!pcmk__str_in_list(hp->target, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
171 continue;
172 }
173
174 PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Failed Fencing Actions");
175 out->message(out, "stonith-event", hp,
176 pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
177 false, stonith__later_succeeded(hp, history), show_opts);
178 out->increment_list(out);
179 }
180
182 return rc;
183}
184
185PCMK__OUTPUT_ARGS("fencing-list", "stonith_history_t *", "GList *", "uint32_t",
186 "uint32_t", "bool")
187static int
188stonith_history(pcmk__output_t *out, va_list args)
189{
190 stonith_history_t *history = va_arg(args, stonith_history_t *);
191 GList *only_node = va_arg(args, GList *);
192 uint32_t section_opts = va_arg(args, uint32_t);
193 uint32_t show_opts = va_arg(args, uint32_t);
194 bool print_spacer = va_arg(args, int);
195
196 int rc = pcmk_rc_no_output;
197
198 for (stonith_history_t *hp = history; hp; hp = hp->next) {
199 if (!pcmk__str_in_list(hp->target, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
200 continue;
201 }
202
203 if (hp->state != st_failed) {
204 PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Fencing History");
205 out->message(out, "stonith-event", hp,
206 pcmk_all_flags_set(section_opts,
208 false, stonith__later_succeeded(hp, history), show_opts);
209 out->increment_list(out);
210 }
211 }
212
214 return rc;
215}
216
217PCMK__OUTPUT_ARGS("full-fencing-list", "crm_exit_t", "stonith_history_t *",
218 "GList *", "uint32_t", "uint32_t", "bool")
219static int
220full_history(pcmk__output_t *out, va_list args)
221{
222 crm_exit_t history_rc G_GNUC_UNUSED = va_arg(args, crm_exit_t);
223 stonith_history_t *history = va_arg(args, stonith_history_t *);
224 GList *only_node = va_arg(args, GList *);
225 uint32_t section_opts = va_arg(args, uint32_t);
226 uint32_t show_opts = va_arg(args, uint32_t);
227 bool print_spacer = va_arg(args, int);
228
229 int rc = pcmk_rc_no_output;
230
231 for (stonith_history_t *hp = history; hp; hp = hp->next) {
232 if (!pcmk__str_in_list(hp->target, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
233 continue;
234 }
235
236 PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Fencing History");
237 out->message(out, "stonith-event", hp,
238 pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
239 false, stonith__later_succeeded(hp, history), show_opts);
240 out->increment_list(out);
241 }
242
244 return rc;
245}
246
247PCMK__OUTPUT_ARGS("full-fencing-list", "crm_exit_t", "stonith_history_t *",
248 "GList *", "uint32_t", "uint32_t", "bool")
249static int
250full_history_xml(pcmk__output_t *out, va_list args)
251{
252 crm_exit_t history_rc = va_arg(args, crm_exit_t);
253 stonith_history_t *history = va_arg(args, stonith_history_t *);
254 GList *only_node = va_arg(args, GList *);
255 uint32_t section_opts = va_arg(args, uint32_t);
256 uint32_t show_opts = va_arg(args, uint32_t);
257 bool print_spacer G_GNUC_UNUSED = va_arg(args, int);
258
259 int rc = pcmk_rc_no_output;
260
261 if (history_rc == 0) {
262 for (stonith_history_t *hp = history; hp; hp = hp->next) {
263 if (!pcmk__str_in_list(hp->target, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
264 continue;
265 }
266
267 PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Fencing History");
268 out->message(out, "stonith-event", hp,
269 pcmk_all_flags_set(section_opts,
271 false, stonith__later_succeeded(hp, history), show_opts);
272 out->increment_list(out);
273 }
274
276 } else {
277 char *rc_s = pcmk__itoa(history_rc);
278
279 pcmk__output_create_xml_node(out, "fence_history",
280 "status", rc_s,
281 NULL);
282 free(rc_s);
283
284 rc = pcmk_rc_ok;
285 }
286
287 return rc;
288}
289
290PCMK__OUTPUT_ARGS("last-fenced", "const char *", "time_t")
291static int
292last_fenced_html(pcmk__output_t *out, va_list args) {
293 const char *target = va_arg(args, const char *);
294 time_t when = va_arg(args, time_t);
295
296 if (when) {
297 char *buf = crm_strdup_printf("Node %s last fenced at: %s", target, ctime(&when));
298 pcmk__output_create_html_node(out, "div", NULL, NULL, buf);
299 free(buf);
300 return pcmk_rc_ok;
301 } else {
302 return pcmk_rc_no_output;
303 }
304}
305
306PCMK__OUTPUT_ARGS("last-fenced", "const char *", "time_t")
307static int
308last_fenced_text(pcmk__output_t *out, va_list args) {
309 const char *target = va_arg(args, const char *);
310 time_t when = va_arg(args, time_t);
311
312 if (when) {
313 pcmk__indented_printf(out, "Node %s last fenced at: %s", target, ctime(&when));
314 } else {
315 pcmk__indented_printf(out, "Node %s has never been fenced\n", target);
316 }
317
318 return pcmk_rc_ok;
319}
320
321PCMK__OUTPUT_ARGS("last-fenced", "const char *", "time_t")
322static int
323last_fenced_xml(pcmk__output_t *out, va_list args) {
324 const char *target = va_arg(args, const char *);
325 time_t when = va_arg(args, time_t);
326
327 if (when) {
328 char *buf = time_t_string(when);
329
330 pcmk__output_create_xml_node(out, "last-fenced",
331 "target", target,
332 "when", buf,
333 NULL);
334
335 free(buf);
336 return pcmk_rc_ok;
337 } else {
338 return pcmk_rc_no_output;
339 }
340}
341
342PCMK__OUTPUT_ARGS("pending-fencing-list", "stonith_history_t *", "GList *",
343 "uint32_t", "uint32_t", "bool")
344static int
345pending_actions(pcmk__output_t *out, va_list args)
346{
347 stonith_history_t *history = va_arg(args, stonith_history_t *);
348 GList *only_node = va_arg(args, GList *);
349 uint32_t section_opts = va_arg(args, uint32_t);
350 uint32_t show_opts = va_arg(args, uint32_t);
351 bool print_spacer = va_arg(args, int);
352
353 int rc = pcmk_rc_no_output;
354
355 for (stonith_history_t *hp = history; hp; hp = hp->next) {
356 if (!pcmk__str_in_list(hp->target, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
357 continue;
358 }
359
360 /* Skip the rest of the history after we see a failed/done action */
361 if ((hp->state == st_failed) || (hp->state == st_done)) {
362 break;
363 }
364
365 PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Pending Fencing Actions");
366 out->message(out, "stonith-event", hp,
367 pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
368 false, stonith__later_succeeded(hp, history), show_opts);
369 out->increment_list(out);
370 }
371
373 return rc;
374}
375
376PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "bool", "bool",
377 "const char *", "uint32_t")
378static int
379stonith_event_html(pcmk__output_t *out, va_list args)
380{
381 stonith_history_t *event = va_arg(args, stonith_history_t *);
382 bool full_history = va_arg(args, int);
383 bool completed_only G_GNUC_UNUSED = va_arg(args, int);
384 const char *succeeded = va_arg(args, const char *);
385 uint32_t show_opts = va_arg(args, uint32_t);
386
387 gchar *desc = stonith__history_description(event, full_history, succeeded,
388 show_opts);
389
390 switch(event->state) {
391 case st_done:
392 out->list_item(out, "successful-stonith-event", "%s", desc);
393 break;
394
395 case st_failed:
396 out->list_item(out, "failed-stonith-event", "%s", desc);
397 break;
398
399 default:
400 out->list_item(out, "pending-stonith-event", "%s", desc);
401 break;
402 }
403 g_free(desc);
404 return pcmk_rc_ok;
405}
406
407PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "bool", "bool",
408 "const char *", "uint32_t")
409static int
410stonith_event_text(pcmk__output_t *out, va_list args)
411{
412 stonith_history_t *event = va_arg(args, stonith_history_t *);
413 bool full_history = va_arg(args, int);
414 bool completed_only = va_arg(args, int);
415 const char *succeeded = va_arg(args, const char *);
416 uint32_t show_opts = va_arg(args, uint32_t);
417
418 if (completed_only) {
419 pcmk__formatted_printf(out, "%lld\n", (long long) event->completed);
420 } else {
421 gchar *desc = stonith__history_description(event, full_history, succeeded,
422 show_opts);
423
424 pcmk__indented_printf(out, "%s\n", desc);
425 g_free(desc);
426 }
427
428 return pcmk_rc_ok;
429}
430
431PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "bool", "bool",
432 "const char *", "uint32_t")
433static int
434stonith_event_xml(pcmk__output_t *out, va_list args)
435{
436 stonith_history_t *event = va_arg(args, stonith_history_t *);
437 bool full_history G_GNUC_UNUSED = va_arg(args, int);
438 bool completed_only G_GNUC_UNUSED = va_arg(args, int);
439 const char *succeeded G_GNUC_UNUSED = va_arg(args, const char *);
440 uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t);
441
442 char *buf = NULL;
443
444 xmlNodePtr node = pcmk__output_create_xml_node(out, "fence_event",
445 "action", event->action,
446 "target", event->target,
447 "client", event->client,
448 "origin", event->origin,
449 NULL);
450
451 switch (event->state) {
452 case st_failed:
453 pcmk__xe_set_props(node, "status", "failed",
454 XML_LRM_ATTR_EXIT_REASON, event->exit_reason,
455 NULL);
456 break;
457
458 case st_done:
459 crm_xml_add(node, "status", "success");
460 break;
461
462 default: {
463 char *state = pcmk__itoa(event->state);
464 pcmk__xe_set_props(node, "status", "pending",
465 "extended-status", state,
466 NULL);
467 free(state);
468 break;
469 }
470 }
471
472 if (event->delegate != NULL) {
473 crm_xml_add(node, "delegate", event->delegate);
474 }
475
476 if (event->state == st_failed || event->state == st_done) {
477 buf = time_t_string(event->completed);
478 crm_xml_add(node, "completed", buf);
479 free(buf);
480 }
481
482 return pcmk_rc_ok;
483}
484
485PCMK__OUTPUT_ARGS("validate", "const char *", "const char *", "char *", "char *", "int")
486static int
487validate_agent_html(pcmk__output_t *out, va_list args) {
488 const char *agent = va_arg(args, const char *);
489 const char *device = va_arg(args, const char *);
490 char *output = va_arg(args, char *);
491 char *error_output = va_arg(args, char *);
492 int rc = va_arg(args, int);
493
494 if (device) {
495 char *buf = crm_strdup_printf("Validation of %s on %s %s", agent, device,
496 rc ? "failed" : "succeeded");
497 pcmk__output_create_html_node(out, "div", NULL, NULL, buf);
498 free(buf);
499 } else {
500 char *buf = crm_strdup_printf("Validation of %s %s", agent,
501 rc ? "failed" : "succeeded");
502 pcmk__output_create_html_node(out, "div", NULL, NULL, buf);
503 free(buf);
504 }
505
506 out->subprocess_output(out, rc, output, error_output);
507 return rc;
508}
509
510PCMK__OUTPUT_ARGS("validate", "const char *", "const char *", "char *", "char *", "int")
511static int
512validate_agent_text(pcmk__output_t *out, va_list args) {
513 const char *agent = va_arg(args, const char *);
514 const char *device = va_arg(args, const char *);
515 char *output = va_arg(args, char *);
516 char *error_output = va_arg(args, char *);
517 int rc = va_arg(args, int);
518
519 if (device) {
520 pcmk__indented_printf(out, "Validation of %s on %s %s\n", agent, device,
521 rc ? "failed" : "succeeded");
522 } else {
523 pcmk__indented_printf(out, "Validation of %s %s\n", agent,
524 rc ? "failed" : "succeeded");
525 }
526
527 out->subprocess_output(out, rc, output, error_output);
528 return rc;
529}
530
531PCMK__OUTPUT_ARGS("validate", "const char *", "const char *", "char *", "char *", "int")
532static int
533validate_agent_xml(pcmk__output_t *out, va_list args) {
534 const char *agent = va_arg(args, const char *);
535 const char *device = va_arg(args, const char *);
536 char *output = va_arg(args, char *);
537 char *error_output = va_arg(args, char *);
538 int rc = va_arg(args, int);
539
540 xmlNodePtr node = pcmk__output_create_xml_node(
541 out, "validate", "agent", agent, "valid", pcmk__btoa(rc == pcmk_ok),
542 NULL);
543
544 if (device != NULL) {
545 crm_xml_add(node, "device", device);
546 }
547
549 out->subprocess_output(out, rc, output, error_output);
551
552 return rc;
553}
554
555static pcmk__message_entry_t fmt_functions[] = {
556 { "failed-fencing-list", "default", failed_history },
557 { "fencing-list", "default", stonith_history },
558 { "full-fencing-list", "default", full_history },
559 { "full-fencing-list", "xml", full_history_xml },
560 { "last-fenced", "html", last_fenced_html },
561 { "last-fenced", "log", last_fenced_text },
562 { "last-fenced", "text", last_fenced_text },
563 { "last-fenced", "xml", last_fenced_xml },
564 { "pending-fencing-list", "default", pending_actions },
565 { "stonith-event", "html", stonith_event_html },
566 { "stonith-event", "log", stonith_event_text },
567 { "stonith-event", "text", stonith_event_text },
568 { "stonith-event", "xml", stonith_event_xml },
569 { "validate", "html", validate_agent_html },
570 { "validate", "log", validate_agent_text },
571 { "validate", "text", validate_agent_text },
572 { "validate", "xml", validate_agent_xml },
573
574 { NULL, NULL, NULL }
575};
576
577void
579 pcmk__register_messages(out, fmt_functions);
580}
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:121
const char * stonith__later_succeeded(stonith_history_t *event, stonith_history_t *top_history)
Definition: st_client.c:2248
ISO_8601 Date handling.
void crm_time_set_timet(crm_time_t *target, const time_t *source)
Definition: iso8601.c:1259
#define crm_time_log_timeofday
Definition: iso8601.h:68
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:140
char * crm_time_as_string(const crm_time_t *dt, int flags)
Definition: iso8601.c:500
#define crm_time_log_with_timezone
Definition: iso8601.h:69
#define crm_time_log_date
Definition: iso8601.h:67
crm_time_t * crm_time_new(const char *string)
Definition: iso8601.c:92
struct crm_time_s crm_time_t
Definition: iso8601.h:32
#define XML_LRM_ATTR_EXIT_REASON
Definition: msg_xml.h:318
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:323
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:513
void pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr parent)
Definition: output_xml.c:500
void pcmk__register_messages(pcmk__output_t *out, const pcmk__message_entry_t *table)
Definition: output.c:161
#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)
Definition: output_html.c:433
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:469
void void void pcmk__formatted_printf(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
#define PCMK__OUTPUT_LIST_FOOTER(out_obj, retcode)
const char * target
Definition: pcmk_fence.c:29
@ pcmk_rc_no_output
Definition: results.h:118
@ pcmk_rc_ok
Definition: results.h:148
#define pcmk_ok
Definition: results.h:68
enum crm_exit_e crm_exit_t
PCMK__OUTPUT_ARGS("failed-fencing-list", "stonith_history_t *", "GList *", "uint32_t", "uint32_t", "bool")
Definition: st_output.c:152
gchar * stonith__history_description(stonith_history_t *history, bool full_history, const char *later_succeeded, uint32_t show_opts)
Definition: st_output.c:74
void stonith__register_messages(pcmk__output_t *out)
Definition: st_output.c:578
Fencing aka. STONITH.
const char * stonith_action_str(const char *action)
Turn fence action into a more readable string.
Definition: st_client.c:2096
@ st_failed
Definition: stonith-ng.h:77
@ st_done
Definition: stonith-ng.h:75
gboolean pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags)
Definition: strings.c:883
@ pcmk__str_star_matches
@ pcmk__str_casei
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1214
This structure contains everything that makes up a single output formatter.
struct stonith_history_s * next
Definition: stonith-ng.h:112
Wrappers for and extensions to libxml2.
void pcmk__xe_set_props(xmlNodePtr node,...) G_GNUC_NULL_TERMINATED
Definition: xml.c:3103