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

   1 /*
   2  * Copyright 2004-2025 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdbool.h>                // bool, true, false
  13 #include <stdint.h>                 // uint32_t
  14 
  15 #include <crm/common/output.h>
  16 #include <crm/pengine/status.h>
  17 #include <crm/pengine/complex.h>
  18 #include <crm/pengine/internal.h>
  19 #include <crm/common/xml.h>
  20 #include <pe_status_private.h>
  21 
  22 /*!
  23  * \internal
  24  * \brief Check whether a resource is active on multiple nodes
  25  */
  26 static bool
  27 is_multiply_active(const pcmk_resource_t *rsc)
     /* [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) {
 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 bool
 205 native_unpack(pcmk_resource_t *rsc)
     /* [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, rsc->priv->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, uint32_t 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 bool
 325 native_active(const pcmk_resource_t *rsc, bool all)
     /* [previous][next][first][last][top][bottom][index][help] */
 326 {
 327     for (GList *gIter = rsc->priv->active_nodes;
 328          gIter != NULL; gIter = gIter->next) {
 329 
 330         pcmk_node_t *a_node = (pcmk_node_t *) gIter->data;
 331 
 332         if (a_node->details->unclean) {
 333             pcmk__rsc_trace(rsc, "Resource %s: %s is unclean",
 334                             rsc->id, pcmk__node_name(a_node));
 335             return TRUE;
 336         } else if (!a_node->details->online
 337                    && pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
 338             pcmk__rsc_trace(rsc, "Resource %s: %s is offline",
 339                             rsc->id, pcmk__node_name(a_node));
 340         } else {
 341             pcmk__rsc_trace(rsc, "Resource %s active on %s",
 342                             rsc->id, pcmk__node_name(a_node));
 343             return TRUE;
 344         }
 345     }
 346     return FALSE;
 347 }
 348 
 349 struct print_data_s {
 350     long options;
 351     void *print_data;
 352 };
 353 
 354 static const char *
 355 native_pending_state(const pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 356 {
 357     const char *pending_state = NULL;
 358 
 359     if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_START,
 360                      pcmk__str_none)) {
 361         pending_state = "Starting";
 362 
 363     } else if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_STOP,
 364                             pcmk__str_none)) {
 365         pending_state = "Stopping";
 366 
 367     } else if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_MIGRATE_TO,
 368                             pcmk__str_none)) {
 369         pending_state = "Migrating";
 370 
 371     } else if (pcmk__str_eq(rsc->priv->pending_action,
 372                             PCMK_ACTION_MIGRATE_FROM, pcmk__str_none)) {
 373        /* Work might be done in here. */
 374         pending_state = "Migrating";
 375 
 376     } else if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_PROMOTE,
 377                             pcmk__str_none)) {
 378         pending_state = "Promoting";
 379 
 380     } else if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_DEMOTE,
 381                             pcmk__str_none)) {
 382         pending_state = "Demoting";
 383     }
 384 
 385     return pending_state;
 386 }
 387 
 388 static const char *
 389 native_pending_action(const pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 390 {
 391     const char *pending_action = NULL;
 392 
 393     if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_MONITOR,
 394                      pcmk__str_none)) {
 395         pending_action = "Monitoring";
 396 
 397     /* Pending probes are not printed, even if pending
 398      * operations are requested. If someone ever requests that
 399      * behavior, uncomment this and the corresponding part of
 400      * unpack.c:unpack_rsc_op().
 401      */
 402 #if 0
 403     } else if (pcmk__str_eq(rsc->private->pending_action, "probe",
 404                             pcmk__str_none)) {
 405         pending_action = "Checking";
 406 #endif
 407     }
 408 
 409     return pending_action;
 410 }
 411 
 412 static enum rsc_role_e
 413 native_displayable_role(const pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 414 {
 415     enum rsc_role_e role = rsc->priv->orig_role;
 416 
 417     if ((role == pcmk_role_started)
 418         && pcmk_is_set(pe__const_top_resource(rsc, false)->flags,
 419                        pcmk__rsc_promotable)) {
 420 
 421         role = pcmk_role_unpromoted;
 422     }
 423     return role;
 424 }
 425 
 426 static const char *
 427 native_displayable_state(const pcmk_resource_t *rsc, bool print_pending)
     /* [previous][next][first][last][top][bottom][index][help] */
 428 {
 429     const char *rsc_state = NULL;
 430 
 431     if (print_pending) {
 432         rsc_state = native_pending_state(rsc);
 433     }
 434     if (rsc_state == NULL) {
 435         rsc_state = pcmk_role_text(native_displayable_role(rsc));
 436     }
 437     return rsc_state;
 438 }
 439 
 440 // Append a flag to resource description string's flags list
 441 static bool
 442 add_output_flag(GString *s, const char *flag_desc, bool have_flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 443 {
 444     g_string_append(s, (have_flags? ", " : " ("));
 445     g_string_append(s, flag_desc);
 446     return true;
 447 }
 448 
 449 // Append a node name to resource description string's node list
 450 static bool
 451 add_output_node(GString *s, const char *node, bool have_nodes)
     /* [previous][next][first][last][top][bottom][index][help] */
 452 {
 453     g_string_append(s, (have_nodes? " " : " [ "));
 454     g_string_append(s, node);
 455     return true;
 456 }
 457 
 458 /*!
 459  * \internal
 460  * \brief Create a string description of a resource
 461  *
 462  * \param[in] rsc          Resource to describe
 463  * \param[in] name         Desired identifier for the resource
 464  * \param[in] node         If not NULL, node that resource is "on"
 465  * \param[in] show_opts    Bitmask of pcmk_show_opt_e.
 466  * \param[in] target_role  Resource's target role
 467  * \param[in] show_nodes   Whether to display nodes when multiply active
 468  *
 469  * \return Newly allocated string description of resource
 470  * \note Caller must free the result with g_free().
 471  */
 472 gchar *
 473 pcmk__native_output_string(const pcmk_resource_t *rsc, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 474                            const pcmk_node_t *node, uint32_t show_opts,
 475                            const char *target_role, bool show_nodes)
 476 {
 477     const char *class = crm_element_value(rsc->priv->xml, PCMK_XA_CLASS);
 478     const char *provider = NULL;
 479     const char *kind = crm_element_value(rsc->priv->xml, PCMK_XA_TYPE);
 480     GString *outstr = NULL;
 481     bool have_flags = false;
 482 
 483     if (!pcmk__is_primitive(rsc)) {
 484         return NULL;
 485     }
 486 
 487     CRM_CHECK(name != NULL, name = "unknown");
 488     CRM_CHECK(kind != NULL, kind = "unknown");
 489     CRM_CHECK(class != NULL, class = "unknown");
 490 
 491     if (pcmk_is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider)) {
 492         provider = crm_element_value(rsc->priv->xml, PCMK_XA_PROVIDER);
 493     }
 494 
 495     if ((node == NULL) && (rsc->priv->lock_node != NULL)) {
 496         node = rsc->priv->lock_node;
 497     }
 498     if (pcmk_any_flags_set(show_opts, pcmk_show_rsc_only)
 499         || pcmk__list_of_multiple(rsc->priv->active_nodes)) {
 500         node = NULL;
 501     }
 502 
 503     outstr = g_string_sized_new(128);
 504 
 505     // Resource name and agent
 506     pcmk__g_strcat(outstr,
 507                    name, "\t(", class, ((provider == NULL)? "" : ":"),
 508                    pcmk__s(provider, ""), ":", kind, "):\t", NULL);
 509 
 510     // State on node
 511     if (pcmk_is_set(rsc->flags, pcmk__rsc_removed)) {
 512         g_string_append(outstr, " ORPHANED");
 513     }
 514     if (pcmk_is_set(rsc->flags, pcmk__rsc_failed)) {
 515         enum rsc_role_e role = native_displayable_role(rsc);
 516 
 517         g_string_append(outstr, " FAILED");
 518         if (role > pcmk_role_unpromoted) {
 519             pcmk__add_word(&outstr, 0, pcmk_role_text(role));
 520         }
 521     } else {
 522         bool show_pending = pcmk_is_set(show_opts, pcmk_show_pending);
 523 
 524         pcmk__add_word(&outstr, 0, native_displayable_state(rsc, show_pending));
 525     }
 526     if (node) {
 527         pcmk__add_word(&outstr, 0, pcmk__node_name(node));
 528     }
 529 
 530     // Failed probe operation
 531     if (native_displayable_role(rsc) == pcmk_role_stopped) {
 532         xmlNode *probe_op = pe__failed_probe_for_rsc(rsc,
 533                                                      node ? node->priv->name : NULL);
 534         if (probe_op != NULL) {
 535             int rc;
 536 
 537             pcmk__scan_min_int(crm_element_value(probe_op, PCMK__XA_RC_CODE),
 538                                &rc, 0);
 539             pcmk__g_strcat(outstr, " (", crm_exit_str(rc), ") ", NULL);
 540         }
 541     }
 542 
 543     // Flags, as: (<flag> [...])
 544     if (node && !(node->details->online) && node->details->unclean) {
 545         have_flags = add_output_flag(outstr, "UNCLEAN", have_flags);
 546     }
 547     if ((node != NULL) && pcmk__same_node(node, rsc->priv->lock_node)) {
 548         have_flags = add_output_flag(outstr, "LOCKED", have_flags);
 549     }
 550     if (pcmk_is_set(show_opts, pcmk_show_pending)) {
 551         const char *pending_action = native_pending_action(rsc);
 552 
 553         if (pending_action != NULL) {
 554             have_flags = add_output_flag(outstr, pending_action, have_flags);
 555         }
 556     }
 557     if (target_role != NULL) {
 558         switch (pcmk_parse_role(target_role)) {
 559             case pcmk_role_unknown:
 560                 pcmk__config_err("Invalid " PCMK_META_TARGET_ROLE
 561                                  " %s for resource %s", target_role, rsc->id);
 562                 break;
 563 
 564             case pcmk_role_stopped:
 565                 have_flags = add_output_flag(outstr, "disabled", have_flags);
 566                 break;
 567 
 568             case pcmk_role_unpromoted:
 569                 if (pcmk_is_set(pe__const_top_resource(rsc, false)->flags,
 570                                 pcmk__rsc_promotable)) {
 571                     have_flags = add_output_flag(outstr,
 572                                                  PCMK_META_TARGET_ROLE ":",
 573                                                  have_flags);
 574                     g_string_append(outstr, target_role);
 575                 }
 576                 break;
 577 
 578             default:
 579                 /* Only show target role if it limits our abilities (i.e. ignore
 580                  * Started, as it is the default anyways, and doesn't prevent
 581                  * the resource from becoming promoted).
 582                  */
 583                 break;
 584         }
 585     }
 586 
 587     // Blocked or maintenance implies unmanaged
 588     if (pcmk_any_flags_set(rsc->flags,
 589                            pcmk__rsc_blocked|pcmk__rsc_maintenance)) {
 590         if (pcmk_is_set(rsc->flags, pcmk__rsc_blocked)) {
 591             have_flags = add_output_flag(outstr, "blocked", have_flags);
 592 
 593         } else if (pcmk_is_set(rsc->flags, pcmk__rsc_maintenance)) {
 594             have_flags = add_output_flag(outstr, "maintenance", have_flags);
 595         }
 596     } else if (!pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
 597         have_flags = add_output_flag(outstr, "unmanaged", have_flags);
 598     }
 599 
 600     if (pcmk_is_set(rsc->flags, pcmk__rsc_ignore_failure)) {
 601         have_flags = add_output_flag(outstr, "failure ignored", have_flags);
 602     }
 603 
 604 
 605     if (have_flags) {
 606         g_string_append_c(outstr, ')');
 607     }
 608 
 609     // User-supplied description
 610     if (pcmk_any_flags_set(show_opts, pcmk_show_rsc_only|pcmk_show_description)
 611         || pcmk__list_of_multiple(rsc->priv->active_nodes)) {
 612         const char *desc = crm_element_value(rsc->priv->xml,
 613                                              PCMK_XA_DESCRIPTION);
 614 
 615         if (desc) {
 616             g_string_append(outstr, " (");
 617             g_string_append(outstr, desc);
 618             g_string_append(outstr, ")");
 619 
 620         }
 621     }
 622 
 623     if (show_nodes && !pcmk_is_set(show_opts, pcmk_show_rsc_only)
 624         && pcmk__list_of_multiple(rsc->priv->active_nodes)) {
 625         bool have_nodes = false;
 626 
 627         for (GList *iter = rsc->priv->active_nodes;
 628              iter != NULL; iter = iter->next) {
 629 
 630             pcmk_node_t *n = (pcmk_node_t *) iter->data;
 631 
 632             have_nodes = add_output_node(outstr, n->priv->name, have_nodes);
 633         }
 634         if (have_nodes) {
 635             g_string_append(outstr, " ]");
 636         }
 637     }
 638 
 639     return g_string_free(outstr, FALSE);
 640 }
 641 
 642 int
 643 pe__common_output_html(pcmk__output_t *out, const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 644                        const char *name, const pcmk_node_t *node,
 645                        uint32_t show_opts)
 646 {
 647     const char *kind = crm_element_value(rsc->priv->xml, PCMK_XA_TYPE);
 648     const char *target_role = NULL;
 649     const char *cl = NULL;
 650 
 651     xmlNode *child = NULL;
 652     gchar *content = NULL;
 653 
 654     pcmk__assert((kind != NULL) && pcmk__is_primitive(rsc));
 655 
 656     if (crm_is_true(g_hash_table_lookup(rsc->priv->meta,
 657                                         PCMK__META_INTERNAL_RSC))
 658         && !pcmk_is_set(show_opts, pcmk_show_implicit_rscs)) {
 659 
 660         crm_trace("skipping print of internal resource %s", rsc->id);
 661         return pcmk_rc_no_output;
 662     }
 663     target_role = g_hash_table_lookup(rsc->priv->meta,
 664                                       PCMK_META_TARGET_ROLE);
 665 
 666     if (!pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
 667         cl = PCMK__VALUE_RSC_MANAGED;
 668 
 669     } else if (pcmk_is_set(rsc->flags, pcmk__rsc_failed)) {
 670         cl = PCMK__VALUE_RSC_FAILED;
 671 
 672     } else if (pcmk__is_primitive(rsc)
 673                && (rsc->priv->active_nodes == NULL)) {
 674         cl = PCMK__VALUE_RSC_FAILED;
 675 
 676     } else if (pcmk__list_of_multiple(rsc->priv->active_nodes)) {
 677         cl = PCMK__VALUE_RSC_MULTIPLE;
 678 
 679     } else if (pcmk_is_set(rsc->flags, pcmk__rsc_ignore_failure)) {
 680         cl = PCMK__VALUE_RSC_FAILURE_IGNORED;
 681 
 682     } else {
 683         cl = PCMK__VALUE_RSC_OK;
 684     }
 685 
 686     child = pcmk__output_create_html_node(out, "li", NULL, NULL, NULL);
 687     child = pcmk__html_create(child, PCMK__XE_SPAN, NULL, cl);
 688     content = pcmk__native_output_string(rsc, name, node, show_opts,
 689                                          target_role, true);
 690     pcmk__xe_set_content(child, "%s", content);
 691     g_free(content);
 692 
 693     return pcmk_rc_ok;
 694 }
 695 
 696 int
 697 pe__common_output_text(pcmk__output_t *out, const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 698                        const char *name, const pcmk_node_t *node,
 699                        uint32_t show_opts)
 700 {
 701     const char *target_role = NULL;
 702 
 703     pcmk__assert(pcmk__is_primitive(rsc));
 704 
 705     if (crm_is_true(g_hash_table_lookup(rsc->priv->meta,
 706                                         PCMK__META_INTERNAL_RSC))
 707         && !pcmk_is_set(show_opts, pcmk_show_implicit_rscs)) {
 708 
 709         crm_trace("skipping print of internal resource %s", rsc->id);
 710         return pcmk_rc_no_output;
 711     }
 712     target_role = g_hash_table_lookup(rsc->priv->meta,
 713                                       PCMK_META_TARGET_ROLE);
 714 
 715     {
 716         gchar *s = pcmk__native_output_string(rsc, name, node, show_opts,
 717                                               target_role, true);
 718 
 719         out->list_item(out, NULL, "%s", s);
 720         g_free(s);
 721     }
 722 
 723     return pcmk_rc_ok;
 724 }
 725 
 726 PCMK__OUTPUT_ARGS("primitive", "uint32_t", "pcmk_resource_t *", "GList *",
     /* [previous][next][first][last][top][bottom][index][help] */
 727                   "GList *")
 728 int
 729 pe__resource_xml(pcmk__output_t *out, va_list args)
 730 {
 731     uint32_t show_opts = va_arg(args, uint32_t);
 732     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
 733     GList *only_node G_GNUC_UNUSED = va_arg(args, GList *);
 734     GList *only_rsc = va_arg(args, GList *);
 735 
 736     int rc = pcmk_rc_no_output;
 737     bool print_pending = pcmk_is_set(show_opts, pcmk_show_pending);
 738     const char *class = crm_element_value(rsc->priv->xml, PCMK_XA_CLASS);
 739     const char *prov = crm_element_value(rsc->priv->xml, PCMK_XA_PROVIDER);
 740 
 741     char ra_name[LINE_MAX];
 742     const char *rsc_state = native_displayable_state(rsc, print_pending);
 743     const char *target_role = NULL;
 744     const char *active = pcmk__btoa(rsc->priv->fns->active(rsc, true));
 745     const char *orphaned = pcmk__flag_text(rsc->flags, pcmk__rsc_removed);
 746     const char *blocked = pcmk__flag_text(rsc->flags, pcmk__rsc_blocked);
 747     const char *maintenance = pcmk__flag_text(rsc->flags,
 748                                               pcmk__rsc_maintenance);
 749     const char *managed = pcmk__flag_text(rsc->flags, pcmk__rsc_managed);
 750     const char *failed = pcmk__flag_text(rsc->flags, pcmk__rsc_failed);
 751     const char *ignored = pcmk__flag_text(rsc->flags, pcmk__rsc_ignore_failure);
 752     char *nodes_running_on = NULL;
 753     const char *pending = print_pending? native_pending_action(rsc) : NULL;
 754     const char *locked_to = NULL;
 755     const char *desc = pe__resource_description(rsc, show_opts);
 756 
 757     pcmk__assert(pcmk__is_primitive(rsc));
 758 
 759     if (rsc->priv->fns->is_filtered(rsc, only_rsc, true)) {
 760         return pcmk_rc_no_output;
 761     }
 762 
 763     // Resource information
 764     snprintf(ra_name, LINE_MAX, "%s%s%s:%s", class,
 765              ((prov == NULL)? "" : ":"), ((prov == NULL)? "" : prov),
 766              crm_element_value(rsc->priv->xml, PCMK_XA_TYPE));
 767 
 768     target_role = g_hash_table_lookup(rsc->priv->meta,
 769                                       PCMK_META_TARGET_ROLE);
 770 
 771     nodes_running_on = pcmk__itoa(g_list_length(rsc->priv->active_nodes));
 772 
 773     if (rsc->priv->lock_node != NULL) {
 774         locked_to = rsc->priv->lock_node->priv->name;
 775     }
 776 
 777     rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_RESOURCE,
 778                                   PCMK_XA_ID, rsc_printable_id(rsc),
 779                                   PCMK_XA_RESOURCE_AGENT, ra_name,
 780                                   PCMK_XA_ROLE, rsc_state,
 781                                   PCMK_XA_TARGET_ROLE, target_role,
 782                                   PCMK_XA_ACTIVE, active,
 783                                   PCMK_XA_ORPHANED, orphaned,
 784                                   PCMK_XA_BLOCKED, blocked,
 785                                   PCMK_XA_MAINTENANCE, maintenance,
 786                                   PCMK_XA_MANAGED, managed,
 787                                   PCMK_XA_FAILED, failed,
 788                                   PCMK_XA_FAILURE_IGNORED, ignored,
 789                                   PCMK_XA_NODES_RUNNING_ON, nodes_running_on,
 790                                   PCMK_XA_PENDING, pending,
 791                                   PCMK_XA_LOCKED_TO, locked_to,
 792                                   PCMK_XA_DESCRIPTION, desc,
 793                                   NULL);
 794     free(nodes_running_on);
 795 
 796     pcmk__assert(rc == pcmk_rc_ok);
 797 
 798     for (GList *gIter = rsc->priv->active_nodes;
 799          gIter != NULL; gIter = gIter->next) {
 800 
 801         pcmk_node_t *node = (pcmk_node_t *) gIter->data;
 802         const char *cached = pcmk__btoa(node->details->online);
 803 
 804         rc = pe__name_and_nvpairs_xml(out, false, PCMK_XE_NODE,
 805                                       PCMK_XA_NAME, node->priv->name,
 806                                       PCMK_XA_ID, node->priv->id,
 807                                       PCMK_XA_CACHED, cached,
 808                                       NULL);
 809         pcmk__assert(rc == pcmk_rc_ok);
 810     }
 811 
 812     pcmk__output_xml_pop_parent(out);
 813     return rc;
 814 }
 815 
 816 PCMK__OUTPUT_ARGS("primitive", "uint32_t", "pcmk_resource_t *", "GList *",
     /* [previous][next][first][last][top][bottom][index][help] */
 817                   "GList *")
 818 int
 819 pe__resource_html(pcmk__output_t *out, va_list args)
 820 {
 821     uint32_t show_opts = va_arg(args, uint32_t);
 822     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
 823     GList *only_node G_GNUC_UNUSED = va_arg(args, GList *);
 824     GList *only_rsc = va_arg(args, GList *);
 825 
 826     const pcmk_node_t *node = pcmk__current_node(rsc);
 827 
 828     if (rsc->priv->fns->is_filtered(rsc, only_rsc, true)) {
 829         return pcmk_rc_no_output;
 830     }
 831 
 832     pcmk__assert(pcmk__is_primitive(rsc));
 833 
 834     if (node == NULL) {
 835         // This is set only if a non-probe action is pending on this node
 836         node = rsc->priv->pending_node;
 837     }
 838     return pe__common_output_html(out, rsc, rsc_printable_id(rsc), node, show_opts);
 839 }
 840 
 841 PCMK__OUTPUT_ARGS("primitive", "uint32_t", "pcmk_resource_t *", "GList *",
     /* [previous][next][first][last][top][bottom][index][help] */
 842                   "GList *")
 843 int
 844 pe__resource_text(pcmk__output_t *out, va_list args)
 845 {
 846     uint32_t show_opts = va_arg(args, uint32_t);
 847     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
 848     GList *only_node G_GNUC_UNUSED = va_arg(args, GList *);
 849     GList *only_rsc = va_arg(args, GList *);
 850 
 851     const pcmk_node_t *node = pcmk__current_node(rsc);
 852 
 853     pcmk__assert(pcmk__is_primitive(rsc));
 854 
 855     if (rsc->priv->fns->is_filtered(rsc, only_rsc, true)) {
 856         return pcmk_rc_no_output;
 857     }
 858 
 859     if (node == NULL) {
 860         // This is set only if a non-probe action is pending on this node
 861         node = rsc->priv->pending_node;
 862     }
 863     return pe__common_output_text(out, rsc, rsc_printable_id(rsc), node, show_opts);
 864 }
 865 
 866 void
 867 native_free(pcmk_resource_t * rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 868 {
 869     pcmk__rsc_trace(rsc, "Freeing resource action list (not the data)");
 870     common_free(rsc);
 871 }
 872 
 873 enum rsc_role_e
 874 native_resource_state(const pcmk_resource_t *rsc, bool current)
     /* [previous][next][first][last][top][bottom][index][help] */
 875 {
 876     enum rsc_role_e role = rsc->priv->next_role;
 877 
 878     if (current) {
 879         role = rsc->priv->orig_role;
 880     }
 881     pcmk__rsc_trace(rsc, "%s state: %s", rsc->id, pcmk_role_text(role));
 882     return role;
 883 }
 884 
 885 /*!
 886  * \internal
 887  * \brief List nodes where a resource (or any of its children) is
 888  *
 889  * \param[in]  rsc      Resource to check
 890  * \param[out] list     List to add result to
 891  * \param[in]  target   Which resource conditions to target (group of
 892  *                      enum pcmk__rsc_node flags)
 893  *
 894  * \return If list contains only one node, that node, or NULL otherwise
 895  */
 896 pcmk_node_t *
 897 native_location(const pcmk_resource_t *rsc, GList **list, uint32_t target)
     /* [previous][next][first][last][top][bottom][index][help] */
 898 {
 899     pcmk_node_t *one = NULL;
 900     GList *result = NULL;
 901 
 902     if (rsc->priv->children != NULL) {
 903 
 904         for (GList *gIter = rsc->priv->children;
 905              gIter != NULL; gIter = gIter->next) {
 906 
 907             pcmk_resource_t *child = (pcmk_resource_t *) gIter->data;
 908 
 909             child->priv->fns->location(child, &result, target);
 910         }
 911 
 912     } else {
 913         if (pcmk_is_set(target, pcmk__rsc_node_current)) {
 914             result = g_list_copy(rsc->priv->active_nodes);
 915         }
 916         if (pcmk_is_set(target, pcmk__rsc_node_pending)
 917             && (rsc->priv->pending_node != NULL)
 918             && !pe_find_node_id(result, rsc->priv->pending_node->priv->id)) {
 919             result = g_list_append(result, (gpointer) rsc->priv->pending_node);
 920         }
 921         if (pcmk_is_set(target, pcmk__rsc_node_assigned)
 922             && (rsc->priv->assigned_node != NULL)) {
 923             result = g_list_append(result, rsc->priv->assigned_node);
 924         }
 925     }
 926 
 927     if (result && (result->next == NULL)) {
 928         one = result->data;
 929     }
 930 
 931     if (list) {
 932         GList *gIter = result;
 933 
 934         for (; gIter != NULL; gIter = gIter->next) {
 935             pcmk_node_t *node = (pcmk_node_t *) gIter->data;
 936 
 937             if ((*list == NULL)
 938                 || (pe_find_node_id(*list, node->priv->id) == NULL)) {
 939                 *list = g_list_append(*list, node);
 940             }
 941         }
 942     }
 943 
 944     g_list_free(result);
 945     return one;
 946 }
 947 
 948 static void
 949 get_rscs_brief(GList *rsc_list, GHashTable * rsc_table, GHashTable * active_table)
     /* [previous][next][first][last][top][bottom][index][help] */
 950 {
 951     GList *gIter = rsc_list;
 952 
 953     for (; gIter != NULL; gIter = gIter->next) {
 954         pcmk_resource_t *rsc = (pcmk_resource_t *) gIter->data;
 955 
 956         const char *class = crm_element_value(rsc->priv->xml, PCMK_XA_CLASS);
 957         const char *kind = crm_element_value(rsc->priv->xml, PCMK_XA_TYPE);
 958 
 959         int offset = 0;
 960         char buffer[LINE_MAX];
 961 
 962         int *rsc_counter = NULL;
 963         int *active_counter = NULL;
 964 
 965         if (!pcmk__is_primitive(rsc)) {
 966             continue;
 967         }
 968 
 969         offset += snprintf(buffer + offset, LINE_MAX - offset, "%s", class);
 970         if (pcmk_is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider)) {
 971             const char *prov = crm_element_value(rsc->priv->xml,
 972                                                  PCMK_XA_PROVIDER);
 973 
 974             if (prov != NULL) {
 975                 offset += snprintf(buffer + offset, LINE_MAX - offset,
 976                                    ":%s", prov);
 977             }
 978         }
 979         offset += snprintf(buffer + offset, LINE_MAX - offset, ":%s", kind);
 980         CRM_LOG_ASSERT(offset > 0);
 981 
 982         if (rsc_table) {
 983             rsc_counter = g_hash_table_lookup(rsc_table, buffer);
 984             if (rsc_counter == NULL) {
 985                 rsc_counter = pcmk__assert_alloc(1, sizeof(int));
 986                 *rsc_counter = 0;
 987                 g_hash_table_insert(rsc_table, strdup(buffer), rsc_counter);
 988             }
 989             (*rsc_counter)++;
 990         }
 991 
 992         if (active_table) {
 993             for (GList *gIter2 = rsc->priv->active_nodes;
 994                  gIter2 != NULL; gIter2 = gIter2->next) {
 995 
 996                 pcmk_node_t *node = (pcmk_node_t *) gIter2->data;
 997                 GHashTable *node_table = NULL;
 998 
 999                 if (node->details->unclean == FALSE && node->details->online == FALSE &&
1000                     pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
1001                     continue;
1002                 }
1003 
1004                 node_table = g_hash_table_lookup(active_table,
1005                                                  node->priv->name);
1006                 if (node_table == NULL) {
1007                     node_table = pcmk__strkey_table(free, free);
1008                     g_hash_table_insert(active_table,
1009                                         strdup(node->priv->name),
1010                                         node_table);
1011                 }
1012 
1013                 active_counter = g_hash_table_lookup(node_table, buffer);
1014                 if (active_counter == NULL) {
1015                     active_counter = pcmk__assert_alloc(1, sizeof(int));
1016                     *active_counter = 0;
1017                     g_hash_table_insert(node_table, strdup(buffer), active_counter);
1018                 }
1019                 (*active_counter)++;
1020             }
1021         }
1022     }
1023 }
1024 
1025 static void
1026 destroy_node_table(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
1027 {
1028     GHashTable *node_table = data;
1029 
1030     if (node_table) {
1031         g_hash_table_destroy(node_table);
1032     }
1033 }
1034 
1035 int
1036 pe__rscs_brief_output(pcmk__output_t *out, GList *rsc_list, uint32_t show_opts)
     /* [previous][next][first][last][top][bottom][index][help] */
1037 {
1038     GHashTable *rsc_table = pcmk__strkey_table(free, free);
1039     GHashTable *active_table = pcmk__strkey_table(free, destroy_node_table);
1040     GList *sorted_rscs;
1041     int rc = pcmk_rc_no_output;
1042 
1043     get_rscs_brief(rsc_list, rsc_table, active_table);
1044 
1045     /* Make a list of the rsc_table keys so that it can be sorted.  This is to make sure
1046      * output order stays consistent between systems.
1047      */
1048     sorted_rscs = g_hash_table_get_keys(rsc_table);
1049     sorted_rscs = g_list_sort(sorted_rscs, (GCompareFunc) strcmp);
1050 
1051     for (GList *gIter = sorted_rscs; gIter; gIter = gIter->next) {
1052         char *type = (char *) gIter->data;
1053         int *rsc_counter = g_hash_table_lookup(rsc_table, type);
1054 
1055         GList *sorted_nodes = NULL;
1056         int active_counter_all = 0;
1057 
1058         /* Also make a list of the active_table keys so it can be sorted.  If there's
1059          * more than one instance of a type of resource running, we need the nodes to
1060          * be sorted to make sure output order stays consistent between systems.
1061          */
1062         sorted_nodes = g_hash_table_get_keys(active_table);
1063         sorted_nodes = g_list_sort(sorted_nodes, (GCompareFunc) pcmk__numeric_strcasecmp);
1064 
1065         for (GList *gIter2 = sorted_nodes; gIter2; gIter2 = gIter2->next) {
1066             char *node_name = (char *) gIter2->data;
1067             GHashTable *node_table = g_hash_table_lookup(active_table, node_name);
1068             int *active_counter = NULL;
1069 
1070             if (node_table == NULL) {
1071                 continue;
1072             }
1073 
1074             active_counter = g_hash_table_lookup(node_table, type);
1075 
1076             if (active_counter == NULL || *active_counter == 0) {
1077                 continue;
1078 
1079             } else {
1080                 active_counter_all += *active_counter;
1081             }
1082 
1083             if (pcmk_is_set(show_opts, pcmk_show_rsc_only)) {
1084                 node_name = NULL;
1085             }
1086 
1087             if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
1088                 out->list_item(out, NULL, "%d/%d\t(%s):\tActive %s",
1089                                *active_counter,
1090                                rsc_counter ? *rsc_counter : 0, type,
1091                                (*active_counter > 0) && node_name ? node_name : "");
1092             } else {
1093                 out->list_item(out, NULL, "%d\t(%s):\tActive %s",
1094                                *active_counter, type,
1095                                (*active_counter > 0) && node_name ? node_name : "");
1096             }
1097 
1098             rc = pcmk_rc_ok;
1099         }
1100 
1101         if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs) && active_counter_all == 0) {
1102             out->list_item(out, NULL, "%d/%d\t(%s):\tActive",
1103                            active_counter_all,
1104                            rsc_counter ? *rsc_counter : 0, type);
1105             rc = pcmk_rc_ok;
1106         }
1107 
1108         if (sorted_nodes) {
1109             g_list_free(sorted_nodes);
1110         }
1111     }
1112 
1113     if (rsc_table) {
1114         g_hash_table_destroy(rsc_table);
1115         rsc_table = NULL;
1116     }
1117     if (active_table) {
1118         g_hash_table_destroy(active_table);
1119         active_table = NULL;
1120     }
1121     if (sorted_rscs) {
1122         g_list_free(sorted_rscs);
1123     }
1124 
1125     return rc;
1126 }
1127 
1128 bool
1129 pe__native_is_filtered(const pcmk_resource_t *rsc, const GList *only_rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
1130                        bool check_parent)
1131 {
1132     if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
1133         pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches)) {
1134         return false;
1135     } else if (check_parent && (rsc->priv->parent != NULL)) {
1136         const pcmk_resource_t *up = pe__const_top_resource(rsc, true);
1137 
1138         return up->priv->fns->is_filtered(up, only_rsc, false);
1139     }
1140 
1141     return true;
1142 }
1143 
1144 /*!
1145  * \internal
1146  * \brief Get maximum primitive resource instances per node
1147  *
1148  * \param[in] rsc  Primitive resource to check
1149  *
1150  * \return Maximum number of \p rsc instances that can be active on one node
1151  */
1152 unsigned int
1153 pe__primitive_max_per_node(const pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1154 {
1155     pcmk__assert(pcmk__is_primitive(rsc));
1156     return 1U;
1157 }

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