Line data Source code
1 : /*
2 : * Copyright 2019-2024 the Pacemaker project contributors
3 : *
4 : * The version control history for this file may have further details.
5 : *
6 : * This source code is licensed under the GNU Lesser General Public License
7 : * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 : */
9 :
10 : #include <crm_internal.h>
11 :
12 : #include <stdint.h>
13 :
14 : #include <crm/common/xml_internal.h>
15 : #include <crm/common/output.h>
16 : #include <crm/common/scheduler_internal.h>
17 : #include <crm/cib/util.h>
18 : #include <crm/common/xml.h>
19 : #include <crm/pengine/internal.h>
20 :
21 : const char *
22 0 : pe__resource_description(const pcmk_resource_t *rsc, uint32_t show_opts)
23 : {
24 0 : const char * desc = NULL;
25 : // User-supplied description
26 0 : if (pcmk_any_flags_set(show_opts, pcmk_show_rsc_only|pcmk_show_description)) {
27 0 : desc = crm_element_value(rsc->xml, PCMK_XA_DESCRIPTION);
28 : }
29 0 : 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 : PCMK__NODE_ATTR_SHUTDOWN, PCMK_NODE_ATTR_TERMINATE, \
35 : PCMK_NODE_ATTR_STANDBY, "#", NULL }
36 :
37 : static int
38 0 : compare_attribute(gconstpointer a, gconstpointer b)
39 : {
40 : int rc;
41 :
42 0 : rc = strcmp((const char *)a, (const char *)b);
43 :
44 0 : return rc;
45 : }
46 :
47 : /*!
48 : * \internal
49 : * \brief Determine whether extended information about an attribute should be added.
50 : *
51 : * \param[in] node Node that ran this resource
52 : * \param[in,out] rsc_list List of resources for this node
53 : * \param[in,out] scheduler Scheduler data
54 : * \param[in] attrname Attribute to find
55 : * \param[out] expected_score Expected value for this attribute
56 : *
57 : * \return true if extended information should be printed, false otherwise
58 : * \note Currently, extended information is only supported for ping/pingd
59 : * resources, for which a message will be printed if connectivity is lost
60 : * or degraded.
61 : */
62 : static bool
63 0 : 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 0 : GList *gIter = NULL;
68 :
69 0 : for (gIter = rsc_list; gIter != NULL; gIter = gIter->next) {
70 0 : pcmk_resource_t *rsc = (pcmk_resource_t *) gIter->data;
71 0 : const char *type = g_hash_table_lookup(rsc->meta, PCMK_XA_TYPE);
72 0 : const char *name = NULL;
73 0 : GHashTable *params = NULL;
74 :
75 0 : if (rsc->children != NULL) {
76 0 : if (add_extra_info(node, rsc->children, scheduler, attrname,
77 : expected_score)) {
78 0 : return true;
79 : }
80 : }
81 :
82 0 : if (!pcmk__strcase_any_of(type, "ping", "pingd", NULL)) {
83 0 : continue;
84 : }
85 :
86 0 : params = pe_rsc_params(rsc, node, scheduler);
87 0 : name = g_hash_table_lookup(params, PCMK_XA_NAME);
88 :
89 0 : if (name == NULL) {
90 0 : name = "pingd";
91 : }
92 :
93 : /* To identify the resource with the attribute name. */
94 0 : if (pcmk__str_eq(name, attrname, pcmk__str_casei)) {
95 0 : int host_list_num = 0;
96 0 : const char *hosts = g_hash_table_lookup(params, "host_list");
97 0 : const char *multiplier = g_hash_table_lookup(params, "multiplier");
98 : int multiplier_i;
99 :
100 0 : if (hosts) {
101 0 : char **host_list = g_strsplit(hosts, " ", 0);
102 0 : host_list_num = g_strv_length(host_list);
103 0 : g_strfreev(host_list);
104 : }
105 :
106 0 : if ((multiplier == NULL)
107 0 : || (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 0 : multiplier_i = 1;
114 : }
115 0 : *expected_score = host_list_num * multiplier_i;
116 :
117 0 : return true;
118 : }
119 : }
120 0 : return false;
121 : }
122 :
123 : static GList *
124 0 : filter_attr_list(GList *attr_list, char *name)
125 : {
126 : int i;
127 0 : const char *filt_str[] = FILTER_STR;
128 :
129 0 : CRM_CHECK(name != NULL, return attr_list);
130 :
131 : /* filtering automatic attributes */
132 0 : for (i = 0; filt_str[i] != NULL; i++) {
133 0 : if (g_str_has_prefix(name, filt_str[i])) {
134 0 : return attr_list;
135 : }
136 : }
137 :
138 0 : return g_list_insert_sorted(attr_list, name, compare_attribute);
139 : }
140 :
141 : static GList *
142 0 : get_operation_list(xmlNode *rsc_entry) {
143 0 : GList *op_list = NULL;
144 0 : xmlNode *rsc_op = NULL;
145 :
146 0 : for (rsc_op = pcmk__xe_first_child(rsc_entry, NULL, NULL, NULL);
147 0 : rsc_op != NULL; rsc_op = pcmk__xe_next(rsc_op)) {
148 :
149 0 : const char *task = crm_element_value(rsc_op, PCMK_XA_OPERATION);
150 0 : const char *interval_ms_s = crm_element_value(rsc_op,
151 : PCMK_META_INTERVAL);
152 0 : const char *op_rc = crm_element_value(rsc_op, PCMK__XA_RC_CODE);
153 : int op_rc_i;
154 :
155 0 : pcmk__scan_min_int(op_rc, &op_rc_i, 0);
156 :
157 : /* Display 0-interval monitors as "probe" */
158 0 : if (pcmk__str_eq(task, PCMK_ACTION_MONITOR, pcmk__str_casei)
159 0 : && pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches | pcmk__str_casei)) {
160 0 : task = "probe";
161 : }
162 :
163 : /* Ignore notifies and some probes */
164 0 : if (pcmk__str_eq(task, PCMK_ACTION_NOTIFY, pcmk__str_none)
165 0 : || (pcmk__str_eq(task, "probe", pcmk__str_none)
166 0 : && (op_rc_i == CRM_EX_NOT_RUNNING))) {
167 0 : continue;
168 : }
169 :
170 0 : if (pcmk__xe_is(rsc_op, PCMK__XE_LRM_RSC_OP)) {
171 0 : op_list = g_list_append(op_list, rsc_op);
172 : }
173 : }
174 :
175 0 : op_list = g_list_sort(op_list, sort_op_by_callid);
176 0 : return op_list;
177 : }
178 :
179 : static void
180 0 : add_dump_node(gpointer key, gpointer value, gpointer user_data)
181 : {
182 0 : xmlNodePtr node = user_data;
183 :
184 0 : node = pcmk__xe_create(node, (const char *) key);
185 0 : pcmk__xe_set_content(node, "%s", (const char *) value);
186 0 : }
187 :
188 : static void
189 0 : append_dump_text(gpointer key, gpointer value, gpointer user_data)
190 : {
191 0 : char **dump_text = user_data;
192 0 : char *new_text = crm_strdup_printf("%s %s=%s",
193 : *dump_text, (char *)key, (char *)value);
194 :
195 0 : free(*dump_text);
196 0 : *dump_text = new_text;
197 0 : }
198 :
199 : #define XPATH_STACK "//" PCMK_XE_NVPAIR \
200 : "[@" PCMK_XA_NAME "='" \
201 : PCMK_OPT_CLUSTER_INFRASTRUCTURE "']"
202 :
203 : static const char *
204 0 : get_cluster_stack(pcmk_scheduler_t *scheduler)
205 : {
206 0 : xmlNode *stack = get_xpath_object(XPATH_STACK, scheduler->input, LOG_DEBUG);
207 :
208 0 : if (stack != NULL) {
209 0 : return crm_element_value(stack, PCMK_XA_VALUE);
210 : }
211 0 : return PCMK_VALUE_UNKNOWN;
212 : }
213 :
214 : static char *
215 0 : last_changed_string(const char *last_written, const char *user,
216 : const char *client, const char *origin) {
217 0 : if (last_written != NULL || user != NULL || client != NULL || origin != NULL) {
218 0 : return crm_strdup_printf("%s%s%s%s%s%s%s",
219 : last_written ? last_written : "",
220 : user ? " by " : "",
221 : user ? user : "",
222 : client ? " via " : "",
223 : client ? client : "",
224 : origin ? " on " : "",
225 : origin ? origin : "");
226 : } else {
227 0 : return strdup("");
228 : }
229 : }
230 :
231 : static char *
232 0 : op_history_string(xmlNode *xml_op, const char *task, const char *interval_ms_s,
233 : int rc, bool print_timing) {
234 0 : const char *call = crm_element_value(xml_op, PCMK__XA_CALL_ID);
235 0 : char *interval_str = NULL;
236 0 : char *buf = NULL;
237 :
238 0 : if (interval_ms_s && !pcmk__str_eq(interval_ms_s, "0", pcmk__str_casei)) {
239 0 : char *pair = pcmk__format_nvpair(PCMK_XA_INTERVAL, interval_ms_s, "ms");
240 0 : interval_str = crm_strdup_printf(" %s", pair);
241 0 : free(pair);
242 : }
243 :
244 0 : if (print_timing) {
245 0 : char *last_change_str = NULL;
246 0 : char *exec_str = NULL;
247 0 : char *queue_str = NULL;
248 :
249 0 : const char *value = NULL;
250 :
251 0 : time_t epoch = 0;
252 :
253 0 : if ((crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE,
254 : &epoch) == pcmk_ok)
255 0 : && (epoch > 0)) {
256 0 : char *epoch_str = pcmk__epoch2str(&epoch, 0);
257 :
258 0 : last_change_str = crm_strdup_printf(" %s=\"%s\"",
259 : PCMK_XA_LAST_RC_CHANGE,
260 : pcmk__s(epoch_str, ""));
261 0 : free(epoch_str);
262 : }
263 :
264 0 : value = crm_element_value(xml_op, PCMK_XA_EXEC_TIME);
265 0 : if (value) {
266 0 : char *pair = pcmk__format_nvpair(PCMK_XA_EXEC_TIME, value, "ms");
267 0 : exec_str = crm_strdup_printf(" %s", pair);
268 0 : free(pair);
269 : }
270 :
271 0 : value = crm_element_value(xml_op, PCMK_XA_QUEUE_TIME);
272 0 : if (value) {
273 0 : char *pair = pcmk__format_nvpair(PCMK_XA_QUEUE_TIME, value, "ms");
274 0 : queue_str = crm_strdup_printf(" %s", pair);
275 0 : free(pair);
276 : }
277 :
278 0 : buf = crm_strdup_printf("(%s) %s:%s%s%s%s rc=%d (%s)", call, task,
279 : interval_str ? interval_str : "",
280 : last_change_str ? last_change_str : "",
281 : exec_str ? exec_str : "",
282 : queue_str ? queue_str : "",
283 : rc, services_ocf_exitcode_str(rc));
284 :
285 0 : if (last_change_str) {
286 0 : free(last_change_str);
287 : }
288 :
289 0 : if (exec_str) {
290 0 : free(exec_str);
291 : }
292 :
293 0 : if (queue_str) {
294 0 : free(queue_str);
295 : }
296 : } else {
297 0 : buf = crm_strdup_printf("(%s) %s%s%s", call, task,
298 : interval_str ? ":" : "",
299 : interval_str ? interval_str : "");
300 : }
301 :
302 0 : if (interval_str) {
303 0 : free(interval_str);
304 : }
305 :
306 0 : return buf;
307 : }
308 :
309 : static char *
310 0 : resource_history_string(pcmk_resource_t *rsc, const char *rsc_id, bool all,
311 : int failcount, time_t last_failure) {
312 0 : char *buf = NULL;
313 :
314 0 : if (rsc == NULL) {
315 0 : buf = crm_strdup_printf("%s: orphan", rsc_id);
316 0 : } else if (all || failcount || last_failure > 0) {
317 0 : char *failcount_s = NULL;
318 0 : char *lastfail_s = NULL;
319 :
320 0 : if (failcount > 0) {
321 0 : failcount_s = crm_strdup_printf(" %s=%d",
322 : PCMK_XA_FAIL_COUNT, failcount);
323 : } else {
324 0 : failcount_s = strdup("");
325 : }
326 0 : if (last_failure > 0) {
327 0 : buf = pcmk__epoch2str(&last_failure, 0);
328 0 : lastfail_s = crm_strdup_printf(" %s='%s'",
329 : PCMK_XA_LAST_FAILURE, buf);
330 0 : free(buf);
331 : }
332 :
333 0 : buf = crm_strdup_printf("%s: " PCMK_META_MIGRATION_THRESHOLD "=%d%s%s",
334 : rsc_id, rsc->migration_threshold, failcount_s,
335 : lastfail_s? lastfail_s : "");
336 0 : free(failcount_s);
337 0 : free(lastfail_s);
338 : } else {
339 0 : buf = crm_strdup_printf("%s:", rsc_id);
340 : }
341 :
342 0 : return buf;
343 : }
344 :
345 : /*!
346 : * \internal
347 : * \brief Get a node's feature set for status display purposes
348 : *
349 : * \param[in] node Node to check
350 : *
351 : * \return String representation of feature set if the node is fully up (using
352 : * "<3.15.1" for older nodes that don't set the #feature-set attribute),
353 : * otherwise NULL
354 : */
355 : static const char *
356 0 : get_node_feature_set(const pcmk_node_t *node)
357 : {
358 0 : if (node->details->online && node->details->expected_up
359 0 : && !pcmk__is_pacemaker_remote_node(node)) {
360 :
361 0 : const char *feature_set = g_hash_table_lookup(node->details->attrs,
362 : CRM_ATTR_FEATURE_SET);
363 :
364 : /* The feature set attribute is present since 3.15.1. If it is missing,
365 : * then the node must be running an earlier version.
366 : */
367 0 : return pcmk__s(feature_set, "<3.15.1");
368 : }
369 0 : return NULL;
370 : }
371 :
372 : static bool
373 0 : is_mixed_version(pcmk_scheduler_t *scheduler)
374 : {
375 0 : const char *feature_set = NULL;
376 0 : for (GList *gIter = scheduler->nodes; gIter != NULL; gIter = gIter->next) {
377 0 : pcmk_node_t *node = gIter->data;
378 0 : const char *node_feature_set = get_node_feature_set(node);
379 0 : if (node_feature_set != NULL) {
380 0 : if (feature_set == NULL) {
381 0 : feature_set = node_feature_set;
382 0 : } else if (strcmp(feature_set, node_feature_set) != 0) {
383 0 : return true;
384 : }
385 : }
386 : }
387 0 : return false;
388 : }
389 :
390 : static void
391 0 : formatted_xml_buf(const pcmk_resource_t *rsc, GString *xml_buf, bool raw)
392 : {
393 0 : if (raw && (rsc->orig_xml != NULL)) {
394 0 : pcmk__xml_string(rsc->orig_xml, pcmk__xml_fmt_pretty, xml_buf, 0);
395 : } else {
396 0 : pcmk__xml_string(rsc->xml, pcmk__xml_fmt_pretty, xml_buf, 0);
397 : }
398 0 : }
399 :
400 : #define XPATH_DC_VERSION "//" PCMK_XE_NVPAIR \
401 : "[@" PCMK_XA_NAME "='" PCMK_OPT_DC_VERSION "']"
402 :
403 : PCMK__OUTPUT_ARGS("cluster-summary", "pcmk_scheduler_t *",
404 : "enum pcmk_pacemakerd_state", "uint32_t", "uint32_t")
405 : static int
406 0 : cluster_summary(pcmk__output_t *out, va_list args) {
407 0 : pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
408 0 : enum pcmk_pacemakerd_state pcmkd_state =
409 : (enum pcmk_pacemakerd_state) va_arg(args, int);
410 0 : uint32_t section_opts = va_arg(args, uint32_t);
411 0 : uint32_t show_opts = va_arg(args, uint32_t);
412 :
413 0 : int rc = pcmk_rc_no_output;
414 0 : const char *stack_s = get_cluster_stack(scheduler);
415 :
416 0 : if (pcmk_is_set(section_opts, pcmk_section_stack)) {
417 0 : PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
418 0 : out->message(out, "cluster-stack", stack_s, pcmkd_state);
419 : }
420 :
421 0 : if (pcmk_is_set(section_opts, pcmk_section_dc)) {
422 0 : xmlNode *dc_version = get_xpath_object(XPATH_DC_VERSION,
423 : scheduler->input, LOG_DEBUG);
424 0 : const char *dc_version_s = dc_version?
425 0 : crm_element_value(dc_version, PCMK_XA_VALUE)
426 0 : : NULL;
427 0 : const char *quorum = crm_element_value(scheduler->input,
428 : PCMK_XA_HAVE_QUORUM);
429 0 : char *dc_name = scheduler->dc_node? pe__node_display_name(scheduler->dc_node, pcmk_is_set(show_opts, pcmk_show_node_id)) : NULL;
430 0 : bool mixed_version = is_mixed_version(scheduler);
431 :
432 0 : PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
433 0 : out->message(out, "cluster-dc", scheduler->dc_node, quorum,
434 : dc_version_s, dc_name, mixed_version);
435 0 : free(dc_name);
436 : }
437 :
438 0 : if (pcmk_is_set(section_opts, pcmk_section_times)) {
439 0 : const char *last_written = crm_element_value(scheduler->input,
440 : PCMK_XA_CIB_LAST_WRITTEN);
441 0 : const char *user = crm_element_value(scheduler->input,
442 : PCMK_XA_UPDATE_USER);
443 0 : const char *client = crm_element_value(scheduler->input,
444 : PCMK_XA_UPDATE_CLIENT);
445 0 : const char *origin = crm_element_value(scheduler->input,
446 : PCMK_XA_UPDATE_ORIGIN);
447 :
448 0 : PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
449 0 : out->message(out, "cluster-times",
450 : scheduler->localhost, last_written, user, client, origin);
451 : }
452 :
453 0 : if (pcmk_is_set(section_opts, pcmk_section_counts)) {
454 0 : PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
455 0 : out->message(out, "cluster-counts", g_list_length(scheduler->nodes),
456 : scheduler->ninstances, scheduler->disabled_resources,
457 : scheduler->blocked_resources);
458 : }
459 :
460 0 : if (pcmk_is_set(section_opts, pcmk_section_options)) {
461 0 : PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
462 0 : out->message(out, "cluster-options", scheduler);
463 : }
464 :
465 0 : PCMK__OUTPUT_LIST_FOOTER(out, rc);
466 :
467 0 : if (pcmk_is_set(section_opts, pcmk_section_maint_mode)) {
468 0 : if (out->message(out, "maint-mode", scheduler->flags) == pcmk_rc_ok) {
469 0 : rc = pcmk_rc_ok;
470 : }
471 : }
472 :
473 0 : return rc;
474 : }
475 :
476 : PCMK__OUTPUT_ARGS("cluster-summary", "pcmk_scheduler_t *",
477 : "enum pcmk_pacemakerd_state", "uint32_t", "uint32_t")
478 : static int
479 0 : cluster_summary_html(pcmk__output_t *out, va_list args) {
480 0 : pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
481 0 : enum pcmk_pacemakerd_state pcmkd_state =
482 : (enum pcmk_pacemakerd_state) va_arg(args, int);
483 0 : uint32_t section_opts = va_arg(args, uint32_t);
484 0 : uint32_t show_opts = va_arg(args, uint32_t);
485 :
486 0 : int rc = pcmk_rc_no_output;
487 0 : const char *stack_s = get_cluster_stack(scheduler);
488 :
489 0 : if (pcmk_is_set(section_opts, pcmk_section_stack)) {
490 0 : PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
491 0 : out->message(out, "cluster-stack", stack_s, pcmkd_state);
492 : }
493 :
494 : /* Always print DC if none, even if not requested */
495 0 : if ((scheduler->dc_node == NULL)
496 0 : || pcmk_is_set(section_opts, pcmk_section_dc)) {
497 0 : xmlNode *dc_version = get_xpath_object(XPATH_DC_VERSION,
498 : scheduler->input, LOG_DEBUG);
499 0 : const char *dc_version_s = dc_version?
500 0 : crm_element_value(dc_version, PCMK_XA_VALUE)
501 0 : : NULL;
502 0 : const char *quorum = crm_element_value(scheduler->input,
503 : PCMK_XA_HAVE_QUORUM);
504 0 : char *dc_name = scheduler->dc_node? pe__node_display_name(scheduler->dc_node, pcmk_is_set(show_opts, pcmk_show_node_id)) : NULL;
505 0 : bool mixed_version = is_mixed_version(scheduler);
506 :
507 0 : PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
508 0 : out->message(out, "cluster-dc", scheduler->dc_node, quorum,
509 : dc_version_s, dc_name, mixed_version);
510 0 : free(dc_name);
511 : }
512 :
513 0 : if (pcmk_is_set(section_opts, pcmk_section_times)) {
514 0 : const char *last_written = crm_element_value(scheduler->input,
515 : PCMK_XA_CIB_LAST_WRITTEN);
516 0 : const char *user = crm_element_value(scheduler->input,
517 : PCMK_XA_UPDATE_USER);
518 0 : const char *client = crm_element_value(scheduler->input,
519 : PCMK_XA_UPDATE_CLIENT);
520 0 : const char *origin = crm_element_value(scheduler->input,
521 : PCMK_XA_UPDATE_ORIGIN);
522 :
523 0 : PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
524 0 : out->message(out, "cluster-times",
525 : scheduler->localhost, last_written, user, client, origin);
526 : }
527 :
528 0 : if (pcmk_is_set(section_opts, pcmk_section_counts)) {
529 0 : PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
530 0 : out->message(out, "cluster-counts", g_list_length(scheduler->nodes),
531 : scheduler->ninstances, scheduler->disabled_resources,
532 : scheduler->blocked_resources);
533 : }
534 :
535 0 : if (pcmk_is_set(section_opts, pcmk_section_options)) {
536 : /* Kind of a hack - close the list we may have opened earlier in this
537 : * function so we can put all the options into their own list. We
538 : * only want to do this on HTML output, though.
539 : */
540 0 : PCMK__OUTPUT_LIST_FOOTER(out, rc);
541 :
542 0 : out->begin_list(out, NULL, NULL, "Config Options");
543 0 : out->message(out, "cluster-options", scheduler);
544 : }
545 :
546 0 : PCMK__OUTPUT_LIST_FOOTER(out, rc);
547 :
548 0 : if (pcmk_is_set(section_opts, pcmk_section_maint_mode)) {
549 0 : if (out->message(out, "maint-mode", scheduler->flags) == pcmk_rc_ok) {
550 0 : rc = pcmk_rc_ok;
551 : }
552 : }
553 :
554 0 : return rc;
555 : }
556 :
557 : char *
558 0 : pe__node_display_name(pcmk_node_t *node, bool print_detail)
559 : {
560 : char *node_name;
561 0 : const char *node_host = NULL;
562 0 : const char *node_id = NULL;
563 : int name_len;
564 :
565 0 : CRM_ASSERT((node != NULL) && (node->details != NULL) && (node->details->uname != NULL));
566 :
567 : /* Host is displayed only if this is a guest node and detail is requested */
568 0 : if (print_detail && pcmk__is_guest_or_bundle_node(node)) {
569 0 : const pcmk_resource_t *container = node->details->remote_rsc->container;
570 0 : const pcmk_node_t *host_node = pcmk__current_node(container);
571 :
572 0 : if (host_node && host_node->details) {
573 0 : node_host = host_node->details->uname;
574 : }
575 0 : if (node_host == NULL) {
576 0 : node_host = ""; /* so we at least get "uname@" to indicate guest */
577 : }
578 : }
579 :
580 : /* Node ID is displayed if different from uname and detail is requested */
581 0 : if (print_detail && !pcmk__str_eq(node->details->uname, node->details->id, pcmk__str_casei)) {
582 0 : node_id = node->details->id;
583 : }
584 :
585 : /* Determine name length */
586 0 : name_len = strlen(node->details->uname) + 1;
587 0 : if (node_host) {
588 0 : name_len += strlen(node_host) + 1; /* "@node_host" */
589 : }
590 0 : if (node_id) {
591 0 : name_len += strlen(node_id) + 3; /* + " (node_id)" */
592 : }
593 :
594 : /* Allocate and populate display name */
595 0 : node_name = pcmk__assert_alloc(name_len, sizeof(char));
596 0 : strcpy(node_name, node->details->uname);
597 0 : if (node_host) {
598 0 : strcat(node_name, "@");
599 0 : strcat(node_name, node_host);
600 : }
601 0 : if (node_id) {
602 0 : strcat(node_name, " (");
603 0 : strcat(node_name, node_id);
604 0 : strcat(node_name, ")");
605 : }
606 0 : return node_name;
607 : }
608 :
609 : int
610 0 : pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name,
611 : ...)
612 : {
613 0 : xmlNodePtr xml_node = NULL;
614 : va_list pairs;
615 :
616 0 : CRM_ASSERT(tag_name != NULL);
617 :
618 0 : xml_node = pcmk__output_xml_peek_parent(out);
619 0 : CRM_ASSERT(xml_node != NULL);
620 0 : xml_node = pcmk__xe_create(xml_node, tag_name);
621 :
622 0 : va_start(pairs, tag_name);
623 0 : pcmk__xe_set_propv(xml_node, pairs);
624 0 : va_end(pairs);
625 :
626 0 : if (is_list) {
627 0 : pcmk__output_xml_push_parent(out, xml_node);
628 : }
629 0 : return pcmk_rc_ok;
630 : }
631 :
632 : static const char *
633 0 : role_desc(enum rsc_role_e role)
634 : {
635 0 : if (role == pcmk_role_promoted) {
636 : #ifdef PCMK__COMPAT_2_0
637 : return "as " PCMK__ROLE_PROMOTED_LEGACY " ";
638 : #else
639 0 : return "in " PCMK_ROLE_PROMOTED " role ";
640 : #endif
641 : }
642 0 : return "";
643 : }
644 :
645 : PCMK__OUTPUT_ARGS("ban", "pcmk_node_t *", "pcmk__location_t *", "uint32_t")
646 : static int
647 0 : ban_html(pcmk__output_t *out, va_list args) {
648 0 : pcmk_node_t *pe_node = va_arg(args, pcmk_node_t *);
649 0 : pcmk__location_t *location = va_arg(args, pcmk__location_t *);
650 0 : uint32_t show_opts = va_arg(args, uint32_t);
651 :
652 0 : char *node_name = pe__node_display_name(pe_node,
653 0 : pcmk_is_set(show_opts, pcmk_show_node_id));
654 0 : char *buf = crm_strdup_printf("%s\tprevents %s from running %son %s",
655 0 : location->id, location->rsc->id,
656 : role_desc(location->role_filter), node_name);
657 :
658 0 : pcmk__output_create_html_node(out, "li", NULL, NULL, buf);
659 :
660 0 : free(node_name);
661 0 : free(buf);
662 0 : return pcmk_rc_ok;
663 : }
664 :
665 : PCMK__OUTPUT_ARGS("ban", "pcmk_node_t *", "pcmk__location_t *", "uint32_t")
666 : static int
667 0 : ban_text(pcmk__output_t *out, va_list args) {
668 0 : pcmk_node_t *pe_node = va_arg(args, pcmk_node_t *);
669 0 : pcmk__location_t *location = va_arg(args, pcmk__location_t *);
670 0 : uint32_t show_opts = va_arg(args, uint32_t);
671 :
672 0 : char *node_name = pe__node_display_name(pe_node,
673 0 : pcmk_is_set(show_opts, pcmk_show_node_id));
674 0 : out->list_item(out, NULL, "%s\tprevents %s from running %son %s",
675 0 : location->id, location->rsc->id,
676 : role_desc(location->role_filter), node_name);
677 :
678 0 : free(node_name);
679 0 : return pcmk_rc_ok;
680 : }
681 :
682 : PCMK__OUTPUT_ARGS("ban", "pcmk_node_t *", "pcmk__location_t *", "uint32_t")
683 : static int
684 0 : ban_xml(pcmk__output_t *out, va_list args) {
685 0 : pcmk_node_t *pe_node = va_arg(args, pcmk_node_t *);
686 0 : pcmk__location_t *location = va_arg(args, pcmk__location_t *);
687 0 : uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t);
688 :
689 0 : const char *promoted_only = pcmk__btoa(location->role_filter == pcmk_role_promoted);
690 0 : char *weight_s = pcmk__itoa(pe_node->weight);
691 :
692 0 : pcmk__output_create_xml_node(out, PCMK_XE_BAN,
693 : PCMK_XA_ID, location->id,
694 0 : PCMK_XA_RESOURCE, location->rsc->id,
695 0 : PCMK_XA_NODE, pe_node->details->uname,
696 : PCMK_XA_WEIGHT, weight_s,
697 : PCMK_XA_PROMOTED_ONLY, promoted_only,
698 : /* This is a deprecated alias for
699 : * promoted_only. Removing it will break
700 : * backward compatibility of the API schema,
701 : * which will require an API schema major
702 : * version bump.
703 : */
704 : PCMK__XA_PROMOTED_ONLY_LEGACY, promoted_only,
705 : NULL);
706 :
707 0 : free(weight_s);
708 0 : return pcmk_rc_ok;
709 : }
710 :
711 : PCMK__OUTPUT_ARGS("ban-list", "pcmk_scheduler_t *", "const char *", "GList *",
712 : "uint32_t", "bool")
713 : static int
714 0 : ban_list(pcmk__output_t *out, va_list args) {
715 0 : pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
716 0 : const char *prefix = va_arg(args, const char *);
717 0 : GList *only_rsc = va_arg(args, GList *);
718 0 : uint32_t show_opts = va_arg(args, uint32_t);
719 0 : bool print_spacer = va_arg(args, int);
720 :
721 : GList *gIter, *gIter2;
722 0 : int rc = pcmk_rc_no_output;
723 :
724 : /* Print each ban */
725 0 : for (gIter = scheduler->placement_constraints;
726 0 : gIter != NULL; gIter = gIter->next) {
727 0 : pcmk__location_t *location = gIter->data;
728 0 : const pcmk_resource_t *rsc = location->rsc;
729 :
730 0 : if (prefix != NULL && !g_str_has_prefix(location->id, prefix)) {
731 0 : continue;
732 : }
733 :
734 0 : if (!pcmk__str_in_list(rsc_printable_id(rsc), only_rsc,
735 : pcmk__str_star_matches)
736 0 : && !pcmk__str_in_list(rsc_printable_id(pe__const_top_resource(rsc, false)),
737 : only_rsc, pcmk__str_star_matches)) {
738 0 : continue;
739 : }
740 :
741 0 : for (gIter2 = location->nodes; gIter2 != NULL; gIter2 = gIter2->next) {
742 0 : pcmk_node_t *node = (pcmk_node_t *) gIter2->data;
743 :
744 0 : if (node->weight < 0) {
745 0 : PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Negative Location Constraints");
746 0 : out->message(out, "ban", node, location, show_opts);
747 : }
748 : }
749 : }
750 :
751 0 : PCMK__OUTPUT_LIST_FOOTER(out, rc);
752 0 : return rc;
753 : }
754 :
755 : PCMK__OUTPUT_ARGS("cluster-counts", "unsigned int", "int", "int", "int")
756 : static int
757 0 : cluster_counts_html(pcmk__output_t *out, va_list args) {
758 0 : unsigned int nnodes = va_arg(args, unsigned int);
759 0 : int nresources = va_arg(args, int);
760 0 : int ndisabled = va_arg(args, int);
761 0 : int nblocked = va_arg(args, int);
762 :
763 0 : xmlNodePtr nodes_node = pcmk__output_create_xml_node(out, "li", NULL);
764 0 : xmlNodePtr resources_node = pcmk__output_create_xml_node(out, "li", NULL);
765 0 : xmlNode *child = NULL;
766 :
767 0 : child = pcmk__html_create(nodes_node, PCMK__XE_SPAN, NULL, NULL);
768 0 : pcmk__xe_set_content(child, "%d node%s configured",
769 : nnodes, pcmk__plural_s(nnodes));
770 :
771 0 : if (ndisabled && nblocked) {
772 0 : child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL);
773 0 : pcmk__xe_set_content(child, "%d resource instance%s configured (%d ",
774 : nresources, pcmk__plural_s(nresources), ndisabled);
775 :
776 0 : child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL,
777 : PCMK__VALUE_BOLD);
778 0 : pcmk__xe_set_content(child, "DISABLED");
779 :
780 0 : child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL);
781 0 : pcmk__xe_set_content(child, ", %d ", nblocked);
782 :
783 0 : child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL,
784 : PCMK__VALUE_BOLD);
785 0 : pcmk__xe_set_content(child, "BLOCKED");
786 :
787 0 : child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL);
788 0 : pcmk__xe_set_content(child, " from further action due to failure)");
789 :
790 0 : } else if (ndisabled && !nblocked) {
791 0 : child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL);
792 0 : pcmk__xe_set_content(child, "%d resource instance%s configured (%d ",
793 : nresources, pcmk__plural_s(nresources),
794 : ndisabled);
795 :
796 0 : child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL,
797 : PCMK__VALUE_BOLD);
798 0 : pcmk__xe_set_content(child, "DISABLED");
799 :
800 0 : child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL);
801 0 : pcmk__xe_set_content(child, ")");
802 :
803 0 : } else if (!ndisabled && nblocked) {
804 0 : child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL);
805 0 : pcmk__xe_set_content(child, "%d resource instance%s configured (%d ",
806 : nresources, pcmk__plural_s(nresources),
807 : nblocked);
808 :
809 0 : child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL,
810 : PCMK__VALUE_BOLD);
811 0 : pcmk__xe_set_content(child, "BLOCKED");
812 :
813 0 : child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL);
814 0 : pcmk__xe_set_content(child, " from further action due to failure)");
815 :
816 : } else {
817 0 : child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL);
818 0 : pcmk__xe_set_content(child, "%d resource instance%s configured",
819 : nresources, pcmk__plural_s(nresources));
820 : }
821 :
822 0 : return pcmk_rc_ok;
823 : }
824 :
825 : PCMK__OUTPUT_ARGS("cluster-counts", "unsigned int", "int", "int", "int")
826 : static int
827 0 : cluster_counts_text(pcmk__output_t *out, va_list args) {
828 0 : unsigned int nnodes = va_arg(args, unsigned int);
829 0 : int nresources = va_arg(args, int);
830 0 : int ndisabled = va_arg(args, int);
831 0 : int nblocked = va_arg(args, int);
832 :
833 0 : out->list_item(out, NULL, "%d node%s configured",
834 : nnodes, pcmk__plural_s(nnodes));
835 :
836 0 : if (ndisabled && nblocked) {
837 0 : out->list_item(out, NULL, "%d resource instance%s configured "
838 : "(%d DISABLED, %d BLOCKED from "
839 : "further action due to failure)",
840 : nresources, pcmk__plural_s(nresources), ndisabled,
841 : nblocked);
842 0 : } else if (ndisabled && !nblocked) {
843 0 : out->list_item(out, NULL, "%d resource instance%s configured "
844 : "(%d DISABLED)",
845 : nresources, pcmk__plural_s(nresources), ndisabled);
846 0 : } else if (!ndisabled && nblocked) {
847 0 : out->list_item(out, NULL, "%d resource instance%s configured "
848 : "(%d BLOCKED from further action "
849 : "due to failure)",
850 : nresources, pcmk__plural_s(nresources), nblocked);
851 : } else {
852 0 : out->list_item(out, NULL, "%d resource instance%s configured",
853 : nresources, pcmk__plural_s(nresources));
854 : }
855 :
856 0 : return pcmk_rc_ok;
857 : }
858 :
859 : PCMK__OUTPUT_ARGS("cluster-counts", "unsigned int", "int", "int", "int")
860 : static int
861 0 : cluster_counts_xml(pcmk__output_t *out, va_list args) {
862 0 : unsigned int nnodes = va_arg(args, unsigned int);
863 0 : int nresources = va_arg(args, int);
864 0 : int ndisabled = va_arg(args, int);
865 0 : int nblocked = va_arg(args, int);
866 :
867 0 : xmlNodePtr nodes_node = NULL;
868 0 : xmlNodePtr resources_node = NULL;
869 0 : char *s = NULL;
870 :
871 0 : nodes_node = pcmk__output_create_xml_node(out, PCMK_XE_NODES_CONFIGURED,
872 : NULL);
873 0 : resources_node = pcmk__output_create_xml_node(out,
874 : PCMK_XE_RESOURCES_CONFIGURED,
875 : NULL);
876 :
877 0 : s = pcmk__itoa(nnodes);
878 0 : crm_xml_add(nodes_node, PCMK_XA_NUMBER, s);
879 0 : free(s);
880 :
881 0 : s = pcmk__itoa(nresources);
882 0 : crm_xml_add(resources_node, PCMK_XA_NUMBER, s);
883 0 : free(s);
884 :
885 0 : s = pcmk__itoa(ndisabled);
886 0 : crm_xml_add(resources_node, PCMK_XA_DISABLED, s);
887 0 : free(s);
888 :
889 0 : s = pcmk__itoa(nblocked);
890 0 : crm_xml_add(resources_node, PCMK_XA_BLOCKED, s);
891 0 : free(s);
892 :
893 0 : return pcmk_rc_ok;
894 : }
895 :
896 : PCMK__OUTPUT_ARGS("cluster-dc", "pcmk_node_t *", "const char *", "const char *",
897 : "char *", "int")
898 : static int
899 0 : cluster_dc_html(pcmk__output_t *out, va_list args) {
900 0 : pcmk_node_t *dc = va_arg(args, pcmk_node_t *);
901 0 : const char *quorum = va_arg(args, const char *);
902 0 : const char *dc_version_s = va_arg(args, const char *);
903 0 : char *dc_name = va_arg(args, char *);
904 0 : bool mixed_version = va_arg(args, int);
905 :
906 0 : xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
907 0 : xmlNode *child = NULL;
908 :
909 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, PCMK__VALUE_BOLD);
910 0 : pcmk__xe_set_content(child, "Current DC: ");
911 :
912 0 : if (dc) {
913 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
914 0 : pcmk__xe_set_content(child, "%s (version %s) -",
915 : dc_name, pcmk__s(dc_version_s, "unknown"));
916 :
917 0 : if (mixed_version) {
918 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL,
919 : PCMK__VALUE_WARNING);
920 0 : pcmk__xe_set_content(child, " MIXED-VERSION");
921 : }
922 :
923 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
924 0 : pcmk__xe_set_content(child, " partition");
925 :
926 0 : if (crm_is_true(quorum)) {
927 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
928 0 : pcmk__xe_set_content(child, " with");
929 :
930 : } else {
931 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL,
932 : PCMK__VALUE_WARNING);
933 0 : pcmk__xe_set_content(child, " WITHOUT");
934 : }
935 :
936 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
937 0 : pcmk__xe_set_content(child, " quorum");
938 :
939 : } else {
940 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL,
941 : PCMK__VALUE_WARNING);
942 0 : pcmk__xe_set_content(child, "NONE");
943 : }
944 :
945 0 : return pcmk_rc_ok;
946 : }
947 :
948 : PCMK__OUTPUT_ARGS("cluster-dc", "pcmk_node_t *", "const char *", "const char *",
949 : "char *", "int")
950 : static int
951 0 : cluster_dc_text(pcmk__output_t *out, va_list args) {
952 0 : pcmk_node_t *dc = va_arg(args, pcmk_node_t *);
953 0 : const char *quorum = va_arg(args, const char *);
954 0 : const char *dc_version_s = va_arg(args, const char *);
955 0 : char *dc_name = va_arg(args, char *);
956 0 : bool mixed_version = va_arg(args, int);
957 :
958 0 : if (dc) {
959 0 : out->list_item(out, "Current DC",
960 : "%s (version %s) - %spartition %s quorum",
961 : dc_name, dc_version_s ? dc_version_s : "unknown",
962 : mixed_version ? "MIXED-VERSION " : "",
963 0 : crm_is_true(quorum) ? "with" : "WITHOUT");
964 : } else {
965 0 : out->list_item(out, "Current DC", "NONE");
966 : }
967 :
968 0 : return pcmk_rc_ok;
969 : }
970 :
971 : PCMK__OUTPUT_ARGS("cluster-dc", "pcmk_node_t *", "const char *", "const char *",
972 : "char *", "int")
973 : static int
974 0 : cluster_dc_xml(pcmk__output_t *out, va_list args) {
975 0 : pcmk_node_t *dc = va_arg(args, pcmk_node_t *);
976 0 : const char *quorum = va_arg(args, const char *);
977 0 : const char *dc_version_s = va_arg(args, const char *);
978 0 : char *dc_name G_GNUC_UNUSED = va_arg(args, char *);
979 0 : bool mixed_version = va_arg(args, int);
980 :
981 0 : if (dc) {
982 0 : const char *with_quorum = pcmk__btoa(crm_is_true(quorum));
983 0 : const char *mixed_version_s = pcmk__btoa(mixed_version);
984 :
985 0 : pcmk__output_create_xml_node(out, PCMK_XE_CURRENT_DC,
986 : PCMK_XA_PRESENT, PCMK_VALUE_TRUE,
987 : PCMK_XA_VERSION, pcmk__s(dc_version_s, ""),
988 0 : PCMK_XA_NAME, dc->details->uname,
989 0 : PCMK_XA_ID, dc->details->id,
990 : PCMK_XA_WITH_QUORUM, with_quorum,
991 : PCMK_XA_MIXED_VERSION, mixed_version_s,
992 : NULL);
993 : } else {
994 0 : pcmk__output_create_xml_node(out, PCMK_XE_CURRENT_DC,
995 : PCMK_XA_PRESENT, PCMK_VALUE_FALSE,
996 : NULL);
997 : }
998 :
999 0 : return pcmk_rc_ok;
1000 : }
1001 :
1002 : PCMK__OUTPUT_ARGS("maint-mode", "unsigned long long int")
1003 : static int
1004 0 : cluster_maint_mode_text(pcmk__output_t *out, va_list args) {
1005 0 : unsigned long long flags = va_arg(args, unsigned long long);
1006 :
1007 0 : if (pcmk_is_set(flags, pcmk_sched_in_maintenance)) {
1008 0 : pcmk__formatted_printf(out, "\n *** Resource management is DISABLED ***\n");
1009 0 : pcmk__formatted_printf(out, " The cluster will not attempt to start, stop or recover services\n");
1010 0 : return pcmk_rc_ok;
1011 0 : } else if (pcmk_is_set(flags, pcmk_sched_stop_all)) {
1012 0 : pcmk__formatted_printf(out, "\n *** Resource management is DISABLED ***\n");
1013 0 : pcmk__formatted_printf(out, " The cluster will keep all resources stopped\n");
1014 0 : return pcmk_rc_ok;
1015 : } else {
1016 0 : return pcmk_rc_no_output;
1017 : }
1018 : }
1019 :
1020 : PCMK__OUTPUT_ARGS("cluster-options", "pcmk_scheduler_t *")
1021 : static int
1022 0 : cluster_options_html(pcmk__output_t *out, va_list args) {
1023 0 : pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
1024 :
1025 0 : if (pcmk_is_set(scheduler->flags, pcmk_sched_fencing_enabled)) {
1026 0 : out->list_item(out, NULL, "STONITH of failed nodes enabled");
1027 : } else {
1028 0 : out->list_item(out, NULL, "STONITH of failed nodes disabled");
1029 : }
1030 :
1031 0 : if (pcmk_is_set(scheduler->flags, pcmk_sched_symmetric_cluster)) {
1032 0 : out->list_item(out, NULL, "Cluster is symmetric");
1033 : } else {
1034 0 : out->list_item(out, NULL, "Cluster is asymmetric");
1035 : }
1036 :
1037 0 : switch (scheduler->no_quorum_policy) {
1038 0 : case pcmk_no_quorum_freeze:
1039 0 : out->list_item(out, NULL, "No quorum policy: Freeze resources");
1040 0 : break;
1041 :
1042 0 : case pcmk_no_quorum_stop:
1043 0 : out->list_item(out, NULL, "No quorum policy: Stop ALL resources");
1044 0 : break;
1045 :
1046 0 : case pcmk_no_quorum_demote:
1047 0 : out->list_item(out, NULL, "No quorum policy: Demote promotable "
1048 : "resources and stop all other resources");
1049 0 : break;
1050 :
1051 0 : case pcmk_no_quorum_ignore:
1052 0 : out->list_item(out, NULL, "No quorum policy: Ignore");
1053 0 : break;
1054 :
1055 0 : case pcmk_no_quorum_fence:
1056 0 : out->list_item(out, NULL, "No quorum policy: Suicide");
1057 0 : break;
1058 : }
1059 :
1060 0 : if (pcmk_is_set(scheduler->flags, pcmk_sched_in_maintenance)) {
1061 0 : xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
1062 0 : xmlNode *child = NULL;
1063 :
1064 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
1065 0 : pcmk__xe_set_content(child, "Resource management: ");
1066 :
1067 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, PCMK__VALUE_BOLD);
1068 0 : pcmk__xe_set_content(child, "DISABLED");
1069 :
1070 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
1071 0 : pcmk__xe_set_content(child,
1072 : " (the cluster will not attempt to start, stop,"
1073 : " or recover services)");
1074 :
1075 0 : } else if (pcmk_is_set(scheduler->flags, pcmk_sched_stop_all)) {
1076 0 : xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
1077 0 : xmlNode *child = NULL;
1078 :
1079 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
1080 0 : pcmk__xe_set_content(child, "Resource management: ");
1081 :
1082 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, PCMK__VALUE_BOLD);
1083 0 : pcmk__xe_set_content(child, "STOPPED");
1084 :
1085 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
1086 0 : pcmk__xe_set_content(child,
1087 : " (the cluster will keep all resources stopped)");
1088 :
1089 : } else {
1090 0 : out->list_item(out, NULL, "Resource management: enabled");
1091 : }
1092 :
1093 0 : return pcmk_rc_ok;
1094 : }
1095 :
1096 : PCMK__OUTPUT_ARGS("cluster-options", "pcmk_scheduler_t *")
1097 : static int
1098 0 : cluster_options_log(pcmk__output_t *out, va_list args) {
1099 0 : pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
1100 :
1101 0 : if (pcmk_is_set(scheduler->flags, pcmk_sched_in_maintenance)) {
1102 0 : return out->info(out, "Resource management is DISABLED. The cluster will not attempt to start, stop or recover services.");
1103 0 : } else if (pcmk_is_set(scheduler->flags, pcmk_sched_stop_all)) {
1104 0 : return out->info(out, "Resource management is DISABLED. The cluster has stopped all resources.");
1105 : } else {
1106 0 : return pcmk_rc_no_output;
1107 : }
1108 : }
1109 :
1110 : PCMK__OUTPUT_ARGS("cluster-options", "pcmk_scheduler_t *")
1111 : static int
1112 0 : cluster_options_text(pcmk__output_t *out, va_list args) {
1113 0 : pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
1114 :
1115 0 : if (pcmk_is_set(scheduler->flags, pcmk_sched_fencing_enabled)) {
1116 0 : out->list_item(out, NULL, "STONITH of failed nodes enabled");
1117 : } else {
1118 0 : out->list_item(out, NULL, "STONITH of failed nodes disabled");
1119 : }
1120 :
1121 0 : if (pcmk_is_set(scheduler->flags, pcmk_sched_symmetric_cluster)) {
1122 0 : out->list_item(out, NULL, "Cluster is symmetric");
1123 : } else {
1124 0 : out->list_item(out, NULL, "Cluster is asymmetric");
1125 : }
1126 :
1127 0 : switch (scheduler->no_quorum_policy) {
1128 0 : case pcmk_no_quorum_freeze:
1129 0 : out->list_item(out, NULL, "No quorum policy: Freeze resources");
1130 0 : break;
1131 :
1132 0 : case pcmk_no_quorum_stop:
1133 0 : out->list_item(out, NULL, "No quorum policy: Stop ALL resources");
1134 0 : break;
1135 :
1136 0 : case pcmk_no_quorum_demote:
1137 0 : out->list_item(out, NULL, "No quorum policy: Demote promotable "
1138 : "resources and stop all other resources");
1139 0 : break;
1140 :
1141 0 : case pcmk_no_quorum_ignore:
1142 0 : out->list_item(out, NULL, "No quorum policy: Ignore");
1143 0 : break;
1144 :
1145 0 : case pcmk_no_quorum_fence:
1146 0 : out->list_item(out, NULL, "No quorum policy: Suicide");
1147 0 : break;
1148 : }
1149 :
1150 0 : return pcmk_rc_ok;
1151 : }
1152 :
1153 : /*!
1154 : * \internal
1155 : * \brief Get readable string representation of a no-quorum policy
1156 : *
1157 : * \param[in] policy No-quorum policy
1158 : *
1159 : * \return String representation of \p policy
1160 : */
1161 : static const char *
1162 0 : no_quorum_policy_text(enum pe_quorum_policy policy)
1163 : {
1164 0 : switch (policy) {
1165 0 : case pcmk_no_quorum_freeze:
1166 0 : return PCMK_VALUE_FREEZE;
1167 :
1168 0 : case pcmk_no_quorum_stop:
1169 0 : return PCMK_VALUE_STOP;
1170 :
1171 0 : case pcmk_no_quorum_demote:
1172 0 : return PCMK_VALUE_DEMOTE;
1173 :
1174 0 : case pcmk_no_quorum_ignore:
1175 0 : return PCMK_VALUE_IGNORE;
1176 :
1177 0 : case pcmk_no_quorum_fence:
1178 0 : return PCMK_VALUE_FENCE_LEGACY;
1179 :
1180 0 : default:
1181 0 : return PCMK_VALUE_UNKNOWN;
1182 : }
1183 : }
1184 :
1185 : PCMK__OUTPUT_ARGS("cluster-options", "pcmk_scheduler_t *")
1186 : static int
1187 0 : cluster_options_xml(pcmk__output_t *out, va_list args) {
1188 0 : pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
1189 :
1190 0 : const char *stonith_enabled = pcmk__flag_text(scheduler->flags,
1191 : pcmk_sched_fencing_enabled);
1192 : const char *symmetric_cluster =
1193 0 : pcmk__flag_text(scheduler->flags, pcmk_sched_symmetric_cluster);
1194 : const char *no_quorum_policy =
1195 0 : no_quorum_policy_text(scheduler->no_quorum_policy);
1196 0 : const char *maintenance_mode = pcmk__flag_text(scheduler->flags,
1197 : pcmk_sched_in_maintenance);
1198 0 : const char *stop_all_resources = pcmk__flag_text(scheduler->flags,
1199 : pcmk_sched_stop_all);
1200 0 : char *stonith_timeout_ms_s = pcmk__itoa(scheduler->stonith_timeout);
1201 : char *priority_fencing_delay_ms_s =
1202 0 : pcmk__itoa(scheduler->priority_fencing_delay * 1000);
1203 :
1204 0 : pcmk__output_create_xml_node(out, PCMK_XE_CLUSTER_OPTIONS,
1205 : PCMK_XA_STONITH_ENABLED, stonith_enabled,
1206 : PCMK_XA_SYMMETRIC_CLUSTER, symmetric_cluster,
1207 : PCMK_XA_NO_QUORUM_POLICY, no_quorum_policy,
1208 : PCMK_XA_MAINTENANCE_MODE, maintenance_mode,
1209 : PCMK_XA_STOP_ALL_RESOURCES, stop_all_resources,
1210 : PCMK_XA_STONITH_TIMEOUT_MS,
1211 : stonith_timeout_ms_s,
1212 : PCMK_XA_PRIORITY_FENCING_DELAY_MS,
1213 : priority_fencing_delay_ms_s,
1214 : NULL);
1215 0 : free(stonith_timeout_ms_s);
1216 0 : free(priority_fencing_delay_ms_s);
1217 :
1218 0 : return pcmk_rc_ok;
1219 : }
1220 :
1221 : PCMK__OUTPUT_ARGS("cluster-stack", "const char *", "enum pcmk_pacemakerd_state")
1222 : static int
1223 0 : cluster_stack_html(pcmk__output_t *out, va_list args) {
1224 0 : const char *stack_s = va_arg(args, const char *);
1225 0 : enum pcmk_pacemakerd_state pcmkd_state =
1226 : (enum pcmk_pacemakerd_state) va_arg(args, int);
1227 :
1228 0 : xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
1229 0 : xmlNode *child = NULL;
1230 :
1231 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, PCMK__VALUE_BOLD);
1232 0 : pcmk__xe_set_content(child, "Stack: ");
1233 :
1234 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
1235 0 : pcmk__xe_set_content(child, "%s", stack_s);
1236 :
1237 0 : if (pcmkd_state != pcmk_pacemakerd_state_invalid) {
1238 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
1239 0 : pcmk__xe_set_content(child, " (");
1240 :
1241 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
1242 0 : pcmk__xe_set_content(child, "%s",
1243 : pcmk__pcmkd_state_enum2friendly(pcmkd_state));
1244 :
1245 0 : child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
1246 0 : pcmk__xe_set_content(child, ")");
1247 : }
1248 0 : return pcmk_rc_ok;
1249 : }
1250 :
1251 : PCMK__OUTPUT_ARGS("cluster-stack", "const char *", "enum pcmk_pacemakerd_state")
1252 : static int
1253 0 : cluster_stack_text(pcmk__output_t *out, va_list args) {
1254 0 : const char *stack_s = va_arg(args, const char *);
1255 0 : enum pcmk_pacemakerd_state pcmkd_state =
1256 : (enum pcmk_pacemakerd_state) va_arg(args, int);
1257 :
1258 0 : if (pcmkd_state != pcmk_pacemakerd_state_invalid) {
1259 0 : out->list_item(out, "Stack", "%s (%s)",
1260 : stack_s, pcmk__pcmkd_state_enum2friendly(pcmkd_state));
1261 : } else {
1262 0 : out->list_item(out, "Stack", "%s", stack_s);
1263 : }
1264 :
1265 0 : return pcmk_rc_ok;
1266 : }
1267 :
1268 : PCMK__OUTPUT_ARGS("cluster-stack", "const char *", "enum pcmk_pacemakerd_state")
1269 : static int
1270 0 : cluster_stack_xml(pcmk__output_t *out, va_list args) {
1271 0 : const char *stack_s = va_arg(args, const char *);
1272 0 : enum pcmk_pacemakerd_state pcmkd_state =
1273 : (enum pcmk_pacemakerd_state) va_arg(args, int);
1274 :
1275 0 : const char *state_s = NULL;
1276 :
1277 0 : if (pcmkd_state != pcmk_pacemakerd_state_invalid) {
1278 0 : state_s = pcmk_pacemakerd_api_daemon_state_enum2text(pcmkd_state);
1279 : }
1280 :
1281 0 : pcmk__output_create_xml_node(out, PCMK_XE_STACK,
1282 : PCMK_XA_TYPE, stack_s,
1283 : PCMK_XA_PACEMAKERD_STATE, state_s,
1284 : NULL);
1285 :
1286 0 : return pcmk_rc_ok;
1287 : }
1288 :
1289 : PCMK__OUTPUT_ARGS("cluster-times", "const char *", "const char *",
1290 : "const char *", "const char *", "const char *")
1291 : static int
1292 0 : cluster_times_html(pcmk__output_t *out, va_list args) {
1293 0 : const char *our_nodename = va_arg(args, const char *);
1294 0 : const char *last_written = va_arg(args, const char *);
1295 0 : const char *user = va_arg(args, const char *);
1296 0 : const char *client = va_arg(args, const char *);
1297 0 : const char *origin = va_arg(args, const char *);
1298 :
1299 0 : xmlNodePtr updated_node = pcmk__output_create_xml_node(out, "li", NULL);
1300 0 : xmlNodePtr changed_node = pcmk__output_create_xml_node(out, "li", NULL);
1301 0 : xmlNode *child = NULL;
1302 :
1303 0 : char *time_s = NULL;
1304 :
1305 0 : child = pcmk__html_create(updated_node, PCMK__XE_SPAN, NULL,
1306 : PCMK__VALUE_BOLD);
1307 0 : pcmk__xe_set_content(child, "Last updated: ");
1308 :
1309 0 : child = pcmk__html_create(updated_node, PCMK__XE_SPAN, NULL, NULL);
1310 0 : time_s = pcmk__epoch2str(NULL, 0);
1311 0 : pcmk__xe_set_content(child, "%s", time_s);
1312 0 : free(time_s);
1313 :
1314 0 : if (our_nodename != NULL) {
1315 0 : child = pcmk__html_create(updated_node, PCMK__XE_SPAN, NULL, NULL);
1316 0 : pcmk__xe_set_content(child, " on ");
1317 :
1318 0 : child = pcmk__html_create(updated_node, PCMK__XE_SPAN, NULL, NULL);
1319 0 : pcmk__xe_set_content(child, "%s", our_nodename);
1320 : }
1321 :
1322 0 : child = pcmk__html_create(changed_node, PCMK__XE_SPAN, NULL,
1323 : PCMK__VALUE_BOLD);
1324 0 : pcmk__xe_set_content(child, "Last change: ");
1325 :
1326 0 : child = pcmk__html_create(changed_node, PCMK__XE_SPAN, NULL, NULL);
1327 0 : time_s = last_changed_string(last_written, user, client, origin);
1328 0 : pcmk__xe_set_content(child, "%s", time_s);
1329 0 : free(time_s);
1330 :
1331 0 : return pcmk_rc_ok;
1332 : }
1333 :
1334 : PCMK__OUTPUT_ARGS("cluster-times", "const char *", "const char *",
1335 : "const char *", "const char *", "const char *")
1336 : static int
1337 0 : cluster_times_xml(pcmk__output_t *out, va_list args) {
1338 0 : const char *our_nodename = va_arg(args, const char *);
1339 0 : const char *last_written = va_arg(args, const char *);
1340 0 : const char *user = va_arg(args, const char *);
1341 0 : const char *client = va_arg(args, const char *);
1342 0 : const char *origin = va_arg(args, const char *);
1343 :
1344 0 : char *time_s = pcmk__epoch2str(NULL, 0);
1345 :
1346 0 : pcmk__output_create_xml_node(out, PCMK_XE_LAST_UPDATE,
1347 : PCMK_XA_TIME, time_s,
1348 : PCMK_XA_ORIGIN, our_nodename,
1349 : NULL);
1350 :
1351 0 : pcmk__output_create_xml_node(out, PCMK_XE_LAST_CHANGE,
1352 : PCMK_XA_TIME, pcmk__s(last_written, ""),
1353 : PCMK_XA_USER, pcmk__s(user, ""),
1354 : PCMK_XA_CLIENT, pcmk__s(client, ""),
1355 : PCMK_XA_ORIGIN, pcmk__s(origin, ""),
1356 : NULL);
1357 :
1358 0 : free(time_s);
1359 0 : return pcmk_rc_ok;
1360 : }
1361 :
1362 : PCMK__OUTPUT_ARGS("cluster-times", "const char *", "const char *",
1363 : "const char *", "const char *", "const char *")
1364 : static int
1365 0 : cluster_times_text(pcmk__output_t *out, va_list args) {
1366 0 : const char *our_nodename = va_arg(args, const char *);
1367 0 : const char *last_written = va_arg(args, const char *);
1368 0 : const char *user = va_arg(args, const char *);
1369 0 : const char *client = va_arg(args, const char *);
1370 0 : const char *origin = va_arg(args, const char *);
1371 :
1372 0 : char *time_s = pcmk__epoch2str(NULL, 0);
1373 :
1374 0 : out->list_item(out, "Last updated", "%s%s%s",
1375 : time_s, (our_nodename != NULL)? " on " : "",
1376 : pcmk__s(our_nodename, ""));
1377 :
1378 0 : free(time_s);
1379 0 : time_s = last_changed_string(last_written, user, client, origin);
1380 :
1381 0 : out->list_item(out, "Last change", " %s", time_s);
1382 :
1383 0 : free(time_s);
1384 0 : return pcmk_rc_ok;
1385 : }
1386 :
1387 : /*!
1388 : * \internal
1389 : * \brief Display a failed action in less-technical natural language
1390 : *
1391 : * \param[in,out] out Output object to use for display
1392 : * \param[in] xml_op XML containing failed action
1393 : * \param[in] op_key Operation key of failed action
1394 : * \param[in] node_name Where failed action occurred
1395 : * \param[in] rc OCF exit code of failed action
1396 : * \param[in] status Execution status of failed action
1397 : * \param[in] exit_reason Exit reason given for failed action
1398 : * \param[in] exec_time String containing execution time in milliseconds
1399 : */
1400 : static void
1401 0 : failed_action_friendly(pcmk__output_t *out, const xmlNode *xml_op,
1402 : const char *op_key, const char *node_name, int rc,
1403 : int status, const char *exit_reason,
1404 : const char *exec_time)
1405 : {
1406 0 : char *rsc_id = NULL;
1407 0 : char *task = NULL;
1408 0 : guint interval_ms = 0;
1409 0 : time_t last_change_epoch = 0;
1410 0 : GString *str = NULL;
1411 :
1412 0 : if (pcmk__str_empty(op_key)
1413 0 : || !parse_op_key(op_key, &rsc_id, &task, &interval_ms)) {
1414 :
1415 0 : pcmk__str_update(&rsc_id, "unknown resource");
1416 0 : pcmk__str_update(&task, "unknown action");
1417 0 : interval_ms = 0;
1418 : }
1419 0 : CRM_ASSERT((rsc_id != NULL) && (task != NULL));
1420 :
1421 0 : str = g_string_sized_new(256); // Should be sufficient for most messages
1422 :
1423 0 : pcmk__g_strcat(str, rsc_id, " ", NULL);
1424 :
1425 0 : if (interval_ms != 0) {
1426 0 : pcmk__g_strcat(str, pcmk__readable_interval(interval_ms), "-interval ",
1427 : NULL);
1428 : }
1429 0 : pcmk__g_strcat(str, pcmk__readable_action(task, interval_ms), " on ",
1430 : node_name, NULL);
1431 :
1432 0 : if (status == PCMK_EXEC_DONE) {
1433 0 : pcmk__g_strcat(str, " returned '", services_ocf_exitcode_str(rc), "'",
1434 : NULL);
1435 0 : if (!pcmk__str_empty(exit_reason)) {
1436 0 : pcmk__g_strcat(str, " (", exit_reason, ")", NULL);
1437 : }
1438 :
1439 : } else {
1440 0 : pcmk__g_strcat(str, " could not be executed (",
1441 : pcmk_exec_status_str(status), NULL);
1442 0 : if (!pcmk__str_empty(exit_reason)) {
1443 0 : pcmk__g_strcat(str, ": ", exit_reason, NULL);
1444 : }
1445 : g_string_append_c(str, ')');
1446 : }
1447 :
1448 :
1449 0 : if (crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE,
1450 : &last_change_epoch) == pcmk_ok) {
1451 0 : char *s = pcmk__epoch2str(&last_change_epoch, 0);
1452 :
1453 0 : pcmk__g_strcat(str, " at ", s, NULL);
1454 0 : free(s);
1455 : }
1456 0 : if (!pcmk__str_empty(exec_time)) {
1457 0 : int exec_time_ms = 0;
1458 :
1459 0 : if ((pcmk__scan_min_int(exec_time, &exec_time_ms, 0) == pcmk_rc_ok)
1460 0 : && (exec_time_ms > 0)) {
1461 :
1462 0 : pcmk__g_strcat(str, " after ",
1463 : pcmk__readable_interval(exec_time_ms), NULL);
1464 : }
1465 : }
1466 :
1467 0 : out->list_item(out, NULL, "%s", str->str);
1468 0 : g_string_free(str, TRUE);
1469 0 : free(rsc_id);
1470 0 : free(task);
1471 0 : }
1472 :
1473 : /*!
1474 : * \internal
1475 : * \brief Display a failed action with technical details
1476 : *
1477 : * \param[in,out] out Output object to use for display
1478 : * \param[in] xml_op XML containing failed action
1479 : * \param[in] op_key Operation key of failed action
1480 : * \param[in] node_name Where failed action occurred
1481 : * \param[in] rc OCF exit code of failed action
1482 : * \param[in] status Execution status of failed action
1483 : * \param[in] exit_reason Exit reason given for failed action
1484 : * \param[in] exec_time String containing execution time in milliseconds
1485 : */
1486 : static void
1487 0 : failed_action_technical(pcmk__output_t *out, const xmlNode *xml_op,
1488 : const char *op_key, const char *node_name, int rc,
1489 : int status, const char *exit_reason,
1490 : const char *exec_time)
1491 : {
1492 0 : const char *call_id = crm_element_value(xml_op, PCMK__XA_CALL_ID);
1493 0 : const char *queue_time = crm_element_value(xml_op, PCMK_XA_QUEUE_TIME);
1494 0 : const char *exit_status = services_ocf_exitcode_str(rc);
1495 0 : const char *lrm_status = pcmk_exec_status_str(status);
1496 0 : time_t last_change_epoch = 0;
1497 0 : GString *str = NULL;
1498 :
1499 0 : if (pcmk__str_empty(op_key)) {
1500 0 : op_key = "unknown operation";
1501 : }
1502 0 : if (pcmk__str_empty(exit_status)) {
1503 0 : exit_status = "unknown exit status";
1504 : }
1505 0 : if (pcmk__str_empty(call_id)) {
1506 0 : call_id = "unknown";
1507 : }
1508 :
1509 0 : str = g_string_sized_new(256);
1510 :
1511 0 : g_string_append_printf(str, "%s on %s '%s' (%d): call=%s, status='%s'",
1512 : op_key, node_name, exit_status, rc, call_id,
1513 : lrm_status);
1514 :
1515 0 : if (!pcmk__str_empty(exit_reason)) {
1516 0 : pcmk__g_strcat(str, ", exitreason='", exit_reason, "'", NULL);
1517 : }
1518 :
1519 0 : if (crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE,
1520 : &last_change_epoch) == pcmk_ok) {
1521 0 : char *last_change_str = pcmk__epoch2str(&last_change_epoch, 0);
1522 :
1523 0 : pcmk__g_strcat(str,
1524 : ", " PCMK_XA_LAST_RC_CHANGE "="
1525 : "'", last_change_str, "'", NULL);
1526 0 : free(last_change_str);
1527 : }
1528 0 : if (!pcmk__str_empty(queue_time)) {
1529 0 : pcmk__g_strcat(str, ", queued=", queue_time, "ms", NULL);
1530 : }
1531 0 : if (!pcmk__str_empty(exec_time)) {
1532 0 : pcmk__g_strcat(str, ", exec=", exec_time, "ms", NULL);
1533 : }
1534 :
1535 0 : out->list_item(out, NULL, "%s", str->str);
1536 0 : g_string_free(str, TRUE);
1537 0 : }
1538 :
1539 : PCMK__OUTPUT_ARGS("failed-action", "xmlNode *", "uint32_t")
1540 : static int
1541 0 : failed_action_default(pcmk__output_t *out, va_list args)
1542 : {
1543 0 : xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
1544 0 : uint32_t show_opts = va_arg(args, uint32_t);
1545 :
1546 0 : const char *op_key = pcmk__xe_history_key(xml_op);
1547 0 : const char *node_name = crm_element_value(xml_op, PCMK_XA_UNAME);
1548 0 : const char *exit_reason = crm_element_value(xml_op, PCMK_XA_EXIT_REASON);
1549 0 : const char *exec_time = crm_element_value(xml_op, PCMK_XA_EXEC_TIME);
1550 :
1551 : int rc;
1552 : int status;
1553 :
1554 0 : pcmk__scan_min_int(crm_element_value(xml_op, PCMK__XA_RC_CODE), &rc, 0);
1555 :
1556 0 : pcmk__scan_min_int(crm_element_value(xml_op, PCMK__XA_OP_STATUS), &status,
1557 : 0);
1558 :
1559 0 : if (pcmk__str_empty(node_name)) {
1560 0 : node_name = "unknown node";
1561 : }
1562 :
1563 0 : if (pcmk_is_set(show_opts, pcmk_show_failed_detail)) {
1564 0 : failed_action_technical(out, xml_op, op_key, node_name, rc, status,
1565 : exit_reason, exec_time);
1566 : } else {
1567 0 : failed_action_friendly(out, xml_op, op_key, node_name, rc, status,
1568 : exit_reason, exec_time);
1569 : }
1570 0 : return pcmk_rc_ok;
1571 : }
1572 :
1573 : PCMK__OUTPUT_ARGS("failed-action", "xmlNode *", "uint32_t")
1574 : static int
1575 0 : failed_action_xml(pcmk__output_t *out, va_list args) {
1576 0 : xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
1577 0 : uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t);
1578 :
1579 0 : const char *op_key = pcmk__xe_history_key(xml_op);
1580 0 : const char *op_key_name = PCMK_XA_OP_KEY;
1581 : int rc;
1582 : int status;
1583 0 : const char *uname = crm_element_value(xml_op, PCMK_XA_UNAME);
1584 0 : const char *call_id = crm_element_value(xml_op, PCMK__XA_CALL_ID);
1585 0 : const char *exitstatus = NULL;
1586 0 : const char *exit_reason = pcmk__s(crm_element_value(xml_op,
1587 : PCMK_XA_EXIT_REASON),
1588 : "none");
1589 0 : const char *status_s = NULL;
1590 :
1591 0 : time_t epoch = 0;
1592 0 : gchar *exit_reason_esc = NULL;
1593 0 : char *rc_s = NULL;
1594 0 : xmlNodePtr node = NULL;
1595 :
1596 0 : if (pcmk__xml_needs_escape(exit_reason, pcmk__xml_escape_attr)) {
1597 0 : exit_reason_esc = pcmk__xml_escape(exit_reason, pcmk__xml_escape_attr);
1598 0 : exit_reason = exit_reason_esc;
1599 : }
1600 0 : pcmk__scan_min_int(crm_element_value(xml_op, PCMK__XA_RC_CODE), &rc, 0);
1601 0 : pcmk__scan_min_int(crm_element_value(xml_op, PCMK__XA_OP_STATUS), &status,
1602 : 0);
1603 :
1604 0 : if (crm_element_value(xml_op, PCMK__XA_OPERATION_KEY) == NULL) {
1605 0 : op_key_name = PCMK_XA_ID;
1606 : }
1607 0 : exitstatus = services_ocf_exitcode_str(rc);
1608 0 : rc_s = pcmk__itoa(rc);
1609 0 : status_s = pcmk_exec_status_str(status);
1610 0 : node = pcmk__output_create_xml_node(out, PCMK_XE_FAILURE,
1611 : op_key_name, op_key,
1612 : PCMK_XA_NODE, uname,
1613 : PCMK_XA_EXITSTATUS, exitstatus,
1614 : PCMK_XA_EXITREASON, exit_reason,
1615 : PCMK_XA_EXITCODE, rc_s,
1616 : PCMK_XA_CALL, call_id,
1617 : PCMK_XA_STATUS, status_s,
1618 : NULL);
1619 0 : free(rc_s);
1620 :
1621 0 : if ((crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE,
1622 0 : &epoch) == pcmk_ok) && (epoch > 0)) {
1623 :
1624 0 : const char *queue_time = crm_element_value(xml_op, PCMK_XA_QUEUE_TIME);
1625 0 : const char *exec = crm_element_value(xml_op, PCMK_XA_EXEC_TIME);
1626 0 : const char *task = crm_element_value(xml_op, PCMK_XA_OPERATION);
1627 0 : guint interval_ms = 0;
1628 0 : char *interval_ms_s = NULL;
1629 0 : char *rc_change = pcmk__epoch2str(&epoch,
1630 : crm_time_log_date
1631 : |crm_time_log_timeofday
1632 : |crm_time_log_with_timezone);
1633 :
1634 0 : crm_element_value_ms(xml_op, PCMK_META_INTERVAL, &interval_ms);
1635 0 : interval_ms_s = crm_strdup_printf("%u", interval_ms);
1636 :
1637 0 : pcmk__xe_set_props(node,
1638 : PCMK_XA_LAST_RC_CHANGE, rc_change,
1639 : PCMK_XA_QUEUED, queue_time,
1640 : PCMK_XA_EXEC, exec,
1641 : PCMK_XA_INTERVAL, interval_ms_s,
1642 : PCMK_XA_TASK, task,
1643 : NULL);
1644 :
1645 0 : free(interval_ms_s);
1646 0 : free(rc_change);
1647 : }
1648 :
1649 0 : g_free(exit_reason_esc);
1650 0 : return pcmk_rc_ok;
1651 : }
1652 :
1653 : PCMK__OUTPUT_ARGS("failed-action-list", "pcmk_scheduler_t *", "GList *",
1654 : "GList *", "uint32_t", "bool")
1655 : static int
1656 0 : failed_action_list(pcmk__output_t *out, va_list args) {
1657 0 : pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
1658 0 : GList *only_node = va_arg(args, GList *);
1659 0 : GList *only_rsc = va_arg(args, GList *);
1660 0 : uint32_t show_opts = va_arg(args, uint32_t);
1661 0 : bool print_spacer = va_arg(args, int);
1662 :
1663 0 : xmlNode *xml_op = NULL;
1664 0 : int rc = pcmk_rc_no_output;
1665 :
1666 0 : if (xmlChildElementCount(scheduler->failed) == 0) {
1667 0 : return rc;
1668 : }
1669 :
1670 0 : for (xml_op = pcmk__xe_first_child(scheduler->failed, NULL, NULL, NULL);
1671 0 : xml_op != NULL; xml_op = pcmk__xe_next(xml_op)) {
1672 :
1673 0 : char *rsc = NULL;
1674 :
1675 0 : if (!pcmk__str_in_list(crm_element_value(xml_op, PCMK_XA_UNAME),
1676 : only_node,
1677 : pcmk__str_star_matches|pcmk__str_casei)) {
1678 0 : continue;
1679 : }
1680 :
1681 0 : if (pcmk_xe_mask_probe_failure(xml_op)) {
1682 0 : continue;
1683 : }
1684 :
1685 0 : if (!parse_op_key(pcmk__xe_history_key(xml_op), &rsc, NULL, NULL)) {
1686 0 : continue;
1687 : }
1688 :
1689 0 : if (!pcmk__str_in_list(rsc, only_rsc, pcmk__str_star_matches)) {
1690 0 : free(rsc);
1691 0 : continue;
1692 : }
1693 :
1694 0 : free(rsc);
1695 :
1696 0 : PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Failed Resource Actions");
1697 0 : out->message(out, "failed-action", xml_op, show_opts);
1698 : }
1699 :
1700 0 : PCMK__OUTPUT_LIST_FOOTER(out, rc);
1701 0 : return rc;
1702 : }
1703 :
1704 : static void
1705 0 : status_node(pcmk_node_t *node, xmlNodePtr parent, uint32_t show_opts)
1706 : {
1707 0 : int health = pe__node_health(node);
1708 0 : xmlNode *child = NULL;
1709 :
1710 : // Cluster membership
1711 0 : if (node->details->online) {
1712 0 : child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL,
1713 : PCMK_VALUE_ONLINE);
1714 0 : pcmk__xe_set_content(child, " online");
1715 :
1716 : } else {
1717 0 : child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL,
1718 : PCMK_VALUE_OFFLINE);
1719 0 : pcmk__xe_set_content(child, " OFFLINE");
1720 : }
1721 :
1722 : // Standby mode
1723 0 : if (node->details->standby_onfail && (node->details->running_rsc != NULL)) {
1724 0 : child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL,
1725 : PCMK_VALUE_STANDBY);
1726 0 : pcmk__xe_set_content(child,
1727 : " (in standby due to " PCMK_META_ON_FAIL ","
1728 : " with active resources)");
1729 :
1730 0 : } else if (node->details->standby_onfail) {
1731 0 : child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL,
1732 : PCMK_VALUE_STANDBY);
1733 0 : pcmk__xe_set_content(child,
1734 : " (in standby due to " PCMK_META_ON_FAIL ")");
1735 :
1736 0 : } else if (node->details->standby && (node->details->running_rsc != NULL)) {
1737 0 : child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL,
1738 : PCMK_VALUE_STANDBY);
1739 0 : pcmk__xe_set_content(child,
1740 : " (in standby, with active resources)");
1741 :
1742 0 : } else if (node->details->standby) {
1743 0 : child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL,
1744 : PCMK_VALUE_STANDBY);
1745 0 : pcmk__xe_set_content(child, " (in standby)");
1746 : }
1747 :
1748 : // Maintenance mode
1749 0 : if (node->details->maintenance) {
1750 0 : child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL,
1751 : PCMK__VALUE_MAINT);
1752 0 : pcmk__xe_set_content(child, " (in maintenance mode)");
1753 : }
1754 :
1755 : // Node health
1756 0 : if (health < 0) {
1757 0 : child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL,
1758 : PCMK__VALUE_HEALTH_RED);
1759 0 : pcmk__xe_set_content(child, " (health is RED)");
1760 :
1761 0 : } else if (health == 0) {
1762 0 : child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL,
1763 : PCMK__VALUE_HEALTH_YELLOW);
1764 0 : pcmk__xe_set_content(child, " (health is YELLOW)");
1765 : }
1766 :
1767 : // Feature set
1768 0 : if (pcmk_is_set(show_opts, pcmk_show_feature_set)) {
1769 0 : const char *feature_set = get_node_feature_set(node);
1770 0 : if (feature_set != NULL) {
1771 0 : child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL, NULL);
1772 0 : pcmk__xe_set_content(child, ", feature set %s", feature_set);
1773 : }
1774 : }
1775 0 : }
1776 :
1777 : PCMK__OUTPUT_ARGS("node", "pcmk_node_t *", "uint32_t", "bool",
1778 : "GList *", "GList *")
1779 : static int
1780 0 : node_html(pcmk__output_t *out, va_list args) {
1781 0 : pcmk_node_t *node = va_arg(args, pcmk_node_t *);
1782 0 : uint32_t show_opts = va_arg(args, uint32_t);
1783 0 : bool full = va_arg(args, int);
1784 0 : GList *only_node = va_arg(args, GList *);
1785 0 : GList *only_rsc = va_arg(args, GList *);
1786 :
1787 0 : char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
1788 :
1789 0 : if (full) {
1790 0 : xmlNode *item_node = NULL;
1791 0 : xmlNode *child = NULL;
1792 :
1793 0 : if (pcmk_all_flags_set(show_opts, pcmk_show_brief | pcmk_show_rscs_by_node)) {
1794 0 : GList *rscs = pe__filter_rsc_list(node->details->running_rsc, only_rsc);
1795 :
1796 0 : out->begin_list(out, NULL, NULL, "%s:", node_name);
1797 0 : item_node = pcmk__output_xml_create_parent(out, "li", NULL);
1798 0 : child = pcmk__html_create(item_node, PCMK__XE_SPAN, NULL, NULL);
1799 0 : pcmk__xe_set_content(child, "Status:");
1800 0 : status_node(node, item_node, show_opts);
1801 :
1802 0 : if (rscs != NULL) {
1803 0 : uint32_t new_show_opts = (show_opts | pcmk_show_rsc_only) & ~pcmk_show_inactive_rscs;
1804 0 : out->begin_list(out, NULL, NULL, "Resources");
1805 0 : pe__rscs_brief_output(out, rscs, new_show_opts);
1806 0 : out->end_list(out);
1807 : }
1808 :
1809 0 : pcmk__output_xml_pop_parent(out);
1810 0 : out->end_list(out);
1811 :
1812 0 : } else if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
1813 0 : GList *lpc2 = NULL;
1814 0 : int rc = pcmk_rc_no_output;
1815 :
1816 0 : out->begin_list(out, NULL, NULL, "%s:", node_name);
1817 0 : item_node = pcmk__output_xml_create_parent(out, "li", NULL);
1818 0 : child = pcmk__html_create(item_node, PCMK__XE_SPAN, NULL, NULL);
1819 0 : pcmk__xe_set_content(child, "Status:");
1820 0 : status_node(node, item_node, show_opts);
1821 :
1822 0 : for (lpc2 = node->details->running_rsc; lpc2 != NULL; lpc2 = lpc2->next) {
1823 0 : pcmk_resource_t *rsc = (pcmk_resource_t *) lpc2->data;
1824 0 : PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Resources");
1825 :
1826 0 : show_opts |= pcmk_show_rsc_only;
1827 0 : out->message(out, pcmk__map_element_name(rsc->xml), show_opts,
1828 : rsc, only_node, only_rsc);
1829 : }
1830 :
1831 0 : PCMK__OUTPUT_LIST_FOOTER(out, rc);
1832 0 : pcmk__output_xml_pop_parent(out);
1833 0 : out->end_list(out);
1834 :
1835 : } else {
1836 0 : item_node = pcmk__output_create_xml_node(out, "li", NULL);
1837 0 : child = pcmk__html_create(item_node, PCMK__XE_SPAN, NULL,
1838 : PCMK__VALUE_BOLD);
1839 0 : pcmk__xe_set_content(child, "%s:", node_name);
1840 0 : status_node(node, item_node, show_opts);
1841 : }
1842 : } else {
1843 0 : out->begin_list(out, NULL, NULL, "%s:", node_name);
1844 : }
1845 :
1846 0 : free(node_name);
1847 0 : return pcmk_rc_ok;
1848 : }
1849 :
1850 : /*!
1851 : * \internal
1852 : * \brief Get a human-friendly textual description of a node's status
1853 : *
1854 : * \param[in] node Node to check
1855 : *
1856 : * \return String representation of node's status
1857 : */
1858 : static const char *
1859 0 : node_text_status(const pcmk_node_t *node)
1860 : {
1861 0 : if (node->details->unclean) {
1862 0 : if (node->details->online) {
1863 0 : return "UNCLEAN (online)";
1864 :
1865 0 : } else if (node->details->pending) {
1866 0 : return "UNCLEAN (pending)";
1867 :
1868 : } else {
1869 0 : return "UNCLEAN (offline)";
1870 : }
1871 :
1872 0 : } else if (node->details->pending) {
1873 0 : return "pending";
1874 :
1875 0 : } else if (node->details->standby_onfail && node->details->online) {
1876 0 : return "standby (" PCMK_META_ON_FAIL ")";
1877 :
1878 0 : } else if (node->details->standby) {
1879 0 : if (node->details->online) {
1880 0 : if (node->details->running_rsc) {
1881 0 : return "standby (with active resources)";
1882 : } else {
1883 0 : return "standby";
1884 : }
1885 : } else {
1886 0 : return "OFFLINE (standby)";
1887 : }
1888 :
1889 0 : } else if (node->details->maintenance) {
1890 0 : if (node->details->online) {
1891 0 : return "maintenance";
1892 : } else {
1893 0 : return "OFFLINE (maintenance)";
1894 : }
1895 :
1896 0 : } else if (node->details->online) {
1897 0 : return "online";
1898 : }
1899 :
1900 0 : return "OFFLINE";
1901 : }
1902 :
1903 : PCMK__OUTPUT_ARGS("node", "pcmk_node_t *", "uint32_t", "bool", "GList *",
1904 : "GList *")
1905 : static int
1906 0 : node_text(pcmk__output_t *out, va_list args) {
1907 0 : pcmk_node_t *node = va_arg(args, pcmk_node_t *);
1908 0 : uint32_t show_opts = va_arg(args, uint32_t);
1909 0 : bool full = va_arg(args, int);
1910 0 : GList *only_node = va_arg(args, GList *);
1911 0 : GList *only_rsc = va_arg(args, GList *);
1912 :
1913 0 : if (full) {
1914 0 : char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
1915 0 : GString *str = g_string_sized_new(64);
1916 0 : int health = pe__node_health(node);
1917 :
1918 : // Create a summary line with node type, name, and status
1919 0 : if (pcmk__is_guest_or_bundle_node(node)) {
1920 0 : g_string_append(str, "GuestNode");
1921 0 : } else if (pcmk__is_remote_node(node)) {
1922 0 : g_string_append(str, "RemoteNode");
1923 : } else {
1924 0 : g_string_append(str, "Node");
1925 : }
1926 0 : pcmk__g_strcat(str, " ", node_name, ": ", node_text_status(node), NULL);
1927 :
1928 0 : if (health < 0) {
1929 0 : g_string_append(str, " (health is RED)");
1930 0 : } else if (health == 0) {
1931 0 : g_string_append(str, " (health is YELLOW)");
1932 : }
1933 0 : if (pcmk_is_set(show_opts, pcmk_show_feature_set)) {
1934 0 : const char *feature_set = get_node_feature_set(node);
1935 0 : if (feature_set != NULL) {
1936 0 : pcmk__g_strcat(str, ", feature set ", feature_set, NULL);
1937 : }
1938 : }
1939 :
1940 : /* If we're grouping by node, print its resources */
1941 0 : if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
1942 0 : if (pcmk_is_set(show_opts, pcmk_show_brief)) {
1943 0 : GList *rscs = pe__filter_rsc_list(node->details->running_rsc, only_rsc);
1944 :
1945 0 : if (rscs != NULL) {
1946 0 : uint32_t new_show_opts = (show_opts | pcmk_show_rsc_only) & ~pcmk_show_inactive_rscs;
1947 0 : out->begin_list(out, NULL, NULL, "%s", str->str);
1948 0 : out->begin_list(out, NULL, NULL, "Resources");
1949 :
1950 0 : pe__rscs_brief_output(out, rscs, new_show_opts);
1951 :
1952 0 : out->end_list(out);
1953 0 : out->end_list(out);
1954 :
1955 0 : g_list_free(rscs);
1956 : }
1957 :
1958 : } else {
1959 0 : GList *gIter2 = NULL;
1960 :
1961 0 : out->begin_list(out, NULL, NULL, "%s", str->str);
1962 0 : out->begin_list(out, NULL, NULL, "Resources");
1963 :
1964 0 : for (gIter2 = node->details->running_rsc; gIter2 != NULL; gIter2 = gIter2->next) {
1965 0 : pcmk_resource_t *rsc = (pcmk_resource_t *) gIter2->data;
1966 :
1967 0 : show_opts |= pcmk_show_rsc_only;
1968 0 : out->message(out, pcmk__map_element_name(rsc->xml),
1969 : show_opts, rsc, only_node, only_rsc);
1970 : }
1971 :
1972 0 : out->end_list(out);
1973 0 : out->end_list(out);
1974 : }
1975 : } else {
1976 0 : out->list_item(out, NULL, "%s", str->str);
1977 : }
1978 :
1979 0 : g_string_free(str, TRUE);
1980 0 : free(node_name);
1981 : } else {
1982 0 : char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
1983 0 : out->begin_list(out, NULL, NULL, "Node: %s", node_name);
1984 0 : free(node_name);
1985 : }
1986 :
1987 0 : return pcmk_rc_ok;
1988 : }
1989 :
1990 : /*!
1991 : * \internal
1992 : * \brief Convert an integer health value to a string representation
1993 : *
1994 : * \param[in] health Integer health value
1995 : *
1996 : * \retval \c PCMK_VALUE_RED if \p health is less than 0
1997 : * \retval \c PCMK_VALUE_YELLOW if \p health is equal to 0
1998 : * \retval \c PCMK_VALUE_GREEN if \p health is greater than 0
1999 : */
2000 : static const char *
2001 0 : health_text(int health)
2002 : {
2003 0 : if (health < 0) {
2004 0 : return PCMK_VALUE_RED;
2005 0 : } else if (health == 0) {
2006 0 : return PCMK_VALUE_YELLOW;
2007 : } else {
2008 0 : return PCMK_VALUE_GREEN;
2009 : }
2010 : }
2011 :
2012 : /*!
2013 : * \internal
2014 : * \brief Convert a node type to a string representation
2015 : *
2016 : * \param[in] type Node type
2017 : *
2018 : * \retval \c PCMK_VALUE_MEMBER if \p node_type is \c pcmk_node_variant_cluster
2019 : * \retval \c PCMK_VALUE_REMOTE if \p node_type is \c pcmk_node_variant_remote
2020 : * \retval \c PCMK__VALUE_PING if \p node_type is \c node_ping
2021 : * \retval \c PCMK_VALUE_UNKNOWN otherwise
2022 : */
2023 : static const char *
2024 0 : node_type_str(enum node_type type)
2025 : {
2026 0 : switch (type) {
2027 0 : case pcmk_node_variant_cluster:
2028 0 : return PCMK_VALUE_MEMBER;
2029 0 : case pcmk_node_variant_remote:
2030 0 : return PCMK_VALUE_REMOTE;
2031 0 : case node_ping:
2032 0 : return PCMK__VALUE_PING;
2033 0 : default:
2034 0 : return PCMK_VALUE_UNKNOWN;
2035 : }
2036 : }
2037 :
2038 : PCMK__OUTPUT_ARGS("node", "pcmk_node_t *", "uint32_t", "bool", "GList *",
2039 : "GList *")
2040 : static int
2041 0 : node_xml(pcmk__output_t *out, va_list args) {
2042 0 : pcmk_node_t *node = va_arg(args, pcmk_node_t *);
2043 0 : uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t);
2044 0 : bool full = va_arg(args, int);
2045 0 : GList *only_node = va_arg(args, GList *);
2046 0 : GList *only_rsc = va_arg(args, GList *);
2047 :
2048 0 : if (full) {
2049 0 : const char *online = pcmk__btoa(node->details->online);
2050 0 : const char *standby = pcmk__btoa(node->details->standby);
2051 0 : const char *standby_onfail = pcmk__btoa(node->details->standby_onfail);
2052 0 : const char *maintenance = pcmk__btoa(node->details->maintenance);
2053 0 : const char *pending = pcmk__btoa(node->details->pending);
2054 0 : const char *unclean = pcmk__btoa(node->details->unclean);
2055 0 : const char *health = health_text(pe__node_health(node));
2056 0 : const char *feature_set = get_node_feature_set(node);
2057 0 : const char *shutdown = pcmk__btoa(node->details->shutdown);
2058 0 : const char *expected_up = pcmk__btoa(node->details->expected_up);
2059 0 : const char *is_dc = pcmk__btoa(node->details->is_dc);
2060 0 : int length = g_list_length(node->details->running_rsc);
2061 0 : char *resources_running = pcmk__itoa(length);
2062 0 : const char *node_type = node_type_str(node->details->type);
2063 :
2064 0 : int rc = pcmk_rc_ok;
2065 :
2066 0 : rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_NODE,
2067 0 : PCMK_XA_NAME, node->details->uname,
2068 0 : PCMK_XA_ID, node->details->id,
2069 : PCMK_XA_ONLINE, online,
2070 : PCMK_XA_STANDBY, standby,
2071 : PCMK_XA_STANDBY_ONFAIL, standby_onfail,
2072 : PCMK_XA_MAINTENANCE, maintenance,
2073 : PCMK_XA_PENDING, pending,
2074 : PCMK_XA_UNCLEAN, unclean,
2075 : PCMK_XA_HEALTH, health,
2076 : PCMK_XA_FEATURE_SET, feature_set,
2077 : PCMK_XA_SHUTDOWN, shutdown,
2078 : PCMK_XA_EXPECTED_UP, expected_up,
2079 : PCMK_XA_IS_DC, is_dc,
2080 : PCMK_XA_RESOURCES_RUNNING, resources_running,
2081 : PCMK_XA_TYPE, node_type,
2082 : NULL);
2083 :
2084 0 : free(resources_running);
2085 0 : CRM_ASSERT(rc == pcmk_rc_ok);
2086 :
2087 0 : if (pcmk__is_guest_or_bundle_node(node)) {
2088 0 : xmlNodePtr xml_node = pcmk__output_xml_peek_parent(out);
2089 0 : crm_xml_add(xml_node, PCMK_XA_ID_AS_RESOURCE,
2090 0 : node->details->remote_rsc->container->id);
2091 : }
2092 :
2093 0 : if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
2094 0 : GList *lpc = NULL;
2095 :
2096 0 : for (lpc = node->details->running_rsc; lpc != NULL; lpc = lpc->next) {
2097 0 : pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data;
2098 :
2099 0 : show_opts |= pcmk_show_rsc_only;
2100 0 : out->message(out, pcmk__map_element_name(rsc->xml), show_opts,
2101 : rsc, only_node, only_rsc);
2102 : }
2103 : }
2104 :
2105 0 : out->end_list(out);
2106 : } else {
2107 0 : pcmk__output_xml_create_parent(out, PCMK_XE_NODE,
2108 0 : PCMK_XA_NAME, node->details->uname,
2109 : NULL);
2110 : }
2111 :
2112 0 : return pcmk_rc_ok;
2113 : }
2114 :
2115 : PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "bool", "int")
2116 : static int
2117 0 : node_attribute_text(pcmk__output_t *out, va_list args) {
2118 0 : const char *name = va_arg(args, const char *);
2119 0 : const char *value = va_arg(args, const char *);
2120 0 : bool add_extra = va_arg(args, int);
2121 0 : int expected_score = va_arg(args, int);
2122 :
2123 0 : if (add_extra) {
2124 : int v;
2125 :
2126 0 : if (value == NULL) {
2127 0 : v = 0;
2128 : } else {
2129 0 : pcmk__scan_min_int(value, &v, INT_MIN);
2130 : }
2131 0 : if (v <= 0) {
2132 0 : out->list_item(out, NULL, "%-32s\t: %-10s\t: Connectivity is lost", name, value);
2133 0 : } else if (v < expected_score) {
2134 0 : out->list_item(out, NULL, "%-32s\t: %-10s\t: Connectivity is degraded (Expected=%d)", name, value, expected_score);
2135 : } else {
2136 0 : out->list_item(out, NULL, "%-32s\t: %-10s", name, value);
2137 : }
2138 : } else {
2139 0 : out->list_item(out, NULL, "%-32s\t: %-10s", name, value);
2140 : }
2141 :
2142 0 : return pcmk_rc_ok;
2143 : }
2144 :
2145 : PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "bool", "int")
2146 : static int
2147 0 : node_attribute_html(pcmk__output_t *out, va_list args) {
2148 0 : const char *name = va_arg(args, const char *);
2149 0 : const char *value = va_arg(args, const char *);
2150 0 : bool add_extra = va_arg(args, int);
2151 0 : int expected_score = va_arg(args, int);
2152 :
2153 0 : if (add_extra) {
2154 0 : int v = 0;
2155 0 : xmlNodePtr item_node = pcmk__output_create_xml_node(out, "li", NULL);
2156 0 : xmlNode *child = NULL;
2157 :
2158 0 : if (value != NULL) {
2159 0 : pcmk__scan_min_int(value, &v, INT_MIN);
2160 : }
2161 :
2162 0 : child = pcmk__html_create(item_node, PCMK__XE_SPAN, NULL, NULL);
2163 0 : pcmk__xe_set_content(child, "%s: %s", name, value);
2164 :
2165 0 : if (v <= 0) {
2166 0 : child = pcmk__html_create(item_node, PCMK__XE_SPAN, NULL,
2167 : PCMK__VALUE_BOLD);
2168 0 : pcmk__xe_set_content(child, "(connectivity is lost)");
2169 :
2170 0 : } else if (v < expected_score) {
2171 0 : child = pcmk__html_create(item_node, PCMK__XE_SPAN, NULL,
2172 : PCMK__VALUE_BOLD);
2173 0 : pcmk__xe_set_content(child,
2174 : "(connectivity is degraded -- expected %d)",
2175 : expected_score);
2176 : }
2177 : } else {
2178 0 : out->list_item(out, NULL, "%s: %s", name, value);
2179 : }
2180 :
2181 0 : return pcmk_rc_ok;
2182 : }
2183 :
2184 : PCMK__OUTPUT_ARGS("node-and-op", "pcmk_scheduler_t *", "xmlNode *")
2185 : static int
2186 0 : node_and_op(pcmk__output_t *out, va_list args) {
2187 0 : pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
2188 0 : xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
2189 :
2190 0 : pcmk_resource_t *rsc = NULL;
2191 0 : gchar *node_str = NULL;
2192 0 : char *last_change_str = NULL;
2193 :
2194 0 : const char *op_rsc = crm_element_value(xml_op, PCMK_XA_RESOURCE);
2195 : int status;
2196 0 : time_t last_change = 0;
2197 :
2198 0 : pcmk__scan_min_int(crm_element_value(xml_op, PCMK__XA_OP_STATUS), &status,
2199 : PCMK_EXEC_UNKNOWN);
2200 :
2201 0 : rsc = pe_find_resource(scheduler->resources, op_rsc);
2202 :
2203 0 : if (rsc) {
2204 0 : const pcmk_node_t *node = pcmk__current_node(rsc);
2205 0 : const char *target_role = g_hash_table_lookup(rsc->meta,
2206 : PCMK_META_TARGET_ROLE);
2207 0 : uint32_t show_opts = pcmk_show_rsc_only | pcmk_show_pending;
2208 :
2209 0 : if (node == NULL) {
2210 0 : node = rsc->pending_node;
2211 : }
2212 :
2213 0 : node_str = pcmk__native_output_string(rsc, rsc_printable_id(rsc), node,
2214 : show_opts, target_role, false);
2215 : } else {
2216 0 : node_str = crm_strdup_printf("Unknown resource %s", op_rsc);
2217 : }
2218 :
2219 0 : if (crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE,
2220 : &last_change) == pcmk_ok) {
2221 0 : const char *exec_time = crm_element_value(xml_op, PCMK_XA_EXEC_TIME);
2222 :
2223 0 : last_change_str = crm_strdup_printf(", %s='%s', exec=%sms",
2224 : PCMK_XA_LAST_RC_CHANGE,
2225 : pcmk__trim(ctime(&last_change)),
2226 : exec_time);
2227 : }
2228 :
2229 0 : out->list_item(out, NULL, "%s: %s (node=%s, call=%s, rc=%s%s): %s",
2230 : node_str, pcmk__xe_history_key(xml_op),
2231 : crm_element_value(xml_op, PCMK_XA_UNAME),
2232 : crm_element_value(xml_op, PCMK__XA_CALL_ID),
2233 : crm_element_value(xml_op, PCMK__XA_RC_CODE),
2234 : last_change_str ? last_change_str : "",
2235 : pcmk_exec_status_str(status));
2236 :
2237 0 : g_free(node_str);
2238 0 : free(last_change_str);
2239 0 : return pcmk_rc_ok;
2240 : }
2241 :
2242 : PCMK__OUTPUT_ARGS("node-and-op", "pcmk_scheduler_t *", "xmlNode *")
2243 : static int
2244 0 : node_and_op_xml(pcmk__output_t *out, va_list args) {
2245 0 : pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
2246 0 : xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
2247 :
2248 0 : pcmk_resource_t *rsc = NULL;
2249 0 : const char *uname = crm_element_value(xml_op, PCMK_XA_UNAME);
2250 0 : const char *call_id = crm_element_value(xml_op, PCMK__XA_CALL_ID);
2251 0 : const char *rc_s = crm_element_value(xml_op, PCMK__XA_RC_CODE);
2252 0 : const char *status_s = NULL;
2253 0 : const char *op_rsc = crm_element_value(xml_op, PCMK_XA_RESOURCE);
2254 : int status;
2255 0 : time_t last_change = 0;
2256 0 : xmlNode *node = NULL;
2257 :
2258 0 : pcmk__scan_min_int(crm_element_value(xml_op, PCMK__XA_OP_STATUS),
2259 : &status, PCMK_EXEC_UNKNOWN);
2260 0 : status_s = pcmk_exec_status_str(status);
2261 :
2262 0 : node = pcmk__output_create_xml_node(out, PCMK_XE_OPERATION,
2263 : PCMK_XA_OP, pcmk__xe_history_key(xml_op),
2264 : PCMK_XA_NODE, uname,
2265 : PCMK_XA_CALL, call_id,
2266 : PCMK_XA_RC, rc_s,
2267 : PCMK_XA_STATUS, status_s,
2268 : NULL);
2269 :
2270 0 : rsc = pe_find_resource(scheduler->resources, op_rsc);
2271 :
2272 0 : if (rsc) {
2273 0 : const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS);
2274 0 : const char *provider = crm_element_value(rsc->xml, PCMK_XA_PROVIDER);
2275 0 : const char *kind = crm_element_value(rsc->xml, PCMK_XA_TYPE);
2276 0 : bool has_provider = pcmk_is_set(pcmk_get_ra_caps(class),
2277 : pcmk_ra_cap_provider);
2278 :
2279 0 : char *agent_tuple = crm_strdup_printf("%s:%s:%s",
2280 : class,
2281 : (has_provider? provider : ""),
2282 : kind);
2283 :
2284 0 : pcmk__xe_set_props(node,
2285 : PCMK_XA_RSC, rsc_printable_id(rsc),
2286 : PCMK_XA_AGENT, agent_tuple,
2287 : NULL);
2288 0 : free(agent_tuple);
2289 : }
2290 :
2291 0 : if (crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE,
2292 : &last_change) == pcmk_ok) {
2293 0 : const char *last_rc_change = pcmk__trim(ctime(&last_change));
2294 0 : const char *exec_time = crm_element_value(xml_op, PCMK_XA_EXEC_TIME);
2295 :
2296 0 : pcmk__xe_set_props(node,
2297 : PCMK_XA_LAST_RC_CHANGE, last_rc_change,
2298 : PCMK_XA_EXEC_TIME, exec_time,
2299 : NULL);
2300 : }
2301 :
2302 0 : return pcmk_rc_ok;
2303 : }
2304 :
2305 : PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "bool", "int")
2306 : static int
2307 0 : node_attribute_xml(pcmk__output_t *out, va_list args) {
2308 0 : const char *name = va_arg(args, const char *);
2309 0 : const char *value = va_arg(args, const char *);
2310 0 : bool add_extra = va_arg(args, int);
2311 0 : int expected_score = va_arg(args, int);
2312 :
2313 0 : xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_ATTRIBUTE,
2314 : PCMK_XA_NAME, name,
2315 : PCMK_XA_VALUE, value,
2316 : NULL);
2317 :
2318 0 : if (add_extra) {
2319 0 : char *buf = pcmk__itoa(expected_score);
2320 0 : crm_xml_add(node, PCMK_XA_EXPECTED, buf);
2321 0 : free(buf);
2322 : }
2323 :
2324 0 : return pcmk_rc_ok;
2325 : }
2326 :
2327 : PCMK__OUTPUT_ARGS("node-attribute-list", "pcmk_scheduler_t *", "uint32_t",
2328 : "bool", "GList *", "GList *")
2329 : static int
2330 0 : node_attribute_list(pcmk__output_t *out, va_list args) {
2331 0 : pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
2332 0 : uint32_t show_opts = va_arg(args, uint32_t);
2333 0 : bool print_spacer = va_arg(args, int);
2334 0 : GList *only_node = va_arg(args, GList *);
2335 0 : GList *only_rsc = va_arg(args, GList *);
2336 :
2337 0 : int rc = pcmk_rc_no_output;
2338 :
2339 : /* Display each node's attributes */
2340 0 : for (GList *gIter = scheduler->nodes; gIter != NULL; gIter = gIter->next) {
2341 0 : pcmk_node_t *node = gIter->data;
2342 :
2343 0 : GList *attr_list = NULL;
2344 : GHashTableIter iter;
2345 : gpointer key;
2346 :
2347 0 : if (!node || !node->details || !node->details->online) {
2348 0 : continue;
2349 : }
2350 :
2351 0 : g_hash_table_iter_init(&iter, node->details->attrs);
2352 0 : while (g_hash_table_iter_next (&iter, &key, NULL)) {
2353 0 : attr_list = filter_attr_list(attr_list, key);
2354 : }
2355 :
2356 0 : if (attr_list == NULL) {
2357 0 : continue;
2358 : }
2359 :
2360 0 : if (!pcmk__str_in_list(node->details->uname, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
2361 0 : g_list_free(attr_list);
2362 0 : continue;
2363 : }
2364 :
2365 0 : PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Node Attributes");
2366 :
2367 0 : out->message(out, "node", node, show_opts, false, only_node, only_rsc);
2368 :
2369 0 : for (GList *aIter = attr_list; aIter != NULL; aIter = aIter->next) {
2370 0 : const char *name = aIter->data;
2371 0 : const char *value = NULL;
2372 0 : int expected_score = 0;
2373 0 : bool add_extra = false;
2374 :
2375 0 : value = pcmk__node_attr(node, name, NULL, pcmk__rsc_node_current);
2376 :
2377 0 : add_extra = add_extra_info(node, node->details->running_rsc,
2378 : scheduler, name, &expected_score);
2379 :
2380 : /* Print attribute name and value */
2381 0 : out->message(out, "node-attribute", name, value, add_extra,
2382 : expected_score);
2383 : }
2384 :
2385 0 : g_list_free(attr_list);
2386 0 : out->end_list(out);
2387 : }
2388 :
2389 0 : PCMK__OUTPUT_LIST_FOOTER(out, rc);
2390 0 : return rc;
2391 : }
2392 :
2393 : PCMK__OUTPUT_ARGS("node-capacity", "const pcmk_node_t *", "const char *")
2394 : static int
2395 0 : node_capacity(pcmk__output_t *out, va_list args)
2396 : {
2397 0 : const pcmk_node_t *node = va_arg(args, pcmk_node_t *);
2398 0 : const char *comment = va_arg(args, const char *);
2399 :
2400 0 : char *dump_text = crm_strdup_printf("%s: %s capacity:",
2401 : comment, pcmk__node_name(node));
2402 :
2403 0 : g_hash_table_foreach(node->details->utilization, append_dump_text, &dump_text);
2404 0 : out->list_item(out, NULL, "%s", dump_text);
2405 0 : free(dump_text);
2406 :
2407 0 : return pcmk_rc_ok;
2408 : }
2409 :
2410 : PCMK__OUTPUT_ARGS("node-capacity", "const pcmk_node_t *", "const char *")
2411 : static int
2412 0 : node_capacity_xml(pcmk__output_t *out, va_list args)
2413 : {
2414 0 : const pcmk_node_t *node = va_arg(args, pcmk_node_t *);
2415 0 : const char *uname = node->details->uname;
2416 0 : const char *comment = va_arg(args, const char *);
2417 :
2418 0 : xmlNodePtr xml_node = pcmk__output_create_xml_node(out, PCMK_XE_CAPACITY,
2419 : PCMK_XA_NODE, uname,
2420 : PCMK_XA_COMMENT, comment,
2421 : NULL);
2422 0 : g_hash_table_foreach(node->details->utilization, add_dump_node, xml_node);
2423 :
2424 0 : return pcmk_rc_ok;
2425 : }
2426 :
2427 : PCMK__OUTPUT_ARGS("node-history-list", "pcmk_scheduler_t *", "pcmk_node_t *",
2428 : "xmlNode *", "GList *", "GList *", "uint32_t", "uint32_t")
2429 : static int
2430 0 : node_history_list(pcmk__output_t *out, va_list args) {
2431 0 : pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
2432 0 : pcmk_node_t *node = va_arg(args, pcmk_node_t *);
2433 0 : xmlNode *node_state = va_arg(args, xmlNode *);
2434 0 : GList *only_node = va_arg(args, GList *);
2435 0 : GList *only_rsc = va_arg(args, GList *);
2436 0 : uint32_t section_opts = va_arg(args, uint32_t);
2437 0 : uint32_t show_opts = va_arg(args, uint32_t);
2438 :
2439 0 : xmlNode *lrm_rsc = NULL;
2440 0 : xmlNode *rsc_entry = NULL;
2441 0 : int rc = pcmk_rc_no_output;
2442 :
2443 0 : lrm_rsc = pcmk__xe_first_child(node_state, PCMK__XE_LRM, NULL, NULL);
2444 0 : lrm_rsc = pcmk__xe_first_child(lrm_rsc, PCMK__XE_LRM_RESOURCES, NULL, NULL);
2445 :
2446 : /* Print history of each of the node's resources */
2447 0 : for (rsc_entry = pcmk__xe_first_child(lrm_rsc, PCMK__XE_LRM_RESOURCE, NULL,
2448 : NULL);
2449 0 : rsc_entry != NULL; rsc_entry = pcmk__xe_next_same(rsc_entry)) {
2450 :
2451 0 : const char *rsc_id = crm_element_value(rsc_entry, PCMK_XA_ID);
2452 0 : pcmk_resource_t *rsc = pe_find_resource(scheduler->resources, rsc_id);
2453 0 : const pcmk_resource_t *parent = pe__const_top_resource(rsc, false);
2454 :
2455 : /* We can't use is_filtered here to filter group resources. For is_filtered,
2456 : * we have to decide whether to check the parent or not. If we check the
2457 : * parent, all elements of a group will always be printed because that's how
2458 : * is_filtered works for groups. If we do not check the parent, sometimes
2459 : * this will filter everything out.
2460 : *
2461 : * For other resource types, is_filtered is okay.
2462 : */
2463 0 : if (parent->variant == pcmk_rsc_variant_group) {
2464 0 : if (!pcmk__str_in_list(rsc_printable_id(rsc), only_rsc,
2465 : pcmk__str_star_matches)
2466 0 : && !pcmk__str_in_list(rsc_printable_id(parent), only_rsc,
2467 : pcmk__str_star_matches)) {
2468 0 : continue;
2469 : }
2470 : } else {
2471 0 : if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
2472 0 : continue;
2473 : }
2474 : }
2475 :
2476 0 : if (!pcmk_is_set(section_opts, pcmk_section_operations)) {
2477 0 : time_t last_failure = 0;
2478 0 : int failcount = pe_get_failcount(node, rsc, &last_failure,
2479 : pcmk__fc_default, NULL);
2480 :
2481 0 : if (failcount <= 0) {
2482 0 : continue;
2483 : }
2484 :
2485 0 : if (rc == pcmk_rc_no_output) {
2486 0 : rc = pcmk_rc_ok;
2487 0 : out->message(out, "node", node, show_opts, false, only_node,
2488 : only_rsc);
2489 : }
2490 :
2491 0 : out->message(out, "resource-history", rsc, rsc_id, false,
2492 : failcount, last_failure, false);
2493 : } else {
2494 0 : GList *op_list = get_operation_list(rsc_entry);
2495 0 : pcmk_resource_t *rsc = NULL;
2496 :
2497 0 : if (op_list == NULL) {
2498 0 : continue;
2499 : }
2500 :
2501 0 : rsc = pe_find_resource(scheduler->resources,
2502 : crm_element_value(rsc_entry, PCMK_XA_ID));
2503 :
2504 0 : if (rc == pcmk_rc_no_output) {
2505 0 : rc = pcmk_rc_ok;
2506 0 : out->message(out, "node", node, show_opts, false, only_node,
2507 : only_rsc);
2508 : }
2509 :
2510 0 : out->message(out, "resource-operation-list", scheduler, rsc, node,
2511 : op_list, show_opts);
2512 : }
2513 : }
2514 :
2515 0 : PCMK__OUTPUT_LIST_FOOTER(out, rc);
2516 0 : return rc;
2517 : }
2518 :
2519 : PCMK__OUTPUT_ARGS("node-list", "GList *", "GList *", "GList *", "uint32_t", "bool")
2520 : static int
2521 0 : node_list_html(pcmk__output_t *out, va_list args) {
2522 0 : GList *nodes = va_arg(args, GList *);
2523 0 : GList *only_node = va_arg(args, GList *);
2524 0 : GList *only_rsc = va_arg(args, GList *);
2525 0 : uint32_t show_opts = va_arg(args, uint32_t);
2526 0 : bool print_spacer G_GNUC_UNUSED = va_arg(args, int);
2527 :
2528 0 : int rc = pcmk_rc_no_output;
2529 :
2530 0 : for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) {
2531 0 : pcmk_node_t *node = (pcmk_node_t *) gIter->data;
2532 :
2533 0 : if (!pcmk__str_in_list(node->details->uname, only_node,
2534 : pcmk__str_star_matches|pcmk__str_casei)) {
2535 0 : continue;
2536 : }
2537 :
2538 0 : PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Node List");
2539 :
2540 0 : out->message(out, "node", node, show_opts, true, only_node, only_rsc);
2541 : }
2542 :
2543 0 : PCMK__OUTPUT_LIST_FOOTER(out, rc);
2544 0 : return rc;
2545 : }
2546 :
2547 : PCMK__OUTPUT_ARGS("node-list", "GList *", "GList *", "GList *", "uint32_t", "bool")
2548 : static int
2549 0 : node_list_text(pcmk__output_t *out, va_list args) {
2550 0 : GList *nodes = va_arg(args, GList *);
2551 0 : GList *only_node = va_arg(args, GList *);
2552 0 : GList *only_rsc = va_arg(args, GList *);
2553 0 : uint32_t show_opts = va_arg(args, uint32_t);
2554 0 : bool print_spacer = va_arg(args, int);
2555 :
2556 : /* space-separated lists of node names */
2557 0 : GString *online_nodes = NULL;
2558 0 : GString *online_remote_nodes = NULL;
2559 0 : GString *online_guest_nodes = NULL;
2560 0 : GString *offline_nodes = NULL;
2561 0 : GString *offline_remote_nodes = NULL;
2562 :
2563 0 : int rc = pcmk_rc_no_output;
2564 :
2565 0 : for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) {
2566 0 : pcmk_node_t *node = (pcmk_node_t *) gIter->data;
2567 0 : char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
2568 :
2569 0 : if (!pcmk__str_in_list(node->details->uname, only_node,
2570 : pcmk__str_star_matches|pcmk__str_casei)) {
2571 0 : free(node_name);
2572 0 : continue;
2573 : }
2574 :
2575 0 : PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Node List");
2576 :
2577 : // Determine whether to display node individually or in a list
2578 0 : if (node->details->unclean || node->details->pending
2579 0 : || (node->details->standby_onfail && node->details->online)
2580 0 : || node->details->standby || node->details->maintenance
2581 0 : || pcmk_is_set(show_opts, pcmk_show_rscs_by_node)
2582 0 : || pcmk_is_set(show_opts, pcmk_show_feature_set)
2583 0 : || (pe__node_health(node) <= 0)) {
2584 : // Display node individually
2585 :
2586 0 : } else if (node->details->online) {
2587 : // Display online node in a list
2588 0 : if (pcmk__is_guest_or_bundle_node(node)) {
2589 0 : pcmk__add_word(&online_guest_nodes, 1024, node_name);
2590 :
2591 0 : } else if (pcmk__is_remote_node(node)) {
2592 0 : pcmk__add_word(&online_remote_nodes, 1024, node_name);
2593 :
2594 : } else {
2595 0 : pcmk__add_word(&online_nodes, 1024, node_name);
2596 : }
2597 0 : free(node_name);
2598 0 : continue;
2599 :
2600 : } else {
2601 : // Display offline node in a list
2602 0 : if (pcmk__is_remote_node(node)) {
2603 0 : pcmk__add_word(&offline_remote_nodes, 1024, node_name);
2604 :
2605 0 : } else if (pcmk__is_guest_or_bundle_node(node)) {
2606 : /* ignore offline guest nodes */
2607 :
2608 : } else {
2609 0 : pcmk__add_word(&offline_nodes, 1024, node_name);
2610 : }
2611 0 : free(node_name);
2612 0 : continue;
2613 : }
2614 :
2615 : /* If we get here, node is in bad state, or we're grouping by node */
2616 0 : out->message(out, "node", node, show_opts, true, only_node, only_rsc);
2617 0 : free(node_name);
2618 : }
2619 :
2620 : /* If we're not grouping by node, summarize nodes by status */
2621 0 : if (online_nodes != NULL) {
2622 0 : out->list_item(out, "Online", "[ %s ]",
2623 0 : (const char *) online_nodes->str);
2624 0 : g_string_free(online_nodes, TRUE);
2625 : }
2626 0 : if (offline_nodes != NULL) {
2627 0 : out->list_item(out, "OFFLINE", "[ %s ]",
2628 0 : (const char *) offline_nodes->str);
2629 0 : g_string_free(offline_nodes, TRUE);
2630 : }
2631 0 : if (online_remote_nodes) {
2632 0 : out->list_item(out, "RemoteOnline", "[ %s ]",
2633 0 : (const char *) online_remote_nodes->str);
2634 0 : g_string_free(online_remote_nodes, TRUE);
2635 : }
2636 0 : if (offline_remote_nodes) {
2637 0 : out->list_item(out, "RemoteOFFLINE", "[ %s ]",
2638 0 : (const char *) offline_remote_nodes->str);
2639 0 : g_string_free(offline_remote_nodes, TRUE);
2640 : }
2641 0 : if (online_guest_nodes != NULL) {
2642 0 : out->list_item(out, "GuestOnline", "[ %s ]",
2643 0 : (const char *) online_guest_nodes->str);
2644 0 : g_string_free(online_guest_nodes, TRUE);
2645 : }
2646 :
2647 0 : PCMK__OUTPUT_LIST_FOOTER(out, rc);
2648 0 : return rc;
2649 : }
2650 :
2651 : PCMK__OUTPUT_ARGS("node-list", "GList *", "GList *", "GList *", "uint32_t", "bool")
2652 : static int
2653 0 : node_list_xml(pcmk__output_t *out, va_list args) {
2654 0 : GList *nodes = va_arg(args, GList *);
2655 0 : GList *only_node = va_arg(args, GList *);
2656 0 : GList *only_rsc = va_arg(args, GList *);
2657 0 : uint32_t show_opts = va_arg(args, uint32_t);
2658 0 : bool print_spacer G_GNUC_UNUSED = va_arg(args, int);
2659 :
2660 : /* PCMK_XE_NODES acts as the list's element name for CLI tools that use
2661 : * pcmk__output_enable_list_element. Otherwise PCMK_XE_NODES is the
2662 : * value of the list's PCMK_XA_NAME attribute.
2663 : */
2664 0 : out->begin_list(out, NULL, NULL, PCMK_XE_NODES);
2665 0 : for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) {
2666 0 : pcmk_node_t *node = (pcmk_node_t *) gIter->data;
2667 :
2668 0 : if (!pcmk__str_in_list(node->details->uname, only_node,
2669 : pcmk__str_star_matches|pcmk__str_casei)) {
2670 0 : continue;
2671 : }
2672 :
2673 0 : out->message(out, "node", node, show_opts, true, only_node, only_rsc);
2674 : }
2675 0 : out->end_list(out);
2676 :
2677 0 : return pcmk_rc_ok;
2678 : }
2679 :
2680 : PCMK__OUTPUT_ARGS("node-summary", "pcmk_scheduler_t *", "GList *", "GList *",
2681 : "uint32_t", "uint32_t", "bool")
2682 : static int
2683 0 : node_summary(pcmk__output_t *out, va_list args) {
2684 0 : pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
2685 0 : GList *only_node = va_arg(args, GList *);
2686 0 : GList *only_rsc = va_arg(args, GList *);
2687 0 : uint32_t section_opts = va_arg(args, uint32_t);
2688 0 : uint32_t show_opts = va_arg(args, uint32_t);
2689 0 : bool print_spacer = va_arg(args, int);
2690 :
2691 0 : xmlNode *node_state = NULL;
2692 0 : xmlNode *cib_status = pcmk_find_cib_element(scheduler->input,
2693 : PCMK_XE_STATUS);
2694 0 : int rc = pcmk_rc_no_output;
2695 :
2696 0 : if (xmlChildElementCount(cib_status) == 0) {
2697 0 : return rc;
2698 : }
2699 :
2700 0 : for (node_state = pcmk__xe_first_child(cib_status, PCMK__XE_NODE_STATE,
2701 : NULL, NULL);
2702 0 : node_state != NULL; node_state = pcmk__xe_next_same(node_state)) {
2703 :
2704 0 : pcmk_node_t *node = pe_find_node_id(scheduler->nodes,
2705 : pcmk__xe_id(node_state));
2706 :
2707 0 : if (!node || !node->details || !node->details->online) {
2708 0 : continue;
2709 : }
2710 :
2711 0 : if (!pcmk__str_in_list(node->details->uname, only_node,
2712 : pcmk__str_star_matches|pcmk__str_casei)) {
2713 0 : continue;
2714 : }
2715 :
2716 0 : PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc,
2717 : pcmk_is_set(section_opts, pcmk_section_operations) ? "Operations" : "Migration Summary");
2718 :
2719 0 : out->message(out, "node-history-list", scheduler, node, node_state,
2720 : only_node, only_rsc, section_opts, show_opts);
2721 : }
2722 :
2723 0 : PCMK__OUTPUT_LIST_FOOTER(out, rc);
2724 0 : return rc;
2725 : }
2726 :
2727 : PCMK__OUTPUT_ARGS("node-weight", "const pcmk_resource_t *", "const char *",
2728 : "const char *", "const char *")
2729 : static int
2730 0 : node_weight(pcmk__output_t *out, va_list args)
2731 : {
2732 0 : const pcmk_resource_t *rsc = va_arg(args, const pcmk_resource_t *);
2733 0 : const char *prefix = va_arg(args, const char *);
2734 0 : const char *uname = va_arg(args, const char *);
2735 0 : const char *score = va_arg(args, const char *);
2736 :
2737 0 : if (rsc) {
2738 0 : out->list_item(out, NULL, "%s: %s allocation score on %s: %s",
2739 0 : prefix, rsc->id, uname, score);
2740 : } else {
2741 0 : out->list_item(out, NULL, "%s: %s = %s", prefix, uname, score);
2742 : }
2743 :
2744 0 : return pcmk_rc_ok;
2745 : }
2746 :
2747 : PCMK__OUTPUT_ARGS("node-weight", "const pcmk_resource_t *", "const char *",
2748 : "const char *", "const char *")
2749 : static int
2750 0 : node_weight_xml(pcmk__output_t *out, va_list args)
2751 : {
2752 0 : const pcmk_resource_t *rsc = va_arg(args, const pcmk_resource_t *);
2753 0 : const char *prefix = va_arg(args, const char *);
2754 0 : const char *uname = va_arg(args, const char *);
2755 0 : const char *score = va_arg(args, const char *);
2756 :
2757 0 : xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_NODE_WEIGHT,
2758 : PCMK_XA_FUNCTION, prefix,
2759 : PCMK_XA_NODE, uname,
2760 : PCMK_XA_SCORE, score,
2761 : NULL);
2762 :
2763 0 : if (rsc) {
2764 0 : crm_xml_add(node, PCMK_XA_ID, rsc->id);
2765 : }
2766 :
2767 0 : return pcmk_rc_ok;
2768 : }
2769 :
2770 : PCMK__OUTPUT_ARGS("op-history", "xmlNode *", "const char *", "const char *", "int", "uint32_t")
2771 : static int
2772 0 : op_history_text(pcmk__output_t *out, va_list args) {
2773 0 : xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
2774 0 : const char *task = va_arg(args, const char *);
2775 0 : const char *interval_ms_s = va_arg(args, const char *);
2776 0 : int rc = va_arg(args, int);
2777 0 : uint32_t show_opts = va_arg(args, uint32_t);
2778 :
2779 0 : char *buf = op_history_string(xml_op, task, interval_ms_s, rc,
2780 0 : pcmk_is_set(show_opts, pcmk_show_timing));
2781 :
2782 0 : out->list_item(out, NULL, "%s", buf);
2783 :
2784 0 : free(buf);
2785 0 : return pcmk_rc_ok;
2786 : }
2787 :
2788 : PCMK__OUTPUT_ARGS("op-history", "xmlNode *", "const char *", "const char *", "int", "uint32_t")
2789 : static int
2790 0 : op_history_xml(pcmk__output_t *out, va_list args) {
2791 0 : xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
2792 0 : const char *task = va_arg(args, const char *);
2793 0 : const char *interval_ms_s = va_arg(args, const char *);
2794 0 : int rc = va_arg(args, int);
2795 0 : uint32_t show_opts = va_arg(args, uint32_t);
2796 :
2797 0 : const char *call_id = crm_element_value(xml_op, PCMK__XA_CALL_ID);
2798 0 : char *rc_s = pcmk__itoa(rc);
2799 0 : const char *rc_text = services_ocf_exitcode_str(rc);
2800 0 : xmlNodePtr node = NULL;
2801 :
2802 0 : node = pcmk__output_create_xml_node(out, PCMK_XE_OPERATION_HISTORY,
2803 : PCMK_XA_CALL, call_id,
2804 : PCMK_XA_TASK, task,
2805 : PCMK_XA_RC, rc_s,
2806 : PCMK_XA_RC_TEXT, rc_text,
2807 : NULL);
2808 0 : free(rc_s);
2809 :
2810 0 : if (interval_ms_s && !pcmk__str_eq(interval_ms_s, "0", pcmk__str_casei)) {
2811 0 : char *s = crm_strdup_printf("%sms", interval_ms_s);
2812 0 : crm_xml_add(node, PCMK_XA_INTERVAL, s);
2813 0 : free(s);
2814 : }
2815 :
2816 0 : if (pcmk_is_set(show_opts, pcmk_show_timing)) {
2817 0 : const char *value = NULL;
2818 0 : time_t epoch = 0;
2819 :
2820 0 : if ((crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE,
2821 0 : &epoch) == pcmk_ok) && (epoch > 0)) {
2822 0 : char *s = pcmk__epoch2str(&epoch, 0);
2823 0 : crm_xml_add(node, PCMK_XA_LAST_RC_CHANGE, s);
2824 0 : free(s);
2825 : }
2826 :
2827 0 : value = crm_element_value(xml_op, PCMK_XA_EXEC_TIME);
2828 0 : if (value) {
2829 0 : char *s = crm_strdup_printf("%sms", value);
2830 0 : crm_xml_add(node, PCMK_XA_EXEC_TIME, s);
2831 0 : free(s);
2832 : }
2833 0 : value = crm_element_value(xml_op, PCMK_XA_QUEUE_TIME);
2834 0 : if (value) {
2835 0 : char *s = crm_strdup_printf("%sms", value);
2836 0 : crm_xml_add(node, PCMK_XA_QUEUE_TIME, s);
2837 0 : free(s);
2838 : }
2839 : }
2840 :
2841 0 : return pcmk_rc_ok;
2842 : }
2843 :
2844 : PCMK__OUTPUT_ARGS("promotion-score", "pcmk_resource_t *", "pcmk_node_t *",
2845 : "const char *")
2846 : static int
2847 0 : promotion_score(pcmk__output_t *out, va_list args)
2848 : {
2849 0 : pcmk_resource_t *child_rsc = va_arg(args, pcmk_resource_t *);
2850 0 : pcmk_node_t *chosen = va_arg(args, pcmk_node_t *);
2851 0 : const char *score = va_arg(args, const char *);
2852 :
2853 0 : out->list_item(out, NULL, "%s promotion score on %s: %s",
2854 : child_rsc->id,
2855 0 : chosen? chosen->details->uname : "none",
2856 : score);
2857 0 : return pcmk_rc_ok;
2858 : }
2859 :
2860 : PCMK__OUTPUT_ARGS("promotion-score", "pcmk_resource_t *", "pcmk_node_t *",
2861 : "const char *")
2862 : static int
2863 0 : promotion_score_xml(pcmk__output_t *out, va_list args)
2864 : {
2865 0 : pcmk_resource_t *child_rsc = va_arg(args, pcmk_resource_t *);
2866 0 : pcmk_node_t *chosen = va_arg(args, pcmk_node_t *);
2867 0 : const char *score = va_arg(args, const char *);
2868 :
2869 0 : xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_PROMOTION_SCORE,
2870 : PCMK_XA_ID, child_rsc->id,
2871 : PCMK_XA_SCORE, score,
2872 : NULL);
2873 :
2874 0 : if (chosen) {
2875 0 : crm_xml_add(node, PCMK_XA_NODE, chosen->details->uname);
2876 : }
2877 :
2878 0 : return pcmk_rc_ok;
2879 : }
2880 :
2881 : PCMK__OUTPUT_ARGS("resource-config", "const pcmk_resource_t *", "bool")
2882 : static int
2883 0 : resource_config(pcmk__output_t *out, va_list args) {
2884 0 : const pcmk_resource_t *rsc = va_arg(args, const pcmk_resource_t *);
2885 0 : GString *xml_buf = g_string_sized_new(1024);
2886 0 : bool raw = va_arg(args, int);
2887 :
2888 0 : formatted_xml_buf(rsc, xml_buf, raw);
2889 :
2890 0 : out->output_xml(out, PCMK_XE_XML, xml_buf->str);
2891 :
2892 0 : g_string_free(xml_buf, TRUE);
2893 0 : return pcmk_rc_ok;
2894 : }
2895 :
2896 : PCMK__OUTPUT_ARGS("resource-config", "const pcmk_resource_t *", "bool")
2897 : static int
2898 0 : resource_config_text(pcmk__output_t *out, va_list args) {
2899 0 : pcmk__formatted_printf(out, "Resource XML:\n");
2900 0 : return resource_config(out, args);
2901 : }
2902 :
2903 : PCMK__OUTPUT_ARGS("resource-history", "pcmk_resource_t *", "const char *",
2904 : "bool", "int", "time_t", "bool")
2905 : static int
2906 0 : resource_history_text(pcmk__output_t *out, va_list args) {
2907 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
2908 0 : const char *rsc_id = va_arg(args, const char *);
2909 0 : bool all = va_arg(args, int);
2910 0 : int failcount = va_arg(args, int);
2911 0 : time_t last_failure = va_arg(args, time_t);
2912 0 : bool as_header = va_arg(args, int);
2913 :
2914 0 : char *buf = resource_history_string(rsc, rsc_id, all, failcount, last_failure);
2915 :
2916 0 : if (as_header) {
2917 0 : out->begin_list(out, NULL, NULL, "%s", buf);
2918 : } else {
2919 0 : out->list_item(out, NULL, "%s", buf);
2920 : }
2921 :
2922 0 : free(buf);
2923 0 : return pcmk_rc_ok;
2924 : }
2925 :
2926 : PCMK__OUTPUT_ARGS("resource-history", "pcmk_resource_t *", "const char *",
2927 : "bool", "int", "time_t", "bool")
2928 : static int
2929 0 : resource_history_xml(pcmk__output_t *out, va_list args) {
2930 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
2931 0 : const char *rsc_id = va_arg(args, const char *);
2932 0 : bool all = va_arg(args, int);
2933 0 : int failcount = va_arg(args, int);
2934 0 : time_t last_failure = va_arg(args, time_t);
2935 0 : bool as_header = va_arg(args, int);
2936 :
2937 0 : xmlNodePtr node = pcmk__output_xml_create_parent(out,
2938 : PCMK_XE_RESOURCE_HISTORY,
2939 : PCMK_XA_ID, rsc_id,
2940 : NULL);
2941 :
2942 0 : if (rsc == NULL) {
2943 0 : pcmk__xe_set_bool_attr(node, PCMK_XA_ORPHAN, true);
2944 0 : } else if (all || failcount || last_failure > 0) {
2945 0 : char *migration_s = pcmk__itoa(rsc->migration_threshold);
2946 :
2947 0 : pcmk__xe_set_props(node,
2948 : PCMK_XA_ORPHAN, PCMK_VALUE_FALSE,
2949 : PCMK_META_MIGRATION_THRESHOLD, migration_s,
2950 : NULL);
2951 0 : free(migration_s);
2952 :
2953 0 : if (failcount > 0) {
2954 0 : char *s = pcmk__itoa(failcount);
2955 :
2956 0 : crm_xml_add(node, PCMK_XA_FAIL_COUNT, s);
2957 0 : free(s);
2958 : }
2959 :
2960 0 : if (last_failure > 0) {
2961 0 : char *s = pcmk__epoch2str(&last_failure, 0);
2962 :
2963 0 : crm_xml_add(node, PCMK_XA_LAST_FAILURE, s);
2964 0 : free(s);
2965 : }
2966 : }
2967 :
2968 0 : if (!as_header) {
2969 0 : pcmk__output_xml_pop_parent(out);
2970 : }
2971 :
2972 0 : return pcmk_rc_ok;
2973 : }
2974 :
2975 : static void
2976 0 : print_resource_header(pcmk__output_t *out, uint32_t show_opts)
2977 : {
2978 0 : if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
2979 : /* Active resources have already been printed by node */
2980 0 : out->begin_list(out, NULL, NULL, "Inactive Resources");
2981 0 : } else if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
2982 0 : out->begin_list(out, NULL, NULL, "Full List of Resources");
2983 : } else {
2984 0 : out->begin_list(out, NULL, NULL, "Active Resources");
2985 : }
2986 0 : }
2987 :
2988 :
2989 : PCMK__OUTPUT_ARGS("resource-list", "pcmk_scheduler_t *", "uint32_t", "bool",
2990 : "GList *", "GList *", "bool")
2991 : static int
2992 0 : resource_list(pcmk__output_t *out, va_list args)
2993 : {
2994 0 : pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
2995 0 : uint32_t show_opts = va_arg(args, uint32_t);
2996 0 : bool print_summary = va_arg(args, int);
2997 0 : GList *only_node = va_arg(args, GList *);
2998 0 : GList *only_rsc = va_arg(args, GList *);
2999 0 : bool print_spacer = va_arg(args, int);
3000 :
3001 : GList *rsc_iter;
3002 0 : int rc = pcmk_rc_no_output;
3003 0 : bool printed_header = false;
3004 :
3005 : /* If we already showed active resources by node, and
3006 : * we're not showing inactive resources, we have nothing to do
3007 : */
3008 0 : if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node) &&
3009 0 : !pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
3010 0 : return rc;
3011 : }
3012 :
3013 : /* If we haven't already printed resources grouped by node,
3014 : * and brief output was requested, print resource summary */
3015 0 : if (pcmk_is_set(show_opts, pcmk_show_brief)
3016 0 : && !pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
3017 0 : GList *rscs = pe__filter_rsc_list(scheduler->resources, only_rsc);
3018 :
3019 0 : PCMK__OUTPUT_SPACER_IF(out, print_spacer);
3020 0 : print_resource_header(out, show_opts);
3021 0 : printed_header = true;
3022 :
3023 0 : rc = pe__rscs_brief_output(out, rscs, show_opts);
3024 0 : g_list_free(rscs);
3025 : }
3026 :
3027 : /* For each resource, display it if appropriate */
3028 0 : for (rsc_iter = scheduler->resources; rsc_iter != NULL; rsc_iter = rsc_iter->next) {
3029 0 : pcmk_resource_t *rsc = (pcmk_resource_t *) rsc_iter->data;
3030 : int x;
3031 :
3032 : /* Complex resources may have some sub-resources active and some inactive */
3033 0 : gboolean is_active = rsc->fns->active(rsc, TRUE);
3034 0 : gboolean partially_active = rsc->fns->active(rsc, FALSE);
3035 :
3036 : /* Skip inactive orphans (deleted but still in CIB) */
3037 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_removed) && !is_active) {
3038 0 : continue;
3039 :
3040 : /* Skip active resources if we already displayed them by node */
3041 0 : } else if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
3042 0 : if (is_active) {
3043 0 : continue;
3044 : }
3045 :
3046 : /* Skip primitives already counted in a brief summary */
3047 0 : } else if (pcmk_is_set(show_opts, pcmk_show_brief)
3048 0 : && (rsc->variant == pcmk_rsc_variant_primitive)) {
3049 0 : continue;
3050 :
3051 : /* Skip resources that aren't at least partially active,
3052 : * unless we're displaying inactive resources
3053 : */
3054 0 : } else if (!partially_active && !pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
3055 0 : continue;
3056 :
3057 0 : } else if (partially_active && !pe__rsc_running_on_any(rsc, only_node)) {
3058 0 : continue;
3059 : }
3060 :
3061 0 : if (!printed_header) {
3062 0 : PCMK__OUTPUT_SPACER_IF(out, print_spacer);
3063 0 : print_resource_header(out, show_opts);
3064 0 : printed_header = true;
3065 : }
3066 :
3067 : /* Print this resource */
3068 0 : x = out->message(out, pcmk__map_element_name(rsc->xml), show_opts, rsc,
3069 : only_node, only_rsc);
3070 0 : if (x == pcmk_rc_ok) {
3071 0 : rc = pcmk_rc_ok;
3072 : }
3073 : }
3074 :
3075 0 : if (print_summary && rc != pcmk_rc_ok) {
3076 0 : if (!printed_header) {
3077 0 : PCMK__OUTPUT_SPACER_IF(out, print_spacer);
3078 0 : print_resource_header(out, show_opts);
3079 0 : printed_header = true;
3080 : }
3081 :
3082 0 : if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
3083 0 : out->list_item(out, NULL, "No inactive resources");
3084 0 : } else if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
3085 0 : out->list_item(out, NULL, "No resources");
3086 : } else {
3087 0 : out->list_item(out, NULL, "No active resources");
3088 : }
3089 : }
3090 :
3091 0 : if (printed_header) {
3092 0 : out->end_list(out);
3093 : }
3094 :
3095 0 : return rc;
3096 : }
3097 :
3098 : PCMK__OUTPUT_ARGS("resource-operation-list", "pcmk_scheduler_t *",
3099 : "pcmk_resource_t *", "pcmk_node_t *", "GList *", "uint32_t")
3100 : static int
3101 0 : resource_operation_list(pcmk__output_t *out, va_list args)
3102 : {
3103 0 : pcmk_scheduler_t *scheduler G_GNUC_UNUSED = va_arg(args,
3104 : pcmk_scheduler_t *);
3105 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
3106 0 : pcmk_node_t *node = va_arg(args, pcmk_node_t *);
3107 0 : GList *op_list = va_arg(args, GList *);
3108 0 : uint32_t show_opts = va_arg(args, uint32_t);
3109 :
3110 0 : GList *gIter = NULL;
3111 0 : int rc = pcmk_rc_no_output;
3112 :
3113 : /* Print each operation */
3114 0 : for (gIter = op_list; gIter != NULL; gIter = gIter->next) {
3115 0 : xmlNode *xml_op = (xmlNode *) gIter->data;
3116 0 : const char *task = crm_element_value(xml_op, PCMK_XA_OPERATION);
3117 0 : const char *interval_ms_s = crm_element_value(xml_op,
3118 : PCMK_META_INTERVAL);
3119 0 : const char *op_rc = crm_element_value(xml_op, PCMK__XA_RC_CODE);
3120 : int op_rc_i;
3121 :
3122 0 : pcmk__scan_min_int(op_rc, &op_rc_i, 0);
3123 :
3124 : /* Display 0-interval monitors as "probe" */
3125 0 : if (pcmk__str_eq(task, PCMK_ACTION_MONITOR, pcmk__str_casei)
3126 0 : && pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches | pcmk__str_casei)) {
3127 0 : task = "probe";
3128 : }
3129 :
3130 : /* If this is the first printed operation, print heading for resource */
3131 0 : if (rc == pcmk_rc_no_output) {
3132 0 : time_t last_failure = 0;
3133 0 : int failcount = pe_get_failcount(node, rsc, &last_failure,
3134 : pcmk__fc_default, NULL);
3135 :
3136 0 : out->message(out, "resource-history", rsc, rsc_printable_id(rsc), true,
3137 : failcount, last_failure, true);
3138 0 : rc = pcmk_rc_ok;
3139 : }
3140 :
3141 : /* Print the operation */
3142 0 : out->message(out, "op-history", xml_op, task, interval_ms_s,
3143 : op_rc_i, show_opts);
3144 : }
3145 :
3146 : /* Free the list we created (no need to free the individual items) */
3147 0 : g_list_free(op_list);
3148 :
3149 0 : PCMK__OUTPUT_LIST_FOOTER(out, rc);
3150 0 : return rc;
3151 : }
3152 :
3153 : PCMK__OUTPUT_ARGS("resource-util", "pcmk_resource_t *", "pcmk_node_t *",
3154 : "const char *")
3155 : static int
3156 0 : resource_util(pcmk__output_t *out, va_list args)
3157 : {
3158 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
3159 0 : pcmk_node_t *node = va_arg(args, pcmk_node_t *);
3160 0 : const char *fn = va_arg(args, const char *);
3161 :
3162 0 : char *dump_text = crm_strdup_printf("%s: %s utilization on %s:",
3163 : fn, rsc->id, pcmk__node_name(node));
3164 :
3165 0 : g_hash_table_foreach(rsc->utilization, append_dump_text, &dump_text);
3166 0 : out->list_item(out, NULL, "%s", dump_text);
3167 0 : free(dump_text);
3168 :
3169 0 : return pcmk_rc_ok;
3170 : }
3171 :
3172 : PCMK__OUTPUT_ARGS("resource-util", "pcmk_resource_t *", "pcmk_node_t *",
3173 : "const char *")
3174 : static int
3175 0 : resource_util_xml(pcmk__output_t *out, va_list args)
3176 : {
3177 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
3178 0 : pcmk_node_t *node = va_arg(args, pcmk_node_t *);
3179 0 : const char *uname = node->details->uname;
3180 0 : const char *fn = va_arg(args, const char *);
3181 :
3182 0 : xmlNodePtr xml_node = NULL;
3183 :
3184 0 : xml_node = pcmk__output_create_xml_node(out, PCMK_XE_UTILIZATION,
3185 : PCMK_XA_RESOURCE, rsc->id,
3186 : PCMK_XA_NODE, uname,
3187 : PCMK_XA_FUNCTION, fn,
3188 : NULL);
3189 0 : g_hash_table_foreach(rsc->utilization, add_dump_node, xml_node);
3190 :
3191 0 : return pcmk_rc_ok;
3192 : }
3193 :
3194 : PCMK__OUTPUT_ARGS("ticket", "pcmk_ticket_t *", "bool", "bool")
3195 : static int
3196 0 : ticket_default(pcmk__output_t *out, va_list args) {
3197 0 : pcmk_ticket_t *ticket = va_arg(args, pcmk_ticket_t *);
3198 0 : bool raw = va_arg(args, int);
3199 0 : bool details = va_arg(args, int);
3200 :
3201 0 : GString *detail_str = NULL;
3202 :
3203 0 : if (raw) {
3204 0 : out->list_item(out, ticket->id, "%s", ticket->id);
3205 0 : return pcmk_rc_ok;
3206 : }
3207 :
3208 0 : if (details && g_hash_table_size(ticket->state) > 0) {
3209 : GHashTableIter iter;
3210 0 : const char *name = NULL;
3211 0 : const char *value = NULL;
3212 0 : bool already_added = false;
3213 :
3214 0 : detail_str = g_string_sized_new(100);
3215 0 : pcmk__g_strcat(detail_str, "\t(", NULL);
3216 :
3217 0 : g_hash_table_iter_init(&iter, ticket->state);
3218 0 : while (g_hash_table_iter_next(&iter, (void **) &name, (void **) &value)) {
3219 0 : if (already_added) {
3220 0 : g_string_append_printf(detail_str, ", %s=", name);
3221 : } else {
3222 0 : g_string_append_printf(detail_str, "%s=", name);
3223 0 : already_added = true;
3224 : }
3225 :
3226 0 : if (pcmk__str_any_of(name, PCMK_XA_LAST_GRANTED, "expires", NULL)) {
3227 0 : char *epoch_str = NULL;
3228 : long long time_ll;
3229 :
3230 0 : pcmk__scan_ll(value, &time_ll, 0);
3231 0 : epoch_str = pcmk__epoch2str((const time_t *) &time_ll, 0);
3232 0 : pcmk__g_strcat(detail_str, epoch_str, NULL);
3233 0 : free(epoch_str);
3234 : } else {
3235 0 : pcmk__g_strcat(detail_str, value, NULL);
3236 : }
3237 : }
3238 :
3239 0 : pcmk__g_strcat(detail_str, ")", NULL);
3240 : }
3241 :
3242 0 : if (ticket->last_granted > -1) {
3243 : /* Prior to the introduction of the details & raw arguments to this
3244 : * function, last-granted would always be added in this block. We need
3245 : * to preserve that behavior. At the same time, we also need to preserve
3246 : * the existing behavior from crm_ticket, which would include last-granted
3247 : * as part of the (...) detail string.
3248 : *
3249 : * Luckily we can check detail_str - if it's NULL, either there were no
3250 : * details, or we are preserving the previous behavior of this function.
3251 : * If it's not NULL, we are either preserving the previous behavior of
3252 : * crm_ticket or we were given details=true as an argument.
3253 : */
3254 0 : if (detail_str == NULL) {
3255 0 : char *epoch_str = pcmk__epoch2str(&(ticket->last_granted), 0);
3256 :
3257 0 : out->list_item(out, NULL, "%s\t%s%s last-granted=\"%s\"",
3258 : ticket->id,
3259 0 : (ticket->granted? "granted" : "revoked"),
3260 0 : (ticket->standby? " [standby]" : ""),
3261 : pcmk__s(epoch_str, ""));
3262 0 : free(epoch_str);
3263 : } else {
3264 0 : out->list_item(out, NULL, "%s\t%s%s %s",
3265 : ticket->id,
3266 0 : (ticket->granted? "granted" : "revoked"),
3267 0 : (ticket->standby? " [standby]" : ""),
3268 : detail_str->str);
3269 : }
3270 : } else {
3271 0 : out->list_item(out, NULL, "%s\t%s%s%s", ticket->id,
3272 0 : ticket->granted ? "granted" : "revoked",
3273 0 : ticket->standby ? " [standby]" : "",
3274 : detail_str != NULL ? detail_str->str : "");
3275 : }
3276 :
3277 0 : if (detail_str != NULL) {
3278 0 : g_string_free(detail_str, TRUE);
3279 : }
3280 :
3281 0 : return pcmk_rc_ok;
3282 : }
3283 :
3284 : PCMK__OUTPUT_ARGS("ticket", "pcmk_ticket_t *", "bool", "bool")
3285 : static int
3286 0 : ticket_xml(pcmk__output_t *out, va_list args) {
3287 0 : pcmk_ticket_t *ticket = va_arg(args, pcmk_ticket_t *);
3288 0 : bool raw G_GNUC_UNUSED = va_arg(args, int);
3289 0 : bool details G_GNUC_UNUSED = va_arg(args, int);
3290 :
3291 0 : const char *status = NULL;
3292 0 : const char *standby = pcmk__btoa(ticket->standby);
3293 :
3294 0 : xmlNodePtr node = NULL;
3295 : GHashTableIter iter;
3296 0 : const char *name = NULL;
3297 0 : const char *value = NULL;
3298 :
3299 0 : status = ticket->granted? PCMK_VALUE_GRANTED : PCMK_VALUE_REVOKED;
3300 :
3301 0 : node = pcmk__output_create_xml_node(out, PCMK_XE_TICKET,
3302 : PCMK_XA_ID, ticket->id,
3303 : PCMK_XA_STATUS, status,
3304 : PCMK_XA_STANDBY, standby,
3305 : NULL);
3306 :
3307 0 : if (ticket->last_granted > -1) {
3308 0 : char *buf = pcmk__epoch2str(&ticket->last_granted, 0);
3309 :
3310 0 : crm_xml_add(node, PCMK_XA_LAST_GRANTED, buf);
3311 0 : free(buf);
3312 : }
3313 :
3314 0 : g_hash_table_iter_init(&iter, ticket->state);
3315 0 : while (g_hash_table_iter_next(&iter, (void **) &name, (void **) &value)) {
3316 : /* PCMK_XA_LAST_GRANTED and "expires" are already added by the check
3317 : * for ticket->last_granted above.
3318 : */
3319 0 : if (pcmk__str_any_of(name, PCMK_XA_LAST_GRANTED, PCMK_XA_EXPIRES,
3320 : NULL)) {
3321 0 : continue;
3322 : }
3323 :
3324 0 : crm_xml_add(node, name, value);
3325 : }
3326 :
3327 0 : return pcmk_rc_ok;
3328 : }
3329 :
3330 : PCMK__OUTPUT_ARGS("ticket-list", "GHashTable *", "bool", "bool", "bool")
3331 : static int
3332 0 : ticket_list(pcmk__output_t *out, va_list args) {
3333 0 : GHashTable *tickets = va_arg(args, GHashTable *);
3334 0 : bool print_spacer = va_arg(args, int);
3335 0 : bool raw = va_arg(args, int);
3336 0 : bool details = va_arg(args, int);
3337 :
3338 : GHashTableIter iter;
3339 : gpointer value;
3340 :
3341 0 : if (g_hash_table_size(tickets) == 0) {
3342 0 : return pcmk_rc_no_output;
3343 : }
3344 :
3345 0 : PCMK__OUTPUT_SPACER_IF(out, print_spacer);
3346 :
3347 : /* Print section heading */
3348 0 : out->begin_list(out, NULL, NULL, "Tickets");
3349 :
3350 : /* Print each ticket */
3351 0 : g_hash_table_iter_init(&iter, tickets);
3352 0 : while (g_hash_table_iter_next(&iter, NULL, &value)) {
3353 0 : pcmk_ticket_t *ticket = (pcmk_ticket_t *) value;
3354 0 : out->message(out, "ticket", ticket, raw, details);
3355 : }
3356 :
3357 : /* Close section */
3358 0 : out->end_list(out);
3359 0 : return pcmk_rc_ok;
3360 : }
3361 :
3362 : static pcmk__message_entry_t fmt_functions[] = {
3363 : { "ban", "default", ban_text },
3364 : { "ban", "html", ban_html },
3365 : { "ban", "xml", ban_xml },
3366 : { "ban-list", "default", ban_list },
3367 : { "bundle", "default", pe__bundle_text },
3368 : { "bundle", "xml", pe__bundle_xml },
3369 : { "bundle", "html", pe__bundle_html },
3370 : { "clone", "default", pe__clone_default },
3371 : { "clone", "xml", pe__clone_xml },
3372 : { "cluster-counts", "default", cluster_counts_text },
3373 : { "cluster-counts", "html", cluster_counts_html },
3374 : { "cluster-counts", "xml", cluster_counts_xml },
3375 : { "cluster-dc", "default", cluster_dc_text },
3376 : { "cluster-dc", "html", cluster_dc_html },
3377 : { "cluster-dc", "xml", cluster_dc_xml },
3378 : { "cluster-options", "default", cluster_options_text },
3379 : { "cluster-options", "html", cluster_options_html },
3380 : { "cluster-options", "log", cluster_options_log },
3381 : { "cluster-options", "xml", cluster_options_xml },
3382 : { "cluster-summary", "default", cluster_summary },
3383 : { "cluster-summary", "html", cluster_summary_html },
3384 : { "cluster-stack", "default", cluster_stack_text },
3385 : { "cluster-stack", "html", cluster_stack_html },
3386 : { "cluster-stack", "xml", cluster_stack_xml },
3387 : { "cluster-times", "default", cluster_times_text },
3388 : { "cluster-times", "html", cluster_times_html },
3389 : { "cluster-times", "xml", cluster_times_xml },
3390 : { "failed-action", "default", failed_action_default },
3391 : { "failed-action", "xml", failed_action_xml },
3392 : { "failed-action-list", "default", failed_action_list },
3393 : { "group", "default", pe__group_default},
3394 : { "group", "xml", pe__group_xml },
3395 : { "maint-mode", "text", cluster_maint_mode_text },
3396 : { "node", "default", node_text },
3397 : { "node", "html", node_html },
3398 : { "node", "xml", node_xml },
3399 : { "node-and-op", "default", node_and_op },
3400 : { "node-and-op", "xml", node_and_op_xml },
3401 : { "node-capacity", "default", node_capacity },
3402 : { "node-capacity", "xml", node_capacity_xml },
3403 : { "node-history-list", "default", node_history_list },
3404 : { "node-list", "default", node_list_text },
3405 : { "node-list", "html", node_list_html },
3406 : { "node-list", "xml", node_list_xml },
3407 : { "node-weight", "default", node_weight },
3408 : { "node-weight", "xml", node_weight_xml },
3409 : { "node-attribute", "default", node_attribute_text },
3410 : { "node-attribute", "html", node_attribute_html },
3411 : { "node-attribute", "xml", node_attribute_xml },
3412 : { "node-attribute-list", "default", node_attribute_list },
3413 : { "node-summary", "default", node_summary },
3414 : { "op-history", "default", op_history_text },
3415 : { "op-history", "xml", op_history_xml },
3416 : { "primitive", "default", pe__resource_text },
3417 : { "primitive", "xml", pe__resource_xml },
3418 : { "primitive", "html", pe__resource_html },
3419 : { "promotion-score", "default", promotion_score },
3420 : { "promotion-score", "xml", promotion_score_xml },
3421 : { "resource-config", "default", resource_config },
3422 : { "resource-config", "text", resource_config_text },
3423 : { "resource-history", "default", resource_history_text },
3424 : { "resource-history", "xml", resource_history_xml },
3425 : { "resource-list", "default", resource_list },
3426 : { "resource-operation-list", "default", resource_operation_list },
3427 : { "resource-util", "default", resource_util },
3428 : { "resource-util", "xml", resource_util_xml },
3429 : { "ticket", "default", ticket_default },
3430 : { "ticket", "xml", ticket_xml },
3431 : { "ticket-list", "default", ticket_list },
3432 :
3433 : { NULL, NULL, NULL }
3434 : };
3435 :
3436 : void
3437 0 : pe__register_messages(pcmk__output_t *out) {
3438 0 : pcmk__register_messages(out, fmt_functions);
3439 0 : }
|