This source file includes following definitions.
- parse_location_role
- score_attribute_name
- score_from_rule
- score_from_attr
- 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
24
25
26
27
28
29
30
31
32 static bool
33 parse_location_role(const char *role_spec, enum rsc_role_e *role)
34 {
35 if (role_spec == NULL) {
36 *role = pcmk_role_unknown;
37 return true;
38 }
39
40 *role = pcmk_parse_role(role_spec);
41 switch (*role) {
42 case pcmk_role_unknown:
43 return false;
44
45 case pcmk_role_started:
46 case pcmk_role_unpromoted:
47
48
49
50
51 *role = pcmk_role_unknown;
52 break;
53
54 default:
55 break;
56 }
57 return true;
58 }
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73 static const char *
74 score_attribute_name(const xmlNode *rule_xml, char **allocated,
75 const pcmk_rule_input_t *rule_input)
76 {
77 const char *name = NULL;
78
79 name = crm_element_value(rule_xml, PCMK_XA_SCORE_ATTRIBUTE);
80 if (name == NULL) {
81 return NULL;
82 }
83
84
85
86
87
88
89 if ((rule_input->rsc_id != NULL) && (rule_input->rsc_id_nmatches > 0)) {
90 *allocated = pcmk__replace_submatches(name, rule_input->rsc_id,
91 rule_input->rsc_id_submatches,
92 rule_input->rsc_id_nmatches);
93 if (*allocated != NULL) {
94 name = *allocated;
95 }
96 }
97 return name;
98 }
99
100
101
102
103
104
105
106
107
108
109 static int
110 score_from_rule(const xmlNode *rule_xml, int *score)
111 {
112 int rc = pcmk_rc_ok;
113 const char *score_s = crm_element_value(rule_xml, PCMK_XA_SCORE);
114
115 if (score_s == NULL) {
116 pcmk__config_err("Ignoring location constraint rule %s because "
117 "neither " PCMK_XA_SCORE " nor "
118 PCMK_XA_SCORE_ATTRIBUTE " was specified",
119 pcmk__xe_id(rule_xml));
120 return pcmk_rc_unpack_error;
121 }
122
123 rc = pcmk_parse_score(score_s, score, 0);
124 if (rc != pcmk_rc_ok) {
125 pcmk__config_err("Ignoring location constraint rule %s because "
126 "'%s' is not a valid " PCMK_XA_SCORE ": %s",
127 pcmk__xe_id(rule_xml), score_s, pcmk_rc_str(rc));
128 return pcmk_rc_unpack_error;
129 }
130
131 return pcmk_rc_ok;
132 }
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148 static int
149 score_from_attr(const char *constraint_id, const char *attr_name,
150 const pcmk_node_t *node, const pcmk_resource_t *rsc, int *score)
151 {
152 int rc = pcmk_rc_ok;
153 const char *target = NULL;
154 const char *score_s = NULL;
155
156 target = g_hash_table_lookup(rsc->priv->meta,
157 PCMK_META_CONTAINER_ATTRIBUTE_TARGET);
158 score_s = pcmk__node_attr(node, attr_name, target, pcmk__rsc_node_current);
159 if (pcmk__str_empty(score_s)) {
160 crm_info("Ignoring location %s for %s on %s "
161 "because it has no node attribute %s",
162 constraint_id, rsc->id, pcmk__node_name(node), attr_name);
163 return ENXIO;
164 }
165
166 rc = pcmk_parse_score(score_s, score, 0);
167 if (rc != pcmk_rc_ok) {
168 crm_warn("Ignoring location %s for node %s because node "
169 "attribute %s value '%s' is not a valid score: %s",
170 constraint_id, pcmk__node_name(node), attr_name,
171 score_s, pcmk_rc_str(rc));
172 return rc;
173 }
174 return pcmk_rc_ok;
175 }
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193 static bool
194 generate_location_rule(pcmk_resource_t *rsc, xmlNode *rule_xml,
195 const char *discovery, crm_time_t *next_change,
196 pcmk_rule_input_t *rule_input, const char *constraint_id)
197 {
198 const char *rule_id = NULL;
199 const char *score_attr = NULL;
200 const char *boolean = NULL;
201 const char *role_spec = NULL;
202
203 GList *iter = NULL;
204 int score = 0;
205 char *local_score_attr = NULL;
206 pcmk__location_t *location_rule = NULL;
207 enum rsc_role_e role = pcmk_role_unknown;
208 enum pcmk__combine combine = pcmk__combine_unknown;
209
210 rule_xml = pcmk__xe_resolve_idref(rule_xml, rsc->priv->scheduler->input);
211 if (rule_xml == NULL) {
212 return false;
213 }
214
215 rule_id = crm_element_value(rule_xml, PCMK_XA_ID);
216 if (rule_id == NULL) {
217 pcmk__config_err("Ignoring location constraint '%s' because its rule "
218 "has no " PCMK_XA_ID,
219 constraint_id);
220 return false;
221 }
222
223 boolean = crm_element_value(rule_xml, PCMK_XA_BOOLEAN_OP);
224 role_spec = crm_element_value(rule_xml, PCMK_XA_ROLE);
225
226 if (parse_location_role(role_spec, &role)) {
227 crm_trace("Setting rule %s role filter to %s", rule_id, role_spec);
228 } else {
229 pcmk__config_err("Ignoring location constraint '%s' because rule '%s' "
230 "has invalid " PCMK_XA_ROLE " '%s'",
231 constraint_id, rule_id, role_spec);
232 return false;
233 }
234
235 combine = pcmk__parse_combine(boolean);
236 switch (combine) {
237 case pcmk__combine_and:
238 case pcmk__combine_or:
239 break;
240
241 default:
242 pcmk__config_err("Ignoring location constraint '%s' because rule "
243 "'%s' has invalid " PCMK_XA_BOOLEAN_OP " '%s'",
244 constraint_id, rule_id, boolean);
245 return false;
246 }
247
248
249
250
251
252 score_attr = score_attribute_name(rule_xml, &local_score_attr, rule_input);
253 if ((score_attr == NULL)
254 && (score_from_rule(rule_xml, &score) != pcmk_rc_ok)) {
255 return false;
256 }
257
258 location_rule = pcmk__new_location(rule_id, rsc, 0, discovery, NULL);
259 CRM_CHECK(location_rule != NULL, return NULL);
260
261 location_rule->role_filter = role;
262
263 for (iter = rsc->priv->scheduler->nodes;
264 iter != NULL; iter = iter->next) {
265
266 pcmk_node_t *node = iter->data;
267 pcmk_node_t *local = NULL;
268
269 rule_input->node_attrs = node->priv->attrs;
270 rule_input->rsc_params = pe_rsc_params(rsc, node,
271 rsc->priv->scheduler);
272
273 if (pcmk_evaluate_rule(rule_xml, rule_input,
274 next_change) != pcmk_rc_ok) {
275 continue;
276 }
277
278 if ((score_attr != NULL)
279 && (score_from_attr(constraint_id, score_attr, node, rsc,
280 &score) != pcmk_rc_ok)) {
281 continue;
282 }
283
284 local = pe__copy_node(node);
285 location_rule->nodes = g_list_prepend(location_rule->nodes, local);
286 local->assign->score = score;
287 pcmk__rsc_trace(rsc,
288 "Location %s score for %s on %s is %s via rule %s",
289 constraint_id, rsc->id, pcmk__node_name(node),
290 pcmk_readable_score(score), rule_id);
291 }
292
293 free(local_score_attr);
294
295 if (location_rule->nodes == NULL) {
296 crm_trace("No matching nodes for location constraint rule %s", rule_id);
297 } else {
298 crm_trace("Location constraint rule %s matched %d nodes",
299 rule_id, g_list_length(location_rule->nodes));
300 }
301 return true;
302 }
303
304 static void
305 unpack_rsc_location(xmlNode *xml_obj, pcmk_resource_t *rsc,
306 const char *role_spec, const char *score,
307 char *rsc_id_match, int rsc_id_nmatches,
308 regmatch_t *rsc_id_submatches)
309 {
310 const char *rsc_id = crm_element_value(xml_obj, PCMK_XA_RSC);
311 const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
312 const char *node = crm_element_value(xml_obj, PCMK_XA_NODE);
313 const char *discovery = crm_element_value(xml_obj,
314 PCMK_XA_RESOURCE_DISCOVERY);
315
316 if (rsc == NULL) {
317 pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
318 "does not exist", id, rsc_id);
319 return;
320 }
321
322 if (score == NULL) {
323 score = crm_element_value(xml_obj, PCMK_XA_SCORE);
324 }
325
326 if ((node != NULL) && (score != NULL)) {
327 int score_i = 0;
328 int rc = pcmk_rc_ok;
329 pcmk_node_t *match = pcmk_find_node(rsc->priv->scheduler, node);
330 enum rsc_role_e role = pcmk_role_unknown;
331 pcmk__location_t *location = NULL;
332
333 if (match == NULL) {
334 crm_info("Ignoring location constraint %s "
335 "because '%s' is not a known node",
336 pcmk__s(id, "without ID"), node);
337 return;
338 }
339
340 rc = pcmk_parse_score(score, &score_i, 0);
341 if (rc != pcmk_rc_ok) {
342 pcmk__config_err("Ignoring location constraint %s "
343 "because '%s' is not a valid score", id, score);
344 return;
345 }
346
347 if (role_spec == NULL) {
348 role_spec = crm_element_value(xml_obj, PCMK_XA_ROLE);
349 }
350 if (parse_location_role(role_spec, &role)) {
351 crm_trace("Setting location constraint %s role filter: %s",
352 id, role_spec);
353 } else {
354 pcmk__config_err("Ignoring location constraint %s "
355 "because '%s' is not a valid " PCMK_XA_ROLE,
356 id, role_spec);
357 return;
358 }
359
360 location = pcmk__new_location(id, rsc, score_i, discovery, match);
361 if (location == NULL) {
362 return;
363 }
364 location->role_filter = role;
365
366 } else {
367 crm_time_t *next_change = crm_time_new_undefined();
368 xmlNode *rule_xml = pcmk__xe_first_child(xml_obj, PCMK_XE_RULE, NULL,
369 NULL);
370 pcmk_rule_input_t rule_input = {
371 .now = rsc->priv->scheduler->priv->now,
372 .rsc_meta = rsc->priv->meta,
373 .rsc_id = rsc_id_match,
374 .rsc_id_submatches = rsc_id_submatches,
375 .rsc_id_nmatches = rsc_id_nmatches,
376 };
377
378 generate_location_rule(rsc, rule_xml, discovery, next_change,
379 &rule_input, id);
380
381
382
383
384 if (crm_time_is_defined(next_change)) {
385 time_t t = (time_t) crm_time_get_seconds_since_epoch(next_change);
386
387 pe__update_recheck_time(t, rsc->priv->scheduler,
388 "location rule evaluation");
389 }
390 crm_time_free(next_change);
391 }
392 }
393
394 static void
395 unpack_simple_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
396 {
397 const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
398 const char *value = crm_element_value(xml_obj, PCMK_XA_RSC);
399
400 if (value) {
401 pcmk_resource_t *rsc;
402
403 rsc = pcmk__find_constraint_resource(scheduler->priv->resources, value);
404 unpack_rsc_location(xml_obj, rsc, NULL, NULL, NULL, 0, NULL);
405 }
406
407 value = crm_element_value(xml_obj, PCMK_XA_RSC_PATTERN);
408 if (value) {
409 regex_t regex;
410 bool invert = false;
411
412 if (value[0] == '!') {
413 value++;
414 invert = true;
415 }
416
417 if (regcomp(®ex, value, REG_EXTENDED) != 0) {
418 pcmk__config_err("Ignoring constraint '%s' because "
419 PCMK_XA_RSC_PATTERN
420 " has invalid value '%s'", id, value);
421 return;
422 }
423
424 for (GList *iter = scheduler->priv->resources;
425 iter != NULL; iter = iter->next) {
426
427 pcmk_resource_t *r = iter->data;
428 int nregs = 0;
429 regmatch_t *pmatch = NULL;
430 int status;
431
432 if (regex.re_nsub > 0) {
433 nregs = regex.re_nsub + 1;
434 } else {
435 nregs = 1;
436 }
437 pmatch = pcmk__assert_alloc(nregs, sizeof(regmatch_t));
438
439 status = regexec(®ex, r->id, nregs, pmatch, 0);
440
441 if (!invert && (status == 0)) {
442 crm_debug("'%s' matched '%s' for %s", r->id, value, id);
443 unpack_rsc_location(xml_obj, r, NULL, NULL, r->id, nregs,
444 pmatch);
445
446 } else if (invert && (status != 0)) {
447 crm_debug("'%s' is an inverted match of '%s' for %s",
448 r->id, value, id);
449 unpack_rsc_location(xml_obj, r, NULL, NULL, NULL, 0, NULL);
450
451 } else {
452 crm_trace("'%s' does not match '%s' for %s", r->id, value, id);
453 }
454
455 free(pmatch);
456 }
457
458 regfree(®ex);
459 }
460 }
461
462
463 static int
464 unpack_location_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
465 pcmk_scheduler_t *scheduler)
466 {
467 const char *id = NULL;
468 const char *rsc_id = NULL;
469 const char *state = NULL;
470 pcmk_resource_t *rsc = NULL;
471 pcmk__idref_t *tag = NULL;
472 xmlNode *rsc_set = NULL;
473
474 *expanded_xml = NULL;
475
476 CRM_CHECK(xml_obj != NULL, return EINVAL);
477
478 id = pcmk__xe_id(xml_obj);
479 if (id == NULL) {
480 pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
481 xml_obj->name);
482 return pcmk_rc_unpack_error;
483 }
484
485
486 *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
487 if (*expanded_xml != NULL) {
488 crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_LOCATION);
489 return pcmk_rc_ok;
490 }
491
492 rsc_id = crm_element_value(xml_obj, PCMK_XA_RSC);
493 if (rsc_id == NULL) {
494 return pcmk_rc_ok;
495 }
496
497 if (!pcmk__valid_resource_or_tag(scheduler, rsc_id, &rsc, &tag)) {
498 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
499 "valid resource or tag", id, rsc_id);
500 return pcmk_rc_unpack_error;
501
502 } else if (rsc != NULL) {
503
504 return pcmk_rc_ok;
505 }
506
507 state = crm_element_value(xml_obj, PCMK_XA_ROLE);
508
509 *expanded_xml = pcmk__xml_copy(NULL, xml_obj);
510
511
512
513
514 if (!pcmk__tag_to_set(*expanded_xml, &rsc_set, PCMK_XA_RSC,
515 false, scheduler)) {
516 pcmk__xml_free(*expanded_xml);
517 *expanded_xml = NULL;
518 return pcmk_rc_unpack_error;
519 }
520
521 if (rsc_set != NULL) {
522 if (state != NULL) {
523
524
525
526 crm_xml_add(rsc_set, PCMK_XA_ROLE, state);
527 pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_ROLE);
528 }
529 crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_LOCATION);
530
531 } else {
532
533 pcmk__xml_free(*expanded_xml);
534 *expanded_xml = NULL;
535 }
536
537 return pcmk_rc_ok;
538 }
539
540
541 static int
542 unpack_location_set(xmlNode *location, xmlNode *set,
543 pcmk_scheduler_t *scheduler)
544 {
545 xmlNode *xml_rsc = NULL;
546 pcmk_resource_t *resource = NULL;
547 const char *set_id;
548 const char *role;
549 const char *local_score;
550
551 CRM_CHECK(set != NULL, return EINVAL);
552
553 set_id = pcmk__xe_id(set);
554 if (set_id == NULL) {
555 pcmk__config_err("Ignoring " PCMK_XE_RESOURCE_SET " without "
556 PCMK_XA_ID " in constraint '%s'",
557 pcmk__s(pcmk__xe_id(location), "(missing ID)"));
558 return pcmk_rc_unpack_error;
559 }
560
561 role = crm_element_value(set, PCMK_XA_ROLE);
562 local_score = crm_element_value(set, PCMK_XA_SCORE);
563
564 for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL, NULL);
565 xml_rsc != NULL;
566 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
567
568 resource = pcmk__find_constraint_resource(scheduler->priv->resources,
569 pcmk__xe_id(xml_rsc));
570 if (resource == NULL) {
571 pcmk__config_err("%s: No resource found for %s",
572 set_id, pcmk__xe_id(xml_rsc));
573 return pcmk_rc_unpack_error;
574 }
575
576 unpack_rsc_location(location, resource, role, local_score, NULL, 0,
577 NULL);
578 }
579
580 return pcmk_rc_ok;
581 }
582
583 void
584 pcmk__unpack_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
585 {
586 xmlNode *set = NULL;
587 bool any_sets = false;
588
589 xmlNode *orig_xml = NULL;
590 xmlNode *expanded_xml = NULL;
591
592 if (unpack_location_tags(xml_obj, &expanded_xml, scheduler) != pcmk_rc_ok) {
593 return;
594 }
595
596 if (expanded_xml) {
597 orig_xml = xml_obj;
598 xml_obj = expanded_xml;
599 }
600
601 for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL);
602 set != NULL; set = pcmk__xe_next(set, PCMK_XE_RESOURCE_SET)) {
603
604 any_sets = true;
605 set = pcmk__xe_resolve_idref(set, scheduler->input);
606 if ((set == NULL)
607 || (unpack_location_set(xml_obj, set, scheduler) != pcmk_rc_ok)) {
608
609 if (expanded_xml) {
610 pcmk__xml_free(expanded_xml);
611 }
612 return;
613 }
614 }
615
616 if (expanded_xml) {
617 pcmk__xml_free(expanded_xml);
618 xml_obj = orig_xml;
619 }
620
621 if (!any_sets) {
622 unpack_simple_location(xml_obj, scheduler);
623 }
624 }
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640 pcmk__location_t *
641 pcmk__new_location(const char *id, pcmk_resource_t *rsc,
642 int node_score, const char *probe_mode, pcmk_node_t *node)
643 {
644 pcmk__location_t *new_con = NULL;
645
646 CRM_CHECK((node != NULL) || (node_score == 0), return NULL);
647
648 if (id == NULL) {
649 pcmk__config_err("Invalid constraint: no ID specified");
650 return NULL;
651 }
652
653 if (rsc == NULL) {
654 pcmk__config_err("Invalid constraint %s: no resource specified", id);
655 return NULL;
656 }
657
658 new_con = pcmk__assert_alloc(1, sizeof(pcmk__location_t));
659 new_con->id = pcmk__str_copy(id);
660 new_con->rsc = rsc;
661 new_con->nodes = NULL;
662 new_con->role_filter = pcmk_role_unknown;
663
664 if (pcmk__str_eq(probe_mode, PCMK_VALUE_ALWAYS,
665 pcmk__str_null_matches|pcmk__str_casei)) {
666 new_con->probe_mode = pcmk__probe_always;
667
668 } else if (pcmk__str_eq(probe_mode, PCMK_VALUE_NEVER, pcmk__str_casei)) {
669 new_con->probe_mode = pcmk__probe_never;
670
671 } else if (pcmk__str_eq(probe_mode, PCMK_VALUE_EXCLUSIVE,
672 pcmk__str_casei)) {
673 new_con->probe_mode = pcmk__probe_exclusive;
674 pcmk__set_rsc_flags(rsc, pcmk__rsc_exclusive_probes);
675
676 } else {
677 pcmk__config_err("Invalid " PCMK_XA_RESOURCE_DISCOVERY " value %s "
678 "in location constraint", probe_mode);
679 }
680
681 if (node != NULL) {
682 pcmk_node_t *copy = pe__copy_node(node);
683
684 copy->assign->score = node_score;
685 new_con->nodes = g_list_prepend(NULL, copy);
686 }
687
688 rsc->priv->scheduler->priv->location_constraints =
689 g_list_prepend(rsc->priv->scheduler->priv->location_constraints,
690 new_con);
691 rsc->priv->location_constraints =
692 g_list_prepend(rsc->priv->location_constraints, new_con);
693
694 return new_con;
695 }
696
697
698
699
700
701
702
703 void
704 pcmk__apply_locations(pcmk_scheduler_t *scheduler)
705 {
706 for (GList *iter = scheduler->priv->location_constraints;
707 iter != NULL; iter = iter->next) {
708 pcmk__location_t *location = iter->data;
709
710 location->rsc->priv->cmds->apply_location(location->rsc, location);
711 }
712 }
713
714
715
716
717
718
719
720
721
722
723
724 void
725 pcmk__apply_location(pcmk_resource_t *rsc, pcmk__location_t *location)
726 {
727 bool need_role = false;
728
729 pcmk__assert((rsc != NULL) && (location != NULL));
730
731
732 need_role = (location->role_filter > pcmk_role_unknown);
733 if (need_role && (location->role_filter != rsc->priv->next_role)) {
734 pcmk__rsc_trace(rsc,
735 "Not applying %s to %s because role will be %s not %s",
736 location->id, rsc->id,
737 pcmk_role_text(rsc->priv->next_role),
738 pcmk_role_text(location->role_filter));
739 return;
740 }
741
742 if (location->nodes == NULL) {
743 pcmk__rsc_trace(rsc, "Not applying %s to %s because no nodes match",
744 location->id, rsc->id);
745 return;
746 }
747
748 for (GList *iter = location->nodes; iter != NULL; iter = iter->next) {
749 pcmk_node_t *node = iter->data;
750 pcmk_node_t *allowed_node = NULL;
751
752 allowed_node = g_hash_table_lookup(rsc->priv->allowed_nodes,
753 node->priv->id);
754
755 pcmk__rsc_trace(rsc, "Applying %s%s%s to %s score on %s: %c %s",
756 location->id,
757 (need_role? " for role " : ""),
758 (need_role? pcmk_role_text(location->role_filter) : ""),
759 rsc->id, pcmk__node_name(node),
760 ((allowed_node == NULL)? '=' : '+'),
761 pcmk_readable_score(node->assign->score));
762
763 if (allowed_node == NULL) {
764 allowed_node = pe__copy_node(node);
765 g_hash_table_insert(rsc->priv->allowed_nodes,
766 (gpointer) allowed_node->priv->id,
767 allowed_node);
768 } else {
769 allowed_node->assign->score =
770 pcmk__add_scores(allowed_node->assign->score,
771 node->assign->score);
772 }
773
774 if (allowed_node->assign->probe_mode < location->probe_mode) {
775 if (location->probe_mode == pcmk__probe_exclusive) {
776 pcmk__set_rsc_flags(rsc, pcmk__rsc_exclusive_probes);
777 }
778
779 allowed_node->assign->probe_mode = location->probe_mode;
780 }
781 }
782 }