pacemaker  2.1.9-49aab99839
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: %s", \
274  pcmk__time_component_attr(component), id, \
275  pcmk_rc_str(sub_rc)); \
276  rc = sub_rc; \
277  } \
278  } while (0)
279 
292 int
293 pcmk__unpack_duration(const xmlNode *duration, const crm_time_t *start,
294  crm_time_t **end)
295 {
296  int rc = pcmk_rc_ok;
297  const char *id = NULL;
298  const char *parent_id = loggable_parent_id(duration);
299 
300  if ((start == NULL) || (duration == NULL)
301  || (end == NULL) || (*end != NULL)) {
302  return EINVAL;
303  }
304 
305  // Get duration ID (for logging)
306  id = pcmk__xe_id(duration);
307  if (pcmk__str_empty(id)) { // Not possible with schema validation enabled
308  /* @COMPAT When we can break behavioral backward compatibility,
309  * return pcmk_rc_unpack_error instead
310  */
311  pcmk__config_warn(PCMK_XE_DURATION " subelement of "
312  PCMK_XE_DATE_EXPRESSION " %s has no " PCMK_XA_ID,
313  parent_id);
314  id = "without ID";
315  }
316 
317  *end = pcmk_copy_time(start);
318 
326 
327  return rc;
328 }
329 
343 static int
344 evaluate_in_range(const xmlNode *date_expression, const char *id,
345  const crm_time_t *now, crm_time_t *next_change)
346 {
347  crm_time_t *start = NULL;
348  crm_time_t *end = NULL;
349 
350  if (pcmk__xe_get_datetime(date_expression, PCMK_XA_START,
351  &start) != pcmk_rc_ok) {
352  /* @COMPAT When we can break behavioral backward compatibility,
353  * return pcmk_rc_unpack_error
354  */
355  pcmk__config_warn("Ignoring " PCMK_XA_START " in "
356  PCMK_XE_DATE_EXPRESSION " %s because it is invalid",
357  id);
358  }
359 
360  if (pcmk__xe_get_datetime(date_expression, PCMK_XA_END,
361  &end) != pcmk_rc_ok) {
362  /* @COMPAT When we can break behavioral backward compatibility,
363  * return pcmk_rc_unpack_error
364  */
365  pcmk__config_warn("Ignoring " PCMK_XA_END " in "
366  PCMK_XE_DATE_EXPRESSION " %s because it is invalid",
367  id);
368  }
369 
370  if ((start == NULL) && (end == NULL)) {
371  // Not possible with schema validation enabled
372  /* @COMPAT When we can break behavioral backward compatibility,
373  * return pcmk_rc_unpack_error
374  */
375  pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION " %s as not "
376  "passing because in_range requires at least one of "
377  PCMK_XA_START " or " PCMK_XA_END, id);
378  return pcmk_rc_undetermined;
379  }
380 
381  if (end == NULL) {
382  xmlNode *duration = pcmk__xe_first_child(date_expression,
383  PCMK_XE_DURATION, NULL, NULL);
384 
385  if (duration != NULL) {
386  /* @COMPAT When we can break behavioral backward compatibility,
387  * return the result of this if not OK
388  */
389  pcmk__unpack_duration(duration, start, &end);
390  }
391  }
392 
393  if ((start != NULL) && (crm_time_compare(now, start) < 0)) {
394  pcmk__set_time_if_earlier(next_change, start);
395  crm_time_free(start);
396  crm_time_free(end);
397  return pcmk_rc_before_range;
398  }
399 
400  if (end != NULL) {
401  if (crm_time_compare(now, end) > 0) {
402  crm_time_free(start);
403  crm_time_free(end);
404  return pcmk_rc_after_range;
405  }
406 
407  // Evaluation doesn't change until second after end
408  if (next_change != NULL) {
409  crm_time_add_seconds(end, 1);
410  pcmk__set_time_if_earlier(next_change, end);
411  }
412  }
413 
414  crm_time_free(start);
415  crm_time_free(end);
416  return pcmk_rc_within_range;
417 }
418 
432 static int
433 evaluate_gt(const xmlNode *date_expression, const char *id,
434  const crm_time_t *now, crm_time_t *next_change)
435 {
436  crm_time_t *start = NULL;
437 
438  if (pcmk__xe_get_datetime(date_expression, PCMK_XA_START,
439  &start) != pcmk_rc_ok) {
440  /* @COMPAT When we can break behavioral backward compatibility,
441  * return pcmk_rc_unpack_error
442  */
443  pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION " %s as not "
444  "passing because " PCMK_XA_START " is invalid",
445  id);
446  return pcmk_rc_undetermined;
447  }
448 
449  if (start == NULL) { // Not possible with schema validation enabled
450  /* @COMPAT When we can break behavioral backward compatibility,
451  * return pcmk_rc_unpack_error
452  */
453  pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION " %s as not "
454  "passing because " PCMK_VALUE_GT " requires "
455  PCMK_XA_START, id);
456  return pcmk_rc_undetermined;
457  }
458 
459  if (crm_time_compare(now, start) > 0) {
460  crm_time_free(start);
461  return pcmk_rc_within_range;
462  }
463 
464  // Evaluation doesn't change until second after start time
465  crm_time_add_seconds(start, 1);
466  pcmk__set_time_if_earlier(next_change, start);
467  crm_time_free(start);
468  return pcmk_rc_before_range;
469 }
470 
484 static int
485 evaluate_lt(const xmlNode *date_expression, const char *id,
486  const crm_time_t *now, crm_time_t *next_change)
487 {
488  crm_time_t *end = NULL;
489 
490  if (pcmk__xe_get_datetime(date_expression, PCMK_XA_END,
491  &end) != pcmk_rc_ok) {
492  /* @COMPAT When we can break behavioral backward compatibility,
493  * return pcmk_rc_unpack_error
494  */
495  pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION " %s as not "
496  "passing because " PCMK_XA_END " is invalid", id);
497  return pcmk_rc_undetermined;
498  }
499 
500  if (end == NULL) { // Not possible with schema validation enabled
501  /* @COMPAT When we can break behavioral backward compatibility,
502  * return pcmk_rc_unpack_error
503  */
504  pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION " %s as not "
505  "passing because " PCMK_VALUE_GT " requires "
506  PCMK_XA_END, id);
507  return pcmk_rc_undetermined;
508  }
509 
510  if (crm_time_compare(now, end) < 0) {
511  pcmk__set_time_if_earlier(next_change, end);
512  crm_time_free(end);
513  return pcmk_rc_within_range;
514  }
515 
516  crm_time_free(end);
517  return pcmk_rc_after_range;
518 }
519 
534 int
535 pcmk__evaluate_date_expression(const xmlNode *date_expression,
536  const crm_time_t *now, crm_time_t *next_change)
537 {
538  const char *id = NULL;
539  const char *op = NULL;
540  int rc = pcmk_rc_undetermined;
541 
542  if ((date_expression == NULL) || (now == NULL)) {
543  return EINVAL;
544  }
545 
546  // Get expression ID (for logging)
547  id = pcmk__xe_id(date_expression);
548  if (pcmk__str_empty(id)) { // Not possible with schema validation enabled
549  /* @COMPAT When we can break behavioral backward compatibility,
550  * return pcmk_rc_unpack_error
551  */
552  pcmk__config_warn(PCMK_XE_DATE_EXPRESSION " element has no "
553  PCMK_XA_ID);
554  id = "without ID"; // for logging
555  }
556 
557  op = crm_element_value(date_expression, PCMK_XA_OPERATION);
558  if (pcmk__str_eq(op, PCMK_VALUE_IN_RANGE,
560  rc = evaluate_in_range(date_expression, id, now, next_change);
561 
562  } else if (pcmk__str_eq(op, PCMK_VALUE_DATE_SPEC, pcmk__str_casei)) {
563  xmlNode *date_spec = pcmk__xe_first_child(date_expression,
564  PCMK_XE_DATE_SPEC, NULL,
565  NULL);
566 
567  if (date_spec == NULL) { // Not possible with schema validation enabled
568  /* @COMPAT When we can break behavioral backward compatibility,
569  * return pcmk_rc_unpack_error
570  */
571  pcmk__config_warn("Treating " PCMK_XE_DATE_EXPRESSION " %s "
572  "as not passing because " PCMK_VALUE_DATE_SPEC
573  " operations require a " PCMK_XE_DATE_SPEC
574  " subelement", id);
575  } else {
576  // @TODO set next_change appropriately
577  rc = pcmk__evaluate_date_spec(date_spec, now);
578  }
579 
580  } else if (pcmk__str_eq(op, PCMK_VALUE_GT, pcmk__str_casei)) {
581  rc = evaluate_gt(date_expression, id, now, next_change);
582 
583  } else if (pcmk__str_eq(op, PCMK_VALUE_LT, pcmk__str_casei)) {
584  rc = evaluate_lt(date_expression, id, now, next_change);
585 
586  } else { // Not possible with schema validation enabled
587  /* @COMPAT When we can break behavioral backward compatibility,
588  * return pcmk_rc_unpack_error
589  */
591  " %s as not passing because '%s' is not a valid "
592  PCMK_XE_OPERATION, id, op);
593  }
594 
595  crm_trace(PCMK_XE_DATE_EXPRESSION " %s (%s): %s (%d)",
596  id, op, pcmk_rc_str(rc), rc);
597  return rc;
598 }
599 
616 static bool
617 process_submatches(const char *string, const char *match,
618  const regmatch_t submatches[], int nmatches,
619  char *expansion, size_t *nbytes)
620 {
621  bool expanded = false;
622  const char *src = string;
623 
624  if (nbytes != NULL) {
625  *nbytes = 1; // Include space for terminator
626  }
627 
628  while (*src != '\0') {
629  int submatch = 0;
630  size_t match_len = 0;
631 
632  if ((src[0] != '%') || !isdigit(src[1])) {
633  /* src does not point to the first character of a %N sequence,
634  * so expand this character as-is
635  */
636  if (expansion != NULL) {
637  *expansion++ = *src;
638  }
639  if (nbytes != NULL) {
640  ++(*nbytes);
641  }
642  ++src;
643  continue;
644  }
645 
646  submatch = src[1] - '0';
647  src += 2; // Skip over %N sequence in source string
648  expanded = true; // Expansion will be different from source
649 
650  // Omit sequence from expansion unless it has a non-empty match
651  if ((nmatches <= submatch) // Not enough submatches
652  || (submatches[submatch].rm_so < 0) // Pattern did not match
653  || (submatches[submatch].rm_eo
654  <= submatches[submatch].rm_so)) { // Match was empty
655  continue;
656  }
657 
658  match_len = submatches[submatch].rm_eo - submatches[submatch].rm_so;
659  if (nbytes != NULL) {
660  *nbytes += match_len;
661  }
662  if (expansion != NULL) {
663  memcpy(expansion, match + submatches[submatch].rm_so,
664  match_len);
665  expansion += match_len;
666  }
667  }
668 
669  return expanded;
670 }
671 
685 char *
686 pcmk__replace_submatches(const char *string, const char *match,
687  const regmatch_t submatches[], int nmatches)
688 {
689  size_t nbytes = 0;
690  char *result = NULL;
691 
692  if (pcmk__str_empty(string) || pcmk__str_empty(match)) {
693  return NULL; // Nothing to expand
694  }
695 
696  // Calculate how much space will be needed for expanded string
697  if (!process_submatches(string, match, submatches, nmatches, NULL,
698  &nbytes)) {
699  return NULL; // No expansions needed
700  }
701 
702  // Allocate enough space for expanded string
703  result = pcmk__assert_alloc(nbytes, sizeof(char));
704 
705  // Expand submatches
706  (void) process_submatches(string, match, submatches, nmatches, result,
707  NULL);
708  return result;
709 }
710 
723 enum pcmk__comparison
724 pcmk__parse_comparison(const char *op)
725 {
726  if (pcmk__str_eq(op, PCMK_VALUE_DEFINED, pcmk__str_casei)) {
728 
729  } else if (pcmk__str_eq(op, PCMK_VALUE_NOT_DEFINED, pcmk__str_casei)) {
731 
732  } else if (pcmk__str_eq(op, PCMK_VALUE_EQ, pcmk__str_casei)) {
733  return pcmk__comparison_eq;
734 
735  } else if (pcmk__str_eq(op, PCMK_VALUE_NE, pcmk__str_casei)) {
736  return pcmk__comparison_ne;
737 
738  } else if (pcmk__str_eq(op, PCMK_VALUE_LT, pcmk__str_casei)) {
739  return pcmk__comparison_lt;
740 
741  } else if (pcmk__str_eq(op, PCMK_VALUE_LTE, pcmk__str_casei)) {
742  return pcmk__comparison_lte;
743 
744  } else if (pcmk__str_eq(op, PCMK_VALUE_GT, pcmk__str_casei)) {
745  return pcmk__comparison_gt;
746 
747  } else if (pcmk__str_eq(op, PCMK_VALUE_GTE, pcmk__str_casei)) {
748  return pcmk__comparison_gte;
749  }
750 
752 }
753 
765 enum pcmk__type
767  const char *value1, const char *value2)
768 {
769  if (type == NULL) {
770  switch (op) {
771  case pcmk__comparison_lt:
773  case pcmk__comparison_gt:
775  if (((value1 != NULL) && (strchr(value1, '.') != NULL))
776  || ((value2 != NULL) && (strchr(value2, '.') != NULL))) {
777  return pcmk__type_number;
778  }
779  return pcmk__type_integer;
780 
781  default:
782  return pcmk__type_string;
783  }
784  }
785 
786  if (pcmk__str_eq(type, PCMK_VALUE_STRING, pcmk__str_casei)) {
787  return pcmk__type_string;
788 
789  } else if (pcmk__str_eq(type, PCMK_VALUE_INTEGER, pcmk__str_casei)) {
790  return pcmk__type_integer;
791 
792  } else if (pcmk__str_eq(type, PCMK_VALUE_NUMBER, pcmk__str_casei)) {
793  return pcmk__type_number;
794 
795  } else if (pcmk__str_eq(type, PCMK_VALUE_VERSION, pcmk__str_casei)) {
796  return pcmk__type_version;
797  }
798 
799  return pcmk__type_unknown;
800 }
801 
814 int
815 pcmk__cmp_by_type(const char *value1, const char *value2, enum pcmk__type type)
816 {
817  // NULL compares as less than non-NULL
818  if (value2 == NULL) {
819  return (value1 == NULL)? 0 : 1;
820  }
821  if (value1 == NULL) {
822  return -1;
823  }
824 
825  switch (type) {
826  case pcmk__type_string:
827  return strcasecmp(value1, value2);
828 
829  case pcmk__type_integer:
830  {
831  long long integer1;
832  long long integer2;
833 
834  if ((pcmk__scan_ll(value1, &integer1, 0LL) != pcmk_rc_ok)
835  || (pcmk__scan_ll(value2, &integer2, 0LL) != pcmk_rc_ok)) {
836  crm_warn("Comparing '%s' and '%s' as strings because "
837  "invalid as integers", value1, value2);
838  return strcasecmp(value1, value2);
839  }
840  return (integer1 < integer2)? -1 : (integer1 > integer2)? 1 : 0;
841  }
842  break;
843 
844  case pcmk__type_number:
845  {
846  double num1;
847  double num2;
848 
849  if ((pcmk__scan_double(value1, &num1, NULL, NULL) != pcmk_rc_ok)
850  || (pcmk__scan_double(value2, &num2, NULL,
851  NULL) != pcmk_rc_ok)) {
852  crm_warn("Comparing '%s' and '%s' as strings because invalid as "
853  "numbers", value1, value2);
854  return strcasecmp(value1, value2);
855  }
856  return (num1 < num2)? -1 : (num1 > num2)? 1 : 0;
857  }
858  break;
859 
860  case pcmk__type_version:
861  return compare_version(value1, value2);
862 
863  default: // Invalid type
864  return 0;
865  }
866 }
867 
877 pcmk__parse_source(const char *source)
878 {
879  if (pcmk__str_eq(source, PCMK_VALUE_LITERAL,
881  return pcmk__source_literal;
882 
883  } else if (pcmk__str_eq(source, PCMK_VALUE_PARAM, pcmk__str_casei)) {
885 
886  } else if (pcmk__str_eq(source, PCMK_VALUE_META, pcmk__str_casei)) {
888 
889  } else {
890  return pcmk__source_unknown;
891  }
892 }
893 
902 enum pcmk__combine
903 pcmk__parse_combine(const char *combine)
904 {
905  if (pcmk__str_eq(combine, PCMK_VALUE_AND,
907  return pcmk__combine_and;
908 
909  } else if (pcmk__str_eq(combine, PCMK_VALUE_OR, pcmk__str_casei)) {
910  return pcmk__combine_or;
911 
912  } else {
913  return pcmk__combine_unknown;
914  }
915 }
916 
931 static int
932 evaluate_attr_comparison(const char *actual, const char *reference,
933  enum pcmk__type type, enum pcmk__comparison comparison)
934 {
935  int cmp = 0;
936 
937  switch (comparison) {
939  return (actual != NULL)? pcmk_rc_ok : pcmk_rc_op_unsatisfied;
940 
942  return (actual == NULL)? pcmk_rc_ok : pcmk_rc_op_unsatisfied;
943 
944  default:
945  break;
946  }
947 
948  cmp = pcmk__cmp_by_type(actual, reference, type);
949 
950  switch (comparison) {
951  case pcmk__comparison_eq:
952  return (cmp == 0)? pcmk_rc_ok : pcmk_rc_op_unsatisfied;
953 
954  case pcmk__comparison_ne:
955  return (cmp != 0)? pcmk_rc_ok : pcmk_rc_op_unsatisfied;
956 
957  default:
958  break;
959  }
960 
961  if ((actual == NULL) || (reference == NULL)) {
962  return pcmk_rc_op_unsatisfied; // Comparison would be meaningless
963  }
964 
965  switch (comparison) {
966  case pcmk__comparison_lt:
967  return (cmp < 0)? pcmk_rc_ok : pcmk_rc_after_range;
968 
970  return (cmp <= 0)? pcmk_rc_ok : pcmk_rc_after_range;
971 
972  case pcmk__comparison_gt:
973  return (cmp > 0)? pcmk_rc_ok : pcmk_rc_before_range;
974 
976  return (cmp >= 0)? pcmk_rc_ok : pcmk_rc_before_range;
977 
978  default: // Not possible with schema validation enabled
979  return pcmk_rc_op_unsatisfied;
980  }
981 }
982 
991 static const char *
992 value_from_source(const char *value, enum pcmk__reference_source source,
993  const pcmk_rule_input_t *rule_input)
994 {
995  GHashTable *table = NULL;
996 
997  if (pcmk__str_empty(value)) {
998  /* @COMPAT When we can break backward compatibility, drop this block so
999  * empty strings are treated as such (there should never be an empty
1000  * string as an instance attribute or meta-attribute name, so those will
1001  * get NULL anyway, but it could matter for literal comparisons)
1002  */
1003  return NULL;
1004  }
1005 
1006  switch (source) {
1007  case pcmk__source_literal:
1008  return value;
1009 
1011  table = rule_input->rsc_params;
1012  break;
1013 
1015  table = rule_input->rsc_meta;
1016  break;
1017 
1018  default:
1019  return NULL; // Not possible
1020  }
1021 
1022  if (table == NULL) {
1023  return NULL;
1024  }
1025  return (const char *) g_hash_table_lookup(table, value);
1026 }
1027 
1038 int
1039 pcmk__evaluate_attr_expression(const xmlNode *expression,
1040  const pcmk_rule_input_t *rule_input)
1041 {
1042  const char *id = NULL;
1043  const char *op = NULL;
1044  const char *attr = NULL;
1045  const char *type_s = NULL;
1046  const char *value = NULL;
1047  const char *actual = NULL;
1048  const char *source_s = NULL;
1049  const char *reference = NULL;
1050  char *expanded_attr = NULL;
1051  int rc = pcmk_rc_ok;
1052 
1055  enum pcmk__comparison comparison = pcmk__comparison_unknown;
1056 
1057  if ((expression == NULL) || (rule_input == NULL)) {
1058  return EINVAL;
1059  }
1060 
1061  // Get expression ID (for logging)
1062  id = pcmk__xe_id(expression);
1063  if (pcmk__str_empty(id)) {
1064  /* @COMPAT When we can break behavioral backward compatibility,
1065  * fail the expression
1066  */
1067  pcmk__config_warn(PCMK_XE_EXPRESSION " element has no " PCMK_XA_ID);
1068  id = "without ID"; // for logging
1069  }
1070 
1071  /* Get name of node attribute to compare (expanding any %0-%9 to
1072  * regular expression submatches)
1073  */
1074  attr = crm_element_value(expression, PCMK_XA_ATTRIBUTE);
1075  if (attr == NULL) {
1076  pcmk__config_err("Treating " PCMK_XE_EXPRESSION " %s as not passing "
1077  "because " PCMK_XA_ATTRIBUTE " was not specified", id);
1078  return pcmk_rc_unpack_error;
1079  }
1080  expanded_attr = pcmk__replace_submatches(attr, rule_input->rsc_id,
1081  rule_input->rsc_id_submatches,
1082  rule_input->rsc_id_nmatches);
1083  if (expanded_attr != NULL) {
1084  attr = expanded_attr;
1085  }
1086 
1087  // Get and validate operation
1088  op = crm_element_value(expression, PCMK_XA_OPERATION);
1089  comparison = pcmk__parse_comparison(op);
1090  if (comparison == pcmk__comparison_unknown) {
1091  // Not possible with schema validation enabled
1092  if (op == NULL) {
1093  pcmk__config_err("Treating " PCMK_XE_EXPRESSION " %s as not "
1094  "passing because it has no " PCMK_XA_OPERATION,
1095  id);
1096  } else {
1097  pcmk__config_err("Treating " PCMK_XE_EXPRESSION " %s as not "
1098  "passing because '%s' is not a valid "
1099  PCMK_XA_OPERATION, id, op);
1100  }
1101  rc = pcmk_rc_unpack_error;
1102  goto done;
1103  }
1104 
1105  // How reference value is obtained (literal, resource meta-attribute, etc.)
1106  source_s = crm_element_value(expression, PCMK_XA_VALUE_SOURCE);
1107  source = pcmk__parse_source(source_s);
1108  if (source == pcmk__source_unknown) {
1109  // Not possible with schema validation enabled
1110  // @COMPAT Fail expression once we can break backward compatibility
1111  pcmk__config_warn("Expression %s has invalid " PCMK_XA_VALUE_SOURCE
1112  " value '%s', using default "
1113  "('" PCMK_VALUE_LITERAL "')", id, source_s);
1114  source = pcmk__source_literal;
1115  }
1116 
1117  // Get and validate reference value
1118  value = crm_element_value(expression, PCMK_XA_VALUE);
1119  switch (comparison) {
1122  if (value != NULL) {
1123  pcmk__config_warn("Ignoring " PCMK_XA_VALUE " in "
1124  PCMK_XE_EXPRESSION " %s because it is unused "
1125  "when " PCMK_XA_BOOLEAN_OP " is %s", id, op);
1126  }
1127  break;
1128 
1129  default:
1130  if (value == NULL) {
1132  PCMK_XA_VALUE, id);
1133  }
1134  break;
1135  }
1136  reference = value_from_source(value, source, rule_input);
1137 
1138  // Get actual value of node attribute
1139  if (rule_input->node_attrs != NULL) {
1140  actual = g_hash_table_lookup(rule_input->node_attrs, attr);
1141  }
1142 
1143  // Get and validate value type (after expanding reference value)
1144  type_s = crm_element_value(expression, PCMK_XA_TYPE);
1145  type = pcmk__parse_type(type_s, comparison, actual, reference);
1146  if (type == pcmk__type_unknown) {
1147  /* Not possible with schema validation enabled
1148  *
1149  * @COMPAT When we can break behavioral backward compatibility, treat
1150  * the expression as not passing.
1151  */
1152  pcmk__config_warn("Non-empty node attribute values will be treated as "
1153  "equal for " PCMK_XE_EXPRESSION " %s because '%s' "
1154  "is not a valid type", id, type_s);
1155  }
1156 
1157  rc = evaluate_attr_comparison(actual, reference, type, comparison);
1158  switch (comparison) {
1161  crm_trace(PCMK_XE_EXPRESSION " %s result: %s (for attribute %s %s)",
1162  id, pcmk_rc_str(rc), attr, op);
1163  break;
1164 
1165  default:
1166  crm_trace(PCMK_XE_EXPRESSION " %s result: "
1167  "%s (attribute %s %s '%s' via %s source as %s type)",
1168  id, pcmk_rc_str(rc), attr, op, pcmk__s(reference, ""),
1169  pcmk__s(source_s, "default"), pcmk__s(type_s, "default"));
1170  break;
1171  }
1172 
1173 done:
1174  free(expanded_attr);
1175  return rc;
1176 }
1177 
1188 int
1189 pcmk__evaluate_rsc_expression(const xmlNode *rsc_expression,
1190  const pcmk_rule_input_t *rule_input)
1191 {
1192  const char *id = NULL;
1193  const char *standard = NULL;
1194  const char *provider = NULL;
1195  const char *type = NULL;
1196 
1197  if ((rsc_expression == NULL) || (rule_input == NULL)) {
1198  return EINVAL;
1199  }
1200 
1201  // Validate XML ID
1202  id = pcmk__xe_id(rsc_expression);
1203  if (pcmk__str_empty(id)) {
1204  // Not possible with schema validation enabled
1205  /* @COMPAT When we can break behavioral backward compatibility,
1206  * fail the expression
1207  */
1209  id = "without ID"; // for logging
1210  }
1211 
1212  // Compare resource standard
1213  standard = crm_element_value(rsc_expression, PCMK_XA_CLASS);
1214  if ((standard != NULL)
1215  && !pcmk__str_eq(standard, rule_input->rsc_standard, pcmk__str_none)) {
1216  crm_trace(PCMK_XE_RSC_EXPRESSION " %s is unsatisfied because "
1217  "actual standard '%s' doesn't match '%s'",
1218  id, pcmk__s(rule_input->rsc_standard, ""), standard);
1219  return pcmk_rc_op_unsatisfied;
1220  }
1221 
1222  // Compare resource provider
1223  provider = crm_element_value(rsc_expression, PCMK_XA_PROVIDER);
1224  if ((provider != NULL)
1225  && !pcmk__str_eq(provider, rule_input->rsc_provider, pcmk__str_none)) {
1226  crm_trace(PCMK_XE_RSC_EXPRESSION " %s is unsatisfied because "
1227  "actual provider '%s' doesn't match '%s'",
1228  id, pcmk__s(rule_input->rsc_provider, ""), provider);
1229  return pcmk_rc_op_unsatisfied;
1230  }
1231 
1232  // Compare resource agent type
1233  type = crm_element_value(rsc_expression, PCMK_XA_TYPE);
1234  if ((type != NULL)
1235  && !pcmk__str_eq(type, rule_input->rsc_agent, pcmk__str_none)) {
1236  crm_trace(PCMK_XE_RSC_EXPRESSION " %s is unsatisfied because "
1237  "actual agent '%s' doesn't match '%s'",
1238  id, pcmk__s(rule_input->rsc_agent, ""), type);
1239  return pcmk_rc_op_unsatisfied;
1240  }
1241 
1242  crm_trace(PCMK_XE_RSC_EXPRESSION " %s is satisfied by %s%s%s:%s",
1243  id, pcmk__s(standard, ""),
1244  ((provider == NULL)? "" : ":"), pcmk__s(provider, ""),
1245  pcmk__s(type, ""));
1246  return pcmk_rc_ok;
1247 }
1248 
1259 int
1260 pcmk__evaluate_op_expression(const xmlNode *op_expression,
1261  const pcmk_rule_input_t *rule_input)
1262 {
1263  const char *id = NULL;
1264  const char *name = NULL;
1265  const char *interval_s = NULL;
1266  guint interval_ms = 0U;
1267 
1268  if ((op_expression == NULL) || (rule_input == NULL)) {
1269  return EINVAL;
1270  }
1271 
1272  // Get operation expression ID (for logging)
1273  id = pcmk__xe_id(op_expression);
1274  if (pcmk__str_empty(id)) { // Not possible with schema validation enabled
1275  /* @COMPAT When we can break behavioral backward compatibility,
1276  * return pcmk_rc_op_unsatisfied
1277  */
1279  id = "without ID"; // for logging
1280  }
1281 
1282  // Validate operation name
1283  name = crm_element_value(op_expression, PCMK_XA_NAME);
1284  if (name == NULL) { // Not possible with schema validation enabled
1285  pcmk__config_warn("Treating " PCMK_XE_OP_EXPRESSION " %s as not "
1286  "passing because it has no " PCMK_XA_NAME, id);
1287  return pcmk_rc_unpack_error;
1288  }
1289 
1290  // Validate operation interval
1291  interval_s = crm_element_value(op_expression, PCMK_META_INTERVAL);
1292  if (pcmk_parse_interval_spec(interval_s, &interval_ms) != pcmk_rc_ok) {
1293  pcmk__config_warn("Treating " PCMK_XE_OP_EXPRESSION " %s as not "
1294  "passing because '%s' is not a valid interval",
1295  id, interval_s);
1296  return pcmk_rc_unpack_error;
1297  }
1298 
1299  // Compare operation name
1300  if (!pcmk__str_eq(name, rule_input->op_name, pcmk__str_none)) {
1301  crm_trace(PCMK_XE_OP_EXPRESSION " %s is unsatisfied because "
1302  "actual name '%s' doesn't match '%s'",
1303  id, pcmk__s(rule_input->op_name, ""), name);
1304  return pcmk_rc_op_unsatisfied;
1305  }
1306 
1307  // Compare operation interval (unspecified interval matches all)
1308  if ((interval_s != NULL) && (interval_ms != rule_input->op_interval_ms)) {
1309  crm_trace(PCMK_XE_OP_EXPRESSION " %s is unsatisfied because "
1310  "actual interval %s doesn't match %s",
1311  id, pcmk__readable_interval(rule_input->op_interval_ms),
1312  pcmk__readable_interval(interval_ms));
1313  return pcmk_rc_op_unsatisfied;
1314  }
1315 
1316  crm_trace(PCMK_XE_OP_EXPRESSION " %s is satisfied (name %s, interval %s)",
1317  id, name, pcmk__readable_interval(rule_input->op_interval_ms));
1318  return pcmk_rc_ok;
1319 }
1320 
1333 int
1334 pcmk__evaluate_condition(xmlNode *condition,
1335  const pcmk_rule_input_t *rule_input,
1336  crm_time_t *next_change)
1337 {
1338 
1339  if ((condition == NULL) || (rule_input == NULL)) {
1340  return EINVAL;
1341  }
1342 
1343  switch (pcmk__condition_type(condition)) {
1344  case pcmk__condition_rule:
1345  return pcmk_evaluate_rule(condition, rule_input, next_change);
1346 
1349  return pcmk__evaluate_attr_expression(condition, rule_input);
1350 
1352  {
1353  int rc = pcmk__evaluate_date_expression(condition,
1354  rule_input->now,
1355  next_change);
1356 
1357  return (rc == pcmk_rc_within_range)? pcmk_rc_ok : rc;
1358  }
1359 
1361  return pcmk__evaluate_rsc_expression(condition, rule_input);
1362 
1364  return pcmk__evaluate_op_expression(condition, rule_input);
1365 
1366  default: // Not possible with schema validation enabled
1367  pcmk__config_err("Treating rule condition %s as not passing "
1368  "because %s is not a valid condition type",
1369  pcmk__s(pcmk__xe_id(condition), "without ID"),
1370  (const char *) condition->name);
1371  return pcmk_rc_unpack_error;
1372  }
1373 }
1374 
1385 int
1386 pcmk_evaluate_rule(xmlNode *rule, const pcmk_rule_input_t *rule_input,
1387  crm_time_t *next_change)
1388 {
1389  bool empty = true;
1390  int rc = pcmk_rc_ok;
1391  const char *id = NULL;
1392  const char *value = NULL;
1393  enum pcmk__combine combine = pcmk__combine_unknown;
1394 
1395  if ((rule == NULL) || (rule_input == NULL)) {
1396  return EINVAL;
1397  }
1398 
1399  rule = expand_idref(rule, NULL);
1400  if (rule == NULL) {
1401  // Not possible with schema validation enabled; message already logged
1402  return pcmk_rc_unpack_error;
1403  }
1404 
1405  // Validate XML ID
1406  id = pcmk__xe_id(rule);
1407  if (pcmk__str_empty(id)) {
1408  /* @COMPAT When we can break behavioral backward compatibility,
1409  * fail the rule
1410  */
1412  id = "without ID"; // for logging
1413  }
1414 
1415  value = crm_element_value(rule, PCMK_XA_BOOLEAN_OP);
1416  combine = pcmk__parse_combine(value);
1417  switch (combine) {
1418  case pcmk__combine_and:
1419  // For "and", rc defaults to success (reset on failure below)
1420  break;
1421 
1422  case pcmk__combine_or:
1423  // For "or", rc defaults to failure (reset on success below)
1425  break;
1426 
1427  default:
1428  /* @COMPAT When we can break behavioral backward compatibility,
1429  * return pcmk_rc_unpack_error
1430  */
1431  pcmk__config_warn("Rule %s has invalid " PCMK_XA_BOOLEAN_OP
1432  " value '%s', using default '" PCMK_VALUE_AND "'",
1433  pcmk__xe_id(rule), value);
1434  combine = pcmk__combine_and;
1435  break;
1436  }
1437 
1438  // Evaluate each condition
1439  for (xmlNode *condition = pcmk__xe_first_child(rule, NULL, NULL, NULL);
1440  condition != NULL; condition = pcmk__xe_next(condition)) {
1441 
1442  empty = false;
1443  if (pcmk__evaluate_condition(condition, rule_input,
1444  next_change) == pcmk_rc_ok) {
1445  if (combine == pcmk__combine_or) {
1446  rc = pcmk_rc_ok; // Any pass is final for "or"
1447  break;
1448  }
1449  } else if (combine == pcmk__combine_and) {
1450  rc = pcmk_rc_op_unsatisfied; // Any failure is final for "and"
1451  break;
1452  }
1453  }
1454 
1455  if (empty) { // Not possible with schema validation enabled
1456  /* @COMPAT Currently, we don't actually ignore "or" rules because
1457  * rc is initialized to failure above in that case. When we can break
1458  * backward compatibility, reset rc to pcmk_rc_ok here.
1459  */
1460  pcmk__config_warn("Ignoring rule %s because it contains no conditions",
1461  id);
1462  }
1463 
1464  crm_trace("Rule %s is %ssatisfied", id, ((rc == pcmk_rc_ok)? "" : "not "));
1465  return rc;
1466 }
1467 
1486 int
1487 pcmk__evaluate_rules(xmlNode *xml, const pcmk_rule_input_t *rule_input,
1488  crm_time_t *next_change)
1489 {
1490  // If there are no rules, pass by default
1491  int rc = pcmk_rc_ok;
1492  bool have_rule = false;
1493 
1494  for (xmlNode *rule = pcmk__xe_first_child(xml, PCMK_XE_RULE, NULL, NULL);
1495  rule != NULL; rule = pcmk__xe_next_same(rule)) {
1496 
1497  if (have_rule) {
1499  "Support for multiple top-level rules is "
1500  "deprecated (replace with a single rule containing "
1501  "the existing rules with " PCMK_XA_BOOLEAN_OP
1502  "set to " PCMK_VALUE_OR " instead)");
1503  } else {
1504  have_rule = true;
1505  }
1506 
1507  rc = pcmk_evaluate_rule(rule, rule_input, next_change);
1508  if (rc == pcmk_rc_ok) {
1509  break;
1510  }
1511  }
1512  return rc;
1513 }
#define PCMK_XA_YEARS
Definition: xml_names.h:456
enum pcmk__type pcmk__parse_type(const char *type, enum pcmk__comparison op, const char *value1, const char *value2)
Definition: rules.c:766
#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:1386
GHashTable * rsc_meta
Definition: rules.h:93
#define PCMK_XA_HOURS
Definition: xml_names.h:300
int crm_time_get_isoweek(const crm_time_t *dt, uint32_t *y, uint32_t *w, uint32_t *d)
Definition: iso8601.c:421
#define PCMK_XA_NAME
Definition: xml_names.h:330
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:1766
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:728
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:455
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:1039
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:448
pcmk__reference_source
#define PCMK_VALUE_NOT_DEFINED
Definition: options.h:180
#define PCMK_XA_WEEKDAYS
Definition: xml_names.h:446
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:724
#define PCMK_XE_DURATION
Definition: xml_names.h:104
crm_time_t * pcmk_copy_time(const crm_time_t *source)
Definition: iso8601.c:1471
#define PCMK_XE_EXPRESSION
Definition: xml_names.h:109
pcmk__type
#define PCMK_VALUE_AND
Definition: options.h:133
#define PCMK_XA_PROVIDER
Definition: xml_names.h:364
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:293
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:503
int crm_time_get_ordinal(const crm_time_t *dt, uint32_t *y, uint32_t *d)
Definition: iso8601.c:413
#define PCMK_XA_TYPE
Definition: xml_names.h:430
#define PCMK_XA_OPERATION
Definition: xml_names.h:349
#define PCMK_XA_START
Definition: xml_names.h:408
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:877
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:462
#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:96
#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:1260
#define PCMK_XE_OP_EXPRESSION
Definition: xml_names.h:148
#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:458
#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:481
#define PCMK_XE_DATE_EXPRESSION
Definition: xml_names.h:96
#define PCMK_VALUE_LITERAL
Definition: options.h:165
#define PCMK_XE_DATE_SPEC
Definition: xml_names.h:97
#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:447
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:2170
#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:903
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:301
#define PCMK_XA_VALUE
Definition: xml_names.h:442
int pcmk__evaluate_condition(xmlNode *condition, const pcmk_rule_input_t *rule_input, crm_time_t *next_change)
Definition: rules.c:1334
int crm_time_get_gregorian(const crm_time_t *dt, uint32_t *y, uint32_t *m, uint32_t *d)
Definition: iso8601.c:380
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1062
#define PCMK_XE_RSC_EXPRESSION
Definition: xml_names.h:187
#define PCMK_XA_MINUTES
Definition: xml_names.h:325
int pcmk__scan_double(const char *text, double *result, const char *default_text, char **end_text)
Definition: strings.c:201
#define pcmk__warn_once(wo_flag, fmt...)
#define PCMK_XE_RULE
Definition: xml_names.h:191
int crm_time_compare(const crm_time_t *a, const crm_time_t *b)
Definition: iso8601.c:1736
#define PCMK_XA_MONTHDAYS
Definition: xml_names.h:327
int pcmk__evaluate_date_expression(const xmlNode *date_expression, const crm_time_t *now, crm_time_t *next_change)
Definition: rules.c:535
#define PCMK_XA_SECONDS
Definition: xml_names.h:399
#define PCMK_XA_CLASS
Definition: xml_names.h:246
#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:686
G_GNUC_INTERNAL void pcmk__set_time_if_earlier(crm_time_t *target, const crm_time_t *source)
Definition: iso8601.c:1461
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:149
int compare_version(const char *version1, const char *version2)
Definition: utils.c:186
#define PCMK_XA_ATTRIBUTE
Definition: xml_names.h:236
#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:1487
#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:240
int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
Definition: strings.c:915
const char * pcmk__readable_interval(guint interval_ms)
Definition: iso8601.c:2206
#define PCMK_VALUE_INTEGER
Definition: options.h:164
#define PCMK_VALUE_NE
Definition: options.h:176
#define PCMK_XA_END
Definition: xml_names.h:267
int pcmk__cmp_by_type(const char *value1, const char *value2, enum pcmk__type type)
Definition: rules.c:815
#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:443
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition: xml.c:2130
int crm_time_get_timeofday(const crm_time_t *dt, uint32_t *h, uint32_t *m, uint32_t *s)
Definition: iso8601.c:314
#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:1189
#define PCMK_XA_MONTHS
Definition: xml_names.h:328
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:150