This source file includes following definitions.
- pe_evaluate_rules
- pe_test_rule
- pe_test_expression
- find_expression_type
- pe_test_role_expression
- pe_test_attr_expression
- phase_of_the_moon
- check_one
- check_passes
- pe_cron_range_satisfied
- pe_parse_xml_duration
- pe_test_date_expression
- crm_time_set_if_earlier
- pe_eval_date_expression
- sort_pairs
- populate_hash
- get_versioned_rule
- add_versioned_attributes
- unpack_attr_set
- unpack_versioned_attr_set
- make_pairs
- unpack_nvpair_blocks
- pe_eval_nvpairs
- pe_unpack_nvpairs
- pe_eval_versioned_attributes
- pe_expand_re_matches
- pe_unpack_versioned_parameters
- pe_eval_rules
- pe_eval_expr
- pe_eval_subexpr
- compare_attr_expr_vals
- accept_attr_expr
- pe__eval_attr_expr
- pe__eval_date_expr
- pe__eval_op_expr
- pe__eval_role_expr
- pe__eval_rsc_expr
- test_ruleset
- test_rule
- pe_test_rule_re
- pe_test_rule_full
- test_expression
- pe_test_expression_re
- pe_test_expression_full
- unpack_instance_attributes
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11 #include <crm/crm.h>
12 #include <crm/msg_xml.h>
13 #include <crm/common/xml.h>
14 #include <crm/common/xml_internal.h>
15
16 #include <glib.h>
17
18 #include <crm/pengine/rules.h>
19 #include <crm/pengine/rules_internal.h>
20 #include <crm/pengine/internal.h>
21
22 #include <sys/types.h>
23 #include <regex.h>
24 #include <ctype.h>
25
26 CRM_TRACE_INIT_DATA(pe_rules);
27
28
29
30
31
32
33
34
35
36
37
38 gboolean
39 pe_evaluate_rules(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now,
40 crm_time_t *next_change)
41 {
42 pe_rule_eval_data_t rule_data = {
43 .node_hash = node_hash,
44 .role = RSC_ROLE_UNKNOWN,
45 .now = now,
46 .match_data = NULL,
47 .rsc_data = NULL,
48 .op_data = NULL
49 };
50
51 return pe_eval_rules(ruleset, &rule_data, next_change);
52 }
53
54 gboolean
55 pe_test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role,
56 crm_time_t *now, crm_time_t *next_change,
57 pe_match_data_t *match_data)
58 {
59 pe_rule_eval_data_t rule_data = {
60 .node_hash = node_hash,
61 .role = role,
62 .now = now,
63 .match_data = match_data,
64 .rsc_data = NULL,
65 .op_data = NULL
66 };
67
68 return pe_eval_expr(rule, &rule_data, next_change);
69 }
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87 gboolean
88 pe_test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role,
89 crm_time_t *now, crm_time_t *next_change,
90 pe_match_data_t *match_data)
91 {
92 pe_rule_eval_data_t rule_data = {
93 .node_hash = node_hash,
94 .role = role,
95 .now = now,
96 .match_data = match_data,
97 .rsc_data = NULL,
98 .op_data = NULL
99 };
100
101 return pe_eval_subexpr(expr, &rule_data, next_change);
102 }
103
104 enum expression_type
105 find_expression_type(xmlNode * expr)
106 {
107 const char *tag = NULL;
108 const char *attr = NULL;
109
110 attr = crm_element_value(expr, XML_EXPR_ATTR_ATTRIBUTE);
111 tag = crm_element_name(expr);
112
113 if (pcmk__str_eq(tag, "date_expression", pcmk__str_casei)) {
114 return time_expr;
115
116 } else if (pcmk__str_eq(tag, "rsc_expression", pcmk__str_casei)) {
117 return rsc_expr;
118
119 } else if (pcmk__str_eq(tag, "op_expression", pcmk__str_casei)) {
120 return op_expr;
121
122 } else if (pcmk__str_eq(tag, XML_TAG_RULE, pcmk__str_casei)) {
123 return nested_rule;
124
125 } else if (!pcmk__str_eq(tag, "expression", pcmk__str_casei)) {
126 return not_expr;
127
128 } else if (pcmk__strcase_any_of(attr, CRM_ATTR_UNAME, CRM_ATTR_KIND, CRM_ATTR_ID, NULL)) {
129 return loc_expr;
130
131 } else if (pcmk__str_eq(attr, CRM_ATTR_ROLE, pcmk__str_casei)) {
132 return role_expr;
133
134 #if ENABLE_VERSIONED_ATTRS
135 } else if (pcmk__str_eq(attr, CRM_ATTR_RA_VERSION, pcmk__str_casei)) {
136 return version_expr;
137 #endif
138 }
139
140 return attr_expr;
141 }
142
143 gboolean
144 pe_test_role_expression(xmlNode *expr, enum rsc_role_e role, crm_time_t *now)
145 {
146 pe_rule_eval_data_t rule_data = {
147 .node_hash = NULL,
148 .role = role,
149 .now = now,
150 .match_data = NULL,
151 .rsc_data = NULL,
152 .op_data = NULL
153 };
154
155 return pe__eval_role_expr(expr, &rule_data);
156 }
157
158 gboolean
159 pe_test_attr_expression(xmlNode *expr, GHashTable *hash, crm_time_t *now,
160 pe_match_data_t *match_data)
161 {
162 pe_rule_eval_data_t rule_data = {
163 .node_hash = hash,
164 .role = RSC_ROLE_UNKNOWN,
165 .now = now,
166 .match_data = match_data,
167 .rsc_data = NULL,
168 .op_data = NULL
169 };
170
171 return pe__eval_attr_expr(expr, &rule_data);
172 }
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191 static int
192 phase_of_the_moon(crm_time_t * now)
193 {
194 uint32_t epact, diy, goldn;
195 uint32_t y;
196
197 crm_time_get_ordinal(now, &y, &diy);
198
199 goldn = (y % 19) + 1;
200 epact = (11 * goldn + 18) % 30;
201 if ((epact == 25 && goldn > 11) || epact == 24)
202 epact++;
203
204 return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7);
205 }
206
207 static int
208 check_one(xmlNode *cron_spec, const char *xml_field, uint32_t time_field) {
209 int rc = pcmk_rc_undetermined;
210 const char *value = crm_element_value(cron_spec, xml_field);
211 long long low, high;
212
213 if (value == NULL) {
214
215 goto bail;
216 }
217
218 if (pcmk__parse_ll_range(value, &low, &high) == pcmk_rc_unknown_format) {
219 goto bail;
220 } else if (low == high) {
221
222 if (time_field < low) {
223 rc = pcmk_rc_before_range;
224 } else if (time_field > high) {
225 rc = pcmk_rc_after_range;
226 } else {
227 rc = pcmk_rc_within_range;
228 }
229 } else if (low != -1 && high != -1) {
230
231 if (time_field < low) {
232 rc = pcmk_rc_before_range;
233 } else if (time_field > high) {
234 rc = pcmk_rc_after_range;
235 } else {
236 rc = pcmk_rc_within_range;
237 }
238 } else if (low == -1) {
239
240 rc = time_field <= high ? pcmk_rc_within_range : pcmk_rc_after_range;
241 } else if (high == -1) {
242
243 rc = time_field >= low ? pcmk_rc_within_range : pcmk_rc_before_range;
244 }
245
246 bail:
247 if (rc == pcmk_rc_within_range) {
248 crm_debug("Condition '%s' in %s: passed", value, xml_field);
249 } else {
250 crm_debug("Condition '%s' in %s: failed", value, xml_field);
251 }
252
253 return rc;
254 }
255
256 static gboolean
257 check_passes(int rc) {
258
259
260
261
262
263 return rc == pcmk_rc_within_range || rc == pcmk_rc_undetermined;
264 }
265
266 #define CHECK_ONE(spec, name, var) do { \
267 int subpart_rc = check_one(spec, name, var); \
268 if (check_passes(subpart_rc) == FALSE) { \
269 return subpart_rc; \
270 } \
271 } while (0)
272
273 int
274 pe_cron_range_satisfied(crm_time_t * now, xmlNode * cron_spec)
275 {
276 uint32_t h, m, s, y, d, w;
277
278 CRM_CHECK(now != NULL, return pcmk_rc_op_unsatisfied);
279
280 crm_time_get_gregorian(now, &y, &m, &d);
281 CHECK_ONE(cron_spec, "years", y);
282 CHECK_ONE(cron_spec, "months", m);
283 CHECK_ONE(cron_spec, "monthdays", d);
284
285 crm_time_get_timeofday(now, &h, &m, &s);
286 CHECK_ONE(cron_spec, "hours", h);
287 CHECK_ONE(cron_spec, "minutes", m);
288 CHECK_ONE(cron_spec, "seconds", s);
289
290 crm_time_get_ordinal(now, &y, &d);
291 CHECK_ONE(cron_spec, "yeardays", d);
292
293 crm_time_get_isoweek(now, &y, &w, &d);
294 CHECK_ONE(cron_spec, "weekyears", y);
295 CHECK_ONE(cron_spec, "weeks", w);
296 CHECK_ONE(cron_spec, "weekdays", d);
297
298 CHECK_ONE(cron_spec, "moon", phase_of_the_moon(now));
299
300
301
302
303
304 return pcmk_rc_ok;
305 }
306
307 #define update_field(xml_field, time_fn) \
308 value = crm_element_value(duration_spec, xml_field); \
309 if(value != NULL) { \
310 int value_i = crm_parse_int(value, "0"); \
311 time_fn(end, value_i); \
312 }
313
314 crm_time_t *
315 pe_parse_xml_duration(crm_time_t * start, xmlNode * duration_spec)
316 {
317 crm_time_t *end = NULL;
318 const char *value = NULL;
319
320 end = crm_time_new(NULL);
321 crm_time_set(end, start);
322
323 update_field("years", crm_time_add_years);
324 update_field("months", crm_time_add_months);
325 update_field("weeks", crm_time_add_weeks);
326 update_field("days", crm_time_add_days);
327 update_field("hours", crm_time_add_hours);
328 update_field("minutes", crm_time_add_minutes);
329 update_field("seconds", crm_time_add_seconds);
330
331 return end;
332 }
333
334
335
336
337
338
339
340
341
342
343
344 gboolean
345 pe_test_date_expression(xmlNode *expr, crm_time_t *now, crm_time_t *next_change)
346 {
347 pe_rule_eval_data_t rule_data = {
348 .node_hash = NULL,
349 .role = RSC_ROLE_UNKNOWN,
350 .now = now,
351 .match_data = NULL,
352 .rsc_data = NULL,
353 .op_data = NULL
354 };
355
356 switch (pe__eval_date_expr(expr, &rule_data, next_change)) {
357 case pcmk_rc_within_range:
358 case pcmk_rc_ok:
359 return TRUE;
360
361 default:
362 return FALSE;
363 }
364 }
365
366
367 static void
368 crm_time_set_if_earlier(crm_time_t *next_change, crm_time_t *t)
369 {
370 if ((next_change != NULL) && (t != NULL)) {
371 if (!crm_time_is_defined(next_change)
372 || (crm_time_compare(t, next_change) < 0)) {
373 crm_time_set(next_change, t);
374 }
375 }
376 }
377
378
379
380
381
382
383
384
385
386
387
388 int
389 pe_eval_date_expression(xmlNode *expr, crm_time_t *now, crm_time_t *next_change)
390 {
391 pe_rule_eval_data_t rule_data = {
392 .node_hash = NULL,
393 .role = RSC_ROLE_UNKNOWN,
394 .now = now,
395 .match_data = NULL,
396 .rsc_data = NULL,
397 .op_data = NULL
398 };
399
400 return pe__eval_date_expr(expr, &rule_data, next_change);
401 }
402
403
404 typedef struct sorted_set_s {
405 int score;
406 const char *name;
407 const char *special_name;
408 xmlNode *attr_set;
409 } sorted_set_t;
410
411 static gint
412 sort_pairs(gconstpointer a, gconstpointer b)
413 {
414 const sorted_set_t *pair_a = a;
415 const sorted_set_t *pair_b = b;
416
417 if (a == NULL && b == NULL) {
418 return 0;
419 } else if (a == NULL) {
420 return 1;
421 } else if (b == NULL) {
422 return -1;
423 }
424
425 if (pcmk__str_eq(pair_a->name, pair_a->special_name, pcmk__str_casei)) {
426 return -1;
427
428 } else if (pcmk__str_eq(pair_b->name, pair_a->special_name, pcmk__str_casei)) {
429 return 1;
430 }
431
432 if (pair_a->score < pair_b->score) {
433 return 1;
434 } else if (pair_a->score > pair_b->score) {
435 return -1;
436 }
437 return 0;
438 }
439
440 static void
441 populate_hash(xmlNode * nvpair_list, GHashTable * hash, gboolean overwrite, xmlNode * top)
442 {
443 const char *name = NULL;
444 const char *value = NULL;
445 const char *old_value = NULL;
446 xmlNode *list = nvpair_list;
447 xmlNode *an_attr = NULL;
448
449 name = crm_element_name(list->children);
450 if (pcmk__str_eq(XML_TAG_ATTRS, name, pcmk__str_casei)) {
451 list = list->children;
452 }
453
454 for (an_attr = pcmk__xe_first_child(list); an_attr != NULL;
455 an_attr = pcmk__xe_next(an_attr)) {
456
457 if (pcmk__str_eq((const char *)an_attr->name, XML_CIB_TAG_NVPAIR, pcmk__str_none)) {
458 xmlNode *ref_nvpair = expand_idref(an_attr, top);
459
460 name = crm_element_value(an_attr, XML_NVPAIR_ATTR_NAME);
461 if (name == NULL) {
462 name = crm_element_value(ref_nvpair, XML_NVPAIR_ATTR_NAME);
463 }
464
465 value = crm_element_value(an_attr, XML_NVPAIR_ATTR_VALUE);
466 if (value == NULL) {
467 value = crm_element_value(ref_nvpair, XML_NVPAIR_ATTR_VALUE);
468 }
469
470 if (name == NULL || value == NULL) {
471 continue;
472 }
473
474 old_value = g_hash_table_lookup(hash, name);
475
476 if (pcmk__str_eq(value, "#default", pcmk__str_casei)) {
477 if (old_value) {
478 crm_trace("Letting %s default (removing explicit value \"%s\")",
479 name, value);
480 g_hash_table_remove(hash, name);
481 }
482 continue;
483
484 } else if (old_value == NULL) {
485 crm_trace("Setting %s=\"%s\"", name, value);
486 g_hash_table_insert(hash, strdup(name), strdup(value));
487
488 } else if (overwrite) {
489 crm_trace("Setting %s=\"%s\" (overwriting old value \"%s\")",
490 name, value, old_value);
491 g_hash_table_replace(hash, strdup(name), strdup(value));
492 }
493 }
494 }
495 }
496
497 #if ENABLE_VERSIONED_ATTRS
498 static xmlNode*
499 get_versioned_rule(xmlNode * attr_set)
500 {
501 xmlNode * rule = NULL;
502 xmlNode * expr = NULL;
503
504 for (rule = pcmk__xe_first_child(attr_set); rule != NULL;
505 rule = pcmk__xe_next(rule)) {
506
507 if (pcmk__str_eq((const char *)rule->name, XML_TAG_RULE,
508 pcmk__str_none)) {
509 for (expr = pcmk__xe_first_child(rule); expr != NULL;
510 expr = pcmk__xe_next(expr)) {
511
512 if (find_expression_type(expr) == version_expr) {
513 return rule;
514 }
515 }
516 }
517 }
518
519 return NULL;
520 }
521
522 static void
523 add_versioned_attributes(xmlNode * attr_set, xmlNode * versioned_attrs)
524 {
525 xmlNode *attr_set_copy = NULL;
526 xmlNode *rule = NULL;
527 xmlNode *expr = NULL;
528
529 if (!attr_set || !versioned_attrs) {
530 return;
531 }
532
533 attr_set_copy = copy_xml(attr_set);
534
535 rule = get_versioned_rule(attr_set_copy);
536 if (!rule) {
537 free_xml(attr_set_copy);
538 return;
539 }
540
541 expr = pcmk__xe_first_child(rule);
542 while (expr != NULL) {
543 if (find_expression_type(expr) != version_expr) {
544 xmlNode *node = expr;
545
546 expr = pcmk__xe_next(expr);
547 free_xml(node);
548 } else {
549 expr = pcmk__xe_next(expr);
550 }
551 }
552
553 add_node_nocopy(versioned_attrs, NULL, attr_set_copy);
554 }
555 #endif
556
557 typedef struct unpack_data_s {
558 gboolean overwrite;
559 void *hash;
560 crm_time_t *next_change;
561 pe_rule_eval_data_t *rule_data;
562 xmlNode *top;
563 } unpack_data_t;
564
565 static void
566 unpack_attr_set(gpointer data, gpointer user_data)
567 {
568 sorted_set_t *pair = data;
569 unpack_data_t *unpack_data = user_data;
570
571 if (!pe_eval_rules(pair->attr_set, unpack_data->rule_data,
572 unpack_data->next_change)) {
573 return;
574 }
575
576 #if ENABLE_VERSIONED_ATTRS
577 if (get_versioned_rule(pair->attr_set) && !(unpack_data->rule_data->node_hash &&
578 g_hash_table_lookup_extended(unpack_data->rule_data->node_hash,
579 CRM_ATTR_RA_VERSION, NULL, NULL))) {
580
581 return;
582 }
583 #endif
584
585 crm_trace("Adding attributes from %s (score %d) %s overwrite",
586 pair->name, pair->score,
587 (unpack_data->overwrite? "with" : "without"));
588 populate_hash(pair->attr_set, unpack_data->hash, unpack_data->overwrite, unpack_data->top);
589 }
590
591 #if ENABLE_VERSIONED_ATTRS
592 static void
593 unpack_versioned_attr_set(gpointer data, gpointer user_data)
594 {
595 sorted_set_t *pair = data;
596 unpack_data_t *unpack_data = user_data;
597
598 if (pe_eval_rules(pair->attr_set, unpack_data->rule_data,
599 unpack_data->next_change)) {
600 add_versioned_attributes(pair->attr_set, unpack_data->hash);
601 }
602 }
603 #endif
604
605
606
607
608
609
610
611
612
613
614
615
616 static GList *
617 make_pairs(xmlNode *top, xmlNode *xml_obj, const char *set_name,
618 const char *always_first)
619 {
620 GListPtr unsorted = NULL;
621 const char *score = NULL;
622 sorted_set_t *pair = NULL;
623 xmlNode *attr_set = NULL;
624
625 if (xml_obj == NULL) {
626 return NULL;
627 }
628 for (attr_set = pcmk__xe_first_child(xml_obj); attr_set != NULL;
629 attr_set = pcmk__xe_next(attr_set)) {
630
631
632 if (pcmk__str_eq(set_name, (const char *)attr_set->name, pcmk__str_null_matches)) {
633 pair = NULL;
634 attr_set = expand_idref(attr_set, top);
635 if (attr_set == NULL) {
636 continue;
637 }
638
639 pair = calloc(1, sizeof(sorted_set_t));
640 pair->name = ID(attr_set);
641 pair->special_name = always_first;
642 pair->attr_set = attr_set;
643
644 score = crm_element_value(attr_set, XML_RULE_ATTR_SCORE);
645 pair->score = char2score(score);
646
647 unsorted = g_list_prepend(unsorted, pair);
648 }
649 }
650 return g_list_sort(unsorted, sort_pairs);
651 }
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667 static void
668 unpack_nvpair_blocks(xmlNode *top, xmlNode *xml_obj, const char *set_name,
669 void *hash, const char *always_first, gboolean overwrite,
670 pe_rule_eval_data_t *rule_data, crm_time_t *next_change,
671 GFunc unpack_func)
672 {
673 GList *pairs = make_pairs(top, xml_obj, set_name, always_first);
674
675 if (pairs) {
676 unpack_data_t data = {
677 .hash = hash,
678 .overwrite = overwrite,
679 .next_change = next_change,
680 .top = top,
681 .rule_data = rule_data
682 };
683
684 g_list_foreach(pairs, unpack_func, &data);
685 g_list_free_full(pairs, free);
686 }
687 }
688
689 void
690 pe_eval_nvpairs(xmlNode *top, xmlNode *xml_obj, const char *set_name,
691 pe_rule_eval_data_t *rule_data, GHashTable *hash,
692 const char *always_first, gboolean overwrite,
693 crm_time_t *next_change)
694 {
695 unpack_nvpair_blocks(top, xml_obj, set_name, hash, always_first,
696 overwrite, rule_data, next_change, unpack_attr_set);
697 }
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712 void
713 pe_unpack_nvpairs(xmlNode *top, xmlNode *xml_obj, const char *set_name,
714 GHashTable *node_hash, GHashTable *hash,
715 const char *always_first, gboolean overwrite,
716 crm_time_t *now, crm_time_t *next_change)
717 {
718 pe_rule_eval_data_t rule_data = {
719 .node_hash = node_hash,
720 .role = RSC_ROLE_UNKNOWN,
721 .now = now,
722 .match_data = NULL,
723 .rsc_data = NULL,
724 .op_data = NULL
725 };
726
727 pe_eval_nvpairs(top, xml_obj, set_name, &rule_data, hash,
728 always_first, overwrite, next_change);
729 }
730
731 #if ENABLE_VERSIONED_ATTRS
732 void
733 pe_eval_versioned_attributes(xmlNode *top, xmlNode *xml_obj, const char *set_name,
734 pe_rule_eval_data_t *rule_data, xmlNode *hash,
735 crm_time_t *next_change)
736 {
737 unpack_nvpair_blocks(top, xml_obj, set_name, hash, NULL, FALSE, rule_data,
738 next_change, unpack_versioned_attr_set);
739 }
740 #endif
741
742 char *
743 pe_expand_re_matches(const char *string, pe_re_match_data_t *match_data)
744 {
745 size_t len = 0;
746 int i;
747 const char *p, *last_match_index;
748 char *p_dst, *result = NULL;
749
750 if (pcmk__str_empty(string) || !match_data) {
751 return NULL;
752 }
753
754 p = last_match_index = string;
755
756 while (*p) {
757 if (*p == '%' && *(p + 1) && isdigit(*(p + 1))) {
758 i = *(p + 1) - '0';
759 if (match_data->nregs >= i && match_data->pmatch[i].rm_so != -1 &&
760 match_data->pmatch[i].rm_eo > match_data->pmatch[i].rm_so) {
761 len += p - last_match_index + (match_data->pmatch[i].rm_eo - match_data->pmatch[i].rm_so);
762 last_match_index = p + 2;
763 }
764 p++;
765 }
766 p++;
767 }
768 len += p - last_match_index + 1;
769
770
771 if (len - 1 <= 0) {
772 return NULL;
773 }
774
775 p_dst = result = calloc(1, len);
776 p = string;
777
778 while (*p) {
779 if (*p == '%' && *(p + 1) && isdigit(*(p + 1))) {
780 i = *(p + 1) - '0';
781 if (match_data->nregs >= i && match_data->pmatch[i].rm_so != -1 &&
782 match_data->pmatch[i].rm_eo > match_data->pmatch[i].rm_so) {
783
784 int match_len = match_data->pmatch[i].rm_eo - match_data->pmatch[i].rm_so;
785 memcpy(p_dst, match_data->string + match_data->pmatch[i].rm_so, match_len);
786 p_dst += match_len;
787 }
788 p++;
789 } else {
790 *(p_dst) = *(p);
791 p_dst++;
792 }
793 p++;
794 }
795
796 return result;
797 }
798
799 #if ENABLE_VERSIONED_ATTRS
800 GHashTable*
801 pe_unpack_versioned_parameters(xmlNode *versioned_params, const char *ra_version)
802 {
803 GHashTable *hash = crm_str_table_new();
804
805 if (versioned_params && ra_version) {
806 GHashTable *node_hash = crm_str_table_new();
807 xmlNode *attr_set = pcmk__xe_first_child(versioned_params);
808
809 if (attr_set) {
810 g_hash_table_insert(node_hash, strdup(CRM_ATTR_RA_VERSION),
811 strdup(ra_version));
812 pe_unpack_nvpairs(NULL, versioned_params,
813 crm_element_name(attr_set), node_hash, hash, NULL,
814 FALSE, NULL, NULL);
815 }
816
817 g_hash_table_destroy(node_hash);
818 }
819
820 return hash;
821 }
822 #endif
823
824 gboolean
825 pe_eval_rules(xmlNode *ruleset, pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
826 {
827
828 gboolean ruleset_default = TRUE;
829
830 for (xmlNode *rule = first_named_child(ruleset, XML_TAG_RULE);
831 rule != NULL; rule = crm_next_same_xml(rule)) {
832
833 ruleset_default = FALSE;
834 if (pe_eval_expr(rule, rule_data, next_change)) {
835
836
837
838
839
840
841 return TRUE;
842 }
843 }
844
845 return ruleset_default;
846 }
847
848 gboolean
849 pe_eval_expr(xmlNode *rule, pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
850 {
851 xmlNode *expr = NULL;
852 gboolean test = TRUE;
853 gboolean empty = TRUE;
854 gboolean passed = TRUE;
855 gboolean do_and = TRUE;
856 const char *value = NULL;
857
858 rule = expand_idref(rule, NULL);
859 value = crm_element_value(rule, XML_RULE_ATTR_BOOLEAN_OP);
860 if (pcmk__str_eq(value, "or", pcmk__str_casei)) {
861 do_and = FALSE;
862 passed = FALSE;
863 }
864
865 crm_trace("Testing rule %s", ID(rule));
866 for (expr = pcmk__xe_first_child(rule); expr != NULL;
867 expr = pcmk__xe_next(expr)) {
868
869 test = pe_eval_subexpr(expr, rule_data, next_change);
870 empty = FALSE;
871
872 if (test && do_and == FALSE) {
873 crm_trace("Expression %s/%s passed", ID(rule), ID(expr));
874 return TRUE;
875
876 } else if (test == FALSE && do_and) {
877 crm_trace("Expression %s/%s failed", ID(rule), ID(expr));
878 return FALSE;
879 }
880 }
881
882 if (empty) {
883 crm_err("Invalid Rule %s: rules must contain at least one expression", ID(rule));
884 }
885
886 crm_trace("Rule %s %s", ID(rule), passed ? "passed" : "failed");
887 return passed;
888 }
889
890 gboolean
891 pe_eval_subexpr(xmlNode *expr, pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
892 {
893 gboolean accept = FALSE;
894 const char *uname = NULL;
895
896 switch (find_expression_type(expr)) {
897 case nested_rule:
898 accept = pe_eval_expr(expr, rule_data, next_change);
899 break;
900 case attr_expr:
901 case loc_expr:
902
903
904
905 if (rule_data->node_hash != NULL) {
906 accept = pe__eval_attr_expr(expr, rule_data);
907 }
908 break;
909
910 case time_expr:
911 accept = pe_test_date_expression(expr, rule_data->now, next_change);
912 break;
913
914 case role_expr:
915 accept = pe__eval_role_expr(expr, rule_data);
916 break;
917
918 case rsc_expr:
919 accept = pe__eval_rsc_expr(expr, rule_data);
920 break;
921
922 case op_expr:
923 accept = pe__eval_op_expr(expr, rule_data);
924 break;
925
926 #if ENABLE_VERSIONED_ATTRS
927 case version_expr:
928 if (rule_data->node_hash &&
929 g_hash_table_lookup_extended(rule_data->node_hash,
930 CRM_ATTR_RA_VERSION, NULL, NULL)) {
931 accept = pe__eval_attr_expr(expr, rule_data);
932 } else {
933
934 accept = TRUE;
935 }
936 break;
937 #endif
938
939 default:
940 CRM_CHECK(FALSE , return FALSE);
941 accept = FALSE;
942 }
943 if (rule_data->node_hash) {
944 uname = g_hash_table_lookup(rule_data->node_hash, CRM_ATTR_UNAME);
945 }
946
947 crm_trace("Expression %s %s on %s",
948 ID(expr), accept ? "passed" : "failed", uname ? uname : "all nodes");
949 return accept;
950 }
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967 static int
968 compare_attr_expr_vals(const char *l_val, const char *r_val, const char *type,
969 const char *op)
970 {
971 int cmp = 0;
972
973 if (l_val != NULL && r_val != NULL) {
974 if (type == NULL) {
975 if (pcmk__strcase_any_of(op, "lt", "lte", "gt", "gte", NULL)) {
976 if (pcmk__char_in_any_str('.', l_val, r_val, NULL)) {
977 type = "number";
978 } else {
979 type = "integer";
980 }
981
982 } else {
983 type = "string";
984 }
985 crm_trace("Defaulting to %s based comparison for '%s' op", type, op);
986 }
987
988 if (pcmk__str_eq(type, "string", pcmk__str_casei)) {
989 cmp = strcasecmp(l_val, r_val);
990
991 } else if (pcmk__str_eq(type, "integer", pcmk__str_casei)) {
992 long long l_val_num = crm_parse_ll(l_val, NULL);
993 int rc1 = errno;
994
995 long long r_val_num = crm_parse_ll(r_val, NULL);
996 int rc2 = errno;
997
998 if (rc1 == 0 && rc2 == 0) {
999 if (l_val_num < r_val_num) {
1000 cmp = -1;
1001 } else if (l_val_num > r_val_num) {
1002 cmp = 1;
1003 } else {
1004 cmp = 0;
1005 }
1006
1007 } else {
1008 crm_debug("Integer parse error. Comparing %s and %s as strings",
1009 l_val, r_val);
1010 cmp = compare_attr_expr_vals(l_val, r_val, "string", op);
1011 }
1012
1013 } else if (pcmk__str_eq(type, "number", pcmk__str_casei)) {
1014 double l_val_num;
1015 double r_val_num;
1016
1017 int rc1 = pcmk__scan_double(l_val, &l_val_num, NULL, NULL);
1018 int rc2 = pcmk__scan_double(r_val, &r_val_num, NULL, NULL);
1019
1020 if (rc1 == pcmk_rc_ok && rc2 == pcmk_rc_ok) {
1021 if (l_val_num < r_val_num) {
1022 cmp = -1;
1023 } else if (l_val_num > r_val_num) {
1024 cmp = 1;
1025 } else {
1026 cmp = 0;
1027 }
1028
1029 } else {
1030 crm_debug("Floating-point parse error. Comparing %s and %s as "
1031 "strings", l_val, r_val);
1032 cmp = compare_attr_expr_vals(l_val, r_val, "string", op);
1033 }
1034
1035 } else if (pcmk__str_eq(type, "version", pcmk__str_casei)) {
1036 cmp = compare_version(l_val, r_val);
1037
1038 }
1039
1040 } else if (l_val == NULL && r_val == NULL) {
1041 cmp = 0;
1042 } else if (r_val == NULL) {
1043 cmp = 1;
1044 } else {
1045 cmp = -1;
1046 }
1047
1048 return cmp;
1049 }
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065 static bool
1066 accept_attr_expr(const char *l_val, const char *r_val, const char *type,
1067 const char *op)
1068 {
1069 int cmp;
1070
1071 if (pcmk__str_eq(op, "defined", pcmk__str_casei)) {
1072 return (l_val != NULL);
1073
1074 } else if (pcmk__str_eq(op, "not_defined", pcmk__str_casei)) {
1075 return (l_val == NULL);
1076
1077 }
1078
1079 cmp = compare_attr_expr_vals(l_val, r_val, type, op);
1080
1081 if (pcmk__str_eq(op, "eq", pcmk__str_casei)) {
1082 return (cmp == 0);
1083
1084 } else if (pcmk__str_eq(op, "ne", pcmk__str_casei)) {
1085 return (cmp != 0);
1086
1087 } else if (l_val == NULL || r_val == NULL) {
1088
1089 return false;
1090
1091 } else if (pcmk__str_eq(op, "lt", pcmk__str_casei)) {
1092 return (cmp < 0);
1093
1094 } else if (pcmk__str_eq(op, "lte", pcmk__str_casei)) {
1095 return (cmp <= 0);
1096
1097 } else if (pcmk__str_eq(op, "gt", pcmk__str_casei)) {
1098 return (cmp > 0);
1099
1100 } else if (pcmk__str_eq(op, "gte", pcmk__str_casei)) {
1101 return (cmp >= 0);
1102 }
1103
1104 return false;
1105 }
1106
1107 gboolean
1108 pe__eval_attr_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data)
1109 {
1110 gboolean attr_allocated = FALSE;
1111 const char *h_val = NULL;
1112 GHashTable *table = NULL;
1113
1114 const char *op = NULL;
1115 const char *type = NULL;
1116 const char *attr = NULL;
1117 const char *value = NULL;
1118 const char *value_source = NULL;
1119
1120 attr = crm_element_value(expr, XML_EXPR_ATTR_ATTRIBUTE);
1121 op = crm_element_value(expr, XML_EXPR_ATTR_OPERATION);
1122 value = crm_element_value(expr, XML_EXPR_ATTR_VALUE);
1123 type = crm_element_value(expr, XML_EXPR_ATTR_TYPE);
1124 value_source = crm_element_value(expr, XML_EXPR_ATTR_VALUE_SOURCE);
1125
1126 if (attr == NULL || op == NULL) {
1127 pe_err("Invalid attribute or operation in expression"
1128 " (\'%s\' \'%s\' \'%s\')", crm_str(attr), crm_str(op), crm_str(value));
1129 return FALSE;
1130 }
1131
1132 if (rule_data->match_data) {
1133 if (rule_data->match_data->re) {
1134 char *resolved_attr = pe_expand_re_matches(attr, rule_data->match_data->re);
1135
1136 if (resolved_attr) {
1137 attr = (const char *) resolved_attr;
1138 attr_allocated = TRUE;
1139 }
1140 }
1141
1142 if (pcmk__str_eq(value_source, "param", pcmk__str_casei)) {
1143 table = rule_data->match_data->params;
1144 } else if (pcmk__str_eq(value_source, "meta", pcmk__str_casei)) {
1145 table = rule_data->match_data->meta;
1146 }
1147 }
1148
1149 if (table) {
1150 const char *param_name = value;
1151 const char *param_value = NULL;
1152
1153 if (param_name && param_name[0]) {
1154 if ((param_value = (const char *)g_hash_table_lookup(table, param_name))) {
1155 value = param_value;
1156 }
1157 }
1158 }
1159
1160 if (rule_data->node_hash != NULL) {
1161 h_val = (const char *)g_hash_table_lookup(rule_data->node_hash, attr);
1162 }
1163
1164 if (attr_allocated) {
1165 free((char *)attr);
1166 attr = NULL;
1167 }
1168
1169 return accept_attr_expr(h_val, value, type, op);
1170 }
1171
1172
1173
1174 int
1175 pe__eval_date_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
1176 {
1177 crm_time_t *start = NULL;
1178 crm_time_t *end = NULL;
1179 const char *value = NULL;
1180 const char *op = crm_element_value(expr, "operation");
1181
1182 xmlNode *duration_spec = NULL;
1183 xmlNode *date_spec = NULL;
1184
1185
1186 int rc = pcmk_rc_undetermined;
1187
1188 crm_trace("Testing expression: %s", ID(expr));
1189
1190 duration_spec = first_named_child(expr, "duration");
1191 date_spec = first_named_child(expr, "date_spec");
1192
1193 value = crm_element_value(expr, "start");
1194 if (value != NULL) {
1195 start = crm_time_new(value);
1196 }
1197 value = crm_element_value(expr, "end");
1198 if (value != NULL) {
1199 end = crm_time_new(value);
1200 }
1201
1202 if (start != NULL && end == NULL && duration_spec != NULL) {
1203 end = pe_parse_xml_duration(start, duration_spec);
1204 }
1205
1206 if (pcmk__str_eq(op, "in_range", pcmk__str_null_matches | pcmk__str_casei)) {
1207 if ((start == NULL) && (end == NULL)) {
1208
1209 } else if ((start != NULL) && (crm_time_compare(rule_data->now, start) < 0)) {
1210 rc = pcmk_rc_before_range;
1211 crm_time_set_if_earlier(next_change, start);
1212 } else if ((end != NULL) && (crm_time_compare(rule_data->now, end) > 0)) {
1213 rc = pcmk_rc_after_range;
1214 } else {
1215 rc = pcmk_rc_within_range;
1216 if (end && next_change) {
1217
1218 crm_time_add_seconds(end, 1);
1219 crm_time_set_if_earlier(next_change, end);
1220 }
1221 }
1222
1223 } else if (pcmk__str_eq(op, "date_spec", pcmk__str_casei)) {
1224 rc = pe_cron_range_satisfied(rule_data->now, date_spec);
1225
1226
1227 } else if (pcmk__str_eq(op, "gt", pcmk__str_casei)) {
1228 if (start == NULL) {
1229
1230 } else if (crm_time_compare(rule_data->now, start) > 0) {
1231 rc = pcmk_rc_within_range;
1232 } else {
1233 rc = pcmk_rc_before_range;
1234
1235
1236 crm_time_add_seconds(start, 1);
1237 crm_time_set_if_earlier(next_change, start);
1238 }
1239
1240 } else if (pcmk__str_eq(op, "lt", pcmk__str_casei)) {
1241 if (end == NULL) {
1242
1243 } else if (crm_time_compare(rule_data->now, end) < 0) {
1244 rc = pcmk_rc_within_range;
1245 crm_time_set_if_earlier(next_change, end);
1246 } else {
1247 rc = pcmk_rc_after_range;
1248 }
1249 }
1250
1251 crm_time_free(start);
1252 crm_time_free(end);
1253 return rc;
1254 }
1255
1256 gboolean
1257 pe__eval_op_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data) {
1258 const char *name = crm_element_value(expr, XML_NVPAIR_ATTR_NAME);
1259 const char *interval_s = crm_element_value(expr, XML_LRM_ATTR_INTERVAL);
1260 guint interval;
1261
1262 crm_trace("Testing op_defaults expression: %s", ID(expr));
1263
1264 if (rule_data->op_data == NULL) {
1265 crm_trace("No operations data provided");
1266 return FALSE;
1267 }
1268
1269 interval = crm_parse_interval_spec(interval_s);
1270 if (interval == 0 && errno != 0) {
1271 crm_trace("Could not parse interval: %s", interval_s);
1272 return FALSE;
1273 }
1274
1275 if (interval_s != NULL && interval != rule_data->op_data->interval) {
1276 crm_trace("Interval doesn't match: %d != %d", interval, rule_data->op_data->interval);
1277 return FALSE;
1278 }
1279
1280 if (!pcmk__str_eq(name, rule_data->op_data->op_name, pcmk__str_none)) {
1281 crm_trace("Name doesn't match: %s != %s", name, rule_data->op_data->op_name);
1282 return FALSE;
1283 }
1284
1285 return TRUE;
1286 }
1287
1288 gboolean
1289 pe__eval_role_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data)
1290 {
1291 gboolean accept = FALSE;
1292 const char *op = NULL;
1293 const char *value = NULL;
1294
1295 if (rule_data->role == RSC_ROLE_UNKNOWN) {
1296 return accept;
1297 }
1298
1299 value = crm_element_value(expr, XML_EXPR_ATTR_VALUE);
1300 op = crm_element_value(expr, XML_EXPR_ATTR_OPERATION);
1301
1302 if (pcmk__str_eq(op, "defined", pcmk__str_casei)) {
1303 if (rule_data->role > RSC_ROLE_STARTED) {
1304 accept = TRUE;
1305 }
1306
1307 } else if (pcmk__str_eq(op, "not_defined", pcmk__str_casei)) {
1308 if (rule_data->role < RSC_ROLE_SLAVE && rule_data->role > RSC_ROLE_UNKNOWN) {
1309 accept = TRUE;
1310 }
1311
1312 } else if (pcmk__str_eq(op, "eq", pcmk__str_casei)) {
1313 if (text2role(value) == rule_data->role) {
1314 accept = TRUE;
1315 }
1316
1317 } else if (pcmk__str_eq(op, "ne", pcmk__str_casei)) {
1318
1319 if (rule_data->role < RSC_ROLE_SLAVE && rule_data->role > RSC_ROLE_UNKNOWN) {
1320 accept = FALSE;
1321
1322 } else if (text2role(value) != rule_data->role) {
1323 accept = TRUE;
1324 }
1325 }
1326 return accept;
1327 }
1328
1329 gboolean
1330 pe__eval_rsc_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data)
1331 {
1332 const char *class = crm_element_value(expr, XML_AGENT_ATTR_CLASS);
1333 const char *provider = crm_element_value(expr, XML_AGENT_ATTR_PROVIDER);
1334 const char *type = crm_element_value(expr, XML_EXPR_ATTR_TYPE);
1335
1336 crm_trace("Testing rsc_defaults expression: %s", ID(expr));
1337
1338 if (rule_data->rsc_data == NULL) {
1339 crm_trace("No resource data provided");
1340 return FALSE;
1341 }
1342
1343 if (class != NULL &&
1344 !pcmk__str_eq(class, rule_data->rsc_data->standard, pcmk__str_none)) {
1345 crm_trace("Class doesn't match: %s != %s", class, rule_data->rsc_data->standard);
1346 return FALSE;
1347 }
1348
1349 if ((provider == NULL && rule_data->rsc_data->provider != NULL) ||
1350 (provider != NULL && rule_data->rsc_data->provider == NULL) ||
1351 !pcmk__str_eq(provider, rule_data->rsc_data->provider, pcmk__str_none)) {
1352 crm_trace("Provider doesn't match: %s != %s", provider, rule_data->rsc_data->provider);
1353 return FALSE;
1354 }
1355
1356 if (type != NULL &&
1357 !pcmk__str_eq(type, rule_data->rsc_data->agent, pcmk__str_none)) {
1358 crm_trace("Agent doesn't match: %s != %s", type, rule_data->rsc_data->agent);
1359 return FALSE;
1360 }
1361
1362 return TRUE;
1363 }
1364
1365
1366 gboolean test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now);
1367 gboolean test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role,
1368 crm_time_t *now);
1369 gboolean pe_test_rule_re(xmlNode *rule, GHashTable *node_hash,
1370 enum rsc_role_e role, crm_time_t *now,
1371 pe_re_match_data_t *re_match_data);
1372 gboolean pe_test_rule_full(xmlNode *rule, GHashTable *node_hash,
1373 enum rsc_role_e role, crm_time_t *now,
1374 pe_match_data_t *match_data);
1375 gboolean test_expression(xmlNode *expr, GHashTable *node_hash,
1376 enum rsc_role_e role, crm_time_t *now);
1377 gboolean pe_test_expression_re(xmlNode *expr, GHashTable *node_hash,
1378 enum rsc_role_e role, crm_time_t *now,
1379 pe_re_match_data_t *re_match_data);
1380 gboolean pe_test_expression_full(xmlNode *expr, GHashTable *node_hash,
1381 enum rsc_role_e role, crm_time_t *now,
1382 pe_match_data_t *match_data);
1383 void unpack_instance_attributes(xmlNode *top, xmlNode *xml_obj,
1384 const char *set_name, GHashTable *node_hash,
1385 GHashTable *hash, const char *always_first,
1386 gboolean overwrite, crm_time_t *now);
1387
1388 gboolean
1389 test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now)
1390 {
1391 return pe_evaluate_rules(ruleset, node_hash, now, NULL);
1392 }
1393
1394 gboolean
1395 test_rule(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
1396 {
1397 return pe_test_rule(rule, node_hash, role, now, NULL, NULL);
1398 }
1399
1400 gboolean
1401 pe_test_rule_re(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now, pe_re_match_data_t * re_match_data)
1402 {
1403 pe_match_data_t match_data = {
1404 .re = re_match_data,
1405 .params = NULL,
1406 .meta = NULL,
1407 };
1408 return pe_test_rule(rule, node_hash, role, now, NULL, &match_data);
1409 }
1410
1411 gboolean
1412 pe_test_rule_full(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role,
1413 crm_time_t *now, pe_match_data_t *match_data)
1414 {
1415 return pe_test_rule(rule, node_hash, role, now, NULL, match_data);
1416 }
1417
1418 gboolean
1419 test_expression(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
1420 {
1421 return pe_test_expression(expr, node_hash, role, now, NULL, NULL);
1422 }
1423
1424 gboolean
1425 pe_test_expression_re(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now, pe_re_match_data_t * re_match_data)
1426 {
1427 pe_match_data_t match_data = {
1428 .re = re_match_data,
1429 .params = NULL,
1430 .meta = NULL,
1431 };
1432 return pe_test_expression(expr, node_hash, role, now, NULL, &match_data);
1433 }
1434
1435 gboolean
1436 pe_test_expression_full(xmlNode *expr, GHashTable *node_hash,
1437 enum rsc_role_e role, crm_time_t *now,
1438 pe_match_data_t *match_data)
1439 {
1440 return pe_test_expression(expr, node_hash, role, now, NULL, match_data);
1441 }
1442
1443 void
1444 unpack_instance_attributes(xmlNode *top, xmlNode *xml_obj, const char *set_name,
1445 GHashTable *node_hash, GHashTable *hash,
1446 const char *always_first, gboolean overwrite,
1447 crm_time_t *now)
1448 {
1449 pe_rule_eval_data_t rule_data = {
1450 .node_hash = node_hash,
1451 .role = RSC_ROLE_UNKNOWN,
1452 .now = now,
1453 .match_data = NULL,
1454 .rsc_data = NULL,
1455 .op_data = NULL
1456 };
1457
1458 unpack_nvpair_blocks(top, xml_obj, set_name, hash, always_first,
1459 overwrite, &rule_data, NULL, unpack_attr_set);
1460 }