Line data Source code
1 : /*
2 : * Copyright 2004-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/output.h>
15 : #include <crm/pengine/rules.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 : #ifdef PCMK__COMPAT_2_0
23 : #define PROVIDER_SEP "::"
24 : #else
25 : #define PROVIDER_SEP ":"
26 : #endif
27 :
28 : /*!
29 : * \internal
30 : * \brief Check whether a resource is active on multiple nodes
31 : */
32 : static bool
33 0 : is_multiply_active(const pcmk_resource_t *rsc)
34 : {
35 0 : unsigned int count = 0;
36 :
37 0 : if (rsc->variant == pcmk_rsc_variant_primitive) {
38 0 : pe__find_active_requires(rsc, &count);
39 : }
40 0 : return count > 1;
41 : }
42 :
43 : static void
44 0 : native_priority_to_node(pcmk_resource_t *rsc, pcmk_node_t *node,
45 : gboolean failed)
46 : {
47 0 : int priority = 0;
48 :
49 0 : if ((rsc->priority == 0) || (failed == TRUE)) {
50 0 : return;
51 : }
52 :
53 0 : if (rsc->role == pcmk_role_promoted) {
54 : // Promoted instance takes base priority + 1
55 0 : priority = rsc->priority + 1;
56 :
57 : } else {
58 0 : priority = rsc->priority;
59 : }
60 :
61 0 : node->details->priority += priority;
62 0 : pcmk__rsc_trace(rsc, "%s now has priority %d with %s'%s' (priority: %d%s)",
63 : pcmk__node_name(node), node->details->priority,
64 : (rsc->role == pcmk_role_promoted)? "promoted " : "",
65 : rsc->id, rsc->priority,
66 : (rsc->role == pcmk_role_promoted)? " + 1" : "");
67 :
68 : /* Priority of a resource running on a guest node is added to the cluster
69 : * node as well. */
70 0 : if (node->details->remote_rsc
71 0 : && node->details->remote_rsc->container) {
72 0 : GList *gIter = node->details->remote_rsc->container->running_on;
73 :
74 0 : for (; gIter != NULL; gIter = gIter->next) {
75 0 : pcmk_node_t *a_node = gIter->data;
76 :
77 0 : a_node->details->priority += priority;
78 0 : pcmk__rsc_trace(rsc,
79 : "%s now has priority %d with %s'%s' "
80 : "(priority: %d%s) from guest node %s",
81 : pcmk__node_name(a_node), a_node->details->priority,
82 : (rsc->role == pcmk_role_promoted)? "promoted " : "",
83 : rsc->id, rsc->priority,
84 : (rsc->role == pcmk_role_promoted)? " + 1" : "",
85 : pcmk__node_name(node));
86 : }
87 : }
88 : }
89 :
90 : void
91 0 : native_add_running(pcmk_resource_t *rsc, pcmk_node_t *node,
92 : pcmk_scheduler_t *scheduler, gboolean failed)
93 : {
94 0 : GList *gIter = rsc->running_on;
95 :
96 0 : CRM_CHECK(node != NULL, return);
97 0 : for (; gIter != NULL; gIter = gIter->next) {
98 0 : pcmk_node_t *a_node = (pcmk_node_t *) gIter->data;
99 :
100 0 : CRM_CHECK(a_node != NULL, return);
101 0 : if (pcmk__str_eq(a_node->details->id, node->details->id, pcmk__str_casei)) {
102 0 : return;
103 : }
104 : }
105 :
106 0 : pcmk__rsc_trace(rsc, "Adding %s to %s %s", rsc->id, pcmk__node_name(node),
107 : pcmk_is_set(rsc->flags, pcmk_rsc_managed)? "" : "(unmanaged)");
108 :
109 0 : rsc->running_on = g_list_append(rsc->running_on, node);
110 0 : if (rsc->variant == pcmk_rsc_variant_primitive) {
111 0 : node->details->running_rsc = g_list_append(node->details->running_rsc, rsc);
112 :
113 0 : native_priority_to_node(rsc, node, failed);
114 : }
115 :
116 0 : if ((rsc->variant == pcmk_rsc_variant_primitive)
117 0 : && node->details->maintenance) {
118 0 : pcmk__clear_rsc_flags(rsc, pcmk_rsc_managed);
119 0 : pcmk__set_rsc_flags(rsc, pcmk_rsc_maintenance);
120 : }
121 :
122 0 : if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
123 0 : pcmk_resource_t *p = rsc->parent;
124 :
125 0 : pcmk__rsc_info(rsc, "resource %s isn't managed", rsc->id);
126 0 : resource_location(rsc, node, PCMK_SCORE_INFINITY,
127 : "not_managed_default", scheduler);
128 :
129 0 : while(p && node->details->online) {
130 : /* add without the additional location constraint */
131 0 : p->running_on = g_list_append(p->running_on, node);
132 0 : p = p->parent;
133 : }
134 0 : return;
135 : }
136 :
137 0 : if (is_multiply_active(rsc)) {
138 0 : switch (rsc->recovery_type) {
139 0 : case pcmk_multiply_active_stop:
140 : {
141 : GHashTableIter gIter;
142 0 : pcmk_node_t *local_node = NULL;
143 :
144 : /* make sure it doesn't come up again */
145 0 : if (rsc->allowed_nodes != NULL) {
146 0 : g_hash_table_destroy(rsc->allowed_nodes);
147 : }
148 0 : rsc->allowed_nodes = pe__node_list2table(scheduler->nodes);
149 0 : g_hash_table_iter_init(&gIter, rsc->allowed_nodes);
150 0 : while (g_hash_table_iter_next(&gIter, NULL, (void **)&local_node)) {
151 0 : local_node->weight = -PCMK_SCORE_INFINITY;
152 : }
153 : }
154 0 : break;
155 0 : case pcmk_multiply_active_block:
156 0 : pcmk__clear_rsc_flags(rsc, pcmk_rsc_managed);
157 0 : pcmk__set_rsc_flags(rsc, pcmk_rsc_blocked);
158 :
159 : /* If the resource belongs to a group or bundle configured with
160 : * PCMK_META_MULTIPLE_ACTIVE=PCMK_VALUE_BLOCK, block the entire
161 : * entity.
162 : */
163 0 : if (rsc->parent
164 0 : && ((rsc->parent->variant == pcmk_rsc_variant_group)
165 0 : || (rsc->parent->variant == pcmk_rsc_variant_bundle))
166 0 : && (rsc->parent->recovery_type == pcmk_multiply_active_block)) {
167 0 : GList *gIter = rsc->parent->children;
168 :
169 0 : for (; gIter != NULL; gIter = gIter->next) {
170 0 : pcmk_resource_t *child = gIter->data;
171 :
172 0 : pcmk__clear_rsc_flags(child, pcmk_rsc_managed);
173 0 : pcmk__set_rsc_flags(child, pcmk_rsc_blocked);
174 : }
175 : }
176 0 : break;
177 :
178 : // pcmk_multiply_active_restart, pcmk_multiply_active_unexpected
179 0 : default:
180 : /* The scheduler will do the right thing because the relevant
181 : * variables and flags are set when unpacking the history.
182 : */
183 0 : break;
184 : }
185 0 : crm_debug("%s is active on multiple nodes including %s: %s",
186 : rsc->id, pcmk__node_name(node),
187 : pcmk_multiply_active_text(rsc->recovery_type));
188 :
189 : } else {
190 0 : pcmk__rsc_trace(rsc, "Resource %s is active on %s",
191 : rsc->id, pcmk__node_name(node));
192 : }
193 :
194 0 : if (rsc->parent != NULL) {
195 0 : native_add_running(rsc->parent, node, scheduler, FALSE);
196 : }
197 : }
198 :
199 : static void
200 0 : recursive_clear_unique(pcmk_resource_t *rsc, gpointer user_data)
201 : {
202 0 : pcmk__clear_rsc_flags(rsc, pcmk_rsc_unique);
203 0 : pcmk__insert_meta(rsc, PCMK_META_GLOBALLY_UNIQUE, PCMK_VALUE_FALSE);
204 0 : g_list_foreach(rsc->children, (GFunc) recursive_clear_unique, NULL);
205 0 : }
206 :
207 : gboolean
208 0 : native_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
209 : {
210 0 : pcmk_resource_t *parent = uber_parent(rsc);
211 0 : const char *standard = crm_element_value(rsc->xml, PCMK_XA_CLASS);
212 0 : uint32_t ra_caps = pcmk_get_ra_caps(standard);
213 :
214 0 : pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id);
215 :
216 : // Only some agent standards support unique and promotable clones
217 0 : if (!pcmk_is_set(ra_caps, pcmk_ra_cap_unique)
218 0 : && pcmk_is_set(rsc->flags, pcmk_rsc_unique)
219 0 : && 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 0 : pe__force_anon(standard, parent, rsc->id, 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 0 : recursive_clear_unique(parent, NULL);
234 0 : recursive_clear_unique(rsc, NULL);
235 : }
236 0 : if (!pcmk_is_set(ra_caps, pcmk_ra_cap_promotable)
237 0 : && pcmk_is_set(parent->flags, pcmk_rsc_promotable)) {
238 :
239 0 : 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 0 : return FALSE;
243 : }
244 0 : return TRUE;
245 : }
246 :
247 : static bool
248 111 : rsc_is_on_node(pcmk_resource_t *rsc, const pcmk_node_t *node, int flags)
249 : {
250 111 : pcmk__rsc_trace(rsc, "Checking whether %s is on %s",
251 : rsc->id, pcmk__node_name(node));
252 :
253 111 : if (pcmk_is_set(flags, pcmk_rsc_match_current_node)
254 86 : && (rsc->running_on != NULL)) {
255 :
256 117 : for (GList *iter = rsc->running_on; iter; iter = iter->next) {
257 85 : if (pcmk__same_node((pcmk_node_t *) iter->data, node)) {
258 48 : return true;
259 : }
260 : }
261 :
262 31 : } else if (pcmk_is_set(flags, pe_find_inactive) // @COMPAT deprecated
263 0 : && (rsc->running_on == NULL)) {
264 0 : return true;
265 :
266 31 : } else if (!pcmk_is_set(flags, pcmk_rsc_match_current_node)
267 25 : && (rsc->allocated_to != NULL)
268 0 : && pcmk__same_node(rsc->allocated_to, node)) {
269 0 : return true;
270 : }
271 63 : return false;
272 : }
273 :
274 : pcmk_resource_t *
275 2525 : native_find_rsc(pcmk_resource_t *rsc, const char *id,
276 : const pcmk_node_t *on_node, int flags)
277 : {
278 2525 : bool match = false;
279 2525 : pcmk_resource_t *result = NULL;
280 :
281 2525 : CRM_CHECK(id && rsc && rsc->id, return NULL);
282 :
283 2522 : if (pcmk_is_set(flags, pcmk_rsc_match_clone_only)) {
284 83 : const char *rid = pcmk__xe_id(rsc->xml);
285 :
286 83 : if (!pcmk__is_clone(pe__const_top_resource(rsc, false))) {
287 38 : match = false;
288 :
289 45 : } else if (!strcmp(id, rsc->id) || pcmk__str_eq(id, rid, pcmk__str_none)) {
290 29 : match = true;
291 : }
292 :
293 2439 : } else if (!strcmp(id, rsc->id)) {
294 310 : match = true;
295 :
296 2129 : } else if (pcmk_is_set(flags, pcmk_rsc_match_history)
297 1894 : && rsc->clone_name && strcmp(rsc->clone_name, id) == 0) {
298 12 : match = true;
299 :
300 2117 : } else if (pcmk_is_set(flags, pcmk_rsc_match_basename)
301 2088 : || (pcmk_is_set(flags, pcmk_rsc_match_anon_basename)
302 29 : && !pcmk_is_set(rsc->flags, pcmk_rsc_unique))) {
303 58 : match = pe_base_name_eq(rsc, id);
304 : }
305 :
306 2522 : if (match && on_node) {
307 111 : if (!rsc_is_on_node(rsc, on_node, flags)) {
308 63 : match = false;
309 : }
310 : }
311 :
312 2522 : if (match) {
313 332 : return rsc;
314 : }
315 :
316 3636 : for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
317 1549 : pcmk_resource_t *child = (pcmk_resource_t *) gIter->data;
318 :
319 1549 : result = rsc->fns->find_rsc(child, id, on_node, flags);
320 1549 : if (result) {
321 103 : return result;
322 : }
323 : }
324 2087 : return NULL;
325 : }
326 :
327 : // create is ignored
328 : char *
329 0 : native_parameter(pcmk_resource_t *rsc, pcmk_node_t *node, gboolean create,
330 : const char *name, pcmk_scheduler_t *scheduler)
331 : {
332 0 : const char *value = NULL;
333 0 : GHashTable *params = NULL;
334 :
335 0 : CRM_CHECK(rsc != NULL, return NULL);
336 0 : CRM_CHECK(name != NULL && strlen(name) != 0, return NULL);
337 :
338 0 : pcmk__rsc_trace(rsc, "Looking up %s in %s", name, rsc->id);
339 0 : params = pe_rsc_params(rsc, node, scheduler);
340 0 : value = g_hash_table_lookup(params, name);
341 0 : if (value == NULL) {
342 : /* try meta attributes instead */
343 0 : value = g_hash_table_lookup(rsc->meta, name);
344 : }
345 0 : return pcmk__str_copy(value);
346 : }
347 :
348 : gboolean
349 0 : native_active(pcmk_resource_t * rsc, gboolean all)
350 : {
351 0 : for (GList *gIter = rsc->running_on; gIter != NULL; gIter = gIter->next) {
352 0 : pcmk_node_t *a_node = (pcmk_node_t *) gIter->data;
353 :
354 0 : if (a_node->details->unclean) {
355 0 : pcmk__rsc_trace(rsc, "Resource %s: %s is unclean",
356 : rsc->id, pcmk__node_name(a_node));
357 0 : return TRUE;
358 0 : } else if (!a_node->details->online
359 0 : && pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
360 0 : pcmk__rsc_trace(rsc, "Resource %s: %s is offline",
361 : rsc->id, pcmk__node_name(a_node));
362 : } else {
363 0 : pcmk__rsc_trace(rsc, "Resource %s active on %s",
364 : rsc->id, pcmk__node_name(a_node));
365 0 : return TRUE;
366 : }
367 : }
368 0 : return FALSE;
369 : }
370 :
371 : struct print_data_s {
372 : long options;
373 : void *print_data;
374 : };
375 :
376 : static const char *
377 0 : native_pending_state(const pcmk_resource_t *rsc)
378 : {
379 0 : const char *pending_state = NULL;
380 :
381 0 : if (pcmk__str_eq(rsc->pending_task, PCMK_ACTION_START, pcmk__str_casei)) {
382 0 : pending_state = "Starting";
383 :
384 0 : } else if (pcmk__str_eq(rsc->pending_task, PCMK_ACTION_STOP,
385 : pcmk__str_casei)) {
386 0 : pending_state = "Stopping";
387 :
388 0 : } else if (pcmk__str_eq(rsc->pending_task, PCMK_ACTION_MIGRATE_TO,
389 : pcmk__str_casei)) {
390 0 : pending_state = "Migrating";
391 :
392 0 : } else if (pcmk__str_eq(rsc->pending_task, PCMK_ACTION_MIGRATE_FROM,
393 : pcmk__str_casei)) {
394 : /* Work might be done in here. */
395 0 : pending_state = "Migrating";
396 :
397 0 : } else if (pcmk__str_eq(rsc->pending_task, PCMK_ACTION_PROMOTE,
398 : pcmk__str_casei)) {
399 0 : pending_state = "Promoting";
400 :
401 0 : } else if (pcmk__str_eq(rsc->pending_task, PCMK_ACTION_DEMOTE,
402 : pcmk__str_casei)) {
403 0 : pending_state = "Demoting";
404 : }
405 :
406 0 : return pending_state;
407 : }
408 :
409 : static const char *
410 0 : native_pending_task(const pcmk_resource_t *rsc)
411 : {
412 0 : const char *pending_task = NULL;
413 :
414 0 : if (pcmk__str_eq(rsc->pending_task, PCMK_ACTION_MONITOR, pcmk__str_casei)) {
415 0 : pending_task = "Monitoring";
416 :
417 : /* Pending probes are not printed, even if pending
418 : * operations are requested. If someone ever requests that
419 : * behavior, uncomment this and the corresponding part of
420 : * unpack.c:unpack_rsc_op().
421 : */
422 : /*
423 : } else if (pcmk__str_eq(rsc->pending_task, "probe", pcmk__str_casei)) {
424 : pending_task = "Checking";
425 : */
426 : }
427 :
428 0 : return pending_task;
429 : }
430 :
431 : static enum rsc_role_e
432 0 : native_displayable_role(const pcmk_resource_t *rsc)
433 : {
434 0 : enum rsc_role_e role = rsc->role;
435 :
436 0 : if ((role == pcmk_role_started)
437 0 : && pcmk_is_set(pe__const_top_resource(rsc, false)->flags,
438 : pcmk_rsc_promotable)) {
439 :
440 0 : role = pcmk_role_unpromoted;
441 : }
442 0 : return role;
443 : }
444 :
445 : static const char *
446 0 : native_displayable_state(const pcmk_resource_t *rsc, bool print_pending)
447 : {
448 0 : const char *rsc_state = NULL;
449 :
450 0 : if (print_pending) {
451 0 : rsc_state = native_pending_state(rsc);
452 : }
453 0 : if (rsc_state == NULL) {
454 0 : rsc_state = pcmk_role_text(native_displayable_role(rsc));
455 : }
456 0 : return rsc_state;
457 : }
458 :
459 : /*!
460 : * \internal
461 : * \deprecated This function will be removed in a future release
462 : */
463 : static void
464 0 : native_print_xml(pcmk_resource_t *rsc, const char *pre_text, long options,
465 : void *print_data)
466 : {
467 0 : const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS);
468 0 : const char *prov = crm_element_value(rsc->xml, PCMK_XA_PROVIDER);
469 0 : const char *rsc_state = native_displayable_state(rsc, pcmk_is_set(options, pe_print_pending));
470 0 : const char *target_role = NULL;
471 :
472 : /* resource information. */
473 0 : status_print("%s<resource ", pre_text);
474 0 : status_print(PCMK_XA_ID "=\"%s\" ", rsc_printable_id(rsc));
475 0 : status_print("resource_agent=\"%s%s%s:%s\" ", class,
476 : ((prov == NULL)? "" : PROVIDER_SEP),
477 : ((prov == NULL)? "" : prov),
478 : crm_element_value(rsc->xml, PCMK_XA_TYPE));
479 :
480 0 : status_print("role=\"%s\" ", rsc_state);
481 0 : if (rsc->meta) {
482 0 : target_role = g_hash_table_lookup(rsc->meta, PCMK_META_TARGET_ROLE);
483 : }
484 0 : if (target_role) {
485 0 : status_print("target_role=\"%s\" ", target_role);
486 : }
487 0 : status_print("active=\"%s\" ", pcmk__btoa(rsc->fns->active(rsc, TRUE)));
488 0 : status_print("orphaned=\"%s\" ",
489 : pcmk__flag_text(rsc->flags, pcmk_rsc_removed));
490 0 : status_print("blocked=\"%s\" ",
491 : pcmk__flag_text(rsc->flags, pcmk_rsc_blocked));
492 0 : status_print("managed=\"%s\" ",
493 : pcmk__flag_text(rsc->flags, pcmk_rsc_managed));
494 0 : status_print("failed=\"%s\" ",
495 : pcmk__flag_text(rsc->flags, pcmk_rsc_failed));
496 0 : status_print("failure_ignored=\"%s\" ",
497 : pcmk__flag_text(rsc->flags, pcmk_rsc_ignore_failure));
498 0 : status_print("nodes_running_on=\"%d\" ", g_list_length(rsc->running_on));
499 :
500 0 : if (options & pe_print_pending) {
501 0 : const char *pending_task = native_pending_task(rsc);
502 :
503 0 : if (pending_task) {
504 0 : status_print("pending=\"%s\" ", pending_task);
505 : }
506 : }
507 :
508 : /* print out the nodes this resource is running on */
509 0 : if (options & pe_print_rsconly) {
510 0 : status_print("/>\n");
511 : /* do nothing */
512 0 : } else if (rsc->running_on != NULL) {
513 0 : GList *gIter = rsc->running_on;
514 :
515 0 : status_print(">\n");
516 0 : for (; gIter != NULL; gIter = gIter->next) {
517 0 : pcmk_node_t *node = (pcmk_node_t *) gIter->data;
518 :
519 0 : status_print("%s <node " PCMK_XA_NAME "=\"%s\" "
520 : PCMK_XA_ID "=\"%s\" cached=\"%s\"/>\n",
521 : pre_text, pcmk__s(node->details->uname, ""),
522 : node->details->id, pcmk__btoa(!node->details->online));
523 : }
524 0 : status_print("%s</resource>\n", pre_text);
525 : } else {
526 0 : status_print("/>\n");
527 : }
528 0 : }
529 :
530 : // Append a flag to resource description string's flags list
531 : static bool
532 0 : add_output_flag(GString *s, const char *flag_desc, bool have_flags)
533 : {
534 0 : g_string_append(s, (have_flags? ", " : " ("));
535 : g_string_append(s, flag_desc);
536 0 : return true;
537 : }
538 :
539 : // Append a node name to resource description string's node list
540 : static bool
541 0 : add_output_node(GString *s, const char *node, bool have_nodes)
542 : {
543 0 : g_string_append(s, (have_nodes? " " : " [ "));
544 : g_string_append(s, node);
545 0 : return true;
546 : }
547 :
548 : /*!
549 : * \internal
550 : * \brief Create a string description of a resource
551 : *
552 : * \param[in] rsc Resource to describe
553 : * \param[in] name Desired identifier for the resource
554 : * \param[in] node If not NULL, node that resource is "on"
555 : * \param[in] show_opts Bitmask of pcmk_show_opt_e.
556 : * \param[in] target_role Resource's target role
557 : * \param[in] show_nodes Whether to display nodes when multiply active
558 : *
559 : * \return Newly allocated string description of resource
560 : * \note Caller must free the result with g_free().
561 : */
562 : gchar *
563 0 : pcmk__native_output_string(const pcmk_resource_t *rsc, const char *name,
564 : const pcmk_node_t *node, uint32_t show_opts,
565 : const char *target_role, bool show_nodes)
566 : {
567 0 : const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS);
568 0 : const char *provider = NULL;
569 0 : const char *kind = crm_element_value(rsc->xml, PCMK_XA_TYPE);
570 0 : GString *outstr = NULL;
571 0 : bool have_flags = false;
572 :
573 0 : if (rsc->variant != pcmk_rsc_variant_primitive) {
574 0 : return NULL;
575 : }
576 :
577 0 : CRM_CHECK(name != NULL, name = "unknown");
578 0 : CRM_CHECK(kind != NULL, kind = "unknown");
579 0 : CRM_CHECK(class != NULL, class = "unknown");
580 :
581 0 : if (pcmk_is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider)) {
582 0 : provider = crm_element_value(rsc->xml, PCMK_XA_PROVIDER);
583 : }
584 :
585 0 : if ((node == NULL) && (rsc->lock_node != NULL)) {
586 0 : node = rsc->lock_node;
587 : }
588 0 : if (pcmk_any_flags_set(show_opts, pcmk_show_rsc_only)
589 0 : || pcmk__list_of_multiple(rsc->running_on)) {
590 0 : node = NULL;
591 : }
592 :
593 0 : outstr = g_string_sized_new(128);
594 :
595 : // Resource name and agent
596 0 : pcmk__g_strcat(outstr,
597 : name, "\t(", class, ((provider == NULL)? "" : PROVIDER_SEP),
598 : pcmk__s(provider, ""), ":", kind, "):\t", NULL);
599 :
600 : // State on node
601 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_removed)) {
602 0 : g_string_append(outstr, " ORPHANED");
603 : }
604 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) {
605 0 : enum rsc_role_e role = native_displayable_role(rsc);
606 :
607 0 : g_string_append(outstr, " FAILED");
608 0 : if (role > pcmk_role_unpromoted) {
609 0 : pcmk__add_word(&outstr, 0, pcmk_role_text(role));
610 : }
611 : } else {
612 0 : bool show_pending = pcmk_is_set(show_opts, pcmk_show_pending);
613 :
614 0 : pcmk__add_word(&outstr, 0, native_displayable_state(rsc, show_pending));
615 : }
616 0 : if (node) {
617 0 : pcmk__add_word(&outstr, 0, pcmk__node_name(node));
618 : }
619 :
620 : // Failed probe operation
621 0 : if (native_displayable_role(rsc) == pcmk_role_stopped) {
622 0 : xmlNode *probe_op = pe__failed_probe_for_rsc(rsc, node ? node->details->uname : NULL);
623 0 : if (probe_op != NULL) {
624 : int rc;
625 :
626 0 : pcmk__scan_min_int(crm_element_value(probe_op, PCMK__XA_RC_CODE),
627 : &rc, 0);
628 0 : pcmk__g_strcat(outstr, " (", services_ocf_exitcode_str(rc), ") ",
629 : NULL);
630 : }
631 : }
632 :
633 : // Flags, as: (<flag> [...])
634 0 : if (node && !(node->details->online) && node->details->unclean) {
635 0 : have_flags = add_output_flag(outstr, "UNCLEAN", have_flags);
636 : }
637 0 : if (node && (node == rsc->lock_node)) {
638 0 : have_flags = add_output_flag(outstr, "LOCKED", have_flags);
639 : }
640 0 : if (pcmk_is_set(show_opts, pcmk_show_pending)) {
641 0 : const char *pending_task = native_pending_task(rsc);
642 :
643 0 : if (pending_task) {
644 0 : have_flags = add_output_flag(outstr, pending_task, have_flags);
645 : }
646 : }
647 0 : if (target_role != NULL) {
648 0 : switch (pcmk_parse_role(target_role)) {
649 0 : case pcmk_role_unknown:
650 0 : pcmk__config_err("Invalid " PCMK_META_TARGET_ROLE
651 : " %s for resource %s", target_role, rsc->id);
652 0 : break;
653 :
654 0 : case pcmk_role_stopped:
655 0 : have_flags = add_output_flag(outstr, "disabled", have_flags);
656 0 : break;
657 :
658 0 : case pcmk_role_unpromoted:
659 0 : if (pcmk_is_set(pe__const_top_resource(rsc, false)->flags,
660 : pcmk_rsc_promotable)) {
661 0 : have_flags = add_output_flag(outstr,
662 : PCMK_META_TARGET_ROLE ":",
663 : have_flags);
664 0 : g_string_append(outstr, target_role);
665 : }
666 0 : break;
667 :
668 0 : default:
669 : /* Only show target role if it limits our abilities (i.e. ignore
670 : * Started, as it is the default anyways, and doesn't prevent
671 : * the resource from becoming promoted).
672 : */
673 0 : break;
674 : }
675 : }
676 :
677 : // Blocked or maintenance implies unmanaged
678 0 : if (pcmk_any_flags_set(rsc->flags,
679 : pcmk_rsc_blocked|pcmk_rsc_maintenance)) {
680 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_blocked)) {
681 0 : have_flags = add_output_flag(outstr, "blocked", have_flags);
682 :
683 0 : } else if (pcmk_is_set(rsc->flags, pcmk_rsc_maintenance)) {
684 0 : have_flags = add_output_flag(outstr, "maintenance", have_flags);
685 : }
686 0 : } else if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
687 0 : have_flags = add_output_flag(outstr, "unmanaged", have_flags);
688 : }
689 :
690 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_ignore_failure)) {
691 0 : have_flags = add_output_flag(outstr, "failure ignored", have_flags);
692 : }
693 :
694 :
695 0 : if (have_flags) {
696 0 : g_string_append_c(outstr, ')');
697 : }
698 :
699 : // User-supplied description
700 0 : if (pcmk_any_flags_set(show_opts, pcmk_show_rsc_only|pcmk_show_description)
701 0 : || pcmk__list_of_multiple(rsc->running_on)) {
702 0 : const char *desc = crm_element_value(rsc->xml, PCMK_XA_DESCRIPTION);
703 :
704 0 : if (desc) {
705 0 : g_string_append(outstr, " (");
706 0 : g_string_append(outstr, desc);
707 0 : g_string_append(outstr, ")");
708 :
709 : }
710 : }
711 :
712 0 : if (show_nodes && !pcmk_is_set(show_opts, pcmk_show_rsc_only)
713 0 : && pcmk__list_of_multiple(rsc->running_on)) {
714 0 : bool have_nodes = false;
715 :
716 0 : for (GList *iter = rsc->running_on; iter != NULL; iter = iter->next) {
717 0 : pcmk_node_t *n = (pcmk_node_t *) iter->data;
718 :
719 0 : have_nodes = add_output_node(outstr, n->details->uname, have_nodes);
720 : }
721 0 : if (have_nodes) {
722 0 : g_string_append(outstr, " ]");
723 : }
724 : }
725 :
726 0 : return g_string_free(outstr, FALSE);
727 : }
728 :
729 : int
730 0 : pe__common_output_html(pcmk__output_t *out, const pcmk_resource_t *rsc,
731 : const char *name, const pcmk_node_t *node,
732 : uint32_t show_opts)
733 : {
734 0 : const char *kind = crm_element_value(rsc->xml, PCMK_XA_TYPE);
735 0 : const char *target_role = NULL;
736 0 : const char *cl = NULL;
737 :
738 0 : xmlNode *child = NULL;
739 0 : gchar *content = NULL;
740 :
741 0 : CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive);
742 0 : CRM_ASSERT(kind != NULL);
743 :
744 0 : if (rsc->meta) {
745 0 : const char *is_internal = g_hash_table_lookup(rsc->meta,
746 : PCMK__META_INTERNAL_RSC);
747 :
748 0 : if (crm_is_true(is_internal)
749 0 : && !pcmk_is_set(show_opts, pcmk_show_implicit_rscs)) {
750 :
751 0 : crm_trace("skipping print of internal resource %s", rsc->id);
752 0 : return pcmk_rc_no_output;
753 : }
754 0 : target_role = g_hash_table_lookup(rsc->meta, PCMK_META_TARGET_ROLE);
755 : }
756 :
757 0 : if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
758 0 : cl = PCMK__VALUE_RSC_MANAGED;
759 :
760 0 : } else if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) {
761 0 : cl = PCMK__VALUE_RSC_FAILED;
762 :
763 0 : } else if ((rsc->variant == pcmk_rsc_variant_primitive)
764 0 : && (rsc->running_on == NULL)) {
765 0 : cl = PCMK__VALUE_RSC_FAILED;
766 :
767 0 : } else if (pcmk__list_of_multiple(rsc->running_on)) {
768 0 : cl = PCMK__VALUE_RSC_MULTIPLE;
769 :
770 0 : } else if (pcmk_is_set(rsc->flags, pcmk_rsc_ignore_failure)) {
771 0 : cl = PCMK__VALUE_RSC_FAILURE_IGNORED;
772 :
773 : } else {
774 0 : cl = PCMK__VALUE_RSC_OK;
775 : }
776 :
777 0 : child = pcmk__output_create_html_node(out, "li", NULL, NULL, NULL);
778 0 : child = pcmk__html_create(child, PCMK__XE_SPAN, NULL, cl);
779 0 : content = pcmk__native_output_string(rsc, name, node, show_opts,
780 : target_role, true);
781 0 : pcmk__xe_set_content(child, "%s", content);
782 0 : g_free(content);
783 :
784 0 : return pcmk_rc_ok;
785 : }
786 :
787 : int
788 0 : pe__common_output_text(pcmk__output_t *out, const pcmk_resource_t *rsc,
789 : const char *name, const pcmk_node_t *node,
790 : uint32_t show_opts)
791 : {
792 0 : const char *target_role = NULL;
793 :
794 0 : CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive);
795 :
796 0 : if (rsc->meta) {
797 0 : const char *is_internal = g_hash_table_lookup(rsc->meta,
798 : PCMK__META_INTERNAL_RSC);
799 :
800 0 : if (crm_is_true(is_internal)
801 0 : && !pcmk_is_set(show_opts, pcmk_show_implicit_rscs)) {
802 :
803 0 : crm_trace("skipping print of internal resource %s", rsc->id);
804 0 : return pcmk_rc_no_output;
805 : }
806 0 : target_role = g_hash_table_lookup(rsc->meta, PCMK_META_TARGET_ROLE);
807 : }
808 :
809 : {
810 0 : gchar *s = pcmk__native_output_string(rsc, name, node, show_opts,
811 : target_role, true);
812 :
813 0 : out->list_item(out, NULL, "%s", s);
814 0 : g_free(s);
815 : }
816 :
817 0 : return pcmk_rc_ok;
818 : }
819 :
820 : /*!
821 : * \internal
822 : * \deprecated This function will be removed in a future release
823 : */
824 : void
825 0 : common_print(pcmk_resource_t *rsc, const char *pre_text, const char *name,
826 : const pcmk_node_t *node, long options, void *print_data)
827 : {
828 0 : const char *target_role = NULL;
829 :
830 0 : CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive);
831 :
832 0 : if (rsc->meta) {
833 0 : const char *is_internal = g_hash_table_lookup(rsc->meta,
834 : PCMK__META_INTERNAL_RSC);
835 :
836 0 : if (crm_is_true(is_internal)
837 0 : && !pcmk_is_set(options, pe_print_implicit)) {
838 :
839 0 : crm_trace("skipping print of internal resource %s", rsc->id);
840 0 : return;
841 : }
842 0 : target_role = g_hash_table_lookup(rsc->meta, PCMK_META_TARGET_ROLE);
843 : }
844 :
845 0 : if (options & pe_print_xml) {
846 0 : native_print_xml(rsc, pre_text, options, print_data);
847 0 : return;
848 : }
849 :
850 0 : if ((pre_text == NULL) && (options & pe_print_printf)) {
851 0 : pre_text = " ";
852 : }
853 :
854 0 : if (options & pe_print_html) {
855 0 : if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
856 0 : status_print("<font color=\"yellow\">");
857 :
858 0 : } else if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) {
859 0 : status_print("<font color=\"red\">");
860 :
861 0 : } else if (rsc->running_on == NULL) {
862 0 : status_print("<font color=\"red\">");
863 :
864 0 : } else if (pcmk__list_of_multiple(rsc->running_on)) {
865 0 : status_print("<font color=\"orange\">");
866 :
867 0 : } else if (pcmk_is_set(rsc->flags, pcmk_rsc_ignore_failure)) {
868 0 : status_print("<font color=\"yellow\">");
869 :
870 : } else {
871 0 : status_print("<font color=\"green\">");
872 : }
873 : }
874 :
875 : {
876 0 : gchar *resource_s = pcmk__native_output_string(rsc, name, node, options,
877 : target_role, false);
878 0 : status_print("%s%s", (pre_text? pre_text : ""), resource_s);
879 0 : g_free(resource_s);
880 : }
881 :
882 0 : if (pcmk_is_set(options, pe_print_html)) {
883 0 : status_print(" </font> ");
884 : }
885 :
886 0 : if (!pcmk_is_set(options, pe_print_rsconly)
887 0 : && pcmk__list_of_multiple(rsc->running_on)) {
888 :
889 0 : GList *gIter = rsc->running_on;
890 0 : int counter = 0;
891 :
892 0 : if (options & pe_print_html) {
893 0 : status_print("<ul>\n");
894 0 : } else if ((options & pe_print_printf)
895 0 : || (options & pe_print_ncurses)) {
896 0 : status_print("[");
897 : }
898 :
899 0 : for (; gIter != NULL; gIter = gIter->next) {
900 0 : pcmk_node_t *n = (pcmk_node_t *) gIter->data;
901 :
902 0 : counter++;
903 :
904 0 : if (options & pe_print_html) {
905 0 : status_print("<li>\n%s", pcmk__node_name(n));
906 :
907 0 : } else if ((options & pe_print_printf)
908 0 : || (options & pe_print_ncurses)) {
909 0 : status_print(" %s", pcmk__node_name(n));
910 :
911 0 : } else if ((options & pe_print_log)) {
912 0 : status_print("\t%d : %s", counter, pcmk__node_name(n));
913 :
914 : } else {
915 0 : status_print("%s", pcmk__node_name(n));
916 : }
917 0 : if (options & pe_print_html) {
918 0 : status_print("</li>\n");
919 :
920 : }
921 : }
922 :
923 0 : if (options & pe_print_html) {
924 0 : status_print("</ul>\n");
925 0 : } else if ((options & pe_print_printf)
926 0 : || (options & pe_print_ncurses)) {
927 0 : status_print(" ]");
928 : }
929 : }
930 :
931 0 : if (options & pe_print_html) {
932 0 : status_print("<br/>\n");
933 0 : } else if (options & pe_print_suppres_nl) {
934 : /* nothing */
935 0 : } else if ((options & pe_print_printf) || (options & pe_print_ncurses)) {
936 0 : status_print("\n");
937 : }
938 : }
939 :
940 : /*!
941 : * \internal
942 : * \deprecated This function will be removed in a future release
943 : */
944 : void
945 0 : native_print(pcmk_resource_t *rsc, const char *pre_text, long options,
946 : void *print_data)
947 : {
948 0 : const pcmk_node_t *node = NULL;
949 :
950 0 : CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive);
951 0 : if (options & pe_print_xml) {
952 0 : native_print_xml(rsc, pre_text, options, print_data);
953 0 : return;
954 : }
955 :
956 0 : node = pcmk__current_node(rsc);
957 :
958 0 : if (node == NULL) {
959 : // This is set only if a non-probe action is pending on this node
960 0 : node = rsc->pending_node;
961 : }
962 :
963 0 : common_print(rsc, pre_text, rsc_printable_id(rsc), node, options, print_data);
964 : }
965 :
966 : PCMK__OUTPUT_ARGS("primitive", "uint32_t", "pcmk_resource_t *", "GList *",
967 : "GList *")
968 : int
969 0 : pe__resource_xml(pcmk__output_t *out, va_list args)
970 : {
971 0 : uint32_t show_opts = va_arg(args, uint32_t);
972 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
973 0 : GList *only_node G_GNUC_UNUSED = va_arg(args, GList *);
974 0 : GList *only_rsc = va_arg(args, GList *);
975 :
976 0 : int rc = pcmk_rc_no_output;
977 0 : bool print_pending = pcmk_is_set(show_opts, pcmk_show_pending);
978 0 : const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS);
979 0 : const char *prov = crm_element_value(rsc->xml, PCMK_XA_PROVIDER);
980 :
981 : char ra_name[LINE_MAX];
982 0 : const char *rsc_state = native_displayable_state(rsc, print_pending);
983 0 : const char *target_role = NULL;
984 0 : const char *active = pcmk__btoa(rsc->fns->active(rsc, TRUE));
985 0 : const char *orphaned = pcmk__flag_text(rsc->flags, pcmk_rsc_removed);
986 0 : const char *blocked = pcmk__flag_text(rsc->flags, pcmk_rsc_blocked);
987 0 : const char *maintenance = pcmk__flag_text(rsc->flags, pcmk_rsc_maintenance);
988 0 : const char *managed = pcmk__flag_text(rsc->flags, pcmk_rsc_managed);
989 0 : const char *failed = pcmk__flag_text(rsc->flags, pcmk_rsc_failed);
990 0 : const char *ignored = pcmk__flag_text(rsc->flags, pcmk_rsc_ignore_failure);
991 0 : char *nodes_running_on = NULL;
992 0 : const char *pending = print_pending? native_pending_task(rsc) : NULL;
993 0 : const char *locked_to = NULL;
994 0 : const char *desc = pe__resource_description(rsc, show_opts);
995 :
996 0 : CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive);
997 :
998 0 : if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
999 0 : return pcmk_rc_no_output;
1000 : }
1001 :
1002 : // Resource information
1003 0 : snprintf(ra_name, LINE_MAX, "%s%s%s:%s", class,
1004 : ((prov == NULL)? "" : PROVIDER_SEP), ((prov == NULL)? "" : prov),
1005 0 : crm_element_value(rsc->xml, PCMK_XA_TYPE));
1006 :
1007 0 : if (rsc->meta != NULL) {
1008 0 : target_role = g_hash_table_lookup(rsc->meta, PCMK_META_TARGET_ROLE);
1009 : }
1010 :
1011 0 : nodes_running_on = pcmk__itoa(g_list_length(rsc->running_on));
1012 :
1013 0 : if (rsc->lock_node != NULL) {
1014 0 : locked_to = rsc->lock_node->details->uname;
1015 : }
1016 :
1017 0 : rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_RESOURCE,
1018 : PCMK_XA_ID, rsc_printable_id(rsc),
1019 : PCMK_XA_RESOURCE_AGENT, ra_name,
1020 : PCMK_XA_ROLE, rsc_state,
1021 : PCMK_XA_TARGET_ROLE, target_role,
1022 : PCMK_XA_ACTIVE, active,
1023 : PCMK_XA_ORPHANED, orphaned,
1024 : PCMK_XA_BLOCKED, blocked,
1025 : PCMK_XA_MAINTENANCE, maintenance,
1026 : PCMK_XA_MANAGED, managed,
1027 : PCMK_XA_FAILED, failed,
1028 : PCMK_XA_FAILURE_IGNORED, ignored,
1029 : PCMK_XA_NODES_RUNNING_ON, nodes_running_on,
1030 : PCMK_XA_PENDING, pending,
1031 : PCMK_XA_LOCKED_TO, locked_to,
1032 : PCMK_XA_DESCRIPTION, desc,
1033 : NULL);
1034 0 : free(nodes_running_on);
1035 :
1036 0 : CRM_ASSERT(rc == pcmk_rc_ok);
1037 :
1038 0 : if (rsc->running_on != NULL) {
1039 0 : GList *gIter = rsc->running_on;
1040 :
1041 0 : for (; gIter != NULL; gIter = gIter->next) {
1042 0 : pcmk_node_t *node = (pcmk_node_t *) gIter->data;
1043 0 : const char *cached = pcmk__btoa(node->details->online);
1044 :
1045 0 : rc = pe__name_and_nvpairs_xml(out, false, PCMK_XE_NODE,
1046 0 : PCMK_XA_NAME, node->details->uname,
1047 0 : PCMK_XA_ID, node->details->id,
1048 : PCMK_XA_CACHED, cached,
1049 : NULL);
1050 0 : CRM_ASSERT(rc == pcmk_rc_ok);
1051 : }
1052 : }
1053 :
1054 0 : pcmk__output_xml_pop_parent(out);
1055 0 : return rc;
1056 : }
1057 :
1058 : PCMK__OUTPUT_ARGS("primitive", "uint32_t", "pcmk_resource_t *", "GList *",
1059 : "GList *")
1060 : int
1061 0 : pe__resource_html(pcmk__output_t *out, va_list args)
1062 : {
1063 0 : uint32_t show_opts = va_arg(args, uint32_t);
1064 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
1065 0 : GList *only_node G_GNUC_UNUSED = va_arg(args, GList *);
1066 0 : GList *only_rsc = va_arg(args, GList *);
1067 :
1068 0 : const pcmk_node_t *node = pcmk__current_node(rsc);
1069 :
1070 0 : if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
1071 0 : return pcmk_rc_no_output;
1072 : }
1073 :
1074 0 : CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive);
1075 :
1076 0 : if (node == NULL) {
1077 : // This is set only if a non-probe action is pending on this node
1078 0 : node = rsc->pending_node;
1079 : }
1080 0 : return pe__common_output_html(out, rsc, rsc_printable_id(rsc), node, show_opts);
1081 : }
1082 :
1083 : PCMK__OUTPUT_ARGS("primitive", "uint32_t", "pcmk_resource_t *", "GList *",
1084 : "GList *")
1085 : int
1086 0 : pe__resource_text(pcmk__output_t *out, va_list args)
1087 : {
1088 0 : uint32_t show_opts = va_arg(args, uint32_t);
1089 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
1090 0 : GList *only_node G_GNUC_UNUSED = va_arg(args, GList *);
1091 0 : GList *only_rsc = va_arg(args, GList *);
1092 :
1093 0 : const pcmk_node_t *node = pcmk__current_node(rsc);
1094 :
1095 0 : CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive);
1096 :
1097 0 : if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
1098 0 : return pcmk_rc_no_output;
1099 : }
1100 :
1101 0 : if (node == NULL) {
1102 : // This is set only if a non-probe action is pending on this node
1103 0 : node = rsc->pending_node;
1104 : }
1105 0 : return pe__common_output_text(out, rsc, rsc_printable_id(rsc), node, show_opts);
1106 : }
1107 :
1108 : void
1109 0 : native_free(pcmk_resource_t * rsc)
1110 : {
1111 0 : pcmk__rsc_trace(rsc, "Freeing resource action list (not the data)");
1112 0 : common_free(rsc);
1113 0 : }
1114 :
1115 : enum rsc_role_e
1116 0 : native_resource_state(const pcmk_resource_t * rsc, gboolean current)
1117 : {
1118 0 : enum rsc_role_e role = rsc->next_role;
1119 :
1120 0 : if (current) {
1121 0 : role = rsc->role;
1122 : }
1123 0 : pcmk__rsc_trace(rsc, "%s state: %s", rsc->id, pcmk_role_text(role));
1124 0 : return role;
1125 : }
1126 :
1127 : /*!
1128 : * \internal
1129 : * \brief List nodes where a resource (or any of its children) is
1130 : *
1131 : * \param[in] rsc Resource to check
1132 : * \param[out] list List to add result to
1133 : * \param[in] current 0 = where allocated, 1 = where running,
1134 : * 2 = where running or pending
1135 : *
1136 : * \return If list contains only one node, that node, or NULL otherwise
1137 : */
1138 : pcmk_node_t *
1139 0 : native_location(const pcmk_resource_t *rsc, GList **list, int current)
1140 : {
1141 : // @COMPAT: Accept a pcmk__rsc_node argument instead of int current
1142 0 : pcmk_node_t *one = NULL;
1143 0 : GList *result = NULL;
1144 :
1145 0 : if (rsc->children) {
1146 0 : GList *gIter = rsc->children;
1147 :
1148 0 : for (; gIter != NULL; gIter = gIter->next) {
1149 0 : pcmk_resource_t *child = (pcmk_resource_t *) gIter->data;
1150 :
1151 0 : child->fns->location(child, &result, current);
1152 : }
1153 :
1154 0 : } else if (current) {
1155 :
1156 0 : if (rsc->running_on) {
1157 0 : result = g_list_copy(rsc->running_on);
1158 : }
1159 0 : if ((current == 2) && rsc->pending_node
1160 0 : && !pe_find_node_id(result, rsc->pending_node->details->id)) {
1161 0 : result = g_list_append(result, rsc->pending_node);
1162 : }
1163 :
1164 0 : } else if (current == FALSE && rsc->allocated_to) {
1165 0 : result = g_list_append(NULL, rsc->allocated_to);
1166 : }
1167 :
1168 0 : if (result && (result->next == NULL)) {
1169 0 : one = result->data;
1170 : }
1171 :
1172 0 : if (list) {
1173 0 : GList *gIter = result;
1174 :
1175 0 : for (; gIter != NULL; gIter = gIter->next) {
1176 0 : pcmk_node_t *node = (pcmk_node_t *) gIter->data;
1177 :
1178 0 : if (*list == NULL || pe_find_node_id(*list, node->details->id) == NULL) {
1179 0 : *list = g_list_append(*list, node);
1180 : }
1181 : }
1182 : }
1183 :
1184 0 : g_list_free(result);
1185 0 : return one;
1186 : }
1187 :
1188 : static void
1189 0 : get_rscs_brief(GList *rsc_list, GHashTable * rsc_table, GHashTable * active_table)
1190 : {
1191 0 : GList *gIter = rsc_list;
1192 :
1193 0 : for (; gIter != NULL; gIter = gIter->next) {
1194 0 : pcmk_resource_t *rsc = (pcmk_resource_t *) gIter->data;
1195 :
1196 0 : const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS);
1197 0 : const char *kind = crm_element_value(rsc->xml, PCMK_XA_TYPE);
1198 :
1199 0 : int offset = 0;
1200 : char buffer[LINE_MAX];
1201 :
1202 0 : int *rsc_counter = NULL;
1203 0 : int *active_counter = NULL;
1204 :
1205 0 : if (rsc->variant != pcmk_rsc_variant_primitive) {
1206 0 : continue;
1207 : }
1208 :
1209 0 : offset += snprintf(buffer + offset, LINE_MAX - offset, "%s", class);
1210 0 : if (pcmk_is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider)) {
1211 0 : const char *prov = crm_element_value(rsc->xml, PCMK_XA_PROVIDER);
1212 :
1213 0 : if (prov != NULL) {
1214 0 : offset += snprintf(buffer + offset, LINE_MAX - offset,
1215 : PROVIDER_SEP "%s", prov);
1216 : }
1217 : }
1218 0 : offset += snprintf(buffer + offset, LINE_MAX - offset, ":%s", kind);
1219 0 : CRM_LOG_ASSERT(offset > 0);
1220 :
1221 0 : if (rsc_table) {
1222 0 : rsc_counter = g_hash_table_lookup(rsc_table, buffer);
1223 0 : if (rsc_counter == NULL) {
1224 0 : rsc_counter = pcmk__assert_alloc(1, sizeof(int));
1225 0 : *rsc_counter = 0;
1226 0 : g_hash_table_insert(rsc_table, strdup(buffer), rsc_counter);
1227 : }
1228 0 : (*rsc_counter)++;
1229 : }
1230 :
1231 0 : if (active_table) {
1232 0 : GList *gIter2 = rsc->running_on;
1233 :
1234 0 : for (; gIter2 != NULL; gIter2 = gIter2->next) {
1235 0 : pcmk_node_t *node = (pcmk_node_t *) gIter2->data;
1236 0 : GHashTable *node_table = NULL;
1237 :
1238 0 : if (node->details->unclean == FALSE && node->details->online == FALSE &&
1239 0 : pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
1240 0 : continue;
1241 : }
1242 :
1243 0 : node_table = g_hash_table_lookup(active_table, node->details->uname);
1244 0 : if (node_table == NULL) {
1245 0 : node_table = pcmk__strkey_table(free, free);
1246 0 : g_hash_table_insert(active_table, strdup(node->details->uname), node_table);
1247 : }
1248 :
1249 0 : active_counter = g_hash_table_lookup(node_table, buffer);
1250 0 : if (active_counter == NULL) {
1251 0 : active_counter = pcmk__assert_alloc(1, sizeof(int));
1252 0 : *active_counter = 0;
1253 0 : g_hash_table_insert(node_table, strdup(buffer), active_counter);
1254 : }
1255 0 : (*active_counter)++;
1256 : }
1257 : }
1258 : }
1259 0 : }
1260 :
1261 : static void
1262 0 : destroy_node_table(gpointer data)
1263 : {
1264 0 : GHashTable *node_table = data;
1265 :
1266 0 : if (node_table) {
1267 0 : g_hash_table_destroy(node_table);
1268 : }
1269 0 : }
1270 :
1271 : /*!
1272 : * \internal
1273 : * \deprecated This function will be removed in a future release
1274 : */
1275 : void
1276 0 : print_rscs_brief(GList *rsc_list, const char *pre_text, long options,
1277 : void *print_data, gboolean print_all)
1278 : {
1279 0 : GHashTable *rsc_table = pcmk__strkey_table(free, free);
1280 0 : GHashTable *active_table = pcmk__strkey_table(free, destroy_node_table);
1281 : GHashTableIter hash_iter;
1282 0 : char *type = NULL;
1283 0 : int *rsc_counter = NULL;
1284 :
1285 0 : get_rscs_brief(rsc_list, rsc_table, active_table);
1286 :
1287 0 : g_hash_table_iter_init(&hash_iter, rsc_table);
1288 0 : while (g_hash_table_iter_next(&hash_iter, (gpointer *)&type, (gpointer *)&rsc_counter)) {
1289 : GHashTableIter hash_iter2;
1290 0 : char *node_name = NULL;
1291 0 : GHashTable *node_table = NULL;
1292 0 : int active_counter_all = 0;
1293 :
1294 0 : g_hash_table_iter_init(&hash_iter2, active_table);
1295 0 : while (g_hash_table_iter_next(&hash_iter2, (gpointer *)&node_name, (gpointer *)&node_table)) {
1296 0 : int *active_counter = g_hash_table_lookup(node_table, type);
1297 :
1298 0 : if (active_counter == NULL || *active_counter == 0) {
1299 0 : continue;
1300 :
1301 : } else {
1302 0 : active_counter_all += *active_counter;
1303 : }
1304 :
1305 0 : if (options & pe_print_rsconly) {
1306 0 : node_name = NULL;
1307 : }
1308 :
1309 0 : if (options & pe_print_html) {
1310 0 : status_print("<li>\n");
1311 : }
1312 :
1313 0 : if (print_all) {
1314 0 : status_print("%s%d/%d\t(%s):\tActive %s\n", pre_text ? pre_text : "",
1315 : active_counter ? *active_counter : 0,
1316 : rsc_counter ? *rsc_counter : 0, type,
1317 : active_counter && (*active_counter > 0) && node_name ? node_name : "");
1318 : } else {
1319 0 : status_print("%s%d\t(%s):\tActive %s\n", pre_text ? pre_text : "",
1320 : active_counter ? *active_counter : 0, type,
1321 : active_counter && (*active_counter > 0) && node_name ? node_name : "");
1322 : }
1323 :
1324 0 : if (options & pe_print_html) {
1325 0 : status_print("</li>\n");
1326 : }
1327 : }
1328 :
1329 0 : if (print_all && active_counter_all == 0) {
1330 0 : if (options & pe_print_html) {
1331 0 : status_print("<li>\n");
1332 : }
1333 :
1334 0 : status_print("%s%d/%d\t(%s):\tActive\n", pre_text ? pre_text : "",
1335 : active_counter_all,
1336 : rsc_counter ? *rsc_counter : 0, type);
1337 :
1338 0 : if (options & pe_print_html) {
1339 0 : status_print("</li>\n");
1340 : }
1341 : }
1342 : }
1343 :
1344 0 : if (rsc_table) {
1345 0 : g_hash_table_destroy(rsc_table);
1346 0 : rsc_table = NULL;
1347 : }
1348 0 : if (active_table) {
1349 0 : g_hash_table_destroy(active_table);
1350 0 : active_table = NULL;
1351 : }
1352 0 : }
1353 :
1354 : int
1355 0 : pe__rscs_brief_output(pcmk__output_t *out, GList *rsc_list, uint32_t show_opts)
1356 : {
1357 0 : GHashTable *rsc_table = pcmk__strkey_table(free, free);
1358 0 : GHashTable *active_table = pcmk__strkey_table(free, destroy_node_table);
1359 : GList *sorted_rscs;
1360 0 : int rc = pcmk_rc_no_output;
1361 :
1362 0 : get_rscs_brief(rsc_list, rsc_table, active_table);
1363 :
1364 : /* Make a list of the rsc_table keys so that it can be sorted. This is to make sure
1365 : * output order stays consistent between systems.
1366 : */
1367 0 : sorted_rscs = g_hash_table_get_keys(rsc_table);
1368 0 : sorted_rscs = g_list_sort(sorted_rscs, (GCompareFunc) strcmp);
1369 :
1370 0 : for (GList *gIter = sorted_rscs; gIter; gIter = gIter->next) {
1371 0 : char *type = (char *) gIter->data;
1372 0 : int *rsc_counter = g_hash_table_lookup(rsc_table, type);
1373 :
1374 0 : GList *sorted_nodes = NULL;
1375 0 : int active_counter_all = 0;
1376 :
1377 : /* Also make a list of the active_table keys so it can be sorted. If there's
1378 : * more than one instance of a type of resource running, we need the nodes to
1379 : * be sorted to make sure output order stays consistent between systems.
1380 : */
1381 0 : sorted_nodes = g_hash_table_get_keys(active_table);
1382 0 : sorted_nodes = g_list_sort(sorted_nodes, (GCompareFunc) pcmk__numeric_strcasecmp);
1383 :
1384 0 : for (GList *gIter2 = sorted_nodes; gIter2; gIter2 = gIter2->next) {
1385 0 : char *node_name = (char *) gIter2->data;
1386 0 : GHashTable *node_table = g_hash_table_lookup(active_table, node_name);
1387 0 : int *active_counter = NULL;
1388 :
1389 0 : if (node_table == NULL) {
1390 0 : continue;
1391 : }
1392 :
1393 0 : active_counter = g_hash_table_lookup(node_table, type);
1394 :
1395 0 : if (active_counter == NULL || *active_counter == 0) {
1396 0 : continue;
1397 :
1398 : } else {
1399 0 : active_counter_all += *active_counter;
1400 : }
1401 :
1402 0 : if (pcmk_is_set(show_opts, pcmk_show_rsc_only)) {
1403 0 : node_name = NULL;
1404 : }
1405 :
1406 0 : if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
1407 0 : out->list_item(out, NULL, "%d/%d\t(%s):\tActive %s",
1408 : *active_counter,
1409 : rsc_counter ? *rsc_counter : 0, type,
1410 0 : (*active_counter > 0) && node_name ? node_name : "");
1411 : } else {
1412 0 : out->list_item(out, NULL, "%d\t(%s):\tActive %s",
1413 : *active_counter, type,
1414 0 : (*active_counter > 0) && node_name ? node_name : "");
1415 : }
1416 :
1417 0 : rc = pcmk_rc_ok;
1418 : }
1419 :
1420 0 : if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs) && active_counter_all == 0) {
1421 0 : out->list_item(out, NULL, "%d/%d\t(%s):\tActive",
1422 : active_counter_all,
1423 : rsc_counter ? *rsc_counter : 0, type);
1424 0 : rc = pcmk_rc_ok;
1425 : }
1426 :
1427 0 : if (sorted_nodes) {
1428 0 : g_list_free(sorted_nodes);
1429 : }
1430 : }
1431 :
1432 0 : if (rsc_table) {
1433 0 : g_hash_table_destroy(rsc_table);
1434 0 : rsc_table = NULL;
1435 : }
1436 0 : if (active_table) {
1437 0 : g_hash_table_destroy(active_table);
1438 0 : active_table = NULL;
1439 : }
1440 0 : if (sorted_rscs) {
1441 0 : g_list_free(sorted_rscs);
1442 : }
1443 :
1444 0 : return rc;
1445 : }
1446 :
1447 : gboolean
1448 0 : pe__native_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc,
1449 : gboolean check_parent)
1450 : {
1451 0 : if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
1452 0 : pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches)) {
1453 0 : return FALSE;
1454 0 : } else if (check_parent && rsc->parent) {
1455 0 : const pcmk_resource_t *up = pe__const_top_resource(rsc, true);
1456 :
1457 0 : return up->fns->is_filtered(up, only_rsc, FALSE);
1458 : }
1459 :
1460 0 : return TRUE;
1461 : }
1462 :
1463 : /*!
1464 : * \internal
1465 : * \brief Get maximum primitive resource instances per node
1466 : *
1467 : * \param[in] rsc Primitive resource to check
1468 : *
1469 : * \return Maximum number of \p rsc instances that can be active on one node
1470 : */
1471 : unsigned int
1472 0 : pe__primitive_max_per_node(const pcmk_resource_t *rsc)
1473 : {
1474 0 : CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_primitive));
1475 0 : return 1U;
1476 : }
|