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