root/lib/pengine/complex.c

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

DEFINITIONS

This source file includes following definitions.
  1. get_resource_type
  2. dup_attr
  3. expand_parents_fixed_nvpairs
  4. get_meta_attributes
  5. get_rsc_attributes
  6. template_op_key
  7. unpack_template
  8. add_template_rsc
  9. detect_promotable
  10. free_params_table
  11. pe_rsc_params
  12. unpack_requires
  13. warn_about_deprecated_classes
  14. pe__unpack_resource
  15. is_parent
  16. uber_parent
  17. pe__const_top_resource
  18. common_free
  19. pe__count_active_node
  20. active_node
  21. pe__find_active_requires
  22. pe__count_common
  23. pe__set_next_role

   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 <crm/pengine/rules.h>
  13 #include <crm/pengine/internal.h>
  14 #include <crm/common/xml.h>
  15 #include <crm/common/xml_internal.h>
  16 #include <crm/common/scheduler_internal.h>
  17 
  18 #include "pe_status_private.h"
  19 
  20 void populate_hash(xmlNode * nvpair_list, GHashTable * hash, const char **attrs, int attrs_length);
  21 
  22 static pcmk_node_t *active_node(const pcmk_resource_t *rsc,
  23                                 unsigned int *count_all,
  24                                 unsigned int *count_clean);
  25 
  26 pcmk_rsc_methods_t resource_class_functions[] = {
  27     {
  28          native_unpack,
  29          native_find_rsc,
  30          native_parameter,
  31          native_print,
  32          native_active,
  33          native_resource_state,
  34          native_location,
  35          native_free,
  36          pe__count_common,
  37          pe__native_is_filtered,
  38          active_node,
  39          pe__primitive_max_per_node,
  40     },
  41     {
  42          group_unpack,
  43          native_find_rsc,
  44          native_parameter,
  45          group_print,
  46          group_active,
  47          group_resource_state,
  48          native_location,
  49          group_free,
  50          pe__count_common,
  51          pe__group_is_filtered,
  52          active_node,
  53          pe__group_max_per_node,
  54     },
  55     {
  56          clone_unpack,
  57          native_find_rsc,
  58          native_parameter,
  59          clone_print,
  60          clone_active,
  61          clone_resource_state,
  62          native_location,
  63          clone_free,
  64          pe__count_common,
  65          pe__clone_is_filtered,
  66          active_node,
  67          pe__clone_max_per_node,
  68     },
  69     {
  70          pe__unpack_bundle,
  71          native_find_rsc,
  72          native_parameter,
  73          pe__print_bundle,
  74          pe__bundle_active,
  75          pe__bundle_resource_state,
  76          native_location,
  77          pe__free_bundle,
  78          pe__count_bundle,
  79          pe__bundle_is_filtered,
  80          pe__bundle_active_node,
  81          pe__bundle_max_per_node,
  82     }
  83 };
  84 
  85 static enum pe_obj_types
  86 get_resource_type(const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
  87 {
  88     if (pcmk__str_eq(name, PCMK_XE_PRIMITIVE, pcmk__str_casei)) {
  89         return pcmk_rsc_variant_primitive;
  90 
  91     } else if (pcmk__str_eq(name, PCMK_XE_GROUP, pcmk__str_casei)) {
  92         return pcmk_rsc_variant_group;
  93 
  94     } else if (pcmk__str_eq(name, PCMK_XE_CLONE, pcmk__str_casei)) {
  95         return pcmk_rsc_variant_clone;
  96 
  97     } else if (pcmk__str_eq(name, PCMK__XE_PROMOTABLE_LEGACY,
  98                             pcmk__str_casei)) {
  99         // @COMPAT deprecated since 2.0.0
 100         return pcmk_rsc_variant_clone;
 101 
 102     } else if (pcmk__str_eq(name, PCMK_XE_BUNDLE, pcmk__str_casei)) {
 103         return pcmk_rsc_variant_bundle;
 104     }
 105 
 106     return pcmk_rsc_variant_unknown;
 107 }
 108 
 109 /*!
 110  * \internal
 111  * \brief Insert a meta-attribute if not already present
 112  *
 113  * \param[in]     key    Meta-attribute name
 114  * \param[in]     value  Meta-attribute value to add if not already present
 115  * \param[in,out] table  Meta-attribute hash table to insert into
 116  *
 117  * \note This is like pcmk__insert_meta() except it won't overwrite existing
 118  *       values.
 119  */
 120 static void
 121 dup_attr(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 122 {
 123     GHashTable *table = user_data;
 124 
 125     CRM_CHECK((key != NULL) && (table != NULL), return);
 126     if (pcmk__str_eq((const char *) value, "#default", pcmk__str_casei)) {
 127         // @COMPAT Deprecated since 2.1.8
 128         pcmk__config_warn("Support for setting meta-attributes (such as %s) to "
 129                           "the explicit value '#default' is deprecated and "
 130                           "will be removed in a future release",
 131                           (const char *) key);
 132     } else if ((value != NULL) && (g_hash_table_lookup(table, key) == NULL)) {
 133         pcmk__insert_dup(table, (const char *) key, (const char *) value);
 134     }
 135 }
 136 
 137 static void
 138 expand_parents_fixed_nvpairs(pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 139                              pe_rule_eval_data_t *rule_data,
 140                              GHashTable *meta_hash, pcmk_scheduler_t *scheduler)
 141 {
 142     GHashTable *parent_orig_meta = pcmk__strkey_table(free, free);
 143     pcmk_resource_t *p = rsc->parent;
 144 
 145     if (p == NULL) {
 146         return ;
 147     }
 148 
 149     /* Search all parent resources, get the fixed value of
 150      * PCMK_XE_META_ATTRIBUTES set only in the original xml, and stack it in the
 151      * hash table. The fixed value of the lower parent resource takes precedence
 152      * and is not overwritten.
 153      */
 154     while(p != NULL) {
 155         /* A hash table for comparison is generated, including the id-ref. */
 156         pe__unpack_dataset_nvpairs(p->xml, PCMK_XE_META_ATTRIBUTES, rule_data,
 157                                    parent_orig_meta, NULL, FALSE, scheduler);
 158         p = p->parent; 
 159     }
 160 
 161     if (parent_orig_meta != NULL) {
 162         // This will not overwrite any values already existing for child
 163         g_hash_table_foreach(parent_orig_meta, dup_attr, meta_hash);
 164     }
 165 
 166     if (parent_orig_meta != NULL) {
 167         g_hash_table_destroy(parent_orig_meta);
 168     }
 169     
 170     return ;
 171 
 172 }
 173 void
 174 get_meta_attributes(GHashTable * meta_hash, pcmk_resource_t * rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 175                     pcmk_node_t *node, pcmk_scheduler_t *scheduler)
 176 {
 177     pe_rsc_eval_data_t rsc_rule_data = {
 178         .standard = crm_element_value(rsc->xml, PCMK_XA_CLASS),
 179         .provider = crm_element_value(rsc->xml, PCMK_XA_PROVIDER),
 180         .agent = crm_element_value(rsc->xml, PCMK_XA_TYPE)
 181     };
 182 
 183     pe_rule_eval_data_t rule_data = {
 184         .node_hash = NULL,
 185         .now = scheduler->now,
 186         .match_data = NULL,
 187         .rsc_data = &rsc_rule_data,
 188         .op_data = NULL
 189     };
 190 
 191     if (node) {
 192         /* @COMPAT Support for node attribute expressions in rules for
 193          * meta-attributes is deprecated. When we can break behavioral backward
 194          * compatibility, drop this block.
 195          */
 196         rule_data.node_hash = node->details->attrs;
 197     }
 198 
 199     for (xmlAttrPtr a = pcmk__xe_first_attr(rsc->xml); a != NULL; a = a->next) {
 200         if (a->children != NULL) {
 201             dup_attr((gpointer) a->name, (gpointer) a->children->content,
 202                      meta_hash);
 203         }
 204     }
 205 
 206     pe__unpack_dataset_nvpairs(rsc->xml, PCMK_XE_META_ATTRIBUTES, &rule_data,
 207                                meta_hash, NULL, FALSE, scheduler);
 208 
 209     /* Set the PCMK_XE_META_ATTRIBUTES explicitly set in the parent resource to
 210      * the hash table of the child resource. If it is already explicitly set as
 211      * a child, it will not be overwritten.
 212      */
 213     if (rsc->parent != NULL) {
 214         expand_parents_fixed_nvpairs(rsc, &rule_data, meta_hash, scheduler);
 215     }
 216 
 217     /* check the defaults */
 218     pe__unpack_dataset_nvpairs(scheduler->rsc_defaults, PCMK_XE_META_ATTRIBUTES,
 219                                &rule_data, meta_hash, NULL, FALSE, scheduler);
 220 
 221     /* If there is PCMK_XE_META_ATTRIBUTES that the parent resource has not
 222      * explicitly set, set a value that is not set from PCMK_XE_RSC_DEFAULTS
 223      * either. The values already set up to this point will not be overwritten.
 224      */
 225     if (rsc->parent) {
 226         g_hash_table_foreach(rsc->parent->meta, dup_attr, meta_hash);
 227     }
 228 }
 229 
 230 void
 231 get_rsc_attributes(GHashTable *meta_hash, const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 232                    const pcmk_node_t *node, pcmk_scheduler_t *scheduler)
 233 {
 234     pe_rule_eval_data_t rule_data = {
 235         .node_hash = NULL,
 236         .now = scheduler->now,
 237         .match_data = NULL,
 238         .rsc_data = NULL,
 239         .op_data = NULL
 240     };
 241 
 242     if (node) {
 243         rule_data.node_hash = node->details->attrs;
 244     }
 245 
 246     pe__unpack_dataset_nvpairs(rsc->xml, PCMK_XE_INSTANCE_ATTRIBUTES,
 247                                &rule_data, meta_hash, NULL, FALSE, scheduler);
 248 
 249     /* set anything else based on the parent */
 250     if (rsc->parent != NULL) {
 251         get_rsc_attributes(meta_hash, rsc->parent, node, scheduler);
 252 
 253     } else {
 254         if (pcmk__xe_first_child(scheduler->rsc_defaults,
 255                                  PCMK_XE_INSTANCE_ATTRIBUTES, NULL,
 256                                  NULL) != NULL) {
 257             /* Not possible with schema validation enabled
 258              *
 259              * @COMPAT Drop support when we can break behavioral
 260              * backward compatibility
 261              */
 262             pcmk__warn_once(pcmk__wo_instance_defaults,
 263                             "Support for " PCMK_XE_INSTANCE_ATTRIBUTES " in "
 264                             PCMK_XE_RSC_DEFAULTS " is deprecated and will be "
 265                             "removed in a future release");
 266         }
 267 
 268         /* and finally check the defaults */
 269         pe__unpack_dataset_nvpairs(scheduler->rsc_defaults,
 270                                    PCMK_XE_INSTANCE_ATTRIBUTES, &rule_data,
 271                                    meta_hash, NULL, FALSE, scheduler);
 272     }
 273 }
 274 
 275 static char *
 276 template_op_key(xmlNode * op)
     /* [previous][next][first][last][top][bottom][index][help] */
 277 {
 278     const char *name = crm_element_value(op, PCMK_XA_NAME);
 279     const char *role = crm_element_value(op, PCMK_XA_ROLE);
 280     char *key = NULL;
 281 
 282     if ((role == NULL)
 283         || pcmk__strcase_any_of(role, PCMK_ROLE_STARTED, PCMK_ROLE_UNPROMOTED,
 284                                 PCMK__ROLE_UNPROMOTED_LEGACY, NULL)) {
 285         role = PCMK__ROLE_UNKNOWN;
 286     }
 287 
 288     key = crm_strdup_printf("%s-%s", name, role);
 289     return key;
 290 }
 291 
 292 static gboolean
 293 unpack_template(xmlNode *xml_obj, xmlNode **expanded_xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 294                 pcmk_scheduler_t *scheduler)
 295 {
 296     xmlNode *cib_resources = NULL;
 297     xmlNode *template = NULL;
 298     xmlNode *new_xml = NULL;
 299     xmlNode *child_xml = NULL;
 300     xmlNode *rsc_ops = NULL;
 301     xmlNode *template_ops = NULL;
 302     const char *template_ref = NULL;
 303     const char *id = NULL;
 304 
 305     if (xml_obj == NULL) {
 306         pcmk__config_err("No resource object for template unpacking");
 307         return FALSE;
 308     }
 309 
 310     template_ref = crm_element_value(xml_obj, PCMK_XA_TEMPLATE);
 311     if (template_ref == NULL) {
 312         return TRUE;
 313     }
 314 
 315     id = pcmk__xe_id(xml_obj);
 316     if (id == NULL) {
 317         pcmk__config_err("'%s' object must have a id", xml_obj->name);
 318         return FALSE;
 319     }
 320 
 321     if (pcmk__str_eq(template_ref, id, pcmk__str_none)) {
 322         pcmk__config_err("The resource object '%s' should not reference itself",
 323                          id);
 324         return FALSE;
 325     }
 326 
 327     cib_resources = get_xpath_object("//" PCMK_XE_RESOURCES, scheduler->input,
 328                                      LOG_TRACE);
 329     if (cib_resources == NULL) {
 330         pcmk__config_err("No resources configured");
 331         return FALSE;
 332     }
 333 
 334     template = pcmk__xe_first_child(cib_resources, PCMK_XE_TEMPLATE,
 335                                     PCMK_XA_ID, template_ref);
 336     if (template == NULL) {
 337         pcmk__config_err("No template named '%s'", template_ref);
 338         return FALSE;
 339     }
 340 
 341     new_xml = pcmk__xml_copy(NULL, template);
 342     xmlNodeSetName(new_xml, xml_obj->name);
 343     crm_xml_add(new_xml, PCMK_XA_ID, id);
 344     crm_xml_add(new_xml, PCMK__META_CLONE,
 345                 crm_element_value(xml_obj, PCMK__META_CLONE));
 346 
 347     template_ops = pcmk__xe_first_child(new_xml, PCMK_XE_OPERATIONS, NULL,
 348                                         NULL);
 349 
 350     for (child_xml = pcmk__xe_first_child(xml_obj, NULL, NULL, NULL);
 351          child_xml != NULL; child_xml = pcmk__xe_next(child_xml)) {
 352 
 353         xmlNode *new_child = pcmk__xml_copy(new_xml, child_xml);
 354 
 355         if (pcmk__xe_is(new_child, PCMK_XE_OPERATIONS)) {
 356             rsc_ops = new_child;
 357         }
 358     }
 359 
 360     if (template_ops && rsc_ops) {
 361         xmlNode *op = NULL;
 362         GHashTable *rsc_ops_hash = pcmk__strkey_table(free, NULL);
 363 
 364         for (op = pcmk__xe_first_child(rsc_ops, NULL, NULL, NULL); op != NULL;
 365              op = pcmk__xe_next(op)) {
 366 
 367             char *key = template_op_key(op);
 368 
 369             g_hash_table_insert(rsc_ops_hash, key, op);
 370         }
 371 
 372         for (op = pcmk__xe_first_child(template_ops, NULL, NULL, NULL);
 373              op != NULL; op = pcmk__xe_next(op)) {
 374 
 375             char *key = template_op_key(op);
 376 
 377             if (g_hash_table_lookup(rsc_ops_hash, key) == NULL) {
 378                 pcmk__xml_copy(rsc_ops, op);
 379             }
 380 
 381             free(key);
 382         }
 383 
 384         if (rsc_ops_hash) {
 385             g_hash_table_destroy(rsc_ops_hash);
 386         }
 387 
 388         free_xml(template_ops);
 389     }
 390 
 391     /*free_xml(*expanded_xml); */
 392     *expanded_xml = new_xml;
 393 
 394 #if 0 /* Disable multi-level templates for now */
 395     if (!unpack_template(new_xml, expanded_xml, scheduler)) {
 396        free_xml(*expanded_xml);
 397        *expanded_xml = NULL;
 398        return FALSE;
 399     }
 400 #endif
 401 
 402     return TRUE;
 403 }
 404 
 405 static gboolean
 406 add_template_rsc(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 407 {
 408     const char *template_ref = NULL;
 409     const char *id = NULL;
 410 
 411     if (xml_obj == NULL) {
 412         pcmk__config_err("No resource object for processing resource list "
 413                          "of template");
 414         return FALSE;
 415     }
 416 
 417     template_ref = crm_element_value(xml_obj, PCMK_XA_TEMPLATE);
 418     if (template_ref == NULL) {
 419         return TRUE;
 420     }
 421 
 422     id = pcmk__xe_id(xml_obj);
 423     if (id == NULL) {
 424         pcmk__config_err("'%s' object must have a id", xml_obj->name);
 425         return FALSE;
 426     }
 427 
 428     if (pcmk__str_eq(template_ref, id, pcmk__str_none)) {
 429         pcmk__config_err("The resource object '%s' should not reference itself",
 430                          id);
 431         return FALSE;
 432     }
 433 
 434     if (add_tag_ref(scheduler->template_rsc_sets, template_ref, id) == FALSE) {
 435         return FALSE;
 436     }
 437 
 438     return TRUE;
 439 }
 440 
 441 static bool
 442 detect_promotable(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 443 {
 444     const char *promotable = g_hash_table_lookup(rsc->meta,
 445                                                  PCMK_META_PROMOTABLE);
 446 
 447     if (crm_is_true(promotable)) {
 448         return TRUE;
 449     }
 450 
 451     // @COMPAT deprecated since 2.0.0
 452     if (pcmk__xe_is(rsc->xml, PCMK__XE_PROMOTABLE_LEGACY)) {
 453         pcmk__warn_once(pcmk__wo_master_element,
 454                         "Support for <" PCMK__XE_PROMOTABLE_LEGACY "> (such "
 455                         "as in %s) is deprecated and will be removed in a "
 456                         "future release. Use <" PCMK_XE_CLONE "> with a "
 457                         PCMK_META_PROMOTABLE " meta-attribute instead.",
 458                         rsc->id);
 459         pcmk__insert_dup(rsc->meta, PCMK_META_PROMOTABLE, PCMK_VALUE_TRUE);
 460         return TRUE;
 461     }
 462     return FALSE;
 463 }
 464 
 465 static void
 466 free_params_table(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 467 {
 468     g_hash_table_destroy((GHashTable *) data);
 469 }
 470 
 471 /*!
 472  * \brief Get a table of resource parameters
 473  *
 474  * \param[in,out] rsc        Resource to query
 475  * \param[in]     node       Node for evaluating rules (NULL for defaults)
 476  * \param[in,out] scheduler  Scheduler data
 477  *
 478  * \return Hash table containing resource parameter names and values
 479  *         (or NULL if \p rsc or \p scheduler is NULL)
 480  * \note The returned table will be destroyed when the resource is freed, so
 481  *       callers should not destroy it.
 482  */
 483 GHashTable *
 484 pe_rsc_params(pcmk_resource_t *rsc, const pcmk_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 485               pcmk_scheduler_t *scheduler)
 486 {
 487     GHashTable *params_on_node = NULL;
 488 
 489     /* A NULL node is used to request the resource's default parameters
 490      * (not evaluated for node), but we always want something non-NULL
 491      * as a hash table key.
 492      */
 493     const char *node_name = "";
 494 
 495     // Sanity check
 496     if ((rsc == NULL) || (scheduler == NULL)) {
 497         return NULL;
 498     }
 499     if ((node != NULL) && (node->details->uname != NULL)) {
 500         node_name = node->details->uname;
 501     }
 502 
 503     // Find the parameter table for given node
 504     if (rsc->parameter_cache == NULL) {
 505         rsc->parameter_cache = pcmk__strikey_table(free, free_params_table);
 506     } else {
 507         params_on_node = g_hash_table_lookup(rsc->parameter_cache, node_name);
 508     }
 509 
 510     // If none exists yet, create one with parameters evaluated for node
 511     if (params_on_node == NULL) {
 512         params_on_node = pcmk__strkey_table(free, free);
 513         get_rsc_attributes(params_on_node, rsc, node, scheduler);
 514         g_hash_table_insert(rsc->parameter_cache, strdup(node_name),
 515                             params_on_node);
 516     }
 517     return params_on_node;
 518 }
 519 
 520 /*!
 521  * \internal
 522  * \brief Unpack a resource's \c PCMK_META_REQUIRES meta-attribute
 523  *
 524  * \param[in,out] rsc         Resource being unpacked
 525  * \param[in]     value       Value of \c PCMK_META_REQUIRES meta-attribute
 526  * \param[in]     is_default  Whether \p value was selected by default
 527  */
 528 static void
 529 unpack_requires(pcmk_resource_t *rsc, const char *value, bool is_default)
     /* [previous][next][first][last][top][bottom][index][help] */
 530 {
 531     if (pcmk__str_eq(value, PCMK_VALUE_NOTHING, pcmk__str_casei)) {
 532 
 533     } else if (pcmk__str_eq(value, PCMK_VALUE_QUORUM, pcmk__str_casei)) {
 534         pcmk__set_rsc_flags(rsc, pcmk_rsc_needs_quorum);
 535 
 536     } else if (pcmk__str_eq(value, PCMK_VALUE_FENCING, pcmk__str_casei)) {
 537         pcmk__set_rsc_flags(rsc, pcmk_rsc_needs_fencing);
 538         if (!pcmk_is_set(rsc->cluster->flags, pcmk_sched_fencing_enabled)) {
 539             pcmk__config_warn("%s requires fencing but fencing is disabled",
 540                               rsc->id);
 541         }
 542 
 543     } else if (pcmk__str_eq(value, PCMK_VALUE_UNFENCING, pcmk__str_casei)) {
 544         if (pcmk_is_set(rsc->flags, pcmk_rsc_fence_device)) {
 545             pcmk__config_warn("Resetting \"" PCMK_META_REQUIRES "\" for %s "
 546                               "to \"" PCMK_VALUE_QUORUM "\" because fencing "
 547                               "devices cannot require unfencing", rsc->id);
 548             unpack_requires(rsc, PCMK_VALUE_QUORUM, true);
 549             return;
 550 
 551         } else if (!pcmk_is_set(rsc->cluster->flags,
 552                                 pcmk_sched_fencing_enabled)) {
 553             pcmk__config_warn("Resetting \"" PCMK_META_REQUIRES "\" for %s "
 554                               "to \"" PCMK_VALUE_QUORUM "\" because fencing is "
 555                               "disabled", rsc->id);
 556             unpack_requires(rsc, PCMK_VALUE_QUORUM, true);
 557             return;
 558 
 559         } else {
 560             pcmk__set_rsc_flags(rsc, pcmk_rsc_needs_fencing
 561                                      |pcmk_rsc_needs_unfencing);
 562         }
 563 
 564     } else {
 565         const char *orig_value = value;
 566 
 567         if (pcmk_is_set(rsc->flags, pcmk_rsc_fence_device)) {
 568             value = PCMK_VALUE_QUORUM;
 569 
 570         } else if (pcmk__is_primitive(rsc)
 571                    && xml_contains_remote_node(rsc->xml)) {
 572             value = PCMK_VALUE_QUORUM;
 573 
 574         } else if (pcmk_is_set(rsc->cluster->flags,
 575                                pcmk_sched_enable_unfencing)) {
 576             value = PCMK_VALUE_UNFENCING;
 577 
 578         } else if (pcmk_is_set(rsc->cluster->flags,
 579                                pcmk_sched_fencing_enabled)) {
 580             value = PCMK_VALUE_FENCING;
 581 
 582         } else if (rsc->cluster->no_quorum_policy == pcmk_no_quorum_ignore) {
 583             value = PCMK_VALUE_NOTHING;
 584 
 585         } else {
 586             value = PCMK_VALUE_QUORUM;
 587         }
 588 
 589         if (orig_value != NULL) {
 590             pcmk__config_err("Resetting '" PCMK_META_REQUIRES "' for %s "
 591                              "to '%s' because '%s' is not valid",
 592                               rsc->id, value, orig_value);
 593         }
 594         unpack_requires(rsc, value, true);
 595         return;
 596     }
 597 
 598     pcmk__rsc_trace(rsc, "\tRequired to start: %s%s", value,
 599                     (is_default? " (default)" : ""));
 600 }
 601 
 602 static void
 603 warn_about_deprecated_classes(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 604 {
 605     const char *std = crm_element_value(rsc->xml, PCMK_XA_CLASS);
 606 
 607     if (pcmk__str_eq(std, PCMK_RESOURCE_CLASS_UPSTART, pcmk__str_none)) {
 608         pcmk__warn_once(pcmk__wo_upstart,
 609                         "Support for Upstart resources (such as %s) is "
 610                         "deprecated and will be removed in a future release",
 611                         rsc->id);
 612 
 613     } else if (pcmk__str_eq(std, PCMK_RESOURCE_CLASS_NAGIOS, pcmk__str_none)) {
 614         pcmk__warn_once(pcmk__wo_nagios,
 615                         "Support for Nagios resources (such as %s) is "
 616                         "deprecated and will be removed in a future release",
 617                         rsc->id);
 618     }
 619 }
 620 
 621 /*!
 622  * \internal
 623  * \brief Unpack configuration XML for a given resource
 624  *
 625  * Unpack the XML object containing a resource's configuration into a new
 626  * \c pcmk_resource_t object.
 627  *
 628  * \param[in]     xml_obj    XML node containing the resource's configuration
 629  * \param[out]    rsc        Where to store the unpacked resource information
 630  * \param[in]     parent     Resource's parent, if any
 631  * \param[in,out] scheduler  Scheduler data
 632  *
 633  * \return Standard Pacemaker return code
 634  * \note If pcmk_rc_ok is returned, \p *rsc is guaranteed to be non-NULL, and
 635  *       the caller is responsible for freeing it using its variant-specific
 636  *       free() method. Otherwise, \p *rsc is guaranteed to be NULL.
 637  */
 638 int
 639 pe__unpack_resource(xmlNode *xml_obj, pcmk_resource_t **rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 640                     pcmk_resource_t *parent, pcmk_scheduler_t *scheduler)
 641 {
 642     xmlNode *expanded_xml = NULL;
 643     xmlNode *ops = NULL;
 644     const char *value = NULL;
 645     const char *id = NULL;
 646     bool guest_node = false;
 647     bool remote_node = false;
 648 
 649     pe_rule_eval_data_t rule_data = {
 650         .node_hash = NULL,
 651         .now = NULL,
 652         .match_data = NULL,
 653         .rsc_data = NULL,
 654         .op_data = NULL
 655     };
 656 
 657     CRM_CHECK(rsc != NULL, return EINVAL);
 658     CRM_CHECK((xml_obj != NULL) && (scheduler != NULL),
 659               *rsc = NULL;
 660               return EINVAL);
 661 
 662     rule_data.now = scheduler->now;
 663 
 664     crm_log_xml_trace(xml_obj, "[raw XML]");
 665 
 666     id = crm_element_value(xml_obj, PCMK_XA_ID);
 667     if (id == NULL) {
 668         pcmk__config_err("Ignoring <%s> configuration without " PCMK_XA_ID,
 669                          xml_obj->name);
 670         return pcmk_rc_unpack_error;
 671     }
 672 
 673     if (unpack_template(xml_obj, &expanded_xml, scheduler) == FALSE) {
 674         return pcmk_rc_unpack_error;
 675     }
 676 
 677     *rsc = calloc(1, sizeof(pcmk_resource_t));
 678     if (*rsc == NULL) {
 679         pcmk__sched_err("Unable to allocate memory for resource '%s'", id);
 680         return ENOMEM;
 681     }
 682     (*rsc)->cluster = scheduler;
 683 
 684     if (expanded_xml) {
 685         crm_log_xml_trace(expanded_xml, "[expanded XML]");
 686         (*rsc)->xml = expanded_xml;
 687         (*rsc)->orig_xml = xml_obj;
 688 
 689     } else {
 690         (*rsc)->xml = xml_obj;
 691         (*rsc)->orig_xml = NULL;
 692     }
 693 
 694     /* Do not use xml_obj from here on, use (*rsc)->xml in case templates are involved */
 695 
 696     (*rsc)->parent = parent;
 697 
 698     ops = pcmk__xe_first_child((*rsc)->xml, PCMK_XE_OPERATIONS, NULL, NULL);
 699     (*rsc)->ops_xml = expand_idref(ops, scheduler->input);
 700 
 701     (*rsc)->variant = get_resource_type((const char *) (*rsc)->xml->name);
 702     if ((*rsc)->variant == pcmk_rsc_variant_unknown) {
 703         pcmk__config_err("Ignoring resource '%s' of unknown type '%s'",
 704                          id, (*rsc)->xml->name);
 705         common_free(*rsc);
 706         *rsc = NULL;
 707         return pcmk_rc_unpack_error;
 708     }
 709 
 710     (*rsc)->meta = pcmk__strkey_table(free, free);
 711     (*rsc)->allowed_nodes = pcmk__strkey_table(NULL, free);
 712     (*rsc)->known_on = pcmk__strkey_table(NULL, free);
 713 
 714     value = crm_element_value((*rsc)->xml, PCMK__META_CLONE);
 715     if (value) {
 716         (*rsc)->id = crm_strdup_printf("%s:%s", id, value);
 717         pcmk__insert_meta(*rsc, PCMK__META_CLONE, value);
 718 
 719     } else {
 720         (*rsc)->id = strdup(id);
 721     }
 722 
 723     warn_about_deprecated_classes(*rsc);
 724 
 725     (*rsc)->fns = &resource_class_functions[(*rsc)->variant];
 726 
 727     get_meta_attributes((*rsc)->meta, *rsc, NULL, scheduler);
 728     (*rsc)->parameters = pe_rsc_params(*rsc, NULL, scheduler); // \deprecated
 729 
 730     (*rsc)->flags = 0;
 731     pcmk__set_rsc_flags(*rsc, pcmk_rsc_runnable|pcmk_rsc_unassigned);
 732 
 733     if (!pcmk_is_set(scheduler->flags, pcmk_sched_in_maintenance)) {
 734         pcmk__set_rsc_flags(*rsc, pcmk_rsc_managed);
 735     }
 736 
 737     (*rsc)->rsc_cons = NULL;
 738     (*rsc)->rsc_tickets = NULL;
 739     (*rsc)->actions = NULL;
 740     (*rsc)->role = pcmk_role_stopped;
 741     (*rsc)->next_role = pcmk_role_unknown;
 742 
 743     (*rsc)->recovery_type = pcmk_multiply_active_restart;
 744     (*rsc)->stickiness = 0;
 745     (*rsc)->migration_threshold = PCMK_SCORE_INFINITY;
 746     (*rsc)->failure_timeout = 0;
 747 
 748     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_PRIORITY);
 749     (*rsc)->priority = char2score(value);
 750 
 751     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_CRITICAL);
 752     if ((value == NULL) || crm_is_true(value)) {
 753         pcmk__set_rsc_flags(*rsc, pcmk_rsc_critical);
 754     }
 755 
 756     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_NOTIFY);
 757     if (crm_is_true(value)) {
 758         pcmk__set_rsc_flags(*rsc, pcmk_rsc_notify);
 759     }
 760 
 761     if (xml_contains_remote_node((*rsc)->xml)) {
 762         (*rsc)->is_remote_node = TRUE;
 763         if (g_hash_table_lookup((*rsc)->meta, PCMK__META_CONTAINER)) {
 764             guest_node = true;
 765         } else {
 766             remote_node = true;
 767         }
 768     }
 769 
 770     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_ALLOW_MIGRATE);
 771     if (crm_is_true(value)) {
 772         pcmk__set_rsc_flags(*rsc, pcmk_rsc_migratable);
 773     } else if ((value == NULL) && remote_node) {
 774         /* By default, we want remote nodes to be able
 775          * to float around the cluster without having to stop all the
 776          * resources within the remote-node before moving. Allowing
 777          * migration support enables this feature. If this ever causes
 778          * problems, migration support can be explicitly turned off with
 779          * PCMK_META_ALLOW_MIGRATE=false.
 780          */
 781         pcmk__set_rsc_flags(*rsc, pcmk_rsc_migratable);
 782     }
 783 
 784     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_IS_MANAGED);
 785     if (value != NULL) {
 786         if (pcmk__str_eq(PCMK_VALUE_DEFAULT, value, pcmk__str_casei)) {
 787             // @COMPAT Deprecated since 2.1.8
 788             pcmk__config_warn("Support for setting " PCMK_META_IS_MANAGED
 789                               " to the explicit value '" PCMK_VALUE_DEFAULT
 790                               "' is deprecated and will be removed in a "
 791                               "future release (just leave it unset)");
 792         } else if (crm_is_true(value)) {
 793             pcmk__set_rsc_flags(*rsc, pcmk_rsc_managed);
 794         } else {
 795             pcmk__clear_rsc_flags(*rsc, pcmk_rsc_managed);
 796         }
 797     }
 798 
 799     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_MAINTENANCE);
 800     if (crm_is_true(value)) {
 801         pcmk__clear_rsc_flags(*rsc, pcmk_rsc_managed);
 802         pcmk__set_rsc_flags(*rsc, pcmk_rsc_maintenance);
 803     }
 804     if (pcmk_is_set(scheduler->flags, pcmk_sched_in_maintenance)) {
 805         pcmk__clear_rsc_flags(*rsc, pcmk_rsc_managed);
 806         pcmk__set_rsc_flags(*rsc, pcmk_rsc_maintenance);
 807     }
 808 
 809     if (pcmk__is_clone(pe__const_top_resource(*rsc, false))) {
 810         value = g_hash_table_lookup((*rsc)->meta, PCMK_META_GLOBALLY_UNIQUE);
 811         if (crm_is_true(value)) {
 812             pcmk__set_rsc_flags(*rsc, pcmk_rsc_unique);
 813         }
 814         if (detect_promotable(*rsc)) {
 815             pcmk__set_rsc_flags(*rsc, pcmk_rsc_promotable);
 816         }
 817     } else {
 818         pcmk__set_rsc_flags(*rsc, pcmk_rsc_unique);
 819     }
 820 
 821     // @COMPAT Deprecated meta-attribute
 822     value = g_hash_table_lookup((*rsc)->meta, PCMK__META_RESTART_TYPE);
 823     if (pcmk__str_eq(value, PCMK_VALUE_RESTART, pcmk__str_casei)) {
 824         (*rsc)->restart_type = pe_restart_restart;
 825         pcmk__rsc_trace(*rsc, "%s dependency restart handling: restart",
 826                         (*rsc)->id);
 827         pcmk__warn_once(pcmk__wo_restart_type,
 828                         "Support for " PCMK__META_RESTART_TYPE " is deprecated "
 829                         "and will be removed in a future release");
 830 
 831     } else {
 832         (*rsc)->restart_type = pe_restart_ignore;
 833         pcmk__rsc_trace(*rsc, "%s dependency restart handling: ignore",
 834                         (*rsc)->id);
 835     }
 836 
 837     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_MULTIPLE_ACTIVE);
 838     if (pcmk__str_eq(value, PCMK_VALUE_STOP_ONLY, pcmk__str_casei)) {
 839         (*rsc)->recovery_type = pcmk_multiply_active_stop;
 840         pcmk__rsc_trace(*rsc, "%s multiple running resource recovery: stop only",
 841                         (*rsc)->id);
 842 
 843     } else if (pcmk__str_eq(value, PCMK_VALUE_BLOCK, pcmk__str_casei)) {
 844         (*rsc)->recovery_type = pcmk_multiply_active_block;
 845         pcmk__rsc_trace(*rsc, "%s multiple running resource recovery: block",
 846                         (*rsc)->id);
 847 
 848     } else if (pcmk__str_eq(value, PCMK_VALUE_STOP_UNEXPECTED,
 849                             pcmk__str_casei)) {
 850         (*rsc)->recovery_type = pcmk_multiply_active_unexpected;
 851         pcmk__rsc_trace(*rsc,
 852                         "%s multiple running resource recovery: "
 853                         "stop unexpected instances",
 854                         (*rsc)->id);
 855 
 856     } else { // PCMK_VALUE_STOP_START
 857         if (!pcmk__str_eq(value, PCMK_VALUE_STOP_START,
 858                           pcmk__str_casei|pcmk__str_null_matches)) {
 859             pcmk__config_warn("%s is not a valid value for "
 860                               PCMK_META_MULTIPLE_ACTIVE
 861                               ", using default of "
 862                               "\"" PCMK_VALUE_STOP_START "\"",
 863                               value);
 864         }
 865         (*rsc)->recovery_type = pcmk_multiply_active_restart;
 866         pcmk__rsc_trace(*rsc,
 867                         "%s multiple running resource recovery: stop/start",
 868                         (*rsc)->id);
 869     }
 870 
 871     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_RESOURCE_STICKINESS);
 872     if (value != NULL) {
 873         if (pcmk__str_eq(PCMK_VALUE_DEFAULT, value, pcmk__str_casei)) {
 874             // @COMPAT Deprecated since 2.1.8
 875             pcmk__config_warn("Support for setting "
 876                               PCMK_META_RESOURCE_STICKINESS
 877                               " to the explicit value '" PCMK_VALUE_DEFAULT
 878                               "' is deprecated and will be removed in a "
 879                               "future release (just leave it unset)");
 880         } else {
 881             (*rsc)->stickiness = char2score(value);
 882         }
 883     }
 884 
 885     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_MIGRATION_THRESHOLD);
 886     if (value != NULL) {
 887         if (pcmk__str_eq(PCMK_VALUE_DEFAULT, value, pcmk__str_casei)) {
 888             // @COMPAT Deprecated since 2.1.8
 889             pcmk__config_warn("Support for setting "
 890                               PCMK_META_MIGRATION_THRESHOLD
 891                               " to the explicit value '" PCMK_VALUE_DEFAULT
 892                               "' is deprecated and will be removed in a "
 893                               "future release (just leave it unset)");
 894         } else {
 895             (*rsc)->migration_threshold = char2score(value);
 896             if ((*rsc)->migration_threshold < 0) {
 897                 /* @COMPAT We use 1 here to preserve previous behavior, but this
 898                  * should probably use the default (INFINITY) or 0 (to disable)
 899                  * instead.
 900                  */
 901                 pcmk__warn_once(pcmk__wo_neg_threshold,
 902                                 PCMK_META_MIGRATION_THRESHOLD
 903                                 " must be non-negative, using 1 instead");
 904                 (*rsc)->migration_threshold = 1;
 905             }
 906         }
 907     }
 908 
 909     if (pcmk__str_eq(crm_element_value((*rsc)->xml, PCMK_XA_CLASS),
 910                      PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) {
 911         pcmk__set_scheduler_flags(scheduler, pcmk_sched_have_fencing);
 912         pcmk__set_rsc_flags(*rsc, pcmk_rsc_fence_device);
 913     }
 914 
 915     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_REQUIRES);
 916     unpack_requires(*rsc, value, false);
 917 
 918     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_FAILURE_TIMEOUT);
 919     if (value != NULL) {
 920         guint interval_ms = 0U;
 921 
 922         // Stored as seconds
 923         pcmk_parse_interval_spec(value, &interval_ms);
 924         (*rsc)->failure_timeout = (int) (interval_ms / 1000);
 925     }
 926 
 927     if (remote_node) {
 928         GHashTable *params = pe_rsc_params(*rsc, NULL, scheduler);
 929 
 930         /* Grabbing the value now means that any rules based on node attributes
 931          * will evaluate to false, so such rules should not be used with
 932          * PCMK_REMOTE_RA_RECONNECT_INTERVAL.
 933          *
 934          * @TODO Evaluate per node before using
 935          */
 936         value = g_hash_table_lookup(params, PCMK_REMOTE_RA_RECONNECT_INTERVAL);
 937         if (value) {
 938             /* reconnect delay works by setting failure_timeout and preventing the
 939              * connection from starting until the failure is cleared. */
 940             pcmk_parse_interval_spec(value, &((*rsc)->remote_reconnect_ms));
 941 
 942             /* We want to override any default failure_timeout in use when remote
 943              * PCMK_REMOTE_RA_RECONNECT_INTERVAL is in use.
 944              */
 945             (*rsc)->failure_timeout = (*rsc)->remote_reconnect_ms / 1000;
 946         }
 947     }
 948 
 949     get_target_role(*rsc, &((*rsc)->next_role));
 950     pcmk__rsc_trace(*rsc, "%s desired next state: %s", (*rsc)->id,
 951                     ((*rsc)->next_role == pcmk_role_unknown)?
 952                         "default" : pcmk_role_text((*rsc)->next_role));
 953 
 954     if ((*rsc)->fns->unpack(*rsc, scheduler) == FALSE) {
 955         (*rsc)->fns->free(*rsc);
 956         *rsc = NULL;
 957         return pcmk_rc_unpack_error;
 958     }
 959 
 960     if (pcmk_is_set(scheduler->flags, pcmk_sched_symmetric_cluster)) {
 961         // This tag must stay exactly the same because it is tested elsewhere
 962         resource_location(*rsc, NULL, 0, "symmetric_default", scheduler);
 963     } else if (guest_node) {
 964         /* remote resources tied to a container resource must always be allowed
 965          * to opt-in to the cluster. Whether the connection resource is actually
 966          * allowed to be placed on a node is dependent on the container resource */
 967         resource_location(*rsc, NULL, 0, "remote_connection_default",
 968                           scheduler);
 969     }
 970 
 971     pcmk__rsc_trace(*rsc, "%s action notification: %s", (*rsc)->id,
 972                     pcmk_is_set((*rsc)->flags, pcmk_rsc_notify)? "required" : "not required");
 973 
 974     (*rsc)->utilization = pcmk__strkey_table(free, free);
 975 
 976     pe__unpack_dataset_nvpairs((*rsc)->xml, PCMK_XE_UTILIZATION, &rule_data,
 977                                (*rsc)->utilization, NULL, FALSE, scheduler);
 978 
 979     if (expanded_xml) {
 980         if (add_template_rsc(xml_obj, scheduler) == FALSE) {
 981             (*rsc)->fns->free(*rsc);
 982             *rsc = NULL;
 983             return pcmk_rc_unpack_error;
 984         }
 985     }
 986     return pcmk_rc_ok;
 987 }
 988 
 989 gboolean
 990 is_parent(pcmk_resource_t *child, pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 991 {
 992     pcmk_resource_t *parent = child;
 993 
 994     if (parent == NULL || rsc == NULL) {
 995         return FALSE;
 996     }
 997     while (parent->parent != NULL) {
 998         if (parent->parent == rsc) {
 999             return TRUE;
1000         }
1001         parent = parent->parent;
1002     }
1003     return FALSE;
1004 }
1005 
1006 pcmk_resource_t *
1007 uber_parent(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1008 {
1009     pcmk_resource_t *parent = rsc;
1010 
1011     if (parent == NULL) {
1012         return NULL;
1013     }
1014     while ((parent->parent != NULL) && !pcmk__is_bundle(parent->parent)) {
1015         parent = parent->parent;
1016     }
1017     return parent;
1018 }
1019 
1020 /*!
1021  * \internal
1022  * \brief Get the topmost parent of a resource as a const pointer
1023  *
1024  * \param[in] rsc             Resource to check
1025  * \param[in] include_bundle  If true, go all the way to bundle
1026  *
1027  * \return \p NULL if \p rsc is NULL, \p rsc if \p rsc has no parent,
1028  *         the bundle if \p rsc is bundled and \p include_bundle is true,
1029  *         otherwise the topmost parent of \p rsc up to a clone
1030  */
1031 const pcmk_resource_t *
1032 pe__const_top_resource(const pcmk_resource_t *rsc, bool include_bundle)
     /* [previous][next][first][last][top][bottom][index][help] */
1033 {
1034     const pcmk_resource_t *parent = rsc;
1035 
1036     if (parent == NULL) {
1037         return NULL;
1038     }
1039     while (parent->parent != NULL) {
1040         if (!include_bundle && pcmk__is_bundle(parent->parent)) {
1041             break;
1042         }
1043         parent = parent->parent;
1044     }
1045     return parent;
1046 }
1047 
1048 void
1049 common_free(pcmk_resource_t * rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1050 {
1051     if (rsc == NULL) {
1052         return;
1053     }
1054 
1055     pcmk__rsc_trace(rsc, "Freeing %s %d", rsc->id, rsc->variant);
1056 
1057     g_list_free(rsc->rsc_cons);
1058     g_list_free(rsc->rsc_cons_lhs);
1059     g_list_free(rsc->rsc_tickets);
1060     g_list_free(rsc->dangling_migrations);
1061 
1062     if (rsc->parameter_cache != NULL) {
1063         g_hash_table_destroy(rsc->parameter_cache);
1064     }
1065     if (rsc->meta != NULL) {
1066         g_hash_table_destroy(rsc->meta);
1067     }
1068     if (rsc->utilization != NULL) {
1069         g_hash_table_destroy(rsc->utilization);
1070     }
1071 
1072     if ((rsc->parent == NULL)
1073         && pcmk_is_set(rsc->flags, pcmk_rsc_removed)) {
1074 
1075         free_xml(rsc->xml);
1076         rsc->xml = NULL;
1077         free_xml(rsc->orig_xml);
1078         rsc->orig_xml = NULL;
1079 
1080         /* if rsc->orig_xml, then rsc->xml is an expanded xml from a template */
1081     } else if (rsc->orig_xml) {
1082         free_xml(rsc->xml);
1083         rsc->xml = NULL;
1084     }
1085     if (rsc->running_on) {
1086         g_list_free(rsc->running_on);
1087         rsc->running_on = NULL;
1088     }
1089     if (rsc->known_on) {
1090         g_hash_table_destroy(rsc->known_on);
1091         rsc->known_on = NULL;
1092     }
1093     if (rsc->actions) {
1094         g_list_free(rsc->actions);
1095         rsc->actions = NULL;
1096     }
1097     if (rsc->allowed_nodes) {
1098         g_hash_table_destroy(rsc->allowed_nodes);
1099         rsc->allowed_nodes = NULL;
1100     }
1101     g_list_free(rsc->fillers);
1102     g_list_free(rsc->rsc_location);
1103     pcmk__rsc_trace(rsc, "Resource freed");
1104     free(rsc->id);
1105     free(rsc->clone_name);
1106     free(rsc->allocated_to);
1107     free(rsc->variant_opaque);
1108     free(rsc->pending_task);
1109     free(rsc);
1110 }
1111 
1112 /*!
1113  * \internal
1114  * \brief Count a node and update most preferred to it as appropriate
1115  *
1116  * \param[in]     rsc          An active resource
1117  * \param[in]     node         A node that \p rsc is active on
1118  * \param[in,out] active       This will be set to \p node if \p node is more
1119  *                             preferred than the current value
1120  * \param[in,out] count_all    If not NULL, this will be incremented
1121  * \param[in,out] count_clean  If not NULL, this will be incremented if \p node
1122  *                             is online and clean
1123  *
1124  * \return true if the count should continue, or false if sufficiently known
1125  */
1126 bool
1127 pe__count_active_node(const pcmk_resource_t *rsc, pcmk_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
1128                       pcmk_node_t **active, unsigned int *count_all,
1129                       unsigned int *count_clean)
1130 {
1131     bool keep_looking = false;
1132     bool is_happy = false;
1133 
1134     CRM_CHECK((rsc != NULL) && (node != NULL) && (active != NULL),
1135               return false);
1136 
1137     is_happy = node->details->online && !node->details->unclean;
1138 
1139     if (count_all != NULL) {
1140         ++*count_all;
1141     }
1142     if ((count_clean != NULL) && is_happy) {
1143         ++*count_clean;
1144     }
1145     if ((count_all != NULL) || (count_clean != NULL)) {
1146         keep_looking = true; // We're counting, so go through entire list
1147     }
1148 
1149     if (rsc->partial_migration_source != NULL) {
1150         if (pcmk__same_node(node, rsc->partial_migration_source)) {
1151             *active = node; // This is the migration source
1152         } else {
1153             keep_looking = true;
1154         }
1155     } else if (!pcmk_is_set(rsc->flags, pcmk_rsc_needs_fencing)) {
1156         if (is_happy && ((*active == NULL) || !(*active)->details->online
1157                          || (*active)->details->unclean)) {
1158             *active = node; // This is the first clean node
1159         } else {
1160             keep_looking = true;
1161         }
1162     }
1163     if (*active == NULL) {
1164         *active = node; // This is the first node checked
1165     }
1166     return keep_looking;
1167 }
1168 
1169 // Shared implementation of pcmk_rsc_methods_t:active_node()
1170 static pcmk_node_t *
1171 active_node(const pcmk_resource_t *rsc, unsigned int *count_all,
     /* [previous][next][first][last][top][bottom][index][help] */
1172             unsigned int *count_clean)
1173 {
1174     pcmk_node_t *active = NULL;
1175 
1176     if (count_all != NULL) {
1177         *count_all = 0;
1178     }
1179     if (count_clean != NULL) {
1180         *count_clean = 0;
1181     }
1182     if (rsc == NULL) {
1183         return NULL;
1184     }
1185     for (GList *iter = rsc->running_on; iter != NULL; iter = iter->next) {
1186         if (!pe__count_active_node(rsc, (pcmk_node_t *) iter->data, &active,
1187                                    count_all, count_clean)) {
1188             break; // Don't waste time iterating if we don't have to
1189         }
1190     }
1191     return active;
1192 }
1193 
1194 /*!
1195  * \brief
1196  * \internal Find and count active nodes according to \c PCMK_META_REQUIRES
1197  *
1198  * \param[in]  rsc    Resource to check
1199  * \param[out] count  If not NULL, will be set to count of active nodes
1200  *
1201  * \return An active node (or NULL if resource is not active anywhere)
1202  *
1203  * \note This is a convenience wrapper for active_node() where the count of all
1204  *       active nodes or only clean active nodes is desired according to the
1205  *       \c PCMK_META_REQUIRES meta-attribute.
1206  */
1207 pcmk_node_t *
1208 pe__find_active_requires(const pcmk_resource_t *rsc, unsigned int *count)
     /* [previous][next][first][last][top][bottom][index][help] */
1209 {
1210     if (rsc == NULL) {
1211         if (count != NULL) {
1212             *count = 0;
1213         }
1214         return NULL;
1215 
1216     } else if (pcmk_is_set(rsc->flags, pcmk_rsc_needs_fencing)) {
1217         return rsc->fns->active_node(rsc, count, NULL);
1218 
1219     } else {
1220         return rsc->fns->active_node(rsc, NULL, count);
1221     }
1222 }
1223 
1224 void
1225 pe__count_common(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1226 {
1227     if (rsc->children != NULL) {
1228         for (GList *item = rsc->children; item != NULL; item = item->next) {
1229             ((pcmk_resource_t *) item->data)->fns->count(item->data);
1230         }
1231 
1232     } else if (!pcmk_is_set(rsc->flags, pcmk_rsc_removed)
1233                || (rsc->role > pcmk_role_stopped)) {
1234         rsc->cluster->ninstances++;
1235         if (pe__resource_is_disabled(rsc)) {
1236             rsc->cluster->disabled_resources++;
1237         }
1238         if (pcmk_is_set(rsc->flags, pcmk_rsc_blocked)) {
1239             rsc->cluster->blocked_resources++;
1240         }
1241     }
1242 }
1243 
1244 /*!
1245  * \internal
1246  * \brief Update a resource's next role
1247  *
1248  * \param[in,out] rsc   Resource to be updated
1249  * \param[in]     role  Resource's new next role
1250  * \param[in]     why   Human-friendly reason why role is changing (for logs)
1251  */
1252 void
1253 pe__set_next_role(pcmk_resource_t *rsc, enum rsc_role_e role, const char *why)
     /* [previous][next][first][last][top][bottom][index][help] */
1254 {
1255     CRM_ASSERT((rsc != NULL) && (why != NULL));
1256     if (rsc->next_role != role) {
1257         pcmk__rsc_trace(rsc, "Resetting next role for %s from %s to %s (%s)",
1258                         rsc->id, pcmk_role_text(rsc->next_role),
1259                         pcmk_role_text(role), why);
1260         rsc->next_role = role;
1261     }
1262 }

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