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_action
  12. native_displayable_role
  13. native_displayable_state
  14. add_output_flag
  15. add_output_node
  16. pcmk__native_output_string
  17. pe__common_output_html
  18. pe__common_output_text
  19. PCMK__OUTPUT_ARGS
  20. PCMK__OUTPUT_ARGS
  21. PCMK__OUTPUT_ARGS
  22. native_free
  23. native_resource_state
  24. native_location
  25. get_rscs_brief
  26. destroy_node_table
  27. pe__rscs_brief_output
  28. pe__native_is_filtered
  29. pe__primitive_max_per_node

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

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