pacemaker  2.1.5-b7adf64e51
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>
22 #include <crm/fencing/internal.h>
23 #include <crm/pengine/internal.h>
24 
25 static char *
26 time_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 
46 static const char *
47 state_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 
73 gchar *
74 stonith__history_description(stonith_history_t *history, bool full_history,
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 
84  pcmk__g_strcat(str,
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 
152 PCMK__OUTPUT_ARGS("failed-fencing-list", "stonith_history_t *", "GList *",
153  "uint32_t", "uint32_t", "bool")
154 static int
155 failed_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 
181  PCMK__OUTPUT_LIST_FOOTER(out, rc);
182  return rc;
183 }
184 
185 PCMK__OUTPUT_ARGS("fencing-list", "stonith_history_t *", "GList *", "uint32_t",
186  "uint32_t", "bool")
187 static int
188 stonith_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 
213  PCMK__OUTPUT_LIST_FOOTER(out, rc);
214  return rc;
215 }
216 
217 PCMK__OUTPUT_ARGS("full-fencing-list", "crm_exit_t", "stonith_history_t *",
218  "GList *", "uint32_t", "uint32_t", "bool")
219 static int
220 full_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 
243  PCMK__OUTPUT_LIST_FOOTER(out, rc);
244  return rc;
245 }
246 
247 PCMK__OUTPUT_ARGS("full-fencing-list", "crm_exit_t", "stonith_history_t *",
248  "GList *", "uint32_t", "uint32_t", "bool")
249 static int
250 full_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 
275  PCMK__OUTPUT_LIST_FOOTER(out, rc);
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 
290 PCMK__OUTPUT_ARGS("last-fenced", "const char *", "time_t")
291 static int
292 last_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 
306 PCMK__OUTPUT_ARGS("last-fenced", "const char *", "time_t")
307 static int
308 last_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 
321 PCMK__OUTPUT_ARGS("last-fenced", "const char *", "time_t")
322 static int
323 last_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 
342 PCMK__OUTPUT_ARGS("pending-fencing-list", "stonith_history_t *", "GList *",
343  "uint32_t", "uint32_t", "bool")
344 static int
345 pending_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 
372  PCMK__OUTPUT_LIST_FOOTER(out, rc);
373  return rc;
374 }
375 
376 PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "bool", "bool",
377  "const char *", "uint32_t")
378 static int
379 stonith_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 
407 PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "bool", "bool",
408  "const char *", "uint32_t")
409 static int
410 stonith_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 
431 PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "bool", "bool",
432  "const char *", "uint32_t")
433 static int
434 stonith_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 
485 PCMK__OUTPUT_ARGS("validate", "const char *", "const char *", "char *", "char *", "int")
486 static int
487 validate_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 
510 PCMK__OUTPUT_ARGS("validate", "const char *", "const char *", "char *", "char *", "int")
511 static int
512 validate_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 
531 PCMK__OUTPUT_ARGS("validate", "const char *", "const char *", "char *", "char *", "int")
532 static int
533 validate_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 
548  pcmk__output_xml_push_parent(out, node);
549  out->subprocess_output(out, rc, output, error_output);
551 
552  return rc;
553 }
554 
555 static 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 
577 void
579  pcmk__register_messages(out, fmt_functions);
580 }
void pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr parent)
Definition: output_xml.c:500
struct stonith_history_s * next
Definition: stonith-ng.h:112
Control output from tools.
void pcmk__register_messages(pcmk__output_t *out, const pcmk__message_entry_t *table)
Definition: output.c:161
#define crm_time_log_timeofday
Definition: iso8601.h:68
struct crm_time_s crm_time_t
Definition: iso8601.h:32
const char * stonith__later_succeeded(stonith_history_t *event, stonith_history_t *top_history)
Definition: st_client.c:2248
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
enum crm_exit_e crm_exit_t
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 void void pcmk__formatted_printf(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
char * crm_time_as_string(const crm_time_t *dt, int flags)
Definition: iso8601.c:500
Formatted output for pacemaker tools.
void crm_time_set_timet(crm_time_t *target, const time_t *source)
Definition: iso8601.c:1259
const char * stonith_action_str(const char *action)
Turn fence action into a more readable string.
Definition: st_client.c:2096
Utility functions.
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1214
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
void stonith__register_messages(pcmk__output_t *out)
Definition: st_output.c:578
Wrappers for and extensions to libxml2.
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 pcmk__indented_printf(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
ISO_8601 Date handling.
xmlNodePtr pcmk__output_create_xml_node(pcmk__output_t *out, const char *name,...) G_GNUC_NULL_TERMINATED
Definition: output_xml.c:469
#define XML_LRM_ATTR_EXIT_REASON
Definition: msg_xml.h:318
#define pcmk_section_fencing_all
Definition: output.h:46
#define crm_time_log_with_timezone
Definition: iso8601.h:69
void pcmk__output_xml_pop_parent(pcmk__output_t *out)
Definition: output_xml.c:513
void pcmk__xe_set_props(xmlNodePtr node,...) G_GNUC_NULL_TERMINATED
Definition: xml.c:3103
#define PCMK__OUTPUT_LIST_FOOTER(out_obj, retcode)
const char * target
Definition: pcmk_fence.c:29
#define PCMK__OUTPUT_LIST_HEADER(out_obj, cond, retcode, title...)
crm_time_t * crm_time_new(const char *string)
Definition: iso8601.c:92
PCMK__OUTPUT_ARGS("failed-fencing-list", "stonith_history_t *", "GList *", "uint32_t", "uint32_t", "bool")
Definition: st_output.c:152
Fencing aka. STONITH.
This structure contains everything that makes up a single output formatter.
#define pcmk_ok
Definition: results.h:68
#define crm_time_log_date
Definition: iso8601.h:67
gboolean pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags)
Definition: strings.c:883
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:140