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 now has weight %d",
170 pe__node_name(node), 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("%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)) {
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 *rsc_id = NULL;
372 const char *state = NULL;
373 pe_resource_t *rsc = NULL;
374 pe_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 crm_element_name(xml_obj));
385 return pcmk_rc_unpack_error;
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 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(data_set, 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, data_set)) {
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, 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 EINVAL);
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 pcmk__s(ID(location), "(missing ID)"));
456 return pcmk_rc_unpack_error;
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_unpack_error;
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 (id == NULL) {
546 pe_err("Invalid constraint: no ID specified");
547 return NULL;
548
549 } else if (rsc == NULL) {
550 pe_err("Invalid constraint %s: no resource specified", id);
551 return NULL;
552
553 } else if (node == NULL) {
554 CRM_CHECK(node_weight == 0, return NULL);
555 }
556
557 new_con = calloc(1, sizeof(pe__location_t));
558 if (new_con != NULL) {
559 new_con->id = strdup(id);
560 new_con->rsc_lh = rsc;
561 new_con->node_list_rh = NULL;
562 new_con->role_filter = RSC_ROLE_UNKNOWN;
563
564 if (pcmk__str_eq(discover_mode, "always",
565 pcmk__str_null_matches|pcmk__str_casei)) {
566 new_con->discover_mode = pe_discover_always;
567
568 } else if (pcmk__str_eq(discover_mode, "never", pcmk__str_casei)) {
569 new_con->discover_mode = pe_discover_never;
570
571 } else if (pcmk__str_eq(discover_mode, "exclusive", pcmk__str_casei)) {
572 new_con->discover_mode = pe_discover_exclusive;
573 rsc->exclusive_discover = TRUE;
574
575 } else {
576 pe_err("Invalid " XML_LOCATION_ATTR_DISCOVERY " value %s "
577 "in location constraint", discover_mode);
578 }
579
580 if (node != NULL) {
581 pe_node_t *copy = pe__copy_node(node);
582
583 copy->weight = node_weight;
584 new_con->node_list_rh = g_list_prepend(NULL, copy);
585 }
586
587 data_set->placement_constraints = g_list_prepend(data_set->placement_constraints,
588 new_con);
589 rsc->rsc_location = g_list_prepend(rsc->rsc_location, new_con);
590 }
591
592 return new_con;
593 }
594
595
596
597
598
599
600
601 void
602 pcmk__apply_locations(pe_working_set_t *data_set)
603 {
604 for (GList *iter = data_set->placement_constraints;
605 iter != NULL; iter = iter->next) {
606 pe__location_t *location = iter->data;
607
608 location->rsc_lh->cmds->apply_location(location->rsc_lh, location);
609 }
610 }
611
612
613
614
615
616
617
618
619
620
621
622 void
623 pcmk__apply_location(pe_resource_t *rsc, pe__location_t *location)
624 {
625 bool need_role = false;
626
627 CRM_CHECK((rsc != NULL) && (location != NULL), return);
628
629
630 need_role = (location->role_filter > RSC_ROLE_UNKNOWN);
631 if (need_role && (location->role_filter != rsc->next_role)) {
632 pe_rsc_trace(rsc,
633 "Not applying %s to %s because role will be %s not %s",
634 location->id, rsc->id, role2text(rsc->next_role),
635 role2text(location->role_filter));
636 return;
637 }
638
639 if (location->node_list_rh == NULL) {
640 pe_rsc_trace(rsc, "Not applying %s to %s because no nodes match",
641 location->id, rsc->id);
642 return;
643 }
644
645 pe_rsc_trace(rsc, "Applying %s%s%s to %s", location->id,
646 (need_role? " for role " : ""),
647 (need_role? role2text(location->role_filter) : ""), rsc->id);
648
649 for (GList *gIter = location->node_list_rh; gIter != NULL;
650 gIter = gIter->next) {
651
652 pe_node_t *node = (pe_node_t *) gIter->data;
653 pe_node_t *weighted_node = NULL;
654
655 weighted_node = (pe_node_t *) pe_hash_table_lookup(rsc->allowed_nodes,
656 node->details->id);
657 if (weighted_node == NULL) {
658 pe_rsc_trace(rsc, "* = %d on %s",
659 node->weight, pe__node_name(node));
660 weighted_node = pe__copy_node(node);
661 g_hash_table_insert(rsc->allowed_nodes,
662 (gpointer) weighted_node->details->id,
663 weighted_node);
664 } else {
665 pe_rsc_trace(rsc, "* + %d on %s",
666 node->weight, pe__node_name(node));
667 weighted_node->weight = pcmk__add_scores(weighted_node->weight,
668 node->weight);
669 }
670
671 if (weighted_node->rsc_discover_mode < location->discover_mode) {
672 if (location->discover_mode == pe_discover_exclusive) {
673 rsc->exclusive_discover = TRUE;
674 }
675
676 weighted_node->rsc_discover_mode = location->discover_mode;
677 }
678 }
679 }