pacemaker 2.1.8-2.1.8
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
rules.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2024 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
12#include <stdio.h> // NULL, size_t
13#include <stdbool.h> // bool
14#include <ctype.h> // isdigit()
15#include <regex.h> // regmatch_t
16#include <stdint.h> // uint32_t
17#include <inttypes.h> // PRIu32
18#include <glib.h> // gboolean, FALSE
19#include <libxml/tree.h> // xmlNode
20
22
26#include "crmcommon_private.h"
27
37pcmk__condition_type(const xmlNode *condition)
38{
39 const char *name = NULL;
40
41 // Expression types based on element name
42
43 if (pcmk__xe_is(condition, PCMK_XE_DATE_EXPRESSION)) {
45
46 } else if (pcmk__xe_is(condition, PCMK_XE_RSC_EXPRESSION)) {
48
49 } else if (pcmk__xe_is(condition, PCMK_XE_OP_EXPRESSION)) {
51
52 } else if (pcmk__xe_is(condition, PCMK_XE_RULE)) {
54
55 } else if (!pcmk__xe_is(condition, PCMK_XE_EXPRESSION)) {
57 }
58
59 // Expression types based on node attribute name
60
62
64 NULL)) {
66 }
67
69}
70
79static const char *
80loggable_parent_id(const xmlNode *xml)
81{
82 // Default if called without parent (likely for unit testing)
83 const char *parent_id = "implied";
84
85 if ((xml != NULL) && (xml->parent != NULL)) {
86 parent_id = pcmk__xe_id(xml->parent);
87 if (parent_id == NULL) { // Not possible with schema validation enabled
88 parent_id = "without ID";
89 }
90 }
91 return parent_id;
92}
93
104static int
105phase_of_the_moon(const crm_time_t *now)
106{
107 /* As per the nethack rules:
108 * - A moon period is 29.53058 days ~= 30
109 * - A year is 365.2422 days
110 * - Number of days moon phase advances on first day of year compared to
111 * preceding year is (365.2422 - 12 * 29.53058) ~= 11
112 * - Number of years until same phases fall on the same days of the month
113 * is 18.6 ~= 19
114 * - Moon phase on first day of year (epact) ~= (11 * (year%19) + 29) % 30
115 * (29 as initial condition)
116 * - Current phase in days = first day phase + days elapsed in year
117 * - 6 moons ~= 177 days ~= 8 reported phases * 22 (+ 11/22 for rounding)
118 */
119 uint32_t epact, diy, goldn;
120 uint32_t y;
121
122 crm_time_get_ordinal(now, &y, &diy);
123 goldn = (y % 19) + 1;
124 epact = (11 * goldn + 18) % 30;
125 if (((epact == 25) && (goldn > 11)) || (epact == 24)) {
126 epact++;
127 }
128 return (((((diy + epact) * 6) + 11) % 177) / 22) & 7;
129}
130
146static int
147check_range(const xmlNode *date_spec, const char *id, const char *attr,
148 uint32_t value)
149{
150 int rc = pcmk_rc_ok;
151 const char *range = crm_element_value(date_spec, attr);
152 long long low, high;
153
154 if (range == NULL) { // Attribute not present
155 goto bail;
156 }
157
158 if (pcmk__parse_ll_range(range, &low, &high) != pcmk_rc_ok) {
159 // Invalid range
160 /* @COMPAT When we can break behavioral backward compatibility, treat
161 * the entire rule as not passing.
162 */
164 " %s attribute %s because '%s' is not a valid range",
165 id, attr, range);
166
167 } else if ((low != -1) && (value < low)) {
169
170 } else if ((high != -1) && (value > high)) {
172 }
173
174bail:
175 crm_trace("Checked " PCMK_XE_DATE_SPEC " %s %s='%s' for %" PRIu32 ": %s",
176 id, attr, pcmk__s(range, ""), value, pcmk_rc_str(rc));
177 return rc;
178}
179
192int
193pcmk__evaluate_date_spec(const xmlNode *date_spec, const crm_time_t *now)
194{
195 const char *id = NULL;
196 const char *parent_id = loggable_parent_id(date_spec);
197
198 // Range attributes that can be specified for a PCMK_XE_DATE_SPEC element
199 struct range {
200 const char *attr;
201 uint32_t value;
202 } ranges[] = {
203 { PCMK_XA_YEARS, 0U },
204 { PCMK_XA_MONTHS, 0U },
205 { PCMK_XA_MONTHDAYS, 0U },
206 { PCMK_XA_HOURS, 0U },
207 { PCMK_XA_MINUTES, 0U },
208 { PCMK_XA_SECONDS, 0U },
209 { PCMK_XA_YEARDAYS, 0U },
210 { PCMK_XA_WEEKYEARS, 0U },
211 { PCMK_XA_WEEKS, 0U },
212 { PCMK_XA_WEEKDAYS, 0U },
213 { PCMK__XA_MOON, 0U },
214 };
215
216 if ((date_spec == NULL) || (now == NULL)) {
217 return EINVAL;
218 }
219
220 // Get specification ID (for logging)
221 id = pcmk__xe_id(date_spec);
222 if (pcmk__str_empty(id)) { // Not possible with schema validation enabled
223 /* @COMPAT When we can break behavioral backward compatibility,
224 * fail the specification
225 */
226 pcmk__config_warn(PCMK_XE_DATE_SPEC " subelement of "
228 parent_id);
229 id = "without ID"; // for logging
230 }
231
232 // Year, month, day
233 crm_time_get_gregorian(now, &(ranges[0].value), &(ranges[1].value),
234 &(ranges[2].value));
235
236 // Hour, minute, second
237 crm_time_get_timeofday(now, &(ranges[3].value), &(ranges[4].value),
238 &(ranges[5].value));
239
240 // Year (redundant) and day of year
241 crm_time_get_ordinal(now, &(ranges[0].value), &(ranges[6].value));
242
243 // Week year, week of week year, day of week
244 crm_time_get_isoweek(now, &(ranges[7].value), &(ranges[8].value),
245 &(ranges[9].value));
246
247 // Moon phase (deprecated)
248 ranges[10].value = phase_of_the_moon(now);
249 if (crm_element_value(date_spec, PCMK__XA_MOON) != NULL) {
250 pcmk__config_warn("Support for '" PCMK__XA_MOON "' in "
251 PCMK_XE_DATE_SPEC " elements (such as %s) is "
252 "deprecated and will be removed in a future release "
253 "of Pacemaker", id);
254 }
255
256 for (int i = 0; i < PCMK__NELEM(ranges); ++i) {
257 int rc = check_range(date_spec, id, ranges[i].attr, ranges[i].value);
258
259 if (rc != pcmk_rc_ok) {
260 return rc;
261 }
262 }
263
264 // All specified ranges passed, or none were given (also considered a pass)
265 return pcmk_rc_ok;
266}
267
268#define ADD_COMPONENT(component) do { \
269 int sub_rc = pcmk__add_time_from_xml(*end, component, duration); \
270 if (sub_rc != pcmk_rc_ok) { \
271 /* @COMPAT return sub_rc when we can break compatibility */ \
272 pcmk__config_warn("Ignoring %s in " PCMK_XE_DURATION " %s " \
273 "because it is invalid", \
274 pcmk__time_component_attr(component), id); \
275 rc = sub_rc; \
276 } \
277 } while (0)
278
291int
292pcmk__unpack_duration(const xmlNode *duration, const crm_time_t *start,
293 crm_time_t **end)
294{
295 int rc = pcmk_rc_ok;
296 const char *id = NULL;
297 const char *parent_id = loggable_parent_id(duration);
298
299 if ((start == NULL) || (duration == NULL)
300 || (end == NULL) || (*end != NULL)) {
301 return EINVAL;
302 }
303
304 // Get duration ID (for logging)
305 id = pcmk__xe_id(duration);
306 if (pcmk__str_empty(id)) { // Not possible with schema validation enabled
307 /* @COMPAT When we can break behavioral backward compatibility,
308 * return pcmk_rc_unpack_error instead
309 */
310 pcmk__config_warn(PCMK_XE_DURATION " subelement of "
312 parent_id);
313 id = "without ID";
314 }
315
316 *end = pcmk_copy_time(start);
317
325
326 return rc;
327}
328
342static int
343evaluate_in_range(const xmlNode *date_expression, const char *id,
344 const crm_time_t *now, crm_time_t *next_change)
345{
346 crm_time_t *start = NULL;
347 crm_time_t *end = NULL;
348
349 if (pcmk__xe_get_datetime(date_expression, PCMK_XA_START,
350 &start) != pcmk_rc_ok) {
351 /* @COMPAT When we can break behavioral backward compatibility,
352 * return pcmk_rc_unpack_error
353 */
354 pcmk__config_warn("Ignoring " PCMK_XA_START " in "
355 PCMK_XE_DATE_EXPRESSION " %s because it is invalid",
356 id);
357 }
358
359 if (pcmk__xe_get_datetime(date_expression, PCMK_XA_END,
360 &end) != pcmk_rc_ok) {
361 /* @COMPAT When we can break behavioral backward compatibility,
362 * return pcmk_rc_unpack_error
363 */
364 pcmk__config_warn("Ignoring " PCMK_XA_END " in "
365 PCMK_XE_DATE_EXPRESSION " %s because it is invalid",
366 id);
367 }
368
369 if ((start == NULL) && (end == NULL)) {
370 // Not possible with schema validation enabled
371 /* @COMPAT When we can break behavioral backward compatibility,
372 * return pcmk_rc_unpack_error
373 */
374 pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION " %s as not "
375 "passing because in_range requires at least one of "
376 PCMK_XA_START " or " PCMK_XA_END, id);
378 }
379
380 if (end == NULL) {
381 xmlNode *duration = pcmk__xe_first_child(date_expression,
382 PCMK_XE_DURATION, NULL, NULL);
383
384 if (duration != NULL) {
385 /* @COMPAT When we can break behavioral backward compatibility,
386 * return the result of this if not OK
387 */
388 pcmk__unpack_duration(duration, start, &end);
389 }
390 }
391
392 if ((start != NULL) && (crm_time_compare(now, start) < 0)) {
393 pcmk__set_time_if_earlier(next_change, start);
394 crm_time_free(start);
395 crm_time_free(end);
397 }
398
399 if (end != NULL) {
400 if (crm_time_compare(now, end) > 0) {
401 crm_time_free(start);
402 crm_time_free(end);
403 return pcmk_rc_after_range;
404 }
405
406 // Evaluation doesn't change until second after end
407 if (next_change != NULL) {
408 crm_time_add_seconds(end, 1);
409 pcmk__set_time_if_earlier(next_change, end);
410 }
411 }
412
413 crm_time_free(start);
414 crm_time_free(end);
416}
417
431static int
432evaluate_gt(const xmlNode *date_expression, const char *id,
433 const crm_time_t *now, crm_time_t *next_change)
434{
435 crm_time_t *start = NULL;
436
437 if (pcmk__xe_get_datetime(date_expression, PCMK_XA_START,
438 &start) != pcmk_rc_ok) {
439 /* @COMPAT When we can break behavioral backward compatibility,
440 * return pcmk_rc_unpack_error
441 */
442 pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION " %s as not "
443 "passing because " PCMK_XA_START " is invalid",
444 id);
446 }
447
448 if (start == NULL) { // Not possible with schema validation enabled
449 /* @COMPAT When we can break behavioral backward compatibility,
450 * return pcmk_rc_unpack_error
451 */
452 pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION " %s as not "
453 "passing because " PCMK_VALUE_GT " requires "
454 PCMK_XA_START, id);
456 }
457
458 if (crm_time_compare(now, start) > 0) {
459 crm_time_free(start);
461 }
462
463 // Evaluation doesn't change until second after start time
464 crm_time_add_seconds(start, 1);
465 pcmk__set_time_if_earlier(next_change, start);
466 crm_time_free(start);
468}
469
483static int
484evaluate_lt(const xmlNode *date_expression, const char *id,
485 const crm_time_t *now, crm_time_t *next_change)
486{
487 crm_time_t *end = NULL;
488
489 if (pcmk__xe_get_datetime(date_expression, PCMK_XA_END,
490 &end) != pcmk_rc_ok) {
491 /* @COMPAT When we can break behavioral backward compatibility,
492 * return pcmk_rc_unpack_error
493 */
494 pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION " %s as not "
495 "passing because " PCMK_XA_END " is invalid", id);
497 }
498
499 if (end == NULL) { // Not possible with schema validation enabled
500 /* @COMPAT When we can break behavioral backward compatibility,
501 * return pcmk_rc_unpack_error
502 */
503 pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION " %s as not "
504 "passing because " PCMK_VALUE_GT " requires "
505 PCMK_XA_END, id);
507 }
508
509 if (crm_time_compare(now, end) < 0) {
510 pcmk__set_time_if_earlier(next_change, end);
511 crm_time_free(end);
513 }
514
515 crm_time_free(end);
516 return pcmk_rc_after_range;
517}
518
533int
534pcmk__evaluate_date_expression(const xmlNode *date_expression,
535 const crm_time_t *now, crm_time_t *next_change)
536{
537 const char *id = NULL;
538 const char *op = NULL;
539 int rc = pcmk_rc_undetermined;
540
541 if ((date_expression == NULL) || (now == NULL)) {
542 return EINVAL;
543 }
544
545 // Get expression ID (for logging)
546 id = pcmk__xe_id(date_expression);
547 if (pcmk__str_empty(id)) { // Not possible with schema validation enabled
548 /* @COMPAT When we can break behavioral backward compatibility,
549 * return pcmk_rc_unpack_error
550 */
552 PCMK_XA_ID);
553 id = "without ID"; // for logging
554 }
555
556 op = crm_element_value(date_expression, PCMK_XA_OPERATION);
557 if (pcmk__str_eq(op, PCMK_VALUE_IN_RANGE,
559 rc = evaluate_in_range(date_expression, id, now, next_change);
560
561 } else if (pcmk__str_eq(op, PCMK_VALUE_DATE_SPEC, pcmk__str_casei)) {
562 xmlNode *date_spec = pcmk__xe_first_child(date_expression,
563 PCMK_XE_DATE_SPEC, NULL,
564 NULL);
565
566 if (date_spec == NULL) { // Not possible with schema validation enabled
567 /* @COMPAT When we can break behavioral backward compatibility,
568 * return pcmk_rc_unpack_error
569 */
571 "as not passing because " PCMK_VALUE_DATE_SPEC
572 " operations require a " PCMK_XE_DATE_SPEC
573 " subelement", id);
574 } else {
575 // @TODO set next_change appropriately
576 rc = pcmk__evaluate_date_spec(date_spec, now);
577 }
578
579 } else if (pcmk__str_eq(op, PCMK_VALUE_GT, pcmk__str_casei)) {
580 rc = evaluate_gt(date_expression, id, now, next_change);
581
582 } else if (pcmk__str_eq(op, PCMK_VALUE_LT, pcmk__str_casei)) {
583 rc = evaluate_lt(date_expression, id, now, next_change);
584
585 } else { // Not possible with schema validation enabled
586 /* @COMPAT When we can break behavioral backward compatibility,
587 * return pcmk_rc_unpack_error
588 */
590 " %s as not passing because '%s' is not a valid "
591 PCMK_XE_OPERATION, id, op);
592 }
593
594 crm_trace(PCMK_XE_DATE_EXPRESSION " %s (%s): %s (%d)",
595 id, op, pcmk_rc_str(rc), rc);
596 return rc;
597}
598
615static bool
616process_submatches(const char *string, const char *match,
617 const regmatch_t submatches[], int nmatches,
618 char *expansion, size_t *nbytes)
619{
620 bool expanded = false;
621 const char *src = string;
622
623 if (nbytes != NULL) {
624 *nbytes = 1; // Include space for terminator
625 }
626
627 while (*src != '\0') {
628 int submatch = 0;
629 size_t match_len = 0;
630
631 if ((src[0] != '%') || !isdigit(src[1])) {
632 /* src does not point to the first character of a %N sequence,
633 * so expand this character as-is
634 */
635 if (expansion != NULL) {
636 *expansion++ = *src;
637 }
638 if (nbytes != NULL) {
639 ++(*nbytes);
640 }
641 ++src;
642 continue;
643 }
644
645 submatch = src[1] - '0';
646 src += 2; // Skip over %N sequence in source string
647 expanded = true; // Expansion will be different from source
648
649 // Omit sequence from expansion unless it has a non-empty match
650 if ((nmatches <= submatch) // Not enough submatches
651 || (submatches[submatch].rm_so < 0) // Pattern did not match
652 || (submatches[submatch].rm_eo
653 <= submatches[submatch].rm_so)) { // Match was empty
654 continue;
655 }
656
657 match_len = submatches[submatch].rm_eo - submatches[submatch].rm_so;
658 if (nbytes != NULL) {
659 *nbytes += match_len;
660 }
661 if (expansion != NULL) {
662 memcpy(expansion, match + submatches[submatch].rm_so,
663 match_len);
664 expansion += match_len;
665 }
666 }
667
668 return expanded;
669}
670
684char *
685pcmk__replace_submatches(const char *string, const char *match,
686 const regmatch_t submatches[], int nmatches)
687{
688 size_t nbytes = 0;
689 char *result = NULL;
690
691 if (pcmk__str_empty(string) || pcmk__str_empty(match)) {
692 return NULL; // Nothing to expand
693 }
694
695 // Calculate how much space will be needed for expanded string
696 if (!process_submatches(string, match, submatches, nmatches, NULL,
697 &nbytes)) {
698 return NULL; // No expansions needed
699 }
700
701 // Allocate enough space for expanded string
702 result = pcmk__assert_alloc(nbytes, sizeof(char));
703
704 // Expand submatches
705 (void) process_submatches(string, match, submatches, nmatches, result,
706 NULL);
707 return result;
708}
709
724{
725 if (pcmk__str_eq(op, PCMK_VALUE_DEFINED, pcmk__str_casei)) {
727
728 } else if (pcmk__str_eq(op, PCMK_VALUE_NOT_DEFINED, pcmk__str_casei)) {
730
731 } else if (pcmk__str_eq(op, PCMK_VALUE_EQ, pcmk__str_casei)) {
732 return pcmk__comparison_eq;
733
734 } else if (pcmk__str_eq(op, PCMK_VALUE_NE, pcmk__str_casei)) {
735 return pcmk__comparison_ne;
736
737 } else if (pcmk__str_eq(op, PCMK_VALUE_LT, pcmk__str_casei)) {
738 return pcmk__comparison_lt;
739
740 } else if (pcmk__str_eq(op, PCMK_VALUE_LTE, pcmk__str_casei)) {
742
743 } else if (pcmk__str_eq(op, PCMK_VALUE_GT, pcmk__str_casei)) {
744 return pcmk__comparison_gt;
745
746 } else if (pcmk__str_eq(op, PCMK_VALUE_GTE, pcmk__str_casei)) {
748 }
749
751}
752
764enum pcmk__type
766 const char *value1, const char *value2)
767{
768 if (type == NULL) {
769 switch (op) {
774 if (((value1 != NULL) && (strchr(value1, '.') != NULL))
775 || ((value2 != NULL) && (strchr(value2, '.') != NULL))) {
776 return pcmk__type_number;
777 }
778 return pcmk__type_integer;
779
780 default:
781 return pcmk__type_string;
782 }
783 }
784
785 if (pcmk__str_eq(type, PCMK_VALUE_STRING, pcmk__str_casei)) {
786 return pcmk__type_string;
787
788 } else if (pcmk__str_eq(type, PCMK_VALUE_INTEGER, pcmk__str_casei)) {
789 return pcmk__type_integer;
790
791 } else if (pcmk__str_eq(type, PCMK_VALUE_NUMBER, pcmk__str_casei)) {
792 return pcmk__type_number;
793
794 } else if (pcmk__str_eq(type, PCMK_VALUE_VERSION, pcmk__str_casei)) {
795 return pcmk__type_version;
796 }
797
798 return pcmk__type_unknown;
799}
800
813int
814pcmk__cmp_by_type(const char *value1, const char *value2, enum pcmk__type type)
815{
816 // NULL compares as less than non-NULL
817 if (value2 == NULL) {
818 return (value1 == NULL)? 0 : 1;
819 }
820 if (value1 == NULL) {
821 return -1;
822 }
823
824 switch (type) {
826 return strcasecmp(value1, value2);
827
829 {
830 long long integer1;
831 long long integer2;
832
833 if ((pcmk__scan_ll(value1, &integer1, 0LL) != pcmk_rc_ok)
834 || (pcmk__scan_ll(value2, &integer2, 0LL) != pcmk_rc_ok)) {
835 crm_warn("Comparing '%s' and '%s' as strings because "
836 "invalid as integers", value1, value2);
837 return strcasecmp(value1, value2);
838 }
839 return (integer1 < integer2)? -1 : (integer1 > integer2)? 1 : 0;
840 }
841 break;
842
844 {
845 double num1;
846 double num2;
847
848 if ((pcmk__scan_double(value1, &num1, NULL, NULL) != pcmk_rc_ok)
849 || (pcmk__scan_double(value2, &num2, NULL,
850 NULL) != pcmk_rc_ok)) {
851 crm_warn("Comparing '%s' and '%s' as strings because invalid as "
852 "numbers", value1, value2);
853 return strcasecmp(value1, value2);
854 }
855 return (num1 < num2)? -1 : (num1 > num2)? 1 : 0;
856 }
857 break;
858
860 return compare_version(value1, value2);
861
862 default: // Invalid type
863 return 0;
864 }
865}
866
876pcmk__parse_source(const char *source)
877{
878 if (pcmk__str_eq(source, PCMK_VALUE_LITERAL,
881
882 } else if (pcmk__str_eq(source, PCMK_VALUE_PARAM, pcmk__str_casei)) {
884
885 } else if (pcmk__str_eq(source, PCMK_VALUE_META, pcmk__str_casei)) {
887
888 } else {
890 }
891}
892
901enum pcmk__combine
902pcmk__parse_combine(const char *combine)
903{
904 if (pcmk__str_eq(combine, PCMK_VALUE_AND,
906 return pcmk__combine_and;
907
908 } else if (pcmk__str_eq(combine, PCMK_VALUE_OR, pcmk__str_casei)) {
909 return pcmk__combine_or;
910
911 } else {
913 }
914}
915
930static int
931evaluate_attr_comparison(const char *actual, const char *reference,
932 enum pcmk__type type, enum pcmk__comparison comparison)
933{
934 int cmp = 0;
935
936 switch (comparison) {
938 return (actual != NULL)? pcmk_rc_ok : pcmk_rc_op_unsatisfied;
939
941 return (actual == NULL)? pcmk_rc_ok : pcmk_rc_op_unsatisfied;
942
943 default:
944 break;
945 }
946
947 cmp = pcmk__cmp_by_type(actual, reference, type);
948
949 switch (comparison) {
951 return (cmp == 0)? pcmk_rc_ok : pcmk_rc_op_unsatisfied;
952
954 return (cmp != 0)? pcmk_rc_ok : pcmk_rc_op_unsatisfied;
955
956 default:
957 break;
958 }
959
960 if ((actual == NULL) || (reference == NULL)) {
961 return pcmk_rc_op_unsatisfied; // Comparison would be meaningless
962 }
963
964 switch (comparison) {
966 return (cmp < 0)? pcmk_rc_ok : pcmk_rc_after_range;
967
969 return (cmp <= 0)? pcmk_rc_ok : pcmk_rc_after_range;
970
972 return (cmp > 0)? pcmk_rc_ok : pcmk_rc_before_range;
973
975 return (cmp >= 0)? pcmk_rc_ok : pcmk_rc_before_range;
976
977 default: // Not possible with schema validation enabled
979 }
980}
981
990static const char *
991value_from_source(const char *value, enum pcmk__reference_source source,
992 const pcmk_rule_input_t *rule_input)
993{
994 GHashTable *table = NULL;
995
996 if (pcmk__str_empty(value)) {
997 /* @COMPAT When we can break backward compatibility, drop this block so
998 * empty strings are treated as such (there should never be an empty
999 * string as an instance attribute or meta-attribute name, so those will
1000 * get NULL anyway, but it could matter for literal comparisons)
1001 */
1002 return NULL;
1003 }
1004
1005 switch (source) {
1007 return value;
1008
1010 table = rule_input->rsc_params;
1011 break;
1012
1014 table = rule_input->rsc_meta;
1015 break;
1016
1017 default:
1018 return NULL; // Not possible
1019 }
1020
1021 if (table == NULL) {
1022 return NULL;
1023 }
1024 return (const char *) g_hash_table_lookup(table, value);
1025}
1026
1037int
1038pcmk__evaluate_attr_expression(const xmlNode *expression,
1039 const pcmk_rule_input_t *rule_input)
1040{
1041 const char *id = NULL;
1042 const char *op = NULL;
1043 const char *attr = NULL;
1044 const char *type_s = NULL;
1045 const char *value = NULL;
1046 const char *actual = NULL;
1047 const char *source_s = NULL;
1048 const char *reference = NULL;
1049 char *expanded_attr = NULL;
1050 int rc = pcmk_rc_ok;
1051
1055
1056 if ((expression == NULL) || (rule_input == NULL)) {
1057 return EINVAL;
1058 }
1059
1060 // Get expression ID (for logging)
1061 id = pcmk__xe_id(expression);
1062 if (pcmk__str_empty(id)) {
1063 /* @COMPAT When we can break behavioral backward compatibility,
1064 * fail the expression
1065 */
1067 id = "without ID"; // for logging
1068 }
1069
1070 /* Get name of node attribute to compare (expanding any %0-%9 to
1071 * regular expression submatches)
1072 */
1073 attr = crm_element_value(expression, PCMK_XA_ATTRIBUTE);
1074 if (attr == NULL) {
1075 pcmk__config_err("Treating " PCMK_XE_EXPRESSION " %s as not passing "
1076 "because " PCMK_XA_ATTRIBUTE " was not specified", id);
1077 return pcmk_rc_unpack_error;
1078 }
1079 expanded_attr = pcmk__replace_submatches(attr, rule_input->rsc_id,
1080 rule_input->rsc_id_submatches,
1081 rule_input->rsc_id_nmatches);
1082 if (expanded_attr != NULL) {
1083 attr = expanded_attr;
1084 }
1085
1086 // Get and validate operation
1087 op = crm_element_value(expression, PCMK_XA_OPERATION);
1088 comparison = pcmk__parse_comparison(op);
1089 if (comparison == pcmk__comparison_unknown) {
1090 // Not possible with schema validation enabled
1091 if (op == NULL) {
1092 pcmk__config_err("Treating " PCMK_XE_EXPRESSION " %s as not "
1093 "passing because it has no " PCMK_XA_OPERATION,
1094 id);
1095 } else {
1096 pcmk__config_err("Treating " PCMK_XE_EXPRESSION " %s as not "
1097 "passing because '%s' is not a valid "
1098 PCMK_XA_OPERATION, id, op);
1099 }
1101 goto done;
1102 }
1103
1104 // How reference value is obtained (literal, resource meta-attribute, etc.)
1105 source_s = crm_element_value(expression, PCMK_XA_VALUE_SOURCE);
1106 source = pcmk__parse_source(source_s);
1107 if (source == pcmk__source_unknown) {
1108 // Not possible with schema validation enabled
1109 // @COMPAT Fail expression once we can break backward compatibility
1110 pcmk__config_warn("Expression %s has invalid " PCMK_XA_VALUE_SOURCE
1111 " value '%s', using default "
1112 "('" PCMK_VALUE_LITERAL "')", id, source_s);
1113 source = pcmk__source_literal;
1114 }
1115
1116 // Get and validate reference value
1117 value = crm_element_value(expression, PCMK_XA_VALUE);
1118 switch (comparison) {
1121 if (value != NULL) {
1122 pcmk__config_warn("Ignoring " PCMK_XA_VALUE " in "
1123 PCMK_XE_EXPRESSION " %s because it is unused "
1124 "when " PCMK_XA_BOOLEAN_OP " is %s", id, op);
1125 }
1126 break;
1127
1128 default:
1129 if (value == NULL) {
1131 PCMK_XA_VALUE, id);
1132 }
1133 break;
1134 }
1135 reference = value_from_source(value, source, rule_input);
1136
1137 // Get actual value of node attribute
1138 if (rule_input->node_attrs != NULL) {
1139 actual = g_hash_table_lookup(rule_input->node_attrs, attr);
1140 }
1141
1142 // Get and validate value type (after expanding reference value)
1143 type_s = crm_element_value(expression, PCMK_XA_TYPE);
1144 type = pcmk__parse_type(type_s, comparison, actual, reference);
1145 if (type == pcmk__type_unknown) {
1146 /* Not possible with schema validation enabled
1147 *
1148 * @COMPAT When we can break behavioral backward compatibility, treat
1149 * the expression as not passing.
1150 */
1151 pcmk__config_warn("Non-empty node attribute values will be treated as "
1152 "equal for " PCMK_XE_EXPRESSION " %s because '%s' "
1153 "is not a valid type", id, type_s);
1154 }
1155
1156 rc = evaluate_attr_comparison(actual, reference, type, comparison);
1157 switch (comparison) {
1160 crm_trace(PCMK_XE_EXPRESSION " %s result: %s (for attribute %s %s)",
1161 id, pcmk_rc_str(rc), attr, op);
1162 break;
1163
1164 default:
1165 crm_trace(PCMK_XE_EXPRESSION " %s result: "
1166 "%s (attribute %s %s '%s' via %s source as %s type)",
1167 id, pcmk_rc_str(rc), attr, op, pcmk__s(reference, ""),
1168 pcmk__s(source_s, "default"), pcmk__s(type_s, "default"));
1169 break;
1170 }
1171
1172done:
1173 free(expanded_attr);
1174 return rc;
1175}
1176
1187int
1188pcmk__evaluate_rsc_expression(const xmlNode *rsc_expression,
1189 const pcmk_rule_input_t *rule_input)
1190{
1191 const char *id = NULL;
1192 const char *standard = NULL;
1193 const char *provider = NULL;
1194 const char *type = NULL;
1195
1196 if ((rsc_expression == NULL) || (rule_input == NULL)) {
1197 return EINVAL;
1198 }
1199
1200 // Validate XML ID
1201 id = pcmk__xe_id(rsc_expression);
1202 if (pcmk__str_empty(id)) {
1203 // Not possible with schema validation enabled
1204 /* @COMPAT When we can break behavioral backward compatibility,
1205 * fail the expression
1206 */
1208 id = "without ID"; // for logging
1209 }
1210
1211 // Compare resource standard
1212 standard = crm_element_value(rsc_expression, PCMK_XA_CLASS);
1213 if ((standard != NULL)
1214 && !pcmk__str_eq(standard, rule_input->rsc_standard, pcmk__str_none)) {
1215 crm_trace(PCMK_XE_RSC_EXPRESSION " %s is unsatisfied because "
1216 "actual standard '%s' doesn't match '%s'",
1217 id, pcmk__s(rule_input->rsc_standard, ""), standard);
1219 }
1220
1221 // Compare resource provider
1222 provider = crm_element_value(rsc_expression, PCMK_XA_PROVIDER);
1223 if ((provider != NULL)
1224 && !pcmk__str_eq(provider, rule_input->rsc_provider, pcmk__str_none)) {
1225 crm_trace(PCMK_XE_RSC_EXPRESSION " %s is unsatisfied because "
1226 "actual provider '%s' doesn't match '%s'",
1227 id, pcmk__s(rule_input->rsc_provider, ""), provider);
1229 }
1230
1231 // Compare resource agent type
1232 type = crm_element_value(rsc_expression, PCMK_XA_TYPE);
1233 if ((type != NULL)
1234 && !pcmk__str_eq(type, rule_input->rsc_agent, pcmk__str_none)) {
1235 crm_trace(PCMK_XE_RSC_EXPRESSION " %s is unsatisfied because "
1236 "actual agent '%s' doesn't match '%s'",
1237 id, pcmk__s(rule_input->rsc_agent, ""), type);
1239 }
1240
1241 crm_trace(PCMK_XE_RSC_EXPRESSION " %s is satisfied by %s%s%s:%s",
1242 id, pcmk__s(standard, ""),
1243 ((provider == NULL)? "" : ":"), pcmk__s(provider, ""),
1244 pcmk__s(type, ""));
1245 return pcmk_rc_ok;
1246}
1247
1258int
1259pcmk__evaluate_op_expression(const xmlNode *op_expression,
1260 const pcmk_rule_input_t *rule_input)
1261{
1262 const char *id = NULL;
1263 const char *name = NULL;
1264 const char *interval_s = NULL;
1265 guint interval_ms = 0U;
1266
1267 if ((op_expression == NULL) || (rule_input == NULL)) {
1268 return EINVAL;
1269 }
1270
1271 // Get operation expression ID (for logging)
1272 id = pcmk__xe_id(op_expression);
1273 if (pcmk__str_empty(id)) { // Not possible with schema validation enabled
1274 /* @COMPAT When we can break behavioral backward compatibility,
1275 * return pcmk_rc_op_unsatisfied
1276 */
1278 id = "without ID"; // for logging
1279 }
1280
1281 // Validate operation name
1282 name = crm_element_value(op_expression, PCMK_XA_NAME);
1283 if (name == NULL) { // Not possible with schema validation enabled
1284 pcmk__config_warn("Treating " PCMK_XE_OP_EXPRESSION " %s as not "
1285 "passing because it has no " PCMK_XA_NAME, id);
1286 return pcmk_rc_unpack_error;
1287 }
1288
1289 // Validate operation interval
1290 interval_s = crm_element_value(op_expression, PCMK_META_INTERVAL);
1291 if (pcmk_parse_interval_spec(interval_s, &interval_ms) != pcmk_rc_ok) {
1292 pcmk__config_warn("Treating " PCMK_XE_OP_EXPRESSION " %s as not "
1293 "passing because '%s' is not a valid interval",
1294 id, interval_s);
1295 return pcmk_rc_unpack_error;
1296 }
1297
1298 // Compare operation name
1299 if (!pcmk__str_eq(name, rule_input->op_name, pcmk__str_none)) {
1300 crm_trace(PCMK_XE_OP_EXPRESSION " %s is unsatisfied because "
1301 "actual name '%s' doesn't match '%s'",
1302 id, pcmk__s(rule_input->op_name, ""), name);
1304 }
1305
1306 // Compare operation interval (unspecified interval matches all)
1307 if ((interval_s != NULL) && (interval_ms != rule_input->op_interval_ms)) {
1308 crm_trace(PCMK_XE_OP_EXPRESSION " %s is unsatisfied because "
1309 "actual interval %s doesn't match %s",
1310 id, pcmk__readable_interval(rule_input->op_interval_ms),
1311 pcmk__readable_interval(interval_ms));
1313 }
1314
1315 crm_trace(PCMK_XE_OP_EXPRESSION " %s is satisfied (name %s, interval %s)",
1316 id, name, pcmk__readable_interval(rule_input->op_interval_ms));
1317 return pcmk_rc_ok;
1318}
1319
1332int
1333pcmk__evaluate_condition(xmlNode *condition,
1334 const pcmk_rule_input_t *rule_input,
1335 crm_time_t *next_change)
1336{
1337
1338 if ((condition == NULL) || (rule_input == NULL)) {
1339 return EINVAL;
1340 }
1341
1342 switch (pcmk__condition_type(condition)) {
1344 return pcmk_evaluate_rule(condition, rule_input, next_change);
1345
1348 return pcmk__evaluate_attr_expression(condition, rule_input);
1349
1351 {
1352 int rc = pcmk__evaluate_date_expression(condition,
1353 rule_input->now,
1354 next_change);
1355
1356 return (rc == pcmk_rc_within_range)? pcmk_rc_ok : rc;
1357 }
1358
1360 return pcmk__evaluate_rsc_expression(condition, rule_input);
1361
1363 return pcmk__evaluate_op_expression(condition, rule_input);
1364
1365 default: // Not possible with schema validation enabled
1366 pcmk__config_err("Treating rule condition %s as not passing "
1367 "because %s is not a valid condition type",
1368 pcmk__s(pcmk__xe_id(condition), "without ID"),
1369 (const char *) condition->name);
1370 return pcmk_rc_unpack_error;
1371 }
1372}
1373
1384int
1385pcmk_evaluate_rule(xmlNode *rule, const pcmk_rule_input_t *rule_input,
1386 crm_time_t *next_change)
1387{
1388 bool empty = true;
1389 int rc = pcmk_rc_ok;
1390 const char *id = NULL;
1391 const char *value = NULL;
1392 enum pcmk__combine combine = pcmk__combine_unknown;
1393
1394 if ((rule == NULL) || (rule_input == NULL)) {
1395 return EINVAL;
1396 }
1397
1398 rule = expand_idref(rule, NULL);
1399 if (rule == NULL) {
1400 // Not possible with schema validation enabled; message already logged
1401 return pcmk_rc_unpack_error;
1402 }
1403
1404 // Validate XML ID
1405 id = pcmk__xe_id(rule);
1406 if (pcmk__str_empty(id)) {
1407 /* @COMPAT When we can break behavioral backward compatibility,
1408 * fail the rule
1409 */
1411 id = "without ID"; // for logging
1412 }
1413
1415 combine = pcmk__parse_combine(value);
1416 switch (combine) {
1417 case pcmk__combine_and:
1418 // For "and", rc defaults to success (reset on failure below)
1419 break;
1420
1421 case pcmk__combine_or:
1422 // For "or", rc defaults to failure (reset on success below)
1424 break;
1425
1426 default:
1427 /* @COMPAT When we can break behavioral backward compatibility,
1428 * return pcmk_rc_unpack_error
1429 */
1430 pcmk__config_warn("Rule %s has invalid " PCMK_XA_BOOLEAN_OP
1431 " value '%s', using default '" PCMK_VALUE_AND "'",
1432 pcmk__xe_id(rule), value);
1433 combine = pcmk__combine_and;
1434 break;
1435 }
1436
1437 // Evaluate each condition
1438 for (xmlNode *condition = pcmk__xe_first_child(rule, NULL, NULL, NULL);
1439 condition != NULL; condition = pcmk__xe_next(condition)) {
1440
1441 empty = false;
1442 if (pcmk__evaluate_condition(condition, rule_input,
1443 next_change) == pcmk_rc_ok) {
1444 if (combine == pcmk__combine_or) {
1445 rc = pcmk_rc_ok; // Any pass is final for "or"
1446 break;
1447 }
1448 } else if (combine == pcmk__combine_and) {
1449 rc = pcmk_rc_op_unsatisfied; // Any failure is final for "and"
1450 break;
1451 }
1452 }
1453
1454 if (empty) { // Not possible with schema validation enabled
1455 /* @COMPAT Currently, we don't actually ignore "or" rules because
1456 * rc is initialized to failure above in that case. When we can break
1457 * backward compatibility, reset rc to pcmk_rc_ok here.
1458 */
1459 pcmk__config_warn("Ignoring rule %s because it contains no conditions",
1460 id);
1461 }
1462
1463 crm_trace("Rule %s is %ssatisfied", id, ((rc == pcmk_rc_ok)? "" : "not "));
1464 return rc;
1465}
1466
1485int
1486pcmk__evaluate_rules(xmlNode *xml, const pcmk_rule_input_t *rule_input,
1487 crm_time_t *next_change)
1488{
1489 // If there are no rules, pass by default
1490 int rc = pcmk_rc_ok;
1491 bool have_rule = false;
1492
1493 for (xmlNode *rule = pcmk__xe_first_child(xml, PCMK_XE_RULE, NULL, NULL);
1494 rule != NULL; rule = pcmk__xe_next_same(rule)) {
1495
1496 if (have_rule) {
1498 "Support for multiple top-level rules is "
1499 "deprecated (replace with a single rule containing "
1500 "the existing rules with " PCMK_XA_BOOLEAN_OP
1501 "set to " PCMK_VALUE_OR " instead)");
1502 } else {
1503 have_rule = true;
1504 }
1505
1506 rc = pcmk_evaluate_rule(rule, rule_input, next_change);
1507 if (rc == pcmk_rc_ok) {
1508 break;
1509 }
1510 }
1511 return rc;
1512}
const char * name
Definition cib.c:26
#define PCMK__NELEM(a)
Definition internal.h:48
#define pcmk__assert_alloc(nmemb, size)
Definition internal.h:297
int pcmk__unpack_duration(const xmlNode *duration, const crm_time_t *start, crm_time_t **end)
Definition rules.c:292
int pcmk_evaluate_rule(xmlNode *rule, const pcmk_rule_input_t *rule_input, crm_time_t *next_change)
Evaluate a single rule, including all its conditions.
Definition rules.c:1385
#define ADD_COMPONENT(component)
Definition rules.c:268
int pcmk__evaluate_rules(xmlNode *xml, const pcmk_rule_input_t *rule_input, crm_time_t *next_change)
Definition rules.c:1486
enum pcmk__reference_source pcmk__parse_source(const char *source)
Definition rules.c:876
int pcmk__evaluate_date_spec(const xmlNode *date_spec, const crm_time_t *now)
Definition rules.c:193
int pcmk__evaluate_date_expression(const xmlNode *date_expression, const crm_time_t *now, crm_time_t *next_change)
Definition rules.c:534
enum expression_type pcmk__condition_type(const xmlNode *condition)
Definition rules.c:37
enum pcmk__comparison pcmk__parse_comparison(const char *op)
Definition rules.c:723
int pcmk__evaluate_attr_expression(const xmlNode *expression, const pcmk_rule_input_t *rule_input)
Definition rules.c:1038
int pcmk__evaluate_rsc_expression(const xmlNode *rsc_expression, const pcmk_rule_input_t *rule_input)
Definition rules.c:1188
int pcmk__evaluate_op_expression(const xmlNode *op_expression, const pcmk_rule_input_t *rule_input)
Definition rules.c:1259
enum pcmk__type pcmk__parse_type(const char *type, enum pcmk__comparison op, const char *value1, const char *value2)
Definition rules.c:765
char * pcmk__replace_submatches(const char *string, const char *match, const regmatch_t submatches[], int nmatches)
Definition rules.c:685
int pcmk__evaluate_condition(xmlNode *condition, const pcmk_rule_input_t *rule_input, crm_time_t *next_change)
Definition rules.c:1333
enum pcmk__combine pcmk__parse_combine(const char *combine)
Definition rules.c:902
int pcmk__cmp_by_type(const char *value1, const char *value2, enum pcmk__type type)
Definition rules.c:814
expression_type
Definition rules.h:33
@ pcmk__condition_rule
Definition rules.h:35
@ pcmk__condition_location
Definition rules.h:37
@ pcmk__condition_unknown
Definition rules.h:34
@ pcmk__condition_attribute
Definition rules.h:36
@ pcmk__condition_datetime
Definition rules.h:38
@ pcmk__condition_resource
Definition rules.h:39
@ pcmk__condition_operation
Definition rules.h:40
pcmk__combine
@ pcmk__combine_or
@ pcmk__combine_unknown
@ pcmk__combine_and
char int pcmk_parse_interval_spec(const char *input, guint *result_ms)
Parse milliseconds from a Pacemaker interval specification.
Definition strings.c:451
int compare_version(const char *version1, const char *version2)
Definition utils.c:188
enum crm_ais_msg_types type
Definition cpg.c:3
#define CRM_ATTR_KIND
Definition crm.h:101
#define CRM_ATTR_UNAME
Definition crm.h:99
#define CRM_ATTR_ID
Definition crm.h:100
G_GNUC_INTERNAL void pcmk__set_time_if_earlier(crm_time_t *target, const crm_time_t *source)
Definition iso8601.c:1414
@ pcmk__type_integer
@ pcmk__type_unknown
@ pcmk__type_version
@ pcmk__type_number
@ pcmk__type_string
pcmk__reference_source
@ pcmk__source_unknown
@ pcmk__source_literal
@ pcmk__source_meta_attrs
@ pcmk__source_instance_attrs
pcmk__comparison
@ pcmk__comparison_gt
@ pcmk__comparison_lte
@ pcmk__comparison_unknown
@ pcmk__comparison_ne
@ pcmk__comparison_eq
@ pcmk__comparison_defined
@ pcmk__comparison_gte
@ pcmk__comparison_undefined
@ pcmk__comparison_lt
@ pcmk__time_hours
@ pcmk__time_minutes
@ pcmk__time_seconds
@ pcmk__time_days
@ pcmk__time_years
@ pcmk__time_months
@ pcmk__time_weeks
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:1719
void crm_time_free(crm_time_t *dt)
Definition iso8601.c:150
int crm_time_get_ordinal(const crm_time_t *dt, uint32_t *y, uint32_t *d)
Definition iso8601.c:398
crm_time_t * pcmk_copy_time(const crm_time_t *source)
Definition iso8601.c:1424
int crm_time_get_timeofday(const crm_time_t *dt, uint32_t *h, uint32_t *m, uint32_t *s)
Definition iso8601.c:299
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:365
int crm_time_compare(const crm_time_t *a, const crm_time_t *b)
Definition iso8601.c:1689
int crm_time_get_isoweek(const crm_time_t *dt, uint32_t *y, uint32_t *w, uint32_t *d)
Definition iso8601.c:406
const char * pcmk__readable_interval(guint interval_ms)
Definition iso8601.c:2134
#define crm_warn(fmt, args...)
Definition logging.h:392
#define crm_trace(fmt, args...)
Definition logging.h:402
#define pcmk__config_warn(fmt...)
#define pcmk__config_err(fmt...)
@ pcmk__wo_multiple_rules
#define pcmk__warn_once(wo_flag, fmt...)
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:446
int pcmk__xe_get_datetime(const xmlNode *xml, const char *attr, crm_time_t **t)
Definition nvpair.c:644
#define PCMK_VALUE_NOT_DEFINED
Definition options.h:180
#define PCMK_META_INTERVAL
Definition options.h:91
#define PCMK_VALUE_NE
Definition options.h:176
#define PCMK_VALUE_INTEGER
Definition options.h:164
#define PCMK_VALUE_IN_RANGE
Definition options.h:162
#define PCMK_VALUE_LT
Definition options.h:166
#define PCMK_VALUE_META
Definition options.h:170
#define PCMK_VALUE_EQ
Definition options.h:149
#define PCMK_VALUE_GTE
Definition options.h:159
#define PCMK_VALUE_DEFINED
Definition options.h:143
#define PCMK_VALUE_AND
Definition options.h:133
#define PCMK_VALUE_OR
Definition options.h:187
#define PCMK_VALUE_GT
Definition options.h:158
#define PCMK_VALUE_PARAM
Definition options.h:189
#define PCMK_VALUE_NUMBER
Definition options.h:182
#define PCMK_VALUE_DATE_SPEC
Definition options.h:141
#define PCMK_VALUE_LITERAL
Definition options.h:165
#define PCMK_VALUE_VERSION
Definition options.h:219
#define PCMK_VALUE_STRING
Definition options.h:208
#define PCMK_VALUE_LTE
Definition options.h:167
pcmk__action_result_t result
Definition pcmk_fence.c:35
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:501
@ pcmk_rc_before_range
Definition results.h:134
@ pcmk_rc_op_unsatisfied
Definition results.h:136
@ pcmk_rc_ok
Definition results.h:162
@ pcmk_rc_undetermined
Definition results.h:135
@ pcmk_rc_within_range
Definition results.h:133
@ pcmk_rc_after_range
Definition results.h:132
@ pcmk_rc_unpack_error
Definition results.h:125
Scheduler API.
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
@ 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:1050
int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
Definition strings.c:903
Data used to evaluate a rule (any NULL items are ignored)
Definition rules.h:57
const char * rsc_standard
Resource standard that rule applies to.
Definition rules.h:62
const char * op_name
Definition rules.h:67
const char * rsc_provider
Resource provider that rule applies to.
Definition rules.h:63
const regmatch_t * rsc_id_submatches
Resource pattern submatches (as set by regexec()) for rsc_id.
Definition rules.h:99
int rsc_id_nmatches
Number of entries in rsc_id_submatches.
Definition rules.h:102
GHashTable * node_attrs
Operation interval that rule applies to.
Definition rules.h:77
GHashTable * rsc_meta
Definition rules.h:93
const crm_time_t * now
Current time for rule evaluation purposes.
Definition rules.h:59
const char * rsc_agent
Resource agent that rule applies to.
Definition rules.h:64
const char * rsc_id
Resource ID to compare against a location constraint's resource pattern.
Definition rules.h:96
guint op_interval_ms
Operation name that rule applies to.
Definition rules.h:68
GHashTable * rsc_params
Definition rules.h:86
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition xml.c:2152
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition xml.c:440
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition xml.c:2108
#define PCMK_XA_VALUE_SOURCE
Definition xml_names.h:438
#define PCMK_XA_START
Definition xml_names.h:403
#define PCMK_XA_CLASS
Definition xml_names.h:241
#define PCMK_XA_MONTHS
Definition xml_names.h:323
#define PCMK_XE_EXPRESSION
Definition xml_names.h:106
#define PCMK_XA_WEEKDAYS
Definition xml_names.h:441
#define PCMK_XE_RULE
Definition xml_names.h:187
#define PCMK_XA_YEARS
Definition xml_names.h:451
#define PCMK_XA_END
Definition xml_names.h:262
#define PCMK_XA_OPERATION
Definition xml_names.h:344
#define PCMK_XA_MINUTES
Definition xml_names.h:320
#define PCMK_XE_OP_EXPRESSION
Definition xml_names.h:145
#define PCMK_XA_ID
Definition xml_names.h:296
#define PCMK_XE_DATE_SPEC
Definition xml_names.h:96
#define PCMK_XA_SECONDS
Definition xml_names.h:394
#define PCMK_XA_PROVIDER
Definition xml_names.h:359
#define PCMK_XA_BOOLEAN_OP
Definition xml_names.h:235
#define PCMK_XA_WEEKS
Definition xml_names.h:442
#define PCMK_XE_RSC_EXPRESSION
Definition xml_names.h:183
#define PCMK_XA_VALUE
Definition xml_names.h:437
#define PCMK_XA_YEARDAYS
Definition xml_names.h:450
#define PCMK_XA_ATTRIBUTE
Definition xml_names.h:231
#define PCMK_XA_TYPE
Definition xml_names.h:425
#define PCMK_XA_WEEKYEARS
Definition xml_names.h:443
#define PCMK_XE_OPERATION
Definition xml_names.h:146
#define PCMK_XA_MONTHDAYS
Definition xml_names.h:322
#define PCMK_XE_DATE_EXPRESSION
Definition xml_names.h:95
#define PCMK_XA_NAME
Definition xml_names.h:325
#define PCMK_XE_DURATION
Definition xml_names.h:103
#define PCMK_XA_HOURS
Definition xml_names.h:295
#define PCMK__XA_MOON