pacemaker  2.1.8-3980678f03
Scalable High-Availability cluster resource manager
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 
21 #include <crm/common/scheduler.h>
22 
26 #include "crmcommon_private.h"
27 
36 enum expression_type
37 pcmk__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)) {
53  return pcmk__condition_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 
79 static const char *
80 loggable_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 
104 static int
105 phase_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 
146 static int
147 check_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)) {
171  rc = pcmk_rc_after_range;
172  }
173 
174 bail:
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 
192 int
193 pcmk__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 "
227  PCMK_XE_DATE_EXPRESSION " %s has no " PCMK_XA_ID,
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 
291 int
292 pcmk__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 "
311  PCMK_XE_DATE_EXPRESSION " %s has no " PCMK_XA_ID,
312  parent_id);
313  id = "without ID";
314  }
315 
316  *end = pcmk_copy_time(start);
317 
325 
326  return rc;
327 }
328 
342 static int
343 evaluate_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);
377  return pcmk_rc_undetermined;
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);
396  return pcmk_rc_before_range;
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);
415  return pcmk_rc_within_range;
416 }
417 
431 static int
432 evaluate_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);
445  return pcmk_rc_undetermined;
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);
455  return pcmk_rc_undetermined;
456  }
457 
458  if (crm_time_compare(now, start) > 0) {
459  crm_time_free(start);
460  return pcmk_rc_within_range;
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);
467  return pcmk_rc_before_range;
468 }
469 
483 static int
484 evaluate_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);
496  return pcmk_rc_undetermined;
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);
506  return pcmk_rc_undetermined;
507  }
508 
509  if (crm_time_compare(now, end) < 0) {
510  pcmk__set_time_if_earlier(next_change, end);
511  crm_time_free(end);
512  return pcmk_rc_within_range;
513  }
514 
515  crm_time_free(end);
516  return pcmk_rc_after_range;
517 }
518 
533 int
534 pcmk__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  */
551  pcmk__config_warn(PCMK_XE_DATE_EXPRESSION " element has no "
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  */
570  pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION " %s "
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 
615 static bool
616 process_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 
684 char *
685 pcmk__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 
722 enum pcmk__comparison
723 pcmk__parse_comparison(const char *op)
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)) {
741  return pcmk__comparison_lte;
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)) {
747  return pcmk__comparison_gte;
748  }
749 
751 }
752 
764 enum pcmk__type
766  const char *value1, const char *value2)
767 {
768  if (type == NULL) {
769  switch (op) {
770  case pcmk__comparison_lt:
772  case pcmk__comparison_gt:
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 
813 int
814 pcmk__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) {
825  case pcmk__type_string:
826  return strcasecmp(value1, value2);
827 
828  case pcmk__type_integer:
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 
843  case pcmk__type_number:
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 
859  case pcmk__type_version:
860  return compare_version(value1, value2);
861 
862  default: // Invalid type
863  return 0;
864  }
865 }
866 
876 pcmk__parse_source(const char *source)
877 {
878  if (pcmk__str_eq(source, PCMK_VALUE_LITERAL,
880  return pcmk__source_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 {
889  return pcmk__source_unknown;
890  }
891 }
892 
901 enum pcmk__combine
902 pcmk__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 {
912  return pcmk__combine_unknown;
913  }
914 }
915 
930 static int
931 evaluate_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) {
950  case pcmk__comparison_eq:
951  return (cmp == 0)? pcmk_rc_ok : pcmk_rc_op_unsatisfied;
952 
953  case pcmk__comparison_ne:
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) {
965  case pcmk__comparison_lt:
966  return (cmp < 0)? pcmk_rc_ok : pcmk_rc_after_range;
967 
969  return (cmp <= 0)? pcmk_rc_ok : pcmk_rc_after_range;
970 
971  case pcmk__comparison_gt:
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
978  return pcmk_rc_op_unsatisfied;
979  }
980 }
981 
990 static const char *
991 value_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) {
1006  case pcmk__source_literal:
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 
1037 int
1038 pcmk__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 
1054  enum pcmk__comparison comparison = pcmk__comparison_unknown;
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  */
1066  pcmk__config_warn(PCMK_XE_EXPRESSION " element has no " PCMK_XA_ID);
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  }
1100  rc = pcmk_rc_unpack_error;
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 
1172 done:
1173  free(expanded_attr);
1174  return rc;
1175 }
1176 
1187 int
1188 pcmk__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);
1218  return pcmk_rc_op_unsatisfied;
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);
1228  return pcmk_rc_op_unsatisfied;
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);
1238  return pcmk_rc_op_unsatisfied;
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 
1258 int
1259 pcmk__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);
1303  return pcmk_rc_op_unsatisfied;
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));
1312  return pcmk_rc_op_unsatisfied;
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 
1332 int
1333 pcmk__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)) {
1343  case pcmk__condition_rule:
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 
1384 int
1385 pcmk_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 
1414  value = crm_element_value(rule, PCMK_XA_BOOLEAN_OP);
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 
1485 int
1486 pcmk__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 }
#define PCMK_XA_YEARS
Definition: xml_names.h:451
enum pcmk__type pcmk__parse_type(const char *type, enum pcmk__comparison op, const char *value1, const char *value2)
Definition: rules.c:765
#define PCMK_VALUE_NUMBER
Definition: options.h:182
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
GHashTable * rsc_meta
Definition: rules.h:93
#define PCMK_XA_HOURS
Definition: xml_names.h:295
int crm_time_get_isoweek(const crm_time_t *dt, uint32_t *y, uint32_t *w, uint32_t *d)
Definition: iso8601.c:406
#define PCMK_XA_NAME
Definition: xml_names.h:325
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
const crm_time_t * now
Current time for rule evaluation purposes.
Definition: rules.h:59
Data used to evaluate a rule (any NULL items are ignored)
Definition: rules.h:57
int pcmk__xe_get_datetime(const xmlNode *xml, const char *attr, crm_time_t **t)
Definition: nvpair.c:644
pcmk__comparison
#define CRM_ATTR_KIND
Definition: crm.h:101
GHashTable * rsc_params
Definition: rules.h:86
#define PCMK_XA_YEARDAYS
Definition: xml_names.h:450
enum expression_type pcmk__condition_type(const xmlNode *condition)
Definition: rules.c:37
int pcmk__evaluate_attr_expression(const xmlNode *expression, const pcmk_rule_input_t *rule_input)
Definition: rules.c:1038
const char * rsc_provider
Resource provider that rule applies to.
Definition: rules.h:63
const char * name
Definition: cib.c:26
struct crm_time_s crm_time_t
Definition: iso8601.h:32
#define PCMK_VALUE_DATE_SPEC
Definition: options.h:141
#define pcmk__config_warn(fmt...)
#define PCMK_VALUE_OR
Definition: options.h:187
#define PCMK_XA_WEEKYEARS
Definition: xml_names.h:443
pcmk__reference_source
#define PCMK_VALUE_NOT_DEFINED
Definition: options.h:180
#define PCMK_XA_WEEKDAYS
Definition: xml_names.h:441
const char * op_name
Definition: rules.h:67
#define pcmk__config_err(fmt...)
enum pcmk__comparison pcmk__parse_comparison(const char *op)
Definition: rules.c:723
#define PCMK_XE_DURATION
Definition: xml_names.h:103
crm_time_t * pcmk_copy_time(const crm_time_t *source)
Definition: iso8601.c:1424
#define PCMK_XE_EXPRESSION
Definition: xml_names.h:106
pcmk__type
#define PCMK_VALUE_AND
Definition: options.h:133
#define PCMK_XA_PROVIDER
Definition: xml_names.h:359
int rsc_id_nmatches
Number of entries in rsc_id_submatches.
Definition: rules.h:102
expression_type
Definition: rules.h:33
enum crm_ais_msg_types type
Definition: cpg.c:51
int pcmk__unpack_duration(const xmlNode *duration, const crm_time_t *start, crm_time_t **end)
Definition: rules.c:292
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:501
int crm_time_get_ordinal(const crm_time_t *dt, uint32_t *y, uint32_t *d)
Definition: iso8601.c:398
#define PCMK_XA_TYPE
Definition: xml_names.h:425
#define PCMK_XA_OPERATION
Definition: xml_names.h:344
#define PCMK_XA_START
Definition: xml_names.h:403
const char * rsc_agent
Resource agent that rule applies to.
Definition: rules.h:64
#define PCMK_VALUE_EQ
Definition: options.h:149
enum pcmk__reference_source pcmk__parse_source(const char *source)
Definition: rules.c:876
Scheduler API.
pcmk__combine
char int pcmk_parse_interval_spec(const char *input, guint *result_ms)
Parse milliseconds from a Pacemaker interval specification.
Definition: strings.c:451
#define PCMK__XA_MOON
#define PCMK_VALUE_META
Definition: options.h:170
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition: strings.c:97
#define crm_warn(fmt, args...)
Definition: logging.h:394
int pcmk__evaluate_op_expression(const xmlNode *op_expression, const pcmk_rule_input_t *rule_input)
Definition: rules.c:1259
#define PCMK_XE_OP_EXPRESSION
Definition: xml_names.h:145
#define PCMK_VALUE_LTE
Definition: options.h:167
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:446
#define PCMK_VALUE_PARAM
Definition: options.h:189
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:440
#define PCMK_XE_DATE_EXPRESSION
Definition: xml_names.h:95
#define PCMK_VALUE_LITERAL
Definition: options.h:165
#define PCMK_XE_DATE_SPEC
Definition: xml_names.h:96
#define CRM_ATTR_UNAME
Definition: crm.h:99
#define crm_trace(fmt, args...)
Definition: logging.h:404
#define PCMK_XA_WEEKS
Definition: xml_names.h:442
int pcmk__evaluate_date_spec(const xmlNode *date_spec, const crm_time_t *now)
Definition: rules.c:193
guint op_interval_ms
Operation name that rule applies to.
Definition: rules.h:68
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2152
#define PCMK_VALUE_GTE
Definition: options.h:159
#define PCMK__NELEM(a)
Definition: internal.h:48
enum pcmk__combine pcmk__parse_combine(const char *combine)
Definition: rules.c:902
const char * rsc_standard
Resource standard that rule applies to.
Definition: rules.h:62
#define PCMK_VALUE_IN_RANGE
Definition: options.h:162
#define PCMK_XA_ID
Definition: xml_names.h:296
#define PCMK_XA_VALUE
Definition: xml_names.h:437
int pcmk__evaluate_condition(xmlNode *condition, const pcmk_rule_input_t *rule_input, crm_time_t *next_change)
Definition: rules.c:1333
int crm_time_get_gregorian(const crm_time_t *dt, uint32_t *y, uint32_t *m, uint32_t *d)
Definition: iso8601.c:365
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1050
#define PCMK_XE_RSC_EXPRESSION
Definition: xml_names.h:183
#define PCMK_XA_MINUTES
Definition: xml_names.h:320
int pcmk__scan_double(const char *text, double *result, const char *default_text, char **end_text)
Definition: strings.c:199
#define pcmk__warn_once(wo_flag, fmt...)
#define PCMK_XE_RULE
Definition: xml_names.h:187
int crm_time_compare(const crm_time_t *a, const crm_time_t *b)
Definition: iso8601.c:1689
#define PCMK_XA_MONTHDAYS
Definition: xml_names.h:322
int pcmk__evaluate_date_expression(const xmlNode *date_expression, const crm_time_t *now, crm_time_t *next_change)
Definition: rules.c:534
#define PCMK_XA_SECONDS
Definition: xml_names.h:394
#define PCMK_XA_CLASS
Definition: xml_names.h:241
#define PCMK_META_INTERVAL
Definition: options.h:91
pcmk__action_result_t result
Definition: pcmk_fence.c:35
char * pcmk__replace_submatches(const char *string, const char *match, const regmatch_t submatches[], int nmatches)
Definition: rules.c:685
G_GNUC_INTERNAL void pcmk__set_time_if_earlier(crm_time_t *target, const crm_time_t *source)
Definition: iso8601.c:1414
const regmatch_t * rsc_id_submatches
Resource pattern submatches (as set by regexec()) for rsc_id.
Definition: rules.h:99
#define PCMK_XE_OPERATION
Definition: xml_names.h:146
int compare_version(const char *version1, const char *version2)
Definition: utils.c:188
#define PCMK_XA_ATTRIBUTE
Definition: xml_names.h:231
#define PCMK_VALUE_GT
Definition: options.h:158
int pcmk__evaluate_rules(xmlNode *xml, const pcmk_rule_input_t *rule_input, crm_time_t *next_change)
Definition: rules.c:1486
#define PCMK_VALUE_VERSION
Definition: options.h:219
GHashTable * node_attrs
Operation interval that rule applies to.
Definition: rules.h:77
#define PCMK_VALUE_STRING
Definition: options.h:208
#define PCMK_VALUE_LT
Definition: options.h:166
#define PCMK_XA_BOOLEAN_OP
Definition: xml_names.h:235
int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
Definition: strings.c:903
const char * pcmk__readable_interval(guint interval_ms)
Definition: iso8601.c:2134
#define PCMK_VALUE_INTEGER
Definition: options.h:164
#define PCMK_VALUE_NE
Definition: options.h:176
#define PCMK_XA_END
Definition: xml_names.h:262
int pcmk__cmp_by_type(const char *value1, const char *value2, enum pcmk__type type)
Definition: rules.c:814
#define PCMK_VALUE_DEFINED
Definition: options.h:143
#define pcmk__assert_alloc(nmemb, size)
Definition: internal.h:297
#define CRM_ATTR_ID
Definition: crm.h:100
const char * rsc_id
Resource ID to compare against a location constraint&#39;s resource pattern.
Definition: rules.h:96
#define PCMK_XA_VALUE_SOURCE
Definition: xml_names.h:438
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition: xml.c:2108
int crm_time_get_timeofday(const crm_time_t *dt, uint32_t *h, uint32_t *m, uint32_t *s)
Definition: iso8601.c:299
#define ADD_COMPONENT(component)
Definition: rules.c:268
int pcmk__evaluate_rsc_expression(const xmlNode *rsc_expression, const pcmk_rule_input_t *rule_input)
Definition: rules.c:1188
#define PCMK_XA_MONTHS
Definition: xml_names.h:323
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:150