This source file includes following definitions.
- get_node_score
- parse_location_role
- generate_location_rule
- unpack_rsc_location
- unpack_simple_location
- unpack_location_tags
- unpack_location_set
- pcmk__unpack_location
- pcmk__new_location
- pcmk__apply_locations
- pcmk__apply_location
1
2
3
4
5
6
7
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,
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
60
61
62
63
64
65
66
67 static bool
68 parse_location_role(const char *role_spec, enum rsc_role_e *role)
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
83
84
85
86 *role = pcmk_role_unknown;
87 break;
88
89 default:
90 break;
91 }
92 return true;
93 }
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110 static bool
111 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 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;
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
170
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,
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
271
272
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;
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
296
297
298
299
300
301
302
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
330
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)
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(®ex, 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(®ex, 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(®ex);
407 }
408 }
409
410
411 static int
412 unpack_location_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
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
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
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
460
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
472
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
481 free_xml(*expanded_xml);
482 *expanded_xml = NULL;
483 }
484
485 return pcmk_rc_ok;
486 }
487
488
489 static int
490 unpack_location_set(xmlNode *location, xmlNode *set,
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)
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)
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
575
576
577
578
579
580
581
582
583
584
585
586
587 pcmk__location_t *
588 pcmk__new_location(const char *id, pcmk_resource_t *rsc,
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
645
646
647
648
649 void
650 pcmk__apply_locations(pcmk_scheduler_t *scheduler)
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
662
663
664
665
666
667
668
669
670 void
671 pcmk__apply_location(pcmk_resource_t *rsc, pcmk__location_t *location)
672 {
673 bool need_role = false;
674
675 pcmk__assert((rsc != NULL) && (location != NULL));
676
677
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
721 allowed_node->rsc_discover_mode = location->discover_mode;
722 }
723 }
724 }