This source file includes following definitions.
- get_node_score
- 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/pengine/status.h>
17 #include <crm/pengine/rules.h>
18 #include <pacemaker-internal.h>
19
20 #include "libpacemaker_private.h"
21
22 static int
23 get_node_score(const char *rule, const char *score, bool raw,
24 pcmk_node_t *node, pcmk_resource_t *rsc)
25 {
26 int score_f = 0;
27
28 if (score == NULL) {
29 pe_err("Rule %s: no score specified. Assuming 0.", rule);
30
31 } else if (raw) {
32 score_f = char2score(score);
33
34 } else {
35 const char *attr_score = NULL;
36
37 attr_score = pe__node_attribute_calculated(node, score, rsc,
38 pcmk__rsc_node_current,
39 false);
40
41 if (attr_score == NULL) {
42 crm_debug("Rule %s: %s did not have a value for %s",
43 rule, pe__node_name(node), score);
44 score_f = -INFINITY;
45
46 } else {
47 crm_debug("Rule %s: %s had value %s for %s",
48 rule, pe__node_name(node), attr_score, score);
49 score_f = char2score(attr_score);
50 }
51 }
52 return score_f;
53 }
54
55 static pe__location_t *
56 generate_location_rule(pcmk_resource_t *rsc, xmlNode *rule_xml,
57 const char *discovery, crm_time_t *next_change,
58 pe_re_match_data_t *re_match_data)
59 {
60 const char *rule_id = NULL;
61 const char *score = NULL;
62 const char *boolean = NULL;
63 const char *role = NULL;
64
65 GList *iter = NULL;
66 GList *nodes = NULL;
67
68 bool do_and = true;
69 bool accept = true;
70 bool raw_score = true;
71 bool score_allocated = false;
72
73 pe__location_t *location_rule = NULL;
74
75 rule_xml = expand_idref(rule_xml, rsc->cluster->input);
76 if (rule_xml == NULL) {
77 return NULL;
78 }
79
80 rule_id = crm_element_value(rule_xml, XML_ATTR_ID);
81 boolean = crm_element_value(rule_xml, XML_RULE_ATTR_BOOLEAN_OP);
82 role = crm_element_value(rule_xml, XML_RULE_ATTR_ROLE);
83
84 crm_trace("Processing rule: %s", rule_id);
85
86 if ((role != NULL) && (text2role(role) == pcmk_role_unknown)) {
87 pe_err("Bad role specified for %s: %s", rule_id, role);
88 return NULL;
89 }
90
91 score = crm_element_value(rule_xml, XML_RULE_ATTR_SCORE);
92 if (score == NULL) {
93 score = crm_element_value(rule_xml, XML_RULE_ATTR_SCORE_ATTRIBUTE);
94 if (score != NULL) {
95 raw_score = false;
96 }
97 }
98 if (pcmk__str_eq(boolean, "or", pcmk__str_casei)) {
99 do_and = false;
100 }
101
102 location_rule = pcmk__new_location(rule_id, rsc, 0, discovery, NULL);
103
104 if (location_rule == NULL) {
105 return NULL;
106 }
107
108 if ((re_match_data != NULL) && (re_match_data->nregs > 0)
109 && (re_match_data->pmatch[0].rm_so != -1) && !raw_score) {
110
111 char *result = pe_expand_re_matches(score, re_match_data);
112
113 if (result != NULL) {
114 score = result;
115 score_allocated = true;
116 }
117 }
118
119 if (role != NULL) {
120 crm_trace("Setting role filter: %s", role);
121 location_rule->role_filter = text2role(role);
122 if (location_rule->role_filter == pcmk_role_unpromoted) {
123
124
125
126
127 location_rule->role_filter = pcmk_role_unknown;
128 }
129 }
130 if (do_and) {
131 nodes = pcmk__copy_node_list(rsc->cluster->nodes, true);
132 for (iter = nodes; iter != NULL; iter = iter->next) {
133 pcmk_node_t *node = iter->data;
134
135 node->weight = get_node_score(rule_id, score, raw_score, node, rsc);
136 }
137 }
138
139 for (iter = rsc->cluster->nodes; iter != NULL; iter = iter->next) {
140 int score_f = 0;
141 pcmk_node_t *node = iter->data;
142 pe_match_data_t match_data = {
143 .re = re_match_data,
144 .params = pe_rsc_params(rsc, node, rsc->cluster),
145 .meta = rsc->meta,
146 };
147
148 accept = pe_test_rule(rule_xml, node->details->attrs, pcmk_role_unknown,
149 rsc->cluster->now, next_change, &match_data);
150
151 crm_trace("Rule %s %s on %s", ID(rule_xml), accept? "passed" : "failed",
152 pe__node_name(node));
153
154 score_f = get_node_score(rule_id, score, raw_score, node, rsc);
155
156 if (accept) {
157 pcmk_node_t *local = pe_find_node_id(nodes, node->details->id);
158
159 if ((local == NULL) && do_and) {
160 continue;
161
162 } else if (local == NULL) {
163 local = pe__copy_node(node);
164 nodes = g_list_append(nodes, local);
165 }
166
167 if (!do_and) {
168 local->weight = pcmk__add_scores(local->weight, score_f);
169 }
170 crm_trace("%s has score %s after %s", pe__node_name(node),
171 pcmk_readable_score(local->weight), rule_id);
172
173 } else if (do_and && !accept) {
174
175 pcmk_node_t *delete = pe_find_node_id(nodes, node->details->id);
176
177 if (delete != NULL) {
178 nodes = g_list_remove(nodes, delete);
179 crm_trace("%s did not match", pe__node_name(node));
180 }
181 free(delete);
182 }
183 }
184
185 if (score_allocated) {
186 free((char *)score);
187 }
188
189 location_rule->node_list_rh = nodes;
190 if (location_rule->node_list_rh == NULL) {
191 crm_trace("No matching nodes for rule %s", rule_id);
192 return NULL;
193 }
194
195 crm_trace("%s: %d nodes matched",
196 rule_id, g_list_length(location_rule->node_list_rh));
197 return location_rule;
198 }
199
200 static void
201 unpack_rsc_location(xmlNode *xml_obj, pcmk_resource_t *rsc, const char *role,
202 const char *score, pe_re_match_data_t *re_match_data)
203 {
204 pe__location_t *location = NULL;
205 const char *rsc_id = crm_element_value(xml_obj, XML_LOC_ATTR_SOURCE);
206 const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
207 const char *node = crm_element_value(xml_obj, XML_CIB_TAG_NODE);
208 const char *discovery = crm_element_value(xml_obj,
209 XML_LOCATION_ATTR_DISCOVERY);
210
211 if (rsc == NULL) {
212 pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
213 "does not exist", id, rsc_id);
214 return;
215 }
216
217 if (score == NULL) {
218 score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
219 }
220
221 if ((node != NULL) && (score != NULL)) {
222 int score_i = char2score(score);
223 pcmk_node_t *match = pe_find_node(rsc->cluster->nodes, node);
224
225 if (!match) {
226 return;
227 }
228 location = pcmk__new_location(id, rsc, score_i, discovery, match);
229
230 } else {
231 bool empty = true;
232 crm_time_t *next_change = crm_time_new_undefined();
233
234
235
236
237
238 for (xmlNode *rule_xml = first_named_child(xml_obj, XML_TAG_RULE);
239 rule_xml != NULL; rule_xml = crm_next_same_xml(rule_xml)) {
240 empty = false;
241 crm_trace("Unpacking %s/%s", id, ID(rule_xml));
242 generate_location_rule(rsc, rule_xml, discovery, next_change,
243 re_match_data);
244 }
245
246 if (empty) {
247 pcmk__config_err("Ignoring constraint '%s' because it contains "
248 "no rules", id);
249 }
250
251
252
253
254 if (crm_time_is_defined(next_change)) {
255 time_t t = (time_t) crm_time_get_seconds_since_epoch(next_change);
256
257 pe__update_recheck_time(t, rsc->cluster,
258 "location rule evaluation");
259 }
260 crm_time_free(next_change);
261 return;
262 }
263
264 if (role == NULL) {
265 role = crm_element_value(xml_obj, XML_RULE_ATTR_ROLE);
266 }
267
268 if ((location != NULL) && (role != NULL)) {
269 if (text2role(role) == pcmk_role_unknown) {
270 pe_err("Invalid constraint %s: Bad role %s", id, role);
271 return;
272
273 } else {
274 enum rsc_role_e r = text2role(role);
275 switch (r) {
276 case pcmk_role_unknown:
277 case pcmk_role_started:
278 case pcmk_role_unpromoted:
279
280 location->role_filter = pcmk_role_unknown;
281 break;
282 default:
283 location->role_filter = r;
284 break;
285 }
286 }
287 }
288 }
289
290 static void
291 unpack_simple_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
292 {
293 const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
294 const char *value = crm_element_value(xml_obj, XML_LOC_ATTR_SOURCE);
295
296 if (value) {
297 pcmk_resource_t *rsc;
298
299 rsc = pcmk__find_constraint_resource(scheduler->resources, value);
300 unpack_rsc_location(xml_obj, rsc, NULL, NULL, NULL);
301 }
302
303 value = crm_element_value(xml_obj, XML_LOC_ATTR_SOURCE_PATTERN);
304 if (value) {
305 regex_t *r_patt = calloc(1, sizeof(regex_t));
306 bool invert = false;
307
308 if (value[0] == '!') {
309 value++;
310 invert = true;
311 }
312
313 if (regcomp(r_patt, value, REG_EXTENDED) != 0) {
314 pcmk__config_err("Ignoring constraint '%s' because "
315 XML_LOC_ATTR_SOURCE_PATTERN
316 " has invalid value '%s'", id, value);
317 free(r_patt);
318 return;
319 }
320
321 for (GList *iter = scheduler->resources; iter != NULL;
322 iter = iter->next) {
323
324 pcmk_resource_t *r = iter->data;
325 int nregs = 0;
326 regmatch_t *pmatch = NULL;
327 int status;
328
329 if (r_patt->re_nsub > 0) {
330 nregs = r_patt->re_nsub + 1;
331 } else {
332 nregs = 1;
333 }
334 pmatch = calloc(nregs, sizeof(regmatch_t));
335
336 status = regexec(r_patt, r->id, nregs, pmatch, 0);
337
338 if (!invert && (status == 0)) {
339 pe_re_match_data_t re_match_data = {
340 .string = r->id,
341 .nregs = nregs,
342 .pmatch = pmatch
343 };
344
345 crm_debug("'%s' matched '%s' for %s", r->id, value, id);
346 unpack_rsc_location(xml_obj, r, NULL, NULL, &re_match_data);
347
348 } else if (invert && (status != 0)) {
349 crm_debug("'%s' is an inverted match of '%s' for %s",
350 r->id, value, id);
351 unpack_rsc_location(xml_obj, r, NULL, NULL, NULL);
352
353 } else {
354 crm_trace("'%s' does not match '%s' for %s", r->id, value, id);
355 }
356
357 free(pmatch);
358 }
359
360 regfree(r_patt);
361 free(r_patt);
362 }
363 }
364
365
366 static int
367 unpack_location_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
368 pcmk_scheduler_t *scheduler)
369 {
370 const char *id = NULL;
371 const char *rsc_id = NULL;
372 const char *state = NULL;
373 pcmk_resource_t *rsc = NULL;
374 pcmk_tag_t *tag = NULL;
375 xmlNode *rsc_set = NULL;
376
377 *expanded_xml = NULL;
378
379 CRM_CHECK(xml_obj != NULL, return EINVAL);
380
381 id = ID(xml_obj);
382 if (id == NULL) {
383 pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
384 xml_obj->name);
385 return pcmk_rc_unpack_error;
386 }
387
388
389 *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
390 if (*expanded_xml != NULL) {
391 crm_log_xml_trace(*expanded_xml, "Expanded rsc_location");
392 return pcmk_rc_ok;
393 }
394
395 rsc_id = crm_element_value(xml_obj, XML_LOC_ATTR_SOURCE);
396 if (rsc_id == NULL) {
397 return pcmk_rc_ok;
398 }
399
400 if (!pcmk__valid_resource_or_tag(scheduler, rsc_id, &rsc, &tag)) {
401 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
402 "valid resource or tag", id, rsc_id);
403 return pcmk_rc_unpack_error;
404
405 } else if (rsc != NULL) {
406
407 return pcmk_rc_ok;
408 }
409
410 state = crm_element_value(xml_obj, XML_RULE_ATTR_ROLE);
411
412 *expanded_xml = copy_xml(xml_obj);
413
414
415 if (!pcmk__tag_to_set(*expanded_xml, &rsc_set, XML_LOC_ATTR_SOURCE,
416 false, scheduler)) {
417 free_xml(*expanded_xml);
418 *expanded_xml = NULL;
419 return pcmk_rc_unpack_error;
420 }
421
422 if (rsc_set != NULL) {
423 if (state != NULL) {
424
425 crm_xml_add(rsc_set, "role", state);
426 xml_remove_prop(*expanded_xml, XML_RULE_ATTR_ROLE);
427 }
428 crm_log_xml_trace(*expanded_xml, "Expanded rsc_location");
429
430 } else {
431
432 free_xml(*expanded_xml);
433 *expanded_xml = NULL;
434 }
435
436 return pcmk_rc_ok;
437 }
438
439
440 static int
441 unpack_location_set(xmlNode *location, xmlNode *set,
442 pcmk_scheduler_t *scheduler)
443 {
444 xmlNode *xml_rsc = NULL;
445 pcmk_resource_t *resource = NULL;
446 const char *set_id;
447 const char *role;
448 const char *local_score;
449
450 CRM_CHECK(set != NULL, return EINVAL);
451
452 set_id = ID(set);
453 if (set_id == NULL) {
454 pcmk__config_err("Ignoring " XML_CONS_TAG_RSC_SET " without "
455 XML_ATTR_ID " in constraint '%s'",
456 pcmk__s(ID(location), "(missing ID)"));
457 return pcmk_rc_unpack_error;
458 }
459
460 role = crm_element_value(set, "role");
461 local_score = crm_element_value(set, XML_RULE_ATTR_SCORE);
462
463 for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
464 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
465
466 resource = pcmk__find_constraint_resource(scheduler->resources,
467 ID(xml_rsc));
468 if (resource == NULL) {
469 pcmk__config_err("%s: No resource found for %s",
470 set_id, ID(xml_rsc));
471 return pcmk_rc_unpack_error;
472 }
473
474 unpack_rsc_location(location, resource, role, local_score, NULL);
475 }
476
477 return pcmk_rc_ok;
478 }
479
480 void
481 pcmk__unpack_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
482 {
483 xmlNode *set = NULL;
484 bool any_sets = false;
485
486 xmlNode *orig_xml = NULL;
487 xmlNode *expanded_xml = NULL;
488
489 if (unpack_location_tags(xml_obj, &expanded_xml, scheduler) != pcmk_rc_ok) {
490 return;
491 }
492
493 if (expanded_xml) {
494 orig_xml = xml_obj;
495 xml_obj = expanded_xml;
496 }
497
498 for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); set != NULL;
499 set = crm_next_same_xml(set)) {
500
501 any_sets = true;
502 set = expand_idref(set, scheduler->input);
503 if ((set == NULL)
504 || (unpack_location_set(xml_obj, set, scheduler) != pcmk_rc_ok)) {
505
506 if (expanded_xml) {
507 free_xml(expanded_xml);
508 }
509 return;
510 }
511 }
512
513 if (expanded_xml) {
514 free_xml(expanded_xml);
515 xml_obj = orig_xml;
516 }
517
518 if (!any_sets) {
519 unpack_simple_location(xml_obj, scheduler);
520 }
521 }
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537 pe__location_t *
538 pcmk__new_location(const char *id, pcmk_resource_t *rsc,
539 int node_score, const char *discover_mode, pcmk_node_t *node)
540 {
541 pe__location_t *new_con = NULL;
542
543 if (id == NULL) {
544 pe_err("Invalid constraint: no ID specified");
545 return NULL;
546
547 } else if (rsc == NULL) {
548 pe_err("Invalid constraint %s: no resource specified", id);
549 return NULL;
550
551 } else if (node == NULL) {
552 CRM_CHECK(node_score == 0, return NULL);
553 }
554
555 new_con = calloc(1, sizeof(pe__location_t));
556 if (new_con != NULL) {
557 new_con->id = strdup(id);
558 new_con->rsc_lh = rsc;
559 new_con->node_list_rh = NULL;
560 new_con->role_filter = pcmk_role_unknown;
561
562 if (pcmk__str_eq(discover_mode, "always",
563 pcmk__str_null_matches|pcmk__str_casei)) {
564 new_con->discover_mode = pcmk_probe_always;
565
566 } else if (pcmk__str_eq(discover_mode, "never", pcmk__str_casei)) {
567 new_con->discover_mode = pcmk_probe_never;
568
569 } else if (pcmk__str_eq(discover_mode, "exclusive", pcmk__str_casei)) {
570 new_con->discover_mode = pcmk_probe_exclusive;
571 rsc->exclusive_discover = TRUE;
572
573 } else {
574 pe_err("Invalid " XML_LOCATION_ATTR_DISCOVERY " value %s "
575 "in location constraint", discover_mode);
576 }
577
578 if (node != NULL) {
579 pcmk_node_t *copy = pe__copy_node(node);
580
581 copy->weight = node_score;
582 new_con->node_list_rh = g_list_prepend(NULL, copy);
583 }
584
585 rsc->cluster->placement_constraints = g_list_prepend(
586 rsc->cluster->placement_constraints, new_con);
587 rsc->rsc_location = g_list_prepend(rsc->rsc_location, new_con);
588 }
589
590 return new_con;
591 }
592
593
594
595
596
597
598
599 void
600 pcmk__apply_locations(pcmk_scheduler_t *scheduler)
601 {
602 for (GList *iter = scheduler->placement_constraints;
603 iter != NULL; iter = iter->next) {
604 pe__location_t *location = iter->data;
605
606 location->rsc_lh->cmds->apply_location(location->rsc_lh, location);
607 }
608 }
609
610
611
612
613
614
615
616
617
618
619
620 void
621 pcmk__apply_location(pcmk_resource_t *rsc, pe__location_t *location)
622 {
623 bool need_role = false;
624
625 CRM_ASSERT((rsc != NULL) && (location != NULL));
626
627
628 need_role = (location->role_filter > pcmk_role_unknown);
629 if (need_role && (location->role_filter != rsc->next_role)) {
630 pe_rsc_trace(rsc,
631 "Not applying %s to %s because role will be %s not %s",
632 location->id, rsc->id, role2text(rsc->next_role),
633 role2text(location->role_filter));
634 return;
635 }
636
637 if (location->node_list_rh == NULL) {
638 pe_rsc_trace(rsc, "Not applying %s to %s because no nodes match",
639 location->id, rsc->id);
640 return;
641 }
642
643 pe_rsc_trace(rsc, "Applying %s%s%s to %s", location->id,
644 (need_role? " for role " : ""),
645 (need_role? role2text(location->role_filter) : ""), rsc->id);
646
647 for (GList *iter = location->node_list_rh;
648 iter != NULL; iter = iter->next) {
649
650 pcmk_node_t *node = iter->data;
651 pcmk_node_t *allowed_node = g_hash_table_lookup(rsc->allowed_nodes,
652 node->details->id);
653
654 if (allowed_node == NULL) {
655 pe_rsc_trace(rsc, "* = %d on %s",
656 node->weight, pe__node_name(node));
657 allowed_node = pe__copy_node(node);
658 g_hash_table_insert(rsc->allowed_nodes,
659 (gpointer) allowed_node->details->id,
660 allowed_node);
661 } else {
662 pe_rsc_trace(rsc, "* + %d on %s",
663 node->weight, pe__node_name(node));
664 allowed_node->weight = pcmk__add_scores(allowed_node->weight,
665 node->weight);
666 }
667
668 if (allowed_node->rsc_discover_mode < location->discover_mode) {
669 if (location->discover_mode == pcmk_probe_exclusive) {
670 rsc->exclusive_discover = TRUE;
671 }
672
673 allowed_node->rsc_discover_mode = location->discover_mode;
674 }
675 }
676 }