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

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