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