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