root/lib/pacemaker/pcmk_sched_location.c

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

DEFINITIONS

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

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