root/lib/pengine/native.c

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

DEFINITIONS

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

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