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. unpack_priority
  15. unpack_stickiness
  16. pe__unpack_resource
  17. is_parent
  18. uber_parent
  19. pe__const_top_resource
  20. common_free
  21. pe__count_active_node
  22. active_node
  23. pe__find_active_requires
  24. pe__count_common
  25. 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 Parse resource priority from meta-attribute
 624  *
 625  * \param[in,out] rsc  Resource being unpacked
 626  */
 627 static void
 628 unpack_priority(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 629 {
 630     const char *value = g_hash_table_lookup(rsc->meta,
 631                                             PCMK_META_PRIORITY);
 632     int rc = pcmk_parse_score(value, &(rsc->priority), 0);
 633 
 634     if (rc != pcmk_rc_ok) {
 635         pcmk__config_warn("Using default (0) for resource %s "
 636                           PCMK_META_PRIORITY
 637                           " because '%s' is not a valid value: %s",
 638                           rsc->id, value, pcmk_rc_str(rc));
 639     }
 640 }
 641 
 642 /*!
 643  * \internal
 644  * \brief Parse resource stickiness from meta-attribute
 645  *
 646  * \param[in,out] rsc  Resource being unpacked
 647  */
 648 static void
 649 unpack_stickiness(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 650 {
 651     const char *value = g_hash_table_lookup(rsc->meta,
 652                                             PCMK_META_RESOURCE_STICKINESS);
 653 
 654     if (pcmk__str_eq(value, PCMK_VALUE_DEFAULT, pcmk__str_casei)) {
 655         // @COMPAT Deprecated since 2.1.8
 656         pcmk__config_warn("Support for setting "
 657                           PCMK_META_RESOURCE_STICKINESS
 658                           " to the explicit value '" PCMK_VALUE_DEFAULT
 659                           "' is deprecated and will be removed in a "
 660                           "future release (just leave it unset)");
 661     } else {
 662         int rc = pcmk_parse_score(value, &(rsc->stickiness), 0);
 663 
 664         if (rc != pcmk_rc_ok) {
 665             pcmk__config_warn("Using default (0) for resource %s "
 666                               PCMK_META_RESOURCE_STICKINESS
 667                               " because '%s' is not a valid value: %s",
 668                               rsc->id, value, pcmk_rc_str(rc));
 669         }
 670     }
 671 }
 672 
 673 /*!
 674  * \internal
 675  * \brief Unpack configuration XML for a given resource
 676  *
 677  * Unpack the XML object containing a resource's configuration into a new
 678  * \c pcmk_resource_t object.
 679  *
 680  * \param[in]     xml_obj    XML node containing the resource's configuration
 681  * \param[out]    rsc        Where to store the unpacked resource information
 682  * \param[in]     parent     Resource's parent, if any
 683  * \param[in,out] scheduler  Scheduler data
 684  *
 685  * \return Standard Pacemaker return code
 686  * \note If pcmk_rc_ok is returned, \p *rsc is guaranteed to be non-NULL, and
 687  *       the caller is responsible for freeing it using its variant-specific
 688  *       free() method. Otherwise, \p *rsc is guaranteed to be NULL.
 689  */
 690 int
 691 pe__unpack_resource(xmlNode *xml_obj, pcmk_resource_t **rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 692                     pcmk_resource_t *parent, pcmk_scheduler_t *scheduler)
 693 {
 694     xmlNode *expanded_xml = NULL;
 695     xmlNode *ops = NULL;
 696     const char *value = NULL;
 697     const char *id = NULL;
 698     bool guest_node = false;
 699     bool remote_node = false;
 700 
 701     pe_rule_eval_data_t rule_data = {
 702         .node_hash = NULL,
 703         .now = NULL,
 704         .match_data = NULL,
 705         .rsc_data = NULL,
 706         .op_data = NULL
 707     };
 708 
 709     CRM_CHECK(rsc != NULL, return EINVAL);
 710     CRM_CHECK((xml_obj != NULL) && (scheduler != NULL),
 711               *rsc = NULL;
 712               return EINVAL);
 713 
 714     rule_data.now = scheduler->now;
 715 
 716     crm_log_xml_trace(xml_obj, "[raw XML]");
 717 
 718     id = crm_element_value(xml_obj, PCMK_XA_ID);
 719     if (id == NULL) {
 720         pcmk__config_err("Ignoring <%s> configuration without " PCMK_XA_ID,
 721                          xml_obj->name);
 722         return pcmk_rc_unpack_error;
 723     }
 724 
 725     if (unpack_template(xml_obj, &expanded_xml, scheduler) == FALSE) {
 726         return pcmk_rc_unpack_error;
 727     }
 728 
 729     *rsc = calloc(1, sizeof(pcmk_resource_t));
 730     if (*rsc == NULL) {
 731         pcmk__sched_err("Unable to allocate memory for resource '%s'", id);
 732         return ENOMEM;
 733     }
 734     (*rsc)->cluster = scheduler;
 735 
 736     if (expanded_xml) {
 737         crm_log_xml_trace(expanded_xml, "[expanded XML]");
 738         (*rsc)->xml = expanded_xml;
 739         (*rsc)->orig_xml = xml_obj;
 740 
 741     } else {
 742         (*rsc)->xml = xml_obj;
 743         (*rsc)->orig_xml = NULL;
 744     }
 745 
 746     /* Do not use xml_obj from here on, use (*rsc)->xml in case templates are involved */
 747 
 748     (*rsc)->parent = parent;
 749 
 750     ops = pcmk__xe_first_child((*rsc)->xml, PCMK_XE_OPERATIONS, NULL, NULL);
 751     (*rsc)->ops_xml = expand_idref(ops, scheduler->input);
 752 
 753     (*rsc)->variant = get_resource_type((const char *) (*rsc)->xml->name);
 754     if ((*rsc)->variant == pcmk_rsc_variant_unknown) {
 755         pcmk__config_err("Ignoring resource '%s' of unknown type '%s'",
 756                          id, (*rsc)->xml->name);
 757         common_free(*rsc);
 758         *rsc = NULL;
 759         return pcmk_rc_unpack_error;
 760     }
 761 
 762     (*rsc)->meta = pcmk__strkey_table(free, free);
 763     (*rsc)->allowed_nodes = pcmk__strkey_table(NULL, free);
 764     (*rsc)->known_on = pcmk__strkey_table(NULL, free);
 765 
 766     value = crm_element_value((*rsc)->xml, PCMK__META_CLONE);
 767     if (value) {
 768         (*rsc)->id = crm_strdup_printf("%s:%s", id, value);
 769         pcmk__insert_meta(*rsc, PCMK__META_CLONE, value);
 770 
 771     } else {
 772         (*rsc)->id = strdup(id);
 773     }
 774 
 775     warn_about_deprecated_classes(*rsc);
 776 
 777     (*rsc)->fns = &resource_class_functions[(*rsc)->variant];
 778 
 779     get_meta_attributes((*rsc)->meta, *rsc, NULL, scheduler);
 780     (*rsc)->parameters = pe_rsc_params(*rsc, NULL, scheduler); // \deprecated
 781 
 782     (*rsc)->flags = 0;
 783     pcmk__set_rsc_flags(*rsc, pcmk_rsc_runnable|pcmk_rsc_unassigned);
 784 
 785     if (!pcmk_is_set(scheduler->flags, pcmk_sched_in_maintenance)) {
 786         pcmk__set_rsc_flags(*rsc, pcmk_rsc_managed);
 787     }
 788 
 789     (*rsc)->rsc_cons = NULL;
 790     (*rsc)->rsc_tickets = NULL;
 791     (*rsc)->actions = NULL;
 792     (*rsc)->role = pcmk_role_stopped;
 793     (*rsc)->next_role = pcmk_role_unknown;
 794 
 795     (*rsc)->recovery_type = pcmk_multiply_active_restart;
 796     (*rsc)->stickiness = 0;
 797     (*rsc)->migration_threshold = PCMK_SCORE_INFINITY;
 798     (*rsc)->failure_timeout = 0;
 799 
 800     unpack_priority(*rsc);
 801 
 802     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_CRITICAL);
 803     if ((value == NULL) || crm_is_true(value)) {
 804         pcmk__set_rsc_flags(*rsc, pcmk_rsc_critical);
 805     }
 806 
 807     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_NOTIFY);
 808     if (crm_is_true(value)) {
 809         pcmk__set_rsc_flags(*rsc, pcmk_rsc_notify);
 810     }
 811 
 812     if (xml_contains_remote_node((*rsc)->xml)) {
 813         (*rsc)->is_remote_node = TRUE;
 814         if (g_hash_table_lookup((*rsc)->meta, PCMK__META_CONTAINER)) {
 815             guest_node = true;
 816         } else {
 817             remote_node = true;
 818         }
 819     }
 820 
 821     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_ALLOW_MIGRATE);
 822     if (crm_is_true(value)) {
 823         pcmk__set_rsc_flags(*rsc, pcmk_rsc_migratable);
 824     } else if ((value == NULL) && remote_node) {
 825         /* By default, we want remote nodes to be able
 826          * to float around the cluster without having to stop all the
 827          * resources within the remote-node before moving. Allowing
 828          * migration support enables this feature. If this ever causes
 829          * problems, migration support can be explicitly turned off with
 830          * PCMK_META_ALLOW_MIGRATE=false.
 831          */
 832         pcmk__set_rsc_flags(*rsc, pcmk_rsc_migratable);
 833     }
 834 
 835     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_IS_MANAGED);
 836     if (value != NULL) {
 837         if (pcmk__str_eq(PCMK_VALUE_DEFAULT, value, pcmk__str_casei)) {
 838             // @COMPAT Deprecated since 2.1.8
 839             pcmk__config_warn("Support for setting " PCMK_META_IS_MANAGED
 840                               " to the explicit value '" PCMK_VALUE_DEFAULT
 841                               "' is deprecated and will be removed in a "
 842                               "future release (just leave it unset)");
 843         } else if (crm_is_true(value)) {
 844             pcmk__set_rsc_flags(*rsc, pcmk_rsc_managed);
 845         } else {
 846             pcmk__clear_rsc_flags(*rsc, pcmk_rsc_managed);
 847         }
 848     }
 849 
 850     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_MAINTENANCE);
 851     if (crm_is_true(value)) {
 852         pcmk__clear_rsc_flags(*rsc, pcmk_rsc_managed);
 853         pcmk__set_rsc_flags(*rsc, pcmk_rsc_maintenance);
 854     }
 855     if (pcmk_is_set(scheduler->flags, pcmk_sched_in_maintenance)) {
 856         pcmk__clear_rsc_flags(*rsc, pcmk_rsc_managed);
 857         pcmk__set_rsc_flags(*rsc, pcmk_rsc_maintenance);
 858     }
 859 
 860     if (pcmk__is_clone(pe__const_top_resource(*rsc, false))) {
 861         value = g_hash_table_lookup((*rsc)->meta, PCMK_META_GLOBALLY_UNIQUE);
 862         if (crm_is_true(value)) {
 863             pcmk__set_rsc_flags(*rsc, pcmk_rsc_unique);
 864         }
 865         if (detect_promotable(*rsc)) {
 866             pcmk__set_rsc_flags(*rsc, pcmk_rsc_promotable);
 867         }
 868     } else {
 869         pcmk__set_rsc_flags(*rsc, pcmk_rsc_unique);
 870     }
 871 
 872     // @COMPAT Deprecated meta-attribute
 873     value = g_hash_table_lookup((*rsc)->meta, PCMK__META_RESTART_TYPE);
 874     if (pcmk__str_eq(value, PCMK_VALUE_RESTART, pcmk__str_casei)) {
 875         (*rsc)->restart_type = pe_restart_restart;
 876         pcmk__rsc_trace(*rsc, "%s dependency restart handling: restart",
 877                         (*rsc)->id);
 878         pcmk__warn_once(pcmk__wo_restart_type,
 879                         "Support for " PCMK__META_RESTART_TYPE " is deprecated "
 880                         "and will be removed in a future release");
 881 
 882     } else {
 883         (*rsc)->restart_type = pe_restart_ignore;
 884         pcmk__rsc_trace(*rsc, "%s dependency restart handling: ignore",
 885                         (*rsc)->id);
 886     }
 887 
 888     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_MULTIPLE_ACTIVE);
 889     if (pcmk__str_eq(value, PCMK_VALUE_STOP_ONLY, pcmk__str_casei)) {
 890         (*rsc)->recovery_type = pcmk_multiply_active_stop;
 891         pcmk__rsc_trace(*rsc, "%s multiple running resource recovery: stop only",
 892                         (*rsc)->id);
 893 
 894     } else if (pcmk__str_eq(value, PCMK_VALUE_BLOCK, pcmk__str_casei)) {
 895         (*rsc)->recovery_type = pcmk_multiply_active_block;
 896         pcmk__rsc_trace(*rsc, "%s multiple running resource recovery: block",
 897                         (*rsc)->id);
 898 
 899     } else if (pcmk__str_eq(value, PCMK_VALUE_STOP_UNEXPECTED,
 900                             pcmk__str_casei)) {
 901         (*rsc)->recovery_type = pcmk_multiply_active_unexpected;
 902         pcmk__rsc_trace(*rsc,
 903                         "%s multiple running resource recovery: "
 904                         "stop unexpected instances",
 905                         (*rsc)->id);
 906 
 907     } else { // PCMK_VALUE_STOP_START
 908         if (!pcmk__str_eq(value, PCMK_VALUE_STOP_START,
 909                           pcmk__str_casei|pcmk__str_null_matches)) {
 910             pcmk__config_warn("%s is not a valid value for "
 911                               PCMK_META_MULTIPLE_ACTIVE
 912                               ", using default of "
 913                               "\"" PCMK_VALUE_STOP_START "\"",
 914                               value);
 915         }
 916         (*rsc)->recovery_type = pcmk_multiply_active_restart;
 917         pcmk__rsc_trace(*rsc,
 918                         "%s multiple running resource recovery: stop/start",
 919                         (*rsc)->id);
 920     }
 921 
 922     unpack_stickiness(*rsc);
 923 
 924     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_MIGRATION_THRESHOLD);
 925     if (value != NULL) {
 926         if (pcmk__str_eq(PCMK_VALUE_DEFAULT, value, pcmk__str_casei)) {
 927             // @COMPAT Deprecated since 2.1.8
 928             pcmk__config_warn("Support for setting "
 929                               PCMK_META_MIGRATION_THRESHOLD
 930                               " to the explicit value '" PCMK_VALUE_DEFAULT
 931                               "' is deprecated and will be removed in a "
 932                               "future release (just leave it unset)");
 933         } else {
 934             (*rsc)->migration_threshold = char2score(value);
 935             if ((*rsc)->migration_threshold < 0) {
 936                 /* @COMPAT We use 1 here to preserve previous behavior, but this
 937                  * should probably use the default (INFINITY) or 0 (to disable)
 938                  * instead.
 939                  */
 940                 pcmk__warn_once(pcmk__wo_neg_threshold,
 941                                 PCMK_META_MIGRATION_THRESHOLD
 942                                 " must be non-negative, using 1 instead");
 943                 (*rsc)->migration_threshold = 1;
 944             }
 945         }
 946     }
 947 
 948     if (pcmk__str_eq(crm_element_value((*rsc)->xml, PCMK_XA_CLASS),
 949                      PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) {
 950         pcmk__set_scheduler_flags(scheduler, pcmk_sched_have_fencing);
 951         pcmk__set_rsc_flags(*rsc, pcmk_rsc_fence_device);
 952     }
 953 
 954     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_REQUIRES);
 955     unpack_requires(*rsc, value, false);
 956 
 957     value = g_hash_table_lookup((*rsc)->meta, PCMK_META_FAILURE_TIMEOUT);
 958     if (value != NULL) {
 959         guint interval_ms = 0U;
 960 
 961         // Stored as seconds
 962         pcmk_parse_interval_spec(value, &interval_ms);
 963         (*rsc)->failure_timeout = (int) (interval_ms / 1000);
 964     }
 965 
 966     if (remote_node) {
 967         GHashTable *params = pe_rsc_params(*rsc, NULL, scheduler);
 968 
 969         /* Grabbing the value now means that any rules based on node attributes
 970          * will evaluate to false, so such rules should not be used with
 971          * PCMK_REMOTE_RA_RECONNECT_INTERVAL.
 972          *
 973          * @TODO Evaluate per node before using
 974          */
 975         value = g_hash_table_lookup(params, PCMK_REMOTE_RA_RECONNECT_INTERVAL);
 976         if (value) {
 977             /* reconnect delay works by setting failure_timeout and preventing the
 978              * connection from starting until the failure is cleared. */
 979             pcmk_parse_interval_spec(value, &((*rsc)->remote_reconnect_ms));
 980 
 981             /* We want to override any default failure_timeout in use when remote
 982              * PCMK_REMOTE_RA_RECONNECT_INTERVAL is in use.
 983              */
 984             (*rsc)->failure_timeout = (*rsc)->remote_reconnect_ms / 1000;
 985         }
 986     }
 987 
 988     get_target_role(*rsc, &((*rsc)->next_role));
 989     pcmk__rsc_trace(*rsc, "%s desired next state: %s", (*rsc)->id,
 990                     ((*rsc)->next_role == pcmk_role_unknown)?
 991                         "default" : pcmk_role_text((*rsc)->next_role));
 992 
 993     if ((*rsc)->fns->unpack(*rsc, scheduler) == FALSE) {
 994         (*rsc)->fns->free(*rsc);
 995         *rsc = NULL;
 996         return pcmk_rc_unpack_error;
 997     }
 998 
 999     if (pcmk_is_set(scheduler->flags, pcmk_sched_symmetric_cluster)) {
1000         // This tag must stay exactly the same because it is tested elsewhere
1001         resource_location(*rsc, NULL, 0, "symmetric_default", scheduler);
1002     } else if (guest_node) {
1003         /* remote resources tied to a container resource must always be allowed
1004          * to opt-in to the cluster. Whether the connection resource is actually
1005          * allowed to be placed on a node is dependent on the container resource */
1006         resource_location(*rsc, NULL, 0, "remote_connection_default",
1007                           scheduler);
1008     }
1009 
1010     pcmk__rsc_trace(*rsc, "%s action notification: %s", (*rsc)->id,
1011                     pcmk_is_set((*rsc)->flags, pcmk_rsc_notify)? "required" : "not required");
1012 
1013     (*rsc)->utilization = pcmk__strkey_table(free, free);
1014 
1015     pe__unpack_dataset_nvpairs((*rsc)->xml, PCMK_XE_UTILIZATION, &rule_data,
1016                                (*rsc)->utilization, NULL, FALSE, scheduler);
1017 
1018     if (expanded_xml) {
1019         if (add_template_rsc(xml_obj, scheduler) == FALSE) {
1020             (*rsc)->fns->free(*rsc);
1021             *rsc = NULL;
1022             return pcmk_rc_unpack_error;
1023         }
1024     }
1025     return pcmk_rc_ok;
1026 }
1027 
1028 gboolean
1029 is_parent(pcmk_resource_t *child, pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1030 {
1031     pcmk_resource_t *parent = child;
1032 
1033     if (parent == NULL || rsc == NULL) {
1034         return FALSE;
1035     }
1036     while (parent->parent != NULL) {
1037         if (parent->parent == rsc) {
1038             return TRUE;
1039         }
1040         parent = parent->parent;
1041     }
1042     return FALSE;
1043 }
1044 
1045 pcmk_resource_t *
1046 uber_parent(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1047 {
1048     pcmk_resource_t *parent = rsc;
1049 
1050     if (parent == NULL) {
1051         return NULL;
1052     }
1053     while ((parent->parent != NULL) && !pcmk__is_bundle(parent->parent)) {
1054         parent = parent->parent;
1055     }
1056     return parent;
1057 }
1058 
1059 /*!
1060  * \internal
1061  * \brief Get the topmost parent of a resource as a const pointer
1062  *
1063  * \param[in] rsc             Resource to check
1064  * \param[in] include_bundle  If true, go all the way to bundle
1065  *
1066  * \return \p NULL if \p rsc is NULL, \p rsc if \p rsc has no parent,
1067  *         the bundle if \p rsc is bundled and \p include_bundle is true,
1068  *         otherwise the topmost parent of \p rsc up to a clone
1069  */
1070 const pcmk_resource_t *
1071 pe__const_top_resource(const pcmk_resource_t *rsc, bool include_bundle)
     /* [previous][next][first][last][top][bottom][index][help] */
1072 {
1073     const pcmk_resource_t *parent = rsc;
1074 
1075     if (parent == NULL) {
1076         return NULL;
1077     }
1078     while (parent->parent != NULL) {
1079         if (!include_bundle && pcmk__is_bundle(parent->parent)) {
1080             break;
1081         }
1082         parent = parent->parent;
1083     }
1084     return parent;
1085 }
1086 
1087 void
1088 common_free(pcmk_resource_t * rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1089 {
1090     if (rsc == NULL) {
1091         return;
1092     }
1093 
1094     pcmk__rsc_trace(rsc, "Freeing %s %d", rsc->id, rsc->variant);
1095 
1096     g_list_free(rsc->rsc_cons);
1097     g_list_free(rsc->rsc_cons_lhs);
1098     g_list_free(rsc->rsc_tickets);
1099     g_list_free(rsc->dangling_migrations);
1100 
1101     if (rsc->parameter_cache != NULL) {
1102         g_hash_table_destroy(rsc->parameter_cache);
1103     }
1104     if (rsc->meta != NULL) {
1105         g_hash_table_destroy(rsc->meta);
1106     }
1107     if (rsc->utilization != NULL) {
1108         g_hash_table_destroy(rsc->utilization);
1109     }
1110 
1111     if ((rsc->parent == NULL)
1112         && pcmk_is_set(rsc->flags, pcmk_rsc_removed)) {
1113 
1114         free_xml(rsc->xml);
1115         rsc->xml = NULL;
1116         free_xml(rsc->orig_xml);
1117         rsc->orig_xml = NULL;
1118 
1119         /* if rsc->orig_xml, then rsc->xml is an expanded xml from a template */
1120     } else if (rsc->orig_xml) {
1121         free_xml(rsc->xml);
1122         rsc->xml = NULL;
1123     }
1124     if (rsc->running_on) {
1125         g_list_free(rsc->running_on);
1126         rsc->running_on = NULL;
1127     }
1128     if (rsc->known_on) {
1129         g_hash_table_destroy(rsc->known_on);
1130         rsc->known_on = NULL;
1131     }
1132     if (rsc->actions) {
1133         g_list_free(rsc->actions);
1134         rsc->actions = NULL;
1135     }
1136     if (rsc->allowed_nodes) {
1137         g_hash_table_destroy(rsc->allowed_nodes);
1138         rsc->allowed_nodes = NULL;
1139     }
1140     g_list_free(rsc->fillers);
1141     g_list_free(rsc->rsc_location);
1142     pcmk__rsc_trace(rsc, "Resource freed");
1143     free(rsc->id);
1144     free(rsc->clone_name);
1145     free(rsc->allocated_to);
1146     free(rsc->variant_opaque);
1147     free(rsc->pending_task);
1148     free(rsc);
1149 }
1150 
1151 /*!
1152  * \internal
1153  * \brief Count a node and update most preferred to it as appropriate
1154  *
1155  * \param[in]     rsc          An active resource
1156  * \param[in]     node         A node that \p rsc is active on
1157  * \param[in,out] active       This will be set to \p node if \p node is more
1158  *                             preferred than the current value
1159  * \param[in,out] count_all    If not NULL, this will be incremented
1160  * \param[in,out] count_clean  If not NULL, this will be incremented if \p node
1161  *                             is online and clean
1162  *
1163  * \return true if the count should continue, or false if sufficiently known
1164  */
1165 bool
1166 pe__count_active_node(const pcmk_resource_t *rsc, pcmk_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
1167                       pcmk_node_t **active, unsigned int *count_all,
1168                       unsigned int *count_clean)
1169 {
1170     bool keep_looking = false;
1171     bool is_happy = false;
1172 
1173     CRM_CHECK((rsc != NULL) && (node != NULL) && (active != NULL),
1174               return false);
1175 
1176     is_happy = node->details->online && !node->details->unclean;
1177 
1178     if (count_all != NULL) {
1179         ++*count_all;
1180     }
1181     if ((count_clean != NULL) && is_happy) {
1182         ++*count_clean;
1183     }
1184     if ((count_all != NULL) || (count_clean != NULL)) {
1185         keep_looking = true; // We're counting, so go through entire list
1186     }
1187 
1188     if (rsc->partial_migration_source != NULL) {
1189         if (pcmk__same_node(node, rsc->partial_migration_source)) {
1190             *active = node; // This is the migration source
1191         } else {
1192             keep_looking = true;
1193         }
1194     } else if (!pcmk_is_set(rsc->flags, pcmk_rsc_needs_fencing)) {
1195         if (is_happy && ((*active == NULL) || !(*active)->details->online
1196                          || (*active)->details->unclean)) {
1197             *active = node; // This is the first clean node
1198         } else {
1199             keep_looking = true;
1200         }
1201     }
1202     if (*active == NULL) {
1203         *active = node; // This is the first node checked
1204     }
1205     return keep_looking;
1206 }
1207 
1208 // Shared implementation of pcmk_rsc_methods_t:active_node()
1209 static pcmk_node_t *
1210 active_node(const pcmk_resource_t *rsc, unsigned int *count_all,
     /* [previous][next][first][last][top][bottom][index][help] */
1211             unsigned int *count_clean)
1212 {
1213     pcmk_node_t *active = NULL;
1214 
1215     if (count_all != NULL) {
1216         *count_all = 0;
1217     }
1218     if (count_clean != NULL) {
1219         *count_clean = 0;
1220     }
1221     if (rsc == NULL) {
1222         return NULL;
1223     }
1224     for (GList *iter = rsc->running_on; iter != NULL; iter = iter->next) {
1225         if (!pe__count_active_node(rsc, (pcmk_node_t *) iter->data, &active,
1226                                    count_all, count_clean)) {
1227             break; // Don't waste time iterating if we don't have to
1228         }
1229     }
1230     return active;
1231 }
1232 
1233 /*!
1234  * \brief
1235  * \internal Find and count active nodes according to \c PCMK_META_REQUIRES
1236  *
1237  * \param[in]  rsc    Resource to check
1238  * \param[out] count  If not NULL, will be set to count of active nodes
1239  *
1240  * \return An active node (or NULL if resource is not active anywhere)
1241  *
1242  * \note This is a convenience wrapper for active_node() where the count of all
1243  *       active nodes or only clean active nodes is desired according to the
1244  *       \c PCMK_META_REQUIRES meta-attribute.
1245  */
1246 pcmk_node_t *
1247 pe__find_active_requires(const pcmk_resource_t *rsc, unsigned int *count)
     /* [previous][next][first][last][top][bottom][index][help] */
1248 {
1249     if (rsc == NULL) {
1250         if (count != NULL) {
1251             *count = 0;
1252         }
1253         return NULL;
1254 
1255     } else if (pcmk_is_set(rsc->flags, pcmk_rsc_needs_fencing)) {
1256         return rsc->fns->active_node(rsc, count, NULL);
1257 
1258     } else {
1259         return rsc->fns->active_node(rsc, NULL, count);
1260     }
1261 }
1262 
1263 void
1264 pe__count_common(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1265 {
1266     if (rsc->children != NULL) {
1267         for (GList *item = rsc->children; item != NULL; item = item->next) {
1268             ((pcmk_resource_t *) item->data)->fns->count(item->data);
1269         }
1270 
1271     } else if (!pcmk_is_set(rsc->flags, pcmk_rsc_removed)
1272                || (rsc->role > pcmk_role_stopped)) {
1273         rsc->cluster->ninstances++;
1274         if (pe__resource_is_disabled(rsc)) {
1275             rsc->cluster->disabled_resources++;
1276         }
1277         if (pcmk_is_set(rsc->flags, pcmk_rsc_blocked)) {
1278             rsc->cluster->blocked_resources++;
1279         }
1280     }
1281 }
1282 
1283 /*!
1284  * \internal
1285  * \brief Update a resource's next role
1286  *
1287  * \param[in,out] rsc   Resource to be updated
1288  * \param[in]     role  Resource's new next role
1289  * \param[in]     why   Human-friendly reason why role is changing (for logs)
1290  */
1291 void
1292 pe__set_next_role(pcmk_resource_t *rsc, enum rsc_role_e role, const char *why)
     /* [previous][next][first][last][top][bottom][index][help] */
1293 {
1294     pcmk__assert((rsc != NULL) && (why != NULL));
1295     if (rsc->next_role != role) {
1296         pcmk__rsc_trace(rsc, "Resetting next role for %s from %s to %s (%s)",
1297                         rsc->id, pcmk_role_text(rsc->next_role),
1298                         pcmk_role_text(role), why);
1299         rsc->next_role = role;
1300     }
1301 }

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