root/lib/pengine/native.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. is_multiply_active
  2. native_priority_to_node
  3. native_add_running
  4. recursive_clear_unique
  5. native_unpack
  6. rsc_is_on_node
  7. native_find_rsc
  8. native_parameter
  9. native_active
  10. native_pending_state
  11. native_pending_task
  12. native_displayable_role
  13. native_displayable_state
  14. native_print_xml
  15. add_output_flag
  16. add_output_node
  17. pcmk__native_output_string
  18. pe__common_output_html
  19. pe__common_output_text
  20. common_print
  21. native_print
  22. PCMK__OUTPUT_ARGS
  23. PCMK__OUTPUT_ARGS
  24. PCMK__OUTPUT_ARGS
  25. native_free
  26. native_resource_state
  27. native_location
  28. get_rscs_brief
  29. destroy_node_table
  30. print_rscs_brief
  31. pe__rscs_brief_output
  32. pe__native_is_filtered

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

/* [previous][next][first][last][top][bottom][index][help] */