LCOV - code coverage report
Current view: top level - pacemaker - pcmk_sched_location.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 314 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 11 0.0 %

          Line data    Source code
       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           0 : get_node_score(const char *rule, const char *score, bool raw,
      25             :                pcmk_node_t *node, pcmk_resource_t *rsc)
      26             : {
      27           0 :     int score_f = 0;
      28             : 
      29           0 :     if (score == NULL) {
      30           0 :         pcmk__config_warn("Rule %s: no score specified (assuming 0)", rule);
      31             : 
      32           0 :     } else if (raw) {
      33           0 :         score_f = char2score(score);
      34             : 
      35             :     } else {
      36           0 :         const char *target = NULL;
      37           0 :         const char *attr_score = NULL;
      38             : 
      39           0 :         target = g_hash_table_lookup(rsc->meta,
      40             :                                      PCMK_META_CONTAINER_ATTRIBUTE_TARGET);
      41             : 
      42           0 :         attr_score = pcmk__node_attr(node, score, target,
      43             :                                      pcmk__rsc_node_current);
      44           0 :         if (attr_score == NULL) {
      45           0 :             crm_debug("Rule %s: %s did not have a value for %s",
      46             :                       rule, pcmk__node_name(node), score);
      47           0 :             score_f = -PCMK_SCORE_INFINITY;
      48             : 
      49             :         } else {
      50           0 :             crm_debug("Rule %s: %s had value %s for %s",
      51             :                       rule, pcmk__node_name(node), attr_score, score);
      52           0 :             score_f = char2score(attr_score);
      53             :         }
      54             :     }
      55           0 :     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           0 : parse_location_role(const char *role_spec, enum rsc_role_e *role)
      69             : {
      70           0 :     if (role_spec == NULL) {
      71           0 :         *role = pcmk_role_unknown;
      72           0 :         return true;
      73             :     }
      74             : 
      75           0 :     *role = pcmk_parse_role(role_spec);
      76           0 :     switch (*role) {
      77           0 :         case pcmk_role_unknown:
      78           0 :             return false;
      79             : 
      80           0 :         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           0 :             *role = pcmk_role_unknown;
      87           0 :             break;
      88             : 
      89           0 :         default:
      90           0 :             break;
      91             :     }
      92           0 :     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           0 : generate_location_rule(pcmk_resource_t *rsc, xmlNode *rule_xml,
     112             :                        const char *discovery, crm_time_t *next_change,
     113             :                        pcmk_rule_input_t *rule_input)
     114             : {
     115           0 :     const char *rule_id = NULL;
     116           0 :     const char *score = NULL;
     117           0 :     const char *boolean = NULL;
     118           0 :     const char *role_spec = NULL;
     119             : 
     120           0 :     GList *iter = NULL;
     121             : 
     122           0 :     bool raw_score = true;
     123           0 :     bool score_allocated = false;
     124             : 
     125           0 :     pcmk__location_t *location_rule = NULL;
     126           0 :     enum rsc_role_e role = pcmk_role_unknown;
     127           0 :     enum pcmk__combine combine = pcmk__combine_unknown;
     128             : 
     129           0 :     rule_xml = expand_idref(rule_xml, rsc->cluster->input);
     130           0 :     if (rule_xml == NULL) {
     131           0 :         return false; // Error already logged
     132             :     }
     133             : 
     134           0 :     rule_id = crm_element_value(rule_xml, PCMK_XA_ID);
     135           0 :     if (rule_id == NULL) {
     136           0 :         pcmk__config_err("Ignoring " PCMK_XE_RULE " without " PCMK_XA_ID
     137             :                          " in location constraint");
     138           0 :         return false;
     139             :     }
     140             : 
     141           0 :     boolean = crm_element_value(rule_xml, PCMK_XA_BOOLEAN_OP);
     142           0 :     role_spec = crm_element_value(rule_xml, PCMK_XA_ROLE);
     143             : 
     144           0 :     if (parse_location_role(role_spec, &role)) {
     145           0 :         crm_trace("Setting rule %s role filter to %s", rule_id, role_spec);
     146             :     } else {
     147           0 :         pcmk__config_err("Ignoring rule %s: Invalid " PCMK_XA_ROLE " '%s'",
     148             :                          rule_id, role_spec);
     149           0 :         return false;
     150             :     }
     151             : 
     152           0 :     crm_trace("Processing location constraint rule %s", rule_id);
     153             : 
     154           0 :     score = crm_element_value(rule_xml, PCMK_XA_SCORE);
     155           0 :     if (score == NULL) {
     156           0 :         score = crm_element_value(rule_xml, PCMK_XA_SCORE_ATTRIBUTE);
     157           0 :         if (score != NULL) {
     158           0 :             raw_score = false;
     159             :         }
     160             :     }
     161             : 
     162           0 :     combine = pcmk__parse_combine(boolean);
     163           0 :     switch (combine) {
     164           0 :         case pcmk__combine_and:
     165             :         case pcmk__combine_or:
     166           0 :             break;
     167             : 
     168           0 :         default:
     169             :             /* @COMPAT When we can break behavioral backward compatibility,
     170             :              * return false
     171             :              */
     172           0 :             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           0 :             combine = pcmk__combine_and;
     177           0 :             break;
     178             :     }
     179             : 
     180           0 :     location_rule = pcmk__new_location(rule_id, rsc, 0, discovery, NULL);
     181           0 :     CRM_CHECK(location_rule != NULL, return NULL);
     182             : 
     183           0 :     location_rule->role_filter = role;
     184             : 
     185           0 :     if ((rule_input->rsc_id != NULL) && (rule_input->rsc_id_nmatches > 0)
     186           0 :         && !raw_score) {
     187             : 
     188           0 :         char *result = pcmk__replace_submatches(score, rule_input->rsc_id,
     189             :                                                 rule_input->rsc_id_submatches,
     190             :                                                 rule_input->rsc_id_nmatches);
     191             : 
     192           0 :         if (result != NULL) {
     193           0 :             score = result;
     194           0 :             score_allocated = true;
     195             :         }
     196             :     }
     197             : 
     198           0 :     for (iter = rsc->cluster->nodes; iter != NULL; iter = iter->next) {
     199           0 :         pcmk_node_t *node = iter->data;
     200             : 
     201           0 :         rule_input->node_attrs = node->details->attrs;
     202           0 :         rule_input->rsc_params = pe_rsc_params(rsc, node, rsc->cluster);
     203             : 
     204           0 :         if (pcmk_evaluate_rule(rule_xml, rule_input,
     205             :                                next_change) == pcmk_rc_ok) {
     206           0 :             pcmk_node_t *local = pe__copy_node(node);
     207             : 
     208           0 :             location_rule->nodes = g_list_prepend(location_rule->nodes, local);
     209           0 :             local->weight = get_node_score(rule_id, score, raw_score, node,
     210             :                                            rsc);
     211           0 :             crm_trace("%s has score %s after %s", pcmk__node_name(node),
     212             :                       pcmk_readable_score(local->weight), rule_id);
     213             :         }
     214             :     }
     215             : 
     216           0 :     if (score_allocated) {
     217           0 :         free((char *)score);
     218             :     }
     219             : 
     220           0 :     if (location_rule->nodes == NULL) {
     221           0 :         crm_trace("No matching nodes for location constraint rule %s", rule_id);
     222             :     } else {
     223           0 :         crm_trace("Location constraint rule %s matched %d nodes",
     224             :                   rule_id, g_list_length(location_rule->nodes));
     225             :     }
     226           0 :     return true;
     227             : }
     228             : 
     229             : static void
     230           0 : unpack_rsc_location(xmlNode *xml_obj, pcmk_resource_t *rsc,
     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           0 :     const char *rsc_id = crm_element_value(xml_obj, PCMK_XA_RSC);
     236           0 :     const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
     237           0 :     const char *node = crm_element_value(xml_obj, PCMK_XE_NODE);
     238           0 :     const char *discovery = crm_element_value(xml_obj,
     239             :                                               PCMK_XA_RESOURCE_DISCOVERY);
     240             : 
     241           0 :     if (rsc == NULL) {
     242           0 :         pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
     243             :                           "does not exist", id, rsc_id);
     244           0 :         return;
     245             :     }
     246             : 
     247           0 :     if (score == NULL) {
     248           0 :         score = crm_element_value(xml_obj, PCMK_XA_SCORE);
     249             :     }
     250             : 
     251           0 :     if ((node != NULL) && (score != NULL)) {
     252           0 :         int score_i = char2score(score);
     253           0 :         pcmk_node_t *match = pcmk_find_node(rsc->cluster, node);
     254           0 :         enum rsc_role_e role = pcmk_role_unknown;
     255           0 :         pcmk__location_t *location = NULL;
     256             : 
     257           0 :         if (match == NULL) {
     258           0 :             crm_info("Ignoring location constraint %s "
     259             :                      "because '%s' is not a known node",
     260             :                      pcmk__s(id, "without ID"), node);
     261           0 :             return;
     262             :         }
     263             : 
     264           0 :         if (role_spec == NULL) {
     265           0 :             role_spec = crm_element_value(xml_obj, PCMK_XA_ROLE);
     266             :         }
     267           0 :         if (parse_location_role(role_spec, &role)) {
     268           0 :             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           0 :             pcmk__config_err("Ignoring role in constraint %s: "
     276             :                              "Invalid value '%s'", id, role_spec);
     277             :         }
     278             : 
     279           0 :         location = pcmk__new_location(id, rsc, score_i, discovery, match);
     280           0 :         if (location == NULL) {
     281           0 :             return; // Error already logged
     282             :         }
     283           0 :         location->role_filter = role;
     284             : 
     285             :     } else {
     286           0 :         bool empty = true;
     287           0 :         crm_time_t *next_change = crm_time_new_undefined();
     288           0 :         pcmk_rule_input_t rule_input = {
     289           0 :             .now = rsc->cluster->now,
     290           0 :             .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           0 :         for (xmlNode *rule_xml = pcmk__xe_first_child(xml_obj, PCMK_XE_RULE,
     306             :                                                       NULL, NULL);
     307           0 :              rule_xml != NULL; rule_xml = pcmk__xe_next_same(rule_xml)) {
     308             : 
     309           0 :             if (generate_location_rule(rsc, rule_xml, discovery, next_change,
     310             :                                        &rule_input)) {
     311           0 :                 if (empty) {
     312           0 :                     empty = false;
     313           0 :                     continue;
     314             :                 }
     315           0 :                 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           0 :         if (empty) {
     326           0 :             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           0 :         if (crm_time_is_defined(next_change)) {
     334           0 :             time_t t = (time_t) crm_time_get_seconds_since_epoch(next_change);
     335             : 
     336           0 :             pe__update_recheck_time(t, rsc->cluster,
     337             :                                     "location rule evaluation");
     338             :         }
     339           0 :         crm_time_free(next_change);
     340             :     }
     341             : }
     342             : 
     343             : static void
     344           0 : unpack_simple_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
     345             : {
     346           0 :     const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
     347           0 :     const char *value = crm_element_value(xml_obj, PCMK_XA_RSC);
     348             : 
     349           0 :     if (value) {
     350             :         pcmk_resource_t *rsc;
     351             : 
     352           0 :         rsc = pcmk__find_constraint_resource(scheduler->resources, value);
     353           0 :         unpack_rsc_location(xml_obj, rsc, NULL, NULL, NULL, 0, NULL);
     354             :     }
     355             : 
     356           0 :     value = crm_element_value(xml_obj, PCMK_XA_RSC_PATTERN);
     357           0 :     if (value) {
     358             :         regex_t regex;
     359           0 :         bool invert = false;
     360             : 
     361           0 :         if (value[0] == '!') {
     362           0 :             value++;
     363           0 :             invert = true;
     364             :         }
     365             : 
     366           0 :         if (regcomp(&regex, value, REG_EXTENDED) != 0) {
     367           0 :             pcmk__config_err("Ignoring constraint '%s' because "
     368             :                              PCMK_XA_RSC_PATTERN
     369             :                              " has invalid value '%s'", id, value);
     370           0 :             return;
     371             :         }
     372             : 
     373           0 :         for (GList *iter = scheduler->resources; iter != NULL;
     374           0 :              iter = iter->next) {
     375             : 
     376           0 :             pcmk_resource_t *r = iter->data;
     377           0 :             int nregs = 0;
     378           0 :             regmatch_t *pmatch = NULL;
     379             :             int status;
     380             : 
     381           0 :             if (regex.re_nsub > 0) {
     382           0 :                 nregs = regex.re_nsub + 1;
     383             :             } else {
     384           0 :                 nregs = 1;
     385             :             }
     386           0 :             pmatch = pcmk__assert_alloc(nregs, sizeof(regmatch_t));
     387             : 
     388           0 :             status = regexec(&regex, r->id, nregs, pmatch, 0);
     389             : 
     390           0 :             if (!invert && (status == 0)) {
     391           0 :                 crm_debug("'%s' matched '%s' for %s", r->id, value, id);
     392           0 :                 unpack_rsc_location(xml_obj, r, NULL, NULL, r->id, nregs,
     393             :                                     pmatch);
     394             : 
     395           0 :             } else if (invert && (status != 0)) {
     396           0 :                 crm_debug("'%s' is an inverted match of '%s' for %s",
     397             :                           r->id, value, id);
     398           0 :                 unpack_rsc_location(xml_obj, r, NULL, NULL, NULL, 0, NULL);
     399             : 
     400             :             } else {
     401           0 :                 crm_trace("'%s' does not match '%s' for %s", r->id, value, id);
     402             :             }
     403             : 
     404           0 :             free(pmatch);
     405             :         }
     406             : 
     407           0 :         regfree(&regex);
     408             :     }
     409             : }
     410             : 
     411             : // \return Standard Pacemaker return code
     412             : static int
     413           0 : unpack_location_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
     414             :                      pcmk_scheduler_t *scheduler)
     415             : {
     416           0 :     const char *id = NULL;
     417           0 :     const char *rsc_id = NULL;
     418           0 :     const char *state = NULL;
     419           0 :     pcmk_resource_t *rsc = NULL;
     420           0 :     pcmk_tag_t *tag = NULL;
     421           0 :     xmlNode *rsc_set = NULL;
     422             : 
     423           0 :     *expanded_xml = NULL;
     424             : 
     425           0 :     CRM_CHECK(xml_obj != NULL, return EINVAL);
     426             : 
     427           0 :     id = pcmk__xe_id(xml_obj);
     428           0 :     if (id == NULL) {
     429           0 :         pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
     430             :                          xml_obj->name);
     431           0 :         return pcmk_rc_unpack_error;
     432             :     }
     433             : 
     434             :     // Check whether there are any resource sets with template or tag references
     435           0 :     *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
     436           0 :     if (*expanded_xml != NULL) {
     437           0 :         crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_LOCATION);
     438           0 :         return pcmk_rc_ok;
     439             :     }
     440             : 
     441           0 :     rsc_id = crm_element_value(xml_obj, PCMK_XA_RSC);
     442           0 :     if (rsc_id == NULL) {
     443           0 :         return pcmk_rc_ok;
     444             :     }
     445             : 
     446           0 :     if (!pcmk__valid_resource_or_tag(scheduler, rsc_id, &rsc, &tag)) {
     447           0 :         pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
     448             :                          "valid resource or tag", id, rsc_id);
     449           0 :         return pcmk_rc_unpack_error;
     450             : 
     451           0 :     } else if (rsc != NULL) {
     452             :         // No template is referenced
     453           0 :         return pcmk_rc_ok;
     454             :     }
     455             : 
     456           0 :     state = crm_element_value(xml_obj, PCMK_XA_ROLE);
     457             : 
     458           0 :     *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           0 :     if (!pcmk__tag_to_set(*expanded_xml, &rsc_set, PCMK_XA_RSC,
     464             :                           false, scheduler)) {
     465           0 :         free_xml(*expanded_xml);
     466           0 :         *expanded_xml = NULL;
     467           0 :         return pcmk_rc_unpack_error;
     468             :     }
     469             : 
     470           0 :     if (rsc_set != NULL) {
     471           0 :         if (state != NULL) {
     472             :             /* Move PCMK_XA_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as
     473             :              * PCMK_XA_ROLE attribute
     474             :              */
     475           0 :             crm_xml_add(rsc_set, PCMK_XA_ROLE, state);
     476           0 :             pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_ROLE);
     477             :         }
     478           0 :         crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_LOCATION);
     479             : 
     480             :     } else {
     481             :         // No sets
     482           0 :         free_xml(*expanded_xml);
     483           0 :         *expanded_xml = NULL;
     484             :     }
     485             : 
     486           0 :     return pcmk_rc_ok;
     487             : }
     488             : 
     489             : // \return Standard Pacemaker return code
     490             : static int
     491           0 : unpack_location_set(xmlNode *location, xmlNode *set,
     492             :                     pcmk_scheduler_t *scheduler)
     493             : {
     494           0 :     xmlNode *xml_rsc = NULL;
     495           0 :     pcmk_resource_t *resource = NULL;
     496             :     const char *set_id;
     497             :     const char *role;
     498             :     const char *local_score;
     499             : 
     500           0 :     CRM_CHECK(set != NULL, return EINVAL);
     501             : 
     502           0 :     set_id = pcmk__xe_id(set);
     503           0 :     if (set_id == NULL) {
     504           0 :         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           0 :         return pcmk_rc_unpack_error;
     508             :     }
     509             : 
     510           0 :     role = crm_element_value(set, PCMK_XA_ROLE);
     511           0 :     local_score = crm_element_value(set, PCMK_XA_SCORE);
     512             : 
     513           0 :     for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL, NULL);
     514           0 :          xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
     515             : 
     516           0 :         resource = pcmk__find_constraint_resource(scheduler->resources,
     517             :                                                   pcmk__xe_id(xml_rsc));
     518           0 :         if (resource == NULL) {
     519           0 :             pcmk__config_err("%s: No resource found for %s",
     520             :                              set_id, pcmk__xe_id(xml_rsc));
     521           0 :             return pcmk_rc_unpack_error;
     522             :         }
     523             : 
     524           0 :         unpack_rsc_location(location, resource, role, local_score, NULL, 0,
     525             :                             NULL);
     526             :     }
     527             : 
     528           0 :     return pcmk_rc_ok;
     529             : }
     530             : 
     531             : void
     532           0 : pcmk__unpack_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
     533             : {
     534           0 :     xmlNode *set = NULL;
     535           0 :     bool any_sets = false;
     536             : 
     537           0 :     xmlNode *orig_xml = NULL;
     538           0 :     xmlNode *expanded_xml = NULL;
     539             : 
     540           0 :     if (unpack_location_tags(xml_obj, &expanded_xml, scheduler) != pcmk_rc_ok) {
     541           0 :         return;
     542             :     }
     543             : 
     544           0 :     if (expanded_xml) {
     545           0 :         orig_xml = xml_obj;
     546           0 :         xml_obj = expanded_xml;
     547             :     }
     548             : 
     549           0 :     for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL);
     550           0 :          set != NULL; set = pcmk__xe_next_same(set)) {
     551             : 
     552           0 :         any_sets = true;
     553           0 :         set = expand_idref(set, scheduler->input);
     554           0 :         if ((set == NULL) // Configuration error, message already logged
     555           0 :             || (unpack_location_set(xml_obj, set, scheduler) != pcmk_rc_ok)) {
     556             : 
     557           0 :             if (expanded_xml) {
     558           0 :                 free_xml(expanded_xml);
     559             :             }
     560           0 :             return;
     561             :         }
     562             :     }
     563             : 
     564           0 :     if (expanded_xml) {
     565           0 :         free_xml(expanded_xml);
     566           0 :         xml_obj = orig_xml;
     567             :     }
     568             : 
     569           0 :     if (!any_sets) {
     570           0 :         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           0 : pcmk__new_location(const char *id, pcmk_resource_t *rsc,
     590             :                    int node_score, const char *discover_mode, pcmk_node_t *node)
     591             : {
     592           0 :     pcmk__location_t *new_con = NULL;
     593             : 
     594           0 :     CRM_CHECK((node != NULL) || (node_score == 0), return NULL);
     595             : 
     596           0 :     if (id == NULL) {
     597           0 :         pcmk__config_err("Invalid constraint: no ID specified");
     598           0 :         return NULL;
     599             :     }
     600             : 
     601           0 :     if (rsc == NULL) {
     602           0 :         pcmk__config_err("Invalid constraint %s: no resource specified", id);
     603           0 :         return NULL;
     604             :     }
     605             : 
     606           0 :     new_con = pcmk__assert_alloc(1, sizeof(pcmk__location_t));
     607           0 :     new_con->id = pcmk__str_copy(id);
     608           0 :     new_con->rsc = rsc;
     609           0 :     new_con->nodes = NULL;
     610           0 :     new_con->role_filter = pcmk_role_unknown;
     611             : 
     612           0 :     if (pcmk__str_eq(discover_mode, PCMK_VALUE_ALWAYS,
     613             :                      pcmk__str_null_matches|pcmk__str_casei)) {
     614           0 :         new_con->discover_mode = pcmk_probe_always;
     615             : 
     616           0 :     } else if (pcmk__str_eq(discover_mode, PCMK_VALUE_NEVER,
     617             :                             pcmk__str_casei)) {
     618           0 :         new_con->discover_mode = pcmk_probe_never;
     619             : 
     620           0 :     } else if (pcmk__str_eq(discover_mode, PCMK_VALUE_EXCLUSIVE,
     621             :                             pcmk__str_casei)) {
     622           0 :         new_con->discover_mode = pcmk_probe_exclusive;
     623           0 :         rsc->exclusive_discover = TRUE;
     624             : 
     625             :     } else {
     626           0 :         pcmk__config_err("Invalid " PCMK_XA_RESOURCE_DISCOVERY " value %s "
     627             :                          "in location constraint", discover_mode);
     628             :     }
     629             : 
     630           0 :     if (node != NULL) {
     631           0 :         pcmk_node_t *copy = pe__copy_node(node);
     632             : 
     633           0 :         copy->weight = node_score;
     634           0 :         new_con->nodes = g_list_prepend(NULL, copy);
     635             :     }
     636             : 
     637           0 :     rsc->cluster->placement_constraints = g_list_prepend(
     638           0 :         rsc->cluster->placement_constraints, new_con);
     639           0 :     rsc->rsc_location = g_list_prepend(rsc->rsc_location, new_con);
     640             : 
     641           0 :     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           0 : pcmk__apply_locations(pcmk_scheduler_t *scheduler)
     652             : {
     653           0 :     for (GList *iter = scheduler->placement_constraints;
     654           0 :          iter != NULL; iter = iter->next) {
     655           0 :         pcmk__location_t *location = iter->data;
     656             : 
     657           0 :         location->rsc->cmds->apply_location(location->rsc, location);
     658             :     }
     659           0 : }
     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           0 : pcmk__apply_location(pcmk_resource_t *rsc, pcmk__location_t *location)
     673             : {
     674           0 :     bool need_role = false;
     675             : 
     676           0 :     CRM_ASSERT((rsc != NULL) && (location != NULL));
     677             : 
     678             :     // If a role was specified, ensure constraint is applicable
     679           0 :     need_role = (location->role_filter > pcmk_role_unknown);
     680           0 :     if (need_role && (location->role_filter != rsc->next_role)) {
     681           0 :         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           0 :         return;
     686             :     }
     687             : 
     688           0 :     if (location->nodes == NULL) {
     689           0 :         pcmk__rsc_trace(rsc, "Not applying %s to %s because no nodes match",
     690             :                         location->id, rsc->id);
     691           0 :         return;
     692             :     }
     693             : 
     694           0 :     pcmk__rsc_trace(rsc, "Applying %s%s%s to %s", location->id,
     695             :                     (need_role? " for role " : ""),
     696             :                     (need_role? pcmk_role_text(location->role_filter) : ""),
     697             :                     rsc->id);
     698             : 
     699           0 :     for (GList *iter = location->nodes; iter != NULL; iter = iter->next) {
     700           0 :         pcmk_node_t *node = iter->data;
     701           0 :         pcmk_node_t *allowed_node = g_hash_table_lookup(rsc->allowed_nodes,
     702           0 :                                                         node->details->id);
     703             : 
     704           0 :         if (allowed_node == NULL) {
     705           0 :             pcmk__rsc_trace(rsc, "* = %d on %s",
     706             :                             node->weight, pcmk__node_name(node));
     707           0 :             allowed_node = pe__copy_node(node);
     708           0 :             g_hash_table_insert(rsc->allowed_nodes,
     709           0 :                                 (gpointer) allowed_node->details->id,
     710             :                                 allowed_node);
     711             :         } else {
     712           0 :             pcmk__rsc_trace(rsc, "* + %d on %s",
     713             :                             node->weight, pcmk__node_name(node));
     714           0 :             allowed_node->weight = pcmk__add_scores(allowed_node->weight,
     715             :                                                     node->weight);
     716             :         }
     717             : 
     718           0 :         if (allowed_node->rsc_discover_mode < location->discover_mode) {
     719           0 :             if (location->discover_mode == pcmk_probe_exclusive) {
     720           0 :                 rsc->exclusive_discover = TRUE;
     721             :             }
     722             :             /* exclusive > never > always... always is default */
     723           0 :             allowed_node->rsc_discover_mode = location->discover_mode;
     724             :         }
     725             :     }
     726             : }

Generated by: LCOV version 1.14