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(®ex, 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(®ex, 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(®ex);
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 : }
|