1 /*
2 * Copyright 2004-2026 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 <stdbool.h> // bool, true, false
13 #include <stdint.h> // uint32_t
14
15 #include <crm/common/output.h>
16 #include <crm/pengine/status.h>
17 #include <crm/pengine/complex.h>
18 #include <crm/pengine/internal.h>
19 #include <crm/common/xml.h>
20 #include <pe_status_private.h>
21
22 /*!
23 * \internal
24 * \brief Check whether a resource is active on multiple nodes
25 */
26 static bool
27 is_multiply_active(const pcmk_resource_t *rsc)
28 {
29 unsigned int count = 0;
30
31 if (pcmk__is_primitive(rsc)) {
32 pe__find_active_requires(rsc, &count);
33 }
34 return count > 1;
35 }
36
37 static void
38 native_priority_to_node(pcmk_resource_t *rsc, pcmk_node_t *node,
39 gboolean failed)
40 {
41 int priority = 0;
42 const bool promoted = (rsc->priv->orig_role == pcmk_role_promoted);
43
44 if ((rsc->priv->priority == 0) || failed) {
45 return;
46 }
47
48 if (promoted) {
49 // Promoted instance takes base priority + 1
50 priority = rsc->priv->priority + 1;
51
52 } else {
53 priority = rsc->priv->priority;
54 }
55
56 node->priv->priority += priority;
57 pcmk__rsc_trace(rsc, "%s now has priority %d with %s'%s' (priority: %d%s)",
58 pcmk__node_name(node), node->priv->priority,
59 (promoted? "promoted " : ""),
60 rsc->id, rsc->priv->priority, (promoted? " + 1" : ""));
61
62 /* Priority of a resource running on a guest node is added to the cluster
63 * node as well. */
64 if ((node->priv->remote != NULL)
65 && (node->priv->remote->priv->launcher != NULL)) {
66 const pcmk_resource_t *launcher = NULL;
67
68 launcher = node->priv->remote->priv->launcher;
69 for (GList *gIter = launcher->priv->active_nodes;
70 gIter != NULL; gIter = gIter->next) {
71
72 pcmk_node_t *a_node = gIter->data;
73
74 a_node->priv->priority += priority;
75 pcmk__rsc_trace(rsc,
76 "%s now has priority %d with %s'%s' "
77 "(priority: %d%s) from guest node %s",
78 pcmk__node_name(a_node), a_node->priv->priority,
79 (promoted? "promoted " : ""), rsc->id,
80 rsc->priv->priority, (promoted? " + 1" : ""),
81 pcmk__node_name(node));
82 }
83 }
84 }
85
86 void
87 native_add_running(pcmk_resource_t *rsc, pcmk_node_t *node,
88 pcmk_scheduler_t *scheduler, gboolean failed)
89 {
90 pcmk_resource_t *parent = rsc->priv->parent;
91
92 CRM_CHECK(node != NULL, return);
93
94 for (GList *gIter = rsc->priv->active_nodes;
95 gIter != NULL; gIter = gIter->next) {
96
97 pcmk_node_t *a_node = (pcmk_node_t *) gIter->data;
98
99 if (pcmk__same_node(a_node, node)) {
100 return;
101 }
102 }
103
104 if (pcmk__is_set(rsc->flags, pcmk__rsc_managed)) {
105 pcmk__rsc_trace(rsc, "Adding %s to %s", rsc->id, pcmk__node_name(node));
106 } else {
107 pcmk__rsc_trace(rsc, "Adding %s to %s (unmanaged)", rsc->id,
108 pcmk__node_name(node));
109 }
110
111 rsc->priv->active_nodes = g_list_append(rsc->priv->active_nodes, node);
112 if (pcmk__is_primitive(rsc)) {
113 node->details->running_rsc = g_list_append(node->details->running_rsc, rsc);
114 native_priority_to_node(rsc, node, failed);
115 if (node->details->maintenance) {
116 pcmk__clear_rsc_flags(rsc, pcmk__rsc_managed);
117 pcmk__set_rsc_flags(rsc, pcmk__rsc_maintenance);
118 }
119 }
120
121 if (!pcmk__is_set(rsc->flags, pcmk__rsc_managed)) {
122 pcmk_resource_t *p = parent;
123
124 pcmk__rsc_info(rsc, "resource %s isn't managed", rsc->id);
125 resource_location(rsc, node, PCMK_SCORE_INFINITY,
126 "not_managed_default", scheduler);
127
128 while(p) {
129 /* add without the additional location constraint */
130 p->priv->active_nodes = g_list_append(p->priv->active_nodes, node);
131 p = p->priv->parent;
132 }
133 return;
134 }
135
136 if (is_multiply_active(rsc)) {
137 switch (rsc->priv->multiply_active_policy) {
138 case pcmk__multiply_active_stop:
139 {
140 GHashTableIter gIter;
141 pcmk_node_t *local_node = NULL;
142
143 /* make sure it doesn't come up again */
144 g_clear_pointer(&rsc->priv->allowed_nodes,
145 g_hash_table_destroy);
146 rsc->priv->allowed_nodes =
147 pe__node_list2table(scheduler->nodes);
148 g_hash_table_iter_init(&gIter, rsc->priv->allowed_nodes);
149 while (g_hash_table_iter_next(&gIter, NULL, (void **)&local_node)) {
150 local_node->assign->score = -PCMK_SCORE_INFINITY;
151 }
152 }
153 break;
154 case pcmk__multiply_active_block:
155 pcmk__clear_rsc_flags(rsc, pcmk__rsc_managed);
156 pcmk__set_rsc_flags(rsc, pcmk__rsc_blocked);
157
158 /* If the resource belongs to a group or bundle configured with
159 * PCMK_META_MULTIPLE_ACTIVE=PCMK_VALUE_BLOCK, block the entire
160 * entity.
161 */
162 if ((pcmk__is_group(parent) || pcmk__is_bundle(parent))
163 && (parent->priv->multiply_active_policy
164 == pcmk__multiply_active_block)) {
165
166 for (GList *gIter = parent->priv->children;
167 gIter != NULL; gIter = gIter->next) {
168 pcmk_resource_t *child = gIter->data;
169
170 pcmk__clear_rsc_flags(child, pcmk__rsc_managed);
171 pcmk__set_rsc_flags(child, pcmk__rsc_blocked);
172 }
173 }
174 break;
175
176 // pcmk__multiply_active_restart, pcmk__multiply_active_unexpected
177 default:
178 /* The scheduler will do the right thing because the relevant
179 * variables and flags are set when unpacking the history.
180 */
181 break;
182 }
183 pcmk__debug("%s is active on multiple nodes including %s: %s",
184 rsc->id, pcmk__node_name(node),
185 pcmk__multiply_active_text(rsc));
186
187 } else {
188 pcmk__rsc_trace(rsc, "Resource %s is active on %s",
189 rsc->id, pcmk__node_name(node));
190 }
191
192 if (parent != NULL) {
193 native_add_running(parent, node, scheduler, FALSE);
194 }
195 }
196
197 static void
198 recursive_clear_unique(pcmk_resource_t *rsc, gpointer user_data)
199 {
200 pcmk__clear_rsc_flags(rsc, pcmk__rsc_unique);
201 pcmk__insert_meta(rsc->priv, PCMK_META_GLOBALLY_UNIQUE,
202 PCMK_VALUE_FALSE);
203 g_list_foreach(rsc->priv->children, (GFunc) recursive_clear_unique,
204 NULL);
205 }
206
207 bool
208 native_unpack(pcmk_resource_t *rsc)
209 {
210 pcmk_resource_t *parent = uber_parent(rsc);
211 const char *standard = pcmk__xe_get(rsc->priv->xml, PCMK_XA_CLASS);
212 uint32_t ra_caps = pcmk_get_ra_caps(standard);
213
214 pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id);
215
216 // Only some agent standards support unique and promotable clones
217 if (!pcmk__is_set(ra_caps, pcmk_ra_cap_unique)
218 && pcmk__is_set(rsc->flags, pcmk__rsc_unique)
219 && pcmk__is_clone(parent)) {
220
221 /* @COMPAT We should probably reject this situation as an error (as we
222 * do for promotable below) rather than warn and convert, but that would
223 * be a backward-incompatible change that we should probably do with a
224 * transform at a schema major version bump.
225 */
226 pe__force_anon(standard, parent, rsc->id, rsc->priv->scheduler);
227
228 /* Clear PCMK_META_GLOBALLY_UNIQUE on the parent and all its descendants
229 * unpacked so far (clearing the parent should make any future children
230 * unpacking correct). We have to clear this resource explicitly because
231 * it isn't hooked into the parent's children yet.
232 */
233 recursive_clear_unique(parent, NULL);
234 recursive_clear_unique(rsc, NULL);
235 }
236 if (!pcmk__is_set(ra_caps, pcmk_ra_cap_promotable)
237 && pcmk__is_set(parent->flags, pcmk__rsc_promotable)) {
238
239 pcmk__config_err("Resource %s is of type %s and therefore "
240 "cannot be used as a promotable clone resource",
241 rsc->id, standard);
242 return FALSE;
243 }
244 return TRUE;
245 }
246
247 static bool
248 rsc_is_on_node(pcmk_resource_t *rsc, const pcmk_node_t *node, int flags)
249 {
250 pcmk__rsc_trace(rsc, "Checking whether %s is on %s",
251 rsc->id, pcmk__node_name(node));
252
253 if (pcmk__is_set(flags, pcmk_rsc_match_current_node)
254 && (rsc->priv->active_nodes != NULL)) {
255
256 for (GList *iter = rsc->priv->active_nodes;
257 iter != NULL; iter = iter->next) {
258
259 if (pcmk__same_node((pcmk_node_t *) iter->data, node)) {
260 return true;
261 }
262 }
263
264 } else if (!pcmk__is_set(flags, pcmk_rsc_match_current_node)
265 && (rsc->priv->assigned_node != NULL)
266 && pcmk__same_node(rsc->priv->assigned_node, node)) {
267 return true;
268 }
269 return false;
270 }
271
272 pcmk_resource_t *
273 native_find_rsc(pcmk_resource_t *rsc, const char *id,
274 const pcmk_node_t *on_node, uint32_t flags)
275 {
276 bool match = false;
277 pcmk_resource_t *result = NULL;
278
279 CRM_CHECK(id && rsc && rsc->id, return NULL);
280
281 if (pcmk__is_set(flags, pcmk_rsc_match_clone_only)) {
282 const char *rid = pcmk__xe_id(rsc->priv->xml);
283
284 if (!pcmk__is_clone(pe__const_top_resource(rsc, false))) {
285 match = false;
286
287 } else if (!strcmp(id, rsc->id) || pcmk__str_eq(id, rid, pcmk__str_none)) {
288 match = true;
289 }
290
291 } else if (!strcmp(id, rsc->id)) {
292 match = true;
293
294 } else if (pcmk__is_set(flags, pcmk_rsc_match_history)
295 && pcmk__str_eq(rsc->priv->history_id, id, pcmk__str_none)) {
296 match = true;
297
298 } else if (pcmk__is_set(flags, pcmk_rsc_match_basename)
299 || (pcmk__is_set(flags, pcmk_rsc_match_anon_basename)
300 && !pcmk__is_set(rsc->flags, pcmk__rsc_unique))) {
301 match = pe_base_name_eq(rsc, id);
302 }
303
304 if (match && on_node) {
305 if (!rsc_is_on_node(rsc, on_node, flags)) {
306 match = false;
307 }
308 }
309
310 if (match) {
311 return rsc;
312 }
313
314 for (GList *gIter = rsc->priv->children;
315 gIter != NULL; gIter = gIter->next) {
316
317 pcmk_resource_t *child = (pcmk_resource_t *) gIter->data;
318
319 result = rsc->priv->fns->find_rsc(child, id, on_node, flags);
320 if (result) {
321 return result;
322 }
323 }
324 return NULL;
325 }
326
327 bool
328 native_active(const pcmk_resource_t *rsc, bool all)
329 {
330 for (GList *gIter = rsc->priv->active_nodes;
331 gIter != NULL; gIter = gIter->next) {
332
333 pcmk_node_t *a_node = (pcmk_node_t *) gIter->data;
334
335 if (a_node->details->unclean) {
336 pcmk__rsc_trace(rsc, "Resource %s: %s is unclean",
337 rsc->id, pcmk__node_name(a_node));
338 return TRUE;
339 } else if (!a_node->details->online
340 && pcmk__is_set(rsc->flags, pcmk__rsc_managed)) {
341 pcmk__rsc_trace(rsc, "Resource %s: %s is offline",
342 rsc->id, pcmk__node_name(a_node));
343 } else {
344 pcmk__rsc_trace(rsc, "Resource %s active on %s",
345 rsc->id, pcmk__node_name(a_node));
346 return TRUE;
347 }
348 }
349 return FALSE;
350 }
351
352 struct print_data_s {
353 long options;
354 void *print_data;
355 };
356
357 static const char *
358 native_pending_state(const pcmk_resource_t *rsc)
359 {
360 const char *pending_state = NULL;
361
362 if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_START,
363 pcmk__str_none)) {
364 pending_state = "Starting";
365
366 } else if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_STOP,
367 pcmk__str_none)) {
368 pending_state = "Stopping";
369
370 } else if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_MIGRATE_TO,
371 pcmk__str_none)) {
372 pending_state = "Migrating";
373
374 } else if (pcmk__str_eq(rsc->priv->pending_action,
375 PCMK_ACTION_MIGRATE_FROM, pcmk__str_none)) {
376 /* Work might be done in here. */
377 pending_state = "Migrating";
378
379 } else if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_PROMOTE,
380 pcmk__str_none)) {
381 pending_state = "Promoting";
382
383 } else if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_DEMOTE,
384 pcmk__str_none)) {
385 pending_state = "Demoting";
386 }
387
388 return pending_state;
389 }
390
391 static const char *
392 native_pending_action(const pcmk_resource_t *rsc)
393 {
394 const char *pending_action = NULL;
395
396 if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_MONITOR,
397 pcmk__str_none)) {
398 pending_action = "Monitoring";
399
400 /* Pending probes are not printed, even if pending
401 * operations are requested. If someone ever requests that
402 * behavior, uncomment this and the corresponding part of
403 * unpack.c:unpack_rsc_op().
404 */
405 #if 0
406 } else if (pcmk__str_eq(rsc->private->pending_action, "probe",
407 pcmk__str_none)) {
408 pending_action = "Checking";
409 #endif
410 }
411
412 return pending_action;
413 }
414
415 static enum rsc_role_e
416 native_displayable_role(const pcmk_resource_t *rsc)
417 {
418 enum rsc_role_e role = rsc->priv->orig_role;
419
420 if ((role == pcmk_role_started)
421 && pcmk__is_set(pe__const_top_resource(rsc, false)->flags,
422 pcmk__rsc_promotable)) {
423
424 role = pcmk_role_unpromoted;
425 }
426 return role;
427 }
428
429 static const char *
430 native_displayable_state(const pcmk_resource_t *rsc, bool print_pending)
431 {
432 const char *rsc_state = NULL;
433
434 if (print_pending) {
435 rsc_state = native_pending_state(rsc);
436 }
437 if (rsc_state == NULL) {
438 rsc_state = pcmk_role_text(native_displayable_role(rsc));
439 }
440 return rsc_state;
441 }
442
443 // Append a flag to resource description string's flags list
444 static bool
445 add_output_flag(GString *s, const char *flag_desc, bool have_flags)
446 {
447 g_string_append(s, (have_flags? ", " : " ("));
448 g_string_append(s, flag_desc);
449 return true;
450 }
451
452 // Append a node name to resource description string's node list
453 static bool
454 add_output_node(GString *s, const char *node, bool have_nodes)
455 {
456 g_string_append(s, (have_nodes? " " : " [ "));
457 g_string_append(s, node);
458 return true;
459 }
460
461 /*!
462 * \internal
463 * \brief Create a string description of a resource
464 *
465 * \param[in] rsc Resource to describe
466 * \param[in] name Desired identifier for the resource
467 * \param[in] node If not NULL, node that resource is "on"
468 * \param[in] show_opts Bitmask of pcmk_show_opt_e.
469 * \param[in] target_role Resource's target role
470 * \param[in] show_nodes Whether to display nodes when multiply active
471 *
472 * \return Newly allocated string description of resource
473 * \note Caller must free the result with g_free().
474 */
475 gchar *
476 pcmk__native_output_string(const pcmk_resource_t *rsc, const char *name,
477 const pcmk_node_t *node, uint32_t show_opts,
478 const char *target_role, bool show_nodes)
479 {
480 const char *class = pcmk__xe_get(rsc->priv->xml, PCMK_XA_CLASS);
481 const char *provider = NULL;
482 const char *kind = pcmk__xe_get(rsc->priv->xml, PCMK_XA_TYPE);
483 GString *outstr = NULL;
484 bool have_flags = false;
485
486 if (!pcmk__is_primitive(rsc)) {
487 return NULL;
488 }
489
490 CRM_CHECK(name != NULL, name = "unknown");
491 CRM_CHECK(kind != NULL, kind = "unknown");
492 CRM_CHECK(class != NULL, class = "unknown");
493
494 if (pcmk__is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider)) {
495 provider = pcmk__xe_get(rsc->priv->xml, PCMK_XA_PROVIDER);
496 }
497
498 if ((node == NULL) && (rsc->priv->lock_node != NULL)) {
499 node = rsc->priv->lock_node;
500 }
501 if (pcmk__any_flags_set(show_opts, pcmk_show_rsc_only)
502 || pcmk__list_of_multiple(rsc->priv->active_nodes)) {
503 node = NULL;
504 }
505
506 outstr = g_string_sized_new(128);
507
508 // Resource name and agent
509 pcmk__g_strcat(outstr,
510 name, "\t(", class, ((provider == NULL)? "" : ":"),
511 pcmk__s(provider, ""), ":", kind, "):\t", NULL);
512
513 // State on node
514 if (pcmk__is_set(rsc->flags, pcmk__rsc_removed)) {
515 /* @COMPAT "ORPHANED" is deprecated since 3.0.2. Replace with "REMOVED"
516 * at a compatibility break.
517 */
518 g_string_append(outstr, " ORPHANED");
519 }
520 if (pcmk__is_set(rsc->flags, pcmk__rsc_failed)) {
521 enum rsc_role_e role = native_displayable_role(rsc);
522
523 g_string_append(outstr, " FAILED");
524 if (role > pcmk_role_unpromoted) {
525 pcmk__add_word(&outstr, 0, pcmk_role_text(role));
526 }
527 } else {
528 const bool show_pending = pcmk__is_set(show_opts, pcmk_show_pending);
529
530 pcmk__add_word(&outstr, 0, native_displayable_state(rsc, show_pending));
531 }
532 if (node) {
533 pcmk__add_word(&outstr, 0, pcmk__node_name(node));
534 }
535
536 // Failed probe operation
537 if (native_displayable_role(rsc) == pcmk_role_stopped) {
538 xmlNode *probe_op = pe__failed_probe_for_rsc(rsc,
539 node ? node->priv->name : NULL);
540 if (probe_op != NULL) {
541 int rc;
542
543 pcmk__scan_min_int(pcmk__xe_get(probe_op, PCMK__XA_RC_CODE), &rc,
544 0);
545 pcmk__g_strcat(outstr, " (", crm_exit_str(rc), ") ", NULL);
546 }
547 }
548
549 // Flags, as: (<flag> [...])
550 if (node && !(node->details->online) && node->details->unclean) {
551 have_flags = add_output_flag(outstr, "UNCLEAN", have_flags);
552 }
553 if ((node != NULL) && pcmk__same_node(node, rsc->priv->lock_node)) {
554 have_flags = add_output_flag(outstr, "LOCKED", have_flags);
555 }
556 if (pcmk__is_set(show_opts, pcmk_show_pending)) {
557 const char *pending_action = native_pending_action(rsc);
558
559 if (pending_action != NULL) {
560 have_flags = add_output_flag(outstr, pending_action, have_flags);
561 }
562 }
563 if (target_role != NULL) {
564 switch (pcmk_parse_role(target_role)) {
565 case pcmk_role_unknown:
566 pcmk__config_err("Invalid " PCMK_META_TARGET_ROLE
567 " %s for resource %s", target_role, rsc->id);
568 break;
569
570 case pcmk_role_stopped:
571 have_flags = add_output_flag(outstr, "disabled", have_flags);
572 break;
573
574 case pcmk_role_unpromoted:
575 if (pcmk__is_set(pe__const_top_resource(rsc, false)->flags,
576 pcmk__rsc_promotable)) {
577 have_flags = add_output_flag(outstr,
578 PCMK_META_TARGET_ROLE ":",
579 have_flags);
580 g_string_append(outstr, target_role);
581 }
582 break;
583
584 default:
585 /* Only show target role if it limits our abilities (i.e. ignore
586 * Started, as it is the default anyways, and doesn't prevent
587 * the resource from becoming promoted).
588 */
589 break;
590 }
591 }
592
593 // Blocked or maintenance implies unmanaged
594 if (pcmk__any_flags_set(rsc->flags,
595 pcmk__rsc_blocked|pcmk__rsc_maintenance)) {
596 if (pcmk__is_set(rsc->flags, pcmk__rsc_blocked)) {
597 have_flags = add_output_flag(outstr, "blocked", have_flags);
598
599 } else if (pcmk__is_set(rsc->flags, pcmk__rsc_maintenance)) {
600 have_flags = add_output_flag(outstr, "maintenance", have_flags);
601 }
602 } else if (!pcmk__is_set(rsc->flags, pcmk__rsc_managed)) {
603 have_flags = add_output_flag(outstr, "unmanaged", have_flags);
604 }
605
606 if (pcmk__is_set(rsc->flags, pcmk__rsc_ignore_failure)) {
607 have_flags = add_output_flag(outstr, "failure ignored", have_flags);
608 }
609
610
611 if (have_flags) {
612 g_string_append_c(outstr, ')');
613 }
614
615 // User-supplied description
616 if (pcmk__any_flags_set(show_opts, pcmk_show_rsc_only|pcmk_show_description)
617 || pcmk__list_of_multiple(rsc->priv->active_nodes)) {
618 const char *desc = pcmk__xe_get(rsc->priv->xml, PCMK_XA_DESCRIPTION);
619
620 if (desc) {
621 g_string_append(outstr, " (");
622 g_string_append(outstr, desc);
623 g_string_append(outstr, ")");
624
625 }
626 }
627
628 if (show_nodes && !pcmk__is_set(show_opts, pcmk_show_rsc_only)
629 && pcmk__list_of_multiple(rsc->priv->active_nodes)) {
630 bool have_nodes = false;
631
632 for (GList *iter = rsc->priv->active_nodes;
633 iter != NULL; iter = iter->next) {
634
635 pcmk_node_t *n = (pcmk_node_t *) iter->data;
636
637 have_nodes = add_output_node(outstr, n->priv->name, have_nodes);
638 }
639 if (have_nodes) {
640 g_string_append(outstr, " ]");
641 }
642 }
643
644 return g_string_free(outstr, FALSE);
645 }
646
647 int
648 pe__common_output_html(pcmk__output_t *out, const pcmk_resource_t *rsc,
649 const char *name, const pcmk_node_t *node,
650 uint32_t show_opts)
651 {
652 const char *kind = pcmk__xe_get(rsc->priv->xml, PCMK_XA_TYPE);
653 const char *target_role = NULL;
654 const char *cl = NULL;
655
656 xmlNode *child = NULL;
657 gchar *content = NULL;
658
659 pcmk__assert((kind != NULL) && pcmk__is_primitive(rsc));
660
661 if (pcmk__is_true(g_hash_table_lookup(rsc->priv->meta,
662 PCMK__META_INTERNAL_RSC))
663 && !pcmk__is_set(show_opts, pcmk_show_implicit_rscs)) {
664
665 pcmk__trace("skipping print of internal resource %s", rsc->id);
666 return pcmk_rc_no_output;
667 }
668 target_role = g_hash_table_lookup(rsc->priv->meta,
669 PCMK_META_TARGET_ROLE);
670
671 if (!pcmk__is_set(rsc->flags, pcmk__rsc_managed)) {
672 cl = PCMK__VALUE_RSC_MANAGED;
673
674 } else if (pcmk__is_set(rsc->flags, pcmk__rsc_failed)) {
675 cl = PCMK__VALUE_RSC_FAILED;
676
677 } else if (pcmk__is_primitive(rsc)
678 && (rsc->priv->active_nodes == NULL)) {
679 cl = PCMK__VALUE_RSC_FAILED;
680
681 } else if (pcmk__list_of_multiple(rsc->priv->active_nodes)) {
682 cl = PCMK__VALUE_RSC_MULTIPLE;
683
684 } else if (pcmk__is_set(rsc->flags, pcmk__rsc_ignore_failure)) {
685 cl = PCMK__VALUE_RSC_FAILURE_IGNORED;
686
687 } else {
688 cl = PCMK__VALUE_RSC_OK;
689 }
690
691 child = pcmk__output_create_html_node(out, "li", NULL, NULL, NULL);
692 child = pcmk__html_create(child, PCMK__XE_SPAN, NULL, cl);
693 content = pcmk__native_output_string(rsc, name, node, show_opts,
694 target_role, true);
695 pcmk__xe_set_content(child, "%s", content);
696 g_free(content);
697
698 return pcmk_rc_ok;
699 }
700
701 int
702 pe__common_output_text(pcmk__output_t *out, const pcmk_resource_t *rsc,
703 const char *name, const pcmk_node_t *node,
704 uint32_t show_opts)
705 {
706 const char *target_role = NULL;
707
708 pcmk__assert(pcmk__is_primitive(rsc));
709
710 if (pcmk__is_true(g_hash_table_lookup(rsc->priv->meta,
711 PCMK__META_INTERNAL_RSC))
712 && !pcmk__is_set(show_opts, pcmk_show_implicit_rscs)) {
713
714 pcmk__trace("skipping print of internal resource %s", rsc->id);
715 return pcmk_rc_no_output;
716 }
717 target_role = g_hash_table_lookup(rsc->priv->meta,
718 PCMK_META_TARGET_ROLE);
719
720 {
721 gchar *s = pcmk__native_output_string(rsc, name, node, show_opts,
722 target_role, true);
723
724 out->list_item(out, NULL, "%s", s);
725 g_free(s);
726 }
727
728 return pcmk_rc_ok;
729 }
730
731 PCMK__OUTPUT_ARGS("primitive", "uint32_t", "pcmk_resource_t *", "GList *",
732 "GList *")
733 int
734 pe__resource_xml(pcmk__output_t *out, va_list args)
735 {
736 uint32_t show_opts = va_arg(args, uint32_t);
737 pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
738 GList *only_node G_GNUC_UNUSED = va_arg(args, GList *);
739 GList *only_rsc = va_arg(args, GList *);
740
741 int rc = pcmk_rc_no_output;
742 const bool print_pending = pcmk__is_set(show_opts, pcmk_show_pending);
743 const char *class = pcmk__xe_get(rsc->priv->xml, PCMK_XA_CLASS);
744 const char *prov = pcmk__xe_get(rsc->priv->xml, PCMK_XA_PROVIDER);
745
746 char *ra_name = NULL;
747 const char *rsc_state = native_displayable_state(rsc, print_pending);
748 const char *target_role = NULL;
749 const char *active = pcmk__btoa(rsc->priv->fns->active(rsc, true));
750 const char *removed = pcmk__flag_text(rsc->flags, pcmk__rsc_removed);
751 const char *blocked = pcmk__flag_text(rsc->flags, pcmk__rsc_blocked);
752 const char *maintenance = pcmk__flag_text(rsc->flags,
753 pcmk__rsc_maintenance);
754 const char *managed = pcmk__flag_text(rsc->flags, pcmk__rsc_managed);
755 const char *failed = pcmk__flag_text(rsc->flags, pcmk__rsc_failed);
756 const char *ignored = pcmk__flag_text(rsc->flags, pcmk__rsc_ignore_failure);
757 char *nodes_running_on = NULL;
758 const char *pending = print_pending? native_pending_action(rsc) : NULL;
759 const char *locked_to = NULL;
760 const char *desc = pe__resource_description(rsc, show_opts);
761
762 pcmk__assert(pcmk__is_primitive(rsc));
763
764 if (rsc->priv->fns->is_filtered(rsc, only_rsc, true)) {
765 return pcmk_rc_no_output;
766 }
767
768 // Resource information
769 ra_name = pcmk__assert_asprintf("%s%s%s:%s", class,
770 ((prov == NULL)? "" : ":"),
771 ((prov == NULL)? "" : prov),
772 pcmk__xe_get(rsc->priv->xml, PCMK_XA_TYPE));
773
774 target_role = g_hash_table_lookup(rsc->priv->meta,
775 PCMK_META_TARGET_ROLE);
776
777 nodes_running_on = pcmk__itoa(g_list_length(rsc->priv->active_nodes));
778
779 if (rsc->priv->lock_node != NULL) {
780 locked_to = rsc->priv->lock_node->priv->name;
781 }
782
783 // @COMPAT PCMK_XA_ORPHANED is deprecated since 3.0.2
784 rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_RESOURCE,
785 PCMK_XA_ID, rsc_printable_id(rsc),
786 PCMK_XA_RESOURCE_AGENT, ra_name,
787 PCMK_XA_ROLE, rsc_state,
788 PCMK_XA_TARGET_ROLE, target_role,
789 PCMK_XA_ACTIVE, active,
790 PCMK_XA_ORPHANED, removed,
791 PCMK_XA_REMOVED, removed,
792 PCMK_XA_BLOCKED, blocked,
793 PCMK_XA_MAINTENANCE, maintenance,
794 PCMK_XA_MANAGED, managed,
795 PCMK_XA_FAILED, failed,
796 PCMK_XA_FAILURE_IGNORED, ignored,
797 PCMK_XA_NODES_RUNNING_ON, nodes_running_on,
798 PCMK_XA_PENDING, pending,
799 PCMK_XA_LOCKED_TO, locked_to,
800 PCMK_XA_DESCRIPTION, desc,
801 NULL);
802 free(ra_name);
803 free(nodes_running_on);
804
805 pcmk__assert(rc == pcmk_rc_ok);
806
807 for (GList *gIter = rsc->priv->active_nodes;
808 gIter != NULL; gIter = gIter->next) {
809
810 pcmk_node_t *node = (pcmk_node_t *) gIter->data;
811 const char *cached = pcmk__btoa(node->details->online);
812
813 rc = pe__name_and_nvpairs_xml(out, false, PCMK_XE_NODE,
814 PCMK_XA_NAME, node->priv->name,
815 PCMK_XA_ID, node->priv->id,
816 PCMK_XA_CACHED, cached,
817 NULL);
818 pcmk__assert(rc == pcmk_rc_ok);
819 }
820
821 pcmk__output_xml_pop_parent(out);
822 return rc;
823 }
824
825 PCMK__OUTPUT_ARGS("primitive", "uint32_t", "pcmk_resource_t *", "GList *",
826 "GList *")
827 int
828 pe__resource_html(pcmk__output_t *out, va_list args)
829 {
830 uint32_t show_opts = va_arg(args, uint32_t);
831 pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
832 GList *only_node G_GNUC_UNUSED = va_arg(args, GList *);
833 GList *only_rsc = va_arg(args, GList *);
834
835 const pcmk_node_t *node = pcmk__current_node(rsc);
836
837 if (rsc->priv->fns->is_filtered(rsc, only_rsc, true)) {
838 return pcmk_rc_no_output;
839 }
840
841 pcmk__assert(pcmk__is_primitive(rsc));
842
843 if (node == NULL) {
844 // This is set only if a non-probe action is pending on this node
845 node = rsc->priv->pending_node;
846 }
847 return pe__common_output_html(out, rsc, rsc_printable_id(rsc), node, show_opts);
848 }
849
850 PCMK__OUTPUT_ARGS("primitive", "uint32_t", "pcmk_resource_t *", "GList *",
851 "GList *")
852 int
853 pe__resource_text(pcmk__output_t *out, va_list args)
854 {
855 uint32_t show_opts = va_arg(args, uint32_t);
856 pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
857 GList *only_node G_GNUC_UNUSED = va_arg(args, GList *);
858 GList *only_rsc = va_arg(args, GList *);
859
860 const pcmk_node_t *node = pcmk__current_node(rsc);
861
862 pcmk__assert(pcmk__is_primitive(rsc));
863
864 if (rsc->priv->fns->is_filtered(rsc, only_rsc, true)) {
865 return pcmk_rc_no_output;
866 }
867
868 if (node == NULL) {
869 // This is set only if a non-probe action is pending on this node
870 node = rsc->priv->pending_node;
871 }
872 return pe__common_output_text(out, rsc, rsc_printable_id(rsc), node, show_opts);
873 }
874
875 void
876 native_free(pcmk_resource_t * rsc)
877 {
878 pcmk__rsc_trace(rsc, "Freeing resource action list (not the data)");
879 common_free(rsc);
880 }
881
882 enum rsc_role_e
883 native_resource_state(const pcmk_resource_t *rsc, bool current)
884 {
885 enum rsc_role_e role = rsc->priv->next_role;
886
887 if (current) {
888 role = rsc->priv->orig_role;
889 }
890 pcmk__rsc_trace(rsc, "%s state: %s", rsc->id, pcmk_role_text(role));
891 return role;
892 }
893
894 /*!
895 * \internal
896 * \brief List nodes where a resource (or any of its children) is
897 *
898 * \param[in] rsc Resource to check
899 * \param[out] list List to add result to
900 * \param[in] target Which resource conditions to target (group of
901 * enum pcmk__rsc_node flags)
902 *
903 * \return If list contains only one node, that node, or NULL otherwise
904 */
905 pcmk_node_t *
906 native_location(const pcmk_resource_t *rsc, GList **list, uint32_t target)
907 {
908 pcmk_node_t *one = NULL;
909 GList *result = NULL;
910
911 if (rsc->priv->children != NULL) {
912
913 for (GList *gIter = rsc->priv->children;
914 gIter != NULL; gIter = gIter->next) {
915
916 pcmk_resource_t *child = (pcmk_resource_t *) gIter->data;
917
918 child->priv->fns->location(child, &result, target);
919 }
920
921 } else {
922 if (pcmk__is_set(target, pcmk__rsc_node_current)) {
923 result = g_list_copy(rsc->priv->active_nodes);
924 }
925 if (pcmk__is_set(target, pcmk__rsc_node_pending)
926 && (rsc->priv->pending_node != NULL)
927 && !pe_find_node_id(result, rsc->priv->pending_node->priv->id)) {
928 result = g_list_append(result, (gpointer) rsc->priv->pending_node);
929 }
930 if (pcmk__is_set(target, pcmk__rsc_node_assigned)
931 && (rsc->priv->assigned_node != NULL)) {
932 result = g_list_append(result, rsc->priv->assigned_node);
933 }
934 }
935
936 if (result && (result->next == NULL)) {
937 one = result->data;
938 }
939
940 if (list) {
941 GList *gIter = result;
942
943 for (; gIter != NULL; gIter = gIter->next) {
944 pcmk_node_t *node = (pcmk_node_t *) gIter->data;
945
946 if ((*list == NULL)
947 || (pe_find_node_id(*list, node->priv->id) == NULL)) {
948 *list = g_list_append(*list, node);
949 }
950 }
951 }
952
953 g_list_free(result);
954 return one;
955 }
956
957 static void
958 get_rscs_brief(GList *rsc_list, GHashTable * rsc_table, GHashTable * active_table)
959 {
960 GList *gIter = rsc_list;
961
962 for (; gIter != NULL; gIter = gIter->next) {
963 pcmk_resource_t *rsc = (pcmk_resource_t *) gIter->data;
964
965 const char *class = pcmk__xe_get(rsc->priv->xml, PCMK_XA_CLASS);
966 const char *kind = pcmk__xe_get(rsc->priv->xml, PCMK_XA_TYPE);
967
968 GString *buffer = NULL;
969
970 int *rsc_counter = NULL;
971 int *active_counter = NULL;
972
973 if (!pcmk__is_primitive(rsc)) {
974 continue;
975 }
976
977 buffer = g_string_sized_new(128);
978
979 g_string_append(buffer, class);
980 if (pcmk__is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider)) {
981 const char *prov = pcmk__xe_get(rsc->priv->xml, PCMK_XA_PROVIDER);
982
983 if (prov != NULL) {
984 pcmk__g_strcat(buffer, ":", prov, NULL);
985 }
986 }
987 pcmk__g_strcat(buffer, ":", kind, NULL);
988
989 if (rsc_table) {
990 rsc_counter = g_hash_table_lookup(rsc_table, buffer->str);
991 if (rsc_counter == NULL) {
992 rsc_counter = pcmk__assert_alloc(1, sizeof(int));
993 *rsc_counter = 0;
994 g_hash_table_insert(rsc_table, strdup(buffer->str),
995 rsc_counter);
996 }
997 (*rsc_counter)++;
998 }
999
1000 if (active_table) {
1001 for (GList *gIter2 = rsc->priv->active_nodes;
1002 gIter2 != NULL; gIter2 = gIter2->next) {
1003
1004 pcmk_node_t *node = (pcmk_node_t *) gIter2->data;
1005 GHashTable *node_table = NULL;
1006
1007 if (!node->details->unclean && !node->details->online
1008 && pcmk__is_set(rsc->flags, pcmk__rsc_managed)) {
1009 continue;
1010 }
1011
1012 node_table = g_hash_table_lookup(active_table,
1013 node->priv->name);
1014 if (node_table == NULL) {
1015 node_table = pcmk__strkey_table(free, free);
1016 g_hash_table_insert(active_table,
1017 strdup(node->priv->name),
1018 node_table);
1019 }
1020
1021 active_counter = g_hash_table_lookup(node_table, buffer->str);
1022 if (active_counter == NULL) {
1023 active_counter = pcmk__assert_alloc(1, sizeof(int));
1024 *active_counter = 0;
1025 g_hash_table_insert(node_table, strdup(buffer->str),
1026 active_counter);
1027 }
1028 (*active_counter)++;
1029 }
1030 }
1031
1032 g_string_free(buffer, TRUE);
1033 }
1034 }
1035
1036 static void
1037 destroy_node_table(gpointer data)
1038 {
|
CID (unavailable; MK=3848b59c5d425ba1f72e484370ffe62f) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS): |
|
(1) Event assign_union_field: |
The union field "in" of "_pp" is written. |
|
(2) Event inconsistent_union_field_access: |
In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in". |
1039 g_clear_pointer(&data, g_hash_table_destroy);
1040 }
1041
1042 int
1043 pe__rscs_brief_output(pcmk__output_t *out, GList *rsc_list, uint32_t show_opts)
1044 {
1045 GHashTable *rsc_table = pcmk__strkey_table(free, free);
1046 GHashTable *active_table = pcmk__strkey_table(free, destroy_node_table);
1047 GList *sorted_rscs;
1048 int rc = pcmk_rc_no_output;
1049
1050 get_rscs_brief(rsc_list, rsc_table, active_table);
1051
1052 /* Make a list of the rsc_table keys so that it can be sorted. This is to make sure
1053 * output order stays consistent between systems.
1054 */
1055 sorted_rscs = g_hash_table_get_keys(rsc_table);
1056 sorted_rscs = g_list_sort(sorted_rscs, (GCompareFunc) strcmp);
1057
1058 for (GList *gIter = sorted_rscs; gIter; gIter = gIter->next) {
1059 char *type = (char *) gIter->data;
1060 int *rsc_counter = g_hash_table_lookup(rsc_table, type);
1061
1062 GList *sorted_nodes = NULL;
1063 int active_counter_all = 0;
1064
1065 /* Also make a list of the active_table keys so it can be sorted. If there's
1066 * more than one instance of a type of resource running, we need the nodes to
1067 * be sorted to make sure output order stays consistent between systems.
1068 */
1069 sorted_nodes = g_hash_table_get_keys(active_table);
1070 sorted_nodes = g_list_sort(sorted_nodes, (GCompareFunc) pcmk__numeric_strcasecmp);
1071
1072 for (GList *gIter2 = sorted_nodes; gIter2; gIter2 = gIter2->next) {
1073 char *node_name = (char *) gIter2->data;
1074 GHashTable *node_table = g_hash_table_lookup(active_table, node_name);
1075 int *active_counter = NULL;
1076
1077 if (node_table == NULL) {
1078 continue;
1079 }
1080
1081 active_counter = g_hash_table_lookup(node_table, type);
1082
1083 if (active_counter == NULL || *active_counter == 0) {
1084 continue;
1085
1086 } else {
1087 active_counter_all += *active_counter;
1088 }
1089
1090 if (pcmk__is_set(show_opts, pcmk_show_rsc_only)) {
1091 node_name = NULL;
1092 }
1093
1094 if (pcmk__is_set(show_opts, pcmk_show_inactive_rscs)) {
1095 out->list_item(out, NULL, "%d/%d\t(%s):\tActive %s",
1096 *active_counter,
1097 rsc_counter ? *rsc_counter : 0, type,
1098 (*active_counter > 0) && node_name ? node_name : "");
1099 } else {
1100 out->list_item(out, NULL, "%d\t(%s):\tActive %s",
1101 *active_counter, type,
1102 (*active_counter > 0) && node_name ? node_name : "");
1103 }
1104
1105 rc = pcmk_rc_ok;
1106 }
1107
1108 if (pcmk__is_set(show_opts, pcmk_show_inactive_rscs)
1109 && (active_counter_all == 0)) {
1110
1111 out->list_item(out, NULL, "%d/%d\t(%s):\tActive",
1112 active_counter_all,
1113 rsc_counter ? *rsc_counter : 0, type);
1114 rc = pcmk_rc_ok;
1115 }
1116
1117 g_list_free(sorted_nodes);
1118 }
1119
1120 g_clear_pointer(&rsc_table, g_hash_table_destroy);
1121 g_clear_pointer(&active_table, g_hash_table_destroy);
1122 g_clear_pointer(&sorted_rscs, g_list_free);
1123 return rc;
1124 }
1125
1126 bool
1127 pe__native_is_filtered(const pcmk_resource_t *rsc, const GList *only_rsc,
1128 bool check_parent)
1129 {
1130 if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc,
1131 pcmk__str_star_matches)
1132 || pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches)) {
1133
1134 return false;
1135 }
1136
1137 if (check_parent && (rsc->priv->parent != NULL)) {
1138 const pcmk_resource_t *up = pe__const_top_resource(rsc, true);
1139
1140 return up->priv->fns->is_filtered(up, only_rsc, false);
1141 }
1142
1143 return true;
1144 }
1145
1146 /*!
1147 * \internal
1148 * \brief Get maximum primitive resource instances per node
1149 *
1150 * \param[in] rsc Primitive resource to check
1151 *
1152 * \return Maximum number of \p rsc instances that can be active on one node
1153 */
1154 unsigned int
1155 pe__primitive_max_per_node(const pcmk_resource_t *rsc)
1156 {
1157 pcmk__assert(pcmk__is_primitive(rsc));
1158 return 1U;
1159 }
1160