pacemaker 2.1.5-a3f44794f94
Scalable High-Availability cluster resource manager
rules.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2022 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 */
9
10#include <crm_internal.h>
11#include <crm/crm.h>
12#include <crm/msg_xml.h>
13#include <crm/common/xml.h>
15
16#include <glib.h>
17
18#include <crm/pengine/rules.h>
21
22#include <sys/types.h>
23#include <regex.h>
24#include <ctype.h>
25
27
38gboolean
39pe_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
54gboolean
55pe_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
87gboolean
88pe_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
105find_expression_type(xmlNode * expr)
106{
107 const char *tag = NULL;
108 const char *attr = NULL;
109
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/* As per the nethack rules:
139 *
140 * moon period = 29.53058 days ~= 30, year = 365.2422 days
141 * days moon phase advances on first day of year compared to preceding year
142 * = 365.2422 - 12*29.53058 ~= 11
143 * years in Metonic cycle (time until same phases fall on the same days of
144 * the month) = 18.6 ~= 19
145 * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30
146 * (29 as initial condition)
147 * current phase in days = first day phase + days elapsed in year
148 * 6 moons ~= 177 days
149 * 177 ~= 8 reported phases * 22
150 * + 11/22 for rounding
151 *
152 * 0-7, with 0: new, 4: full
153 */
154
155static int
156phase_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
171static int
172check_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 /* Return pe_date_result_undetermined if the field is missing. */
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 /* A single number was given, not a range. */
186 if (time_field < low) {
188 } else if (time_field > high) {
190 } else {
192 }
193 } else if (low != -1 && high != -1) {
194 /* This is a range with both bounds. */
195 if (time_field < low) {
197 } else if (time_field > high) {
199 } else {
201 }
202 } else if (low == -1) {
203 /* This is a range with no starting value. */
204 rc = time_field <= high ? pcmk_rc_within_range : pcmk_rc_after_range;
205 } else if (high == -1) {
206 /* This is a range with no ending value. */
207 rc = time_field >= low ? pcmk_rc_within_range : pcmk_rc_before_range;
208 }
209
210bail:
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
220static gboolean
221check_passes(int rc) {
222 /* _within_range is obvious. _undetermined is a pass because
223 * this is the return value if a field is not given. In this
224 * case, we just want to ignore it and check other fields to
225 * see if they place some restriction on what can pass.
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
237int
238pe_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 /* If we get here, either no fields were specified (which is success), or all
265 * the fields that were specified had their conditions met (which is also a
266 * success). Thus, the result is success.
267 */
268 return pcmk_rc_ok;
269}
270
271static void
272update_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
284pe_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// Set next_change to t if t is earlier
300static void
301crm_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// Information about a block of nvpair elements
312typedef struct sorted_set_s {
313 int score; // This block's score for sorting
314 const char *name; // This block's ID
315 const char *special_name; // ID that should sort first
316 xmlNode *attr_set; // This block
318
319static gint
320sort_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
348static void
349populate_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
369 if (name == NULL) {
371 }
372
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
405typedef 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;
412
413static void
414unpack_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
441static GList *
442make_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,
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 // Schema (if not "none") prevents this
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
492static void
493unpack_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) {
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
514void
515pe_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
537void
538pe_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
556char *
557pe_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 /* FIXME: Excessive? */
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 /* rm_eo can be equal to rm_so, but then there is nothing to do */
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
613gboolean
614pe_eval_rules(xmlNode *ruleset, pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
615{
616 // If there are no rules, pass by default
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 /* Only the deprecated "lifetime" element of location constraints
625 * may contain more than one rule at the top level -- the schema
626 * limits a block of nvpairs to a single top-level rule. So, this
627 * effectively means that a lifetime is active if any rule it
628 * contains is active.
629 */
630 return TRUE;
631 }
632 }
633
634 return ruleset_default;
635}
636
637gboolean
638pe_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);
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
679gboolean
680pe_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 /* these expressions can never succeed if there is
692 * no node to compare with
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)) {
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 /* bad type */ , 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
752static int
753compare_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 { // l_val == NULL && r_val != NULL
830 cmp = -1;
831 }
832
833 return cmp;
834}
835
850static bool
851accept_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 // The comparison is meaningless from this point on
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; // Should never reach this point
890}
891
900static const char *
901expand_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; // value_source is irrelevant
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 { // literal
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
935gboolean
936pe__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
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 // Expand any regular expression submatches (%0-%9) in attribute name
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 // Get value appropriate to value-source
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
999int
1000pe__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 // "undetermined" will also be returned for parsing errors
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 // in_range requires at least one of start or end
1034 } else if ((start != NULL) && (crm_time_compare(rule_data->now, start) < 0)) {
1036 crm_time_set_if_earlier(next_change, start);
1037 } else if ((end != NULL) && (crm_time_compare(rule_data->now, end) > 0)) {
1039 } else {
1041 if (end && next_change) {
1042 // Evaluation doesn't change until second after end
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 // @TODO set next_change appropriately
1051
1052 } else if (pcmk__str_eq(op, "gt", pcmk__str_casei)) {
1053 if (start == NULL) {
1054 // gt requires start
1055 } else if (crm_time_compare(rule_data->now, start) > 0) {
1057 } else {
1059
1060 // Evaluation doesn't change until second after start
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 // lt requires end
1068 } else if (crm_time_compare(rule_data->now, end) < 0) {
1070 crm_time_set_if_earlier(next_change, end);
1071 } else {
1073 }
1074 }
1075
1076 crm_time_free(start);
1077 crm_time_free(end);
1078 return rc;
1079}
1080
1081gboolean
1082pe__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
1122gboolean
1123pe__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
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 // Test "ne" only with promotable clone roles
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
1165gboolean
1166pe__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// Deprecated functions kept only for backward API compatibility
1202// LCOV_EXCL_START
1203
1205
1206gboolean
1207test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now)
1208{
1209 return pe_evaluate_rules(ruleset, node_hash, now, NULL);
1210}
1211
1212gboolean
1213test_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
1218gboolean
1219pe_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
1229gboolean
1230pe_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
1236gboolean
1237test_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
1242gboolean
1243pe_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
1253gboolean
1254pe_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
1261void
1262unpack_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// LCOV_EXCL_STOP
1281// End deprecated API
const char * name
Definition: cib.c:24
char guint crm_parse_interval_spec(const char *input)
Parse milliseconds from a Pacemaker interval specification.
Definition: utils.c:271
int char2score(const char *score)
Get the integer value of a score string.
Definition: scores.c:36
int compare_version(const char *version1, const char *version2)
Definition: utils.c:189
rsc_role_e
Possible roles that a resource can be in.
Definition: common.h:92
@ RSC_ROLE_STARTED
Definition: common.h:95
@ RSC_ROLE_UNKNOWN
Definition: common.h:93
@ RSC_ROLE_UNPROMOTED
Definition: common.h:96
enum rsc_role_e text2role(const char *role)
Definition: common.c:483
void populate_hash(xmlNode *nvpair_list, GHashTable *hash, const char **attrs, int attrs_length)
enum crm_ais_msg_types type
Definition: cpg.c:3
char uname[MAX_NAME]
Definition: cpg.c:5
char data[0]
Definition: cpg.c:10
A dumping ground.
#define CRM_ATTR_ROLE
Definition: crm.h:116
#define CRM_ATTR_KIND
Definition: crm.h:115
#define CRM_ATTR_UNAME
Definition: crm.h:113
#define CRM_ATTR_ID
Definition: crm.h:114
void crm_time_add_hours(crm_time_t *dt, int value)
Definition: iso8601.c:1515
void crm_time_add_seconds(crm_time_t *dt, int value)
Add a given number of seconds to a date/time or duration.
Definition: iso8601.c:1422
void crm_time_set(crm_time_t *target, const crm_time_t *source)
Definition: iso8601.c:1196
void crm_time_add_weeks(crm_time_t *dt, int value)
Definition: iso8601.c:1521
void crm_time_add_days(crm_time_t *dt, int value)
Definition: iso8601.c:1442
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:140
int crm_time_get_ordinal(const crm_time_t *dt, uint32_t *y, uint32_t *d)
Definition: iso8601.c:391
crm_time_t * pcmk_copy_time(const crm_time_t *source)
Definition: iso8601.c:1265
void crm_time_add_years(crm_time_t *dt, int value)
Definition: iso8601.c:1527
bool crm_time_is_defined(const crm_time_t *t)
Check whether a time object has been initialized yet.
Definition: iso8601.c:132
void crm_time_add_months(crm_time_t *dt, int value)
Definition: iso8601.c:1467
crm_time_t * crm_time_new(const char *string)
Definition: iso8601.c:92
int crm_time_get_timeofday(const crm_time_t *dt, uint32_t *h, uint32_t *m, uint32_t *s)
Definition: iso8601.c:292
struct crm_time_s crm_time_t
Definition: iso8601.h:32
int crm_time_get_gregorian(const crm_time_t *dt, uint32_t *y, uint32_t *m, uint32_t *d)
Definition: iso8601.c:358
int crm_time_compare(const crm_time_t *a, const crm_time_t *b)
Definition: iso8601.c:1392
int crm_time_get_isoweek(const crm_time_t *dt, uint32_t *y, uint32_t *w, uint32_t *d)
Definition: iso8601.c:399
void crm_time_add_minutes(crm_time_t *dt, int value)
Definition: iso8601.c:1509
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:227
#define crm_debug(fmt, args...)
Definition: logging.h:364
#define crm_err(fmt, args...)
Definition: logging.h:359
#define crm_trace(fmt, args...)
Definition: logging.h:365
#define XML_RULE_ATTR_BOOLEAN_OP
Definition: msg_xml.h:339
#define ID(x)
Definition: msg_xml.h:468
#define XML_EXPR_ATTR_TYPE
Definition: msg_xml.h:348
#define XML_NVPAIR_ATTR_VALUE
Definition: msg_xml.h:392
#define XML_TAG_EXPRESSION
Definition: msg_xml.h:341
#define XML_RULE_ATTR_SCORE
Definition: msg_xml.h:336
#define XML_EXPR_ATTR_OPERATION
Definition: msg_xml.h:346
#define PCMK_XE_OP_EXPRESSION
Definition: msg_xml.h:343
#define XML_TAG_RULE
Definition: msg_xml.h:335
#define XML_LRM_ATTR_INTERVAL
Definition: msg_xml.h:294
#define XML_AGENT_ATTR_PROVIDER
Definition: msg_xml.h:270
#define XML_AGENT_ATTR_CLASS
Definition: msg_xml.h:269
#define XML_TAG_ATTRS
Definition: msg_xml.h:211
#define PCMK_XE_RSC_EXPRESSION
Definition: msg_xml.h:344
#define XML_NVPAIR_ATTR_NAME
Definition: msg_xml.h:391
#define XML_EXPR_ATTR_VALUE
Definition: msg_xml.h:347
#define XML_CIB_TAG_NVPAIR
Definition: msg_xml.h:206
#define XML_EXPR_ATTR_VALUE_SOURCE
Definition: msg_xml.h:349
#define PCMK_XE_DATE_EXPRESSION
Definition: msg_xml.h:342
#define XML_EXPR_ATTR_ATTRIBUTE
Definition: msg_xml.h:345
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:517
pcmk__action_result_t result
Definition: pcmk_fence.c:35
#define pe_err(fmt...)
Definition: internal.h:49
@ pcmk_rc_before_range
Definition: results.h:121
@ pcmk_rc_op_unsatisfied
Definition: results.h:123
@ pcmk_rc_ok
Definition: results.h:148
@ pcmk_rc_undetermined
Definition: results.h:122
@ pcmk_rc_unknown_format
Definition: results.h:142
@ pcmk_rc_within_range
Definition: results.h:120
@ pcmk_rc_after_range
Definition: results.h:119
gboolean 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)
Definition: rules.c:1219
void pe_eval_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name, pe_rule_eval_data_t *rule_data, GHashTable *hash, const char *always_first, gboolean overwrite, crm_time_t *next_change)
Definition: rules.c:515
struct sorted_set_s sorted_set_t
void pe_unpack_nvpairs(xmlNode *top, xmlNode *xml_obj, const char *set_name, GHashTable *node_hash, GHashTable *hash, const char *always_first, gboolean overwrite, crm_time_t *now, crm_time_t *next_change)
Extract nvpair blocks contained by an XML element into a hash table.
Definition: rules.c:538
gboolean pe__eval_op_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data)
Definition: rules.c:1082
int pe__eval_date_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Definition: rules.c:1000
enum expression_type find_expression_type(xmlNode *expr)
Definition: rules.c:105
gboolean test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition: rules.c:1213
gboolean pe_eval_subexpr(xmlNode *expr, pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Definition: rules.c:680
gboolean pe_eval_expr(xmlNode *rule, pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Definition: rules.c:638
int pe_cron_range_satisfied(crm_time_t *now, xmlNode *cron_spec)
Definition: rules.c:238
gboolean pe_test_expression_full(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now, pe_match_data_t *match_data)
Definition: rules.c:1254
gboolean pe__eval_attr_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data)
Definition: rules.c:936
gboolean pe_test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now, crm_time_t *next_change, pe_match_data_t *match_data)
Definition: rules.c:55
gboolean pe__eval_rsc_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data)
Definition: rules.c:1166
gboolean pe_evaluate_rules(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now, crm_time_t *next_change)
Evaluate any rules contained by given XML element.
Definition: rules.c:39
struct unpack_data_s unpack_data_t
gboolean test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition: rules.c:1237
gboolean pe_eval_rules(xmlNode *ruleset, pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Definition: rules.c:614
gboolean pe_test_rule_full(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now, pe_match_data_t *match_data)
Definition: rules.c:1230
char * pe_expand_re_matches(const char *string, pe_re_match_data_t *match_data)
Definition: rules.c:557
void unpack_instance_attributes(xmlNode *top, xmlNode *xml_obj, const char *set_name, GHashTable *node_hash, GHashTable *hash, const char *always_first, gboolean overwrite, crm_time_t *now)
Definition: rules.c:1262
gboolean pe_test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now, crm_time_t *next_change, pe_match_data_t *match_data)
Evaluate one rule subelement (pass/fail)
Definition: rules.c:88
gboolean pe__eval_role_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data)
Definition: rules.c:1123
#define CHECK_ONE(spec, name, var)
Definition: rules.c:230
gboolean 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)
Definition: rules.c:1243
CRM_TRACE_INIT_DATA(pe_rules)
gboolean test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now)
Definition: rules.c:1207
crm_time_t * pe_parse_xml_duration(crm_time_t *start, xmlNode *duration_spec)
Definition: rules.c:284
expression_type
Definition: rules.h:22
@ not_expr
Definition: rules.h:23
@ rsc_expr
Definition: rules.h:33
@ op_expr
Definition: rules.h:34
@ role_expr
Definition: rules.h:27
@ attr_expr
Definition: rules.h:25
@ nested_rule
Definition: rules.h:24
@ time_expr
Definition: rules.h:28
@ loc_expr
Definition: rules.h:26
Deprecated Pacemaker rule API.
bool pcmk__char_in_any_str(int ch,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:975
int pcmk__scan_double(const char *text, double *result, const char *default_text, char **end_text)
Definition: strings.c:199
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition: strings.c:97
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:928
@ pcmk__str_none
@ pcmk__str_null_matches
@ pcmk__str_casei
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:952
int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
Definition: strings.c:810
GHashTable * meta
Definition: common.h:179
pe_re_match_data_t * re
Definition: common.h:177
GHashTable * params
Definition: common.h:178
const char * op_name
Definition: common.h:189
guint interval
Definition: common.h:190
regmatch_t * pmatch
Definition: common.h:173
char * string
Definition: common.h:171
const char * agent
Definition: common.h:185
const char * provider
Definition: common.h:184
const char * standard
Definition: common.h:183
pe_rsc_eval_data_t * rsc_data
Definition: common.h:198
GHashTable * node_hash
Definition: common.h:194
enum rsc_role_e role
Definition: common.h:195
crm_time_t * now
Definition: common.h:196
pe_match_data_t * match_data
Definition: common.h:197
pe_op_eval_data_t * op_data
Definition: common.h:199
Wrappers for and extensions to libxml2.
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:3002
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2930
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2956