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

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