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: %s did not have a value for %s",
38 rule, pe__node_name(node), score);
39 score_f = -INFINITY;
40
41 } else {
42 crm_debug("Rule %s: %s had value %s for %s",
43 rule, pe__node_name(node), 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 pe__node_name(node));
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 = pcmk__add_scores(local->weight, score_f);
168 }
169 crm_trace("%s has score %s after %s", pe__node_name(node),
170 pcmk_readable_score(local->weight), rule_id);
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("%s did not match", pe__node_name(node));
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, 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 *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, XML_LOCATION_ATTR_DISCOVERY);
209
210 if (rsc == NULL) {
211 pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
212 "does not exist", id, rsc_id);
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, 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, 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;
297
298 rsc = pcmk__find_constraint_resource(data_set->resources, value);
299 unpack_rsc_location(xml_obj, rsc, 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) != 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 (rIter = data_set->resources; rIter; rIter = rIter->next) {
322 pe_resource_t *r = rIter->data;
323 int nregs = 0;
324 regmatch_t *pmatch = NULL;
325 int status;
326
327 if(r_patt->re_nsub > 0) {
328 nregs = r_patt->re_nsub + 1;
329 } else {
330 nregs = 1;
331 }
332 pmatch = calloc(nregs, sizeof(regmatch_t));
333
334 status = regexec(r_patt, r->id, nregs, pmatch, 0);
335
336 if (!invert && (status == 0)) {
337 pe_re_match_data_t re_match_data = {
338 .string = r->id,
339 .nregs = nregs,
340 .pmatch = pmatch
341 };
342
343 crm_debug("'%s' matched '%s' for %s", r->id, value, id);
344 unpack_rsc_location(xml_obj, r, NULL, NULL, data_set,
345 &re_match_data);
346
347 } else if (invert && (status != 0)) {
348 crm_debug("'%s' is an inverted match of '%s' for %s",
349 r->id, value, id);
350 unpack_rsc_location(xml_obj, r, NULL, NULL, data_set, NULL);
351
352 } else {
353 crm_trace("'%s' does not match '%s' for %s", r->id, value, id);
354 }
355
356 free(pmatch);
357 }
358
359 regfree(r_patt);
360 free(r_patt);
361 }
362 }
363
364
365 static int
366 unpack_location_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
367 pe_working_set_t *data_set)
368 {
369 const char *id = NULL;
370 const char *rsc_id = NULL;
371 const char *state = NULL;
372 pe_resource_t *rsc = NULL;
373 pe_tag_t *tag = NULL;
374 xmlNode *rsc_set = NULL;
375
376 *expanded_xml = NULL;
377
378 CRM_CHECK(xml_obj != NULL, return EINVAL);
379
380 id = ID(xml_obj);
381 if (id == NULL) {
382 pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
383 crm_element_name(xml_obj));
384 return pcmk_rc_unpack_error;
385 }
386
387
388 *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, data_set);
389 if (*expanded_xml != NULL) {
390 crm_log_xml_trace(*expanded_xml, "Expanded rsc_location");
391 return pcmk_rc_ok;
392 }
393
394 rsc_id = crm_element_value(xml_obj, XML_LOC_ATTR_SOURCE);
395 if (rsc_id == NULL) {
396 return pcmk_rc_ok;
397 }
398
399 if (!pcmk__valid_resource_or_tag(data_set, rsc_id, &rsc, &tag)) {
400 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
401 "valid resource or tag", id, rsc_id);
402 return pcmk_rc_unpack_error;
403
404 } else if (rsc != NULL) {
405
406 return pcmk_rc_ok;
407 }
408
409 state = crm_element_value(xml_obj, XML_RULE_ATTR_ROLE);
410
411 *expanded_xml = copy_xml(xml_obj);
412
413
414 if (!pcmk__tag_to_set(*expanded_xml, &rsc_set, XML_LOC_ATTR_SOURCE,
415 false, data_set)) {
416 free_xml(*expanded_xml);
417 *expanded_xml = NULL;
418 return pcmk_rc_unpack_error;
419 }
420
421 if (rsc_set != NULL) {
422 if (state != NULL) {
423
424 crm_xml_add(rsc_set, "role", state);
425 xml_remove_prop(*expanded_xml, XML_RULE_ATTR_ROLE);
426 }
427 crm_log_xml_trace(*expanded_xml, "Expanded rsc_location");
428
429 } else {
430
431 free_xml(*expanded_xml);
432 *expanded_xml = NULL;
433 }
434
435 return pcmk_rc_ok;
436 }
437
438
439 static int
440 unpack_location_set(xmlNode *location, xmlNode *set, pe_working_set_t *data_set)
441 {
442 xmlNode *xml_rsc = NULL;
443 pe_resource_t *resource = NULL;
444 const char *set_id;
445 const char *role;
446 const char *local_score;
447
448 CRM_CHECK(set != NULL, return EINVAL);
449
450 set_id = ID(set);
451 if (set_id == NULL) {
452 pcmk__config_err("Ignoring " XML_CONS_TAG_RSC_SET " without "
453 XML_ATTR_ID " in constraint '%s'",
454 pcmk__s(ID(location), "(missing ID)"));
455 return pcmk_rc_unpack_error;
456 }
457
458 role = crm_element_value(set, "role");
459 local_score = crm_element_value(set, XML_RULE_ATTR_SCORE);
460
461 for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
462 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
463
464 resource = pcmk__find_constraint_resource(data_set->resources,
465 ID(xml_rsc));
466 if (resource == NULL) {
467 pcmk__config_err("%s: No resource found for %s",
468 set_id, ID(xml_rsc));
469 return pcmk_rc_unpack_error;
470 }
471
472 unpack_rsc_location(location, resource, role, local_score, data_set,
473 NULL);
474 }
475
476 return pcmk_rc_ok;
477 }
478
479 void
480 pcmk__unpack_location(xmlNode *xml_obj, pe_working_set_t *data_set)
481 {
482 xmlNode *set = NULL;
483 bool any_sets = false;
484
485 xmlNode *orig_xml = NULL;
486 xmlNode *expanded_xml = NULL;
487
488 if (unpack_location_tags(xml_obj, &expanded_xml, data_set) != pcmk_rc_ok) {
489 return;
490 }
491
492 if (expanded_xml) {
493 orig_xml = xml_obj;
494 xml_obj = expanded_xml;
495 }
496
497 for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); set != NULL;
498 set = crm_next_same_xml(set)) {
499
500 any_sets = true;
501 set = expand_idref(set, data_set->input);
502 if ((set == NULL)
503 || (unpack_location_set(xml_obj, set, data_set) != pcmk_rc_ok)) {
504
505 if (expanded_xml) {
506 free_xml(expanded_xml);
507 }
508 return;
509 }
510 }
511
512 if (expanded_xml) {
513 free_xml(expanded_xml);
514 xml_obj = orig_xml;
515 }
516
517 if (!any_sets) {
518 unpack_simple_location(xml_obj, data_set);
519 }
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, pe_resource_t *rsc,
539 int node_weight, const char *discover_mode,
540 pe_node_t *node, pe_working_set_t *data_set)
541 {
542 pe__location_t *new_con = NULL;
543
544 if (id == NULL) {
545 pe_err("Invalid constraint: no ID specified");
546 return NULL;
547
548 } else if (rsc == NULL) {
549 pe_err("Invalid constraint %s: no resource specified", id);
550 return NULL;
551
552 } else if (node == NULL) {
553 CRM_CHECK(node_weight == 0, return NULL);
554 }
555
556 new_con = calloc(1, sizeof(pe__location_t));
557 if (new_con != NULL) {
558 new_con->id = strdup(id);
559 new_con->rsc_lh = rsc;
560 new_con->node_list_rh = NULL;
561 new_con->role_filter = RSC_ROLE_UNKNOWN;
562
563 if (pcmk__str_eq(discover_mode, "always",
564 pcmk__str_null_matches|pcmk__str_casei)) {
565 new_con->discover_mode = pe_discover_always;
566
567 } else if (pcmk__str_eq(discover_mode, "never", pcmk__str_casei)) {
568 new_con->discover_mode = pe_discover_never;
569
570 } else if (pcmk__str_eq(discover_mode, "exclusive", pcmk__str_casei)) {
571 new_con->discover_mode = pe_discover_exclusive;
572 rsc->exclusive_discover = TRUE;
573
574 } else {
575 pe_err("Invalid " XML_LOCATION_ATTR_DISCOVERY " value %s "
576 "in location constraint", discover_mode);
577 }
578
579 if (node != NULL) {
580 pe_node_t *copy = pe__copy_node(node);
581
582 copy->weight = node_weight;
583 new_con->node_list_rh = g_list_prepend(NULL, copy);
584 }
585
586 data_set->placement_constraints = g_list_prepend(data_set->placement_constraints,
587 new_con);
588 rsc->rsc_location = g_list_prepend(rsc->rsc_location, new_con);
589 }
590
591 return new_con;
592 }
593
594
595
596
597
598
599
600 void
601 pcmk__apply_locations(pe_working_set_t *data_set)
602 {
603 for (GList *iter = data_set->placement_constraints;
604 iter != NULL; iter = iter->next) {
605 pe__location_t *location = iter->data;
606
607 location->rsc_lh->cmds->apply_location(location->rsc_lh, location);
608 }
609 }
610
611
612
613
614
615
616
617
618
619
620
621 void
622 pcmk__apply_location(pe_resource_t *rsc, pe__location_t *location)
623 {
624 bool need_role = false;
625
626 CRM_CHECK((rsc != NULL) && (location != NULL), return);
627
628
629 need_role = (location->role_filter > RSC_ROLE_UNKNOWN);
630 if (need_role && (location->role_filter != rsc->next_role)) {
631 pe_rsc_trace(rsc,
632 "Not applying %s to %s because role will be %s not %s",
633 location->id, rsc->id, role2text(rsc->next_role),
634 role2text(location->role_filter));
635 return;
636 }
637
638 if (location->node_list_rh == NULL) {
639 pe_rsc_trace(rsc, "Not applying %s to %s because no nodes match",
640 location->id, rsc->id);
641 return;
642 }
643
644 pe_rsc_trace(rsc, "Applying %s%s%s to %s", location->id,
645 (need_role? " for role " : ""),
646 (need_role? role2text(location->role_filter) : ""), rsc->id);
647
648 for (GList *gIter = location->node_list_rh; gIter != NULL;
649 gIter = gIter->next) {
650
651 pe_node_t *node = (pe_node_t *) gIter->data;
652 pe_node_t *weighted_node = NULL;
653
654 weighted_node = (pe_node_t *) pe_hash_table_lookup(rsc->allowed_nodes,
655 node->details->id);
656 if (weighted_node == NULL) {
657 pe_rsc_trace(rsc, "* = %d on %s",
658 node->weight, pe__node_name(node));
659 weighted_node = pe__copy_node(node);
660 g_hash_table_insert(rsc->allowed_nodes,
661 (gpointer) weighted_node->details->id,
662 weighted_node);
663 } else {
664 pe_rsc_trace(rsc, "* + %d on %s",
665 node->weight, pe__node_name(node));
666 weighted_node->weight = pcmk__add_scores(weighted_node->weight,
667 node->weight);
668 }
669
670 if (weighted_node->rsc_discover_mode < location->discover_mode) {
671 if (location->discover_mode == pe_discover_exclusive) {
672 rsc->exclusive_discover = TRUE;
673 }
674
675 weighted_node->rsc_discover_mode = location->discover_mode;
676 }
677 }
678 }