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 <pacemaker-internal.h>
18
19 #include "libpacemaker_private.h"
20
21 static int
22 get_node_score(const char *rule, const char *score, bool raw,
23 pe_node_t *node, pe_resource_t *rsc)
24 {
25 int score_f = 0;
26
27 if (score == NULL) {
28 pe_err("Rule %s: no score specified. Assuming 0.", rule);
29
30 } else if (raw) {
31 score_f = char2score(score);
32
33 } else {
34 const char *attr_score = pe_node_attribute_calculated(node, score, rsc);
35
36 if (attr_score == NULL) {
37 crm_debug("Rule %s: node %s did not have a value for %s",
38 rule, node->details->uname, score);
39 score_f = -INFINITY;
40
41 } else {
42 crm_debug("Rule %s: node %s had value %s for %s",
43 rule, node->details->uname, attr_score, score);
44 score_f = char2score(attr_score);
45 }
46 }
47 return score_f;
48 }
49
50 static pe__location_t *
51 generate_location_rule(pe_resource_t *rsc, xmlNode *rule_xml,
52 const char *discovery, crm_time_t *next_change,
53 pe_working_set_t *data_set,
54 pe_re_match_data_t *re_match_data)
55 {
56 const char *rule_id = NULL;
57 const char *score = NULL;
58 const char *boolean = NULL;
59 const char *role = NULL;
60
61 GList *gIter = NULL;
62 GList *match_L = NULL;
63
64 bool do_and = true;
65 bool accept = true;
66 bool raw_score = true;
67 bool score_allocated = false;
68
69 pe__location_t *location_rule = NULL;
70
71 rule_xml = expand_idref(rule_xml, data_set->input);
72 if (rule_xml == NULL) {
73 return NULL;
74 }
75
76 rule_id = crm_element_value(rule_xml, XML_ATTR_ID);
77 boolean = crm_element_value(rule_xml, XML_RULE_ATTR_BOOLEAN_OP);
78 role = crm_element_value(rule_xml, XML_RULE_ATTR_ROLE);
79
80 crm_trace("Processing rule: %s", rule_id);
81
82 if ((role != NULL) && (text2role(role) == RSC_ROLE_UNKNOWN)) {
83 pe_err("Bad role specified for %s: %s", rule_id, role);
84 return NULL;
85 }
86
87 score = crm_element_value(rule_xml, XML_RULE_ATTR_SCORE);
88 if (score == NULL) {
89 score = crm_element_value(rule_xml, XML_RULE_ATTR_SCORE_ATTRIBUTE);
90 if (score != NULL) {
91 raw_score = false;
92 }
93 }
94 if (pcmk__str_eq(boolean, "or", pcmk__str_casei)) {
95 do_and = false;
96 }
97
98 location_rule = pcmk__new_location(rule_id, rsc, 0, discovery, NULL,
99 data_set);
100
101 if (location_rule == NULL) {
102 return NULL;
103 }
104
105 if ((re_match_data != NULL) && (re_match_data->nregs > 0)
106 && (re_match_data->pmatch[0].rm_so != -1) && !raw_score) {
107
108 char *result = pe_expand_re_matches(score, re_match_data);
109
110 if (result != NULL) {
111 score = result;
112 score_allocated = true;
113 }
114 }
115
116 if (role != NULL) {
117 crm_trace("Setting role filter: %s", role);
118 location_rule->role_filter = text2role(role);
119 if (location_rule->role_filter == RSC_ROLE_UNPROMOTED) {
120
121
122
123
124 location_rule->role_filter = RSC_ROLE_UNKNOWN;
125 }
126 }
127 if (do_and) {
128 GList *gIter = NULL;
129
130 match_L = pcmk__copy_node_list(data_set->nodes, true);
131 for (gIter = match_L; gIter != NULL; gIter = gIter->next) {
132 pe_node_t *node = (pe_node_t *) gIter->data;
133
134 node->weight = get_node_score(rule_id, score, raw_score, node, rsc);
135 }
136 }
137
138 for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
139 int score_f = 0;
140 pe_node_t *node = (pe_node_t *) gIter->data;
141 pe_match_data_t match_data = {
142 .re = re_match_data,
143 .params = pe_rsc_params(rsc, node, data_set),
144 .meta = rsc->meta,
145 };
146
147 accept = pe_test_rule(rule_xml, node->details->attrs, RSC_ROLE_UNKNOWN,
148 data_set->now, next_change, &match_data);
149
150 crm_trace("Rule %s %s on %s", ID(rule_xml), accept? "passed" : "failed",
151 node->details->uname);
152
153 score_f = get_node_score(rule_id, score, raw_score, node, rsc);
154
155 if (accept) {
156 pe_node_t *local = pe_find_node_id(match_L, node->details->id);
157
158 if ((local == NULL) && do_and) {
159 continue;
160
161 } else if (local == NULL) {
162 local = pe__copy_node(node);
163 match_L = g_list_append(match_L, local);
164 }
165
166 if (!do_and) {
167 local->weight = pe__add_scores(local->weight, score_f);
168 }
169 crm_trace("node %s now has weight %d",
170 node->details->uname, local->weight);
171
172 } else if (do_and && !accept) {
173
174 pe_node_t *delete = pe_find_node_id(match_L, node->details->id);
175
176 if (delete != NULL) {
177 match_L = g_list_remove(match_L, delete);
178 crm_trace("node %s did not match", node->details->uname);
179 }
180 free(delete);
181 }
182 }
183
184 if (score_allocated) {
185 free((char *)score);
186 }
187
188 location_rule->node_list_rh = match_L;
189 if (location_rule->node_list_rh == NULL) {
190 crm_trace("No matching nodes for rule %s", rule_id);
191 return NULL;
192 }
193
194 crm_trace("%s: %d nodes matched",
195 rule_id, g_list_length(location_rule->node_list_rh));
196 return location_rule;
197 }
198
199 static void
200 unpack_rsc_location(xmlNode *xml_obj, pe_resource_t *rsc_lh, const char *role,
201 const char *score, pe_working_set_t *data_set,
202 pe_re_match_data_t *re_match_data)
203 {
204 pe__location_t *location = NULL;
205 const char *id_lh = 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, XML_LOCATION_ATTR_DISCOVERY);
209
210 if (rsc_lh == NULL) {
211 pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
212 "does not exist", id, id_lh);
213 return;
214 }
215
216 if (score == NULL) {
217 score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
218 }
219
220 if ((node != NULL) && (score != NULL)) {
221 int score_i = char2score(score);
222 pe_node_t *match = pe_find_node(data_set->nodes, node);
223
224 if (!match) {
225 return;
226 }
227 location = pcmk__new_location(id, rsc_lh, score_i, discovery, match,
228 data_set);
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_lh, rule_xml, discovery, next_change,
243 data_set, 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, data_set);
258 }
259 crm_time_free(next_change);
260 return;
261 }
262
263 if (role == NULL) {
264 role = crm_element_value(xml_obj, XML_RULE_ATTR_ROLE);
265 }
266
267 if ((location != NULL) && (role != NULL)) {
268 if (text2role(role) == RSC_ROLE_UNKNOWN) {
269 pe_err("Invalid constraint %s: Bad role %s", id, role);
270 return;
271
272 } else {
273 enum rsc_role_e r = text2role(role);
274 switch(r) {
275 case RSC_ROLE_UNKNOWN:
276 case RSC_ROLE_STARTED:
277 case RSC_ROLE_UNPROMOTED:
278
279 location->role_filter = RSC_ROLE_UNKNOWN;
280 break;
281 default:
282 location->role_filter = r;
283 break;
284 }
285 }
286 }
287 }
288
289 static void
290 unpack_simple_location(xmlNode *xml_obj, pe_working_set_t *data_set)
291 {
292 const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
293 const char *value = crm_element_value(xml_obj, XML_LOC_ATTR_SOURCE);
294
295 if (value) {
296 pe_resource_t *rsc_lh;
297
298 rsc_lh = pcmk__find_constraint_resource(data_set->resources, value);
299 unpack_rsc_location(xml_obj, rsc_lh, NULL, NULL, data_set, NULL);
300 }
301
302 value = crm_element_value(xml_obj, XML_LOC_ATTR_SOURCE_PATTERN);
303 if (value) {
304 regex_t *r_patt = calloc(1, sizeof(regex_t));
305 bool invert = false;
306 GList *rIter = NULL;
307
308 if (value[0] == '!') {
309 value++;
310 invert = true;
311 }
312
313 if (regcomp(r_patt, value, REG_EXTENDED)) {
314 pcmk__config_err("Ignoring constraint '%s' because "
315 XML_LOC_ATTR_SOURCE_PATTERN
316 " has invalid value '%s'", id, value);
317 regfree(r_patt);
318 free(r_patt);
319 return;
320 }
321
322 for (rIter = data_set->resources; rIter; rIter = rIter->next) {
323 pe_resource_t *r = rIter->data;
324 int nregs = 0;
325 regmatch_t *pmatch = NULL;
326 int status;
327
328 if(r_patt->re_nsub > 0) {
329 nregs = r_patt->re_nsub + 1;
330 } else {
331 nregs = 1;
332 }
333 pmatch = calloc(nregs, sizeof(regmatch_t));
334
335 status = regexec(r_patt, r->id, nregs, pmatch, 0);
336
337 if (!invert && (status == 0)) {
338 pe_re_match_data_t re_match_data = {
339 .string = r->id,
340 .nregs = nregs,
341 .pmatch = pmatch
342 };
343
344 crm_debug("'%s' matched '%s' for %s", r->id, value, id);
345 unpack_rsc_location(xml_obj, r, NULL, NULL, data_set,
346 &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, data_set, 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 pe_working_set_t *data_set)
369 {
370 const char *id = NULL;
371 const char *id_lh = NULL;
372 const char *state_lh = NULL;
373 pe_resource_t *rsc_lh = NULL;
374 pe_tag_t *tag_lh = NULL;
375 xmlNode *rsc_set_lh = NULL;
376
377 *expanded_xml = NULL;
378
379 CRM_CHECK(xml_obj != NULL, return pcmk_rc_schema_validation);
380
381 id = ID(xml_obj);
382 if (id == NULL) {
383 pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
384 crm_element_name(xml_obj));
385 return pcmk_rc_schema_validation;
386 }
387
388
389 *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, data_set);
390 if (*expanded_xml != NULL) {
391 crm_log_xml_trace(*expanded_xml, "Expanded rsc_location");
392 return pcmk_rc_ok;
393 }
394
395 id_lh = crm_element_value(xml_obj, XML_LOC_ATTR_SOURCE);
396 if (id_lh == NULL) {
397 return pcmk_rc_ok;
398 }
399
400 if (!pcmk__valid_resource_or_tag(data_set, id_lh, &rsc_lh, &tag_lh)) {
401 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
402 "valid resource or tag", id, id_lh);
403 return pcmk_rc_schema_validation;
404
405 } else if (rsc_lh != NULL) {
406
407 return pcmk_rc_ok;
408 }
409
410 state_lh = 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_lh, XML_LOC_ATTR_SOURCE,
416 false, data_set)) {
417 free_xml(*expanded_xml);
418 *expanded_xml = NULL;
419 return pcmk_rc_schema_validation;
420 }
421
422 if (rsc_set_lh != NULL) {
423 if (state_lh != NULL) {
424
425 crm_xml_add(rsc_set_lh, "role", state_lh);
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, pe_working_set_t *data_set)
442 {
443 xmlNode *xml_rsc = NULL;
444 pe_resource_t *resource = NULL;
445 const char *set_id;
446 const char *role;
447 const char *local_score;
448
449 CRM_CHECK(set != NULL, return pcmk_rc_schema_validation);
450
451 set_id = ID(set);
452 if (set_id == NULL) {
453 pcmk__config_err("Ignoring " XML_CONS_TAG_RSC_SET " without "
454 XML_ATTR_ID " in constraint '%s'",
455 crm_str(ID(location)));
456 return pcmk_rc_schema_validation;
457 }
458
459 role = crm_element_value(set, "role");
460 local_score = crm_element_value(set, XML_RULE_ATTR_SCORE);
461
462 for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
463 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
464
465 resource = pcmk__find_constraint_resource(data_set->resources,
466 ID(xml_rsc));
467 if (resource == NULL) {
468 pcmk__config_err("%s: No resource found for %s",
469 set_id, ID(xml_rsc));
470 return pcmk_rc_schema_validation;
471 }
472
473 unpack_rsc_location(location, resource, role, local_score, data_set,
474 NULL);
475 }
476
477 return pcmk_rc_ok;
478 }
479
480 void
481 pcmk__unpack_location(xmlNode *xml_obj, pe_working_set_t *data_set)
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, data_set) != 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, data_set->input);
503 if ((set == NULL)
504 || (unpack_location_set(xml_obj, set, data_set) != 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, data_set);
520 }
521 }
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538 pe__location_t *
539 pcmk__new_location(const char *id, pe_resource_t *rsc,
540 int node_weight, const char *discover_mode,
541 pe_node_t *node, pe_working_set_t *data_set)
542 {
543 pe__location_t *new_con = NULL;
544
545 if ((rsc == NULL) || (id == NULL)) {
546 pe_err("Invalid constraint %s for rsc=%p", crm_str(id), rsc);
547 return NULL;
548
549 } else if (node == NULL) {
550 CRM_CHECK(node_weight == 0, return NULL);
551 }
552
553 new_con = calloc(1, sizeof(pe__location_t));
554 if (new_con != NULL) {
555 new_con->id = strdup(id);
556 new_con->rsc_lh = rsc;
557 new_con->node_list_rh = NULL;
558 new_con->role_filter = RSC_ROLE_UNKNOWN;
559
560 if (pcmk__str_eq(discover_mode, "always",
561 pcmk__str_null_matches|pcmk__str_casei)) {
562 new_con->discover_mode = pe_discover_always;
563
564 } else if (pcmk__str_eq(discover_mode, "never", pcmk__str_casei)) {
565 new_con->discover_mode = pe_discover_never;
566
567 } else if (pcmk__str_eq(discover_mode, "exclusive", pcmk__str_casei)) {
568 new_con->discover_mode = pe_discover_exclusive;
569 rsc->exclusive_discover = TRUE;
570
571 } else {
572 pe_err("Invalid " XML_LOCATION_ATTR_DISCOVERY " value %s "
573 "in location constraint", discover_mode);
574 }
575
576 if (node != NULL) {
577 pe_node_t *copy = pe__copy_node(node);
578
579 copy->weight = node_weight;
580 new_con->node_list_rh = g_list_prepend(NULL, copy);
581 }
582
583 data_set->placement_constraints = g_list_prepend(data_set->placement_constraints,
584 new_con);
585 rsc->rsc_location = g_list_prepend(rsc->rsc_location, new_con);
586 }
587
588 return new_con;
589 }
590
591
592
593
594
595
596
597 void
598 pcmk__apply_locations(pe_working_set_t *data_set)
599 {
600 for (GList *iter = data_set->placement_constraints;
601 iter != NULL; iter = iter->next) {
602 pe__location_t *location = iter->data;
603
604 location->rsc_lh->cmds->rsc_location(location->rsc_lh, location);
605 }
606 }
607
608
609
610
611
612
613
614
615 void
616 pcmk__apply_location(pe__location_t *constraint, pe_resource_t *rsc)
617 {
618 bool need_role = false;
619
620 CRM_CHECK((constraint != NULL) && (rsc != NULL), return);
621
622
623 need_role = (constraint->role_filter > RSC_ROLE_UNKNOWN);
624 if (need_role && (constraint->role_filter != rsc->next_role)) {
625 pe_rsc_trace(rsc,
626 "Not applying %s to %s because role will be %s not %s",
627 constraint->id, rsc->id, role2text(rsc->next_role),
628 role2text(constraint->role_filter));
629 return;
630 }
631
632 if (constraint->node_list_rh == NULL) {
633 pe_rsc_trace(rsc, "Not applying %s to %s because no nodes match",
634 constraint->id, rsc->id);
635 return;
636 }
637
638 pe_rsc_trace(rsc, "Applying %s%s%s to %s", constraint->id,
639 (need_role? " for role " : ""),
640 (need_role? role2text(constraint->role_filter) : ""), rsc->id);
641
642 for (GList *gIter = constraint->node_list_rh; gIter != NULL;
643 gIter = gIter->next) {
644
645 pe_node_t *node = (pe_node_t *) gIter->data;
646 pe_node_t *weighted_node = NULL;
647
648 weighted_node = (pe_node_t *) pe_hash_table_lookup(rsc->allowed_nodes,
649 node->details->id);
650 if (weighted_node == NULL) {
651 pe_rsc_trace(rsc, "* = %d on %s",
652 node->weight, node->details->uname);
653 weighted_node = pe__copy_node(node);
654 g_hash_table_insert(rsc->allowed_nodes,
655 (gpointer) weighted_node->details->id,
656 weighted_node);
657 } else {
658 pe_rsc_trace(rsc, "* + %d on %s",
659 node->weight, node->details->uname);
660 weighted_node->weight = pe__add_scores(weighted_node->weight,
661 node->weight);
662 }
663
664 if (weighted_node->rsc_discover_mode < constraint->discover_mode) {
665 if (constraint->discover_mode == pe_discover_exclusive) {
666 rsc->exclusive_discover = TRUE;
667 }
668
669 weighted_node->rsc_discover_mode = constraint->discover_mode;
670 }
671 }
672 }