pacemaker  2.1.7-0f7f88312f
Scalable High-Availability cluster resource manager
pe_output.c
Go to the documentation of this file.
1 /*
2  * Copyright 2019-2023 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 
12 #include <stdint.h>
13 
15 #include <crm/common/output.h>
17 #include <crm/cib/util.h>
18 #include <crm/msg_xml.h>
19 #include <crm/pengine/internal.h>
20 
21 const char *
22 pe__resource_description(const pcmk_resource_t *rsc, uint32_t show_opts)
23 {
24  const char * desc = NULL;
25  // User-supplied description
26  if (pcmk_any_flags_set(show_opts, pcmk_show_rsc_only|pcmk_show_description)) {
27  desc = crm_element_value(rsc->xml, XML_ATTR_DESC);
28  }
29  return desc;
30 }
31 
32 /* Never display node attributes whose name starts with one of these prefixes */
33 #define FILTER_STR { PCMK__FAIL_COUNT_PREFIX, PCMK__LAST_FAILURE_PREFIX, \
34  "shutdown", PCMK_NODE_ATTR_TERMINATE, "standby", "#", \
35  NULL }
36 
37 static int
38 compare_attribute(gconstpointer a, gconstpointer b)
39 {
40  int rc;
41 
42  rc = strcmp((const char *)a, (const char *)b);
43 
44  return rc;
45 }
46 
62 static bool
63 add_extra_info(const pcmk_node_t *node, GList *rsc_list,
64  pcmk_scheduler_t *scheduler, const char *attrname,
65  int *expected_score)
66 {
67  GList *gIter = NULL;
68 
69  for (gIter = rsc_list; gIter != NULL; gIter = gIter->next) {
70  pcmk_resource_t *rsc = (pcmk_resource_t *) gIter->data;
71  const char *type = g_hash_table_lookup(rsc->meta, "type");
72  const char *name = NULL;
73  GHashTable *params = NULL;
74 
75  if (rsc->children != NULL) {
76  if (add_extra_info(node, rsc->children, scheduler, attrname,
77  expected_score)) {
78  return true;
79  }
80  }
81 
82  if (!pcmk__strcase_any_of(type, "ping", "pingd", NULL)) {
83  continue;
84  }
85 
86  params = pe_rsc_params(rsc, node, scheduler);
87  name = g_hash_table_lookup(params, "name");
88 
89  if (name == NULL) {
90  name = "pingd";
91  }
92 
93  /* To identify the resource with the attribute name. */
94  if (pcmk__str_eq(name, attrname, pcmk__str_casei)) {
95  int host_list_num = 0;
96  const char *hosts = g_hash_table_lookup(params, "host_list");
97  const char *multiplier = g_hash_table_lookup(params, "multiplier");
98  int multiplier_i;
99 
100  if (hosts) {
101  char **host_list = g_strsplit(hosts, " ", 0);
102  host_list_num = g_strv_length(host_list);
103  g_strfreev(host_list);
104  }
105 
106  if ((multiplier == NULL)
107  || (pcmk__scan_min_int(multiplier, &multiplier_i,
108  INT_MIN) != pcmk_rc_ok)) {
109  /* The ocf:pacemaker:ping resource agent defaults multiplier to
110  * 1. The agent currently does not handle invalid text, but it
111  * should, and this would be a reasonable choice ...
112  */
113  multiplier_i = 1;
114  }
115  *expected_score = host_list_num * multiplier_i;
116 
117  return true;
118  }
119  }
120  return false;
121 }
122 
123 static GList *
124 filter_attr_list(GList *attr_list, char *name)
125 {
126  int i;
127  const char *filt_str[] = FILTER_STR;
128 
129  CRM_CHECK(name != NULL, return attr_list);
130 
131  /* filtering automatic attributes */
132  for (i = 0; filt_str[i] != NULL; i++) {
133  if (g_str_has_prefix(name, filt_str[i])) {
134  return attr_list;
135  }
136  }
137 
138  return g_list_insert_sorted(attr_list, name, compare_attribute);
139 }
140 
141 static GList *
142 get_operation_list(xmlNode *rsc_entry) {
143  GList *op_list = NULL;
144  xmlNode *rsc_op = NULL;
145 
146  for (rsc_op = pcmk__xe_first_child(rsc_entry); rsc_op != NULL;
147  rsc_op = pcmk__xe_next(rsc_op)) {
148  const char *task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK);
149  const char *interval_ms_s = crm_element_value(rsc_op,
151  const char *op_rc = crm_element_value(rsc_op, XML_LRM_ATTR_RC);
152  int op_rc_i;
153 
154  pcmk__scan_min_int(op_rc, &op_rc_i, 0);
155 
156  /* Display 0-interval monitors as "probe" */
157  if (pcmk__str_eq(task, PCMK_ACTION_MONITOR, pcmk__str_casei)
158  && pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches | pcmk__str_casei)) {
159  task = "probe";
160  }
161 
162  /* Ignore notifies and some probes */
163  if (pcmk__str_eq(task, PCMK_ACTION_NOTIFY, pcmk__str_none)
164  || (pcmk__str_eq(task, "probe", pcmk__str_none)
165  && (op_rc_i == CRM_EX_NOT_RUNNING))) {
166  continue;
167  }
168 
169  if (pcmk__str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, pcmk__str_none)) {
170  op_list = g_list_append(op_list, rsc_op);
171  }
172  }
173 
174  op_list = g_list_sort(op_list, sort_op_by_callid);
175  return op_list;
176 }
177 
178 static void
179 add_dump_node(gpointer key, gpointer value, gpointer user_data)
180 {
181  xmlNodePtr node = user_data;
182  pcmk_create_xml_text_node(node, (const char *) key, (const char *) value);
183 }
184 
185 static void
186 append_dump_text(gpointer key, gpointer value, gpointer user_data)
187 {
188  char **dump_text = user_data;
189  char *new_text = crm_strdup_printf("%s %s=%s",
190  *dump_text, (char *)key, (char *)value);
191 
192  free(*dump_text);
193  *dump_text = new_text;
194 }
195 
196 static const char *
197 get_cluster_stack(pcmk_scheduler_t *scheduler)
198 {
199  xmlNode *stack = get_xpath_object("//nvpair[@name='cluster-infrastructure']",
200  scheduler->input, LOG_DEBUG);
201  return stack? crm_element_value(stack, XML_NVPAIR_ATTR_VALUE) : "unknown";
202 }
203 
204 static char *
205 last_changed_string(const char *last_written, const char *user,
206  const char *client, const char *origin) {
207  if (last_written != NULL || user != NULL || client != NULL || origin != NULL) {
208  return crm_strdup_printf("%s%s%s%s%s%s%s",
209  last_written ? last_written : "",
210  user ? " by " : "",
211  user ? user : "",
212  client ? " via " : "",
213  client ? client : "",
214  origin ? " on " : "",
215  origin ? origin : "");
216  } else {
217  return strdup("");
218  }
219 }
220 
221 static char *
222 op_history_string(xmlNode *xml_op, const char *task, const char *interval_ms_s,
223  int rc, bool print_timing) {
224  const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID);
225  char *interval_str = NULL;
226  char *buf = NULL;
227 
228  if (interval_ms_s && !pcmk__str_eq(interval_ms_s, "0", pcmk__str_casei)) {
229  char *pair = pcmk__format_nvpair("interval", interval_ms_s, "ms");
230  interval_str = crm_strdup_printf(" %s", pair);
231  free(pair);
232  }
233 
234  if (print_timing) {
235  char *last_change_str = NULL;
236  char *exec_str = NULL;
237  char *queue_str = NULL;
238 
239  const char *value = NULL;
240 
241  time_t epoch = 0;
242 
243  if ((crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE, &epoch) == pcmk_ok)
244  && (epoch > 0)) {
245  char *epoch_str = pcmk__epoch2str(&epoch, 0);
246 
247  last_change_str = crm_strdup_printf(" %s=\"%s\"",
249  pcmk__s(epoch_str, ""));
250  free(epoch_str);
251  }
252 
253  value = crm_element_value(xml_op, XML_RSC_OP_T_EXEC);
254  if (value) {
255  char *pair = pcmk__format_nvpair(XML_RSC_OP_T_EXEC, value, "ms");
256  exec_str = crm_strdup_printf(" %s", pair);
257  free(pair);
258  }
259 
260  value = crm_element_value(xml_op, XML_RSC_OP_T_QUEUE);
261  if (value) {
262  char *pair = pcmk__format_nvpair(XML_RSC_OP_T_QUEUE, value, "ms");
263  queue_str = crm_strdup_printf(" %s", pair);
264  free(pair);
265  }
266 
267  buf = crm_strdup_printf("(%s) %s:%s%s%s%s rc=%d (%s)", call, task,
268  interval_str ? interval_str : "",
269  last_change_str ? last_change_str : "",
270  exec_str ? exec_str : "",
271  queue_str ? queue_str : "",
272  rc, services_ocf_exitcode_str(rc));
273 
274  if (last_change_str) {
275  free(last_change_str);
276  }
277 
278  if (exec_str) {
279  free(exec_str);
280  }
281 
282  if (queue_str) {
283  free(queue_str);
284  }
285  } else {
286  buf = crm_strdup_printf("(%s) %s%s%s", call, task,
287  interval_str ? ":" : "",
288  interval_str ? interval_str : "");
289  }
290 
291  if (interval_str) {
292  free(interval_str);
293  }
294 
295  return buf;
296 }
297 
298 static char *
299 resource_history_string(pcmk_resource_t *rsc, const char *rsc_id, bool all,
300  int failcount, time_t last_failure) {
301  char *buf = NULL;
302 
303  if (rsc == NULL) {
304  buf = crm_strdup_printf("%s: orphan", rsc_id);
305  } else if (all || failcount || last_failure > 0) {
306  char *failcount_s = NULL;
307  char *lastfail_s = NULL;
308 
309  if (failcount > 0) {
310  failcount_s = crm_strdup_printf(" %s=%d", PCMK__FAIL_COUNT_PREFIX,
311  failcount);
312  } else {
313  failcount_s = strdup("");
314  }
315  if (last_failure > 0) {
316  buf = pcmk__epoch2str(&last_failure, 0);
317  lastfail_s = crm_strdup_printf(" %s='%s'",
319  free(buf);
320  }
321 
322  buf = crm_strdup_printf("%s: migration-threshold=%d%s%s",
323  rsc_id, rsc->migration_threshold, failcount_s,
324  lastfail_s? lastfail_s : "");
325  free(failcount_s);
326  free(lastfail_s);
327  } else {
328  buf = crm_strdup_printf("%s:", rsc_id);
329  }
330 
331  return buf;
332 }
333 
344 static const char *
345 get_node_feature_set(const pcmk_node_t *node)
346 {
347  if (node->details->online && node->details->expected_up
348  && !pe__is_guest_or_remote_node(node)) {
349 
350  const char *feature_set = g_hash_table_lookup(node->details->attrs,
352 
353  /* The feature set attribute is present since 3.15.1. If it is missing,
354  * then the node must be running an earlier version.
355  */
356  return pcmk__s(feature_set, "<3.15.1");
357  }
358  return NULL;
359 }
360 
361 static bool
362 is_mixed_version(pcmk_scheduler_t *scheduler)
363 {
364  const char *feature_set = NULL;
365  for (GList *gIter = scheduler->nodes; gIter != NULL; gIter = gIter->next) {
366  pcmk_node_t *node = gIter->data;
367  const char *node_feature_set = get_node_feature_set(node);
368  if (node_feature_set != NULL) {
369  if (feature_set == NULL) {
370  feature_set = node_feature_set;
371  } else if (strcmp(feature_set, node_feature_set) != 0) {
372  return true;
373  }
374  }
375  }
376  return false;
377 }
378 
379 static char *
380 formatted_xml_buf(const pcmk_resource_t *rsc, bool raw)
381 {
382  if (raw) {
383  return dump_xml_formatted(rsc->orig_xml ? rsc->orig_xml : rsc->xml);
384  } else {
385  return dump_xml_formatted(rsc->xml);
386  }
387 }
388 
389 PCMK__OUTPUT_ARGS("cluster-summary", "pcmk_scheduler_t *",
390  "enum pcmk_pacemakerd_state", "uint32_t", "uint32_t")
391 static int
392 cluster_summary(pcmk__output_t *out, va_list args) {
393  pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
394  enum pcmk_pacemakerd_state pcmkd_state =
395  (enum pcmk_pacemakerd_state) va_arg(args, int);
396  uint32_t section_opts = va_arg(args, uint32_t);
397  uint32_t show_opts = va_arg(args, uint32_t);
398 
399  int rc = pcmk_rc_no_output;
400  const char *stack_s = get_cluster_stack(scheduler);
401 
402  if (pcmk_is_set(section_opts, pcmk_section_stack)) {
403  PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
404  out->message(out, "cluster-stack", stack_s, pcmkd_state);
405  }
406 
407  if (pcmk_is_set(section_opts, pcmk_section_dc)) {
408  xmlNode *dc_version = get_xpath_object("//nvpair[@name='dc-version']",
409  scheduler->input, LOG_DEBUG);
410  const char *dc_version_s = dc_version?
412  : NULL;
413  const char *quorum = crm_element_value(scheduler->input,
415  char *dc_name = scheduler->dc_node? pe__node_display_name(scheduler->dc_node, pcmk_is_set(show_opts, pcmk_show_node_id)) : NULL;
416  bool mixed_version = is_mixed_version(scheduler);
417 
418  PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
419  out->message(out, "cluster-dc", scheduler->dc_node, quorum,
420  dc_version_s, dc_name, mixed_version);
421  free(dc_name);
422  }
423 
424  if (pcmk_is_set(section_opts, pcmk_section_times)) {
425  const char *last_written = crm_element_value(scheduler->input,
427  const char *user = crm_element_value(scheduler->input,
429  const char *client = crm_element_value(scheduler->input,
431  const char *origin = crm_element_value(scheduler->input,
433 
434  PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
435  out->message(out, "cluster-times",
436  scheduler->localhost, last_written, user, client, origin);
437  }
438 
439  if (pcmk_is_set(section_opts, pcmk_section_counts)) {
440  PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
441  out->message(out, "cluster-counts", g_list_length(scheduler->nodes),
444  }
445 
446  if (pcmk_is_set(section_opts, pcmk_section_options)) {
447  PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
448  out->message(out, "cluster-options", scheduler);
449  }
450 
451  PCMK__OUTPUT_LIST_FOOTER(out, rc);
452 
453  if (pcmk_is_set(section_opts, pcmk_section_maint_mode)) {
454  if (out->message(out, "maint-mode", scheduler->flags) == pcmk_rc_ok) {
455  rc = pcmk_rc_ok;
456  }
457  }
458 
459  return rc;
460 }
461 
462 PCMK__OUTPUT_ARGS("cluster-summary", "pcmk_scheduler_t *",
463  "enum pcmk_pacemakerd_state", "uint32_t", "uint32_t")
464 static int
465 cluster_summary_html(pcmk__output_t *out, va_list args) {
466  pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
467  enum pcmk_pacemakerd_state pcmkd_state =
468  (enum pcmk_pacemakerd_state) va_arg(args, int);
469  uint32_t section_opts = va_arg(args, uint32_t);
470  uint32_t show_opts = va_arg(args, uint32_t);
471 
472  int rc = pcmk_rc_no_output;
473  const char *stack_s = get_cluster_stack(scheduler);
474 
475  if (pcmk_is_set(section_opts, pcmk_section_stack)) {
476  PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
477  out->message(out, "cluster-stack", stack_s, pcmkd_state);
478  }
479 
480  /* Always print DC if none, even if not requested */
481  if ((scheduler->dc_node == NULL)
482  || pcmk_is_set(section_opts, pcmk_section_dc)) {
483  xmlNode *dc_version = get_xpath_object("//nvpair[@name='dc-version']",
484  scheduler->input, LOG_DEBUG);
485  const char *dc_version_s = dc_version?
487  : NULL;
488  const char *quorum = crm_element_value(scheduler->input,
490  char *dc_name = scheduler->dc_node? pe__node_display_name(scheduler->dc_node, pcmk_is_set(show_opts, pcmk_show_node_id)) : NULL;
491  bool mixed_version = is_mixed_version(scheduler);
492 
493  PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
494  out->message(out, "cluster-dc", scheduler->dc_node, quorum,
495  dc_version_s, dc_name, mixed_version);
496  free(dc_name);
497  }
498 
499  if (pcmk_is_set(section_opts, pcmk_section_times)) {
500  const char *last_written = crm_element_value(scheduler->input,
502  const char *user = crm_element_value(scheduler->input,
504  const char *client = crm_element_value(scheduler->input,
506  const char *origin = crm_element_value(scheduler->input,
508 
509  PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
510  out->message(out, "cluster-times",
511  scheduler->localhost, last_written, user, client, origin);
512  }
513 
514  if (pcmk_is_set(section_opts, pcmk_section_counts)) {
515  PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
516  out->message(out, "cluster-counts", g_list_length(scheduler->nodes),
519  }
520 
521  if (pcmk_is_set(section_opts, pcmk_section_options)) {
522  /* Kind of a hack - close the list we may have opened earlier in this
523  * function so we can put all the options into their own list. We
524  * only want to do this on HTML output, though.
525  */
526  PCMK__OUTPUT_LIST_FOOTER(out, rc);
527 
528  out->begin_list(out, NULL, NULL, "Config Options");
529  out->message(out, "cluster-options", scheduler);
530  }
531 
532  PCMK__OUTPUT_LIST_FOOTER(out, rc);
533 
534  if (pcmk_is_set(section_opts, pcmk_section_maint_mode)) {
535  if (out->message(out, "maint-mode", scheduler->flags) == pcmk_rc_ok) {
536  rc = pcmk_rc_ok;
537  }
538  }
539 
540  return rc;
541 }
542 
543 char *
544 pe__node_display_name(pcmk_node_t *node, bool print_detail)
545 {
546  char *node_name;
547  const char *node_host = NULL;
548  const char *node_id = NULL;
549  int name_len;
550 
551  CRM_ASSERT((node != NULL) && (node->details != NULL) && (node->details->uname != NULL));
552 
553  /* Host is displayed only if this is a guest node and detail is requested */
554  if (print_detail && pe__is_guest_node(node)) {
555  const pcmk_resource_t *container = node->details->remote_rsc->container;
556  const pcmk_node_t *host_node = pe__current_node(container);
557 
558  if (host_node && host_node->details) {
559  node_host = host_node->details->uname;
560  }
561  if (node_host == NULL) {
562  node_host = ""; /* so we at least get "uname@" to indicate guest */
563  }
564  }
565 
566  /* Node ID is displayed if different from uname and detail is requested */
567  if (print_detail && !pcmk__str_eq(node->details->uname, node->details->id, pcmk__str_casei)) {
568  node_id = node->details->id;
569  }
570 
571  /* Determine name length */
572  name_len = strlen(node->details->uname) + 1;
573  if (node_host) {
574  name_len += strlen(node_host) + 1; /* "@node_host" */
575  }
576  if (node_id) {
577  name_len += strlen(node_id) + 3; /* + " (node_id)" */
578  }
579 
580  /* Allocate and populate display name */
581  node_name = malloc(name_len);
582  CRM_ASSERT(node_name != NULL);
583  strcpy(node_name, node->details->uname);
584  if (node_host) {
585  strcat(node_name, "@");
586  strcat(node_name, node_host);
587  }
588  if (node_id) {
589  strcat(node_name, " (");
590  strcat(node_name, node_id);
591  strcat(node_name, ")");
592  }
593  return node_name;
594 }
595 
596 int
597 pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name
598  , size_t pairs_count, ...)
599 {
600  xmlNodePtr xml_node = NULL;
601  va_list args;
602 
603  CRM_ASSERT(tag_name != NULL);
604 
605  xml_node = pcmk__output_xml_peek_parent(out);
606  CRM_ASSERT(xml_node != NULL);
607  xml_node = create_xml_node(xml_node, tag_name);
608 
609  va_start(args, pairs_count);
610  while(pairs_count--) {
611  const char *param_name = va_arg(args, const char *);
612  const char *param_value = va_arg(args, const char *);
613  if (param_name && param_value) {
614  crm_xml_add(xml_node, param_name, param_value);
615  }
616  };
617  va_end(args);
618 
619  if (is_list) {
620  pcmk__output_xml_push_parent(out, xml_node);
621  }
622  return pcmk_rc_ok;
623 }
624 
625 static const char *
626 role_desc(enum rsc_role_e role)
627 {
628  if (role == pcmk_role_promoted) {
629 #ifdef PCMK__COMPAT_2_0
630  return "as " PCMK__ROLE_PROMOTED_LEGACY " ";
631 #else
632  return "in " PCMK__ROLE_PROMOTED " role ";
633 #endif
634  }
635  return "";
636 }
637 
638 PCMK__OUTPUT_ARGS("ban", "pcmk_node_t *", "pe__location_t *", "uint32_t")
639 static int
640 ban_html(pcmk__output_t *out, va_list args) {
641  pcmk_node_t *pe_node = va_arg(args, pcmk_node_t *);
642  pe__location_t *location = va_arg(args, pe__location_t *);
643  uint32_t show_opts = va_arg(args, uint32_t);
644 
645  char *node_name = pe__node_display_name(pe_node,
646  pcmk_is_set(show_opts, pcmk_show_node_id));
647  char *buf = crm_strdup_printf("%s\tprevents %s from running %son %s",
648  location->id, location->rsc_lh->id,
649  role_desc(location->role_filter), node_name);
650 
651  pcmk__output_create_html_node(out, "li", NULL, NULL, buf);
652 
653  free(node_name);
654  free(buf);
655  return pcmk_rc_ok;
656 }
657 
658 PCMK__OUTPUT_ARGS("ban", "pcmk_node_t *", "pe__location_t *", "uint32_t")
659 static int
660 ban_text(pcmk__output_t *out, va_list args) {
661  pcmk_node_t *pe_node = va_arg(args, pcmk_node_t *);
662  pe__location_t *location = va_arg(args, pe__location_t *);
663  uint32_t show_opts = va_arg(args, uint32_t);
664 
665  char *node_name = pe__node_display_name(pe_node,
666  pcmk_is_set(show_opts, pcmk_show_node_id));
667  out->list_item(out, NULL, "%s\tprevents %s from running %son %s",
668  location->id, location->rsc_lh->id,
669  role_desc(location->role_filter), node_name);
670 
671  free(node_name);
672  return pcmk_rc_ok;
673 }
674 
675 PCMK__OUTPUT_ARGS("ban", "pcmk_node_t *", "pe__location_t *", "uint32_t")
676 static int
677 ban_xml(pcmk__output_t *out, va_list args) {
678  pcmk_node_t *pe_node = va_arg(args, pcmk_node_t *);
679  pe__location_t *location = va_arg(args, pe__location_t *);
680  uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t);
681 
682  const char *promoted_only = pcmk__btoa(location->role_filter == pcmk_role_promoted);
683  char *weight_s = pcmk__itoa(pe_node->weight);
684 
685  pcmk__output_create_xml_node(out, "ban",
686  "id", location->id,
687  "resource", location->rsc_lh->id,
688  "node", pe_node->details->uname,
689  "weight", weight_s,
690  "promoted-only", promoted_only,
691  /* This is a deprecated alias for
692  * promoted_only. Removing it will break
693  * backward compatibility of the API schema,
694  * which will require an API schema major
695  * version bump.
696  */
697  "master_only", promoted_only,
698  NULL);
699 
700  free(weight_s);
701  return pcmk_rc_ok;
702 }
703 
704 PCMK__OUTPUT_ARGS("ban-list", "pcmk_scheduler_t *", "const char *", "GList *",
705  "uint32_t", "bool")
706 static int
707 ban_list(pcmk__output_t *out, va_list args) {
708  pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
709  const char *prefix = va_arg(args, const char *);
710  GList *only_rsc = va_arg(args, GList *);
711  uint32_t show_opts = va_arg(args, uint32_t);
712  bool print_spacer = va_arg(args, int);
713 
714  GList *gIter, *gIter2;
715  int rc = pcmk_rc_no_output;
716 
717  /* Print each ban */
718  for (gIter = scheduler->placement_constraints;
719  gIter != NULL; gIter = gIter->next) {
720  pe__location_t *location = gIter->data;
721  const pcmk_resource_t *rsc = location->rsc_lh;
722 
723  if (prefix != NULL && !g_str_has_prefix(location->id, prefix)) {
724  continue;
725  }
726 
727  if (!pcmk__str_in_list(rsc_printable_id(rsc), only_rsc,
730  only_rsc, pcmk__str_star_matches)) {
731  continue;
732  }
733 
734  for (gIter2 = location->node_list_rh; gIter2 != NULL; gIter2 = gIter2->next) {
735  pcmk_node_t *node = (pcmk_node_t *) gIter2->data;
736 
737  if (node->weight < 0) {
738  PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Negative Location Constraints");
739  out->message(out, "ban", node, location, show_opts);
740  }
741  }
742  }
743 
744  PCMK__OUTPUT_LIST_FOOTER(out, rc);
745  return rc;
746 }
747 
748 PCMK__OUTPUT_ARGS("cluster-counts", "unsigned int", "int", "int", "int")
749 static int
750 cluster_counts_html(pcmk__output_t *out, va_list args) {
751  unsigned int nnodes = va_arg(args, unsigned int);
752  int nresources = va_arg(args, int);
753  int ndisabled = va_arg(args, int);
754  int nblocked = va_arg(args, int);
755 
756  xmlNodePtr nodes_node = pcmk__output_create_xml_node(out, "li", NULL);
757  xmlNodePtr resources_node = pcmk__output_create_xml_node(out, "li", NULL);
758 
759  char *nnodes_str = crm_strdup_printf("%d node%s configured",
760  nnodes, pcmk__plural_s(nnodes));
761 
762  pcmk_create_html_node(nodes_node, "span", NULL, NULL, nnodes_str);
763  free(nnodes_str);
764 
765  if (ndisabled && nblocked) {
766  char *s = crm_strdup_printf("%d resource instance%s configured (%d ",
767  nresources, pcmk__plural_s(nresources),
768  ndisabled);
769  pcmk_create_html_node(resources_node, "span", NULL, NULL, s);
770  free(s);
771 
772  pcmk_create_html_node(resources_node, "span", NULL, "bold", "DISABLED");
773 
774  s = crm_strdup_printf(", %d ", nblocked);
775  pcmk_create_html_node(resources_node, "span", NULL, NULL, s);
776  free(s);
777 
778  pcmk_create_html_node(resources_node, "span", NULL, "bold", "BLOCKED");
779  pcmk_create_html_node(resources_node, "span", NULL, NULL,
780  " from further action due to failure)");
781  } else if (ndisabled && !nblocked) {
782  char *s = crm_strdup_printf("%d resource instance%s configured (%d ",
783  nresources, pcmk__plural_s(nresources),
784  ndisabled);
785  pcmk_create_html_node(resources_node, "span", NULL, NULL, s);
786  free(s);
787 
788  pcmk_create_html_node(resources_node, "span", NULL, "bold", "DISABLED");
789  pcmk_create_html_node(resources_node, "span", NULL, NULL, ")");
790  } else if (!ndisabled && nblocked) {
791  char *s = crm_strdup_printf("%d resource instance%s configured (%d ",
792  nresources, pcmk__plural_s(nresources),
793  nblocked);
794  pcmk_create_html_node(resources_node, "span", NULL, NULL, s);
795  free(s);
796 
797  pcmk_create_html_node(resources_node, "span", NULL, "bold", "BLOCKED");
798  pcmk_create_html_node(resources_node, "span", NULL, NULL,
799  " from further action due to failure)");
800  } else {
801  char *s = crm_strdup_printf("%d resource instance%s configured",
802  nresources, pcmk__plural_s(nresources));
803  pcmk_create_html_node(resources_node, "span", NULL, NULL, s);
804  free(s);
805  }
806 
807  return pcmk_rc_ok;
808 }
809 
810 PCMK__OUTPUT_ARGS("cluster-counts", "unsigned int", "int", "int", "int")
811 static int
812 cluster_counts_text(pcmk__output_t *out, va_list args) {
813  unsigned int nnodes = va_arg(args, unsigned int);
814  int nresources = va_arg(args, int);
815  int ndisabled = va_arg(args, int);
816  int nblocked = va_arg(args, int);
817 
818  out->list_item(out, NULL, "%d node%s configured",
819  nnodes, pcmk__plural_s(nnodes));
820 
821  if (ndisabled && nblocked) {
822  out->list_item(out, NULL, "%d resource instance%s configured "
823  "(%d DISABLED, %d BLOCKED from "
824  "further action due to failure)",
825  nresources, pcmk__plural_s(nresources), ndisabled,
826  nblocked);
827  } else if (ndisabled && !nblocked) {
828  out->list_item(out, NULL, "%d resource instance%s configured "
829  "(%d DISABLED)",
830  nresources, pcmk__plural_s(nresources), ndisabled);
831  } else if (!ndisabled && nblocked) {
832  out->list_item(out, NULL, "%d resource instance%s configured "
833  "(%d BLOCKED from further action "
834  "due to failure)",
835  nresources, pcmk__plural_s(nresources), nblocked);
836  } else {
837  out->list_item(out, NULL, "%d resource instance%s configured",
838  nresources, pcmk__plural_s(nresources));
839  }
840 
841  return pcmk_rc_ok;
842 }
843 
844 PCMK__OUTPUT_ARGS("cluster-counts", "unsigned int", "int", "int", "int")
845 static int
846 cluster_counts_xml(pcmk__output_t *out, va_list args) {
847  unsigned int nnodes = va_arg(args, unsigned int);
848  int nresources = va_arg(args, int);
849  int ndisabled = va_arg(args, int);
850  int nblocked = va_arg(args, int);
851 
852  xmlNodePtr nodes_node = pcmk__output_create_xml_node(out, "nodes_configured", NULL);
853  xmlNodePtr resources_node = pcmk__output_create_xml_node(out, "resources_configured", NULL);
854 
855  char *s = pcmk__itoa(nnodes);
856  crm_xml_add(nodes_node, "number", s);
857  free(s);
858 
859  s = pcmk__itoa(nresources);
860  crm_xml_add(resources_node, "number", s);
861  free(s);
862 
863  s = pcmk__itoa(ndisabled);
864  crm_xml_add(resources_node, "disabled", s);
865  free(s);
866 
867  s = pcmk__itoa(nblocked);
868  crm_xml_add(resources_node, "blocked", s);
869  free(s);
870 
871  return pcmk_rc_ok;
872 }
873 
874 PCMK__OUTPUT_ARGS("cluster-dc", "pcmk_node_t *", "const char *", "const char *",
875  "char *", "int")
876 static int
877 cluster_dc_html(pcmk__output_t *out, va_list args) {
878  pcmk_node_t *dc = va_arg(args, pcmk_node_t *);
879  const char *quorum = va_arg(args, const char *);
880  const char *dc_version_s = va_arg(args, const char *);
881  char *dc_name = va_arg(args, char *);
882  bool mixed_version = va_arg(args, int);
883 
884  xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
885 
886  pcmk_create_html_node(node, "span", NULL, "bold", "Current DC: ");
887 
888  if (dc) {
889  char *buf = crm_strdup_printf("%s (version %s) -", dc_name,
890  dc_version_s ? dc_version_s : "unknown");
891  pcmk_create_html_node(node, "span", NULL, NULL, buf);
892  free(buf);
893 
894  if (mixed_version) {
895  pcmk_create_html_node(node, "span", NULL, "warning",
896  " MIXED-VERSION");
897  }
898  pcmk_create_html_node(node, "span", NULL, NULL, " partition");
899  if (crm_is_true(quorum)) {
900  pcmk_create_html_node(node, "span", NULL, NULL, " with");
901  } else {
902  pcmk_create_html_node(node, "span", NULL, "warning", " WITHOUT");
903  }
904  pcmk_create_html_node(node, "span", NULL, NULL, " quorum");
905  } else {
906  pcmk_create_html_node(node, "span", NULL, "warning", "NONE");
907  }
908 
909  return pcmk_rc_ok;
910 }
911 
912 PCMK__OUTPUT_ARGS("cluster-dc", "pcmk_node_t *", "const char *", "const char *",
913  "char *", "int")
914 static int
915 cluster_dc_text(pcmk__output_t *out, va_list args) {
916  pcmk_node_t *dc = va_arg(args, pcmk_node_t *);
917  const char *quorum = va_arg(args, const char *);
918  const char *dc_version_s = va_arg(args, const char *);
919  char *dc_name = va_arg(args, char *);
920  bool mixed_version = va_arg(args, int);
921 
922  if (dc) {
923  out->list_item(out, "Current DC",
924  "%s (version %s) - %spartition %s quorum",
925  dc_name, dc_version_s ? dc_version_s : "unknown",
926  mixed_version ? "MIXED-VERSION " : "",
927  crm_is_true(quorum) ? "with" : "WITHOUT");
928  } else {
929  out->list_item(out, "Current DC", "NONE");
930  }
931 
932  return pcmk_rc_ok;
933 }
934 
935 PCMK__OUTPUT_ARGS("cluster-dc", "pcmk_node_t *", "const char *", "const char *",
936  "char *", "int")
937 static int
938 cluster_dc_xml(pcmk__output_t *out, va_list args) {
939  pcmk_node_t *dc = va_arg(args, pcmk_node_t *);
940  const char *quorum = va_arg(args, const char *);
941  const char *dc_version_s = va_arg(args, const char *);
942  char *dc_name G_GNUC_UNUSED = va_arg(args, char *);
943  bool mixed_version = va_arg(args, int);
944 
945  if (dc) {
946  pcmk__output_create_xml_node(out, "current_dc",
947  "present", "true",
948  "version", dc_version_s ? dc_version_s : "",
949  "name", dc->details->uname,
950  "id", dc->details->id,
951  "with_quorum", pcmk__btoa(crm_is_true(quorum)),
952  "mixed_version", pcmk__btoa(mixed_version),
953  NULL);
954  } else {
955  pcmk__output_create_xml_node(out, "current_dc",
956  "present", "false",
957  NULL);
958  }
959 
960  return pcmk_rc_ok;
961 }
962 
963 PCMK__OUTPUT_ARGS("maint-mode", "unsigned long long int")
964 static int
965 cluster_maint_mode_text(pcmk__output_t *out, va_list args) {
966  unsigned long long flags = va_arg(args, unsigned long long);
967 
969  pcmk__formatted_printf(out, "\n *** Resource management is DISABLED ***\n");
970  pcmk__formatted_printf(out, " The cluster will not attempt to start, stop or recover services\n");
971  return pcmk_rc_ok;
972  } else if (pcmk_is_set(flags, pcmk_sched_stop_all)) {
973  pcmk__formatted_printf(out, "\n *** Resource management is DISABLED ***\n");
974  pcmk__formatted_printf(out, " The cluster will keep all resources stopped\n");
975  return pcmk_rc_ok;
976  } else {
977  return pcmk_rc_no_output;
978  }
979 }
980 
981 PCMK__OUTPUT_ARGS("cluster-options", "pcmk_scheduler_t *")
982 static int
983 cluster_options_html(pcmk__output_t *out, va_list args) {
984  pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
985 
987  out->list_item(out, NULL, "STONITH of failed nodes enabled");
988  } else {
989  out->list_item(out, NULL, "STONITH of failed nodes disabled");
990  }
991 
993  out->list_item(out, NULL, "Cluster is symmetric");
994  } else {
995  out->list_item(out, NULL, "Cluster is asymmetric");
996  }
997 
998  switch (scheduler->no_quorum_policy) {
1000  out->list_item(out, NULL, "No quorum policy: Freeze resources");
1001  break;
1002 
1003  case pcmk_no_quorum_stop:
1004  out->list_item(out, NULL, "No quorum policy: Stop ALL resources");
1005  break;
1006 
1007  case pcmk_no_quorum_demote:
1008  out->list_item(out, NULL, "No quorum policy: Demote promotable "
1009  "resources and stop all other resources");
1010  break;
1011 
1012  case pcmk_no_quorum_ignore:
1013  out->list_item(out, NULL, "No quorum policy: Ignore");
1014  break;
1015 
1016  case pcmk_no_quorum_fence:
1017  out->list_item(out, NULL, "No quorum policy: Suicide");
1018  break;
1019  }
1020 
1022  xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
1023 
1024  pcmk_create_html_node(node, "span", NULL, NULL, "Resource management: ");
1025  pcmk_create_html_node(node, "span", NULL, "bold", "DISABLED");
1026  pcmk_create_html_node(node, "span", NULL, NULL,
1027  " (the cluster will not attempt to start, stop, or recover services)");
1029  xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
1030 
1031  pcmk_create_html_node(node, "span", NULL, NULL, "Resource management: ");
1032  pcmk_create_html_node(node, "span", NULL, "bold", "STOPPED");
1033  pcmk_create_html_node(node, "span", NULL, NULL,
1034  " (the cluster will keep all resources stopped)");
1035  } else {
1036  out->list_item(out, NULL, "Resource management: enabled");
1037  }
1038 
1039  return pcmk_rc_ok;
1040 }
1041 
1042 PCMK__OUTPUT_ARGS("cluster-options", "pcmk_scheduler_t *")
1043 static int
1044 cluster_options_log(pcmk__output_t *out, va_list args) {
1045  pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
1046 
1048  return out->info(out, "Resource management is DISABLED. The cluster will not attempt to start, stop or recover services.");
1050  return out->info(out, "Resource management is DISABLED. The cluster has stopped all resources.");
1051  } else {
1052  return pcmk_rc_no_output;
1053  }
1054 }
1055 
1056 PCMK__OUTPUT_ARGS("cluster-options", "pcmk_scheduler_t *")
1057 static int
1058 cluster_options_text(pcmk__output_t *out, va_list args) {
1059  pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
1060 
1062  out->list_item(out, NULL, "STONITH of failed nodes enabled");
1063  } else {
1064  out->list_item(out, NULL, "STONITH of failed nodes disabled");
1065  }
1066 
1068  out->list_item(out, NULL, "Cluster is symmetric");
1069  } else {
1070  out->list_item(out, NULL, "Cluster is asymmetric");
1071  }
1072 
1073  switch (scheduler->no_quorum_policy) {
1074  case pcmk_no_quorum_freeze:
1075  out->list_item(out, NULL, "No quorum policy: Freeze resources");
1076  break;
1077 
1078  case pcmk_no_quorum_stop:
1079  out->list_item(out, NULL, "No quorum policy: Stop ALL resources");
1080  break;
1081 
1082  case pcmk_no_quorum_demote:
1083  out->list_item(out, NULL, "No quorum policy: Demote promotable "
1084  "resources and stop all other resources");
1085  break;
1086 
1087  case pcmk_no_quorum_ignore:
1088  out->list_item(out, NULL, "No quorum policy: Ignore");
1089  break;
1090 
1091  case pcmk_no_quorum_fence:
1092  out->list_item(out, NULL, "No quorum policy: Suicide");
1093  break;
1094  }
1095 
1096  return pcmk_rc_ok;
1097 }
1098 
1099 #define bv(flag) pcmk__btoa(pcmk_is_set(scheduler->flags, (flag)))
1100 
1101 PCMK__OUTPUT_ARGS("cluster-options", "pcmk_scheduler_t *")
1102 static int
1103 cluster_options_xml(pcmk__output_t *out, va_list args) {
1104  pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
1105 
1106  const char *no_quorum_policy = NULL;
1107  char *stonith_timeout_str = pcmk__itoa(scheduler->stonith_timeout);
1108  char *priority_fencing_delay_str = pcmk__itoa(scheduler->priority_fencing_delay * 1000);
1109 
1110  switch (scheduler->no_quorum_policy) {
1111  case pcmk_no_quorum_freeze:
1112  no_quorum_policy = "freeze";
1113  break;
1114 
1115  case pcmk_no_quorum_stop:
1116  no_quorum_policy = "stop";
1117  break;
1118 
1119  case pcmk_no_quorum_demote:
1120  no_quorum_policy = "demote";
1121  break;
1122 
1123  case pcmk_no_quorum_ignore:
1124  no_quorum_policy = "ignore";
1125  break;
1126 
1127  case pcmk_no_quorum_fence:
1128  no_quorum_policy = "suicide";
1129  break;
1130  }
1131 
1132  pcmk__output_create_xml_node(out, "cluster_options",
1133  "stonith-enabled",
1135  "symmetric-cluster",
1137  "no-quorum-policy", no_quorum_policy,
1138  "maintenance-mode",
1140  "stop-all-resources", bv(pcmk_sched_stop_all),
1141  "stonith-timeout-ms", stonith_timeout_str,
1142  "priority-fencing-delay-ms", priority_fencing_delay_str,
1143  NULL);
1144  free(stonith_timeout_str);
1145  free(priority_fencing_delay_str);
1146 
1147  return pcmk_rc_ok;
1148 }
1149 
1150 PCMK__OUTPUT_ARGS("cluster-stack", "const char *", "enum pcmk_pacemakerd_state")
1151 static int
1152 cluster_stack_html(pcmk__output_t *out, va_list args) {
1153  const char *stack_s = va_arg(args, const char *);
1154  enum pcmk_pacemakerd_state pcmkd_state =
1155  (enum pcmk_pacemakerd_state) va_arg(args, int);
1156 
1157  xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
1158 
1159  pcmk_create_html_node(node, "span", NULL, "bold", "Stack: ");
1160  pcmk_create_html_node(node, "span", NULL, NULL, stack_s);
1161 
1162  if (pcmkd_state != pcmk_pacemakerd_state_invalid) {
1163  pcmk_create_html_node(node, "span", NULL, NULL, " (");
1164  pcmk_create_html_node(node, "span", NULL, NULL,
1165  pcmk__pcmkd_state_enum2friendly(pcmkd_state));
1166  pcmk_create_html_node(node, "span", NULL, NULL, ")");
1167  }
1168  return pcmk_rc_ok;
1169 }
1170 
1171 PCMK__OUTPUT_ARGS("cluster-stack", "const char *", "enum pcmk_pacemakerd_state")
1172 static int
1173 cluster_stack_text(pcmk__output_t *out, va_list args) {
1174  const char *stack_s = va_arg(args, const char *);
1175  enum pcmk_pacemakerd_state pcmkd_state =
1176  (enum pcmk_pacemakerd_state) va_arg(args, int);
1177 
1178  if (pcmkd_state != pcmk_pacemakerd_state_invalid) {
1179  out->list_item(out, "Stack", "%s (%s)",
1180  stack_s, pcmk__pcmkd_state_enum2friendly(pcmkd_state));
1181  } else {
1182  out->list_item(out, "Stack", "%s", stack_s);
1183  }
1184 
1185  return pcmk_rc_ok;
1186 }
1187 
1188 PCMK__OUTPUT_ARGS("cluster-stack", "const char *", "enum pcmk_pacemakerd_state")
1189 static int
1190 cluster_stack_xml(pcmk__output_t *out, va_list args) {
1191  const char *stack_s = va_arg(args, const char *);
1192  enum pcmk_pacemakerd_state pcmkd_state =
1193  (enum pcmk_pacemakerd_state) va_arg(args, int);
1194 
1195  const char *state_s = NULL;
1196 
1197  if (pcmkd_state != pcmk_pacemakerd_state_invalid) {
1198  state_s = pcmk_pacemakerd_api_daemon_state_enum2text(pcmkd_state);
1199  }
1200 
1201  pcmk__output_create_xml_node(out, "stack",
1202  "type", stack_s,
1203  "pacemakerd-state", state_s,
1204  NULL);
1205 
1206  return pcmk_rc_ok;
1207 }
1208 
1209 PCMK__OUTPUT_ARGS("cluster-times", "const char *", "const char *",
1210  "const char *", "const char *", "const char *")
1211 static int
1212 cluster_times_html(pcmk__output_t *out, va_list args) {
1213  const char *our_nodename = va_arg(args, const char *);
1214  const char *last_written = va_arg(args, const char *);
1215  const char *user = va_arg(args, const char *);
1216  const char *client = va_arg(args, const char *);
1217  const char *origin = va_arg(args, const char *);
1218 
1219  xmlNodePtr updated_node = pcmk__output_create_xml_node(out, "li", NULL);
1220  xmlNodePtr changed_node = pcmk__output_create_xml_node(out, "li", NULL);
1221 
1222  char *time_s = pcmk__epoch2str(NULL, 0);
1223 
1224  pcmk_create_html_node(updated_node, "span", NULL, "bold", "Last updated: ");
1225  pcmk_create_html_node(updated_node, "span", NULL, NULL, time_s);
1226 
1227  if (our_nodename != NULL) {
1228  pcmk_create_html_node(updated_node, "span", NULL, NULL, " on ");
1229  pcmk_create_html_node(updated_node, "span", NULL, NULL, our_nodename);
1230  }
1231 
1232  free(time_s);
1233  time_s = last_changed_string(last_written, user, client, origin);
1234 
1235  pcmk_create_html_node(changed_node, "span", NULL, "bold", "Last change: ");
1236  pcmk_create_html_node(changed_node, "span", NULL, NULL, time_s);
1237 
1238  free(time_s);
1239  return pcmk_rc_ok;
1240 }
1241 
1242 PCMK__OUTPUT_ARGS("cluster-times", "const char *", "const char *",
1243  "const char *", "const char *", "const char *")
1244 static int
1245 cluster_times_xml(pcmk__output_t *out, va_list args) {
1246  const char *our_nodename = va_arg(args, const char *);
1247  const char *last_written = va_arg(args, const char *);
1248  const char *user = va_arg(args, const char *);
1249  const char *client = va_arg(args, const char *);
1250  const char *origin = va_arg(args, const char *);
1251 
1252  char *time_s = pcmk__epoch2str(NULL, 0);
1253 
1254  pcmk__output_create_xml_node(out, "last_update",
1255  "time", time_s,
1256  "origin", our_nodename,
1257  NULL);
1258 
1259  pcmk__output_create_xml_node(out, "last_change",
1260  "time", last_written ? last_written : "",
1261  "user", user ? user : "",
1262  "client", client ? client : "",
1263  "origin", origin ? origin : "",
1264  NULL);
1265 
1266  free(time_s);
1267  return pcmk_rc_ok;
1268 }
1269 
1270 PCMK__OUTPUT_ARGS("cluster-times", "const char *", "const char *",
1271  "const char *", "const char *", "const char *")
1272 static int
1273 cluster_times_text(pcmk__output_t *out, va_list args) {
1274  const char *our_nodename = va_arg(args, const char *);
1275  const char *last_written = va_arg(args, const char *);
1276  const char *user = va_arg(args, const char *);
1277  const char *client = va_arg(args, const char *);
1278  const char *origin = va_arg(args, const char *);
1279 
1280  char *time_s = pcmk__epoch2str(NULL, 0);
1281 
1282  out->list_item(out, "Last updated", "%s%s%s",
1283  time_s, (our_nodename != NULL)? " on " : "",
1284  pcmk__s(our_nodename, ""));
1285 
1286  free(time_s);
1287  time_s = last_changed_string(last_written, user, client, origin);
1288 
1289  out->list_item(out, "Last change", " %s", time_s);
1290 
1291  free(time_s);
1292  return pcmk_rc_ok;
1293 }
1294 
1308 static void
1309 failed_action_friendly(pcmk__output_t *out, const xmlNode *xml_op,
1310  const char *op_key, const char *node_name, int rc,
1311  int status, const char *exit_reason,
1312  const char *exec_time)
1313 {
1314  char *rsc_id = NULL;
1315  char *task = NULL;
1316  guint interval_ms = 0;
1317  time_t last_change_epoch = 0;
1318  GString *str = NULL;
1319 
1320  if (pcmk__str_empty(op_key)
1321  || !parse_op_key(op_key, &rsc_id, &task, &interval_ms)) {
1322  rsc_id = strdup("unknown resource");
1323  task = strdup("unknown action");
1324  interval_ms = 0;
1325  }
1326  CRM_ASSERT((rsc_id != NULL) && (task != NULL));
1327 
1328  str = g_string_sized_new(256); // Should be sufficient for most messages
1329 
1330  pcmk__g_strcat(str, rsc_id, " ", NULL);
1331 
1332  if (interval_ms != 0) {
1333  pcmk__g_strcat(str, pcmk__readable_interval(interval_ms), "-interval ",
1334  NULL);
1335  }
1336  pcmk__g_strcat(str, pcmk__readable_action(task, interval_ms), " on ",
1337  node_name, NULL);
1338 
1339  if (status == PCMK_EXEC_DONE) {
1340  pcmk__g_strcat(str, " returned '", services_ocf_exitcode_str(rc), "'",
1341  NULL);
1342  if (!pcmk__str_empty(exit_reason)) {
1343  pcmk__g_strcat(str, " (", exit_reason, ")", NULL);
1344  }
1345 
1346  } else {
1347  pcmk__g_strcat(str, " could not be executed (",
1348  pcmk_exec_status_str(status), NULL);
1349  if (!pcmk__str_empty(exit_reason)) {
1350  pcmk__g_strcat(str, ": ", exit_reason, NULL);
1351  }
1352  g_string_append_c(str, ')');
1353  }
1354 
1355 
1357  &last_change_epoch) == pcmk_ok) {
1358  char *s = pcmk__epoch2str(&last_change_epoch, 0);
1359 
1360  pcmk__g_strcat(str, " at ", s, NULL);
1361  free(s);
1362  }
1363  if (!pcmk__str_empty(exec_time)) {
1364  int exec_time_ms = 0;
1365 
1366  if ((pcmk__scan_min_int(exec_time, &exec_time_ms, 0) == pcmk_rc_ok)
1367  && (exec_time_ms > 0)) {
1368 
1369  pcmk__g_strcat(str, " after ",
1370  pcmk__readable_interval(exec_time_ms), NULL);
1371  }
1372  }
1373 
1374  out->list_item(out, NULL, "%s", str->str);
1375  g_string_free(str, TRUE);
1376  free(rsc_id);
1377  free(task);
1378 }
1379 
1393 static void
1394 failed_action_technical(pcmk__output_t *out, const xmlNode *xml_op,
1395  const char *op_key, const char *node_name, int rc,
1396  int status, const char *exit_reason,
1397  const char *exec_time)
1398 {
1399  const char *call_id = crm_element_value(xml_op, XML_LRM_ATTR_CALLID);
1400  const char *queue_time = crm_element_value(xml_op, XML_RSC_OP_T_QUEUE);
1401  const char *exit_status = services_ocf_exitcode_str(rc);
1402  const char *lrm_status = pcmk_exec_status_str(status);
1403  time_t last_change_epoch = 0;
1404  GString *str = NULL;
1405 
1406  if (pcmk__str_empty(op_key)) {
1407  op_key = "unknown operation";
1408  }
1409  if (pcmk__str_empty(exit_status)) {
1410  exit_status = "unknown exit status";
1411  }
1412  if (pcmk__str_empty(call_id)) {
1413  call_id = "unknown";
1414  }
1415 
1416  str = g_string_sized_new(256);
1417 
1418  g_string_append_printf(str, "%s on %s '%s' (%d): call=%s, status='%s'",
1419  op_key, node_name, exit_status, rc, call_id,
1420  lrm_status);
1421 
1422  if (!pcmk__str_empty(exit_reason)) {
1423  pcmk__g_strcat(str, ", exitreason='", exit_reason, "'", NULL);
1424  }
1425 
1427  &last_change_epoch) == pcmk_ok) {
1428  char *last_change_str = pcmk__epoch2str(&last_change_epoch, 0);
1429 
1430  pcmk__g_strcat(str,
1431  ", " XML_RSC_OP_LAST_CHANGE "="
1432  "'", last_change_str, "'", NULL);
1433  free(last_change_str);
1434  }
1435  if (!pcmk__str_empty(queue_time)) {
1436  pcmk__g_strcat(str, ", queued=", queue_time, "ms", NULL);
1437  }
1438  if (!pcmk__str_empty(exec_time)) {
1439  pcmk__g_strcat(str, ", exec=", exec_time, "ms", NULL);
1440  }
1441 
1442  out->list_item(out, NULL, "%s", str->str);
1443  g_string_free(str, TRUE);
1444 }
1445 
1446 PCMK__OUTPUT_ARGS("failed-action", "xmlNodePtr", "uint32_t")
1447 static int
1448 failed_action_default(pcmk__output_t *out, va_list args)
1449 {
1450  xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
1451  uint32_t show_opts = va_arg(args, uint32_t);
1452 
1453  const char *op_key = pe__xe_history_key(xml_op);
1454  const char *node_name = crm_element_value(xml_op, XML_ATTR_UNAME);
1455  const char *exit_reason = crm_element_value(xml_op,
1457  const char *exec_time = crm_element_value(xml_op, XML_RSC_OP_T_EXEC);
1458 
1459  int rc;
1460  int status;
1461 
1463 
1465  &status, 0);
1466 
1467  if (pcmk__str_empty(node_name)) {
1468  node_name = "unknown node";
1469  }
1470 
1471  if (pcmk_is_set(show_opts, pcmk_show_failed_detail)) {
1472  failed_action_technical(out, xml_op, op_key, node_name, rc, status,
1473  exit_reason, exec_time);
1474  } else {
1475  failed_action_friendly(out, xml_op, op_key, node_name, rc, status,
1476  exit_reason, exec_time);
1477  }
1478  return pcmk_rc_ok;
1479 }
1480 
1481 PCMK__OUTPUT_ARGS("failed-action", "xmlNodePtr", "uint32_t")
1482 static int
1483 failed_action_xml(pcmk__output_t *out, va_list args) {
1484  xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
1485  uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t);
1486 
1487  const char *op_key = pe__xe_history_key(xml_op);
1488  const char *op_key_name = "op_key";
1489  int rc;
1490  int status;
1491  const char *exit_reason = crm_element_value(xml_op, XML_LRM_ATTR_EXIT_REASON);
1492 
1493  time_t epoch = 0;
1494  char *rc_s = NULL;
1495  char *reason_s = crm_xml_escape(exit_reason ? exit_reason : "none");
1496  xmlNodePtr node = NULL;
1497 
1500  &status, 0);
1501 
1502  rc_s = pcmk__itoa(rc);
1503  if (crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY) == NULL) {
1504  op_key_name = "id";
1505  }
1506  node = pcmk__output_create_xml_node(out, "failure",
1507  op_key_name, op_key,
1508  "node", crm_element_value(xml_op, XML_ATTR_UNAME),
1509  "exitstatus", services_ocf_exitcode_str(rc),
1510  "exitreason", pcmk__s(reason_s, ""),
1511  "exitcode", rc_s,
1512  "call", crm_element_value(xml_op, XML_LRM_ATTR_CALLID),
1513  "status", pcmk_exec_status_str(status),
1514  NULL);
1515  free(rc_s);
1516 
1518  &epoch) == pcmk_ok) && (epoch > 0)) {
1519  guint interval_ms = 0;
1520  char *interval_ms_s = NULL;
1521  char *rc_change = pcmk__epoch2str(&epoch,
1525 
1526  crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms);
1527  interval_ms_s = crm_strdup_printf("%u", interval_ms);
1528 
1529  pcmk__xe_set_props(node, XML_RSC_OP_LAST_CHANGE, rc_change,
1530  "queued", crm_element_value(xml_op, XML_RSC_OP_T_QUEUE),
1531  "exec", crm_element_value(xml_op, XML_RSC_OP_T_EXEC),
1532  "interval", interval_ms_s,
1533  "task", crm_element_value(xml_op, XML_LRM_ATTR_TASK),
1534  NULL);
1535 
1536  free(interval_ms_s);
1537  free(rc_change);
1538  }
1539 
1540  free(reason_s);
1541  return pcmk_rc_ok;
1542 }
1543 
1544 PCMK__OUTPUT_ARGS("failed-action-list", "pcmk_scheduler_t *", "GList *",
1545  "GList *", "uint32_t", "bool")
1546 static int
1547 failed_action_list(pcmk__output_t *out, va_list args) {
1548  pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
1549  GList *only_node = va_arg(args, GList *);
1550  GList *only_rsc = va_arg(args, GList *);
1551  uint32_t show_opts = va_arg(args, uint32_t);
1552  bool print_spacer = va_arg(args, int);
1553 
1554  xmlNode *xml_op = NULL;
1555  int rc = pcmk_rc_no_output;
1556 
1557  if (xmlChildElementCount(scheduler->failed) == 0) {
1558  return rc;
1559  }
1560 
1561  for (xml_op = pcmk__xml_first_child(scheduler->failed); xml_op != NULL;
1562  xml_op = pcmk__xml_next(xml_op)) {
1563  char *rsc = NULL;
1564 
1565  if (!pcmk__str_in_list(crm_element_value(xml_op, XML_ATTR_UNAME), only_node,
1567  continue;
1568  }
1569 
1570  if (pcmk_xe_mask_probe_failure(xml_op)) {
1571  continue;
1572  }
1573 
1574  if (!parse_op_key(pe__xe_history_key(xml_op), &rsc, NULL, NULL)) {
1575  continue;
1576  }
1577 
1578  if (!pcmk__str_in_list(rsc, only_rsc, pcmk__str_star_matches)) {
1579  free(rsc);
1580  continue;
1581  }
1582 
1583  free(rsc);
1584 
1585  PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Failed Resource Actions");
1586  out->message(out, "failed-action", xml_op, show_opts);
1587  }
1588 
1589  PCMK__OUTPUT_LIST_FOOTER(out, rc);
1590  return rc;
1591 }
1592 
1593 static void
1594 status_node(pcmk_node_t *node, xmlNodePtr parent, uint32_t show_opts)
1595 {
1596  int health = pe__node_health(node);
1597 
1598  // Cluster membership
1599  if (node->details->online) {
1600  pcmk_create_html_node(parent, "span", NULL, "online", " online");
1601  } else {
1602  pcmk_create_html_node(parent, "span", NULL, "offline", " OFFLINE");
1603  }
1604 
1605  // Standby mode
1606  if (node->details->standby_onfail && (node->details->running_rsc != NULL)) {
1607  pcmk_create_html_node(parent, "span", NULL, "standby",
1608  " (in standby due to on-fail,"
1609  " with active resources)");
1610  } else if (node->details->standby_onfail) {
1611  pcmk_create_html_node(parent, "span", NULL, "standby",
1612  " (in standby due to on-fail)");
1613  } else if (node->details->standby && (node->details->running_rsc != NULL)) {
1614  pcmk_create_html_node(parent, "span", NULL, "standby",
1615  " (in standby, with active resources)");
1616  } else if (node->details->standby) {
1617  pcmk_create_html_node(parent, "span", NULL, "standby", " (in standby)");
1618  }
1619 
1620  // Maintenance mode
1621  if (node->details->maintenance) {
1622  pcmk_create_html_node(parent, "span", NULL, "maint",
1623  " (in maintenance mode)");
1624  }
1625 
1626  // Node health
1627  if (health < 0) {
1628  pcmk_create_html_node(parent, "span", NULL, "health_red",
1629  " (health is RED)");
1630  } else if (health == 0) {
1631  pcmk_create_html_node(parent, "span", NULL, "health_yellow",
1632  " (health is YELLOW)");
1633  }
1634 
1635  // Feature set
1636  if (pcmk_is_set(show_opts, pcmk_show_feature_set)) {
1637  const char *feature_set = get_node_feature_set(node);
1638  if (feature_set != NULL) {
1639  char *buf = crm_strdup_printf(", feature set %s", feature_set);
1640  pcmk_create_html_node(parent, "span", NULL, NULL, buf);
1641  free(buf);
1642  }
1643  }
1644 }
1645 
1646 PCMK__OUTPUT_ARGS("node", "pcmk_node_t *", "uint32_t", "bool",
1647  "GList *", "GList *")
1648 static int
1649 node_html(pcmk__output_t *out, va_list args) {
1650  pcmk_node_t *node = va_arg(args, pcmk_node_t *);
1651  uint32_t show_opts = va_arg(args, uint32_t);
1652  bool full = va_arg(args, int);
1653  GList *only_node = va_arg(args, GList *);
1654  GList *only_rsc = va_arg(args, GList *);
1655 
1656  char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
1657 
1658  if (full) {
1659  xmlNodePtr item_node;
1660 
1661  if (pcmk_all_flags_set(show_opts, pcmk_show_brief | pcmk_show_rscs_by_node)) {
1662  GList *rscs = pe__filter_rsc_list(node->details->running_rsc, only_rsc);
1663 
1664  out->begin_list(out, NULL, NULL, "%s:", node_name);
1665  item_node = pcmk__output_xml_create_parent(out, "li", NULL);
1666  pcmk_create_html_node(item_node, "span", NULL, NULL, "Status:");
1667  status_node(node, item_node, show_opts);
1668 
1669  if (rscs != NULL) {
1670  uint32_t new_show_opts = (show_opts | pcmk_show_rsc_only) & ~pcmk_show_inactive_rscs;
1671  out->begin_list(out, NULL, NULL, "Resources");
1672  pe__rscs_brief_output(out, rscs, new_show_opts);
1673  out->end_list(out);
1674  }
1675 
1677  out->end_list(out);
1678 
1679  } else if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
1680  GList *lpc2 = NULL;
1681  int rc = pcmk_rc_no_output;
1682 
1683  out->begin_list(out, NULL, NULL, "%s:", node_name);
1684  item_node = pcmk__output_xml_create_parent(out, "li", NULL);
1685  pcmk_create_html_node(item_node, "span", NULL, NULL, "Status:");
1686  status_node(node, item_node, show_opts);
1687 
1688  for (lpc2 = node->details->running_rsc; lpc2 != NULL; lpc2 = lpc2->next) {
1689  pcmk_resource_t *rsc = (pcmk_resource_t *) lpc2->data;
1690  PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Resources");
1691 
1692  show_opts |= pcmk_show_rsc_only;
1693  out->message(out, crm_map_element_name(rsc->xml), show_opts,
1694  rsc, only_node, only_rsc);
1695  }
1696 
1697  PCMK__OUTPUT_LIST_FOOTER(out, rc);
1699  out->end_list(out);
1700 
1701  } else {
1702  char *buf = crm_strdup_printf("%s:", node_name);
1703 
1704  item_node = pcmk__output_create_xml_node(out, "li", NULL);
1705  pcmk_create_html_node(item_node, "span", NULL, "bold", buf);
1706  status_node(node, item_node, show_opts);
1707 
1708  free(buf);
1709  }
1710  } else {
1711  out->begin_list(out, NULL, NULL, "%s:", node_name);
1712  }
1713 
1714  free(node_name);
1715  return pcmk_rc_ok;
1716 }
1717 
1726 static const char *
1727 node_text_status(const pcmk_node_t *node)
1728 {
1729  if (node->details->unclean) {
1730  if (node->details->online) {
1731  return "UNCLEAN (online)";
1732 
1733  } else if (node->details->pending) {
1734  return "UNCLEAN (pending)";
1735 
1736  } else {
1737  return "UNCLEAN (offline)";
1738  }
1739 
1740  } else if (node->details->pending) {
1741  return "pending";
1742 
1743  } else if (node->details->standby_onfail && node->details->online) {
1744  return "standby (on-fail)";
1745 
1746  } else if (node->details->standby) {
1747  if (node->details->online) {
1748  if (node->details->running_rsc) {
1749  return "standby (with active resources)";
1750  } else {
1751  return "standby";
1752  }
1753  } else {
1754  return "OFFLINE (standby)";
1755  }
1756 
1757  } else if (node->details->maintenance) {
1758  if (node->details->online) {
1759  return "maintenance";
1760  } else {
1761  return "OFFLINE (maintenance)";
1762  }
1763 
1764  } else if (node->details->online) {
1765  return "online";
1766  }
1767 
1768  return "OFFLINE";
1769 }
1770 
1771 PCMK__OUTPUT_ARGS("node", "pcmk_node_t *", "uint32_t", "bool", "GList *",
1772  "GList *")
1773 static int
1774 node_text(pcmk__output_t *out, va_list args) {
1775  pcmk_node_t *node = va_arg(args, pcmk_node_t *);
1776  uint32_t show_opts = va_arg(args, uint32_t);
1777  bool full = va_arg(args, int);
1778  GList *only_node = va_arg(args, GList *);
1779  GList *only_rsc = va_arg(args, GList *);
1780 
1781  if (full) {
1782  char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
1783  GString *str = g_string_sized_new(64);
1784  int health = pe__node_health(node);
1785 
1786  // Create a summary line with node type, name, and status
1787  if (pe__is_guest_node(node)) {
1788  g_string_append(str, "GuestNode");
1789  } else if (pe__is_remote_node(node)) {
1790  g_string_append(str, "RemoteNode");
1791  } else {
1792  g_string_append(str, "Node");
1793  }
1794  pcmk__g_strcat(str, " ", node_name, ": ", node_text_status(node), NULL);
1795 
1796  if (health < 0) {
1797  g_string_append(str, " (health is RED)");
1798  } else if (health == 0) {
1799  g_string_append(str, " (health is YELLOW)");
1800  }
1801  if (pcmk_is_set(show_opts, pcmk_show_feature_set)) {
1802  const char *feature_set = get_node_feature_set(node);
1803  if (feature_set != NULL) {
1804  pcmk__g_strcat(str, ", feature set ", feature_set, NULL);
1805  }
1806  }
1807 
1808  /* If we're grouping by node, print its resources */
1809  if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
1810  if (pcmk_is_set(show_opts, pcmk_show_brief)) {
1811  GList *rscs = pe__filter_rsc_list(node->details->running_rsc, only_rsc);
1812 
1813  if (rscs != NULL) {
1814  uint32_t new_show_opts = (show_opts | pcmk_show_rsc_only) & ~pcmk_show_inactive_rscs;
1815  out->begin_list(out, NULL, NULL, "%s", str->str);
1816  out->begin_list(out, NULL, NULL, "Resources");
1817 
1818  pe__rscs_brief_output(out, rscs, new_show_opts);
1819 
1820  out->end_list(out);
1821  out->end_list(out);
1822 
1823  g_list_free(rscs);
1824  }
1825 
1826  } else {
1827  GList *gIter2 = NULL;
1828 
1829  out->begin_list(out, NULL, NULL, "%s", str->str);
1830  out->begin_list(out, NULL, NULL, "Resources");
1831 
1832  for (gIter2 = node->details->running_rsc; gIter2 != NULL; gIter2 = gIter2->next) {
1833  pcmk_resource_t *rsc = (pcmk_resource_t *) gIter2->data;
1834 
1835  show_opts |= pcmk_show_rsc_only;
1836  out->message(out, crm_map_element_name(rsc->xml), show_opts,
1837  rsc, only_node, only_rsc);
1838  }
1839 
1840  out->end_list(out);
1841  out->end_list(out);
1842  }
1843  } else {
1844  out->list_item(out, NULL, "%s", str->str);
1845  }
1846 
1847  g_string_free(str, TRUE);
1848  free(node_name);
1849  } else {
1850  char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
1851  out->begin_list(out, NULL, NULL, "Node: %s", node_name);
1852  free(node_name);
1853  }
1854 
1855  return pcmk_rc_ok;
1856 }
1857 
1858 PCMK__OUTPUT_ARGS("node", "pcmk_node_t *", "uint32_t", "bool", "GList *",
1859  "GList *")
1860 static int
1861 node_xml(pcmk__output_t *out, va_list args) {
1862  pcmk_node_t *node = va_arg(args, pcmk_node_t *);
1863  uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t);
1864  bool full = va_arg(args, int);
1865  GList *only_node = va_arg(args, GList *);
1866  GList *only_rsc = va_arg(args, GList *);
1867 
1868  if (full) {
1869  const char *node_type = "unknown";
1870  char *length_s = pcmk__itoa(g_list_length(node->details->running_rsc));
1871  int health = pe__node_health(node);
1872  const char *health_s = NULL;
1873  const char *feature_set;
1874 
1875  switch (node->details->type) {
1877  node_type = "member";
1878  break;
1880  node_type = "remote";
1881  break;
1882  case node_ping:
1883  node_type = "ping";
1884  break;
1885  }
1886 
1887  if (health < 0) {
1888  health_s = "red";
1889  } else if (health == 0) {
1890  health_s = "yellow";
1891  } else {
1892  health_s = "green";
1893  }
1894 
1895  feature_set = get_node_feature_set(node);
1896 
1897  pe__name_and_nvpairs_xml(out, true, "node", 15,
1898  "name", node->details->uname,
1899  "id", node->details->id,
1900  "online", pcmk__btoa(node->details->online),
1901  "standby", pcmk__btoa(node->details->standby),
1902  "standby_onfail", pcmk__btoa(node->details->standby_onfail),
1903  "maintenance", pcmk__btoa(node->details->maintenance),
1904  "pending", pcmk__btoa(node->details->pending),
1905  "unclean", pcmk__btoa(node->details->unclean),
1906  "health", health_s,
1907  "feature_set", feature_set,
1908  "shutdown", pcmk__btoa(node->details->shutdown),
1909  "expected_up", pcmk__btoa(node->details->expected_up),
1910  "is_dc", pcmk__btoa(node->details->is_dc),
1911  "resources_running", length_s,
1912  "type", node_type);
1913 
1914  if (pe__is_guest_node(node)) {
1915  xmlNodePtr xml_node = pcmk__output_xml_peek_parent(out);
1916  crm_xml_add(xml_node, "id_as_resource", node->details->remote_rsc->container->id);
1917  }
1918 
1919  if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
1920  GList *lpc = NULL;
1921 
1922  for (lpc = node->details->running_rsc; lpc != NULL; lpc = lpc->next) {
1923  pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data;
1924 
1925  show_opts |= pcmk_show_rsc_only;
1926  out->message(out, crm_map_element_name(rsc->xml), show_opts,
1927  rsc, only_node, only_rsc);
1928  }
1929  }
1930 
1931  free(length_s);
1932 
1933  out->end_list(out);
1934  } else {
1935  pcmk__output_xml_create_parent(out, "node",
1936  "name", node->details->uname,
1937  NULL);
1938  }
1939 
1940  return pcmk_rc_ok;
1941 }
1942 
1943 PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "bool", "int")
1944 static int
1945 node_attribute_text(pcmk__output_t *out, va_list args) {
1946  const char *name = va_arg(args, const char *);
1947  const char *value = va_arg(args, const char *);
1948  bool add_extra = va_arg(args, int);
1949  int expected_score = va_arg(args, int);
1950 
1951  if (add_extra) {
1952  int v;
1953 
1954  if (value == NULL) {
1955  v = 0;
1956  } else {
1957  pcmk__scan_min_int(value, &v, INT_MIN);
1958  }
1959  if (v <= 0) {
1960  out->list_item(out, NULL, "%-32s\t: %-10s\t: Connectivity is lost", name, value);
1961  } else if (v < expected_score) {
1962  out->list_item(out, NULL, "%-32s\t: %-10s\t: Connectivity is degraded (Expected=%d)", name, value, expected_score);
1963  } else {
1964  out->list_item(out, NULL, "%-32s\t: %-10s", name, value);
1965  }
1966  } else {
1967  out->list_item(out, NULL, "%-32s\t: %-10s", name, value);
1968  }
1969 
1970  return pcmk_rc_ok;
1971 }
1972 
1973 PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "bool", "int")
1974 static int
1975 node_attribute_html(pcmk__output_t *out, va_list args) {
1976  const char *name = va_arg(args, const char *);
1977  const char *value = va_arg(args, const char *);
1978  bool add_extra = va_arg(args, int);
1979  int expected_score = va_arg(args, int);
1980 
1981  if (add_extra) {
1982  int v;
1983  char *s = crm_strdup_printf("%s: %s", name, value);
1984  xmlNodePtr item_node = pcmk__output_create_xml_node(out, "li", NULL);
1985 
1986  if (value == NULL) {
1987  v = 0;
1988  } else {
1989  pcmk__scan_min_int(value, &v, INT_MIN);
1990  }
1991 
1992  pcmk_create_html_node(item_node, "span", NULL, NULL, s);
1993  free(s);
1994 
1995  if (v <= 0) {
1996  pcmk_create_html_node(item_node, "span", NULL, "bold", "(connectivity is lost)");
1997  } else if (v < expected_score) {
1998  char *buf = crm_strdup_printf("(connectivity is degraded -- expected %d", expected_score);
1999  pcmk_create_html_node(item_node, "span", NULL, "bold", buf);
2000  free(buf);
2001  }
2002  } else {
2003  out->list_item(out, NULL, "%s: %s", name, value);
2004  }
2005 
2006  return pcmk_rc_ok;
2007 }
2008 
2009 PCMK__OUTPUT_ARGS("node-and-op", "pcmk_scheduler_t *", "xmlNodePtr")
2010 static int
2011 node_and_op(pcmk__output_t *out, va_list args) {
2012  pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
2013  xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
2014 
2015  pcmk_resource_t *rsc = NULL;
2016  gchar *node_str = NULL;
2017  char *last_change_str = NULL;
2018 
2019  const char *op_rsc = crm_element_value(xml_op, "resource");
2020  int status;
2021  time_t last_change = 0;
2022 
2024  &status, PCMK_EXEC_UNKNOWN);
2025 
2026  rsc = pe_find_resource(scheduler->resources, op_rsc);
2027 
2028  if (rsc) {
2029  const pcmk_node_t *node = pe__current_node(rsc);
2030  const char *target_role = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
2031  uint32_t show_opts = pcmk_show_rsc_only | pcmk_show_pending;
2032 
2033  if (node == NULL) {
2034  node = rsc->pending_node;
2035  }
2036 
2037  node_str = pcmk__native_output_string(rsc, rsc_printable_id(rsc), node,
2038  show_opts, target_role, false);
2039  } else {
2040  node_str = crm_strdup_printf("Unknown resource %s", op_rsc);
2041  }
2042 
2044  &last_change) == pcmk_ok) {
2045  last_change_str = crm_strdup_printf(", %s='%s', exec=%sms",
2047  pcmk__trim(ctime(&last_change)),
2049  }
2050 
2051  out->list_item(out, NULL, "%s: %s (node=%s, call=%s, rc=%s%s): %s",
2052  node_str, pe__xe_history_key(xml_op),
2056  last_change_str ? last_change_str : "",
2057  pcmk_exec_status_str(status));
2058 
2059  g_free(node_str);
2060  free(last_change_str);
2061  return pcmk_rc_ok;
2062 }
2063 
2064 PCMK__OUTPUT_ARGS("node-and-op", "pcmk_scheduler_t *", "xmlNodePtr")
2065 static int
2066 node_and_op_xml(pcmk__output_t *out, va_list args) {
2067  pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
2068  xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
2069 
2070  pcmk_resource_t *rsc = NULL;
2071  const char *op_rsc = crm_element_value(xml_op, "resource");
2072  int status;
2073  time_t last_change = 0;
2074  xmlNode *node = NULL;
2075 
2077  &status, PCMK_EXEC_UNKNOWN);
2078  node = pcmk__output_create_xml_node(out, "operation",
2079  "op", pe__xe_history_key(xml_op),
2080  "node", crm_element_value(xml_op, XML_ATTR_UNAME),
2081  "call", crm_element_value(xml_op, XML_LRM_ATTR_CALLID),
2082  "rc", crm_element_value(xml_op, XML_LRM_ATTR_RC),
2083  "status", pcmk_exec_status_str(status),
2084  NULL);
2085 
2086  rsc = pe_find_resource(scheduler->resources, op_rsc);
2087 
2088  if (rsc) {
2089  const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
2090  const char *kind = crm_element_value(rsc->xml, XML_ATTR_TYPE);
2091  char *agent_tuple = NULL;
2092 
2093  agent_tuple = crm_strdup_printf("%s:%s:%s", class,
2095  kind);
2096 
2097  pcmk__xe_set_props(node, "rsc", rsc_printable_id(rsc),
2098  "agent", agent_tuple,
2099  NULL);
2100  free(agent_tuple);
2101  }
2102 
2104  &last_change) == pcmk_ok) {
2106  pcmk__trim(ctime(&last_change)),
2108  NULL);
2109  }
2110 
2111  return pcmk_rc_ok;
2112 }
2113 
2114 PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "bool", "int")
2115 static int
2116 node_attribute_xml(pcmk__output_t *out, va_list args) {
2117  const char *name = va_arg(args, const char *);
2118  const char *value = va_arg(args, const char *);
2119  bool add_extra = va_arg(args, int);
2120  int expected_score = va_arg(args, int);
2121 
2122  xmlNodePtr node = pcmk__output_create_xml_node(out, "attribute",
2123  "name", name,
2124  "value", value,
2125  NULL);
2126 
2127  if (add_extra) {
2128  char *buf = pcmk__itoa(expected_score);
2129  crm_xml_add(node, "expected", buf);
2130  free(buf);
2131  }
2132 
2133  return pcmk_rc_ok;
2134 }
2135 
2136 PCMK__OUTPUT_ARGS("node-attribute-list", "pcmk_scheduler_t *", "uint32_t",
2137  "bool", "GList *", "GList *")
2138 static int
2139 node_attribute_list(pcmk__output_t *out, va_list args) {
2140  pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
2141  uint32_t show_opts = va_arg(args, uint32_t);
2142  bool print_spacer = va_arg(args, int);
2143  GList *only_node = va_arg(args, GList *);
2144  GList *only_rsc = va_arg(args, GList *);
2145 
2146  int rc = pcmk_rc_no_output;
2147 
2148  /* Display each node's attributes */
2149  for (GList *gIter = scheduler->nodes; gIter != NULL; gIter = gIter->next) {
2150  pcmk_node_t *node = gIter->data;
2151 
2152  GList *attr_list = NULL;
2153  GHashTableIter iter;
2154  gpointer key;
2155 
2156  if (!node || !node->details || !node->details->online) {
2157  continue;
2158  }
2159 
2160  g_hash_table_iter_init(&iter, node->details->attrs);
2161  while (g_hash_table_iter_next (&iter, &key, NULL)) {
2162  attr_list = filter_attr_list(attr_list, key);
2163  }
2164 
2165  if (attr_list == NULL) {
2166  continue;
2167  }
2168 
2170  g_list_free(attr_list);
2171  continue;
2172  }
2173 
2174  PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Node Attributes");
2175 
2176  out->message(out, "node", node, show_opts, false, only_node, only_rsc);
2177 
2178  for (GList *aIter = attr_list; aIter != NULL; aIter = aIter->next) {
2179  const char *name = aIter->data;
2180  const char *value = NULL;
2181  int expected_score = 0;
2182  bool add_extra = false;
2183 
2184  value = pe_node_attribute_raw(node, name);
2185 
2186  add_extra = add_extra_info(node, node->details->running_rsc,
2187  scheduler, name, &expected_score);
2188 
2189  /* Print attribute name and value */
2190  out->message(out, "node-attribute", name, value, add_extra,
2191  expected_score);
2192  }
2193 
2194  g_list_free(attr_list);
2195  out->end_list(out);
2196  }
2197 
2198  PCMK__OUTPUT_LIST_FOOTER(out, rc);
2199  return rc;
2200 }
2201 
2202 PCMK__OUTPUT_ARGS("node-capacity", "const pcmk_node_t *", "const char *")
2203 static int
2204 node_capacity(pcmk__output_t *out, va_list args)
2205 {
2206  const pcmk_node_t *node = va_arg(args, pcmk_node_t *);
2207  const char *comment = va_arg(args, const char *);
2208 
2209  char *dump_text = crm_strdup_printf("%s: %s capacity:",
2210  comment, pe__node_name(node));
2211 
2212  g_hash_table_foreach(node->details->utilization, append_dump_text, &dump_text);
2213  out->list_item(out, NULL, "%s", dump_text);
2214  free(dump_text);
2215 
2216  return pcmk_rc_ok;
2217 }
2218 
2219 PCMK__OUTPUT_ARGS("node-capacity", "const pcmk_node_t *", "const char *")
2220 static int
2221 node_capacity_xml(pcmk__output_t *out, va_list args)
2222 {
2223  const pcmk_node_t *node = va_arg(args, pcmk_node_t *);
2224  const char *comment = va_arg(args, const char *);
2225 
2226  xmlNodePtr xml_node = pcmk__output_create_xml_node(out, "capacity",
2227  "node", node->details->uname,
2228  "comment", comment,
2229  NULL);
2230  g_hash_table_foreach(node->details->utilization, add_dump_node, xml_node);
2231 
2232  return pcmk_rc_ok;
2233 }
2234 
2235 PCMK__OUTPUT_ARGS("node-history-list", "pcmk_scheduler_t *", "pcmk_node_t *",
2236  "xmlNodePtr", "GList *", "GList *", "uint32_t", "uint32_t")
2237 static int
2238 node_history_list(pcmk__output_t *out, va_list args) {
2239  pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
2240  pcmk_node_t *node = va_arg(args, pcmk_node_t *);
2241  xmlNode *node_state = va_arg(args, xmlNode *);
2242  GList *only_node = va_arg(args, GList *);
2243  GList *only_rsc = va_arg(args, GList *);
2244  uint32_t section_opts = va_arg(args, uint32_t);
2245  uint32_t show_opts = va_arg(args, uint32_t);
2246 
2247  xmlNode *lrm_rsc = NULL;
2248  xmlNode *rsc_entry = NULL;
2249  int rc = pcmk_rc_no_output;
2250 
2251  lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
2252  lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE);
2253 
2254  /* Print history of each of the node's resources */
2255  for (rsc_entry = first_named_child(lrm_rsc, XML_LRM_TAG_RESOURCE);
2256  rsc_entry != NULL; rsc_entry = crm_next_same_xml(rsc_entry)) {
2257  const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID);
2259  const pcmk_resource_t *parent = pe__const_top_resource(rsc, false);
2260 
2261  /* We can't use is_filtered here to filter group resources. For is_filtered,
2262  * we have to decide whether to check the parent or not. If we check the
2263  * parent, all elements of a group will always be printed because that's how
2264  * is_filtered works for groups. If we do not check the parent, sometimes
2265  * this will filter everything out.
2266  *
2267  * For other resource types, is_filtered is okay.
2268  */
2269  if (parent->variant == pcmk_rsc_variant_group) {
2270  if (!pcmk__str_in_list(rsc_printable_id(rsc), only_rsc,
2272  && !pcmk__str_in_list(rsc_printable_id(parent), only_rsc,
2274  continue;
2275  }
2276  } else {
2277  if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
2278  continue;
2279  }
2280  }
2281 
2282  if (!pcmk_is_set(section_opts, pcmk_section_operations)) {
2283  time_t last_failure = 0;
2284  int failcount = pe_get_failcount(node, rsc, &last_failure,
2285  pcmk__fc_default, NULL);
2286 
2287  if (failcount <= 0) {
2288  continue;
2289  }
2290 
2291  if (rc == pcmk_rc_no_output) {
2292  rc = pcmk_rc_ok;
2293  out->message(out, "node", node, show_opts, false, only_node,
2294  only_rsc);
2295  }
2296 
2297  out->message(out, "resource-history", rsc, rsc_id, false,
2298  failcount, last_failure, false);
2299  } else {
2300  GList *op_list = get_operation_list(rsc_entry);
2302  crm_element_value(rsc_entry, XML_ATTR_ID));
2303 
2304  if (op_list == NULL) {
2305  continue;
2306  }
2307 
2308  if (rc == pcmk_rc_no_output) {
2309  rc = pcmk_rc_ok;
2310  out->message(out, "node", node, show_opts, false, only_node,
2311  only_rsc);
2312  }
2313 
2314  out->message(out, "resource-operation-list", scheduler, rsc, node,
2315  op_list, show_opts);
2316  }
2317  }
2318 
2319  PCMK__OUTPUT_LIST_FOOTER(out, rc);
2320  return rc;
2321 }
2322 
2323 PCMK__OUTPUT_ARGS("node-list", "GList *", "GList *", "GList *", "uint32_t", "bool")
2324 static int
2325 node_list_html(pcmk__output_t *out, va_list args) {
2326  GList *nodes = va_arg(args, GList *);
2327  GList *only_node = va_arg(args, GList *);
2328  GList *only_rsc = va_arg(args, GList *);
2329  uint32_t show_opts = va_arg(args, uint32_t);
2330  bool print_spacer G_GNUC_UNUSED = va_arg(args, int);
2331 
2332  int rc = pcmk_rc_no_output;
2333 
2334  for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) {
2335  pcmk_node_t *node = (pcmk_node_t *) gIter->data;
2336 
2337  if (!pcmk__str_in_list(node->details->uname, only_node,
2339  continue;
2340  }
2341 
2342  PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Node List");
2343 
2344  out->message(out, "node", node, show_opts, true, only_node, only_rsc);
2345  }
2346 
2347  PCMK__OUTPUT_LIST_FOOTER(out, rc);
2348  return rc;
2349 }
2350 
2351 PCMK__OUTPUT_ARGS("node-list", "GList *", "GList *", "GList *", "uint32_t", "bool")
2352 static int
2353 node_list_text(pcmk__output_t *out, va_list args) {
2354  GList *nodes = va_arg(args, GList *);
2355  GList *only_node = va_arg(args, GList *);
2356  GList *only_rsc = va_arg(args, GList *);
2357  uint32_t show_opts = va_arg(args, uint32_t);
2358  bool print_spacer = va_arg(args, int);
2359 
2360  /* space-separated lists of node names */
2361  GString *online_nodes = NULL;
2362  GString *online_remote_nodes = NULL;
2363  GString *online_guest_nodes = NULL;
2364  GString *offline_nodes = NULL;
2365  GString *offline_remote_nodes = NULL;
2366 
2367  int rc = pcmk_rc_no_output;
2368 
2369  for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) {
2370  pcmk_node_t *node = (pcmk_node_t *) gIter->data;
2371  char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
2372 
2373  if (!pcmk__str_in_list(node->details->uname, only_node,
2375  free(node_name);
2376  continue;
2377  }
2378 
2379  PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Node List");
2380 
2381  // Determine whether to display node individually or in a list
2382  if (node->details->unclean || node->details->pending
2383  || (node->details->standby_onfail && node->details->online)
2384  || node->details->standby || node->details->maintenance
2385  || pcmk_is_set(show_opts, pcmk_show_rscs_by_node)
2386  || pcmk_is_set(show_opts, pcmk_show_feature_set)
2387  || (pe__node_health(node) <= 0)) {
2388  // Display node individually
2389 
2390  } else if (node->details->online) {
2391  // Display online node in a list
2392  if (pe__is_guest_node(node)) {
2393  pcmk__add_word(&online_guest_nodes, 1024, node_name);
2394 
2395  } else if (pe__is_remote_node(node)) {
2396  pcmk__add_word(&online_remote_nodes, 1024, node_name);
2397 
2398  } else {
2399  pcmk__add_word(&online_nodes, 1024, node_name);
2400  }
2401  free(node_name);
2402  continue;
2403 
2404  } else {
2405  // Display offline node in a list
2406  if (pe__is_remote_node(node)) {
2407  pcmk__add_word(&offline_remote_nodes, 1024, node_name);
2408 
2409  } else if (pe__is_guest_node(node)) {
2410  /* ignore offline guest nodes */
2411 
2412  } else {
2413  pcmk__add_word(&offline_nodes, 1024, node_name);
2414  }
2415  free(node_name);
2416  continue;
2417  }
2418 
2419  /* If we get here, node is in bad state, or we're grouping by node */
2420  out->message(out, "node", node, show_opts, true, only_node, only_rsc);
2421  free(node_name);
2422  }
2423 
2424  /* If we're not grouping by node, summarize nodes by status */
2425  if (online_nodes != NULL) {
2426  out->list_item(out, "Online", "[ %s ]",
2427  (const char *) online_nodes->str);
2428  g_string_free(online_nodes, TRUE);
2429  }
2430  if (offline_nodes != NULL) {
2431  out->list_item(out, "OFFLINE", "[ %s ]",
2432  (const char *) offline_nodes->str);
2433  g_string_free(offline_nodes, TRUE);
2434  }
2435  if (online_remote_nodes) {
2436  out->list_item(out, "RemoteOnline", "[ %s ]",
2437  (const char *) online_remote_nodes->str);
2438  g_string_free(online_remote_nodes, TRUE);
2439  }
2440  if (offline_remote_nodes) {
2441  out->list_item(out, "RemoteOFFLINE", "[ %s ]",
2442  (const char *) offline_remote_nodes->str);
2443  g_string_free(offline_remote_nodes, TRUE);
2444  }
2445  if (online_guest_nodes != NULL) {
2446  out->list_item(out, "GuestOnline", "[ %s ]",
2447  (const char *) online_guest_nodes->str);
2448  g_string_free(online_guest_nodes, TRUE);
2449  }
2450 
2451  PCMK__OUTPUT_LIST_FOOTER(out, rc);
2452  return rc;
2453 }
2454 
2455 PCMK__OUTPUT_ARGS("node-list", "GList *", "GList *", "GList *", "uint32_t", "bool")
2456 static int
2457 node_list_xml(pcmk__output_t *out, va_list args) {
2458  GList *nodes = va_arg(args, GList *);
2459  GList *only_node = va_arg(args, GList *);
2460  GList *only_rsc = va_arg(args, GList *);
2461  uint32_t show_opts = va_arg(args, uint32_t);
2462  bool print_spacer G_GNUC_UNUSED = va_arg(args, int);
2463 
2464  out->begin_list(out, NULL, NULL, "nodes");
2465  for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) {
2466  pcmk_node_t *node = (pcmk_node_t *) gIter->data;
2467 
2468  if (!pcmk__str_in_list(node->details->uname, only_node,
2470  continue;
2471  }
2472 
2473  out->message(out, "node", node, show_opts, true, only_node, only_rsc);
2474  }
2475  out->end_list(out);
2476 
2477  return pcmk_rc_ok;
2478 }
2479 
2480 PCMK__OUTPUT_ARGS("node-summary", "pcmk_scheduler_t *", "GList *", "GList *",
2481  "uint32_t", "uint32_t", "bool")
2482 static int
2483 node_summary(pcmk__output_t *out, va_list args) {
2484  pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
2485  GList *only_node = va_arg(args, GList *);
2486  GList *only_rsc = va_arg(args, GList *);
2487  uint32_t section_opts = va_arg(args, uint32_t);
2488  uint32_t show_opts = va_arg(args, uint32_t);
2489  bool print_spacer = va_arg(args, int);
2490 
2491  xmlNode *node_state = NULL;
2492  xmlNode *cib_status = pcmk_find_cib_element(scheduler->input,
2494  int rc = pcmk_rc_no_output;
2495 
2496  if (xmlChildElementCount(cib_status) == 0) {
2497  return rc;
2498  }
2499 
2500  for (node_state = first_named_child(cib_status, XML_CIB_TAG_STATE);
2501  node_state != NULL; node_state = crm_next_same_xml(node_state)) {
2502  pcmk_node_t *node = pe_find_node_id(scheduler->nodes, ID(node_state));
2503 
2504  if (!node || !node->details || !node->details->online) {
2505  continue;
2506  }
2507 
2508  if (!pcmk__str_in_list(node->details->uname, only_node,
2510  continue;
2511  }
2512 
2513  PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc,
2514  pcmk_is_set(section_opts, pcmk_section_operations) ? "Operations" : "Migration Summary");
2515 
2516  out->message(out, "node-history-list", scheduler, node, node_state,
2517  only_node, only_rsc, section_opts, show_opts);
2518  }
2519 
2520  PCMK__OUTPUT_LIST_FOOTER(out, rc);
2521  return rc;
2522 }
2523 
2524 PCMK__OUTPUT_ARGS("node-weight", "const pcmk_resource_t *", "const char *",
2525  "const char *", "const char *")
2526 static int
2527 node_weight(pcmk__output_t *out, va_list args)
2528 {
2529  const pcmk_resource_t *rsc = va_arg(args, const pcmk_resource_t *);
2530  const char *prefix = va_arg(args, const char *);
2531  const char *uname = va_arg(args, const char *);
2532  const char *score = va_arg(args, const char *);
2533 
2534  if (rsc) {
2535  out->list_item(out, NULL, "%s: %s allocation score on %s: %s",
2536  prefix, rsc->id, uname, score);
2537  } else {
2538  out->list_item(out, NULL, "%s: %s = %s", prefix, uname, score);
2539  }
2540 
2541  return pcmk_rc_ok;
2542 }
2543 
2544 PCMK__OUTPUT_ARGS("node-weight", "const pcmk_resource_t *", "const char *",
2545  "const char *", "const char *")
2546 static int
2547 node_weight_xml(pcmk__output_t *out, va_list args)
2548 {
2549  const pcmk_resource_t *rsc = va_arg(args, const pcmk_resource_t *);
2550  const char *prefix = va_arg(args, const char *);
2551  const char *uname = va_arg(args, const char *);
2552  const char *score = va_arg(args, const char *);
2553 
2554  xmlNodePtr node = pcmk__output_create_xml_node(out, "node_weight",
2555  "function", prefix,
2556  "node", uname,
2557  "score", score,
2558  NULL);
2559 
2560  if (rsc) {
2561  crm_xml_add(node, "id", rsc->id);
2562  }
2563 
2564  return pcmk_rc_ok;
2565 }
2566 
2567 PCMK__OUTPUT_ARGS("op-history", "xmlNodePtr", "const char *", "const char *", "int", "uint32_t")
2568 static int
2569 op_history_text(pcmk__output_t *out, va_list args) {
2570  xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
2571  const char *task = va_arg(args, const char *);
2572  const char *interval_ms_s = va_arg(args, const char *);
2573  int rc = va_arg(args, int);
2574  uint32_t show_opts = va_arg(args, uint32_t);
2575 
2576  char *buf = op_history_string(xml_op, task, interval_ms_s, rc,
2577  pcmk_is_set(show_opts, pcmk_show_timing));
2578 
2579  out->list_item(out, NULL, "%s", buf);
2580 
2581  free(buf);
2582  return pcmk_rc_ok;
2583 }
2584 
2585 PCMK__OUTPUT_ARGS("op-history", "xmlNodePtr", "const char *", "const char *", "int", "uint32_t")
2586 static int
2587 op_history_xml(pcmk__output_t *out, va_list args) {
2588  xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
2589  const char *task = va_arg(args, const char *);
2590  const char *interval_ms_s = va_arg(args, const char *);
2591  int rc = va_arg(args, int);
2592  uint32_t show_opts = va_arg(args, uint32_t);
2593 
2594  char *rc_s = pcmk__itoa(rc);
2595  xmlNodePtr node = pcmk__output_create_xml_node(out, "operation_history",
2596  "call", crm_element_value(xml_op, XML_LRM_ATTR_CALLID),
2597  "task", task,
2598  "rc", rc_s,
2599  "rc_text", services_ocf_exitcode_str(rc),
2600  NULL);
2601  free(rc_s);
2602 
2603  if (interval_ms_s && !pcmk__str_eq(interval_ms_s, "0", pcmk__str_casei)) {
2604  char *s = crm_strdup_printf("%sms", interval_ms_s);
2605  crm_xml_add(node, "interval", s);
2606  free(s);
2607  }
2608 
2609  if (pcmk_is_set(show_opts, pcmk_show_timing)) {
2610  const char *value = NULL;
2611  time_t epoch = 0;
2612 
2614  &epoch) == pcmk_ok) && (epoch > 0)) {
2615  char *s = pcmk__epoch2str(&epoch, 0);
2617  free(s);
2618  }
2619 
2620  value = crm_element_value(xml_op, XML_RSC_OP_T_EXEC);
2621  if (value) {
2622  char *s = crm_strdup_printf("%sms", value);
2623  crm_xml_add(node, XML_RSC_OP_T_EXEC, s);
2624  free(s);
2625  }
2626  value = crm_element_value(xml_op, XML_RSC_OP_T_QUEUE);
2627  if (value) {
2628  char *s = crm_strdup_printf("%sms", value);
2629  crm_xml_add(node, XML_RSC_OP_T_QUEUE, s);
2630  free(s);
2631  }
2632  }
2633 
2634  return pcmk_rc_ok;
2635 }
2636 
2637 PCMK__OUTPUT_ARGS("promotion-score", "pcmk_resource_t *", "pcmk_node_t *",
2638  "const char *")
2639 static int
2640 promotion_score(pcmk__output_t *out, va_list args)
2641 {
2642  pcmk_resource_t *child_rsc = va_arg(args, pcmk_resource_t *);
2643  pcmk_node_t *chosen = va_arg(args, pcmk_node_t *);
2644  const char *score = va_arg(args, const char *);
2645 
2646  out->list_item(out, NULL, "%s promotion score on %s: %s",
2647  child_rsc->id,
2648  chosen? chosen->details->uname : "none",
2649  score);
2650  return pcmk_rc_ok;
2651 }
2652 
2653 PCMK__OUTPUT_ARGS("promotion-score", "pcmk_resource_t *", "pcmk_node_t *",
2654  "const char *")
2655 static int
2656 promotion_score_xml(pcmk__output_t *out, va_list args)
2657 {
2658  pcmk_resource_t *child_rsc = va_arg(args, pcmk_resource_t *);
2659  pcmk_node_t *chosen = va_arg(args, pcmk_node_t *);
2660  const char *score = va_arg(args, const char *);
2661 
2662  xmlNodePtr node = pcmk__output_create_xml_node(out, "promotion_score",
2663  "id", child_rsc->id,
2664  "score", score,
2665  NULL);
2666 
2667  if (chosen) {
2668  crm_xml_add(node, "node", chosen->details->uname);
2669  }
2670 
2671  return pcmk_rc_ok;
2672 }
2673 
2674 PCMK__OUTPUT_ARGS("resource-config", "const pcmk_resource_t *", "bool")
2675 static int
2676 resource_config(pcmk__output_t *out, va_list args) {
2677  const pcmk_resource_t *rsc = va_arg(args, const pcmk_resource_t *);
2678  bool raw = va_arg(args, int);
2679 
2680  char *rsc_xml = formatted_xml_buf(rsc, raw);
2681 
2682  out->output_xml(out, "xml", rsc_xml);
2683 
2684  free(rsc_xml);
2685  return pcmk_rc_ok;
2686 }
2687 
2688 PCMK__OUTPUT_ARGS("resource-config", "const pcmk_resource_t *", "bool")
2689 static int
2690 resource_config_text(pcmk__output_t *out, va_list args) {
2691  const pcmk_resource_t *rsc = va_arg(args, const pcmk_resource_t *);
2692  bool raw = va_arg(args, int);
2693 
2694  char *rsc_xml = formatted_xml_buf(rsc, raw);
2695 
2696  pcmk__formatted_printf(out, "Resource XML:\n");
2697  out->output_xml(out, "xml", rsc_xml);
2698 
2699  free(rsc_xml);
2700  return pcmk_rc_ok;
2701 }
2702 
2703 PCMK__OUTPUT_ARGS("resource-history", "pcmk_resource_t *", "const char *",
2704  "bool", "int", "time_t", "bool")
2705 static int
2706 resource_history_text(pcmk__output_t *out, va_list args) {
2707  pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
2708  const char *rsc_id = va_arg(args, const char *);
2709  bool all = va_arg(args, int);
2710  int failcount = va_arg(args, int);
2711  time_t last_failure = va_arg(args, time_t);
2712  bool as_header = va_arg(args, int);
2713 
2714  char *buf = resource_history_string(rsc, rsc_id, all, failcount, last_failure);
2715 
2716  if (as_header) {
2717  out->begin_list(out, NULL, NULL, "%s", buf);
2718  } else {
2719  out->list_item(out, NULL, "%s", buf);
2720  }
2721 
2722  free(buf);
2723  return pcmk_rc_ok;
2724 }
2725 
2726 PCMK__OUTPUT_ARGS("resource-history", "pcmk_resource_t *", "const char *",
2727  "bool", "int", "time_t", "bool")
2728 static int
2729 resource_history_xml(pcmk__output_t *out, va_list args) {
2730  pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
2731  const char *rsc_id = va_arg(args, const char *);
2732  bool all = va_arg(args, int);
2733  int failcount = va_arg(args, int);
2734  time_t last_failure = va_arg(args, time_t);
2735  bool as_header = va_arg(args, int);
2736 
2737  xmlNodePtr node = pcmk__output_xml_create_parent(out, "resource_history",
2738  "id", rsc_id,
2739  NULL);
2740 
2741  if (rsc == NULL) {
2742  pcmk__xe_set_bool_attr(node, "orphan", true);
2743  } else if (all || failcount || last_failure > 0) {
2744  char *migration_s = pcmk__itoa(rsc->migration_threshold);
2745 
2746  pcmk__xe_set_props(node, "orphan", "false",
2747  "migration-threshold", migration_s,
2748  NULL);
2749  free(migration_s);
2750 
2751  if (failcount > 0) {
2752  char *s = pcmk__itoa(failcount);
2753 
2755  free(s);
2756  }
2757 
2758  if (last_failure > 0) {
2759  char *s = pcmk__epoch2str(&last_failure, 0);
2760 
2762  free(s);
2763  }
2764  }
2765 
2766  if (!as_header) {
2768  }
2769 
2770  return pcmk_rc_ok;
2771 }
2772 
2773 static void
2774 print_resource_header(pcmk__output_t *out, uint32_t show_opts)
2775 {
2776  if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
2777  /* Active resources have already been printed by node */
2778  out->begin_list(out, NULL, NULL, "Inactive Resources");
2779  } else if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
2780  out->begin_list(out, NULL, NULL, "Full List of Resources");
2781  } else {
2782  out->begin_list(out, NULL, NULL, "Active Resources");
2783  }
2784 }
2785 
2786 
2787 PCMK__OUTPUT_ARGS("resource-list", "pcmk_scheduler_t *", "uint32_t", "bool",
2788  "GList *", "GList *", "bool")
2789 static int
2790 resource_list(pcmk__output_t *out, va_list args)
2791 {
2792  pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
2793  uint32_t show_opts = va_arg(args, uint32_t);
2794  bool print_summary = va_arg(args, int);
2795  GList *only_node = va_arg(args, GList *);
2796  GList *only_rsc = va_arg(args, GList *);
2797  bool print_spacer = va_arg(args, int);
2798 
2799  GList *rsc_iter;
2800  int rc = pcmk_rc_no_output;
2801  bool printed_header = false;
2802 
2803  /* If we already showed active resources by node, and
2804  * we're not showing inactive resources, we have nothing to do
2805  */
2806  if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node) &&
2807  !pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
2808  return rc;
2809  }
2810 
2811  /* If we haven't already printed resources grouped by node,
2812  * and brief output was requested, print resource summary */
2813  if (pcmk_is_set(show_opts, pcmk_show_brief)
2814  && !pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
2815  GList *rscs = pe__filter_rsc_list(scheduler->resources, only_rsc);
2816 
2817  PCMK__OUTPUT_SPACER_IF(out, print_spacer);
2818  print_resource_header(out, show_opts);
2819  printed_header = true;
2820 
2821  rc = pe__rscs_brief_output(out, rscs, show_opts);
2822  g_list_free(rscs);
2823  }
2824 
2825  /* For each resource, display it if appropriate */
2826  for (rsc_iter = scheduler->resources; rsc_iter != NULL; rsc_iter = rsc_iter->next) {
2827  pcmk_resource_t *rsc = (pcmk_resource_t *) rsc_iter->data;
2828  int x;
2829 
2830  /* Complex resources may have some sub-resources active and some inactive */
2831  gboolean is_active = rsc->fns->active(rsc, TRUE);
2832  gboolean partially_active = rsc->fns->active(rsc, FALSE);
2833 
2834  /* Skip inactive orphans (deleted but still in CIB) */
2835  if (pcmk_is_set(rsc->flags, pcmk_rsc_removed) && !is_active) {
2836  continue;
2837 
2838  /* Skip active resources if we already displayed them by node */
2839  } else if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
2840  if (is_active) {
2841  continue;
2842  }
2843 
2844  /* Skip primitives already counted in a brief summary */
2845  } else if (pcmk_is_set(show_opts, pcmk_show_brief)
2846  && (rsc->variant == pcmk_rsc_variant_primitive)) {
2847  continue;
2848 
2849  /* Skip resources that aren't at least partially active,
2850  * unless we're displaying inactive resources
2851  */
2852  } else if (!partially_active && !pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
2853  continue;
2854 
2855  } else if (partially_active && !pe__rsc_running_on_any(rsc, only_node)) {
2856  continue;
2857  }
2858 
2859  if (!printed_header) {
2860  PCMK__OUTPUT_SPACER_IF(out, print_spacer);
2861  print_resource_header(out, show_opts);
2862  printed_header = true;
2863  }
2864 
2865  /* Print this resource */
2866  x = out->message(out, crm_map_element_name(rsc->xml), show_opts, rsc,
2867  only_node, only_rsc);
2868  if (x == pcmk_rc_ok) {
2869  rc = pcmk_rc_ok;
2870  }
2871  }
2872 
2873  if (print_summary && rc != pcmk_rc_ok) {
2874  if (!printed_header) {
2875  PCMK__OUTPUT_SPACER_IF(out, print_spacer);
2876  print_resource_header(out, show_opts);
2877  printed_header = true;
2878  }
2879 
2880  if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
2881  out->list_item(out, NULL, "No inactive resources");
2882  } else if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
2883  out->list_item(out, NULL, "No resources");
2884  } else {
2885  out->list_item(out, NULL, "No active resources");
2886  }
2887  }
2888 
2889  if (printed_header) {
2890  out->end_list(out);
2891  }
2892 
2893  return rc;
2894 }
2895 
2896 PCMK__OUTPUT_ARGS("resource-operation-list", "pcmk_scheduler_t *",
2897  "pcmk_resource_t *", "pcmk_node_t *", "GList *", "uint32_t")
2898 static int
2899 resource_operation_list(pcmk__output_t *out, va_list args)
2900 {
2901  pcmk_scheduler_t *scheduler G_GNUC_UNUSED = va_arg(args,
2902  pcmk_scheduler_t *);
2903  pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
2904  pcmk_node_t *node = va_arg(args, pcmk_node_t *);
2905  GList *op_list = va_arg(args, GList *);
2906  uint32_t show_opts = va_arg(args, uint32_t);
2907 
2908  GList *gIter = NULL;
2909  int rc = pcmk_rc_no_output;
2910 
2911  /* Print each operation */
2912  for (gIter = op_list; gIter != NULL; gIter = gIter->next) {
2913  xmlNode *xml_op = (xmlNode *) gIter->data;
2914  const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
2915  const char *interval_ms_s = crm_element_value(xml_op,
2917  const char *op_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC);
2918  int op_rc_i;
2919 
2920  pcmk__scan_min_int(op_rc, &op_rc_i, 0);
2921 
2922  /* Display 0-interval monitors as "probe" */
2923  if (pcmk__str_eq(task, PCMK_ACTION_MONITOR, pcmk__str_casei)
2924  && pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches | pcmk__str_casei)) {
2925  task = "probe";
2926  }
2927 
2928  /* If this is the first printed operation, print heading for resource */
2929  if (rc == pcmk_rc_no_output) {
2930  time_t last_failure = 0;
2931  int failcount = pe_get_failcount(node, rsc, &last_failure,
2932  pcmk__fc_default, NULL);
2933 
2934  out->message(out, "resource-history", rsc, rsc_printable_id(rsc), true,
2935  failcount, last_failure, true);
2936  rc = pcmk_rc_ok;
2937  }
2938 
2939  /* Print the operation */
2940  out->message(out, "op-history", xml_op, task, interval_ms_s,
2941  op_rc_i, show_opts);
2942  }
2943 
2944  /* Free the list we created (no need to free the individual items) */
2945  g_list_free(op_list);
2946 
2947  PCMK__OUTPUT_LIST_FOOTER(out, rc);
2948  return rc;
2949 }
2950 
2951 PCMK__OUTPUT_ARGS("resource-util", "pcmk_resource_t *", "pcmk_node_t *",
2952  "const char *")
2953 static int
2954 resource_util(pcmk__output_t *out, va_list args)
2955 {
2956  pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
2957  pcmk_node_t *node = va_arg(args, pcmk_node_t *);
2958  const char *fn = va_arg(args, const char *);
2959 
2960  char *dump_text = crm_strdup_printf("%s: %s utilization on %s:",
2961  fn, rsc->id, pe__node_name(node));
2962 
2963  g_hash_table_foreach(rsc->utilization, append_dump_text, &dump_text);
2964  out->list_item(out, NULL, "%s", dump_text);
2965  free(dump_text);
2966 
2967  return pcmk_rc_ok;
2968 }
2969 
2970 PCMK__OUTPUT_ARGS("resource-util", "pcmk_resource_t *", "pcmk_node_t *",
2971  "const char *")
2972 static int
2973 resource_util_xml(pcmk__output_t *out, va_list args)
2974 {
2975  pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
2976  pcmk_node_t *node = va_arg(args, pcmk_node_t *);
2977  const char *fn = va_arg(args, const char *);
2978 
2979  xmlNodePtr xml_node = pcmk__output_create_xml_node(out, "utilization",
2980  "resource", rsc->id,
2981  "node", node->details->uname,
2982  "function", fn,
2983  NULL);
2984  g_hash_table_foreach(rsc->utilization, add_dump_node, xml_node);
2985 
2986  return pcmk_rc_ok;
2987 }
2988 
2989 PCMK__OUTPUT_ARGS("ticket", "pcmk_ticket_t *")
2990 static int
2991 ticket_html(pcmk__output_t *out, va_list args) {
2992  pcmk_ticket_t *ticket = va_arg(args, pcmk_ticket_t *);
2993 
2994  if (ticket->last_granted > -1) {
2995  char *epoch_str = pcmk__epoch2str(&(ticket->last_granted), 0);
2996 
2997  out->list_item(out, NULL, "%s:\t%s%s %s=\"%s\"", ticket->id,
2998  ticket->granted ? "granted" : "revoked",
2999  ticket->standby ? " [standby]" : "",
3000  "last-granted", pcmk__s(epoch_str, ""));
3001  free(epoch_str);
3002  } else {
3003  out->list_item(out, NULL, "%s:\t%s%s", ticket->id,
3004  ticket->granted ? "granted" : "revoked",
3005  ticket->standby ? " [standby]" : "");
3006  }
3007 
3008  return pcmk_rc_ok;
3009 }
3010 
3011 PCMK__OUTPUT_ARGS("ticket", "pcmk_ticket_t *")
3012 static int
3013 ticket_text(pcmk__output_t *out, va_list args) {
3014  pcmk_ticket_t *ticket = va_arg(args, pcmk_ticket_t *);
3015 
3016  if (ticket->last_granted > -1) {
3017  char *epoch_str = pcmk__epoch2str(&(ticket->last_granted), 0);
3018 
3019  out->list_item(out, ticket->id, "%s%s %s=\"%s\"",
3020  ticket->granted ? "granted" : "revoked",
3021  ticket->standby ? " [standby]" : "",
3022  "last-granted", pcmk__s(epoch_str, ""));
3023  free(epoch_str);
3024  } else {
3025  out->list_item(out, ticket->id, "%s%s",
3026  ticket->granted ? "granted" : "revoked",
3027  ticket->standby ? " [standby]" : "");
3028  }
3029 
3030  return pcmk_rc_ok;
3031 }
3032 
3033 PCMK__OUTPUT_ARGS("ticket", "pcmk_ticket_t *")
3034 static int
3035 ticket_xml(pcmk__output_t *out, va_list args) {
3036  pcmk_ticket_t *ticket = va_arg(args, pcmk_ticket_t *);
3037 
3038  xmlNodePtr node = NULL;
3039 
3040  node = pcmk__output_create_xml_node(out, "ticket",
3041  "id", ticket->id,
3042  "status", ticket->granted ? "granted" : "revoked",
3043  "standby", pcmk__btoa(ticket->standby),
3044  NULL);
3045 
3046  if (ticket->last_granted > -1) {
3047  char *buf = pcmk__epoch2str(&ticket->last_granted, 0);
3048 
3049  crm_xml_add(node, "last-granted", buf);
3050  free(buf);
3051  }
3052 
3053  return pcmk_rc_ok;
3054 }
3055 
3056 PCMK__OUTPUT_ARGS("ticket-list", "pcmk_scheduler_t *", "bool")
3057 static int
3058 ticket_list(pcmk__output_t *out, va_list args) {
3059  pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
3060  bool print_spacer = va_arg(args, int);
3061 
3062  GHashTableIter iter;
3063  gpointer key, value;
3064 
3065  if (g_hash_table_size(scheduler->tickets) == 0) {
3066  return pcmk_rc_no_output;
3067  }
3068 
3069  PCMK__OUTPUT_SPACER_IF(out, print_spacer);
3070 
3071  /* Print section heading */
3072  out->begin_list(out, NULL, NULL, "Tickets");
3073 
3074  /* Print each ticket */
3075  g_hash_table_iter_init(&iter, scheduler->tickets);
3076  while (g_hash_table_iter_next(&iter, &key, &value)) {
3077  pcmk_ticket_t *ticket = (pcmk_ticket_t *) value;
3078  out->message(out, "ticket", ticket);
3079  }
3080 
3081  /* Close section */
3082  out->end_list(out);
3083  return pcmk_rc_ok;
3084 }
3085 
3086 static pcmk__message_entry_t fmt_functions[] = {
3087  { "ban", "default", ban_text },
3088  { "ban", "html", ban_html },
3089  { "ban", "xml", ban_xml },
3090  { "ban-list", "default", ban_list },
3091  { "bundle", "default", pe__bundle_text },
3092  { "bundle", "xml", pe__bundle_xml },
3093  { "bundle", "html", pe__bundle_html },
3094  { "clone", "default", pe__clone_default },
3095  { "clone", "xml", pe__clone_xml },
3096  { "cluster-counts", "default", cluster_counts_text },
3097  { "cluster-counts", "html", cluster_counts_html },
3098  { "cluster-counts", "xml", cluster_counts_xml },
3099  { "cluster-dc", "default", cluster_dc_text },
3100  { "cluster-dc", "html", cluster_dc_html },
3101  { "cluster-dc", "xml", cluster_dc_xml },
3102  { "cluster-options", "default", cluster_options_text },
3103  { "cluster-options", "html", cluster_options_html },
3104  { "cluster-options", "log", cluster_options_log },
3105  { "cluster-options", "xml", cluster_options_xml },
3106  { "cluster-summary", "default", cluster_summary },
3107  { "cluster-summary", "html", cluster_summary_html },
3108  { "cluster-stack", "default", cluster_stack_text },
3109  { "cluster-stack", "html", cluster_stack_html },
3110  { "cluster-stack", "xml", cluster_stack_xml },
3111  { "cluster-times", "default", cluster_times_text },
3112  { "cluster-times", "html", cluster_times_html },
3113  { "cluster-times", "xml", cluster_times_xml },
3114  { "failed-action", "default", failed_action_default },
3115  { "failed-action", "xml", failed_action_xml },
3116  { "failed-action-list", "default", failed_action_list },
3117  { "group", "default", pe__group_default},
3118  { "group", "xml", pe__group_xml },
3119  { "maint-mode", "text", cluster_maint_mode_text },
3120  { "node", "default", node_text },
3121  { "node", "html", node_html },
3122  { "node", "xml", node_xml },
3123  { "node-and-op", "default", node_and_op },
3124  { "node-and-op", "xml", node_and_op_xml },
3125  { "node-capacity", "default", node_capacity },
3126  { "node-capacity", "xml", node_capacity_xml },
3127  { "node-history-list", "default", node_history_list },
3128  { "node-list", "default", node_list_text },
3129  { "node-list", "html", node_list_html },
3130  { "node-list", "xml", node_list_xml },
3131  { "node-weight", "default", node_weight },
3132  { "node-weight", "xml", node_weight_xml },
3133  { "node-attribute", "default", node_attribute_text },
3134  { "node-attribute", "html", node_attribute_html },
3135  { "node-attribute", "xml", node_attribute_xml },
3136  { "node-attribute-list", "default", node_attribute_list },
3137  { "node-summary", "default", node_summary },
3138  { "op-history", "default", op_history_text },
3139  { "op-history", "xml", op_history_xml },
3140  { "primitive", "default", pe__resource_text },
3141  { "primitive", "xml", pe__resource_xml },
3142  { "primitive", "html", pe__resource_html },
3143  { "promotion-score", "default", promotion_score },
3144  { "promotion-score", "xml", promotion_score_xml },
3145  { "resource-config", "default", resource_config },
3146  { "resource-config", "text", resource_config_text },
3147  { "resource-history", "default", resource_history_text },
3148  { "resource-history", "xml", resource_history_xml },
3149  { "resource-list", "default", resource_list },
3150  { "resource-operation-list", "default", resource_operation_list },
3151  { "resource-util", "default", resource_util },
3152  { "resource-util", "xml", resource_util_xml },
3153  { "ticket", "default", ticket_text },
3154  { "ticket", "html", ticket_html },
3155  { "ticket", "xml", ticket_xml },
3156  { "ticket-list", "default", ticket_list },
3157 
3158  { NULL, NULL, NULL }
3159 };
3160 
3161 void
3163  pcmk__register_messages(out, fmt_functions);
3164 }
const pcmk_resource_t * pe__const_top_resource(const pcmk_resource_t *rsc, bool include_bundle)
Definition: complex.c:962
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:238
void pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr parent)
Definition: output_xml.c:509
#define XML_RSC_OP_LAST_CHANGE
Definition: msg_xml.h:326
#define XML_ATTR_UPDATE_ORIG
Definition: msg_xml.h:163
enum rsc_role_e role_filter
Definition: internal.h:161
xmlNode * orig_xml
Original resource configuration, if using template.
Definition: resources.h:407
enum pe_quorum_policy no_quorum_policy
Response to loss of quorum.
Definition: scheduler.h:186
bool pe__is_guest_or_remote_node(const pcmk_node_t *node)
Definition: remote.c:41
#define XML_ATTR_UPDATE_CLIENT
Definition: msg_xml.h:164
xmlNode * failed
History entries of failed actions.
Definition: scheduler.h:205
GHashTable * attrs
Node attributes.
Definition: nodes.h:115
node_type
Possible node types.
Definition: nodes.h:33
#define bv(flag)
Definition: pe_output.c:1099
Control output from tools.
void pcmk__register_messages(pcmk__output_t *out, const pcmk__message_entry_t *table)
Definition: output.c:196
const char * pe_node_attribute_raw(const pcmk_node_t *node, const char *name)
Definition: common.c:621
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition: strings.c:127
gchar * pcmk__native_output_string(const pcmk_resource_t *rsc, const char *name, const pcmk_node_t *node, uint32_t show_opts, const char *target_role, bool show_nodes)
Definition: native.c:563
#define crm_time_log_timeofday
Definition: iso8601.h:68
const char * name
Definition: cib.c:26
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:933
#define XML_ATTR_TYPE
Definition: msg_xml.h:160
Whether cluster is symmetric (via symmetric-cluster property)
Definition: scheduler.h:74
#define FILTER_STR
Definition: pe_output.c:33
PCMK__OUTPUT_ARGS("cluster-summary", "pcmk_scheduler_t *", "enum pcmk_pacemakerd_state", "uint32_t", "uint32_t")
Definition: pe_output.c:389
GList * children
Resource&#39;s child resources, if any.
Definition: resources.h:475
gboolean standby
Whether ticket is temporarily suspended.
Definition: tickets.h:31
int priority_fencing_delay
Priority fencing delay.
Definition: scheduler.h:226
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2484
xmlNode * xml
Resource configuration (possibly expanded from template)
Definition: resources.h:404
xmlNode * pcmk_create_html_node(xmlNode *parent, const char *element_name, const char *id, const char *class_name, const char *text)
Definition: xml.c:684
#define XML_ATTR_UPDATE_USER
Definition: msg_xml.h:165
int pe__group_default(pcmk__output_t *out, va_list args)
xmlNode * find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
Definition: xml.c:384
#define PCMK_ACTION_MONITOR
Definition: actions.h:59
GHashTable * meta
Resource&#39;s meta-attributes.
Definition: resources.h:471
#define XML_RSC_OP_T_EXEC
Definition: msg_xml.h:327
#define XML_LRM_TAG_RESOURCE
Definition: msg_xml.h:278
int pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name, size_t pairs_count,...)
Definition: pe_output.c:597
xmlNodePtr pcmk__output_xml_create_parent(pcmk__output_t *out, const char *name,...) G_GNUC_NULL_TERMINATED
Definition: output_xml.c:442
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:302
int pe__clone_default(pcmk__output_t *out, va_list args)
xmlNodePtr pcmk__output_xml_peek_parent(pcmk__output_t *out)
Definition: output_xml.c:535
gboolean pending
Whether controller membership is pending.
Definition: nodes.h:75
Promoted.
Definition: roles.h:32
gint sort_op_by_callid(gconstpointer a, gconstpointer b)
Definition: pe_actions.c:1815
void pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value)
Definition: nvpair.c:872
Group resource.
Definition: resources.h:35
enum crm_ais_msg_types type
Definition: cpg.c:48
const char * rsc_printable_id(const pcmk_resource_t *rsc)
Definition: utils.c:546
#define XML_CIB_TAG_LRM
Definition: msg_xml.h:276
int migration_threshold
Migration threshold.
Definition: resources.h:426
GHashTable * tickets
Definition: scheduler.h:190
Cluster layer node.
Definition: nodes.h:34
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:434
pcmk_resource_t * container
Resource containing this one, if any.
Definition: resources.h:480
int pe__resource_text(pcmk__output_t *out, va_list args)
#define XML_RSC_OP_T_QUEUE
Definition: msg_xml.h:328
Implementation of pcmk_scheduler_t.
Definition: scheduler.h:172
pcmk_resource_t * rsc_lh
Definition: internal.h:160
xmlNode * get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
Definition: xpath.c:211
#define PCMK__LAST_FAILURE_PREFIX
Definition: internal.h:297
int pe_get_failcount(const pcmk_node_t *node, pcmk_resource_t *rsc, time_t *last_failure, uint32_t flags, const xmlNode *xml_op)
Definition: failcounts.c:360
int pe__bundle_html(pcmk__output_t *out, va_list args)
int ninstances
Total number of resource instances.
Definition: scheduler.h:224
void void void pcmk__formatted_printf(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
GList * resources
Resources in cluster.
Definition: scheduler.h:196
#define PCMK__ROLE_PROMOTED
int stonith_timeout
Value of stonith-timeout property.
Definition: scheduler.h:185
GList * nodes
Nodes in cluster.
Definition: scheduler.h:195
GList * pe__filter_rsc_list(GList *rscs, GList *filter)
Definition: utils.c:781
char * pcmk__format_nvpair(const char *name, const char *value, const char *units)
Definition: nvpair.c:284
#define PCMK__OUTPUT_SPACER_IF(out_obj, cond)
gboolean is_dc
Whether node is cluster&#39;s DC.
Definition: nodes.h:80
#define XML_LRM_ATTR_TASK_KEY
Definition: msg_xml.h:307
#define PCMK__ROLE_PROMOTED_LEGACY
#define XML_LRM_ATTR_TASK
Definition: msg_xml.h:306
int weight
Node score for a given resource.
Definition: nodes.h:131
pcmk_resource_t * pe_find_resource(GList *rsc_list, const char *id_rh)
Definition: status.c:391
#define CRM_ATTR_FEATURE_SET
Definition: crm.h:124
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
Definition: nvpair.c:540
int pe__rscs_brief_output(pcmk__output_t *out, GList *rsc_list, unsigned int options)
Implementation of pcmk_resource_t.
Definition: resources.h:399
int pe__bundle_text(pcmk__output_t *out, va_list args)
Used only to initialize variables.
Definition: results.h:316
Primitive resource.
Definition: resources.h:34
#define XML_ATTR_ID
Definition: msg_xml.h:156
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:447
pcmk_node_t * pe_find_node_id(const GList *node_list, const char *id)
Find a node by ID in a list of nodes.
Definition: status.c:448
#define XML_CIB_TAG_STATE
Definition: msg_xml.h:222
xmlNode * pcmk_find_cib_element(xmlNode *cib, const char *element_name)
Find an element in the CIB.
Definition: cib.c:172
xmlNode * pcmk_create_xml_text_node(xmlNode *parent, const char *name, const char *content)
Definition: xml.c:672
bool pcmk_xe_mask_probe_failure(const xmlNode *xml_op)
Definition: actions.c:518
int pe__clone_xml(pcmk__output_t *out, va_list args)
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1217
bool pe__rsc_running_on_any(pcmk_resource_t *rsc, GList *node_list)
Definition: utils.c:761
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int pe__node_health(pcmk_node_t *node)
Definition: pe_health.c:114
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:99
int blocked_resources
Number of blocked resources in cluster.
Definition: scheduler.h:219
struct pe_node_shared_s * details
Basic node information.
Definition: nodes.h:134
#define XML_AGENT_ATTR_PROVIDER
Definition: msg_xml.h:281
#define XML_ATTR_HAVE_QUORUM
Definition: msg_xml.h:145
unsigned long long flags
Group of enum pcmk_rsc_flags.
Definition: resources.h:429
const char * uname
Node name in cluster.
Definition: nodes.h:68
const char * pe__resource_description(const pcmk_resource_t *rsc, uint32_t show_opts)
Definition: pe_output.c:22
rsc_role_e
Definition: roles.h:27
#define XML_ATTR_UNAME
Definition: msg_xml.h:178
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:638
Action completed, result is known.
Definition: results.h:318
Ticket constraint object.
Definition: tickets.h:27
xmlNodePtr pcmk__output_create_xml_node(pcmk__output_t *out, const char *name,...) G_GNUC_NULL_TERMINATED
Definition: output_xml.c:478
#define XML_ATTR_DESC
Definition: msg_xml.h:155
int crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest)
Retrieve the seconds-since-epoch value of an XML attribute.
Definition: nvpair.c:568
#define PCMK__FAIL_COUNT_PREFIX
Definition: internal.h:296
time_t last_granted
When cluster was last granted the ticket.
Definition: tickets.h:30
GHashTable * utilization
Resource&#39;s utilization attributes.
Definition: resources.h:473
#define XML_RSC_ATTR_TARGET_ROLE
Definition: msg_xml.h:249
gboolean standby
Whether in standby mode.
Definition: nodes.h:73
#define XML_LRM_ATTR_EXIT_REASON
Definition: msg_xml.h:324
gboolean expected_up
Whether expected join state is member.
Definition: nodes.h:79
#define crm_time_log_with_timezone
Definition: iso8601.h:69
Implementation of pcmk_node_t.
Definition: nodes.h:130
enum pe_obj_types variant
Resource variant.
Definition: resources.h:414
xmlNode * input
CIB XML.
Definition: scheduler.h:175
gboolean granted
Whether cluster has been granted the ticket.
Definition: tickets.h:29
void pcmk__output_xml_pop_parent(pcmk__output_t *out)
Definition: output_xml.c:522
Service safely stopped.
Definition: results.h:249
const char * id
Node ID at the cluster layer.
Definition: nodes.h:67
char * id
XML ID of ticket constraint or state.
Definition: tickets.h:28
void pcmk__xe_set_props(xmlNodePtr node,...) G_GNUC_NULL_TERMINATED
Definition: xml.c:2654
int pe__resource_xml(pcmk__output_t *out, va_list args)
#define PCMK__OUTPUT_LIST_FOOTER(out_obj, retcode)
GList * running_rsc
List of resources active on node.
Definition: nodes.h:113
pcmk_pacemakerd_state
Pacemaker Remote node.
Definition: nodes.h:35
bool pe__is_guest_node(const pcmk_node_t *node)
Definition: remote.c:33
gboolean(* active)(pcmk_resource_t *rsc, gboolean all)
Check whether a resource is active.
Definition: resources.h:317
const char * localhost
Definition: scheduler.h:216
int pe__bundle_xml(pcmk__output_t *out, va_list args)
#define PCMK__OUTPUT_LIST_HEADER(out_obj, cond, retcode, title...)
pcmk_rsc_methods_t * fns
Resource object methods.
Definition: resources.h:416
Whether to stop all resources (via stop-all-resources property)
Definition: scheduler.h:104
Whether fencing is enabled (via stonith-enabled property)
Definition: scheduler.h:80
#define XML_LRM_TAG_RESOURCES
Definition: msg_xml.h:277
pcmk_scheduler_t * scheduler
const char * pcmk_pacemakerd_api_daemon_state_enum2text(enum pcmk_pacemakerd_state state)
char * pe__node_display_name(pcmk_node_t *node, bool print_detail)
Definition: pe_output.c:544
#define CRM_ASSERT(expr)
Definition: results.h:42
void(*) void(* list_item)(pcmk__output_t *out, const char *name, const char *format,...) G_GNUC_PRINTF(3
int disabled_resources
Number of disabled resources in cluster.
Definition: scheduler.h:220
char * pcmk__epoch2str(const time_t *source, uint32_t flags)
Definition: iso8601.c:1858
#define XML_CIB_ATTR_WRITTEN
Definition: msg_xml.h:153
int pe__group_xml(pcmk__output_t *out, va_list args)
This structure contains everything that makes up a single output formatter.
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition: actions.c:96
#define XML_LRM_ATTR_INTERVAL_MS
Definition: msg_xml.h:304
#define XML_LRM_ATTR_CALLID
Definition: msg_xml.h:318
#define XML_NVPAIR_ATTR_VALUE
Definition: msg_xml.h:394
GHashTable * utilization
Node utilization attributes.
Definition: nodes.h:116
gboolean shutdown
Whether shutting down.
Definition: nodes.h:78
void(* begin_list)(pcmk__output_t *out, const char *singular_noun, const char *plural_noun, const char *format,...) G_GNUC_PRINTF(4
char uname[MAX_NAME]
Definition: cpg.c:50
#define XML_LRM_ATTR_OPSTATUS
Definition: msg_xml.h:316
bool pe__is_remote_node(const pcmk_node_t *node)
Definition: remote.c:25
int pe__resource_html(pcmk__output_t *out, va_list args)
const char * pcmk__pcmkd_state_enum2friendly(enum pcmk_pacemakerd_state state)
#define pcmk__plural_s(i)
gboolean(* is_filtered)(const pcmk_resource_t *rsc, GList *only_rsc, gboolean check_parent)
Check whether a given resource is in a list of resources.
Definition: resources.h:369
gboolean maintenance
Whether in maintenance mode.
Definition: nodes.h:81
#define XML_LRM_ATTR_RC
Definition: msg_xml.h:317
#define pcmk_ok
Definition: results.h:68
GList * placement_constraints
Location constraints.
Definition: scheduler.h:197
char * dump_xml_formatted(const xmlNode *xml)
Definition: xml.c:1649
#define XML_CIB_TAG_STATUS
Definition: msg_xml.h:204
const char * pcmk__readable_interval(guint interval_ms)
Definition: iso8601.c:1926
uint32_t pcmk_get_ra_caps(const char *standard)
Get capabilities of a resource agent standard.
Definition: agents.c:31
pcmk_node_t * pending_node
Node on which pending_task is happening.
Definition: resources.h:484
gboolean crm_is_true(const char *s)
Definition: strings.c:416
char * pcmk__trim(char *str)
Definition: strings.c:453
#define XML_LRM_TAG_RSC_OP
Definition: msg_xml.h:279
GHashTable * pe_rsc_params(pcmk_resource_t *rsc, const pcmk_node_t *node, pcmk_scheduler_t *scheduler)
Get a table of resource parameters.
Definition: complex.c:446
#define ID(x)
Definition: msg_xml.h:474
unsigned long long flags
Group of enum pcmk_scheduler_flags.
Definition: scheduler.h:183
const char * parent
Definition: cib.c:27
char * crm_xml_escape(const char *text)
Replace special characters with their XML escape sequences.
Definition: xml.c:1329
gboolean standby_onfail
Whether in standby mode due to on-fail.
Definition: nodes.h:74
gboolean unclean
Whether node requires fencing.
Definition: nodes.h:76
Whether cluster is in maintenance mode (via maintenance-mode property)
Definition: scheduler.h:77
void pe__register_messages(pcmk__output_t *out)
Definition: pe_output.c:3162
Whether resource has been removed from the configuration.
Definition: resources.h:103
enum node_type type
Node variant.
Definition: nodes.h:69
#define crm_time_log_date
Definition: iso8601.h:67
pcmk_node_t * dc_node
Node object for DC.
Definition: scheduler.h:178
gboolean online
Whether online.
Definition: nodes.h:72
uint64_t flags
Definition: remote.c:215
pcmk_resource_t * remote_rsc
Remote connection resource for node, if it is a Pacemaker Remote node.
Definition: nodes.h:111
#define PCMK_ACTION_NOTIFY
Definition: actions.h:61
#define XML_AGENT_ATTR_CLASS
Definition: msg_xml.h:280
char * id
Resource ID in configuration.
Definition: resources.h:400
gboolean pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags)
Definition: strings.c:888
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2510