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_print_attr
  11. native_pending_state
  12. native_pending_task
  13. native_displayable_role
  14. native_displayable_state
  15. native_print_xml
  16. add_output_flag
  17. add_output_node
  18. native_output_string
  19. pe__common_output_html
  20. pe__common_output_text
  21. common_print
  22. native_print
  23. PCMK__OUTPUT_ARGS
  24. PCMK__OUTPUT_ARGS
  25. PCMK__OUTPUT_ARGS
  26. native_free
  27. native_resource_state
  28. native_location
  29. get_rscs_brief
  30. destroy_node_table
  31. print_rscs_brief
  32. pe__rscs_brief_output
  33. pe__native_is_filtered

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

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