root/lib/pacemaker/pcmk_sched_location.c

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

DEFINITIONS

This source file includes following definitions.
  1. parse_location_role
  2. score_attribute_name
  3. score_from_rule
  4. score_from_attr
  5. generate_location_rule
  6. unpack_rsc_location
  7. unpack_simple_location
  8. unpack_location_tags
  9. unpack_location_set
  10. pcmk__unpack_location
  11. pcmk__new_location
  12. pcmk__apply_locations
  13. pcmk__apply_location

   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 General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdbool.h>
  13 #include <glib.h>
  14 
  15 #include <crm/crm.h>
  16 #include <crm/common/rules_internal.h>
  17 #include <crm/pengine/status.h>
  18 #include <crm/pengine/rules.h>
  19 #include <pacemaker-internal.h>
  20 
  21 #include "libpacemaker_private.h"
  22 
  23 /*!
  24  * \internal
  25  * \brief Parse a role configuration for a location constraint
  26  *
  27  * \param[in]  role_spec  Role specification
  28  * \param[out] role       Where to store parsed role
  29  *
  30  * \return true if role specification is valid, otherwise false
  31  */
  32 static bool
  33 parse_location_role(const char *role_spec, enum rsc_role_e *role)
     /* [previous][next][first][last][top][bottom][index][help] */
  34 {
  35     if (role_spec == NULL) {
  36         *role = pcmk_role_unknown;
  37         return true;
  38     }
  39 
  40     *role = pcmk_parse_role(role_spec);
  41     switch (*role) {
  42         case pcmk_role_unknown:
  43             return false;
  44 
  45         case pcmk_role_started:
  46         case pcmk_role_unpromoted:
  47             /* Any promotable clone instance cannot be promoted without being in
  48              * the unpromoted role first. Therefore, any constraint for the
  49              * started or unpromoted role applies to every role.
  50              */
  51             *role = pcmk_role_unknown;
  52             break;
  53 
  54         default:
  55             break;
  56     }
  57     return true;
  58 }
  59 
  60 /*!
  61  * \internal
  62  * \brief Get the score attribute name (if any) used for a rule
  63  *
  64  * \param[in]  rule_xml    Rule XML
  65  * \param[out] allocated   If the score attribute name needs to be allocated,
  66  *                         this will be set to the non-const equivalent of the
  67  *                         return value (should be set to NULL when passed)
  68  * \param[in]  rule_input  Values used to evaluate rule criteria
  69  *
  70  * \return Score attribute name used for rule, or NULL if none
  71  * \note The caller is responsible for freeing \p *allocated if it is non-NULL.
  72  */
  73 static const char *
  74 score_attribute_name(const xmlNode *rule_xml, char **allocated,
     /* [previous][next][first][last][top][bottom][index][help] */
  75                      const pcmk_rule_input_t *rule_input)
  76 {
  77     const char *name = NULL;
  78 
  79     name = crm_element_value(rule_xml, PCMK_XA_SCORE_ATTRIBUTE);
  80     if (name == NULL) {
  81         return NULL;
  82     }
  83 
  84     /* A score attribute name may use submatches extracted from a
  85      * resource ID regular expression. For example, if score-attribute is
  86      * "loc-\1", rsc-pattern is "ip-(.*)", and the resource ID is "ip-db", then
  87      * the score attribute name is "loc-db".
  88      */
  89     if ((rule_input->rsc_id != NULL) && (rule_input->rsc_id_nmatches > 0)) {
  90         *allocated = pcmk__replace_submatches(name, rule_input->rsc_id,
  91                                               rule_input->rsc_id_submatches,
  92                                               rule_input->rsc_id_nmatches);
  93         if (*allocated != NULL) {
  94             name = *allocated;
  95         }
  96     }
  97     return name;
  98 }
  99 
 100 /*!
 101  * \internal
 102  * \brief Parse a score from a rule without a score attribute
 103  *
 104  * \param[in]  rule_xml    Rule XML
 105  * \param[out] score       Where to store parsed score
 106  *
 107  * \return Standard Pacemaker return code
 108  */
 109 static int
 110 score_from_rule(const xmlNode *rule_xml, int *score)
     /* [previous][next][first][last][top][bottom][index][help] */
 111 {
 112     int rc = pcmk_rc_ok;
 113     const char *score_s = crm_element_value(rule_xml, PCMK_XA_SCORE);
 114 
 115     if (score_s == NULL) { // Not possible with schema validation enabled
 116         pcmk__config_err("Ignoring location constraint rule %s because "
 117                          "neither " PCMK_XA_SCORE " nor "
 118                          PCMK_XA_SCORE_ATTRIBUTE " was specified",
 119                          pcmk__xe_id(rule_xml));
 120         return pcmk_rc_unpack_error;
 121     }
 122 
 123     rc = pcmk_parse_score(score_s, score, 0);
 124     if (rc != pcmk_rc_ok) { // Not possible with schema validation enabled
 125         pcmk__config_err("Ignoring location constraint rule %s because "
 126                          "'%s' is not a valid " PCMK_XA_SCORE ": %s",
 127                          pcmk__xe_id(rule_xml), score_s, pcmk_rc_str(rc));
 128         return pcmk_rc_unpack_error;
 129     }
 130 
 131     return pcmk_rc_ok;
 132 }
 133 
 134 /*!
 135  * \internal
 136  * \brief Get a rule score from a node attribute
 137  *
 138  * \param[in]  constraint_id  Location constraint ID (for logging only)
 139  * \param[in]  attr_name      Name of node attribute with score
 140  * \param[in]  node           Node to get attribute for
 141  * \param[in]  rsc            Resource being located
 142  * \param[out] score          Where to store parsed score
 143  *
 144  * \return Standard Pacemaker return code (pcmk_rc_ok if a valid score was
 145  *         parsed, ENXIO if the node attribute was unset, and some other value
 146  *         if the node attribute value was invalid)
 147  */
 148 static int
 149 score_from_attr(const char *constraint_id, const char *attr_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 150                 const pcmk_node_t *node, const pcmk_resource_t *rsc, int *score)
 151 {
 152     int rc = pcmk_rc_ok;
 153     const char *target = NULL;
 154     const char *score_s = NULL;
 155 
 156     target = g_hash_table_lookup(rsc->priv->meta,
 157                                  PCMK_META_CONTAINER_ATTRIBUTE_TARGET);
 158     score_s = pcmk__node_attr(node, attr_name, target, pcmk__rsc_node_current);
 159     if (pcmk__str_empty(score_s)) {
 160         crm_info("Ignoring location %s for %s on %s "
 161                  "because it has no node attribute %s",
 162                  constraint_id, rsc->id, pcmk__node_name(node), attr_name);
 163         return ENXIO;
 164     }
 165 
 166     rc = pcmk_parse_score(score_s, score, 0);
 167     if (rc != pcmk_rc_ok) {
 168         crm_warn("Ignoring location %s for node %s because node "
 169                  "attribute %s value '%s' is not a valid score: %s",
 170                  constraint_id, pcmk__node_name(node), attr_name,
 171                  score_s, pcmk_rc_str(rc));
 172         return rc;
 173     }
 174     return pcmk_rc_ok;
 175 }
 176 
 177 /*!
 178  * \internal
 179  * \brief Generate a location constraint from a rule
 180  *
 181  * \param[in,out] rsc            Resource that constraint is for
 182  * \param[in]     rule_xml       Rule XML (sub-element of location constraint)
 183  * \param[in]     discovery      Value of \c PCMK_XA_RESOURCE_DISCOVERY for
 184  *                               constraint
 185  * \param[out]    next_change    Where to set when rule evaluation will change
 186  * \param[in,out] rule_input     Values used to evaluate rule criteria
 187  *                               (node-specific values will be overwritten by
 188  *                               this function)
 189  * \param[in]     constraint_id  ID of location constraint (for logging only)
 190  *
 191  * \return true if rule is valid, otherwise false
 192  */
 193 static bool
 194 generate_location_rule(pcmk_resource_t *rsc, xmlNode *rule_xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 195                        const char *discovery, crm_time_t *next_change,
 196                        pcmk_rule_input_t *rule_input, const char *constraint_id)
 197 {
 198     const char *rule_id = NULL;
 199     const char *score_attr = NULL;
 200     const char *boolean = NULL;
 201     const char *role_spec = NULL;
 202 
 203     GList *iter = NULL;
 204     int score = 0;
 205     char *local_score_attr = NULL;
 206     pcmk__location_t *location_rule = NULL;
 207     enum rsc_role_e role = pcmk_role_unknown;
 208     enum pcmk__combine combine = pcmk__combine_unknown;
 209 
 210     rule_xml = pcmk__xe_resolve_idref(rule_xml, rsc->priv->scheduler->input);
 211     if (rule_xml == NULL) {
 212         return false; // Error already logged
 213     }
 214 
 215     rule_id = crm_element_value(rule_xml, PCMK_XA_ID);
 216     if (rule_id == NULL) {
 217         pcmk__config_err("Ignoring location constraint '%s' because its rule "
 218                          "has no " PCMK_XA_ID,
 219                          constraint_id);
 220         return false;
 221     }
 222 
 223     boolean = crm_element_value(rule_xml, PCMK_XA_BOOLEAN_OP);
 224     role_spec = crm_element_value(rule_xml, PCMK_XA_ROLE);
 225 
 226     if (parse_location_role(role_spec, &role)) {
 227         crm_trace("Setting rule %s role filter to %s", rule_id, role_spec);
 228     } else {
 229         pcmk__config_err("Ignoring location constraint '%s' because rule '%s' "
 230                          "has invalid " PCMK_XA_ROLE " '%s'",
 231                          constraint_id, rule_id, role_spec);
 232         return false;
 233     }
 234 
 235     combine = pcmk__parse_combine(boolean);
 236     switch (combine) {
 237         case pcmk__combine_and:
 238         case pcmk__combine_or:
 239             break;
 240 
 241         default: // Not possible with schema validation enabled
 242             pcmk__config_err("Ignoring location constraint '%s' because rule "
 243                              "'%s' has invalid " PCMK_XA_BOOLEAN_OP " '%s'",
 244                              constraint_id, rule_id, boolean);
 245             return false;
 246     }
 247 
 248     /* Users may configure the rule with either a score or the name of a
 249      * node attribute whose value should be used as the constraint score for
 250      * that node.
 251      */
 252     score_attr = score_attribute_name(rule_xml, &local_score_attr, rule_input);
 253     if ((score_attr == NULL)
 254         && (score_from_rule(rule_xml, &score) != pcmk_rc_ok)) {
 255         return false;
 256     }
 257 
 258     location_rule = pcmk__new_location(rule_id, rsc, 0, discovery, NULL);
 259     CRM_CHECK(location_rule != NULL, return NULL);
 260 
 261     location_rule->role_filter = role;
 262 
 263     for (iter = rsc->priv->scheduler->nodes;
 264          iter != NULL; iter = iter->next) {
 265 
 266         pcmk_node_t *node = iter->data;
 267         pcmk_node_t *local = NULL;
 268 
 269         rule_input->node_attrs = node->priv->attrs;
 270         rule_input->rsc_params = pe_rsc_params(rsc, node,
 271                                                rsc->priv->scheduler);
 272 
 273         if (pcmk_evaluate_rule(rule_xml, rule_input,
 274                                next_change) != pcmk_rc_ok) {
 275             continue;
 276         }
 277 
 278         if ((score_attr != NULL)
 279             && (score_from_attr(constraint_id, score_attr, node, rsc,
 280                                 &score) != pcmk_rc_ok)) {
 281             continue; // Message already logged
 282         }
 283 
 284         local = pe__copy_node(node);
 285         location_rule->nodes = g_list_prepend(location_rule->nodes, local);
 286         local->assign->score = score;
 287         pcmk__rsc_trace(rsc,
 288                         "Location %s score for %s on %s is %s via rule %s",
 289                         constraint_id, rsc->id, pcmk__node_name(node),
 290                         pcmk_readable_score(score), rule_id);
 291     }
 292 
 293     free(local_score_attr);
 294 
 295     if (location_rule->nodes == NULL) {
 296         crm_trace("No matching nodes for location constraint rule %s", rule_id);
 297     } else {
 298         crm_trace("Location constraint rule %s matched %d nodes",
 299                   rule_id, g_list_length(location_rule->nodes));
 300     }
 301     return true;
 302 }
 303 
 304 static void
 305 unpack_rsc_location(xmlNode *xml_obj, pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 306                     const char *role_spec, const char *score,
 307                     char *rsc_id_match, int rsc_id_nmatches,
 308                     regmatch_t *rsc_id_submatches)
 309 {
 310     const char *rsc_id = crm_element_value(xml_obj, PCMK_XA_RSC);
 311     const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
 312     const char *node = crm_element_value(xml_obj, PCMK_XA_NODE);
 313     const char *discovery = crm_element_value(xml_obj,
 314                                               PCMK_XA_RESOURCE_DISCOVERY);
 315 
 316     if (rsc == NULL) {
 317         pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
 318                           "does not exist", id, rsc_id);
 319         return;
 320     }
 321 
 322     if (score == NULL) {
 323         score = crm_element_value(xml_obj, PCMK_XA_SCORE);
 324     }
 325 
 326     if ((node != NULL) && (score != NULL)) {
 327         int score_i = 0;
 328         int rc = pcmk_rc_ok;
 329         pcmk_node_t *match = pcmk_find_node(rsc->priv->scheduler, node);
 330         enum rsc_role_e role = pcmk_role_unknown;
 331         pcmk__location_t *location = NULL;
 332 
 333         if (match == NULL) {
 334             crm_info("Ignoring location constraint %s "
 335                      "because '%s' is not a known node",
 336                      pcmk__s(id, "without ID"), node);
 337             return;
 338         }
 339 
 340         rc = pcmk_parse_score(score, &score_i, 0);
 341         if (rc != pcmk_rc_ok) { // Not possible with schema validation enabled
 342             pcmk__config_err("Ignoring location constraint %s "
 343                              "because '%s' is not a valid score", id, score);
 344             return;
 345         }
 346 
 347         if (role_spec == NULL) {
 348             role_spec = crm_element_value(xml_obj, PCMK_XA_ROLE);
 349         }
 350         if (parse_location_role(role_spec, &role)) {
 351             crm_trace("Setting location constraint %s role filter: %s",
 352                       id, role_spec);
 353         } else { // Not possible with schema validation enabled
 354             pcmk__config_err("Ignoring location constraint %s "
 355                              "because '%s' is not a valid " PCMK_XA_ROLE,
 356                              id, role_spec);
 357             return;
 358         }
 359 
 360         location = pcmk__new_location(id, rsc, score_i, discovery, match);
 361         if (location == NULL) {
 362             return; // Error already logged
 363         }
 364         location->role_filter = role;
 365 
 366     } else {
 367         crm_time_t *next_change = crm_time_new_undefined();
 368         xmlNode *rule_xml = pcmk__xe_first_child(xml_obj, PCMK_XE_RULE, NULL,
 369                                                  NULL);
 370         pcmk_rule_input_t rule_input = {
 371             .now = rsc->priv->scheduler->priv->now,
 372             .rsc_meta = rsc->priv->meta,
 373             .rsc_id = rsc_id_match,
 374             .rsc_id_submatches = rsc_id_submatches,
 375             .rsc_id_nmatches = rsc_id_nmatches,
 376         };
 377 
 378         generate_location_rule(rsc, rule_xml, discovery, next_change,
 379                                &rule_input, id);
 380 
 381         /* If there is a point in the future when the evaluation of a rule will
 382          * change, make sure the scheduler is re-run by that time.
 383          */
 384         if (crm_time_is_defined(next_change)) {
 385             time_t t = (time_t) crm_time_get_seconds_since_epoch(next_change);
 386 
 387             pe__update_recheck_time(t, rsc->priv->scheduler,
 388                                     "location rule evaluation");
 389         }
 390         crm_time_free(next_change);
 391     }
 392 }
 393 
 394 static void
 395 unpack_simple_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 396 {
 397     const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
 398     const char *value = crm_element_value(xml_obj, PCMK_XA_RSC);
 399 
 400     if (value) {
 401         pcmk_resource_t *rsc;
 402 
 403         rsc = pcmk__find_constraint_resource(scheduler->priv->resources, value);
 404         unpack_rsc_location(xml_obj, rsc, NULL, NULL, NULL, 0, NULL);
 405     }
 406 
 407     value = crm_element_value(xml_obj, PCMK_XA_RSC_PATTERN);
 408     if (value) {
 409         regex_t regex;
 410         bool invert = false;
 411 
 412         if (value[0] == '!') {
 413             value++;
 414             invert = true;
 415         }
 416 
 417         if (regcomp(&regex, value, REG_EXTENDED) != 0) {
 418             pcmk__config_err("Ignoring constraint '%s' because "
 419                              PCMK_XA_RSC_PATTERN
 420                              " has invalid value '%s'", id, value);
 421             return;
 422         }
 423 
 424         for (GList *iter = scheduler->priv->resources;
 425              iter != NULL; iter = iter->next) {
 426 
 427             pcmk_resource_t *r = iter->data;
 428             int nregs = 0;
 429             regmatch_t *pmatch = NULL;
 430             int status;
 431 
 432             if (regex.re_nsub > 0) {
 433                 nregs = regex.re_nsub + 1;
 434             } else {
 435                 nregs = 1;
 436             }
 437             pmatch = pcmk__assert_alloc(nregs, sizeof(regmatch_t));
 438 
 439             status = regexec(&regex, r->id, nregs, pmatch, 0);
 440 
 441             if (!invert && (status == 0)) {
 442                 crm_debug("'%s' matched '%s' for %s", r->id, value, id);
 443                 unpack_rsc_location(xml_obj, r, NULL, NULL, r->id, nregs,
 444                                     pmatch);
 445 
 446             } else if (invert && (status != 0)) {
 447                 crm_debug("'%s' is an inverted match of '%s' for %s",
 448                           r->id, value, id);
 449                 unpack_rsc_location(xml_obj, r, NULL, NULL, NULL, 0, NULL);
 450 
 451             } else {
 452                 crm_trace("'%s' does not match '%s' for %s", r->id, value, id);
 453             }
 454 
 455             free(pmatch);
 456         }
 457 
 458         regfree(&regex);
 459     }
 460 }
 461 
 462 // \return Standard Pacemaker return code
 463 static int
 464 unpack_location_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 465                      pcmk_scheduler_t *scheduler)
 466 {
 467     const char *id = NULL;
 468     const char *rsc_id = NULL;
 469     const char *state = NULL;
 470     pcmk_resource_t *rsc = NULL;
 471     pcmk__idref_t *tag = NULL;
 472     xmlNode *rsc_set = NULL;
 473 
 474     *expanded_xml = NULL;
 475 
 476     CRM_CHECK(xml_obj != NULL, return EINVAL);
 477 
 478     id = pcmk__xe_id(xml_obj);
 479     if (id == NULL) {
 480         pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
 481                          xml_obj->name);
 482         return pcmk_rc_unpack_error;
 483     }
 484 
 485     // Check whether there are any resource sets with template or tag references
 486     *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
 487     if (*expanded_xml != NULL) {
 488         crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_LOCATION);
 489         return pcmk_rc_ok;
 490     }
 491 
 492     rsc_id = crm_element_value(xml_obj, PCMK_XA_RSC);
 493     if (rsc_id == NULL) {
 494         return pcmk_rc_ok;
 495     }
 496 
 497     if (!pcmk__valid_resource_or_tag(scheduler, rsc_id, &rsc, &tag)) {
 498         pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
 499                          "valid resource or tag", id, rsc_id);
 500         return pcmk_rc_unpack_error;
 501 
 502     } else if (rsc != NULL) {
 503         // No template is referenced
 504         return pcmk_rc_ok;
 505     }
 506 
 507     state = crm_element_value(xml_obj, PCMK_XA_ROLE);
 508 
 509     *expanded_xml = pcmk__xml_copy(NULL, xml_obj);
 510 
 511     /* Convert any template or tag reference into constraint
 512      * PCMK_XE_RESOURCE_SET
 513      */
 514     if (!pcmk__tag_to_set(*expanded_xml, &rsc_set, PCMK_XA_RSC,
 515                           false, scheduler)) {
 516         pcmk__xml_free(*expanded_xml);
 517         *expanded_xml = NULL;
 518         return pcmk_rc_unpack_error;
 519     }
 520 
 521     if (rsc_set != NULL) {
 522         if (state != NULL) {
 523             /* Move PCMK_XA_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as
 524              * PCMK_XA_ROLE attribute
 525              */
 526             crm_xml_add(rsc_set, PCMK_XA_ROLE, state);
 527             pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_ROLE);
 528         }
 529         crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_LOCATION);
 530 
 531     } else {
 532         // No sets
 533         pcmk__xml_free(*expanded_xml);
 534         *expanded_xml = NULL;
 535     }
 536 
 537     return pcmk_rc_ok;
 538 }
 539 
 540 // \return Standard Pacemaker return code
 541 static int
 542 unpack_location_set(xmlNode *location, xmlNode *set,
     /* [previous][next][first][last][top][bottom][index][help] */
 543                     pcmk_scheduler_t *scheduler)
 544 {
 545     xmlNode *xml_rsc = NULL;
 546     pcmk_resource_t *resource = NULL;
 547     const char *set_id;
 548     const char *role;
 549     const char *local_score;
 550 
 551     CRM_CHECK(set != NULL, return EINVAL);
 552 
 553     set_id = pcmk__xe_id(set);
 554     if (set_id == NULL) {
 555         pcmk__config_err("Ignoring " PCMK_XE_RESOURCE_SET " without "
 556                          PCMK_XA_ID " in constraint '%s'",
 557                          pcmk__s(pcmk__xe_id(location), "(missing ID)"));
 558         return pcmk_rc_unpack_error;
 559     }
 560 
 561     role = crm_element_value(set, PCMK_XA_ROLE);
 562     local_score = crm_element_value(set, PCMK_XA_SCORE);
 563 
 564     for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL, NULL);
 565          xml_rsc != NULL;
 566          xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
 567 
 568         resource = pcmk__find_constraint_resource(scheduler->priv->resources,
 569                                                   pcmk__xe_id(xml_rsc));
 570         if (resource == NULL) {
 571             pcmk__config_err("%s: No resource found for %s",
 572                              set_id, pcmk__xe_id(xml_rsc));
 573             return pcmk_rc_unpack_error;
 574         }
 575 
 576         unpack_rsc_location(location, resource, role, local_score, NULL, 0,
 577                             NULL);
 578     }
 579 
 580     return pcmk_rc_ok;
 581 }
 582 
 583 void
 584 pcmk__unpack_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 585 {
 586     xmlNode *set = NULL;
 587     bool any_sets = false;
 588 
 589     xmlNode *orig_xml = NULL;
 590     xmlNode *expanded_xml = NULL;
 591 
 592     if (unpack_location_tags(xml_obj, &expanded_xml, scheduler) != pcmk_rc_ok) {
 593         return;
 594     }
 595 
 596     if (expanded_xml) {
 597         orig_xml = xml_obj;
 598         xml_obj = expanded_xml;
 599     }
 600 
 601     for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL);
 602          set != NULL; set = pcmk__xe_next(set, PCMK_XE_RESOURCE_SET)) {
 603 
 604         any_sets = true;
 605         set = pcmk__xe_resolve_idref(set, scheduler->input);
 606         if ((set == NULL) // Configuration error, message already logged
 607             || (unpack_location_set(xml_obj, set, scheduler) != pcmk_rc_ok)) {
 608 
 609             if (expanded_xml) {
 610                 pcmk__xml_free(expanded_xml);
 611             }
 612             return;
 613         }
 614     }
 615 
 616     if (expanded_xml) {
 617         pcmk__xml_free(expanded_xml);
 618         xml_obj = orig_xml;
 619     }
 620 
 621     if (!any_sets) {
 622         unpack_simple_location(xml_obj, scheduler);
 623     }
 624 }
 625 
 626 /*!
 627  * \internal
 628  * \brief Add a new location constraint to scheduler data
 629  *
 630  * \param[in]     id             XML ID of location constraint
 631  * \param[in,out] rsc            Resource in location constraint
 632  * \param[in]     node_score     Constraint score
 633  * \param[in]     probe_mode     When resource should be probed on node
 634  * \param[in]     node           Node in constraint (or NULL if rule-based)
 635  *
 636  * \return Newly allocated location constraint on success, otherwise NULL
 637  * \note The result will be added to the cluster (via \p rsc) and should not be
 638  *       freed separately.
 639  */
 640 pcmk__location_t *
 641 pcmk__new_location(const char *id, pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 642                    int node_score, const char *probe_mode, pcmk_node_t *node)
 643 {
 644     pcmk__location_t *new_con = NULL;
 645 
 646     CRM_CHECK((node != NULL) || (node_score == 0), return NULL);
 647 
 648     if (id == NULL) {
 649         pcmk__config_err("Invalid constraint: no ID specified");
 650         return NULL;
 651     }
 652 
 653     if (rsc == NULL) {
 654         pcmk__config_err("Invalid constraint %s: no resource specified", id);
 655         return NULL;
 656     }
 657 
 658     new_con = pcmk__assert_alloc(1, sizeof(pcmk__location_t));
 659     new_con->id = pcmk__str_copy(id);
 660     new_con->rsc = rsc;
 661     new_con->nodes = NULL;
 662     new_con->role_filter = pcmk_role_unknown;
 663 
 664     if (pcmk__str_eq(probe_mode, PCMK_VALUE_ALWAYS,
 665                      pcmk__str_null_matches|pcmk__str_casei)) {
 666         new_con->probe_mode = pcmk__probe_always;
 667 
 668     } else if (pcmk__str_eq(probe_mode, PCMK_VALUE_NEVER, pcmk__str_casei)) {
 669         new_con->probe_mode = pcmk__probe_never;
 670 
 671     } else if (pcmk__str_eq(probe_mode, PCMK_VALUE_EXCLUSIVE,
 672                             pcmk__str_casei)) {
 673         new_con->probe_mode = pcmk__probe_exclusive;
 674         pcmk__set_rsc_flags(rsc, pcmk__rsc_exclusive_probes);
 675 
 676     } else {
 677         pcmk__config_err("Invalid " PCMK_XA_RESOURCE_DISCOVERY " value %s "
 678                          "in location constraint", probe_mode);
 679     }
 680 
 681     if (node != NULL) {
 682         pcmk_node_t *copy = pe__copy_node(node);
 683 
 684         copy->assign->score = node_score;
 685         new_con->nodes = g_list_prepend(NULL, copy);
 686     }
 687 
 688     rsc->priv->scheduler->priv->location_constraints =
 689         g_list_prepend(rsc->priv->scheduler->priv->location_constraints,
 690                        new_con);
 691     rsc->priv->location_constraints =
 692         g_list_prepend(rsc->priv->location_constraints, new_con);
 693 
 694     return new_con;
 695 }
 696 
 697 /*!
 698  * \internal
 699  * \brief Apply all location constraints
 700  *
 701  * \param[in,out] scheduler  Scheduler data
 702  */
 703 void
 704 pcmk__apply_locations(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 705 {
 706     for (GList *iter = scheduler->priv->location_constraints;
 707          iter != NULL; iter = iter->next) {
 708         pcmk__location_t *location = iter->data;
 709 
 710         location->rsc->priv->cmds->apply_location(location->rsc, location);
 711     }
 712 }
 713 
 714 /*!
 715  * \internal
 716  * \brief Apply a location constraint to a resource's allowed node scores
 717  *
 718  * \param[in,out] rsc         Resource to apply constraint to
 719  * \param[in,out] location    Location constraint to apply
 720  *
 721  * \note This does not consider the resource's children, so the resource's
 722  *       apply_location() method should be used instead in most cases.
 723  */
 724 void
 725 pcmk__apply_location(pcmk_resource_t *rsc, pcmk__location_t *location)
     /* [previous][next][first][last][top][bottom][index][help] */
 726 {
 727     bool need_role = false;
 728 
 729     pcmk__assert((rsc != NULL) && (location != NULL));
 730 
 731     // If a role was specified, ensure constraint is applicable
 732     need_role = (location->role_filter > pcmk_role_unknown);
 733     if (need_role && (location->role_filter != rsc->priv->next_role)) {
 734         pcmk__rsc_trace(rsc,
 735                         "Not applying %s to %s because role will be %s not %s",
 736                         location->id, rsc->id,
 737                         pcmk_role_text(rsc->priv->next_role),
 738                         pcmk_role_text(location->role_filter));
 739         return;
 740     }
 741 
 742     if (location->nodes == NULL) {
 743         pcmk__rsc_trace(rsc, "Not applying %s to %s because no nodes match",
 744                         location->id, rsc->id);
 745         return;
 746     }
 747 
 748     for (GList *iter = location->nodes; iter != NULL; iter = iter->next) {
 749         pcmk_node_t *node = iter->data;
 750         pcmk_node_t *allowed_node = NULL;
 751 
 752         allowed_node = g_hash_table_lookup(rsc->priv->allowed_nodes,
 753                                            node->priv->id);
 754 
 755         pcmk__rsc_trace(rsc, "Applying %s%s%s to %s score on %s: %c %s",
 756                         location->id,
 757                         (need_role? " for role " : ""),
 758                         (need_role? pcmk_role_text(location->role_filter) : ""),
 759                         rsc->id, pcmk__node_name(node),
 760                         ((allowed_node == NULL)? '=' : '+'),
 761                         pcmk_readable_score(node->assign->score));
 762 
 763         if (allowed_node == NULL) {
 764             allowed_node = pe__copy_node(node);
 765             g_hash_table_insert(rsc->priv->allowed_nodes,
 766                                 (gpointer) allowed_node->priv->id,
 767                                 allowed_node);
 768         } else {
 769             allowed_node->assign->score =
 770                 pcmk__add_scores(allowed_node->assign->score,
 771                                  node->assign->score);
 772         }
 773 
 774         if (allowed_node->assign->probe_mode < location->probe_mode) {
 775             if (location->probe_mode == pcmk__probe_exclusive) {
 776                 pcmk__set_rsc_flags(rsc, pcmk__rsc_exclusive_probes);
 777             }
 778             /* exclusive > never > always... always is default */
 779             allowed_node->assign->probe_mode = location->probe_mode;
 780         }
 781     }
 782 }

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